From ed70f3a264e9f746eaf17c96ccc4c9b7eda742dc Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 4 Jun 2014 10:11:06 +0100 Subject: ASoC: arizona: Implement TDM support for Arizona devices Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- include/linux/mfd/arizona/core.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h index 5cf8b91ce996..11783b511b9a 100644 --- a/include/linux/mfd/arizona/core.h +++ b/include/linux/mfd/arizona/core.h @@ -110,6 +110,9 @@ struct arizona { int clk32k_ref; struct snd_soc_dapm_context *dapm; + + int tdm_width[ARIZONA_MAX_AIF]; + int tdm_slots[ARIZONA_MAX_AIF]; }; int arizona_clk32k_enable(struct arizona *arizona); -- cgit v1.2.3 From cc9e92431ee9c7fe974266e0e6533a1a68e45539 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 6 Jun 2014 14:14:05 +0100 Subject: ASoC: wm5102: Add controls to allow shaping of ultrasonic response Add controls to allow custom shaping of the ultrasonic response. This custom shaping can be turned on/off at runtime, although, it should be noted that settings will not affect a currently open audio stream, they will be applied when the next audio stream is started. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- include/linux/mfd/arizona/core.h | 3 ++ sound/soc/codecs/arizona.c | 34 ++++++++++++++++++++++ sound/soc/codecs/wm5102.c | 62 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+) (limited to 'include') diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h index 11783b511b9a..55926517d50b 100644 --- a/include/linux/mfd/arizona/core.h +++ b/include/linux/mfd/arizona/core.h @@ -113,6 +113,9 @@ struct arizona { int tdm_width[ARIZONA_MAX_AIF]; int tdm_slots[ARIZONA_MAX_AIF]; + + uint16_t dac_comp_coeff; + uint8_t dac_comp_enabled; }; int arizona_clk32k_enable(struct arizona *arizona); diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index e77f61c387f7..41b56ee6ff51 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1127,6 +1127,31 @@ static int arizona_startup(struct snd_pcm_substream *substream, constraint); } +static void arizona_wm5102_set_dac_comp(struct snd_soc_codec *codec, + unsigned int rate) +{ + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->arizona; + struct reg_default dac_comp[] = { + { 0x80, 0x3 }, + { ARIZONA_DAC_COMP_1, 0 }, + { ARIZONA_DAC_COMP_2, 0 }, + { 0x80, 0x0 }, + }; + + mutex_lock(&codec->mutex); + + dac_comp[1].def = arizona->dac_comp_coeff; + if (rate >= 176400) + dac_comp[2].def = arizona->dac_comp_enabled; + + mutex_unlock(&codec->mutex); + + regmap_multi_reg_write(arizona->regmap, + dac_comp, + ARRAY_SIZE(dac_comp)); +} + static int arizona_hw_params_rate(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -1153,6 +1178,15 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream, switch (dai_priv->clk) { case ARIZONA_CLK_SYSCLK: + switch (priv->arizona->type) { + case WM5102: + arizona_wm5102_set_dac_comp(codec, + params_rate(params)); + break; + default: + break; + } + snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1, ARIZONA_SAMPLE_RATE_1_MASK, sr_val); if (base) diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index dcf1d12cfef8..7bf2397fc25a 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -612,6 +612,62 @@ static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w, return 0; } +static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct arizona *arizona = dev_get_drvdata(codec->dev->parent); + uint16_t data; + + mutex_lock(&codec->mutex); + data = cpu_to_be16(arizona->dac_comp_coeff); + memcpy(ucontrol->value.bytes.data, &data, sizeof(data)); + mutex_unlock(&codec->mutex); + + return 0; +} + +static int wm5102_out_comp_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct arizona *arizona = dev_get_drvdata(codec->dev->parent); + + mutex_lock(&codec->mutex); + memcpy(&arizona->dac_comp_coeff, ucontrol->value.bytes.data, + sizeof(arizona->dac_comp_coeff)); + arizona->dac_comp_coeff = be16_to_cpu(arizona->dac_comp_coeff); + mutex_unlock(&codec->mutex); + + return 0; +} + +static int wm5102_out_comp_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct arizona *arizona = dev_get_drvdata(codec->dev->parent); + + mutex_lock(&codec->mutex); + ucontrol->value.integer.value[0] = arizona->dac_comp_enabled; + mutex_unlock(&codec->mutex); + + return 0; +} + +static int wm5102_out_comp_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct arizona *arizona = dev_get_drvdata(codec->dev->parent); + + mutex_lock(&codec->mutex); + arizona->dac_comp_enabled = ucontrol->value.integer.value[0]; + mutex_unlock(&codec->mutex); + + return 0; +} + static const char *wm5102_osr_text[] = { "Low power", "Normal", "High performance", }; @@ -843,6 +899,12 @@ SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL, ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv), SOC_ENUM("Noise Gate Hold", arizona_ng_hold), +SND_SOC_BYTES_EXT("Output Compensation Coefficient", 2, + wm5102_out_comp_coeff_get, wm5102_out_comp_coeff_put), + +SOC_SINGLE_EXT("Output Compensation Switch", 0, 0, 1, 0, + wm5102_out_comp_switch_get, wm5102_out_comp_switch_put), + WM5102_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L), WM5102_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R), WM5102_NG_SRC("HPOUT2L", ARIZONA_NOISE_GATE_SELECT_2L), -- cgit v1.2.3 From 962bd40bc30e412828e091bfda041b7547e779c8 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 10 Jun 2014 12:24:40 -0400 Subject: locks: add missing memory barrier in break_deleg break_deleg is subject to the same potential race as break_lease. Add a memory barrier to prevent it. Signed-off-by: Jeff Layton --- include/linux/fs.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/linux/fs.h b/include/linux/fs.h index c3f46e499dd0..22ae79650b82 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1914,6 +1914,12 @@ static inline int break_lease(struct inode *inode, unsigned int mode) static inline int break_deleg(struct inode *inode, unsigned int mode) { + /* + * Since this check is lockless, we must ensure that any refcounts + * taken are done before checking inode->i_flock. Otherwise, we could + * end up racing with tasks trying to set a new lease on this file. + */ + smp_mb(); if (inode->i_flock) return __break_lease(inode, mode, FL_DELEG); return 0; -- cgit v1.2.3 From 161d0dc1dccb17ff7a38f462c7c0d4ef8bcc5662 Mon Sep 17 00:00:00 2001 From: Matt Roper Date: Tue, 10 Jun 2014 08:28:10 -0700 Subject: drm: Support legacy cursor ioctls via universal planes when possible (v4) If drivers support universal planes and have registered a cursor plane with the DRM core, we should use that universal plane support when handling legacy cursor ioctls. Drivers that transition to universal planes won't have to maintain separate legacy ioctl handling; drivers that don't transition to universal planes will continue to operate without any change to behavior. Note that there's a bit of a mismatch between the legacy cursor ioctls and the universal plane API's --- legacy ioctl's use driver buffer handles directly whereas the universal plane API takes drm_framebuffers. Since there's no way to recover the driver handle from a drm_framebuffer, we can implement legacy ioctl's in terms of universal plane interfaces, but cannot implement universal plane interfaces in terms of legacy ioctls. Specifically, there's no way to create a general cursor helper in the way we previously created a primary plane helper. It's important to land this patch before any patches that add universal cursor support to individual drivers so that drivers don't have to worry about juggling two different styles of reference counting for cursor buffers when userspace mixes and matches legacy and universal cursor calls. With this patch, a driver that switches to universal cursor support may assume that all cursor buffers are wrapped in a drm_framebuffer and can rely on framebuffer reference counting for all cursor operations. v4: - Add comments pointing out setplane_internal's reference-eating semantics. v3: - Drop drm_mode_rmfb() call that is no longer needed now that we're using setplane_internal(), which takes care of deref'ing the appropriate framebuffer. v2: - Use new add_framebuffer_internal() function to create framebuffer rather than trying to call directly into the ioctl interface and look up the handle returned. - Use new setplane_internal() function to update the cursor plane rather than calling through the ioctl interface. Note that since we're no longer looking up an fb_id, no extra reference will be taken here. - Grab extra reference to fb under lock in !BO case to avoid issues where racing userspace could cause the fb to be destroyed out from under us after we grab the fb pointer. Reviewed-by: Daniel Vetter Signed-off-by: Matt Roper Reviewed-by: Pallavi G Acked-by: Dave Airlie Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 107 +++++++++++++++++++++++++++++++++++++++++++++ include/drm/drm_crtc.h | 4 ++ 2 files changed, 111 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 27eae03bd38e..b5bce5b6bf61 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2288,6 +2288,10 @@ int drm_mode_setplane(struct drm_device *dev, void *data, crtc = obj_to_crtc(obj); } + /* + * setplane_internal will take care of deref'ing either the old or new + * framebuffer depending on success. + */ return setplane_internal(crtc, plane, fb, plane_req->crtc_x, plane_req->crtc_y, plane_req->crtc_w, plane_req->crtc_h, @@ -2541,6 +2545,102 @@ out: return ret; } +/** + * drm_mode_cursor_universal - translate legacy cursor ioctl call into a + * universal plane handler call + * @crtc: crtc to update cursor for + * @req: data pointer for the ioctl + * @file_priv: drm file for the ioctl call + * + * Legacy cursor ioctl's work directly with driver buffer handles. To + * translate legacy ioctl calls into universal plane handler calls, we need to + * wrap the native buffer handle in a drm_framebuffer. + * + * Note that we assume any handle passed to the legacy ioctls was a 32-bit ARGB + * buffer with a pitch of 4*width; the universal plane interface should be used + * directly in cases where the hardware can support other buffer settings and + * userspace wants to make use of these capabilities. + * + * Returns: + * Zero on success, errno on failure. + */ +static int drm_mode_cursor_universal(struct drm_crtc *crtc, + struct drm_mode_cursor2 *req, + struct drm_file *file_priv) +{ + struct drm_device *dev = crtc->dev; + struct drm_framebuffer *fb = NULL; + struct drm_mode_fb_cmd2 fbreq = { + .width = req->width, + .height = req->height, + .pixel_format = DRM_FORMAT_ARGB8888, + .pitches = { req->width * 4 }, + .handles = { req->handle }, + }; + int32_t crtc_x, crtc_y; + uint32_t crtc_w = 0, crtc_h = 0; + uint32_t src_w = 0, src_h = 0; + int ret = 0; + + BUG_ON(!crtc->cursor); + + /* + * Obtain fb we'll be using (either new or existing) and take an extra + * reference to it if fb != null. setplane will take care of dropping + * the reference if the plane update fails. + */ + if (req->flags & DRM_MODE_CURSOR_BO) { + if (req->handle) { + fb = add_framebuffer_internal(dev, &fbreq, file_priv); + if (IS_ERR(fb)) { + DRM_DEBUG_KMS("failed to wrap cursor buffer in drm framebuffer\n"); + return PTR_ERR(fb); + } + + drm_framebuffer_reference(fb); + } else { + fb = NULL; + } + } else { + mutex_lock(&dev->mode_config.mutex); + fb = crtc->cursor->fb; + if (fb) + drm_framebuffer_reference(fb); + mutex_unlock(&dev->mode_config.mutex); + } + + if (req->flags & DRM_MODE_CURSOR_MOVE) { + crtc_x = req->x; + crtc_y = req->y; + } else { + crtc_x = crtc->cursor_x; + crtc_y = crtc->cursor_y; + } + + if (fb) { + crtc_w = fb->width; + crtc_h = fb->height; + src_w = fb->width << 16; + src_h = fb->height << 16; + } + + /* + * setplane_internal will take care of deref'ing either the old or new + * framebuffer depending on success. + */ + ret = setplane_internal(crtc, crtc->cursor, fb, + crtc_x, crtc_y, crtc_w, crtc_h, + 0, 0, src_w, src_h); + + /* Update successful; save new cursor position, if necessary */ + if (ret == 0 && req->flags & DRM_MODE_CURSOR_MOVE) { + crtc->cursor_x = req->x; + crtc->cursor_y = req->y; + } + + return ret; +} + static int drm_mode_cursor_common(struct drm_device *dev, struct drm_mode_cursor2 *req, struct drm_file *file_priv) @@ -2560,6 +2660,13 @@ static int drm_mode_cursor_common(struct drm_device *dev, return -ENOENT; } + /* + * If this crtc has a universal cursor plane, call that plane's update + * handler rather than using legacy cursor handlers. + */ + if (crtc->cursor) + return drm_mode_cursor_universal(crtc, req, file_priv); + drm_modeset_lock(&crtc->mutex, NULL); if (req->flags & DRM_MODE_CURSOR_BO) { if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) { diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 251b75e6bf7a..b8c7a9a8cb6b 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -331,6 +331,10 @@ struct drm_crtc { struct drm_plane *primary; struct drm_plane *cursor; + /* position of cursor plane on crtc */ + int cursor_x; + int cursor_y; + /* Temporary tracking of the old fb while a modeset is ongoing. Used * by drm_mode_set_config_internal to implement correct refcounting. */ struct drm_framebuffer *old_fb; -- cgit v1.2.3 From fc1d3e44ef7c1db93384150fdbf8948dcf949f15 Mon Sep 17 00:00:00 2001 From: Matt Roper Date: Tue, 10 Jun 2014 08:28:11 -0700 Subject: drm: Allow drivers to register cursor planes with crtc Universal plane support had placeholders for cursor planes, but didn't actually do anything with them. Save the cursor plane reference inside the crtc and update the cursor plane parameter from void* to drm_plane. Reviewed-by: Daniel Vetter Signed-off-by: Matt Roper Reviewed-by: Pallavi G Acked-by: Dave Airlie Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 5 ++++- include/drm/drm_crtc.h | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index d86d254693e4..41c7212081b8 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -727,7 +727,7 @@ DEFINE_WW_CLASS(crtc_ww_class); */ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, struct drm_plane *primary, - void *cursor, + struct drm_plane *cursor, const struct drm_crtc_funcs *funcs) { struct drm_mode_config *config = &dev->mode_config; @@ -752,8 +752,11 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, config->num_crtc++; crtc->primary = primary; + crtc->cursor = cursor; if (primary) primary->possible_crtcs = 1 << drm_crtc_index(crtc); + if (cursor) + cursor->possible_crtcs = 1 << drm_crtc_index(crtc); out: drm_modeset_unlock_all(dev); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index b8c7a9a8cb6b..4ee7e26a012f 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -856,7 +856,7 @@ struct drm_prop_enum_list { extern int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, struct drm_plane *primary, - void *cursor, + struct drm_plane *cursor, const struct drm_crtc_funcs *funcs); extern int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, -- cgit v1.2.3 From 398fd22b6b94cb15c1c299bceecd63644a1b17b4 Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Sat, 6 Dec 2014 06:46:00 +0000 Subject: iio: Remove timestamp argument from iio_trigger_poll() and iio_trigger_poll_chained() argument has been ignored; adjust drivers accordingly Signed-off-by: Peter Meerwald Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad_sigma_delta.c | 2 +- drivers/iio/adc/at91_adc.c | 2 +- drivers/iio/adc/xilinx-xadc-core.c | 2 +- drivers/iio/industrialio-trigger.c | 8 ++++---- drivers/iio/light/gp2ap020a00f.c | 2 +- drivers/iio/proximity/as3935.c | 2 +- drivers/iio/trigger/iio-trig-interrupt.c | 3 +-- drivers/iio/trigger/iio-trig-sysfs.c | 2 +- drivers/staging/iio/accel/lis3l02dq_ring.c | 2 +- drivers/staging/iio/adc/mxs-lradc.c | 2 +- drivers/staging/iio/meter/ade7758_trigger.c | 2 +- drivers/staging/iio/trigger/iio-trig-bfin-timer.c | 2 +- drivers/staging/iio/trigger/iio-trig-periodic-rtc.c | 3 +-- include/linux/iio/trigger.h | 5 ++--- 14 files changed, 18 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 9a4e0e32a771..c55b81f7f970 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -410,7 +410,7 @@ static irqreturn_t ad_sd_data_rdy_trig_poll(int irq, void *private) complete(&sigma_delta->completion); disable_irq_nosync(irq); sigma_delta->irq_dis = true; - iio_trigger_poll(sigma_delta->trig, iio_get_time_ns()); + iio_trigger_poll(sigma_delta->trig); return IRQ_HANDLED; } diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 89777ed9abd8..b9592010511e 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -149,7 +149,7 @@ void handle_adc_eoc_trigger(int irq, struct iio_dev *idev) if (iio_buffer_enabled(idev)) { disable_irq_nosync(irq); - iio_trigger_poll(idev->trig, iio_get_time_ns()); + iio_trigger_poll(idev->trig); } else { st->last_value = at91_adc_readl(st, AT91_ADC_LCDR); st->done = true; diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c index ab52be29141b..fd2745c62943 100644 --- a/drivers/iio/adc/xilinx-xadc-core.c +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -486,7 +486,7 @@ static irqreturn_t xadc_axi_interrupt_handler(int irq, void *devid) return IRQ_NONE; if ((status & XADC_AXI_INT_EOS) && xadc->trigger) - iio_trigger_poll(xadc->trigger, 0); + iio_trigger_poll(xadc->trigger); if (status & XADC_AXI_INT_ALARM_MASK) { /* diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index 3383b025f62e..d31098e0c43f 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -114,7 +114,7 @@ static struct iio_trigger *iio_trigger_find_by_name(const char *name, return trig; } -void iio_trigger_poll(struct iio_trigger *trig, s64 time) +void iio_trigger_poll(struct iio_trigger *trig) { int i; @@ -133,12 +133,12 @@ EXPORT_SYMBOL(iio_trigger_poll); irqreturn_t iio_trigger_generic_data_rdy_poll(int irq, void *private) { - iio_trigger_poll(private, iio_get_time_ns()); + iio_trigger_poll(private); return IRQ_HANDLED; } EXPORT_SYMBOL(iio_trigger_generic_data_rdy_poll); -void iio_trigger_poll_chained(struct iio_trigger *trig, s64 time) +void iio_trigger_poll_chained(struct iio_trigger *trig) { int i; @@ -161,7 +161,7 @@ void iio_trigger_notify_done(struct iio_trigger *trig) trig->ops->try_reenable) if (trig->ops->try_reenable(trig)) /* Missed an interrupt so launch new poll now */ - iio_trigger_poll(trig, 0); + iio_trigger_poll(trig); } EXPORT_SYMBOL(iio_trigger_notify_done); diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c index 04bdb85d2d9f..221ed16de1f7 100644 --- a/drivers/iio/light/gp2ap020a00f.c +++ b/drivers/iio/light/gp2ap020a00f.c @@ -827,7 +827,7 @@ static void gp2ap020a00f_iio_trigger_work(struct irq_work *work) struct gp2ap020a00f_data *data = container_of(work, struct gp2ap020a00f_data, work); - iio_trigger_poll(data->trig, 0); + iio_trigger_poll(data->trig); } static irqreturn_t gp2ap020a00f_prox_sensing_handler(int irq, void *data) diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index bf677bfe8eb2..5e780ef206f3 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -232,7 +232,7 @@ static void as3935_event_work(struct work_struct *work) switch (val) { case AS3935_EVENT_INT: - iio_trigger_poll(st->trig, iio_get_time_ns()); + iio_trigger_poll(st->trig); break; case AS3935_NOISE_INT: dev_warn(&st->spi->dev, "noise level is too high"); diff --git a/drivers/iio/trigger/iio-trig-interrupt.c b/drivers/iio/trigger/iio-trig-interrupt.c index 02577ec54c6b..7a149a7822bc 100644 --- a/drivers/iio/trigger/iio-trig-interrupt.c +++ b/drivers/iio/trigger/iio-trig-interrupt.c @@ -24,8 +24,7 @@ struct iio_interrupt_trigger_info { static irqreturn_t iio_interrupt_trigger_poll(int irq, void *private) { - /* Timestamp not currently provided */ - iio_trigger_poll(private, 0); + iio_trigger_poll(private); return IRQ_HANDLED; } diff --git a/drivers/iio/trigger/iio-trig-sysfs.c b/drivers/iio/trigger/iio-trig-sysfs.c index 15e3b850f513..254c7e906127 100644 --- a/drivers/iio/trigger/iio-trig-sysfs.c +++ b/drivers/iio/trigger/iio-trig-sysfs.c @@ -96,7 +96,7 @@ static void iio_sysfs_trigger_work(struct irq_work *work) struct iio_sysfs_trig *trig = container_of(work, struct iio_sysfs_trig, work); - iio_trigger_poll(trig->trig, 0); + iio_trigger_poll(trig->trig); } static ssize_t iio_sysfs_trigger_poll(struct device *dev, diff --git a/drivers/staging/iio/accel/lis3l02dq_ring.c b/drivers/staging/iio/accel/lis3l02dq_ring.c index 79cefe0a516a..bf33fdead479 100644 --- a/drivers/staging/iio/accel/lis3l02dq_ring.c +++ b/drivers/staging/iio/accel/lis3l02dq_ring.c @@ -31,7 +31,7 @@ irqreturn_t lis3l02dq_data_rdy_trig_poll(int irq, void *private) struct lis3l02dq_state *st = iio_priv(indio_dev); if (st->trigger_on) { - iio_trigger_poll(st->trig, iio_get_time_ns()); + iio_trigger_poll(st->trig); return IRQ_HANDLED; } else return IRQ_WAKE_THREAD; diff --git a/drivers/staging/iio/adc/mxs-lradc.c b/drivers/staging/iio/adc/mxs-lradc.c index dae8d1a9038e..d8619993c6ff 100644 --- a/drivers/staging/iio/adc/mxs-lradc.c +++ b/drivers/staging/iio/adc/mxs-lradc.c @@ -1166,7 +1166,7 @@ static irqreturn_t mxs_lradc_handle_irq(int irq, void *data) mxs_lradc_handle_touch(lradc); if (iio_buffer_enabled(iio)) - iio_trigger_poll(iio->trig, iio_get_time_ns()); + iio_trigger_poll(iio->trig); else if (reg & LRADC_CTRL1_LRADC_IRQ(0)) complete(&lradc->completion); diff --git a/drivers/staging/iio/meter/ade7758_trigger.c b/drivers/staging/iio/meter/ade7758_trigger.c index 7a94ddd42f59..ea01b8f7a2c3 100644 --- a/drivers/staging/iio/meter/ade7758_trigger.c +++ b/drivers/staging/iio/meter/ade7758_trigger.c @@ -21,7 +21,7 @@ static irqreturn_t ade7758_data_rdy_trig_poll(int irq, void *private) { disable_irq_nosync(irq); - iio_trigger_poll(private, iio_get_time_ns()); + iio_trigger_poll(private); return IRQ_HANDLED; } diff --git a/drivers/staging/iio/trigger/iio-trig-bfin-timer.c b/drivers/staging/iio/trigger/iio-trig-bfin-timer.c index 26e1ca0b7800..16f1a06bcd89 100644 --- a/drivers/staging/iio/trigger/iio-trig-bfin-timer.c +++ b/drivers/staging/iio/trigger/iio-trig-bfin-timer.c @@ -154,7 +154,7 @@ static irqreturn_t iio_bfin_tmr_trigger_isr(int irq, void *devid) struct bfin_tmr_state *st = devid; clear_gptimer_intr(st->t->id); - iio_trigger_poll(st->trig, 0); + iio_trigger_poll(st->trig); return IRQ_HANDLED; } diff --git a/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c b/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c index b5108a163e37..b1aeb88273c9 100644 --- a/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c +++ b/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c @@ -106,8 +106,7 @@ static const struct attribute_group *iio_trig_prtc_attr_groups[] = { static void iio_prtc_trigger_poll(void *private_data) { - /* Timestamp is not provided currently */ - iio_trigger_poll(private_data, 0); + iio_trigger_poll(private_data); } static const struct iio_trigger_ops iio_prtc_trigger_ops = { diff --git a/include/linux/iio/trigger.h b/include/linux/iio/trigger.h index 369cf2cd5144..4b79ffe7b188 100644 --- a/include/linux/iio/trigger.h +++ b/include/linux/iio/trigger.h @@ -129,12 +129,11 @@ void iio_trigger_unregister(struct iio_trigger *trig_info); /** * iio_trigger_poll() - called on a trigger occurring * @trig: trigger which occurred - * @time: timestamp when trigger occurred * * Typically called in relevant hardware interrupt handler. **/ -void iio_trigger_poll(struct iio_trigger *trig, s64 time); -void iio_trigger_poll_chained(struct iio_trigger *trig, s64 time); +void iio_trigger_poll(struct iio_trigger *trig); +void iio_trigger_poll_chained(struct iio_trigger *trig); irqreturn_t iio_trigger_generic_data_rdy_poll(int irq, void *private); -- cgit v1.2.3 From 1a4fbf6a9286a6e3db497bc7bbae2024f0f1ad90 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Thu, 6 Nov 2014 23:07:00 +0000 Subject: iio: accel: kxcjk1013 3-axis accelerometer driver This patch adds IIO driver for KXCJK 1013 triaxis accelerometer sensor. The specifications for this driver is downloaded from: http://www.kionix.com/sites/default/files/KXCJK-1013%20Specifications%20Rev%202.pdf Signed-off-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- drivers/iio/accel/Kconfig | 12 + drivers/iio/accel/Makefile | 1 + drivers/iio/accel/kxcjk-1013.c | 760 +++++++++++++++++++++++++++++++++++ include/linux/iio/accel/kxcjk_1013.h | 22 + 4 files changed, 795 insertions(+) create mode 100644 drivers/iio/accel/kxcjk-1013.c create mode 100644 include/linux/iio/accel/kxcjk_1013.h (limited to 'include') diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 1e120fa1e156..12addf272a61 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -77,4 +77,16 @@ config MMA8452 To compile this driver as a module, choose M here: the module will be called mma8452. +config KXCJK1013 + tristate "Kionix 3-Axis Accelerometer Driver" + depends on I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say Y here if you want to build a driver for the Kionix KXCJK-1013 + triaxial acceleration sensor. + + To compile this driver as a module, choose M here: the module will + be called kxcjk-1013. + endmenu diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index dc0e379c2592..6578ca1a8e09 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -5,6 +5,7 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_BMA180) += bma180.o obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o +obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o obj-$(CONFIG_KXSD9) += kxsd9.o obj-$(CONFIG_MMA8452) += mma8452.o diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c new file mode 100644 index 000000000000..2326e430c999 --- /dev/null +++ b/drivers/iio/accel/kxcjk-1013.c @@ -0,0 +1,760 @@ +/* + * KXCJK-1013 3-axis accelerometer driver + * Copyright (c) 2014, Intel Corporation. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define KXCJK1013_DRV_NAME "kxcjk1013" +#define KXCJK1013_IRQ_NAME "kxcjk1013_event" + +#define KXCJK1013_REG_XOUT_L 0x06 +/* + * From low byte X axis register, all the other addresses of Y and Z can be + * obtained by just applying axis offset. The following axis defines are just + * provide clarity, but not used. + */ +#define KXCJK1013_REG_XOUT_H 0x07 +#define KXCJK1013_REG_YOUT_L 0x08 +#define KXCJK1013_REG_YOUT_H 0x09 +#define KXCJK1013_REG_ZOUT_L 0x0A +#define KXCJK1013_REG_ZOUT_H 0x0B + +#define KXCJK1013_REG_DCST_RESP 0x0C +#define KXCJK1013_REG_WHO_AM_I 0x0F +#define KXCJK1013_REG_INT_SRC1 0x16 +#define KXCJK1013_REG_INT_SRC2 0x17 +#define KXCJK1013_REG_STATUS_REG 0x18 +#define KXCJK1013_REG_INT_REL 0x1A +#define KXCJK1013_REG_CTRL1 0x1B +#define KXCJK1013_REG_CTRL2 0x1D +#define KXCJK1013_REG_INT_CTRL1 0x1E +#define KXCJK1013_REG_INT_CTRL2 0x1F +#define KXCJK1013_REG_DATA_CTRL 0x21 +#define KXCJK1013_REG_WAKE_TIMER 0x29 +#define KXCJK1013_REG_SELF_TEST 0x3A +#define KXCJK1013_REG_WAKE_THRES 0x6A + +#define KXCJK1013_REG_CTRL1_BIT_PC1 BIT(7) +#define KXCJK1013_REG_CTRL1_BIT_RES BIT(6) +#define KXCJK1013_REG_CTRL1_BIT_DRDY BIT(5) +#define KXCJK1013_REG_CTRL1_BIT_GSEL1 BIT(4) +#define KXCJK1013_REG_CTRL1_BIT_GSEL0 BIT(3) +#define KXCJK1013_REG_CTRL1_BIT_WUFE BIT(1) +#define KXCJK1013_REG_INT_REG1_BIT_IEA BIT(4) +#define KXCJK1013_REG_INT_REG1_BIT_IEN BIT(5) + +#define KXCJK1013_DATA_MASK_12_BIT 0x0FFF +#define KXCJK1013_MAX_STARTUP_TIME_US 100000 + +struct kxcjk1013_data { + struct i2c_client *client; + struct iio_trigger *trig; + bool trig_mode; + struct mutex mutex; + s16 buffer[8]; + int power_state; + u8 odr_bits; + bool active_high_intr; +}; + +enum kxcjk1013_axis { + AXIS_X, + AXIS_Y, + AXIS_Z, +}; + +enum kxcjk1013_mode { + STANDBY, + OPERATION, +}; + +static const struct { + int val; + int val2; + int odr_bits; +} samp_freq_table[] = { {0, 781000, 0x08}, {1, 563000, 0x09}, + {3, 125000, 0x0A}, {6, 25000, 0x0B}, {12, 5000, 0}, + {25, 0, 0x01}, {50, 0, 0x02}, {100, 0, 0x03}, + {200, 0, 0x04}, {400, 0, 0x05}, {800, 0, 0x06}, + {1600, 0, 0x07} }; + +/* Refer to section 4 of the specification */ +static const struct { + int odr_bits; + int usec; +} odr_start_up_times[] = { {0x08, 100000}, {0x09, 100000}, {0x0A, 100000}, + {0x0B, 100000}, { 0, 80000}, {0x01, 41000}, + {0x02, 21000}, {0x03, 11000}, {0x04, 6400}, + {0x05, 3900}, {0x06, 2700}, {0x07, 2100} }; + +static int kxcjk1013_set_mode(struct kxcjk1013_data *data, + enum kxcjk1013_mode mode) +{ + int ret; + + ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_CTRL1); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); + return ret; + } + + if (mode == STANDBY) + ret &= ~KXCJK1013_REG_CTRL1_BIT_PC1; + else + ret |= KXCJK1013_REG_CTRL1_BIT_PC1; + + ret = i2c_smbus_write_byte_data(data->client, + KXCJK1013_REG_CTRL1, ret); + if (ret < 0) { + dev_err(&data->client->dev, "Error writing reg_ctrl1\n"); + return ret; + } + + return 0; +} + +static int kxcjk1013_chip_ack_intr(struct kxcjk1013_data *data) +{ + int ret; + + ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_INT_REL); + if (ret < 0) { + dev_err(&data->client->dev, "Error writing reg_int_rel\n"); + return ret; + } + + return ret; +} + +static int kxcjk1013_chip_init(struct kxcjk1013_data *data) +{ + int ret; + + ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_WHO_AM_I); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading who_am_i\n"); + return ret; + } + + dev_dbg(&data->client->dev, "KXCJK1013 Chip Id %x\n", ret); + + ret = kxcjk1013_set_mode(data, STANDBY); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_CTRL1); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); + return ret; + } + + /* Setting range to 4G */ + ret |= KXCJK1013_REG_CTRL1_BIT_GSEL0; + ret &= ~KXCJK1013_REG_CTRL1_BIT_GSEL1; + + /* Set 12 bit mode */ + ret |= KXCJK1013_REG_CTRL1_BIT_RES; + + ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_CTRL1, + ret); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_ctrl\n"); + return ret; + } + + ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_DATA_CTRL); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_data_ctrl\n"); + return ret; + } + + data->odr_bits = ret; + + /* Set up INT polarity */ + ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_INT_CTRL1); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_int_ctrl1\n"); + return ret; + } + + if (data->active_high_intr) + ret |= KXCJK1013_REG_INT_REG1_BIT_IEA; + else + ret &= ~KXCJK1013_REG_INT_REG1_BIT_IEA; + + ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_INT_CTRL1, + ret); + if (ret < 0) { + dev_err(&data->client->dev, "Error writing reg_int_ctrl1\n"); + return ret; + } + + return 0; +} + +static int kxcjk1013_chip_setup_interrupt(struct kxcjk1013_data *data, + bool status) +{ + int ret; + + /* This is requirement by spec to change state to STANDBY */ + ret = kxcjk1013_set_mode(data, STANDBY); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_INT_CTRL1); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_int_ctrl1\n"); + return ret; + } + + if (status) + ret |= KXCJK1013_REG_INT_REG1_BIT_IEN; + else + ret &= ~KXCJK1013_REG_INT_REG1_BIT_IEN; + + ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_INT_CTRL1, + ret); + if (ret < 0) { + dev_err(&data->client->dev, "Error writing reg_int_ctrl1\n"); + return ret; + } + + ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_CTRL1); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); + return ret; + } + + if (status) + ret |= KXCJK1013_REG_CTRL1_BIT_DRDY; + else + ret &= ~KXCJK1013_REG_CTRL1_BIT_DRDY; + + ret = i2c_smbus_write_byte_data(data->client, + KXCJK1013_REG_CTRL1, ret); + if (ret < 0) { + dev_err(&data->client->dev, "Error writing reg_ctrl1\n"); + return ret; + } + + return ret; +} + +static int kxcjk1013_convert_freq_to_bit(int val, int val2) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(samp_freq_table); ++i) { + if (samp_freq_table[i].val == val && + samp_freq_table[i].val2 == val2) { + return samp_freq_table[i].odr_bits; + } + } + + return -EINVAL; +} + +static int kxcjk1013_set_odr(struct kxcjk1013_data *data, int val, int val2) +{ + int ret; + int odr_bits; + + odr_bits = kxcjk1013_convert_freq_to_bit(val, val2); + if (odr_bits < 0) + return odr_bits; + + /* To change ODR, the chip must be set to STANDBY as per spec */ + ret = kxcjk1013_set_mode(data, STANDBY); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_DATA_CTRL, + odr_bits); + if (ret < 0) { + dev_err(&data->client->dev, "Error writing data_ctrl\n"); + return ret; + } + + data->odr_bits = odr_bits; + + /* Check, if the ODR is changed after data enable */ + if (data->power_state) { + /* Set the state back to operation */ + ret = kxcjk1013_set_mode(data, OPERATION); + if (ret < 0) + return ret; + } + + return 0; +} + +static int kxcjk1013_get_odr(struct kxcjk1013_data *data, int *val, int *val2) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(samp_freq_table); ++i) { + if (samp_freq_table[i].odr_bits == data->odr_bits) { + *val = samp_freq_table[i].val; + *val2 = samp_freq_table[i].val2; + return IIO_VAL_INT_PLUS_MICRO; + } + } + + return -EINVAL; +} + +static int kxcjk1013_get_acc_reg(struct kxcjk1013_data *data, int axis) +{ + u8 reg = KXCJK1013_REG_XOUT_L + axis * 2; + int ret; + + ret = i2c_smbus_read_word_data(data->client, reg); + if (ret < 0) { + dev_err(&data->client->dev, + "failed to read accel_%c registers\n", 'x' + axis); + return ret; + } + + return ret; +} + +static int kxcjk1013_get_startup_times(struct kxcjk1013_data *data) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(odr_start_up_times); ++i) { + if (odr_start_up_times[i].odr_bits == data->odr_bits) + return odr_start_up_times[i].usec; + } + + return KXCJK1013_MAX_STARTUP_TIME_US; +} + +static int kxcjk1013_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct kxcjk1013_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&data->mutex); + if (iio_buffer_enabled(indio_dev)) + ret = -EBUSY; + else { + int sleep_val; + + ret = kxcjk1013_set_mode(data, OPERATION); + if (ret < 0) + return ret; + ++data->power_state; + sleep_val = kxcjk1013_get_startup_times(data); + if (sleep_val < 20000) + usleep_range(sleep_val, 20000); + else + msleep_interruptible(sleep_val/1000); + ret = kxcjk1013_get_acc_reg(data, chan->scan_index); + if (--data->power_state == 0) + kxcjk1013_set_mode(data, STANDBY); + } + mutex_unlock(&data->mutex); + + if (ret < 0) + return ret; + + *val = sign_extend32(ret >> 4, 11); + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = 19163; /* range +-4g (4/2047*9.806650) */ + return IIO_VAL_INT_PLUS_MICRO; + + case IIO_CHAN_INFO_SAMP_FREQ: + mutex_lock(&data->mutex); + ret = kxcjk1013_get_odr(data, val, val2); + mutex_unlock(&data->mutex); + return ret; + + default: + return -EINVAL; + } +} + +static int kxcjk1013_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct kxcjk1013_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + mutex_lock(&data->mutex); + ret = kxcjk1013_set_odr(data, val, val2); + mutex_unlock(&data->mutex); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int kxcjk1013_validate_trigger(struct iio_dev *indio_dev, + struct iio_trigger *trig) +{ + struct kxcjk1013_data *data = iio_priv(indio_dev); + + if (data->trig != trig) + return -EINVAL; + + return 0; +} + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL( + "0.781000 1.563000 3.125000 6.250000 12.500000 25 50 100 200 400 800 1600"); + +static struct attribute *kxcjk1013_attributes[] = { + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group kxcjk1013_attrs_group = { + .attrs = kxcjk1013_attributes, +}; + +#define KXCJK1013_CHANNEL(_axis) { \ + .type = IIO_ACCEL, \ + .modified = 1, \ + .channel2 = IIO_MOD_##_axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = AXIS_##_axis, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 4, \ + .endianness = IIO_LE, \ + }, \ +} + +static const struct iio_chan_spec kxcjk1013_channels[] = { + KXCJK1013_CHANNEL(X), + KXCJK1013_CHANNEL(Y), + KXCJK1013_CHANNEL(Z), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +static const struct iio_info kxcjk1013_info = { + .attrs = &kxcjk1013_attrs_group, + .read_raw = kxcjk1013_read_raw, + .write_raw = kxcjk1013_write_raw, + .validate_trigger = kxcjk1013_validate_trigger, + .driver_module = THIS_MODULE, +}; + +static irqreturn_t kxcjk1013_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct kxcjk1013_data *data = iio_priv(indio_dev); + int bit, ret, i = 0; + + mutex_lock(&data->mutex); + + for_each_set_bit(bit, indio_dev->buffer->scan_mask, + indio_dev->masklength) { + ret = kxcjk1013_get_acc_reg(data, bit); + if (ret < 0) { + kxcjk1013_chip_ack_intr(data); + mutex_unlock(&data->mutex); + goto err; + } + data->buffer[i++] = ret; + } + + kxcjk1013_chip_ack_intr(data); + + mutex_unlock(&data->mutex); + + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + pf->timestamp); +err: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int kxcjk1013_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct kxcjk1013_data *data = iio_priv(indio_dev); + + mutex_lock(&data->mutex); + if (state) { + kxcjk1013_chip_setup_interrupt(data, true); + kxcjk1013_set_mode(data, OPERATION); + ++data->power_state; + } else { + if (--data->power_state) + return 0; + kxcjk1013_chip_setup_interrupt(data, false); + kxcjk1013_set_mode(data, STANDBY); + } + mutex_unlock(&data->mutex); + + return 0; +} + +static const struct iio_trigger_ops kxcjk1013_trigger_ops = { + .set_trigger_state = kxcjk1013_data_rdy_trigger_set_state, + .owner = THIS_MODULE, +}; + +static int kxcjk1013_acpi_gpio_probe(struct i2c_client *client, + struct kxcjk1013_data *data) +{ + const struct acpi_device_id *id; + struct device *dev; + struct gpio_desc *gpio; + int ret; + + if (!client) + return -EINVAL; + + dev = &client->dev; + if (!ACPI_HANDLE(dev)) + return -ENODEV; + + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return -ENODEV; + + /* data ready gpio interrupt pin */ + gpio = devm_gpiod_get_index(dev, "kxcjk1013_int", 0); + if (IS_ERR(gpio)) { + dev_err(dev, "acpi gpio get index failed\n"); + return PTR_ERR(gpio); + } + + ret = gpiod_direction_input(gpio); + if (ret) + return ret; + + ret = gpiod_to_irq(gpio); + + dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret); + + return ret; +} + +static int kxcjk1013_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct kxcjk1013_data *data; + struct iio_dev *indio_dev; + struct iio_trigger *trig = NULL; + struct kxcjk_1013_platform_data *pdata; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + + pdata = dev_get_platdata(&client->dev); + if (pdata) + data->active_high_intr = pdata->active_high_intr; + else + data->active_high_intr = true; /* default polarity */ + + ret = kxcjk1013_chip_init(data); + if (ret < 0) + return ret; + + mutex_init(&data->mutex); + + indio_dev->dev.parent = &client->dev; + indio_dev->channels = kxcjk1013_channels; + indio_dev->num_channels = ARRAY_SIZE(kxcjk1013_channels); + indio_dev->name = KXCJK1013_DRV_NAME; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &kxcjk1013_info; + + if (client->irq < 0) + client->irq = kxcjk1013_acpi_gpio_probe(client, data); + + if (client->irq >= 0) { + trig = iio_trigger_alloc("%s-dev%d", indio_dev->name, + indio_dev->id); + if (!trig) + return -ENOMEM; + + data->trig_mode = true; + + ret = devm_request_irq(&client->dev, client->irq, + iio_trigger_generic_data_rdy_poll, + IRQF_TRIGGER_RISING, + KXCJK1013_IRQ_NAME, + trig); + if (ret) { + dev_err(&client->dev, "unable to request IRQ\n"); + goto err_trigger_free; + } + + trig->dev.parent = &client->dev; + trig->ops = &kxcjk1013_trigger_ops; + iio_trigger_set_drvdata(trig, indio_dev); + data->trig = trig; + indio_dev->trig = trig; + + ret = iio_trigger_register(trig); + if (ret) + goto err_trigger_free; + + ret = iio_triggered_buffer_setup(indio_dev, + &iio_pollfunc_store_time, + kxcjk1013_trigger_handler, + NULL); + if (ret < 0) { + dev_err(&client->dev, + "iio triggered buffer setup failed\n"); + goto err_trigger_unregister; + } + } + + ret = devm_iio_device_register(&client->dev, indio_dev); + if (ret < 0) { + dev_err(&client->dev, "unable to register iio device\n"); + goto err_buffer_cleanup; + } + + return 0; + +err_buffer_cleanup: + if (data->trig_mode) + iio_triggered_buffer_cleanup(indio_dev); +err_trigger_unregister: + if (data->trig_mode) + iio_trigger_unregister(trig); +err_trigger_free: + if (data->trig_mode) + iio_trigger_free(trig); + + return ret; +} + +static int kxcjk1013_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct kxcjk1013_data *data = iio_priv(indio_dev); + + if (data->trig_mode) { + iio_triggered_buffer_cleanup(indio_dev); + iio_trigger_unregister(data->trig); + iio_trigger_free(data->trig); + } + + mutex_lock(&data->mutex); + kxcjk1013_set_mode(data, STANDBY); + mutex_unlock(&data->mutex); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int kxcjk1013_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct kxcjk1013_data *data = iio_priv(indio_dev); + + mutex_lock(&data->mutex); + kxcjk1013_set_mode(data, STANDBY); + mutex_unlock(&data->mutex); + + return 0; +} + +static int kxcjk1013_resume(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct kxcjk1013_data *data = iio_priv(indio_dev); + + mutex_lock(&data->mutex); + + if (data->power_state) + kxcjk1013_set_mode(data, OPERATION); + + mutex_unlock(&data->mutex); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(kxcjk1013_pm_ops, kxcjk1013_suspend, kxcjk1013_resume); +#define KXCJK1013_PM_OPS (&kxcjk1013_pm_ops) +#else +#define KXCJK1013_PM_OPS NULL +#endif + +static const struct acpi_device_id kx_acpi_match[] = { + {"KXCJ1013", 0}, + { }, +}; +MODULE_DEVICE_TABLE(acpi, kx_acpi_match); + +static const struct i2c_device_id kxcjk1013_id[] = { + {"kxcjk1013", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, kxcjk1013_id); + +static struct i2c_driver kxcjk1013_driver = { + .driver = { + .name = KXCJK1013_DRV_NAME, + .acpi_match_table = ACPI_PTR(kx_acpi_match), + .pm = KXCJK1013_PM_OPS, + }, + .probe = kxcjk1013_probe, + .remove = kxcjk1013_remove, + .id_table = kxcjk1013_id, +}; +module_i2c_driver(kxcjk1013_driver); + +MODULE_AUTHOR("Srinivas Pandruvada "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("KXCJK1013 accelerometer driver"); diff --git a/include/linux/iio/accel/kxcjk_1013.h b/include/linux/iio/accel/kxcjk_1013.h new file mode 100644 index 000000000000..fd1d540ea62d --- /dev/null +++ b/include/linux/iio/accel/kxcjk_1013.h @@ -0,0 +1,22 @@ +/* + * KXCJK-1013 3-axis accelerometer Interface + * Copyright (c) 2014, Intel Corporation. + * + * 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. + */ + +#ifndef __IIO_KXCJK_1013_H__ +#define __IIO_KXCJK_1013_H__ + +struct kxcjk_1013_platform_data { + bool active_high_intr; +}; + +#endif -- cgit v1.2.3 From a288d648715bab6a1ab2b72cb1c1cc79cdc8cb43 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Fri, 23 May 2014 12:54:57 +0100 Subject: extcon: arizona: support inverted jack detect switch Add config option for inverted jack detect switch that opens when jack is inserted. Signed-off-by: Richard Fitzgerald [Acked by Lee Jones for MFD part] Acked-by: Lee Jones Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-arizona.c | 34 ++++++++++++++++++++++++++-------- include/linux/mfd/arizona/pdata.h | 3 +++ 2 files changed, 29 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index 6c84e3d12043..cf907430a698 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -39,6 +39,11 @@ #define ARIZONA_ACCDET_MODE_HPL 1 #define ARIZONA_ACCDET_MODE_HPR 2 +#define ARIZONA_MICD_CLAMP_MODE_JDL 0x4 +#define ARIZONA_MICD_CLAMP_MODE_JDH 0x5 +#define ARIZONA_MICD_CLAMP_MODE_JDL_GP5H 0x9 +#define ARIZONA_MICD_CLAMP_MODE_JDH_GP5H 0xb + #define ARIZONA_HPDET_MAX 10000 #define HPDET_DEBOUNCE 500 @@ -962,10 +967,16 @@ static irqreturn_t arizona_jackdet(int irq, void *data) if (arizona->pdata.jd_gpio5) { mask = ARIZONA_MICD_CLAMP_STS; - present = 0; + if (arizona->pdata.jd_invert) + present = ARIZONA_MICD_CLAMP_STS; + else + present = 0; } else { mask = ARIZONA_JD1_STS; - present = ARIZONA_JD1_STS; + if (arizona->pdata.jd_invert) + present = 0; + else + present = ARIZONA_JD1_STS; } ret = regmap_read(arizona->regmap, ARIZONA_AOD_IRQ_RAW_STATUS, &val); @@ -1096,6 +1107,7 @@ static int arizona_extcon_probe(struct platform_device *pdev) struct arizona_pdata *pdata = &arizona->pdata; struct arizona_extcon_info *info; unsigned int val; + unsigned int clamp_mode; int jack_irq_fall, jack_irq_rise; int ret, mode, i, j; @@ -1305,15 +1317,21 @@ static int arizona_extcon_probe(struct platform_device *pdev) regmap_write(arizona->regmap, ARIZONA_GPIO5_CTRL, val); - regmap_update_bits(arizona->regmap, - ARIZONA_MICD_CLAMP_CONTROL, - ARIZONA_MICD_CLAMP_MODE_MASK, 0x9); + if (arizona->pdata.jd_invert) + clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDH_GP5H; + else + clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDL_GP5H; } else { - regmap_update_bits(arizona->regmap, - ARIZONA_MICD_CLAMP_CONTROL, - ARIZONA_MICD_CLAMP_MODE_MASK, 0x4); + if (arizona->pdata.jd_invert) + clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDH; + else + clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDL; } + regmap_update_bits(arizona->regmap, + ARIZONA_MICD_CLAMP_CONTROL, + ARIZONA_MICD_CLAMP_MODE_MASK, clamp_mode); + regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_DEBOUNCE, ARIZONA_MICD_CLAMP_DB, diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index 12a5c135c746..4578c72c9b86 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -127,6 +127,9 @@ struct arizona_pdata { /** Internal pull on GPIO5 is disabled when used for jack detection */ bool jd_gpio5_nopull; + /** set to true if jackdet contact opens on insert */ + bool jd_invert; + /** Use the headphone detect circuit to identify the accessory */ bool hpdet_acc_id; -- cgit v1.2.3 From a0a7379e16b6e4c229d082f24c7e3ef9e812ed46 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 10 Jun 2014 10:53:01 +0200 Subject: netfilter: nf_tables: use u32 for chain use counter Since 4fefee5 ("netfilter: nf_tables: allow to delete several objects from a batch"), every new rule bumps the chain use counter. However, this is limited to 16 bits, which means that it will overrun after 2^16 rules. Use a u32 chain counter and check for overflows (just like we do for table objects). Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 6 +++--- net/netfilter/nf_tables_api.c | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 7ee6ce6564ae..713b0b88bd5a 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -503,9 +503,9 @@ enum nft_chain_flags { * @net: net namespace that this chain belongs to * @table: table that this chain belongs to * @handle: chain handle - * @flags: bitmask of enum nft_chain_flags * @use: number of jump references to this chain * @level: length of longest path to this chain + * @flags: bitmask of enum nft_chain_flags * @name: name of the chain */ struct nft_chain { @@ -514,9 +514,9 @@ struct nft_chain { struct net *net; struct nft_table *table; u64 handle; - u8 flags; - u16 use; + u32 use; u16 level; + u8 flags; char name[NFT_CHAIN_MAXNAMELEN]; }; diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index ba37c10e5139..5586426a6169 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -1730,6 +1730,9 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, if (!create || nlh->nlmsg_flags & NLM_F_REPLACE) return -EINVAL; handle = nf_tables_alloc_handle(table); + + if (chain->use == UINT_MAX) + return -EOVERFLOW; } if (nla[NFTA_RULE_POSITION]) { -- cgit v1.2.3 From 478850160636c4f0b2558451df0e42f8c5a10939 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Thu, 8 May 2014 01:37:48 +0200 Subject: irq_work: Implement remote queueing irq work currently only supports local callbacks. However its code is mostly ready to run remote callbacks and we have some potential user. The full nohz subsystem currently open codes its own remote irq work on top of the scheduler ipi when it wants a CPU to reevaluate its next tick. However this ad hoc solution bloats the scheduler IPI. Lets just extend the irq work subsystem to support remote queuing on top of the generic SMP IPI to handle this kind of user. This shouldn't add noticeable overhead. Suggested-by: Peter Zijlstra Acked-by: Peter Zijlstra Cc: Andrew Morton Cc: Eric Dumazet Cc: Ingo Molnar Cc: Kevin Hilman Cc: Paul E. McKenney Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Viresh Kumar Signed-off-by: Frederic Weisbecker --- include/linux/irq_work.h | 5 +++++ kernel/irq_work.c | 25 ++++++++++++++++++++++++- kernel/smp.c | 9 +++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/irq_work.h b/include/linux/irq_work.h index 19ae05d4b8ec..bf9422c3aefe 100644 --- a/include/linux/irq_work.h +++ b/include/linux/irq_work.h @@ -33,6 +33,11 @@ void init_irq_work(struct irq_work *work, void (*func)(struct irq_work *)) #define DEFINE_IRQ_WORK(name, _f) struct irq_work name = { .func = (_f), } bool irq_work_queue(struct irq_work *work); + +#ifdef CONFIG_SMP +bool irq_work_queue_on(struct irq_work *work, int cpu); +#endif + void irq_work_run(void); void irq_work_sync(struct irq_work *work); diff --git a/kernel/irq_work.c b/kernel/irq_work.c index 126f254614bf..4b0a890a304a 100644 --- a/kernel/irq_work.c +++ b/kernel/irq_work.c @@ -16,6 +16,7 @@ #include #include #include +#include #include @@ -55,12 +56,34 @@ void __weak arch_irq_work_raise(void) */ } +#ifdef CONFIG_SMP /* - * Enqueue the irq_work @entry unless it's already pending + * Enqueue the irq_work @work on @cpu unless it's already pending * somewhere. * * Can be re-enqueued while the callback is still in progress. */ +bool irq_work_queue_on(struct irq_work *work, int cpu) +{ + /* All work should have been flushed before going offline */ + WARN_ON_ONCE(cpu_is_offline(cpu)); + + /* Arch remote IPI send/receive backend aren't NMI safe */ + WARN_ON_ONCE(in_nmi()); + + /* Only queue if not already pending */ + if (!irq_work_claim(work)) + return false; + + if (llist_add(&work->llnode, &per_cpu(raised_list, cpu))) + arch_send_call_function_single_ipi(cpu); + + return true; +} +EXPORT_SYMBOL_GPL(irq_work_queue_on); +#endif + +/* Enqueue the irq work @work on the current CPU */ bool irq_work_queue(struct irq_work *work) { /* Only queue if not already pending */ diff --git a/kernel/smp.c b/kernel/smp.c index 306f8180b0d5..a1812d184aed 100644 --- a/kernel/smp.c +++ b/kernel/smp.c @@ -3,6 +3,7 @@ * * (C) Jens Axboe 2008 */ +#include #include #include #include @@ -210,6 +211,14 @@ void generic_smp_call_function_single_interrupt(void) csd->func(csd->info); csd_unlock(csd); } + + /* + * Handle irq works queued remotely by irq_work_queue_on(). + * Smp functions above are typically synchronous so they + * better run first since some other CPUs may be busy waiting + * for them. + */ + irq_work_run(); } /* -- cgit v1.2.3 From 3d36aebc2e78923095575df954f3f3b430ac0a30 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 4 Jun 2014 16:17:33 +0200 Subject: nohz: Support nohz full remote kick Remotely kicking a full nohz CPU in order to make it re-evaluate its next tick is currently implemented using the scheduler IPI. However this bloats a scheduler fast path with an off-topic feature. The scheduler tick was abused here for its cool "callable anywhere/anytime" properties. But now that the irq work subsystem can queue remote callbacks, it's a perfect fit to safely queue IPIs when interrupts are disabled without worrying about concurrent callers. So lets implement remote kick on top of irq work. This is going to be used when a new event requires the next tick to be recalculated: more than 1 task competing on the CPU, timer armed, ... Acked-by: Peter Zijlstra Cc: Andrew Morton Cc: Ingo Molnar Cc: Kevin Hilman Cc: Paul E. McKenney Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Viresh Kumar Signed-off-by: Frederic Weisbecker --- include/linux/tick.h | 9 ++++++++- kernel/time/tick-sched.c | 10 ++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/tick.h b/include/linux/tick.h index b84773cb9f4c..8a4987f2294a 100644 --- a/include/linux/tick.h +++ b/include/linux/tick.h @@ -181,7 +181,13 @@ static inline bool tick_nohz_full_cpu(int cpu) extern void tick_nohz_init(void); extern void __tick_nohz_full_check(void); -extern void tick_nohz_full_kick(void); +extern void tick_nohz_full_kick_cpu(int cpu); + +static inline void tick_nohz_full_kick(void) +{ + tick_nohz_full_kick_cpu(smp_processor_id()); +} + extern void tick_nohz_full_kick_all(void); extern void __tick_nohz_task_switch(struct task_struct *tsk); #else @@ -189,6 +195,7 @@ static inline void tick_nohz_init(void) { } static inline bool tick_nohz_full_enabled(void) { return false; } static inline bool tick_nohz_full_cpu(int cpu) { return false; } static inline void __tick_nohz_full_check(void) { } +static inline void tick_nohz_full_kick_cpu(int cpu) { } static inline void tick_nohz_full_kick(void) { } static inline void tick_nohz_full_kick_all(void) { } static inline void __tick_nohz_task_switch(struct task_struct *tsk) { } diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 6558b7ac112d..3d63944a3eca 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -224,13 +224,15 @@ static DEFINE_PER_CPU(struct irq_work, nohz_full_kick_work) = { }; /* - * Kick the current CPU if it's full dynticks in order to force it to + * Kick the CPU if it's full dynticks in order to force it to * re-evaluate its dependency on the tick and restart it if necessary. */ -void tick_nohz_full_kick(void) +void tick_nohz_full_kick_cpu(int cpu) { - if (tick_nohz_full_cpu(smp_processor_id())) - irq_work_queue(&__get_cpu_var(nohz_full_kick_work)); + if (!tick_nohz_full_cpu(cpu)) + return; + + irq_work_queue_on(&per_cpu(nohz_full_kick_work, cpu), cpu); } static void nohz_full_kick_ipi(void *info) -- cgit v1.2.3 From 0a00fd5e20fd5dc89e976e163588d7c54edaf745 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Tue, 3 Jun 2014 16:32:53 +0800 Subject: ACPICA: Restore error table definitions to reduce code differences between Linux and ACPICA upstream. The following commit has changed ACPICA table header definitions: Commit: 88f074f4871a8c212b212b725e4dcdcdb09613c1 Subject: ACPI, CPER: Update cper info While such definitions are currently maintained in ACPICA. As the modifications applying to the table definitions affect other OSPMs' drivers, it is very difficult for ACPICA to initiate a process to complete the merge. Thus this commit finally only leaves us divergences. Revert such naming modifications to reduce the source code differecnes between Linux and ACPICA upstream. No functional changes. Signed-off-by: Lv Zheng Cc: Bob Moore Cc: Chen, Gong Cc: Tony Luck Cc: Borislav Petkov Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_extlog.c | 14 +++++++------- drivers/acpi/apei/apei-internal.h | 10 +++++----- drivers/acpi/apei/ghes.c | 32 ++++++++++++++++---------------- drivers/firmware/efi/cper.c | 20 ++++++++++---------- include/acpi/actbl1.h | 14 +++++++------- include/acpi/ghes.h | 2 +- 6 files changed, 46 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/drivers/acpi/acpi_extlog.c b/drivers/acpi/acpi_extlog.c index 185334114d71..340d09518f8e 100644 --- a/drivers/acpi/acpi_extlog.c +++ b/drivers/acpi/acpi_extlog.c @@ -69,11 +69,11 @@ static u32 l1_percpu_entry; #define ELOG_ENTRY_ADDR(phyaddr) \ (phyaddr - elog_base + (u8 *)elog_addr) -static struct acpi_generic_status *extlog_elog_entry_check(int cpu, int bank) +static struct acpi_hest_generic_status *extlog_elog_entry_check(int cpu, int bank) { int idx; u64 data; - struct acpi_generic_status *estatus; + struct acpi_hest_generic_status *estatus; WARN_ON(cpu < 0); idx = ELOG_IDX(cpu, bank); @@ -82,7 +82,7 @@ static struct acpi_generic_status *extlog_elog_entry_check(int cpu, int bank) return NULL; data &= EXT_ELOG_ENTRY_MASK; - estatus = (struct acpi_generic_status *)ELOG_ENTRY_ADDR(data); + estatus = (struct acpi_hest_generic_status *)ELOG_ENTRY_ADDR(data); /* if no valid data in elog entry, just return */ if (estatus->block_status == 0) @@ -92,7 +92,7 @@ static struct acpi_generic_status *extlog_elog_entry_check(int cpu, int bank) } static void __print_extlog_rcd(const char *pfx, - struct acpi_generic_status *estatus, int cpu) + struct acpi_hest_generic_status *estatus, int cpu) { static atomic_t seqno; unsigned int curr_seqno; @@ -111,7 +111,7 @@ static void __print_extlog_rcd(const char *pfx, } static int print_extlog_rcd(const char *pfx, - struct acpi_generic_status *estatus, int cpu) + struct acpi_hest_generic_status *estatus, int cpu) { /* Not more than 2 messages every 5 seconds */ static DEFINE_RATELIMIT_STATE(ratelimit_corrected, 5*HZ, 2); @@ -137,7 +137,7 @@ static int extlog_print(struct notifier_block *nb, unsigned long val, struct mce *mce = (struct mce *)data; int bank = mce->bank; int cpu = mce->extcpu; - struct acpi_generic_status *estatus; + struct acpi_hest_generic_status *estatus; int rc; estatus = extlog_elog_entry_check(cpu, bank); @@ -148,7 +148,7 @@ static int extlog_print(struct notifier_block *nb, unsigned long val, /* clear record status to enable BIOS to update it again */ estatus->block_status = 0; - rc = print_extlog_rcd(NULL, (struct acpi_generic_status *)elog_buf, cpu); + rc = print_extlog_rcd(NULL, (struct acpi_hest_generic_status *)elog_buf, cpu); return NOTIFY_STOP; } diff --git a/drivers/acpi/apei/apei-internal.h b/drivers/acpi/apei/apei-internal.h index e5bcd919d4e6..16129c78b489 100644 --- a/drivers/acpi/apei/apei-internal.h +++ b/drivers/acpi/apei/apei-internal.h @@ -121,11 +121,11 @@ struct dentry; struct dentry *apei_get_debugfs_dir(void); #define apei_estatus_for_each_section(estatus, section) \ - for (section = (struct acpi_generic_data *)(estatus + 1); \ + for (section = (struct acpi_hest_generic_data *)(estatus + 1); \ (void *)section - (void *)estatus < estatus->data_length; \ section = (void *)(section+1) + section->error_data_length) -static inline u32 cper_estatus_len(struct acpi_generic_status *estatus) +static inline u32 cper_estatus_len(struct acpi_hest_generic_status *estatus) { if (estatus->raw_data_length) return estatus->raw_data_offset + \ @@ -135,9 +135,9 @@ static inline u32 cper_estatus_len(struct acpi_generic_status *estatus) } void cper_estatus_print(const char *pfx, - const struct acpi_generic_status *estatus); -int cper_estatus_check_header(const struct acpi_generic_status *estatus); -int cper_estatus_check(const struct acpi_generic_status *estatus); + const struct acpi_hest_generic_status *estatus); +int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus); +int cper_estatus_check(const struct acpi_hest_generic_status *estatus); int apei_osc_setup(void); #endif diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index dab7cb7349df..7a38d1465b61 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -74,13 +74,13 @@ #define GHES_ESTATUS_CACHE_LEN(estatus_len) \ (sizeof(struct ghes_estatus_cache) + (estatus_len)) #define GHES_ESTATUS_FROM_CACHE(estatus_cache) \ - ((struct acpi_generic_status *) \ + ((struct acpi_hest_generic_status *) \ ((struct ghes_estatus_cache *)(estatus_cache) + 1)) #define GHES_ESTATUS_NODE_LEN(estatus_len) \ (sizeof(struct ghes_estatus_node) + (estatus_len)) #define GHES_ESTATUS_FROM_NODE(estatus_node) \ - ((struct acpi_generic_status *) \ + ((struct acpi_hest_generic_status *) \ ((struct ghes_estatus_node *)(estatus_node) + 1)) bool ghes_disable; @@ -408,7 +408,7 @@ static void ghes_clear_estatus(struct ghes *ghes) ghes->flags &= ~GHES_TO_CLEAR; } -static void ghes_handle_memory_failure(struct acpi_generic_data *gdata, int sev) +static void ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata, int sev) { #ifdef CONFIG_ACPI_APEI_MEMORY_FAILURE unsigned long pfn; @@ -441,10 +441,10 @@ static void ghes_handle_memory_failure(struct acpi_generic_data *gdata, int sev) } static void ghes_do_proc(struct ghes *ghes, - const struct acpi_generic_status *estatus) + const struct acpi_hest_generic_status *estatus) { int sev, sec_sev; - struct acpi_generic_data *gdata; + struct acpi_hest_generic_data *gdata; sev = ghes_severity(estatus->error_severity); apei_estatus_for_each_section(estatus, gdata) { @@ -498,7 +498,7 @@ static void ghes_do_proc(struct ghes *ghes, static void __ghes_print_estatus(const char *pfx, const struct acpi_hest_generic *generic, - const struct acpi_generic_status *estatus) + const struct acpi_hest_generic_status *estatus) { static atomic_t seqno; unsigned int curr_seqno; @@ -520,7 +520,7 @@ static void __ghes_print_estatus(const char *pfx, static int ghes_print_estatus(const char *pfx, const struct acpi_hest_generic *generic, - const struct acpi_generic_status *estatus) + const struct acpi_hest_generic_status *estatus) { /* Not more than 2 messages every 5 seconds */ static DEFINE_RATELIMIT_STATE(ratelimit_corrected, 5*HZ, 2); @@ -542,13 +542,13 @@ static int ghes_print_estatus(const char *pfx, * GHES error status reporting throttle, to report more kinds of * errors, instead of just most frequently occurred errors. */ -static int ghes_estatus_cached(struct acpi_generic_status *estatus) +static int ghes_estatus_cached(struct acpi_hest_generic_status *estatus) { u32 len; int i, cached = 0; unsigned long long now; struct ghes_estatus_cache *cache; - struct acpi_generic_status *cache_estatus; + struct acpi_hest_generic_status *cache_estatus; len = cper_estatus_len(estatus); rcu_read_lock(); @@ -573,12 +573,12 @@ static int ghes_estatus_cached(struct acpi_generic_status *estatus) static struct ghes_estatus_cache *ghes_estatus_cache_alloc( struct acpi_hest_generic *generic, - struct acpi_generic_status *estatus) + struct acpi_hest_generic_status *estatus) { int alloced; u32 len, cache_len; struct ghes_estatus_cache *cache; - struct acpi_generic_status *cache_estatus; + struct acpi_hest_generic_status *cache_estatus; alloced = atomic_add_return(1, &ghes_estatus_cache_alloced); if (alloced > GHES_ESTATUS_CACHE_ALLOCED_MAX) { @@ -621,7 +621,7 @@ static void ghes_estatus_cache_rcu_free(struct rcu_head *head) static void ghes_estatus_cache_add( struct acpi_hest_generic *generic, - struct acpi_generic_status *estatus) + struct acpi_hest_generic_status *estatus) { int i, slot = -1, count; unsigned long long now, duration, period, max_period = 0; @@ -753,7 +753,7 @@ static void ghes_proc_in_irq(struct irq_work *irq_work) struct llist_node *llnode, *next; struct ghes_estatus_node *estatus_node; struct acpi_hest_generic *generic; - struct acpi_generic_status *estatus; + struct acpi_hest_generic_status *estatus; u32 len, node_len; llnode = llist_del_all(&ghes_estatus_llist); @@ -786,7 +786,7 @@ static void ghes_print_queued_estatus(void) struct llist_node *llnode; struct ghes_estatus_node *estatus_node; struct acpi_hest_generic *generic; - struct acpi_generic_status *estatus; + struct acpi_hest_generic_status *estatus; u32 len, node_len; llnode = llist_del_all(&ghes_estatus_llist); @@ -845,7 +845,7 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) #ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG u32 len, node_len; struct ghes_estatus_node *estatus_node; - struct acpi_generic_status *estatus; + struct acpi_hest_generic_status *estatus; #endif if (!(ghes->flags & GHES_TO_CLEAR)) continue; @@ -925,7 +925,7 @@ static int ghes_probe(struct platform_device *ghes_dev) rc = -EIO; if (generic->error_block_length < - sizeof(struct acpi_generic_status)) { + sizeof(struct acpi_hest_generic_status)) { pr_warning(FW_BUG GHES_PFX "Invalid error block length: %u for generic hardware error source: %d\n", generic->error_block_length, generic->header.source_id); diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c index 1491dd4f08f9..65f2f3fdde24 100644 --- a/drivers/firmware/efi/cper.c +++ b/drivers/firmware/efi/cper.c @@ -262,7 +262,7 @@ static const char *cper_pcie_port_type_strs[] = { }; static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie, - const struct acpi_generic_data *gdata) + const struct acpi_hest_generic_data *gdata) { if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE) printk("%s""port_type: %d, %s\n", pfx, pcie->port_type, @@ -298,7 +298,7 @@ static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie, } static void cper_estatus_print_section( - const char *pfx, const struct acpi_generic_data *gdata, int sec_no) + const char *pfx, const struct acpi_hest_generic_data *gdata, int sec_no) { uuid_le *sec_type = (uuid_le *)gdata->section_type; __u16 severity; @@ -344,9 +344,9 @@ err_section_too_small: } void cper_estatus_print(const char *pfx, - const struct acpi_generic_status *estatus) + const struct acpi_hest_generic_status *estatus) { - struct acpi_generic_data *gdata; + struct acpi_hest_generic_data *gdata; unsigned int data_len, gedata_len; int sec_no = 0; char newpfx[64]; @@ -359,7 +359,7 @@ void cper_estatus_print(const char *pfx, "and requires no further action"); printk("%s""event severity: %s\n", pfx, cper_severity_str(severity)); data_len = estatus->data_length; - gdata = (struct acpi_generic_data *)(estatus + 1); + gdata = (struct acpi_hest_generic_data *)(estatus + 1); snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP); while (data_len >= sizeof(*gdata)) { gedata_len = gdata->error_data_length; @@ -371,10 +371,10 @@ void cper_estatus_print(const char *pfx, } EXPORT_SYMBOL_GPL(cper_estatus_print); -int cper_estatus_check_header(const struct acpi_generic_status *estatus) +int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus) { if (estatus->data_length && - estatus->data_length < sizeof(struct acpi_generic_data)) + estatus->data_length < sizeof(struct acpi_hest_generic_data)) return -EINVAL; if (estatus->raw_data_length && estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length) @@ -384,9 +384,9 @@ int cper_estatus_check_header(const struct acpi_generic_status *estatus) } EXPORT_SYMBOL_GPL(cper_estatus_check_header); -int cper_estatus_check(const struct acpi_generic_status *estatus) +int cper_estatus_check(const struct acpi_hest_generic_status *estatus) { - struct acpi_generic_data *gdata; + struct acpi_hest_generic_data *gdata; unsigned int data_len, gedata_len; int rc; @@ -394,7 +394,7 @@ int cper_estatus_check(const struct acpi_generic_status *estatus) if (rc) return rc; data_len = estatus->data_length; - gdata = (struct acpi_generic_data *)(estatus + 1); + gdata = (struct acpi_hest_generic_data *)(estatus + 1); while (data_len >= sizeof(*gdata)) { gedata_len = gdata->error_data_length; if (gedata_len > data_len - sizeof(*gdata)) diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h index 4ad7da805180..9613e8e97960 100644 --- a/include/acpi/actbl1.h +++ b/include/acpi/actbl1.h @@ -604,7 +604,7 @@ struct acpi_hest_generic { /* Generic Error Status block */ -struct acpi_generic_status { +struct acpi_hest_generic_status { u32 block_status; u32 raw_data_offset; u32 raw_data_length; @@ -614,15 +614,15 @@ struct acpi_generic_status { /* Values for block_status flags above */ -#define ACPI_GEN_ERR_UC BIT(0) -#define ACPI_GEN_ERR_CE BIT(1) -#define ACPI_GEN_ERR_MULTI_UC BIT(2) -#define ACPI_GEN_ERR_MULTI_CE BIT(3) -#define ACPI_GEN_ERR_COUNT_SHIFT (0xFF<<4) /* 8 bits, error count */ +#define ACPI_HEST_UNCORRECTABLE (1) +#define ACPI_HEST_CORRECTABLE (1<<1) +#define ACPI_HEST_MULTIPLE_UNCORRECTABLE (1<<2) +#define ACPI_HEST_MULTIPLE_CORRECTABLE (1<<3) +#define ACPI_HEST_ERROR_ENTRY_COUNT (0xFF<<4) /* 8 bits, error count */ /* Generic Error Data entry */ -struct acpi_generic_data { +struct acpi_hest_generic_data { u8 section_type[16]; u32 error_severity; u16 revision; diff --git a/include/acpi/ghes.h b/include/acpi/ghes.h index dfd60d0bfd27..720446cb243e 100644 --- a/include/acpi/ghes.h +++ b/include/acpi/ghes.h @@ -14,7 +14,7 @@ struct ghes { struct acpi_hest_generic *generic; - struct acpi_generic_status *estatus; + struct acpi_hest_generic_status *estatus; u64 buffer_paddr; unsigned long flags; union { -- cgit v1.2.3 From bcde37225438687231bb9510c07e60dcb8db1988 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 10 Jun 2014 23:53:27 -0700 Subject: ARM: shmobile: r8a7790: add MSTP10 support on DTSI Signed-off-by: Kuninori Morimoto Acked-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- arch/arm/boot/dts/r8a7790.dtsi | 33 +++++++++++++++++++++++++++++++ include/dt-bindings/clock/r8a7790-clock.h | 26 ++++++++++++++++++++++++ 2 files changed, 59 insertions(+) (limited to 'include') diff --git a/arch/arm/boot/dts/r8a7790.dtsi b/arch/arm/boot/dts/r8a7790.dtsi index e990d3c4fe62..0f83bd778a3d 100644 --- a/arch/arm/boot/dts/r8a7790.dtsi +++ b/arch/arm/boot/dts/r8a7790.dtsi @@ -835,6 +835,39 @@ "rcan1", "rcan0", "qspi_mod", "iic3", "i2c3", "i2c2", "i2c1", "i2c0"; }; + mstp10_clks: mstp10_clks@e6150998 { + compatible = "renesas,r8a7790-mstp-clocks", "renesas,cpg-mstp-clocks"; + reg = <0 0xe6150998 0 4>, <0 0xe61509a8 0 4>; + clocks = <&p_clk>, + <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>, + <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>, + <&p_clk>, + <&mstp10_clks R8A7790_CLK_SCU_ALL>, <&mstp10_clks R8A7790_CLK_SCU_ALL>, + <&mstp10_clks R8A7790_CLK_SCU_ALL>, <&mstp10_clks R8A7790_CLK_SCU_ALL>, + <&mstp10_clks R8A7790_CLK_SCU_ALL>, <&mstp10_clks R8A7790_CLK_SCU_ALL>, + <&mstp10_clks R8A7790_CLK_SCU_ALL>, <&mstp10_clks R8A7790_CLK_SCU_ALL>, + <&mstp10_clks R8A7790_CLK_SCU_ALL>, <&mstp10_clks R8A7790_CLK_SCU_ALL>, + <&mstp10_clks R8A7790_CLK_SCU_ALL>, <&mstp10_clks R8A7790_CLK_SCU_ALL>; + + #clock-cells = <1>; + clock-indices = < + R8A7790_CLK_SSI_ALL + R8A7790_CLK_SSI9 R8A7790_CLK_SSI8 R8A7790_CLK_SSI7 R8A7790_CLK_SSI6 R8A7790_CLK_SSI5 + R8A7790_CLK_SSI4 R8A7790_CLK_SSI3 R8A7790_CLK_SSI2 R8A7790_CLK_SSI1 R8A7790_CLK_SSI0 + R8A7790_CLK_SCU_ALL + R8A7790_CLK_SCU_DVC1 R8A7790_CLK_SCU_DVC0 + R8A7790_CLK_SCU_SRC9 R8A7790_CLK_SCU_SRC8 R8A7790_CLK_SCU_SRC7 R8A7790_CLK_SCU_SRC6 R8A7790_CLK_SCU_SRC5 + R8A7790_CLK_SCU_SRC4 R8A7790_CLK_SCU_SRC3 R8A7790_CLK_SCU_SRC2 R8A7790_CLK_SCU_SRC1 R8A7790_CLK_SCU_SRC0 + >; + clock-output-names = + "ssi-all", + "ssi9", "ssi8", "ssi7", "ssi6", "ssi5", + "ssi4", "ssi3", "ssi2", "ssi1", "ssi0", + "scu-all", + "scu-dvc1", "scu-dvc0", + "scu-src9", "scu-src8", "scu-src7", "scu-src6", "scu-src5", + "scu-src4", "scu-src3", "scu-src2", "scu-src1", "scu-src0"; + }; }; qspi: spi@e6b10000 { diff --git a/include/dt-bindings/clock/r8a7790-clock.h b/include/dt-bindings/clock/r8a7790-clock.h index 1118f7a4bca6..a16df68ac97d 100644 --- a/include/dt-bindings/clock/r8a7790-clock.h +++ b/include/dt-bindings/clock/r8a7790-clock.h @@ -107,4 +107,30 @@ #define R8A7790_CLK_I2C1 30 #define R8A7790_CLK_I2C0 31 +/* MSTP10 */ +#define R8A7790_CLK_SSI_ALL 5 +#define R8A7790_CLK_SSI9 6 +#define R8A7790_CLK_SSI8 7 +#define R8A7790_CLK_SSI7 8 +#define R8A7790_CLK_SSI6 9 +#define R8A7790_CLK_SSI5 10 +#define R8A7790_CLK_SSI4 11 +#define R8A7790_CLK_SSI3 12 +#define R8A7790_CLK_SSI2 13 +#define R8A7790_CLK_SSI1 14 +#define R8A7790_CLK_SSI0 15 +#define R8A7790_CLK_SCU_ALL 17 +#define R8A7790_CLK_SCU_DVC1 18 +#define R8A7790_CLK_SCU_DVC0 19 +#define R8A7790_CLK_SCU_SRC9 22 +#define R8A7790_CLK_SCU_SRC8 23 +#define R8A7790_CLK_SCU_SRC7 24 +#define R8A7790_CLK_SCU_SRC6 25 +#define R8A7790_CLK_SCU_SRC5 26 +#define R8A7790_CLK_SCU_SRC4 27 +#define R8A7790_CLK_SCU_SRC3 28 +#define R8A7790_CLK_SCU_SRC2 29 +#define R8A7790_CLK_SCU_SRC1 30 +#define R8A7790_CLK_SCU_SRC0 31 + #endif /* __DT_BINDINGS_CLOCK_R8A7790_H__ */ -- cgit v1.2.3 From ee9141522dcf13f861ed3cef6490c6cbfaafefb9 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 11 Jun 2014 21:44:16 -0700 Subject: ARM: shmobile: r8a7791: add MSTP10 support on DTSI Signed-off-by: Kuninori Morimoto Acked-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- arch/arm/boot/dts/r8a7791.dtsi | 33 +++++++++++++++++++++++++++++++ include/dt-bindings/clock/r8a7791-clock.h | 26 ++++++++++++++++++++++++ 2 files changed, 59 insertions(+) (limited to 'include') diff --git a/arch/arm/boot/dts/r8a7791.dtsi b/arch/arm/boot/dts/r8a7791.dtsi index f3b6219cc09e..380d058e4210 100644 --- a/arch/arm/boot/dts/r8a7791.dtsi +++ b/arch/arm/boot/dts/r8a7791.dtsi @@ -865,6 +865,39 @@ "rcan1", "rcan0", "qspi_mod", "i2c5", "i2c6", "i2c4", "i2c3", "i2c2", "i2c1", "i2c0"; }; + mstp10_clks: mstp10_clks@e6150998 { + compatible = "renesas,r8a7791-mstp-clocks", "renesas,cpg-mstp-clocks"; + reg = <0 0xe6150998 0 4>, <0 0xe61509a8 0 4>; + clocks = <&p_clk>, + <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>, + <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>, + <&p_clk>, + <&mstp10_clks R8A7791_CLK_SCU_ALL>, <&mstp10_clks R8A7791_CLK_SCU_ALL>, + <&mstp10_clks R8A7791_CLK_SCU_ALL>, <&mstp10_clks R8A7791_CLK_SCU_ALL>, + <&mstp10_clks R8A7791_CLK_SCU_ALL>, <&mstp10_clks R8A7791_CLK_SCU_ALL>, + <&mstp10_clks R8A7791_CLK_SCU_ALL>, <&mstp10_clks R8A7791_CLK_SCU_ALL>, + <&mstp10_clks R8A7791_CLK_SCU_ALL>, <&mstp10_clks R8A7791_CLK_SCU_ALL>, + <&mstp10_clks R8A7791_CLK_SCU_ALL>, <&mstp10_clks R8A7791_CLK_SCU_ALL>; + + #clock-cells = <1>; + clock-indices = < + R8A7791_CLK_SSI_ALL + R8A7791_CLK_SSI9 R8A7791_CLK_SSI8 R8A7791_CLK_SSI7 R8A7791_CLK_SSI6 R8A7791_CLK_SSI5 + R8A7791_CLK_SSI4 R8A7791_CLK_SSI3 R8A7791_CLK_SSI2 R8A7791_CLK_SSI1 R8A7791_CLK_SSI0 + R8A7791_CLK_SCU_ALL + R8A7791_CLK_SCU_DVC1 R8A7791_CLK_SCU_DVC0 + R8A7791_CLK_SCU_SRC9 R8A7791_CLK_SCU_SRC8 R8A7791_CLK_SCU_SRC7 R8A7791_CLK_SCU_SRC6 R8A7791_CLK_SCU_SRC5 + R8A7791_CLK_SCU_SRC4 R8A7791_CLK_SCU_SRC3 R8A7791_CLK_SCU_SRC2 R8A7791_CLK_SCU_SRC1 R8A7791_CLK_SCU_SRC0 + >; + clock-output-names = + "ssi-all", + "ssi9", "ssi8", "ssi7", "ssi6", "ssi5", + "ssi4", "ssi3", "ssi2", "ssi1", "ssi0", + "scu-all", + "scu-dvc1", "scu-dvc0", + "scu-src9", "scu-src8", "scu-src7", "scu-src6", "scu-src5", + "scu-src4", "scu-src3", "scu-src2", "scu-src1", "scu-src0"; + }; mstp11_clks: mstp11_clks@e615099c { compatible = "renesas,r8a7791-mstp-clocks", "renesas,cpg-mstp-clocks"; reg = <0 0xe615099c 0 4>, <0 0xe61509ac 0 4>; diff --git a/include/dt-bindings/clock/r8a7791-clock.h b/include/dt-bindings/clock/r8a7791-clock.h index b050d18437ce..0410bea2ad1d 100644 --- a/include/dt-bindings/clock/r8a7791-clock.h +++ b/include/dt-bindings/clock/r8a7791-clock.h @@ -107,6 +107,32 @@ #define R8A7791_CLK_I2C1 30 #define R8A7791_CLK_I2C0 31 +/* MSTP10 */ +#define R8A7791_CLK_SSI_ALL 5 +#define R8A7791_CLK_SSI9 6 +#define R8A7791_CLK_SSI8 7 +#define R8A7791_CLK_SSI7 8 +#define R8A7791_CLK_SSI6 9 +#define R8A7791_CLK_SSI5 10 +#define R8A7791_CLK_SSI4 11 +#define R8A7791_CLK_SSI3 12 +#define R8A7791_CLK_SSI2 13 +#define R8A7791_CLK_SSI1 14 +#define R8A7791_CLK_SSI0 15 +#define R8A7791_CLK_SCU_ALL 17 +#define R8A7791_CLK_SCU_DVC1 18 +#define R8A7791_CLK_SCU_DVC0 19 +#define R8A7791_CLK_SCU_SRC9 22 +#define R8A7791_CLK_SCU_SRC8 23 +#define R8A7791_CLK_SCU_SRC7 24 +#define R8A7791_CLK_SCU_SRC6 25 +#define R8A7791_CLK_SCU_SRC5 26 +#define R8A7791_CLK_SCU_SRC4 27 +#define R8A7791_CLK_SCU_SRC3 28 +#define R8A7791_CLK_SCU_SRC2 29 +#define R8A7791_CLK_SCU_SRC1 30 +#define R8A7791_CLK_SCU_SRC0 31 + /* MSTP11 */ #define R8A7791_CLK_SCIFA3 6 #define R8A7791_CLK_SCIFA4 7 -- cgit v1.2.3 From ecafea8cd261833d7bb857aad76cf2e721821e88 Mon Sep 17 00:00:00 2001 From: Phil Edworthy Date: Fri, 13 Jun 2014 10:37:15 +0100 Subject: ARM: shmobile: r8a7790: Add PCIEC clock device tree node This patch adds the device tree clock node for the PCIe Controller Signed-off-by: Phil Edworthy Acked-by: Laurent Pinchart Signed-off-by: Simon Horman --- arch/arm/boot/dts/r8a7790.dtsi | 6 +++--- include/dt-bindings/clock/r8a7790-clock.h | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/arch/arm/boot/dts/r8a7790.dtsi b/arch/arm/boot/dts/r8a7790.dtsi index 29907c9d8e5b..9df57122e9c0 100644 --- a/arch/arm/boot/dts/r8a7790.dtsi +++ b/arch/arm/boot/dts/r8a7790.dtsi @@ -765,17 +765,17 @@ reg = <0 0xe615013c 0 4>, <0 0xe6150048 0 4>; clocks = <&hp_clk>, <&cp_clk>, <&mmc1_clk>, <&sd3_clk>, <&sd2_clk>, <&cpg_clocks R8A7790_CLK_SD1>, <&cpg_clocks R8A7790_CLK_SD0>, <&mmc0_clk>, - <&hp_clk>, <&hp_clk>, <&mp_clk>, <&rclk_clk>; + <&hp_clk>, <&mp_clk>, <&hp_clk>, <&mp_clk>, <&rclk_clk>; #clock-cells = <1>; renesas,clock-indices = < R8A7790_CLK_IIC2 R8A7790_CLK_TPU0 R8A7790_CLK_MMCIF1 R8A7790_CLK_SDHI3 R8A7790_CLK_SDHI2 R8A7790_CLK_SDHI1 R8A7790_CLK_SDHI0 R8A7790_CLK_MMCIF0 - R8A7790_CLK_IIC0 R8A7790_CLK_IIC1 R8A7790_CLK_SSUSB R8A7790_CLK_CMT1 + R8A7790_CLK_IIC0 R8A7790_CLK_PCIEC R8A7790_CLK_IIC1 R8A7790_CLK_SSUSB R8A7790_CLK_CMT1 >; clock-output-names = "iic2", "tpu0", "mmcif1", "sdhi3", "sdhi2", "sdhi1", "sdhi0", "mmcif0", - "iic0", "iic1", "ssusb", "cmt1"; + "iic0", "pciec", "iic1", "ssusb", "cmt1"; }; mstp5_clks: mstp5_clks@e6150144 { compatible = "renesas,r8a7790-mstp-clocks", "renesas,cpg-mstp-clocks"; diff --git a/include/dt-bindings/clock/r8a7790-clock.h b/include/dt-bindings/clock/r8a7790-clock.h index a16df68ac97d..f929a79e6998 100644 --- a/include/dt-bindings/clock/r8a7790-clock.h +++ b/include/dt-bindings/clock/r8a7790-clock.h @@ -59,6 +59,7 @@ #define R8A7790_CLK_SDHI0 14 #define R8A7790_CLK_MMCIF0 15 #define R8A7790_CLK_IIC0 18 +#define R8A7790_CLK_PCIEC 19 #define R8A7790_CLK_IIC1 23 #define R8A7790_CLK_SSUSB 28 #define R8A7790_CLK_CMT1 29 -- cgit v1.2.3 From 4bfb37675b5343798f5260adad92a67444a9fd47 Mon Sep 17 00:00:00 2001 From: Phil Edworthy Date: Fri, 13 Jun 2014 10:37:18 +0100 Subject: ARM: shmobile: r8a7791: Add PCIEC clock device tree node This patch adds the device tree clock node for the PCIe Controller Signed-off-by: Phil Edworthy Acked-by: Laurent Pinchart Signed-off-by: Simon Horman --- arch/arm/boot/dts/r8a7791.dtsi | 7 ++++--- include/dt-bindings/clock/r8a7791-clock.h | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/arch/arm/boot/dts/r8a7791.dtsi b/arch/arm/boot/dts/r8a7791.dtsi index 380d058e4210..a15bf7af63e4 100644 --- a/arch/arm/boot/dts/r8a7791.dtsi +++ b/arch/arm/boot/dts/r8a7791.dtsi @@ -795,15 +795,16 @@ compatible = "renesas,r8a7791-mstp-clocks", "renesas,cpg-mstp-clocks"; reg = <0 0xe615013c 0 4>, <0 0xe6150048 0 4>; clocks = <&cp_clk>, <&sd2_clk>, <&sd1_clk>, <&cpg_clocks R8A7791_CLK_SD0>, - <&mmc0_clk>, <&hp_clk>, <&hp_clk>, <&mp_clk>, <&rclk_clk>; + <&mmc0_clk>, <&hp_clk>, <&mp_clk>, <&hp_clk>, <&mp_clk>, <&rclk_clk>; #clock-cells = <1>; renesas,clock-indices = < R8A7791_CLK_TPU0 R8A7791_CLK_SDHI2 R8A7791_CLK_SDHI1 R8A7791_CLK_SDHI0 - R8A7791_CLK_MMCIF0 R8A7791_CLK_IIC0 R8A7791_CLK_IIC1 R8A7791_CLK_SSUSB R8A7791_CLK_CMT1 + R8A7791_CLK_MMCIF0 R8A7791_CLK_IIC0 R8A7791_CLK_PCIEC R8A7791_CLK_IIC1 + R8A7791_CLK_SSUSB R8A7791_CLK_CMT1 >; clock-output-names = "tpu0", "sdhi2", "sdhi1", "sdhi0", - "mmcif0", "i2c7", "i2c8", "ssusb", "cmt1"; + "mmcif0", "i2c7", "pciec", "i2c8", "ssusb", "cmt1"; }; mstp5_clks: mstp5_clks@e6150144 { compatible = "renesas,r8a7791-mstp-clocks", "renesas,cpg-mstp-clocks"; diff --git a/include/dt-bindings/clock/r8a7791-clock.h b/include/dt-bindings/clock/r8a7791-clock.h index 0410bea2ad1d..f0d4d1049162 100644 --- a/include/dt-bindings/clock/r8a7791-clock.h +++ b/include/dt-bindings/clock/r8a7791-clock.h @@ -53,6 +53,7 @@ #define R8A7791_CLK_SDHI0 14 #define R8A7791_CLK_MMCIF0 15 #define R8A7791_CLK_IIC0 18 +#define R8A7791_CLK_PCIEC 19 #define R8A7791_CLK_IIC1 23 #define R8A7791_CLK_SSUSB 28 #define R8A7791_CLK_CMT1 29 -- cgit v1.2.3 From 4ca2ad55553ef528c055761a9fa4d2c140f7318b Mon Sep 17 00:00:00 2001 From: Fugang Duan Date: Mon, 19 May 2014 15:46:41 +0800 Subject: ARM: imx6sl: add missing enet clock for imx6sl There's a enet clock gate missing in clock tree, thus add it. Signed-off-by: Fugang Duan Signed-off-by: Shawn Guo --- arch/arm/mach-imx/clk-imx6sl.c | 1 + include/dt-bindings/clock/imx6sl-clock.h | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/arm/mach-imx/clk-imx6sl.c b/arch/arm/mach-imx/clk-imx6sl.c index 21cf06cebade..5408ca70c8d6 100644 --- a/arch/arm/mach-imx/clk-imx6sl.c +++ b/arch/arm/mach-imx/clk-imx6sl.c @@ -312,6 +312,7 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node) clks[IMX6SL_CLK_ECSPI2] = imx_clk_gate2("ecspi2", "ecspi_root", base + 0x6c, 2); clks[IMX6SL_CLK_ECSPI3] = imx_clk_gate2("ecspi3", "ecspi_root", base + 0x6c, 4); clks[IMX6SL_CLK_ECSPI4] = imx_clk_gate2("ecspi4", "ecspi_root", base + 0x6c, 6); + clks[IMX6SL_CLK_ENET] = imx_clk_gate2("enet", "ipg", base + 0x6c, 10); clks[IMX6SL_CLK_EPIT1] = imx_clk_gate2("epit1", "perclk", base + 0x6c, 12); clks[IMX6SL_CLK_EPIT2] = imx_clk_gate2("epit2", "perclk", base + 0x6c, 14); clks[IMX6SL_CLK_EXTERN_AUDIO] = imx_clk_gate2("extern_audio", "extern_audio_podf", base + 0x6c, 16); diff --git a/include/dt-bindings/clock/imx6sl-clock.h b/include/dt-bindings/clock/imx6sl-clock.h index 7cf5c9969336..b91dd462ba85 100644 --- a/include/dt-bindings/clock/imx6sl-clock.h +++ b/include/dt-bindings/clock/imx6sl-clock.h @@ -145,6 +145,7 @@ #define IMX6SL_CLK_USDHC4 132 #define IMX6SL_CLK_PLL4_AUDIO_DIV 133 #define IMX6SL_CLK_SPBA 134 -#define IMX6SL_CLK_END 135 +#define IMX6SL_CLK_ENET 135 +#define IMX6SL_CLK_END 136 #endif /* __DT_BINDINGS_CLOCK_IMX6SL_H */ -- cgit v1.2.3 From d0540f91cf74fab90e1143d8d40da8a5b5fabc8a Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Wed, 21 May 2014 08:52:47 +0200 Subject: mfd: max77693: Remove unnecessary wrapper functions This patch removes wrapper functions used to access regmap, and make driver using regmap_*() functions instead. Signed-off-by: Robert Baldyga Reviewed-by: Krzysztof Kozlowski Acked-by: Mark Brown Acked-by: Chanwoo Choi Signed-off-by: Lee Jones --- drivers/extcon/extcon-max77693.c | 32 ++++++++++----------- drivers/mfd/max77693-irq.c | 50 +++++++++++++++++++------------- drivers/mfd/max77693.c | 56 ++---------------------------------- drivers/regulator/max77693.c | 12 ++++---- include/linux/mfd/max77693-private.h | 8 ------ 5 files changed, 54 insertions(+), 104 deletions(-) (limited to 'include') diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c index 2c7c3e191591..ba84a6e77e03 100644 --- a/drivers/extcon/extcon-max77693.c +++ b/drivers/extcon/extcon-max77693.c @@ -255,10 +255,10 @@ static int max77693_muic_set_debounce_time(struct max77693_muic_info *info, case ADC_DEBOUNCE_TIME_10MS: case ADC_DEBOUNCE_TIME_25MS: case ADC_DEBOUNCE_TIME_38_62MS: - ret = max77693_update_reg(info->max77693->regmap_muic, + ret = regmap_update_bits(info->max77693->regmap_muic, MAX77693_MUIC_REG_CTRL3, - time << CONTROL3_ADCDBSET_SHIFT, - CONTROL3_ADCDBSET_MASK); + CONTROL3_ADCDBSET_MASK, + time << CONTROL3_ADCDBSET_SHIFT); if (ret) { dev_err(info->dev, "failed to set ADC debounce time\n"); return ret; @@ -286,15 +286,15 @@ static int max77693_muic_set_path(struct max77693_muic_info *info, u8 val, bool attached) { int ret = 0; - u8 ctrl1, ctrl2 = 0; + unsigned int ctrl1, ctrl2 = 0; if (attached) ctrl1 = val; else ctrl1 = CONTROL1_SW_OPEN; - ret = max77693_update_reg(info->max77693->regmap_muic, - MAX77693_MUIC_REG_CTRL1, ctrl1, COMP_SW_MASK); + ret = regmap_update_bits(info->max77693->regmap_muic, + MAX77693_MUIC_REG_CTRL1, COMP_SW_MASK, ctrl1); if (ret < 0) { dev_err(info->dev, "failed to update MUIC register\n"); return ret; @@ -305,9 +305,9 @@ static int max77693_muic_set_path(struct max77693_muic_info *info, else ctrl2 |= CONTROL2_LOWPWR_MASK; /* LowPwr=1, CPEn=0 */ - ret = max77693_update_reg(info->max77693->regmap_muic, - MAX77693_MUIC_REG_CTRL2, ctrl2, - CONTROL2_LOWPWR_MASK | CONTROL2_CPEN_MASK); + ret = regmap_update_bits(info->max77693->regmap_muic, + MAX77693_MUIC_REG_CTRL2, + CONTROL2_LOWPWR_MASK | CONTROL2_CPEN_MASK, ctrl2); if (ret < 0) { dev_err(info->dev, "failed to update MUIC register\n"); return ret; @@ -969,8 +969,8 @@ static void max77693_muic_irq_work(struct work_struct *work) if (info->irq == muic_irqs[i].virq) irq_type = muic_irqs[i].irq; - ret = max77693_bulk_read(info->max77693->regmap_muic, - MAX77693_MUIC_REG_STATUS1, 2, info->status); + ret = regmap_bulk_read(info->max77693->regmap_muic, + MAX77693_MUIC_REG_STATUS1, info->status, 2); if (ret) { dev_err(info->dev, "failed to read MUIC register\n"); mutex_unlock(&info->mutex); @@ -1042,8 +1042,8 @@ static int max77693_muic_detect_accessory(struct max77693_muic_info *info) mutex_lock(&info->mutex); /* Read STATUSx register to detect accessory */ - ret = max77693_bulk_read(info->max77693->regmap_muic, - MAX77693_MUIC_REG_STATUS1, 2, info->status); + ret = regmap_bulk_read(info->max77693->regmap_muic, + MAX77693_MUIC_REG_STATUS1, info->status, 2); if (ret) { dev_err(info->dev, "failed to read MUIC register\n"); mutex_unlock(&info->mutex); @@ -1095,7 +1095,7 @@ static int max77693_muic_probe(struct platform_device *pdev) int delay_jiffies; int ret; int i; - u8 id; + unsigned int id; info = devm_kzalloc(&pdev->dev, sizeof(struct max77693_muic_info), GFP_KERNEL); @@ -1204,7 +1204,7 @@ static int max77693_muic_probe(struct platform_device *pdev) enum max77693_irq_source irq_src = MAX77693_IRQ_GROUP_NR; - max77693_write_reg(info->max77693->regmap_muic, + regmap_write(info->max77693->regmap_muic, init_data[i].addr, init_data[i].data); @@ -1262,7 +1262,7 @@ static int max77693_muic_probe(struct platform_device *pdev) max77693_muic_set_path(info, info->path_uart, true); /* Check revision number of MUIC device*/ - ret = max77693_read_reg(info->max77693->regmap_muic, + ret = regmap_read(info->max77693->regmap_muic, MAX77693_MUIC_REG_ID, &id); if (ret < 0) { dev_err(&pdev->dev, "failed to read revision number\n"); diff --git a/drivers/mfd/max77693-irq.c b/drivers/mfd/max77693-irq.c index 66b58fe77094..7d8f99f94f27 100644 --- a/drivers/mfd/max77693-irq.c +++ b/drivers/mfd/max77693-irq.c @@ -30,8 +30,9 @@ #include #include #include +#include -static const u8 max77693_mask_reg[] = { +static const unsigned int max77693_mask_reg[] = { [LED_INT] = MAX77693_LED_REG_FLASH_INT_MASK, [TOPSYS_INT] = MAX77693_PMIC_REG_TOPSYS_INT_MASK, [CHG_INT] = MAX77693_CHG_REG_CHG_INT_MASK, @@ -118,7 +119,7 @@ static void max77693_irq_sync_unlock(struct irq_data *data) continue; max77693->irq_masks_cache[i] = max77693->irq_masks_cur[i]; - max77693_write_reg(map, max77693_mask_reg[i], + regmap_write(map, max77693_mask_reg[i], max77693->irq_masks_cur[i]); } @@ -178,11 +179,11 @@ static irqreturn_t max77693_irq_thread(int irq, void *data) { struct max77693_dev *max77693 = data; u8 irq_reg[MAX77693_IRQ_GROUP_NR] = {}; - u8 irq_src; + unsigned int irq_src; int ret; int i, cur_irq; - ret = max77693_read_reg(max77693->regmap, MAX77693_PMIC_REG_INTSRC, + ret = regmap_read(max77693->regmap, MAX77693_PMIC_REG_INTSRC, &irq_src); if (ret < 0) { dev_err(max77693->dev, "Failed to read interrupt source: %d\n", @@ -190,25 +191,34 @@ static irqreturn_t max77693_irq_thread(int irq, void *data) return IRQ_NONE; } - if (irq_src & MAX77693_IRQSRC_CHG) + if (irq_src & MAX77693_IRQSRC_CHG) { /* CHG_INT */ - ret = max77693_read_reg(max77693->regmap, MAX77693_CHG_REG_CHG_INT, - &irq_reg[CHG_INT]); + unsigned int data; + ret = regmap_read(max77693->regmap, + MAX77693_CHG_REG_CHG_INT, &data); + irq_reg[CHG_INT] = data; + } - if (irq_src & MAX77693_IRQSRC_TOP) + if (irq_src & MAX77693_IRQSRC_TOP) { /* TOPSYS_INT */ - ret = max77693_read_reg(max77693->regmap, - MAX77693_PMIC_REG_TOPSYS_INT, &irq_reg[TOPSYS_INT]); + unsigned int data; + ret = regmap_read(max77693->regmap, + MAX77693_PMIC_REG_TOPSYS_INT, &data); + irq_reg[TOPSYS_INT] = data; + } - if (irq_src & MAX77693_IRQSRC_FLASH) + if (irq_src & MAX77693_IRQSRC_FLASH) { /* LED_INT */ - ret = max77693_read_reg(max77693->regmap, - MAX77693_LED_REG_FLASH_INT, &irq_reg[LED_INT]); + unsigned int data; + ret = regmap_read(max77693->regmap, + MAX77693_LED_REG_FLASH_INT, &data); + irq_reg[LED_INT] = data; + } if (irq_src & MAX77693_IRQSRC_MUIC) /* MUIC INT1 ~ INT3 */ - max77693_bulk_read(max77693->regmap_muic, MAX77693_MUIC_REG_INT1, - MAX77693_NUM_IRQ_MUIC_REGS, &irq_reg[MUIC_INT1]); + regmap_bulk_read(max77693->regmap_muic, MAX77693_MUIC_REG_INT1, + &irq_reg[MUIC_INT1], MAX77693_NUM_IRQ_MUIC_REGS); /* Apply masking */ for (i = 0; i < MAX77693_IRQ_GROUP_NR; i++) { @@ -263,7 +273,7 @@ int max77693_irq_init(struct max77693_dev *max77693) struct irq_domain *domain; int i; int ret = 0; - u8 intsrc_mask; + unsigned int intsrc_mask; mutex_init(&max77693->irqlock); @@ -286,9 +296,9 @@ int max77693_irq_init(struct max77693_dev *max77693) if (max77693_mask_reg[i] == MAX77693_REG_INVALID) continue; if (i >= MUIC_INT1 && i <= MUIC_INT3) - max77693_write_reg(map, max77693_mask_reg[i], 0x00); + regmap_write(map, max77693_mask_reg[i], 0x00); else - max77693_write_reg(map, max77693_mask_reg[i], 0xff); + regmap_write(map, max77693_mask_reg[i], 0xff); } domain = irq_domain_add_linear(NULL, MAX77693_IRQ_NR, @@ -301,7 +311,7 @@ int max77693_irq_init(struct max77693_dev *max77693) max77693->irq_domain = domain; /* Unmask max77693 interrupt */ - ret = max77693_read_reg(max77693->regmap, + ret = regmap_read(max77693->regmap, MAX77693_PMIC_REG_INTSRC_MASK, &intsrc_mask); if (ret < 0) { dev_err(max77693->dev, "fail to read PMIC register\n"); @@ -311,7 +321,7 @@ int max77693_irq_init(struct max77693_dev *max77693) intsrc_mask &= ~(MAX77693_IRQSRC_CHG); intsrc_mask &= ~(MAX77693_IRQSRC_FLASH); intsrc_mask &= ~(MAX77693_IRQSRC_MUIC); - ret = max77693_write_reg(max77693->regmap, + ret = regmap_write(max77693->regmap, MAX77693_PMIC_REG_INTSRC_MASK, intsrc_mask); if (ret < 0) { dev_err(max77693->dev, "fail to write PMIC register\n"); diff --git a/drivers/mfd/max77693.c b/drivers/mfd/max77693.c index 7e05428c756d..a0308336adaf 100644 --- a/drivers/mfd/max77693.c +++ b/drivers/mfd/max77693.c @@ -49,58 +49,6 @@ static const struct mfd_cell max77693_devs[] = { { .name = "max77693-haptic", }, }; -int max77693_read_reg(struct regmap *map, u8 reg, u8 *dest) -{ - unsigned int val; - int ret; - - ret = regmap_read(map, reg, &val); - *dest = val; - - return ret; -} -EXPORT_SYMBOL_GPL(max77693_read_reg); - -int max77693_bulk_read(struct regmap *map, u8 reg, int count, u8 *buf) -{ - int ret; - - ret = regmap_bulk_read(map, reg, buf, count); - - return ret; -} -EXPORT_SYMBOL_GPL(max77693_bulk_read); - -int max77693_write_reg(struct regmap *map, u8 reg, u8 value) -{ - int ret; - - ret = regmap_write(map, reg, value); - - return ret; -} -EXPORT_SYMBOL_GPL(max77693_write_reg); - -int max77693_bulk_write(struct regmap *map, u8 reg, int count, u8 *buf) -{ - int ret; - - ret = regmap_bulk_write(map, reg, buf, count); - - return ret; -} -EXPORT_SYMBOL_GPL(max77693_bulk_write); - -int max77693_update_reg(struct regmap *map, u8 reg, u8 val, u8 mask) -{ - int ret; - - ret = regmap_update_bits(map, reg, mask, val); - - return ret; -} -EXPORT_SYMBOL_GPL(max77693_update_reg); - static const struct regmap_config max77693_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -117,7 +65,7 @@ static int max77693_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct max77693_dev *max77693; - u8 reg_data; + unsigned int reg_data; int ret = 0; max77693 = devm_kzalloc(&i2c->dev, @@ -139,7 +87,7 @@ static int max77693_i2c_probe(struct i2c_client *i2c, return ret; } - ret = max77693_read_reg(max77693->regmap, MAX77693_PMIC_REG_PMIC_ID2, + ret = regmap_read(max77693->regmap, MAX77693_PMIC_REG_PMIC_ID2, ®_data); if (ret < 0) { dev_err(max77693->dev, "device not found on this channel\n"); diff --git a/drivers/regulator/max77693.c b/drivers/regulator/max77693.c index 653a58b49cdf..c67ff05fc1dd 100644 --- a/drivers/regulator/max77693.c +++ b/drivers/regulator/max77693.c @@ -31,6 +31,7 @@ #include #include #include +#include #define CHGIN_ILIM_STEP_20mA 20000 @@ -39,9 +40,9 @@ static int max77693_chg_is_enabled(struct regulator_dev *rdev) { int ret; - u8 val; + unsigned int val; - ret = max77693_read_reg(rdev->regmap, rdev->desc->enable_reg, &val); + ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, &val); if (ret) return ret; @@ -57,12 +58,11 @@ static int max77693_chg_get_current_limit(struct regulator_dev *rdev) { unsigned int chg_min_uA = rdev->constraints->min_uA; unsigned int chg_max_uA = rdev->constraints->max_uA; - u8 reg, sel; + unsigned int reg, sel; unsigned int val; int ret; - ret = max77693_read_reg(rdev->regmap, - MAX77693_CHG_REG_CHG_CNFG_09, ®); + ret = regmap_read(rdev->regmap, MAX77693_CHG_REG_CHG_CNFG_09, ®); if (ret < 0) return ret; @@ -96,7 +96,7 @@ static int max77693_chg_set_current_limit(struct regulator_dev *rdev, /* the first four codes for charger current are all 60mA */ sel += 3; - return max77693_write_reg(rdev->regmap, + return regmap_write(rdev->regmap, MAX77693_CHG_REG_CHG_CNFG_09, sel); } /* end of CHARGER regulator ops */ diff --git a/include/linux/mfd/max77693-private.h b/include/linux/mfd/max77693-private.h index 3e050b933dd0..80ec31d561c4 100644 --- a/include/linux/mfd/max77693-private.h +++ b/include/linux/mfd/max77693-private.h @@ -332,14 +332,6 @@ enum max77693_types { TYPE_MAX77693, }; -extern int max77693_read_reg(struct regmap *map, u8 reg, u8 *dest); -extern int max77693_bulk_read(struct regmap *map, u8 reg, int count, - u8 *buf); -extern int max77693_write_reg(struct regmap *map, u8 reg, u8 value); -extern int max77693_bulk_write(struct regmap *map, u8 reg, int count, - u8 *buf); -extern int max77693_update_reg(struct regmap *map, u8 reg, u8 val, u8 mask); - extern int max77693_irq_init(struct max77693_dev *max77686); extern void max77693_irq_exit(struct max77693_dev *max77686); extern int max77693_irq_resume(struct max77693_dev *max77686); -- cgit v1.2.3 From 342d669c1ee421323f552a62729d3a3d0065093c Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Wed, 21 May 2014 08:52:48 +0200 Subject: mfd: max77693: Handle IRQs using regmap This patch modifies mfd driver to use regmap for handling interrupts. It allows to simplify irq handling process. This modifications needed to make small changes in function drivers, which use interrupts. Signed-off-by: Robert Baldyga Reviewed-by: Krzysztof Kozlowski Acked-by: Chanwoo Choi Signed-off-by: Lee Jones --- drivers/extcon/extcon-max77693.c | 3 +- drivers/mfd/Kconfig | 1 + drivers/mfd/Makefile | 2 +- drivers/mfd/max77693-irq.c | 346 ----------------------------------- drivers/mfd/max77693.c | 158 ++++++++++++++-- include/linux/mfd/max77693-private.h | 46 ++++- 6 files changed, 195 insertions(+), 361 deletions(-) delete mode 100644 drivers/mfd/max77693-irq.c (limited to 'include') diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c index ba84a6e77e03..c7278b1649da 100644 --- a/drivers/extcon/extcon-max77693.c +++ b/drivers/extcon/extcon-max77693.c @@ -1154,7 +1154,8 @@ static int max77693_muic_probe(struct platform_device *pdev) struct max77693_muic_irq *muic_irq = &muic_irqs[i]; unsigned int virq = 0; - virq = irq_create_mapping(max77693->irq_domain, muic_irq->irq); + virq = regmap_irq_get_virq(max77693->irq_data_muic, + muic_irq->irq); if (!virq) { ret = -EINVAL; goto err_irq; diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index ee8204cc31e9..2feac14d1085 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -384,6 +384,7 @@ config MFD_MAX77693 depends on I2C=y select MFD_CORE select REGMAP_I2C + select REGMAP_IRQ help Say yes here to add support for Maxim Semiconductor MAX77693. This is a companion Power Management IC with Flash, Haptic, Charger, diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 8afedba535c7..8c6e7bba4660 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -116,7 +116,7 @@ obj-$(CONFIG_MFD_DA9063) += da9063.o obj-$(CONFIG_MFD_MAX14577) += max14577.o obj-$(CONFIG_MFD_MAX77686) += max77686.o max77686-irq.o -obj-$(CONFIG_MFD_MAX77693) += max77693.o max77693-irq.o +obj-$(CONFIG_MFD_MAX77693) += max77693.o obj-$(CONFIG_MFD_MAX8907) += max8907.o max8925-objs := max8925-core.o max8925-i2c.o obj-$(CONFIG_MFD_MAX8925) += max8925.o diff --git a/drivers/mfd/max77693-irq.c b/drivers/mfd/max77693-irq.c deleted file mode 100644 index 7d8f99f94f27..000000000000 --- a/drivers/mfd/max77693-irq.c +++ /dev/null @@ -1,346 +0,0 @@ -/* - * max77693-irq.c - Interrupt controller support for MAX77693 - * - * Copyright (C) 2012 Samsung Electronics Co.Ltd - * SangYoung Son - * - * This program is not provided / owned by Maxim Integrated Products. - * - * 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 - -static const unsigned int max77693_mask_reg[] = { - [LED_INT] = MAX77693_LED_REG_FLASH_INT_MASK, - [TOPSYS_INT] = MAX77693_PMIC_REG_TOPSYS_INT_MASK, - [CHG_INT] = MAX77693_CHG_REG_CHG_INT_MASK, - [MUIC_INT1] = MAX77693_MUIC_REG_INTMASK1, - [MUIC_INT2] = MAX77693_MUIC_REG_INTMASK2, - [MUIC_INT3] = MAX77693_MUIC_REG_INTMASK3, -}; - -static struct regmap *max77693_get_regmap(struct max77693_dev *max77693, - enum max77693_irq_source src) -{ - switch (src) { - case LED_INT ... CHG_INT: - return max77693->regmap; - case MUIC_INT1 ... MUIC_INT3: - return max77693->regmap_muic; - default: - return ERR_PTR(-EINVAL); - } -} - -struct max77693_irq_data { - int mask; - enum max77693_irq_source group; -}; - -#define DECLARE_IRQ(idx, _group, _mask) \ - [(idx)] = { .group = (_group), .mask = (_mask) } -static const struct max77693_irq_data max77693_irqs[] = { - DECLARE_IRQ(MAX77693_LED_IRQ_FLED2_OPEN, LED_INT, 1 << 0), - DECLARE_IRQ(MAX77693_LED_IRQ_FLED2_SHORT, LED_INT, 1 << 1), - DECLARE_IRQ(MAX77693_LED_IRQ_FLED1_OPEN, LED_INT, 1 << 2), - DECLARE_IRQ(MAX77693_LED_IRQ_FLED1_SHORT, LED_INT, 1 << 3), - DECLARE_IRQ(MAX77693_LED_IRQ_MAX_FLASH, LED_INT, 1 << 4), - - DECLARE_IRQ(MAX77693_TOPSYS_IRQ_T120C_INT, TOPSYS_INT, 1 << 0), - DECLARE_IRQ(MAX77693_TOPSYS_IRQ_T140C_INT, TOPSYS_INT, 1 << 1), - DECLARE_IRQ(MAX77693_TOPSYS_IRQ_LOWSYS_INT, TOPSYS_INT, 1 << 3), - - DECLARE_IRQ(MAX77693_CHG_IRQ_BYP_I, CHG_INT, 1 << 0), - DECLARE_IRQ(MAX77693_CHG_IRQ_THM_I, CHG_INT, 1 << 2), - DECLARE_IRQ(MAX77693_CHG_IRQ_BAT_I, CHG_INT, 1 << 3), - DECLARE_IRQ(MAX77693_CHG_IRQ_CHG_I, CHG_INT, 1 << 4), - DECLARE_IRQ(MAX77693_CHG_IRQ_CHGIN_I, CHG_INT, 1 << 6), - - DECLARE_IRQ(MAX77693_MUIC_IRQ_INT1_ADC, MUIC_INT1, 1 << 0), - DECLARE_IRQ(MAX77693_MUIC_IRQ_INT1_ADC_LOW, MUIC_INT1, 1 << 1), - DECLARE_IRQ(MAX77693_MUIC_IRQ_INT1_ADC_ERR, MUIC_INT1, 1 << 2), - DECLARE_IRQ(MAX77693_MUIC_IRQ_INT1_ADC1K, MUIC_INT1, 1 << 3), - - DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_CHGTYP, MUIC_INT2, 1 << 0), - DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_CHGDETREUN, MUIC_INT2, 1 << 1), - DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_DCDTMR, MUIC_INT2, 1 << 2), - DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_DXOVP, MUIC_INT2, 1 << 3), - DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_VBVOLT, MUIC_INT2, 1 << 4), - DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_VIDRM, MUIC_INT2, 1 << 5), - - DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_EOC, MUIC_INT3, 1 << 0), - DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_CGMBC, MUIC_INT3, 1 << 1), - DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_OVP, MUIC_INT3, 1 << 2), - DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_MBCCHG_ERR, MUIC_INT3, 1 << 3), - DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_CHG_ENABLED, MUIC_INT3, 1 << 4), - DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_BAT_DET, MUIC_INT3, 1 << 5), -}; - -static void max77693_irq_lock(struct irq_data *data) -{ - struct max77693_dev *max77693 = irq_get_chip_data(data->irq); - - mutex_lock(&max77693->irqlock); -} - -static void max77693_irq_sync_unlock(struct irq_data *data) -{ - struct max77693_dev *max77693 = irq_get_chip_data(data->irq); - int i; - - for (i = 0; i < MAX77693_IRQ_GROUP_NR; i++) { - u8 mask_reg = max77693_mask_reg[i]; - struct regmap *map = max77693_get_regmap(max77693, i); - - if (mask_reg == MAX77693_REG_INVALID || - IS_ERR_OR_NULL(map)) - continue; - max77693->irq_masks_cache[i] = max77693->irq_masks_cur[i]; - - regmap_write(map, max77693_mask_reg[i], - max77693->irq_masks_cur[i]); - } - - mutex_unlock(&max77693->irqlock); -} - -static const inline struct max77693_irq_data * -irq_to_max77693_irq(struct max77693_dev *max77693, int irq) -{ - struct irq_data *data = irq_get_irq_data(irq); - return &max77693_irqs[data->hwirq]; -} - -static void max77693_irq_mask(struct irq_data *data) -{ - struct max77693_dev *max77693 = irq_get_chip_data(data->irq); - const struct max77693_irq_data *irq_data = - irq_to_max77693_irq(max77693, data->irq); - - if (irq_data->group >= MAX77693_IRQ_GROUP_NR) - return; - - if (irq_data->group >= MUIC_INT1 && irq_data->group <= MUIC_INT3) - max77693->irq_masks_cur[irq_data->group] &= ~irq_data->mask; - else - max77693->irq_masks_cur[irq_data->group] |= irq_data->mask; -} - -static void max77693_irq_unmask(struct irq_data *data) -{ - struct max77693_dev *max77693 = irq_get_chip_data(data->irq); - const struct max77693_irq_data *irq_data = - irq_to_max77693_irq(max77693, data->irq); - - if (irq_data->group >= MAX77693_IRQ_GROUP_NR) - return; - - if (irq_data->group >= MUIC_INT1 && irq_data->group <= MUIC_INT3) - max77693->irq_masks_cur[irq_data->group] |= irq_data->mask; - else - max77693->irq_masks_cur[irq_data->group] &= ~irq_data->mask; -} - -static struct irq_chip max77693_irq_chip = { - .name = "max77693", - .irq_bus_lock = max77693_irq_lock, - .irq_bus_sync_unlock = max77693_irq_sync_unlock, - .irq_mask = max77693_irq_mask, - .irq_unmask = max77693_irq_unmask, -}; - -#define MAX77693_IRQSRC_CHG (1 << 0) -#define MAX77693_IRQSRC_TOP (1 << 1) -#define MAX77693_IRQSRC_FLASH (1 << 2) -#define MAX77693_IRQSRC_MUIC (1 << 3) -static irqreturn_t max77693_irq_thread(int irq, void *data) -{ - struct max77693_dev *max77693 = data; - u8 irq_reg[MAX77693_IRQ_GROUP_NR] = {}; - unsigned int irq_src; - int ret; - int i, cur_irq; - - ret = regmap_read(max77693->regmap, MAX77693_PMIC_REG_INTSRC, - &irq_src); - if (ret < 0) { - dev_err(max77693->dev, "Failed to read interrupt source: %d\n", - ret); - return IRQ_NONE; - } - - if (irq_src & MAX77693_IRQSRC_CHG) { - /* CHG_INT */ - unsigned int data; - ret = regmap_read(max77693->regmap, - MAX77693_CHG_REG_CHG_INT, &data); - irq_reg[CHG_INT] = data; - } - - if (irq_src & MAX77693_IRQSRC_TOP) { - /* TOPSYS_INT */ - unsigned int data; - ret = regmap_read(max77693->regmap, - MAX77693_PMIC_REG_TOPSYS_INT, &data); - irq_reg[TOPSYS_INT] = data; - } - - if (irq_src & MAX77693_IRQSRC_FLASH) { - /* LED_INT */ - unsigned int data; - ret = regmap_read(max77693->regmap, - MAX77693_LED_REG_FLASH_INT, &data); - irq_reg[LED_INT] = data; - } - - if (irq_src & MAX77693_IRQSRC_MUIC) - /* MUIC INT1 ~ INT3 */ - regmap_bulk_read(max77693->regmap_muic, MAX77693_MUIC_REG_INT1, - &irq_reg[MUIC_INT1], MAX77693_NUM_IRQ_MUIC_REGS); - - /* Apply masking */ - for (i = 0; i < MAX77693_IRQ_GROUP_NR; i++) { - if (i >= MUIC_INT1 && i <= MUIC_INT3) - irq_reg[i] &= max77693->irq_masks_cur[i]; - else - irq_reg[i] &= ~max77693->irq_masks_cur[i]; - } - - /* Report */ - for (i = 0; i < MAX77693_IRQ_NR; i++) { - if (irq_reg[max77693_irqs[i].group] & max77693_irqs[i].mask) { - cur_irq = irq_find_mapping(max77693->irq_domain, i); - if (cur_irq) - handle_nested_irq(cur_irq); - } - } - - return IRQ_HANDLED; -} - -int max77693_irq_resume(struct max77693_dev *max77693) -{ - if (max77693->irq) - max77693_irq_thread(0, max77693); - - return 0; -} - -static int max77693_irq_domain_map(struct irq_domain *d, unsigned int irq, - irq_hw_number_t hw) -{ - struct max77693_dev *max77693 = d->host_data; - - irq_set_chip_data(irq, max77693); - irq_set_chip_and_handler(irq, &max77693_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 max77693_irq_domain_ops = { - .map = max77693_irq_domain_map, -}; - -int max77693_irq_init(struct max77693_dev *max77693) -{ - struct irq_domain *domain; - int i; - int ret = 0; - unsigned int intsrc_mask; - - mutex_init(&max77693->irqlock); - - /* Mask individual interrupt sources */ - for (i = 0; i < MAX77693_IRQ_GROUP_NR; i++) { - struct regmap *map; - /* MUIC IRQ 0:MASK 1:NOT MASK */ - /* Other IRQ 1:MASK 0:NOT MASK */ - if (i >= MUIC_INT1 && i <= MUIC_INT3) { - max77693->irq_masks_cur[i] = 0x00; - max77693->irq_masks_cache[i] = 0x00; - } else { - max77693->irq_masks_cur[i] = 0xff; - max77693->irq_masks_cache[i] = 0xff; - } - map = max77693_get_regmap(max77693, i); - - if (IS_ERR_OR_NULL(map)) - continue; - if (max77693_mask_reg[i] == MAX77693_REG_INVALID) - continue; - if (i >= MUIC_INT1 && i <= MUIC_INT3) - regmap_write(map, max77693_mask_reg[i], 0x00); - else - regmap_write(map, max77693_mask_reg[i], 0xff); - } - - domain = irq_domain_add_linear(NULL, MAX77693_IRQ_NR, - &max77693_irq_domain_ops, max77693); - if (!domain) { - dev_err(max77693->dev, "could not create irq domain\n"); - ret = -ENODEV; - goto err_irq; - } - max77693->irq_domain = domain; - - /* Unmask max77693 interrupt */ - ret = regmap_read(max77693->regmap, - MAX77693_PMIC_REG_INTSRC_MASK, &intsrc_mask); - if (ret < 0) { - dev_err(max77693->dev, "fail to read PMIC register\n"); - goto err_irq; - } - - intsrc_mask &= ~(MAX77693_IRQSRC_CHG); - intsrc_mask &= ~(MAX77693_IRQSRC_FLASH); - intsrc_mask &= ~(MAX77693_IRQSRC_MUIC); - ret = regmap_write(max77693->regmap, - MAX77693_PMIC_REG_INTSRC_MASK, intsrc_mask); - if (ret < 0) { - dev_err(max77693->dev, "fail to write PMIC register\n"); - goto err_irq; - } - - ret = request_threaded_irq(max77693->irq, NULL, max77693_irq_thread, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "max77693-irq", max77693); - if (ret) - dev_err(max77693->dev, "Failed to request IRQ %d: %d\n", - max77693->irq, ret); - -err_irq: - return ret; -} - -void max77693_irq_exit(struct max77693_dev *max77693) -{ - if (max77693->irq) - free_irq(max77693->irq, max77693); -} diff --git a/drivers/mfd/max77693.c b/drivers/mfd/max77693.c index a0308336adaf..249c139ef04a 100644 --- a/drivers/mfd/max77693.c +++ b/drivers/mfd/max77693.c @@ -55,12 +55,95 @@ static const struct regmap_config max77693_regmap_config = { .max_register = MAX77693_PMIC_REG_END, }; +static const struct regmap_irq max77693_led_irqs[] = { + { .mask = LED_IRQ_FLED2_OPEN, }, + { .mask = LED_IRQ_FLED2_SHORT, }, + { .mask = LED_IRQ_FLED1_OPEN, }, + { .mask = LED_IRQ_FLED1_SHORT, }, + { .mask = LED_IRQ_MAX_FLASH, }, +}; + +static const struct regmap_irq_chip max77693_led_irq_chip = { + .name = "max77693-led", + .status_base = MAX77693_LED_REG_FLASH_INT, + .mask_base = MAX77693_LED_REG_FLASH_INT_MASK, + .mask_invert = false, + .num_regs = 1, + .irqs = max77693_led_irqs, + .num_irqs = ARRAY_SIZE(max77693_led_irqs), +}; + +static const struct regmap_irq max77693_topsys_irqs[] = { + { .mask = TOPSYS_IRQ_T120C_INT, }, + { .mask = TOPSYS_IRQ_T140C_INT, }, + { .mask = TOPSYS_IRQ_LOWSYS_INT, }, +}; + +static const struct regmap_irq_chip max77693_topsys_irq_chip = { + .name = "max77693-topsys", + .status_base = MAX77693_PMIC_REG_TOPSYS_INT, + .mask_base = MAX77693_PMIC_REG_TOPSYS_INT_MASK, + .mask_invert = false, + .num_regs = 1, + .irqs = max77693_topsys_irqs, + .num_irqs = ARRAY_SIZE(max77693_topsys_irqs), +}; + +static const struct regmap_irq max77693_charger_irqs[] = { + { .mask = CHG_IRQ_BYP_I, }, + { .mask = CHG_IRQ_THM_I, }, + { .mask = CHG_IRQ_BAT_I, }, + { .mask = CHG_IRQ_CHG_I, }, + { .mask = CHG_IRQ_CHGIN_I, }, +}; + +static const struct regmap_irq_chip max77693_charger_irq_chip = { + .name = "max77693-charger", + .status_base = MAX77693_CHG_REG_CHG_INT, + .mask_base = MAX77693_CHG_REG_CHG_INT_MASK, + .mask_invert = false, + .num_regs = 1, + .irqs = max77693_charger_irqs, + .num_irqs = ARRAY_SIZE(max77693_charger_irqs), +}; + static const struct regmap_config max77693_regmap_muic_config = { .reg_bits = 8, .val_bits = 8, .max_register = MAX77693_MUIC_REG_END, }; +static const struct regmap_irq max77693_muic_irqs[] = { + { .reg_offset = 0, .mask = MUIC_IRQ_INT1_ADC, }, + { .reg_offset = 0, .mask = MUIC_IRQ_INT1_ADC_LOW, }, + { .reg_offset = 0, .mask = MUIC_IRQ_INT1_ADC_ERR, }, + { .reg_offset = 0, .mask = MUIC_IRQ_INT1_ADC1K, }, + + { .reg_offset = 1, .mask = MUIC_IRQ_INT2_CHGTYP, }, + { .reg_offset = 1, .mask = MUIC_IRQ_INT2_CHGDETREUN, }, + { .reg_offset = 1, .mask = MUIC_IRQ_INT2_DCDTMR, }, + { .reg_offset = 1, .mask = MUIC_IRQ_INT2_DXOVP, }, + { .reg_offset = 1, .mask = MUIC_IRQ_INT2_VBVOLT, }, + { .reg_offset = 1, .mask = MUIC_IRQ_INT2_VIDRM, }, + + { .reg_offset = 2, .mask = MUIC_IRQ_INT3_EOC, }, + { .reg_offset = 2, .mask = MUIC_IRQ_INT3_CGMBC, }, + { .reg_offset = 2, .mask = MUIC_IRQ_INT3_OVP, }, + { .reg_offset = 2, .mask = MUIC_IRQ_INT3_MBCCHG_ERR, }, + { .reg_offset = 2, .mask = MUIC_IRQ_INT3_CHG_ENABLED, }, + { .reg_offset = 2, .mask = MUIC_IRQ_INT3_BAT_DET, }, +}; + +static const struct regmap_irq_chip max77693_muic_irq_chip = { + .name = "max77693-muic", + .status_base = MAX77693_MUIC_REG_INT1, + .mask_base = MAX77693_MUIC_REG_INTMASK1, + .mask_invert = true, + .num_regs = 3, + .irqs = max77693_muic_irqs, + .num_irqs = ARRAY_SIZE(max77693_muic_irqs), +}; + static int max77693_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -124,9 +207,45 @@ static int max77693_i2c_probe(struct i2c_client *i2c, goto err_regmap_muic; } - ret = max77693_irq_init(max77693); - if (ret < 0) - goto err_irq; + ret = regmap_add_irq_chip(max77693->regmap, max77693->irq, + IRQF_ONESHOT | IRQF_SHARED | + IRQF_TRIGGER_FALLING, 0, + &max77693_led_irq_chip, + &max77693->irq_data_led); + if (ret) { + dev_err(max77693->dev, "failed to add irq chip: %d\n", ret); + goto err_regmap_muic; + } + + ret = regmap_add_irq_chip(max77693->regmap, max77693->irq, + IRQF_ONESHOT | IRQF_SHARED | + IRQF_TRIGGER_FALLING, 0, + &max77693_topsys_irq_chip, + &max77693->irq_data_topsys); + if (ret) { + dev_err(max77693->dev, "failed to add irq chip: %d\n", ret); + goto err_irq_topsys; + } + + ret = regmap_add_irq_chip(max77693->regmap, max77693->irq, + IRQF_ONESHOT | IRQF_SHARED | + IRQF_TRIGGER_FALLING, 0, + &max77693_charger_irq_chip, + &max77693->irq_data_charger); + if (ret) { + dev_err(max77693->dev, "failed to add irq chip: %d\n", ret); + goto err_irq_charger; + } + + ret = regmap_add_irq_chip(max77693->regmap, max77693->irq, + IRQF_ONESHOT | IRQF_SHARED | + IRQF_TRIGGER_FALLING, 0, + &max77693_muic_irq_chip, + &max77693->irq_data_muic); + if (ret) { + dev_err(max77693->dev, "failed to add irq chip: %d\n", ret); + goto err_irq_muic; + } pm_runtime_set_active(max77693->dev); @@ -138,8 +257,14 @@ static int max77693_i2c_probe(struct i2c_client *i2c, return ret; err_mfd: - max77693_irq_exit(max77693); -err_irq: + mfd_remove_devices(max77693->dev); + regmap_del_irq_chip(max77693->irq, max77693->irq_data_muic); +err_irq_muic: + regmap_del_irq_chip(max77693->irq, max77693->irq_data_charger); +err_irq_charger: + regmap_del_irq_chip(max77693->irq, max77693->irq_data_topsys); +err_irq_topsys: + regmap_del_irq_chip(max77693->irq, max77693->irq_data_led); err_regmap_muic: i2c_unregister_device(max77693->haptic); err_i2c_haptic: @@ -152,7 +277,12 @@ static int max77693_i2c_remove(struct i2c_client *i2c) struct max77693_dev *max77693 = i2c_get_clientdata(i2c); mfd_remove_devices(max77693->dev); - max77693_irq_exit(max77693); + + regmap_del_irq_chip(max77693->irq, max77693->irq_data_muic); + regmap_del_irq_chip(max77693->irq, max77693->irq_data_charger); + regmap_del_irq_chip(max77693->irq, max77693->irq_data_topsys); + regmap_del_irq_chip(max77693->irq, max77693->irq_data_led); + i2c_unregister_device(max77693->muic); i2c_unregister_device(max77693->haptic); @@ -170,8 +300,11 @@ static int max77693_suspend(struct device *dev) struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); struct max77693_dev *max77693 = i2c_get_clientdata(i2c); - if (device_may_wakeup(dev)) - irq_set_irq_wake(max77693->irq, 1); + if (device_may_wakeup(dev)) { + enable_irq_wake(max77693->irq); + disable_irq(max77693->irq); + } + return 0; } @@ -180,9 +313,12 @@ static int max77693_resume(struct device *dev) struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); struct max77693_dev *max77693 = i2c_get_clientdata(i2c); - if (device_may_wakeup(dev)) - irq_set_irq_wake(max77693->irq, 0); - return max77693_irq_resume(max77693); + if (device_may_wakeup(dev)) { + disable_irq_wake(max77693->irq); + enable_irq(max77693->irq); + } + + return 0; } static const struct dev_pm_ops max77693_pm = { diff --git a/include/linux/mfd/max77693-private.h b/include/linux/mfd/max77693-private.h index 80ec31d561c4..c466ff3e16b8 100644 --- a/include/linux/mfd/max77693-private.h +++ b/include/linux/mfd/max77693-private.h @@ -262,6 +262,41 @@ enum max77693_irq_source { MAX77693_IRQ_GROUP_NR, }; +#define LED_IRQ_FLED2_OPEN BIT(0) +#define LED_IRQ_FLED2_SHORT BIT(1) +#define LED_IRQ_FLED1_OPEN BIT(2) +#define LED_IRQ_FLED1_SHORT BIT(3) +#define LED_IRQ_MAX_FLASH BIT(4) + +#define TOPSYS_IRQ_T120C_INT BIT(0) +#define TOPSYS_IRQ_T140C_INT BIT(1) +#define TOPSYS_IRQ_LOWSYS_INT BIT(3) + +#define CHG_IRQ_BYP_I BIT(0) +#define CHG_IRQ_THM_I BIT(2) +#define CHG_IRQ_BAT_I BIT(3) +#define CHG_IRQ_CHG_I BIT(4) +#define CHG_IRQ_CHGIN_I BIT(6) + +#define MUIC_IRQ_INT1_ADC BIT(0) +#define MUIC_IRQ_INT1_ADC_LOW BIT(1) +#define MUIC_IRQ_INT1_ADC_ERR BIT(2) +#define MUIC_IRQ_INT1_ADC1K BIT(3) + +#define MUIC_IRQ_INT2_CHGTYP BIT(0) +#define MUIC_IRQ_INT2_CHGDETREUN BIT(1) +#define MUIC_IRQ_INT2_DCDTMR BIT(2) +#define MUIC_IRQ_INT2_DXOVP BIT(3) +#define MUIC_IRQ_INT2_VBVOLT BIT(4) +#define MUIC_IRQ_INT2_VIDRM BIT(5) + +#define MUIC_IRQ_INT3_EOC BIT(0) +#define MUIC_IRQ_INT3_CGMBC BIT(1) +#define MUIC_IRQ_INT3_OVP BIT(2) +#define MUIC_IRQ_INT3_MBCCHG_ERR BIT(3) +#define MUIC_IRQ_INT3_CHG_ENABLED BIT(4) +#define MUIC_IRQ_INT3_BAT_DET BIT(5) + enum max77693_irq { /* PMIC - FLASH */ MAX77693_LED_IRQ_FLED2_OPEN, @@ -282,6 +317,10 @@ enum max77693_irq { MAX77693_CHG_IRQ_CHG_I, MAX77693_CHG_IRQ_CHGIN_I, + MAX77693_IRQ_NR, +}; + +enum max77693_irq_muic { /* MUIC INT1 */ MAX77693_MUIC_IRQ_INT1_ADC, MAX77693_MUIC_IRQ_INT1_ADC_LOW, @@ -304,7 +343,7 @@ enum max77693_irq { MAX77693_MUIC_IRQ_INT3_CHG_ENABLED, MAX77693_MUIC_IRQ_INT3_BAT_DET, - MAX77693_IRQ_NR, + MAX77693_MUIC_IRQ_NR, }; struct max77693_dev { @@ -319,7 +358,10 @@ struct max77693_dev { struct regmap *regmap_muic; struct regmap *regmap_haptic; - struct irq_domain *irq_domain; + struct regmap_irq_chip_data *irq_data_led; + struct regmap_irq_chip_data *irq_data_topsys; + struct regmap_irq_chip_data *irq_data_charger; + struct regmap_irq_chip_data *irq_data_muic; int irq; int irq_gpio; -- cgit v1.2.3 From 9796853e902447e53a17dae5df9eb609f0e31e6a Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Mon, 16 Jun 2014 11:23:00 +0200 Subject: ARM: STi: DT: Properly define sti-ethclk & stmmaceth for stih415/6 This patch fixes two problems: - 1) The device tree isn't currently providing sti-ethclk which is required by the dwmac glue code to correctly configure the ethernet PHY clock speed. This means depending on what the bootloader/jtag has configured this clock to, and what switch/hub the board is plugged into you most likely will NOT successfully negotiate a ethernet link. 2) The stmmaceth clock was associated with the wrong clock. It was referencing the PHY clock rather than the interconnect clock which clocks the IP. This patch also brings us closer to not having to boot the upstream kernel with the clk_ignore_unused parameter. Acked-by: Lee Jones Signed-off-by: Peter Griffin Signed-off-by: Maxime Coquelin --- arch/arm/boot/dts/stih415.dtsi | 8 ++++---- arch/arm/boot/dts/stih416.dtsi | 8 ++++---- include/dt-bindings/clock/stih415-clks.h | 1 + include/dt-bindings/clock/stih416-clks.h | 1 + 4 files changed, 10 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/arch/arm/boot/dts/stih415.dtsi b/arch/arm/boot/dts/stih415.dtsi index d6f254f302fe..a0f6f75fe3b5 100644 --- a/arch/arm/boot/dts/stih415.dtsi +++ b/arch/arm/boot/dts/stih415.dtsi @@ -169,8 +169,8 @@ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_mii0>; - clock-names = "stmmaceth"; - clocks = <&clk_s_a1_ls CLK_GMAC0_PHY>; + clock-names = "stmmaceth", "sti-ethclk"; + clocks = <&clk_s_a1_ls CLK_ICN_IF_2>, <&clk_s_a1_ls CLK_GMAC0_PHY>; }; ethernet1: dwmac@fef08000 { @@ -192,8 +192,8 @@ reset-names = "stmmaceth"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_mii1>; - clock-names = "stmmaceth"; - clocks = <&clk_s_a0_ls CLK_ETH1_PHY>; + clock-names = "stmmaceth", "sti-ethclk"; + clocks = <&clk_s_a0_ls CLK_ICN_REG>, <&clk_s_a0_ls CLK_ETH1_PHY>; }; rc: rc@fe518000 { diff --git a/arch/arm/boot/dts/stih416.dtsi b/arch/arm/boot/dts/stih416.dtsi index 06473c5d9ea9..84758d76d064 100644 --- a/arch/arm/boot/dts/stih416.dtsi +++ b/arch/arm/boot/dts/stih416.dtsi @@ -175,8 +175,8 @@ reset-names = "stmmaceth"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_mii0>; - clock-names = "stmmaceth"; - clocks = <&clk_s_a1_ls CLK_GMAC0_PHY>; + clock-names = "stmmaceth", "sti-ethclk"; + clocks = <&clk_s_a1_ls CLK_ICN_IF_2>, <&clk_s_a1_ls CLK_GMAC0_PHY>; }; ethernet1: dwmac@fef08000 { @@ -197,8 +197,8 @@ reset-names = "stmmaceth"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_mii1>; - clock-names = "stmmaceth"; - clocks = <&clk_s_a0_ls CLK_ETH1_PHY>; + clock-names = "stmmaceth", "sti-ethclk"; + clocks = <&clk_s_a0_ls CLK_ICN_REG>, <&clk_s_a0_ls CLK_ETH1_PHY>; }; rc: rc@fe518000 { diff --git a/include/dt-bindings/clock/stih415-clks.h b/include/dt-bindings/clock/stih415-clks.h index 0d2c7397e028..d80caa68aebd 100644 --- a/include/dt-bindings/clock/stih415-clks.h +++ b/include/dt-bindings/clock/stih415-clks.h @@ -10,6 +10,7 @@ #define CLK_ETH1_PHY 4 /* CLOCKGEN A1 */ +#define CLK_ICN_IF_2 0 #define CLK_GMAC0_PHY 3 #endif diff --git a/include/dt-bindings/clock/stih416-clks.h b/include/dt-bindings/clock/stih416-clks.h index 552c779eb6af..f9bdbd13568d 100644 --- a/include/dt-bindings/clock/stih416-clks.h +++ b/include/dt-bindings/clock/stih416-clks.h @@ -10,6 +10,7 @@ #define CLK_ETH1_PHY 4 /* CLOCKGEN A1 */ +#define CLK_ICN_IF_2 0 #define CLK_GMAC0_PHY 3 #endif -- cgit v1.2.3 From 5165238460068e53c740eaa621ebb6623dc4a50d Mon Sep 17 00:00:00 2001 From: "Zhu, Lejun" Date: Tue, 3 Jun 2014 13:26:02 +0800 Subject: mfd: intel_soc_pmic: Core driver This patch provides the common I2C driver code for Intel SoC PMICs. Signed-off-by: Yang, Bin Signed-off-by: Zhu, Lejun Signed-off-by: Lee Jones --- drivers/mfd/intel_soc_pmic_core.c | 168 +++++++++++++++++++++++++++++++++++++ drivers/mfd/intel_soc_pmic_core.h | 32 +++++++ include/linux/mfd/intel_soc_pmic.h | 30 +++++++ 3 files changed, 230 insertions(+) create mode 100644 drivers/mfd/intel_soc_pmic_core.c create mode 100644 drivers/mfd/intel_soc_pmic_core.h create mode 100644 include/linux/mfd/intel_soc_pmic.h (limited to 'include') diff --git a/drivers/mfd/intel_soc_pmic_core.c b/drivers/mfd/intel_soc_pmic_core.c new file mode 100644 index 000000000000..cddbf5a72f89 --- /dev/null +++ b/drivers/mfd/intel_soc_pmic_core.c @@ -0,0 +1,168 @@ +/* + * intel_soc_pmic_core.c - Intel SoC PMIC MFD Driver + * + * Copyright (C) 2013, 2014 Intel Corporation. All rights reserved. + * + * 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. + * + * Author: Yang, Bin + * Author: Zhu, Lejun + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "intel_soc_pmic_core.h" + +/* + * On some boards the PMIC interrupt may come from a GPIO line. + * Try to lookup the ACPI table and see if such connection exists. If not, + * return -ENOENT and use the IRQ provided by I2C. + */ +static int intel_soc_pmic_find_gpio_irq(struct device *dev) +{ + struct gpio_desc *desc; + int irq; + + desc = devm_gpiod_get_index(dev, "intel_soc_pmic", 0); + if (IS_ERR(desc)) + return -ENOENT; + + irq = gpiod_to_irq(desc); + if (irq < 0) + dev_warn(dev, "Can't get irq: %d\n", irq); + + return irq; +} + +static int intel_soc_pmic_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *i2c_id) +{ + struct device *dev = &i2c->dev; + const struct acpi_device_id *id; + struct intel_soc_pmic_config *config; + struct intel_soc_pmic *pmic; + int ret; + int irq; + + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id || !id->driver_data) + return -ENODEV; + + config = (struct intel_soc_pmic_config *)id->driver_data; + + pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL); + dev_set_drvdata(dev, pmic); + + pmic->regmap = devm_regmap_init_i2c(i2c, config->regmap_config); + + irq = intel_soc_pmic_find_gpio_irq(dev); + pmic->irq = (irq < 0) ? i2c->irq : irq; + + ret = regmap_add_irq_chip(pmic->regmap, pmic->irq, + config->irq_flags | IRQF_ONESHOT, + 0, config->irq_chip, + &pmic->irq_chip_data); + if (ret) + return ret; + + ret = enable_irq_wake(pmic->irq); + if (ret) + dev_warn(dev, "Can't enable IRQ as wake source: %d\n", ret); + + ret = mfd_add_devices(dev, -1, config->cell_dev, + config->n_cell_devs, NULL, 0, + regmap_irq_get_domain(pmic->irq_chip_data)); + if (ret) + goto err_del_irq_chip; + + return 0; + +err_del_irq_chip: + regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data); + return ret; +} + +static int intel_soc_pmic_i2c_remove(struct i2c_client *i2c) +{ + struct intel_soc_pmic *pmic = dev_get_drvdata(&i2c->dev); + + regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data); + + mfd_remove_devices(&i2c->dev); + + return 0; +} + +static void intel_soc_pmic_shutdown(struct i2c_client *i2c) +{ + struct intel_soc_pmic *pmic = dev_get_drvdata(&i2c->dev); + + disable_irq(pmic->irq); + + return; +} + +static int intel_soc_pmic_suspend(struct device *dev) +{ + struct intel_soc_pmic *pmic = dev_get_drvdata(dev); + + disable_irq(pmic->irq); + + return 0; +} + +static int intel_soc_pmic_resume(struct device *dev) +{ + struct intel_soc_pmic *pmic = dev_get_drvdata(dev); + + enable_irq(pmic->irq); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(intel_soc_pmic_pm_ops, intel_soc_pmic_suspend, + intel_soc_pmic_resume); + +static const struct i2c_device_id intel_soc_pmic_i2c_id[] = { + { } +}; +MODULE_DEVICE_TABLE(i2c, intel_soc_pmic_i2c_id); + +static struct acpi_device_id intel_soc_pmic_acpi_match[] = { + {"INT33FD", (kernel_ulong_t)&intel_soc_pmic_config_crc}, + { }, +}; +MODULE_DEVICE_TABLE(acpi, intel_soc_pmic_acpi_match); + +static struct i2c_driver intel_soc_pmic_i2c_driver = { + .driver = { + .name = "intel_soc_pmic_i2c", + .owner = THIS_MODULE, + .pm = &intel_soc_pmic_pm_ops, + .acpi_match_table = ACPI_PTR(intel_soc_pmic_acpi_match), + }, + .probe = intel_soc_pmic_i2c_probe, + .remove = intel_soc_pmic_i2c_remove, + .id_table = intel_soc_pmic_i2c_id, + .shutdown = intel_soc_pmic_shutdown, +}; + +module_i2c_driver(intel_soc_pmic_i2c_driver); + +MODULE_DESCRIPTION("I2C driver for Intel SoC PMIC"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Yang, Bin "); +MODULE_AUTHOR("Zhu, Lejun "); diff --git a/drivers/mfd/intel_soc_pmic_core.h b/drivers/mfd/intel_soc_pmic_core.h new file mode 100644 index 000000000000..33aacd9baddc --- /dev/null +++ b/drivers/mfd/intel_soc_pmic_core.h @@ -0,0 +1,32 @@ +/* + * intel_soc_pmic_core.h - Intel SoC PMIC MFD Driver + * + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. + * + * 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. + * + * Author: Yang, Bin + * Author: Zhu, Lejun + */ + +#ifndef __INTEL_SOC_PMIC_CORE_H__ +#define __INTEL_SOC_PMIC_CORE_H__ + +struct intel_soc_pmic_config { + unsigned long irq_flags; + struct mfd_cell *cell_dev; + int n_cell_devs; + struct regmap_config *regmap_config; + struct regmap_irq_chip *irq_chip; +}; + +extern struct intel_soc_pmic_config intel_soc_pmic_config_crc; + +#endif /* __INTEL_SOC_PMIC_CORE_H__ */ diff --git a/include/linux/mfd/intel_soc_pmic.h b/include/linux/mfd/intel_soc_pmic.h new file mode 100644 index 000000000000..abcbfcf32d10 --- /dev/null +++ b/include/linux/mfd/intel_soc_pmic.h @@ -0,0 +1,30 @@ +/* + * intel_soc_pmic.h - Intel SoC PMIC Driver + * + * Copyright (C) 2012-2014 Intel Corporation. All rights reserved. + * + * 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. + * + * Author: Yang, Bin + * Author: Zhu, Lejun + */ + +#ifndef __INTEL_SOC_PMIC_H__ +#define __INTEL_SOC_PMIC_H__ + +#include + +struct intel_soc_pmic { + int irq; + struct regmap *regmap; + struct regmap_irq_chip_data *irq_chip_data; +}; + +#endif /* __INTEL_SOC_PMIC_H__ */ -- cgit v1.2.3 From e0f802fbcaa3bffe4728e37a8fa1279b5d554173 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Tue, 17 Jun 2014 11:25:37 +0300 Subject: tcp: move ir_mark initialization to tcp_openreq_init ir_mark initialization is done for both TCP v4 and v6, move it in the common tcp_openreq_init function. Signed-off-by: Octavian Purdila Signed-off-by: David S. Miller --- include/net/tcp.h | 3 ++- net/ipv4/tcp_ipv4.c | 3 +-- net/ipv6/tcp_ipv6.c | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 7286db80e8b8..06a3023080c0 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1098,7 +1098,7 @@ static inline int tcp_full_space(const struct sock *sk) static inline void tcp_openreq_init(struct request_sock *req, struct tcp_options_received *rx_opt, - struct sk_buff *skb) + struct sk_buff *skb, struct sock *sk) { struct inet_request_sock *ireq = inet_rsk(req); @@ -1117,6 +1117,7 @@ static inline void tcp_openreq_init(struct request_sock *req, ireq->ecn_ok = 0; ireq->ir_rmt_port = tcp_hdr(skb)->source; ireq->ir_num = ntohs(tcp_hdr(skb)->dest); + ireq->ir_mark = inet_request_mark(sk, skb); } extern void tcp_openreq_init_rwin(struct request_sock *req, diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 77cccda1ad0c..180336d47df6 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1311,14 +1311,13 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) tcp_clear_options(&tmp_opt); tmp_opt.tstamp_ok = tmp_opt.saw_tstamp; - tcp_openreq_init(req, &tmp_opt, skb); + tcp_openreq_init(req, &tmp_opt, skb, sk); ireq = inet_rsk(req); ireq->ir_loc_addr = daddr; ireq->ir_rmt_addr = saddr; ireq->no_srccheck = inet_sk(sk)->transparent; ireq->opt = tcp_v4_save_options(skb); - ireq->ir_mark = inet_request_mark(sk, skb); if (security_inet_conn_request(sk, skb, req)) goto drop_and_free; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 229239ad96b1..08ae3da0db4a 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1025,7 +1025,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) tcp_clear_options(&tmp_opt); tmp_opt.tstamp_ok = tmp_opt.saw_tstamp; - tcp_openreq_init(req, &tmp_opt, skb); + tcp_openreq_init(req, &tmp_opt, skb, sk); ireq = inet_rsk(req); ireq->ir_v6_rmt_addr = ipv6_hdr(skb)->saddr; @@ -1034,7 +1034,6 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) TCP_ECN_create_request(req, skb, sock_net(sk)); ireq->ir_iif = sk->sk_bound_dev_if; - ireq->ir_mark = inet_request_mark(sk, skb); /* So that link locals have meaning */ if (!sk->sk_bound_dev_if && -- cgit v1.2.3 From 6adc5cac538b5d9162d8bcdb6145319592afc0d6 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 17 Jun 2014 19:12:33 -0400 Subject: percpu: disallow archs from overriding SHIFT_PERCPU_PTR() It has been about half a decade since all archs started using the dynamic percpu allocator and thus the same SHIFT_PERCPU_PTR() implementation. There's no benefit in overriding SHIFT_PERCPU_PTR() anymore. Remove #ifndef around it to clarify that this is identical regardless of the arch. This patch doesn't cause any functional difference. Signed-off-by: Tejun Heo Acked-by: Christoph Lameter --- include/asm-generic/percpu.h | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/asm-generic/percpu.h b/include/asm-generic/percpu.h index 0703aa75b5e8..63d2b68c826e 100644 --- a/include/asm-generic/percpu.h +++ b/include/asm-generic/percpu.h @@ -36,17 +36,14 @@ extern unsigned long __per_cpu_offset[NR_CPUS]; #endif /* - * Add a offset to a pointer but keep the pointer as is. - * - * Only S390 provides its own means of moving the pointer. + * Add an offset to a pointer but keep the pointer as-is. Use RELOC_HIDE() + * to prevent the compiler from making incorrect assumptions about the + * pointer value. The weird cast keeps both GCC and sparse happy. */ -#ifndef SHIFT_PERCPU_PTR -/* Weird cast keeps both GCC and sparse happy. */ #define SHIFT_PERCPU_PTR(__p, __offset) ({ \ __verify_pcpu_ptr((__p)); \ RELOC_HIDE((typeof(*(__p)) __kernel __force *)(__p), (__offset)); \ }) -#endif /* * A percpu variable may point to a discarded regions. The following are -- cgit v1.2.3 From bbc344e1e3aef3034a0edc79f7f64a912517926b Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 17 Jun 2014 19:12:34 -0400 Subject: percpu: introduce arch_raw_cpu_ptr() Currently, archs can override raw_cpu_ptr() directly; however, we wanna build a layer of indirection in the generic part of percpu so that we can implement generic features there without affecting archs. Introduce arch_raw_cpu_ptr() which is used to define raw_cpu_ptr() by generic percpu code. The two are identical for now. x86 is currently the only arch which overrides raw_cpu_ptr() and is converted to define arch_raw_cpu_ptr() instead. This doesn't introduce any functional difference. Signed-off-by: Tejun Heo Cc: Christoph Lameter Cc: Thomas Gleixner Cc: Ingo Molnar Cc: "H. Peter Anvin" --- arch/x86/include/asm/percpu.h | 2 +- include/asm-generic/percpu.h | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/arch/x86/include/asm/percpu.h b/arch/x86/include/asm/percpu.h index 851bcdc5db04..9bc23f18a6fa 100644 --- a/arch/x86/include/asm/percpu.h +++ b/arch/x86/include/asm/percpu.h @@ -52,7 +52,7 @@ * Compared to the generic __my_cpu_offset version, the following * saves one instruction and avoids clobbering a temp register. */ -#define raw_cpu_ptr(ptr) \ +#define arch_raw_cpu_ptr(ptr) \ ({ \ unsigned long tcp_ptr__; \ __verify_pcpu_ptr(ptr); \ diff --git a/include/asm-generic/percpu.h b/include/asm-generic/percpu.h index 63d2b68c826e..a247d80b6630 100644 --- a/include/asm-generic/percpu.h +++ b/include/asm-generic/percpu.h @@ -53,9 +53,16 @@ extern unsigned long __per_cpu_offset[NR_CPUS]; #define per_cpu(var, cpu) \ (*SHIFT_PERCPU_PTR(&(var), per_cpu_offset(cpu))) -#ifndef raw_cpu_ptr -#define raw_cpu_ptr(ptr) SHIFT_PERCPU_PTR(ptr, __my_cpu_offset) +/* + * Arch may define arch_raw_cpu_ptr() to provide more efficient address + * translations for raw_cpu_ptr(). + */ +#ifndef arch_raw_cpu_ptr +#define arch_raw_cpu_ptr(ptr) SHIFT_PERCPU_PTR(ptr, __my_cpu_offset) #endif + +#define raw_cpu_ptr(ptr) arch_raw_cpu_ptr(ptr) + #ifdef CONFIG_DEBUG_PREEMPT #define this_cpu_ptr(ptr) SHIFT_PERCPU_PTR(ptr, my_cpu_offset) #else -- cgit v1.2.3 From 62fde54123fb64879326c8b71c3f92cc5db1c452 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 17 Jun 2014 19:12:34 -0400 Subject: percpu: include/asm-generic/percpu.h should contain only arch-overridable parts The roles of the various percpu header files has become unclear. There are four header files involved. include/linux/percpu-defs.h include/linux/percpu.h include/asm-generic/percpu.h arch/*/include/asm/percpu.h The original intention for include/asm-generic/percpu.h is providing generic definitions for arch-overridable parts; however, it now hosts various stuff which can't be overridden by archs. Also, include/linux/percpu-defs.h was initially added to contain section and percpu variable definition macros so that arch header files can make use of them without worrying about introducing cyclic inclusion dependency by including include/linux/percpu.h; however, arch headers sometimes need to access percpu variables too and this is one of the reasons why some accessors were implemented in include/linux/asm-generic/percpu.h. Let's clear up the situation by making include/asm-generic/percpu.h contain only arch-overridable parts and moving accessors and operations into include/linux/percpu-defs. Note that this patch only moves things from include/asm-generic/percpu.h. include/linux/percpu.h will be taken care of by later patches. This patch moves the followings. * SHIFT_PERCPU_PTR() / VERIFY_PERCPU_PTR() * per_cpu() * raw_cpu_ptr() * this_cpu_ptr() * __get_cpu_var() * __raw_get_cpu_var() * __this_cpu_ptr() * PER_CPU_[SHARED_]ALIGNED_SECTION * PER_CPU_[SHARED_]ALIGNED_SECTION * PER_CPU_FIRST_SECTION This patch is pure reorganization. Signed-off-by: Tejun Heo Acked-by: Christoph Lameter --- include/asm-generic/percpu.h | 64 ------------------------------- include/linux/percpu-defs.h | 89 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 64 deletions(-) (limited to 'include') diff --git a/include/asm-generic/percpu.h b/include/asm-generic/percpu.h index a247d80b6630..e5ace4d49084 100644 --- a/include/asm-generic/percpu.h +++ b/include/asm-generic/percpu.h @@ -35,24 +35,6 @@ extern unsigned long __per_cpu_offset[NR_CPUS]; #define my_cpu_offset __my_cpu_offset #endif -/* - * Add an offset to a pointer but keep the pointer as-is. Use RELOC_HIDE() - * to prevent the compiler from making incorrect assumptions about the - * pointer value. The weird cast keeps both GCC and sparse happy. - */ -#define SHIFT_PERCPU_PTR(__p, __offset) ({ \ - __verify_pcpu_ptr((__p)); \ - RELOC_HIDE((typeof(*(__p)) __kernel __force *)(__p), (__offset)); \ -}) - -/* - * A percpu variable may point to a discarded regions. The following are - * established ways to produce a usable pointer from the percpu variable - * offset. - */ -#define per_cpu(var, cpu) \ - (*SHIFT_PERCPU_PTR(&(var), per_cpu_offset(cpu))) - /* * Arch may define arch_raw_cpu_ptr() to provide more efficient address * translations for raw_cpu_ptr(). @@ -61,34 +43,10 @@ extern unsigned long __per_cpu_offset[NR_CPUS]; #define arch_raw_cpu_ptr(ptr) SHIFT_PERCPU_PTR(ptr, __my_cpu_offset) #endif -#define raw_cpu_ptr(ptr) arch_raw_cpu_ptr(ptr) - -#ifdef CONFIG_DEBUG_PREEMPT -#define this_cpu_ptr(ptr) SHIFT_PERCPU_PTR(ptr, my_cpu_offset) -#else -#define this_cpu_ptr(ptr) raw_cpu_ptr(ptr) -#endif - -#define __get_cpu_var(var) (*this_cpu_ptr(&(var))) -#define __raw_get_cpu_var(var) (*raw_cpu_ptr(&(var))) - #ifdef CONFIG_HAVE_SETUP_PER_CPU_AREA extern void setup_per_cpu_areas(void); #endif -#else /* ! SMP */ - -#define VERIFY_PERCPU_PTR(__p) ({ \ - __verify_pcpu_ptr((__p)); \ - (typeof(*(__p)) __kernel __force *)(__p); \ -}) - -#define per_cpu(var, cpu) (*((void)(cpu), VERIFY_PERCPU_PTR(&(var)))) -#define __get_cpu_var(var) (*VERIFY_PERCPU_PTR(&(var))) -#define __raw_get_cpu_var(var) (*VERIFY_PERCPU_PTR(&(var))) -#define this_cpu_ptr(ptr) per_cpu_ptr(ptr, 0) -#define raw_cpu_ptr(ptr) this_cpu_ptr(ptr) - #endif /* SMP */ #ifndef PER_CPU_BASE_SECTION @@ -99,25 +57,6 @@ extern void setup_per_cpu_areas(void); #endif #endif -#ifdef CONFIG_SMP - -#ifdef MODULE -#define PER_CPU_SHARED_ALIGNED_SECTION "" -#define PER_CPU_ALIGNED_SECTION "" -#else -#define PER_CPU_SHARED_ALIGNED_SECTION "..shared_aligned" -#define PER_CPU_ALIGNED_SECTION "..shared_aligned" -#endif -#define PER_CPU_FIRST_SECTION "..first" - -#else - -#define PER_CPU_SHARED_ALIGNED_SECTION "" -#define PER_CPU_ALIGNED_SECTION "..shared_aligned" -#define PER_CPU_FIRST_SECTION "" - -#endif - #ifndef PER_CPU_ATTRIBUTES #define PER_CPU_ATTRIBUTES #endif @@ -126,7 +65,4 @@ extern void setup_per_cpu_areas(void); #define PER_CPU_DEF_ATTRIBUTES #endif -/* Keep until we have removed all uses of __this_cpu_ptr */ -#define __this_cpu_ptr raw_cpu_ptr - #endif /* _ASM_GENERIC_PERCPU_H_ */ diff --git a/include/linux/percpu-defs.h b/include/linux/percpu-defs.h index a5fc7d01aad6..1a1af3e06a71 100644 --- a/include/linux/percpu-defs.h +++ b/include/linux/percpu-defs.h @@ -1,6 +1,40 @@ +/* + * linux/percpu-defs.h - basic definitions for percpu areas + * + * DO NOT INCLUDE DIRECTLY OUTSIDE PERCPU IMPLEMENTATION PROPER. + * + * This file is separate from linux/percpu.h to avoid cyclic inclusion + * dependency from arch header files. Only to be included from + * asm/percpu.h. + * + * This file includes macros necessary to declare percpu sections and + * variables, and definitions of percpu accessors and operations. It + * should provide enough percpu features to arch header files even when + * they can only include asm/percpu.h to avoid cyclic inclusion dependency. + */ + #ifndef _LINUX_PERCPU_DEFS_H #define _LINUX_PERCPU_DEFS_H +#ifdef CONFIG_SMP + +#ifdef MODULE +#define PER_CPU_SHARED_ALIGNED_SECTION "" +#define PER_CPU_ALIGNED_SECTION "" +#else +#define PER_CPU_SHARED_ALIGNED_SECTION "..shared_aligned" +#define PER_CPU_ALIGNED_SECTION "..shared_aligned" +#endif +#define PER_CPU_FIRST_SECTION "..first" + +#else + +#define PER_CPU_SHARED_ALIGNED_SECTION "" +#define PER_CPU_ALIGNED_SECTION "..shared_aligned" +#define PER_CPU_FIRST_SECTION "" + +#endif + /* * Base implementations of per-CPU variable declarations and definitions, where * the section in which the variable is to be placed is provided by the @@ -164,4 +198,59 @@ #define EXPORT_PER_CPU_SYMBOL_GPL(var) #endif +/* + * Accessors and operations. + */ +#ifndef __ASSEMBLY__ + +#ifdef CONFIG_SMP + +/* + * Add an offset to a pointer but keep the pointer as-is. Use RELOC_HIDE() + * to prevent the compiler from making incorrect assumptions about the + * pointer value. The weird cast keeps both GCC and sparse happy. + */ +#define SHIFT_PERCPU_PTR(__p, __offset) ({ \ + __verify_pcpu_ptr((__p)); \ + RELOC_HIDE((typeof(*(__p)) __kernel __force *)(__p), (__offset)); \ +}) + +/* + * A percpu variable may point to a discarded regions. The following are + * established ways to produce a usable pointer from the percpu variable + * offset. + */ +#define per_cpu(var, cpu) \ + (*SHIFT_PERCPU_PTR(&(var), per_cpu_offset(cpu))) + +#define raw_cpu_ptr(ptr) arch_raw_cpu_ptr(ptr) + +#ifdef CONFIG_DEBUG_PREEMPT +#define this_cpu_ptr(ptr) SHIFT_PERCPU_PTR(ptr, my_cpu_offset) +#else +#define this_cpu_ptr(ptr) raw_cpu_ptr(ptr) +#endif + +#define __get_cpu_var(var) (*this_cpu_ptr(&(var))) +#define __raw_get_cpu_var(var) (*raw_cpu_ptr(&(var))) + +#else /* CONFIG_SMP */ + +#define VERIFY_PERCPU_PTR(__p) ({ \ + __verify_pcpu_ptr((__p)); \ + (typeof(*(__p)) __kernel __force *)(__p); \ +}) + +#define per_cpu(var, cpu) (*((void)(cpu), VERIFY_PERCPU_PTR(&(var)))) +#define __get_cpu_var(var) (*VERIFY_PERCPU_PTR(&(var))) +#define __raw_get_cpu_var(var) (*VERIFY_PERCPU_PTR(&(var))) +#define this_cpu_ptr(ptr) per_cpu_ptr(ptr, 0) +#define raw_cpu_ptr(ptr) this_cpu_ptr(ptr) + +#endif /* CONFIG_SMP */ + +/* keep until we have removed all uses of __this_cpu_ptr */ +#define __this_cpu_ptr(ptr) raw_cpu_ptr(ptr) + +#endif /* __ASSEMBLY__ */ #endif /* _LINUX_PERCPU_DEFS_H */ -- cgit v1.2.3 From 9defda18f913181debfe7cdc8c0a752f707ac861 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 17 Jun 2014 19:12:34 -0400 Subject: percpu: move accessors from include/linux/percpu.h to percpu-defs.h include/linux/percpu-defs.h is gonna host all accessors and operations so that arch headers can make use of them too without worrying about circular dependency through include/linux/percpu.h. This patch moves the following accessors from include/linux/percpu.h to include/linux/percpu-defs.h. * get/put_cpu_var() * get/put_cpu_ptr() * per_cpu_ptr() This is pure reorgniazation. Signed-off-by: Tejun Heo Acked-by: Christoph Lameter --- include/linux/percpu-defs.h | 32 ++++++++++++++++++++++++++++++++ include/linux/percpu.h | 37 ------------------------------------- 2 files changed, 32 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/include/linux/percpu-defs.h b/include/linux/percpu-defs.h index 1a1af3e06a71..f782f98004db 100644 --- a/include/linux/percpu-defs.h +++ b/include/linux/percpu-defs.h @@ -252,5 +252,37 @@ /* keep until we have removed all uses of __this_cpu_ptr */ #define __this_cpu_ptr(ptr) raw_cpu_ptr(ptr) +/* + * Must be an lvalue. Since @var must be a simple identifier, + * we force a syntax error here if it isn't. + */ +#define get_cpu_var(var) (*({ \ + preempt_disable(); \ + this_cpu_ptr(&var); })) + +/* + * The weird & is necessary because sparse considers (void)(var) to be + * a direct dereference of percpu variable (var). + */ +#define put_cpu_var(var) do { \ + (void)&(var); \ + preempt_enable(); \ +} while (0) + +#define get_cpu_ptr(var) ({ \ + preempt_disable(); \ + this_cpu_ptr(var); }) + +#define put_cpu_ptr(var) do { \ + (void)(var); \ + preempt_enable(); \ +} while (0) + +#ifdef CONFIG_SMP +#define per_cpu_ptr(ptr, cpu) SHIFT_PERCPU_PTR((ptr), per_cpu_offset((cpu))) +#else +#define per_cpu_ptr(ptr, cpu) ({ (void)(cpu); VERIFY_PERCPU_PTR((ptr)); }) +#endif + #endif /* __ASSEMBLY__ */ #endif /* _LINUX_PERCPU_DEFS_H */ diff --git a/include/linux/percpu.h b/include/linux/percpu.h index 8419053d0f2e..97b207990c45 100644 --- a/include/linux/percpu.h +++ b/include/linux/percpu.h @@ -23,32 +23,6 @@ PERCPU_MODULE_RESERVE) #endif -/* - * Must be an lvalue. Since @var must be a simple identifier, - * we force a syntax error here if it isn't. - */ -#define get_cpu_var(var) (*({ \ - preempt_disable(); \ - this_cpu_ptr(&var); })) - -/* - * The weird & is necessary because sparse considers (void)(var) to be - * a direct dereference of percpu variable (var). - */ -#define put_cpu_var(var) do { \ - (void)&(var); \ - preempt_enable(); \ -} while (0) - -#define get_cpu_ptr(var) ({ \ - preempt_disable(); \ - this_cpu_ptr(var); }) - -#define put_cpu_ptr(var) do { \ - (void)(var); \ - preempt_enable(); \ -} while (0) - /* minimum unit size, also is the maximum supported allocation size */ #define PCPU_MIN_UNIT_SIZE PFN_ALIGN(32 << 10) @@ -140,17 +114,6 @@ extern int __init pcpu_page_first_chunk(size_t reserved_size, pcpu_fc_populate_pte_fn_t populate_pte_fn); #endif -/* - * Use this to get to a cpu's version of the per-cpu object - * dynamically allocated. Non-atomic access to the current CPU's - * version should probably be combined with get_cpu()/put_cpu(). - */ -#ifdef CONFIG_SMP -#define per_cpu_ptr(ptr, cpu) SHIFT_PERCPU_PTR((ptr), per_cpu_offset((cpu))) -#else -#define per_cpu_ptr(ptr, cpu) ({ (void)(cpu); VERIFY_PERCPU_PTR((ptr)); }) -#endif - extern void __percpu *__alloc_reserved_percpu(size_t size, size_t align); extern bool is_kernel_percpu_address(unsigned long addr); -- cgit v1.2.3 From 3b8ed91d6463f48ab180f5ebedc9663eddfa0587 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 17 Jun 2014 19:12:37 -0400 Subject: percpu: reorganize include/linux/percpu-defs.h Reorganize for better readability. * Accessor definitions are collected into one place and SMP and UP now define them in the same order. * Definitions are layered when possible - e.g. per_cpu() is now defined in terms of this_cpu_ptr(). * Rather pointless comment dropped. * per_cpu(), __raw_get_cpu_var() and __get_cpu_var() are defined in a way which can be shared between SMP and UP and moved out of CONFIG_SMP blocks. This patch doesn't introduce any functional difference. Signed-off-by: Tejun Heo Cc: Christoph Lameter --- include/linux/percpu-defs.h | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/include/linux/percpu-defs.h b/include/linux/percpu-defs.h index f782f98004db..94cd90afadac 100644 --- a/include/linux/percpu-defs.h +++ b/include/linux/percpu-defs.h @@ -215,15 +215,8 @@ RELOC_HIDE((typeof(*(__p)) __kernel __force *)(__p), (__offset)); \ }) -/* - * A percpu variable may point to a discarded regions. The following are - * established ways to produce a usable pointer from the percpu variable - * offset. - */ -#define per_cpu(var, cpu) \ - (*SHIFT_PERCPU_PTR(&(var), per_cpu_offset(cpu))) - -#define raw_cpu_ptr(ptr) arch_raw_cpu_ptr(ptr) +#define per_cpu_ptr(ptr, cpu) SHIFT_PERCPU_PTR((ptr), per_cpu_offset((cpu))) +#define raw_cpu_ptr(ptr) arch_raw_cpu_ptr(ptr) #ifdef CONFIG_DEBUG_PREEMPT #define this_cpu_ptr(ptr) SHIFT_PERCPU_PTR(ptr, my_cpu_offset) @@ -231,9 +224,6 @@ #define this_cpu_ptr(ptr) raw_cpu_ptr(ptr) #endif -#define __get_cpu_var(var) (*this_cpu_ptr(&(var))) -#define __raw_get_cpu_var(var) (*raw_cpu_ptr(&(var))) - #else /* CONFIG_SMP */ #define VERIFY_PERCPU_PTR(__p) ({ \ @@ -241,14 +231,16 @@ (typeof(*(__p)) __kernel __force *)(__p); \ }) -#define per_cpu(var, cpu) (*((void)(cpu), VERIFY_PERCPU_PTR(&(var)))) -#define __get_cpu_var(var) (*VERIFY_PERCPU_PTR(&(var))) -#define __raw_get_cpu_var(var) (*VERIFY_PERCPU_PTR(&(var))) -#define this_cpu_ptr(ptr) per_cpu_ptr(ptr, 0) -#define raw_cpu_ptr(ptr) this_cpu_ptr(ptr) +#define per_cpu_ptr(ptr, cpu) ({ (void)(cpu); VERIFY_PERCPU_PTR((ptr)); }) +#define raw_cpu_ptr(ptr) per_cpu_ptr(ptr, 0) +#define this_cpu_ptr(ptr) raw_cpu_ptr(ptr) #endif /* CONFIG_SMP */ +#define per_cpu(var, cpu) (*per_cpu_ptr(&(var), cpu)) +#define __raw_get_cpu_var(var) (*raw_cpu_ptr(&(var))) +#define __get_cpu_var(var) (*this_cpu_ptr(&(var))) + /* keep until we have removed all uses of __this_cpu_ptr */ #define __this_cpu_ptr(ptr) raw_cpu_ptr(ptr) @@ -278,11 +270,5 @@ preempt_enable(); \ } while (0) -#ifdef CONFIG_SMP -#define per_cpu_ptr(ptr, cpu) SHIFT_PERCPU_PTR((ptr), per_cpu_offset((cpu))) -#else -#define per_cpu_ptr(ptr, cpu) ({ (void)(cpu); VERIFY_PERCPU_PTR((ptr)); }) -#endif - #endif /* __ASSEMBLY__ */ #endif /* _LINUX_PERCPU_DEFS_H */ -- cgit v1.2.3 From dcba4333683c3a0642fd575e475c6c740122a037 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 17 Jun 2014 19:12:39 -0400 Subject: percpu: only allow sized arch overrides for {raw|this}_cpu_*() ops Currently, percpu allows two separate methods for overriding {raw|this}_cpu_*() ops - for a given operation, an arch can provide whole replacement or sized sub operations to override specific parts of it. e.g. arch either can provide this_cpu_add() or this_cpu_add_4() to override only the 4 byte operation. While quite flexible on a glance, the dual-overriding scheme complicates the code path for no actual gain. It compilcates the already complex operation definitions and if an arch wants to override all sizes, it can easily provide all variants anyway. In fact, no arch is actually making use of whole operation override. Another oddity is that __this_cpu_*() operations are defined in the same way as raw_cpu_*() but ignores full overrides of the raw_cpu_*() and doesn't allow full operation override, so if an arch provides whole overrides for raw_cpu_*() operations __this_cpu_*() ends up using the generic implementations. More importantly, it takes away the layering between arch-specific and generic parts making it impossible for the generic part to implement arch-independent features on top of arch-specific overrides. This patch removes the support for whole operation overrides. As no arch is using it, this doesn't cause any actual difference. Signed-off-by: Tejun Heo Acked-by: Christoph Lameter --- include/linux/percpu.h | 94 +++----------------------------------------------- 1 file changed, 5 insertions(+), 89 deletions(-) (limited to 'include') diff --git a/include/linux/percpu.h b/include/linux/percpu.h index 97b207990c45..95d380e5d246 100644 --- a/include/linux/percpu.h +++ b/include/linux/percpu.h @@ -226,17 +226,11 @@ do { \ * safe. Interrupts may occur. If the interrupt modifies the variable * too then RMW actions will not be reliable. * - * The arch code can provide optimized functions in two ways: - * - * 1. Override the function completely. F.e. define this_cpu_add(). - * The arch must then ensure that the various scalar format passed - * are handled correctly. - * - * 2. Provide functions for certain scalar sizes. F.e. provide - * this_cpu_add_2() to provide per cpu atomic operations for 2 byte - * sized RMW actions. If arch code does not provide operations for - * a scalar size then the fallback in the generic code will be - * used. + * The arch code can provide optimized implementation by defining macros + * for certain scalar sizes. F.e. provide this_cpu_add_2() to provide per + * cpu atomic operations for 2 byte sized RMW actions. If arch code does + * not provide operations for a scalar size then the fallback in the + * generic code will be used. */ #define _this_cpu_generic_read(pcp) \ @@ -247,7 +241,6 @@ do { \ ret__; \ }) -#ifndef this_cpu_read # ifndef this_cpu_read_1 # define this_cpu_read_1(pcp) _this_cpu_generic_read(pcp) # endif @@ -261,7 +254,6 @@ do { \ # define this_cpu_read_8(pcp) _this_cpu_generic_read(pcp) # endif # define this_cpu_read(pcp) __pcpu_size_call_return(this_cpu_read_, (pcp)) -#endif #define _this_cpu_generic_to_op(pcp, val, op) \ do { \ @@ -271,7 +263,6 @@ do { \ raw_local_irq_restore(flags); \ } while (0) -#ifndef this_cpu_write # ifndef this_cpu_write_1 # define this_cpu_write_1(pcp, val) _this_cpu_generic_to_op((pcp), (val), =) # endif @@ -285,9 +276,7 @@ do { \ # define this_cpu_write_8(pcp, val) _this_cpu_generic_to_op((pcp), (val), =) # endif # define this_cpu_write(pcp, val) __pcpu_size_call(this_cpu_write_, (pcp), (val)) -#endif -#ifndef this_cpu_add # ifndef this_cpu_add_1 # define this_cpu_add_1(pcp, val) _this_cpu_generic_to_op((pcp), (val), +=) # endif @@ -301,21 +290,11 @@ do { \ # define this_cpu_add_8(pcp, val) _this_cpu_generic_to_op((pcp), (val), +=) # endif # define this_cpu_add(pcp, val) __pcpu_size_call(this_cpu_add_, (pcp), (val)) -#endif -#ifndef this_cpu_sub # define this_cpu_sub(pcp, val) this_cpu_add((pcp), -(typeof(pcp))(val)) -#endif - -#ifndef this_cpu_inc # define this_cpu_inc(pcp) this_cpu_add((pcp), 1) -#endif - -#ifndef this_cpu_dec # define this_cpu_dec(pcp) this_cpu_sub((pcp), 1) -#endif -#ifndef this_cpu_and # ifndef this_cpu_and_1 # define this_cpu_and_1(pcp, val) _this_cpu_generic_to_op((pcp), (val), &=) # endif @@ -329,9 +308,7 @@ do { \ # define this_cpu_and_8(pcp, val) _this_cpu_generic_to_op((pcp), (val), &=) # endif # define this_cpu_and(pcp, val) __pcpu_size_call(this_cpu_and_, (pcp), (val)) -#endif -#ifndef this_cpu_or # ifndef this_cpu_or_1 # define this_cpu_or_1(pcp, val) _this_cpu_generic_to_op((pcp), (val), |=) # endif @@ -345,7 +322,6 @@ do { \ # define this_cpu_or_8(pcp, val) _this_cpu_generic_to_op((pcp), (val), |=) # endif # define this_cpu_or(pcp, val) __pcpu_size_call(this_cpu_or_, (pcp), (val)) -#endif #define _this_cpu_generic_add_return(pcp, val) \ ({ \ @@ -358,7 +334,6 @@ do { \ ret__; \ }) -#ifndef this_cpu_add_return # ifndef this_cpu_add_return_1 # define this_cpu_add_return_1(pcp, val) _this_cpu_generic_add_return(pcp, val) # endif @@ -372,7 +347,6 @@ do { \ # define this_cpu_add_return_8(pcp, val) _this_cpu_generic_add_return(pcp, val) # endif # define this_cpu_add_return(pcp, val) __pcpu_size_call_return2(this_cpu_add_return_, pcp, val) -#endif #define this_cpu_sub_return(pcp, val) this_cpu_add_return(pcp, -(typeof(pcp))(val)) #define this_cpu_inc_return(pcp) this_cpu_add_return(pcp, 1) @@ -388,7 +362,6 @@ do { \ ret__; \ }) -#ifndef this_cpu_xchg # ifndef this_cpu_xchg_1 # define this_cpu_xchg_1(pcp, nval) _this_cpu_generic_xchg(pcp, nval) # endif @@ -403,7 +376,6 @@ do { \ # endif # define this_cpu_xchg(pcp, nval) \ __pcpu_size_call_return2(this_cpu_xchg_, (pcp), nval) -#endif #define _this_cpu_generic_cmpxchg(pcp, oval, nval) \ ({ \ @@ -417,7 +389,6 @@ do { \ ret__; \ }) -#ifndef this_cpu_cmpxchg # ifndef this_cpu_cmpxchg_1 # define this_cpu_cmpxchg_1(pcp, oval, nval) _this_cpu_generic_cmpxchg(pcp, oval, nval) # endif @@ -432,7 +403,6 @@ do { \ # endif # define this_cpu_cmpxchg(pcp, oval, nval) \ __pcpu_size_call_return2(this_cpu_cmpxchg_, pcp, oval, nval) -#endif /* * cmpxchg_double replaces two adjacent scalars at once. The first @@ -453,7 +423,6 @@ do { \ ret__; \ }) -#ifndef this_cpu_cmpxchg_double # ifndef this_cpu_cmpxchg_double_1 # define this_cpu_cmpxchg_double_1(pcp1, pcp2, oval1, oval2, nval1, nval2) \ _this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) @@ -472,7 +441,6 @@ do { \ # endif # define this_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ __pcpu_double_call_return_bool(this_cpu_cmpxchg_double_, (pcp1), (pcp2), (oval1), (oval2), (nval1), (nval2)) -#endif /* * Generic percpu operations for contexts where we do not want to do @@ -484,7 +452,6 @@ do { \ * or an interrupt occurred and the same percpu variable was modified from * the interrupt context. */ -#ifndef raw_cpu_read # ifndef raw_cpu_read_1 # define raw_cpu_read_1(pcp) (*raw_cpu_ptr(&(pcp))) # endif @@ -498,15 +465,12 @@ do { \ # define raw_cpu_read_8(pcp) (*raw_cpu_ptr(&(pcp))) # endif # define raw_cpu_read(pcp) __pcpu_size_call_return(raw_cpu_read_, (pcp)) -#endif #define raw_cpu_generic_to_op(pcp, val, op) \ do { \ *raw_cpu_ptr(&(pcp)) op val; \ } while (0) - -#ifndef raw_cpu_write # ifndef raw_cpu_write_1 # define raw_cpu_write_1(pcp, val) raw_cpu_generic_to_op((pcp), (val), =) # endif @@ -520,9 +484,7 @@ do { \ # define raw_cpu_write_8(pcp, val) raw_cpu_generic_to_op((pcp), (val), =) # endif # define raw_cpu_write(pcp, val) __pcpu_size_call(raw_cpu_write_, (pcp), (val)) -#endif -#ifndef raw_cpu_add # ifndef raw_cpu_add_1 # define raw_cpu_add_1(pcp, val) raw_cpu_generic_to_op((pcp), (val), +=) # endif @@ -536,21 +498,13 @@ do { \ # define raw_cpu_add_8(pcp, val) raw_cpu_generic_to_op((pcp), (val), +=) # endif # define raw_cpu_add(pcp, val) __pcpu_size_call(raw_cpu_add_, (pcp), (val)) -#endif -#ifndef raw_cpu_sub # define raw_cpu_sub(pcp, val) raw_cpu_add((pcp), -(val)) -#endif -#ifndef raw_cpu_inc # define raw_cpu_inc(pcp) raw_cpu_add((pcp), 1) -#endif -#ifndef raw_cpu_dec # define raw_cpu_dec(pcp) raw_cpu_sub((pcp), 1) -#endif -#ifndef raw_cpu_and # ifndef raw_cpu_and_1 # define raw_cpu_and_1(pcp, val) raw_cpu_generic_to_op((pcp), (val), &=) # endif @@ -564,9 +518,7 @@ do { \ # define raw_cpu_and_8(pcp, val) raw_cpu_generic_to_op((pcp), (val), &=) # endif # define raw_cpu_and(pcp, val) __pcpu_size_call(raw_cpu_and_, (pcp), (val)) -#endif -#ifndef raw_cpu_or # ifndef raw_cpu_or_1 # define raw_cpu_or_1(pcp, val) raw_cpu_generic_to_op((pcp), (val), |=) # endif @@ -580,7 +532,6 @@ do { \ # define raw_cpu_or_8(pcp, val) raw_cpu_generic_to_op((pcp), (val), |=) # endif # define raw_cpu_or(pcp, val) __pcpu_size_call(raw_cpu_or_, (pcp), (val)) -#endif #define raw_cpu_generic_add_return(pcp, val) \ ({ \ @@ -588,7 +539,6 @@ do { \ raw_cpu_read(pcp); \ }) -#ifndef raw_cpu_add_return # ifndef raw_cpu_add_return_1 # define raw_cpu_add_return_1(pcp, val) raw_cpu_generic_add_return(pcp, val) # endif @@ -603,7 +553,6 @@ do { \ # endif # define raw_cpu_add_return(pcp, val) \ __pcpu_size_call_return2(raw_cpu_add_return_, pcp, val) -#endif #define raw_cpu_sub_return(pcp, val) raw_cpu_add_return(pcp, -(typeof(pcp))(val)) #define raw_cpu_inc_return(pcp) raw_cpu_add_return(pcp, 1) @@ -616,7 +565,6 @@ do { \ ret__; \ }) -#ifndef raw_cpu_xchg # ifndef raw_cpu_xchg_1 # define raw_cpu_xchg_1(pcp, nval) raw_cpu_generic_xchg(pcp, nval) # endif @@ -631,7 +579,6 @@ do { \ # endif # define raw_cpu_xchg(pcp, nval) \ __pcpu_size_call_return2(raw_cpu_xchg_, (pcp), nval) -#endif #define raw_cpu_generic_cmpxchg(pcp, oval, nval) \ ({ \ @@ -642,7 +589,6 @@ do { \ ret__; \ }) -#ifndef raw_cpu_cmpxchg # ifndef raw_cpu_cmpxchg_1 # define raw_cpu_cmpxchg_1(pcp, oval, nval) raw_cpu_generic_cmpxchg(pcp, oval, nval) # endif @@ -657,7 +603,6 @@ do { \ # endif # define raw_cpu_cmpxchg(pcp, oval, nval) \ __pcpu_size_call_return2(raw_cpu_cmpxchg_, pcp, oval, nval) -#endif #define raw_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ ({ \ @@ -671,7 +616,6 @@ do { \ (__ret); \ }) -#ifndef raw_cpu_cmpxchg_double # ifndef raw_cpu_cmpxchg_double_1 # define raw_cpu_cmpxchg_double_1(pcp1, pcp2, oval1, oval2, nval1, nval2) \ raw_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) @@ -690,79 +634,51 @@ do { \ # endif # define raw_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ __pcpu_double_call_return_bool(raw_cpu_cmpxchg_double_, (pcp1), (pcp2), (oval1), (oval2), (nval1), (nval2)) -#endif /* * Generic percpu operations for context that are safe from preemption/interrupts. */ -#ifndef __this_cpu_read # define __this_cpu_read(pcp) \ (__this_cpu_preempt_check("read"),__pcpu_size_call_return(raw_cpu_read_, (pcp))) -#endif -#ifndef __this_cpu_write # define __this_cpu_write(pcp, val) \ do { __this_cpu_preempt_check("write"); \ __pcpu_size_call(raw_cpu_write_, (pcp), (val)); \ } while (0) -#endif -#ifndef __this_cpu_add # define __this_cpu_add(pcp, val) \ do { __this_cpu_preempt_check("add"); \ __pcpu_size_call(raw_cpu_add_, (pcp), (val)); \ } while (0) -#endif -#ifndef __this_cpu_sub # define __this_cpu_sub(pcp, val) __this_cpu_add((pcp), -(typeof(pcp))(val)) -#endif - -#ifndef __this_cpu_inc # define __this_cpu_inc(pcp) __this_cpu_add((pcp), 1) -#endif - -#ifndef __this_cpu_dec # define __this_cpu_dec(pcp) __this_cpu_sub((pcp), 1) -#endif -#ifndef __this_cpu_and # define __this_cpu_and(pcp, val) \ do { __this_cpu_preempt_check("and"); \ __pcpu_size_call(raw_cpu_and_, (pcp), (val)); \ } while (0) -#endif - -#ifndef __this_cpu_or # define __this_cpu_or(pcp, val) \ do { __this_cpu_preempt_check("or"); \ __pcpu_size_call(raw_cpu_or_, (pcp), (val)); \ } while (0) -#endif -#ifndef __this_cpu_add_return # define __this_cpu_add_return(pcp, val) \ (__this_cpu_preempt_check("add_return"),__pcpu_size_call_return2(raw_cpu_add_return_, pcp, val)) -#endif #define __this_cpu_sub_return(pcp, val) __this_cpu_add_return(pcp, -(typeof(pcp))(val)) #define __this_cpu_inc_return(pcp) __this_cpu_add_return(pcp, 1) #define __this_cpu_dec_return(pcp) __this_cpu_add_return(pcp, -1) -#ifndef __this_cpu_xchg # define __this_cpu_xchg(pcp, nval) \ (__this_cpu_preempt_check("xchg"),__pcpu_size_call_return2(raw_cpu_xchg_, (pcp), nval)) -#endif -#ifndef __this_cpu_cmpxchg # define __this_cpu_cmpxchg(pcp, oval, nval) \ (__this_cpu_preempt_check("cmpxchg"),__pcpu_size_call_return2(raw_cpu_cmpxchg_, pcp, oval, nval)) -#endif -#ifndef __this_cpu_cmpxchg_double # define __this_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ (__this_cpu_preempt_check("cmpxchg_double"),__pcpu_double_call_return_bool(raw_cpu_cmpxchg_double_, (pcp1), (pcp2), (oval1), (oval2), (nval1), (nval2))) -#endif #endif /* __LINUX_PERCPU_H */ -- cgit v1.2.3 From 47b69ad673d9aa53c1d6032a6a522fc0ce8d6fc1 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 17 Jun 2014 19:12:39 -0400 Subject: percpu: move generic {raw|this}_cpu_*_N() definitions to include/asm-generic/percpu.h {raw|this}_cpu_*_N() operations are expected to be provided by archs and the generic definitions are provided as fallbacks. As such, these firmly belong to include/asm-generic/percpu.h. Move the generic definitions to include/asm-generic/percpu.h. The code is moved mostly verbatim; however, raw_cpu_*_N() are placed above this_cpu_*_N() which is more conventional as the raw operations may be used to defined other variants. This is pure reorganization. Signed-off-by: Tejun Heo Acked-by: Christoph Lameter --- include/asm-generic/percpu.h | 341 ++++++++++++++++++++++++++++++++++++++++++ include/linux/percpu.h | 344 ------------------------------------------- 2 files changed, 341 insertions(+), 344 deletions(-) (limited to 'include') diff --git a/include/asm-generic/percpu.h b/include/asm-generic/percpu.h index e5ace4d49084..932ce602128f 100644 --- a/include/asm-generic/percpu.h +++ b/include/asm-generic/percpu.h @@ -65,4 +65,345 @@ extern void setup_per_cpu_areas(void); #define PER_CPU_DEF_ATTRIBUTES #endif +# ifndef raw_cpu_read_1 +# define raw_cpu_read_1(pcp) (*raw_cpu_ptr(&(pcp))) +# endif +# ifndef raw_cpu_read_2 +# define raw_cpu_read_2(pcp) (*raw_cpu_ptr(&(pcp))) +# endif +# ifndef raw_cpu_read_4 +# define raw_cpu_read_4(pcp) (*raw_cpu_ptr(&(pcp))) +# endif +# ifndef raw_cpu_read_8 +# define raw_cpu_read_8(pcp) (*raw_cpu_ptr(&(pcp))) +# endif + +#define raw_cpu_generic_to_op(pcp, val, op) \ +do { \ + *raw_cpu_ptr(&(pcp)) op val; \ +} while (0) + +# ifndef raw_cpu_write_1 +# define raw_cpu_write_1(pcp, val) raw_cpu_generic_to_op((pcp), (val), =) +# endif +# ifndef raw_cpu_write_2 +# define raw_cpu_write_2(pcp, val) raw_cpu_generic_to_op((pcp), (val), =) +# endif +# ifndef raw_cpu_write_4 +# define raw_cpu_write_4(pcp, val) raw_cpu_generic_to_op((pcp), (val), =) +# endif +# ifndef raw_cpu_write_8 +# define raw_cpu_write_8(pcp, val) raw_cpu_generic_to_op((pcp), (val), =) +# endif + +# ifndef raw_cpu_add_1 +# define raw_cpu_add_1(pcp, val) raw_cpu_generic_to_op((pcp), (val), +=) +# endif +# ifndef raw_cpu_add_2 +# define raw_cpu_add_2(pcp, val) raw_cpu_generic_to_op((pcp), (val), +=) +# endif +# ifndef raw_cpu_add_4 +# define raw_cpu_add_4(pcp, val) raw_cpu_generic_to_op((pcp), (val), +=) +# endif +# ifndef raw_cpu_add_8 +# define raw_cpu_add_8(pcp, val) raw_cpu_generic_to_op((pcp), (val), +=) +# endif + +# ifndef raw_cpu_and_1 +# define raw_cpu_and_1(pcp, val) raw_cpu_generic_to_op((pcp), (val), &=) +# endif +# ifndef raw_cpu_and_2 +# define raw_cpu_and_2(pcp, val) raw_cpu_generic_to_op((pcp), (val), &=) +# endif +# ifndef raw_cpu_and_4 +# define raw_cpu_and_4(pcp, val) raw_cpu_generic_to_op((pcp), (val), &=) +# endif +# ifndef raw_cpu_and_8 +# define raw_cpu_and_8(pcp, val) raw_cpu_generic_to_op((pcp), (val), &=) +# endif + +# ifndef raw_cpu_or_1 +# define raw_cpu_or_1(pcp, val) raw_cpu_generic_to_op((pcp), (val), |=) +# endif +# ifndef raw_cpu_or_2 +# define raw_cpu_or_2(pcp, val) raw_cpu_generic_to_op((pcp), (val), |=) +# endif +# ifndef raw_cpu_or_4 +# define raw_cpu_or_4(pcp, val) raw_cpu_generic_to_op((pcp), (val), |=) +# endif +# ifndef raw_cpu_or_8 +# define raw_cpu_or_8(pcp, val) raw_cpu_generic_to_op((pcp), (val), |=) +# endif + +#define raw_cpu_generic_add_return(pcp, val) \ +({ \ + raw_cpu_add(pcp, val); \ + raw_cpu_read(pcp); \ +}) + +# ifndef raw_cpu_add_return_1 +# define raw_cpu_add_return_1(pcp, val) raw_cpu_generic_add_return(pcp, val) +# endif +# ifndef raw_cpu_add_return_2 +# define raw_cpu_add_return_2(pcp, val) raw_cpu_generic_add_return(pcp, val) +# endif +# ifndef raw_cpu_add_return_4 +# define raw_cpu_add_return_4(pcp, val) raw_cpu_generic_add_return(pcp, val) +# endif +# ifndef raw_cpu_add_return_8 +# define raw_cpu_add_return_8(pcp, val) raw_cpu_generic_add_return(pcp, val) +# endif + +#define raw_cpu_generic_xchg(pcp, nval) \ +({ typeof(pcp) ret__; \ + ret__ = raw_cpu_read(pcp); \ + raw_cpu_write(pcp, nval); \ + ret__; \ +}) + +# ifndef raw_cpu_xchg_1 +# define raw_cpu_xchg_1(pcp, nval) raw_cpu_generic_xchg(pcp, nval) +# endif +# ifndef raw_cpu_xchg_2 +# define raw_cpu_xchg_2(pcp, nval) raw_cpu_generic_xchg(pcp, nval) +# endif +# ifndef raw_cpu_xchg_4 +# define raw_cpu_xchg_4(pcp, nval) raw_cpu_generic_xchg(pcp, nval) +# endif +# ifndef raw_cpu_xchg_8 +# define raw_cpu_xchg_8(pcp, nval) raw_cpu_generic_xchg(pcp, nval) +# endif + +#define raw_cpu_generic_cmpxchg(pcp, oval, nval) \ +({ \ + typeof(pcp) ret__; \ + ret__ = raw_cpu_read(pcp); \ + if (ret__ == (oval)) \ + raw_cpu_write(pcp, nval); \ + ret__; \ +}) + +# ifndef raw_cpu_cmpxchg_1 +# define raw_cpu_cmpxchg_1(pcp, oval, nval) raw_cpu_generic_cmpxchg(pcp, oval, nval) +# endif +# ifndef raw_cpu_cmpxchg_2 +# define raw_cpu_cmpxchg_2(pcp, oval, nval) raw_cpu_generic_cmpxchg(pcp, oval, nval) +# endif +# ifndef raw_cpu_cmpxchg_4 +# define raw_cpu_cmpxchg_4(pcp, oval, nval) raw_cpu_generic_cmpxchg(pcp, oval, nval) +# endif +# ifndef raw_cpu_cmpxchg_8 +# define raw_cpu_cmpxchg_8(pcp, oval, nval) raw_cpu_generic_cmpxchg(pcp, oval, nval) +# endif + +#define raw_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ +({ \ + int __ret = 0; \ + if (raw_cpu_read(pcp1) == (oval1) && \ + raw_cpu_read(pcp2) == (oval2)) { \ + raw_cpu_write(pcp1, (nval1)); \ + raw_cpu_write(pcp2, (nval2)); \ + __ret = 1; \ + } \ + (__ret); \ +}) + +# ifndef raw_cpu_cmpxchg_double_1 +# define raw_cpu_cmpxchg_double_1(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + raw_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) +# endif +# ifndef raw_cpu_cmpxchg_double_2 +# define raw_cpu_cmpxchg_double_2(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + raw_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) +# endif +# ifndef raw_cpu_cmpxchg_double_4 +# define raw_cpu_cmpxchg_double_4(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + raw_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) +# endif +# ifndef raw_cpu_cmpxchg_double_8 +# define raw_cpu_cmpxchg_double_8(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + raw_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) +# endif + +#define _this_cpu_generic_read(pcp) \ +({ typeof(pcp) ret__; \ + preempt_disable(); \ + ret__ = *this_cpu_ptr(&(pcp)); \ + preempt_enable(); \ + ret__; \ +}) + +# ifndef this_cpu_read_1 +# define this_cpu_read_1(pcp) _this_cpu_generic_read(pcp) +# endif +# ifndef this_cpu_read_2 +# define this_cpu_read_2(pcp) _this_cpu_generic_read(pcp) +# endif +# ifndef this_cpu_read_4 +# define this_cpu_read_4(pcp) _this_cpu_generic_read(pcp) +# endif +# ifndef this_cpu_read_8 +# define this_cpu_read_8(pcp) _this_cpu_generic_read(pcp) +# endif + +#define _this_cpu_generic_to_op(pcp, val, op) \ +do { \ + unsigned long flags; \ + raw_local_irq_save(flags); \ + *raw_cpu_ptr(&(pcp)) op val; \ + raw_local_irq_restore(flags); \ +} while (0) + +# ifndef this_cpu_write_1 +# define this_cpu_write_1(pcp, val) _this_cpu_generic_to_op((pcp), (val), =) +# endif +# ifndef this_cpu_write_2 +# define this_cpu_write_2(pcp, val) _this_cpu_generic_to_op((pcp), (val), =) +# endif +# ifndef this_cpu_write_4 +# define this_cpu_write_4(pcp, val) _this_cpu_generic_to_op((pcp), (val), =) +# endif +# ifndef this_cpu_write_8 +# define this_cpu_write_8(pcp, val) _this_cpu_generic_to_op((pcp), (val), =) +# endif + +# ifndef this_cpu_add_1 +# define this_cpu_add_1(pcp, val) _this_cpu_generic_to_op((pcp), (val), +=) +# endif +# ifndef this_cpu_add_2 +# define this_cpu_add_2(pcp, val) _this_cpu_generic_to_op((pcp), (val), +=) +# endif +# ifndef this_cpu_add_4 +# define this_cpu_add_4(pcp, val) _this_cpu_generic_to_op((pcp), (val), +=) +# endif +# ifndef this_cpu_add_8 +# define this_cpu_add_8(pcp, val) _this_cpu_generic_to_op((pcp), (val), +=) +# endif + +# ifndef this_cpu_and_1 +# define this_cpu_and_1(pcp, val) _this_cpu_generic_to_op((pcp), (val), &=) +# endif +# ifndef this_cpu_and_2 +# define this_cpu_and_2(pcp, val) _this_cpu_generic_to_op((pcp), (val), &=) +# endif +# ifndef this_cpu_and_4 +# define this_cpu_and_4(pcp, val) _this_cpu_generic_to_op((pcp), (val), &=) +# endif +# ifndef this_cpu_and_8 +# define this_cpu_and_8(pcp, val) _this_cpu_generic_to_op((pcp), (val), &=) +# endif + +# ifndef this_cpu_or_1 +# define this_cpu_or_1(pcp, val) _this_cpu_generic_to_op((pcp), (val), |=) +# endif +# ifndef this_cpu_or_2 +# define this_cpu_or_2(pcp, val) _this_cpu_generic_to_op((pcp), (val), |=) +# endif +# ifndef this_cpu_or_4 +# define this_cpu_or_4(pcp, val) _this_cpu_generic_to_op((pcp), (val), |=) +# endif +# ifndef this_cpu_or_8 +# define this_cpu_or_8(pcp, val) _this_cpu_generic_to_op((pcp), (val), |=) +# endif + +#define _this_cpu_generic_add_return(pcp, val) \ +({ \ + typeof(pcp) ret__; \ + unsigned long flags; \ + raw_local_irq_save(flags); \ + raw_cpu_add(pcp, val); \ + ret__ = raw_cpu_read(pcp); \ + raw_local_irq_restore(flags); \ + ret__; \ +}) + +# ifndef this_cpu_add_return_1 +# define this_cpu_add_return_1(pcp, val) _this_cpu_generic_add_return(pcp, val) +# endif +# ifndef this_cpu_add_return_2 +# define this_cpu_add_return_2(pcp, val) _this_cpu_generic_add_return(pcp, val) +# endif +# ifndef this_cpu_add_return_4 +# define this_cpu_add_return_4(pcp, val) _this_cpu_generic_add_return(pcp, val) +# endif +# ifndef this_cpu_add_return_8 +# define this_cpu_add_return_8(pcp, val) _this_cpu_generic_add_return(pcp, val) +# endif + +#define _this_cpu_generic_xchg(pcp, nval) \ +({ typeof(pcp) ret__; \ + unsigned long flags; \ + raw_local_irq_save(flags); \ + ret__ = raw_cpu_read(pcp); \ + raw_cpu_write(pcp, nval); \ + raw_local_irq_restore(flags); \ + ret__; \ +}) + +# ifndef this_cpu_xchg_1 +# define this_cpu_xchg_1(pcp, nval) _this_cpu_generic_xchg(pcp, nval) +# endif +# ifndef this_cpu_xchg_2 +# define this_cpu_xchg_2(pcp, nval) _this_cpu_generic_xchg(pcp, nval) +# endif +# ifndef this_cpu_xchg_4 +# define this_cpu_xchg_4(pcp, nval) _this_cpu_generic_xchg(pcp, nval) +# endif +# ifndef this_cpu_xchg_8 +# define this_cpu_xchg_8(pcp, nval) _this_cpu_generic_xchg(pcp, nval) +# endif + +#define _this_cpu_generic_cmpxchg(pcp, oval, nval) \ +({ \ + typeof(pcp) ret__; \ + unsigned long flags; \ + raw_local_irq_save(flags); \ + ret__ = raw_cpu_read(pcp); \ + if (ret__ == (oval)) \ + raw_cpu_write(pcp, nval); \ + raw_local_irq_restore(flags); \ + ret__; \ +}) + +# ifndef this_cpu_cmpxchg_1 +# define this_cpu_cmpxchg_1(pcp, oval, nval) _this_cpu_generic_cmpxchg(pcp, oval, nval) +# endif +# ifndef this_cpu_cmpxchg_2 +# define this_cpu_cmpxchg_2(pcp, oval, nval) _this_cpu_generic_cmpxchg(pcp, oval, nval) +# endif +# ifndef this_cpu_cmpxchg_4 +# define this_cpu_cmpxchg_4(pcp, oval, nval) _this_cpu_generic_cmpxchg(pcp, oval, nval) +# endif +# ifndef this_cpu_cmpxchg_8 +# define this_cpu_cmpxchg_8(pcp, oval, nval) _this_cpu_generic_cmpxchg(pcp, oval, nval) +# endif + +#define _this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ +({ \ + int ret__; \ + unsigned long flags; \ + raw_local_irq_save(flags); \ + ret__ = raw_cpu_generic_cmpxchg_double(pcp1, pcp2, \ + oval1, oval2, nval1, nval2); \ + raw_local_irq_restore(flags); \ + ret__; \ +}) + +# ifndef this_cpu_cmpxchg_double_1 +# define this_cpu_cmpxchg_double_1(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + _this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) +# endif +# ifndef this_cpu_cmpxchg_double_2 +# define this_cpu_cmpxchg_double_2(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + _this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) +# endif +# ifndef this_cpu_cmpxchg_double_4 +# define this_cpu_cmpxchg_double_4(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + _this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) +# endif +# ifndef this_cpu_cmpxchg_double_8 +# define this_cpu_cmpxchg_double_8(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + _this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) +# endif + #endif /* _ASM_GENERIC_PERCPU_H_ */ diff --git a/include/linux/percpu.h b/include/linux/percpu.h index 95d380e5d246..20b953532596 100644 --- a/include/linux/percpu.h +++ b/include/linux/percpu.h @@ -233,174 +233,20 @@ do { \ * generic code will be used. */ -#define _this_cpu_generic_read(pcp) \ -({ typeof(pcp) ret__; \ - preempt_disable(); \ - ret__ = *this_cpu_ptr(&(pcp)); \ - preempt_enable(); \ - ret__; \ -}) - -# ifndef this_cpu_read_1 -# define this_cpu_read_1(pcp) _this_cpu_generic_read(pcp) -# endif -# ifndef this_cpu_read_2 -# define this_cpu_read_2(pcp) _this_cpu_generic_read(pcp) -# endif -# ifndef this_cpu_read_4 -# define this_cpu_read_4(pcp) _this_cpu_generic_read(pcp) -# endif -# ifndef this_cpu_read_8 -# define this_cpu_read_8(pcp) _this_cpu_generic_read(pcp) -# endif # define this_cpu_read(pcp) __pcpu_size_call_return(this_cpu_read_, (pcp)) - -#define _this_cpu_generic_to_op(pcp, val, op) \ -do { \ - unsigned long flags; \ - raw_local_irq_save(flags); \ - *raw_cpu_ptr(&(pcp)) op val; \ - raw_local_irq_restore(flags); \ -} while (0) - -# ifndef this_cpu_write_1 -# define this_cpu_write_1(pcp, val) _this_cpu_generic_to_op((pcp), (val), =) -# endif -# ifndef this_cpu_write_2 -# define this_cpu_write_2(pcp, val) _this_cpu_generic_to_op((pcp), (val), =) -# endif -# ifndef this_cpu_write_4 -# define this_cpu_write_4(pcp, val) _this_cpu_generic_to_op((pcp), (val), =) -# endif -# ifndef this_cpu_write_8 -# define this_cpu_write_8(pcp, val) _this_cpu_generic_to_op((pcp), (val), =) -# endif # define this_cpu_write(pcp, val) __pcpu_size_call(this_cpu_write_, (pcp), (val)) - -# ifndef this_cpu_add_1 -# define this_cpu_add_1(pcp, val) _this_cpu_generic_to_op((pcp), (val), +=) -# endif -# ifndef this_cpu_add_2 -# define this_cpu_add_2(pcp, val) _this_cpu_generic_to_op((pcp), (val), +=) -# endif -# ifndef this_cpu_add_4 -# define this_cpu_add_4(pcp, val) _this_cpu_generic_to_op((pcp), (val), +=) -# endif -# ifndef this_cpu_add_8 -# define this_cpu_add_8(pcp, val) _this_cpu_generic_to_op((pcp), (val), +=) -# endif # define this_cpu_add(pcp, val) __pcpu_size_call(this_cpu_add_, (pcp), (val)) - # define this_cpu_sub(pcp, val) this_cpu_add((pcp), -(typeof(pcp))(val)) # define this_cpu_inc(pcp) this_cpu_add((pcp), 1) # define this_cpu_dec(pcp) this_cpu_sub((pcp), 1) - -# ifndef this_cpu_and_1 -# define this_cpu_and_1(pcp, val) _this_cpu_generic_to_op((pcp), (val), &=) -# endif -# ifndef this_cpu_and_2 -# define this_cpu_and_2(pcp, val) _this_cpu_generic_to_op((pcp), (val), &=) -# endif -# ifndef this_cpu_and_4 -# define this_cpu_and_4(pcp, val) _this_cpu_generic_to_op((pcp), (val), &=) -# endif -# ifndef this_cpu_and_8 -# define this_cpu_and_8(pcp, val) _this_cpu_generic_to_op((pcp), (val), &=) -# endif # define this_cpu_and(pcp, val) __pcpu_size_call(this_cpu_and_, (pcp), (val)) - -# ifndef this_cpu_or_1 -# define this_cpu_or_1(pcp, val) _this_cpu_generic_to_op((pcp), (val), |=) -# endif -# ifndef this_cpu_or_2 -# define this_cpu_or_2(pcp, val) _this_cpu_generic_to_op((pcp), (val), |=) -# endif -# ifndef this_cpu_or_4 -# define this_cpu_or_4(pcp, val) _this_cpu_generic_to_op((pcp), (val), |=) -# endif -# ifndef this_cpu_or_8 -# define this_cpu_or_8(pcp, val) _this_cpu_generic_to_op((pcp), (val), |=) -# endif # define this_cpu_or(pcp, val) __pcpu_size_call(this_cpu_or_, (pcp), (val)) - -#define _this_cpu_generic_add_return(pcp, val) \ -({ \ - typeof(pcp) ret__; \ - unsigned long flags; \ - raw_local_irq_save(flags); \ - raw_cpu_add(pcp, val); \ - ret__ = raw_cpu_read(pcp); \ - raw_local_irq_restore(flags); \ - ret__; \ -}) - -# ifndef this_cpu_add_return_1 -# define this_cpu_add_return_1(pcp, val) _this_cpu_generic_add_return(pcp, val) -# endif -# ifndef this_cpu_add_return_2 -# define this_cpu_add_return_2(pcp, val) _this_cpu_generic_add_return(pcp, val) -# endif -# ifndef this_cpu_add_return_4 -# define this_cpu_add_return_4(pcp, val) _this_cpu_generic_add_return(pcp, val) -# endif -# ifndef this_cpu_add_return_8 -# define this_cpu_add_return_8(pcp, val) _this_cpu_generic_add_return(pcp, val) -# endif # define this_cpu_add_return(pcp, val) __pcpu_size_call_return2(this_cpu_add_return_, pcp, val) - #define this_cpu_sub_return(pcp, val) this_cpu_add_return(pcp, -(typeof(pcp))(val)) #define this_cpu_inc_return(pcp) this_cpu_add_return(pcp, 1) #define this_cpu_dec_return(pcp) this_cpu_add_return(pcp, -1) - -#define _this_cpu_generic_xchg(pcp, nval) \ -({ typeof(pcp) ret__; \ - unsigned long flags; \ - raw_local_irq_save(flags); \ - ret__ = raw_cpu_read(pcp); \ - raw_cpu_write(pcp, nval); \ - raw_local_irq_restore(flags); \ - ret__; \ -}) - -# ifndef this_cpu_xchg_1 -# define this_cpu_xchg_1(pcp, nval) _this_cpu_generic_xchg(pcp, nval) -# endif -# ifndef this_cpu_xchg_2 -# define this_cpu_xchg_2(pcp, nval) _this_cpu_generic_xchg(pcp, nval) -# endif -# ifndef this_cpu_xchg_4 -# define this_cpu_xchg_4(pcp, nval) _this_cpu_generic_xchg(pcp, nval) -# endif -# ifndef this_cpu_xchg_8 -# define this_cpu_xchg_8(pcp, nval) _this_cpu_generic_xchg(pcp, nval) -# endif # define this_cpu_xchg(pcp, nval) \ __pcpu_size_call_return2(this_cpu_xchg_, (pcp), nval) - -#define _this_cpu_generic_cmpxchg(pcp, oval, nval) \ -({ \ - typeof(pcp) ret__; \ - unsigned long flags; \ - raw_local_irq_save(flags); \ - ret__ = raw_cpu_read(pcp); \ - if (ret__ == (oval)) \ - raw_cpu_write(pcp, nval); \ - raw_local_irq_restore(flags); \ - ret__; \ -}) - -# ifndef this_cpu_cmpxchg_1 -# define this_cpu_cmpxchg_1(pcp, oval, nval) _this_cpu_generic_cmpxchg(pcp, oval, nval) -# endif -# ifndef this_cpu_cmpxchg_2 -# define this_cpu_cmpxchg_2(pcp, oval, nval) _this_cpu_generic_cmpxchg(pcp, oval, nval) -# endif -# ifndef this_cpu_cmpxchg_4 -# define this_cpu_cmpxchg_4(pcp, oval, nval) _this_cpu_generic_cmpxchg(pcp, oval, nval) -# endif -# ifndef this_cpu_cmpxchg_8 -# define this_cpu_cmpxchg_8(pcp, oval, nval) _this_cpu_generic_cmpxchg(pcp, oval, nval) -# endif # define this_cpu_cmpxchg(pcp, oval, nval) \ __pcpu_size_call_return2(this_cpu_cmpxchg_, pcp, oval, nval) @@ -412,33 +258,6 @@ do { \ * very limited hardware support for these operations, so only certain * sizes may work. */ -#define _this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ -({ \ - int ret__; \ - unsigned long flags; \ - raw_local_irq_save(flags); \ - ret__ = raw_cpu_generic_cmpxchg_double(pcp1, pcp2, \ - oval1, oval2, nval1, nval2); \ - raw_local_irq_restore(flags); \ - ret__; \ -}) - -# ifndef this_cpu_cmpxchg_double_1 -# define this_cpu_cmpxchg_double_1(pcp1, pcp2, oval1, oval2, nval1, nval2) \ - _this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) -# endif -# ifndef this_cpu_cmpxchg_double_2 -# define this_cpu_cmpxchg_double_2(pcp1, pcp2, oval1, oval2, nval1, nval2) \ - _this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) -# endif -# ifndef this_cpu_cmpxchg_double_4 -# define this_cpu_cmpxchg_double_4(pcp1, pcp2, oval1, oval2, nval1, nval2) \ - _this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) -# endif -# ifndef this_cpu_cmpxchg_double_8 -# define this_cpu_cmpxchg_double_8(pcp1, pcp2, oval1, oval2, nval1, nval2) \ - _this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) -# endif # define this_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ __pcpu_double_call_return_bool(this_cpu_cmpxchg_double_, (pcp1), (pcp2), (oval1), (oval2), (nval1), (nval2)) @@ -452,186 +271,23 @@ do { \ * or an interrupt occurred and the same percpu variable was modified from * the interrupt context. */ -# ifndef raw_cpu_read_1 -# define raw_cpu_read_1(pcp) (*raw_cpu_ptr(&(pcp))) -# endif -# ifndef raw_cpu_read_2 -# define raw_cpu_read_2(pcp) (*raw_cpu_ptr(&(pcp))) -# endif -# ifndef raw_cpu_read_4 -# define raw_cpu_read_4(pcp) (*raw_cpu_ptr(&(pcp))) -# endif -# ifndef raw_cpu_read_8 -# define raw_cpu_read_8(pcp) (*raw_cpu_ptr(&(pcp))) -# endif # define raw_cpu_read(pcp) __pcpu_size_call_return(raw_cpu_read_, (pcp)) - -#define raw_cpu_generic_to_op(pcp, val, op) \ -do { \ - *raw_cpu_ptr(&(pcp)) op val; \ -} while (0) - -# ifndef raw_cpu_write_1 -# define raw_cpu_write_1(pcp, val) raw_cpu_generic_to_op((pcp), (val), =) -# endif -# ifndef raw_cpu_write_2 -# define raw_cpu_write_2(pcp, val) raw_cpu_generic_to_op((pcp), (val), =) -# endif -# ifndef raw_cpu_write_4 -# define raw_cpu_write_4(pcp, val) raw_cpu_generic_to_op((pcp), (val), =) -# endif -# ifndef raw_cpu_write_8 -# define raw_cpu_write_8(pcp, val) raw_cpu_generic_to_op((pcp), (val), =) -# endif # define raw_cpu_write(pcp, val) __pcpu_size_call(raw_cpu_write_, (pcp), (val)) - -# ifndef raw_cpu_add_1 -# define raw_cpu_add_1(pcp, val) raw_cpu_generic_to_op((pcp), (val), +=) -# endif -# ifndef raw_cpu_add_2 -# define raw_cpu_add_2(pcp, val) raw_cpu_generic_to_op((pcp), (val), +=) -# endif -# ifndef raw_cpu_add_4 -# define raw_cpu_add_4(pcp, val) raw_cpu_generic_to_op((pcp), (val), +=) -# endif -# ifndef raw_cpu_add_8 -# define raw_cpu_add_8(pcp, val) raw_cpu_generic_to_op((pcp), (val), +=) -# endif # define raw_cpu_add(pcp, val) __pcpu_size_call(raw_cpu_add_, (pcp), (val)) - # define raw_cpu_sub(pcp, val) raw_cpu_add((pcp), -(val)) - # define raw_cpu_inc(pcp) raw_cpu_add((pcp), 1) - # define raw_cpu_dec(pcp) raw_cpu_sub((pcp), 1) - -# ifndef raw_cpu_and_1 -# define raw_cpu_and_1(pcp, val) raw_cpu_generic_to_op((pcp), (val), &=) -# endif -# ifndef raw_cpu_and_2 -# define raw_cpu_and_2(pcp, val) raw_cpu_generic_to_op((pcp), (val), &=) -# endif -# ifndef raw_cpu_and_4 -# define raw_cpu_and_4(pcp, val) raw_cpu_generic_to_op((pcp), (val), &=) -# endif -# ifndef raw_cpu_and_8 -# define raw_cpu_and_8(pcp, val) raw_cpu_generic_to_op((pcp), (val), &=) -# endif # define raw_cpu_and(pcp, val) __pcpu_size_call(raw_cpu_and_, (pcp), (val)) - -# ifndef raw_cpu_or_1 -# define raw_cpu_or_1(pcp, val) raw_cpu_generic_to_op((pcp), (val), |=) -# endif -# ifndef raw_cpu_or_2 -# define raw_cpu_or_2(pcp, val) raw_cpu_generic_to_op((pcp), (val), |=) -# endif -# ifndef raw_cpu_or_4 -# define raw_cpu_or_4(pcp, val) raw_cpu_generic_to_op((pcp), (val), |=) -# endif -# ifndef raw_cpu_or_8 -# define raw_cpu_or_8(pcp, val) raw_cpu_generic_to_op((pcp), (val), |=) -# endif # define raw_cpu_or(pcp, val) __pcpu_size_call(raw_cpu_or_, (pcp), (val)) - -#define raw_cpu_generic_add_return(pcp, val) \ -({ \ - raw_cpu_add(pcp, val); \ - raw_cpu_read(pcp); \ -}) - -# ifndef raw_cpu_add_return_1 -# define raw_cpu_add_return_1(pcp, val) raw_cpu_generic_add_return(pcp, val) -# endif -# ifndef raw_cpu_add_return_2 -# define raw_cpu_add_return_2(pcp, val) raw_cpu_generic_add_return(pcp, val) -# endif -# ifndef raw_cpu_add_return_4 -# define raw_cpu_add_return_4(pcp, val) raw_cpu_generic_add_return(pcp, val) -# endif -# ifndef raw_cpu_add_return_8 -# define raw_cpu_add_return_8(pcp, val) raw_cpu_generic_add_return(pcp, val) -# endif # define raw_cpu_add_return(pcp, val) \ __pcpu_size_call_return2(raw_cpu_add_return_, pcp, val) - #define raw_cpu_sub_return(pcp, val) raw_cpu_add_return(pcp, -(typeof(pcp))(val)) #define raw_cpu_inc_return(pcp) raw_cpu_add_return(pcp, 1) #define raw_cpu_dec_return(pcp) raw_cpu_add_return(pcp, -1) - -#define raw_cpu_generic_xchg(pcp, nval) \ -({ typeof(pcp) ret__; \ - ret__ = raw_cpu_read(pcp); \ - raw_cpu_write(pcp, nval); \ - ret__; \ -}) - -# ifndef raw_cpu_xchg_1 -# define raw_cpu_xchg_1(pcp, nval) raw_cpu_generic_xchg(pcp, nval) -# endif -# ifndef raw_cpu_xchg_2 -# define raw_cpu_xchg_2(pcp, nval) raw_cpu_generic_xchg(pcp, nval) -# endif -# ifndef raw_cpu_xchg_4 -# define raw_cpu_xchg_4(pcp, nval) raw_cpu_generic_xchg(pcp, nval) -# endif -# ifndef raw_cpu_xchg_8 -# define raw_cpu_xchg_8(pcp, nval) raw_cpu_generic_xchg(pcp, nval) -# endif # define raw_cpu_xchg(pcp, nval) \ __pcpu_size_call_return2(raw_cpu_xchg_, (pcp), nval) - -#define raw_cpu_generic_cmpxchg(pcp, oval, nval) \ -({ \ - typeof(pcp) ret__; \ - ret__ = raw_cpu_read(pcp); \ - if (ret__ == (oval)) \ - raw_cpu_write(pcp, nval); \ - ret__; \ -}) - -# ifndef raw_cpu_cmpxchg_1 -# define raw_cpu_cmpxchg_1(pcp, oval, nval) raw_cpu_generic_cmpxchg(pcp, oval, nval) -# endif -# ifndef raw_cpu_cmpxchg_2 -# define raw_cpu_cmpxchg_2(pcp, oval, nval) raw_cpu_generic_cmpxchg(pcp, oval, nval) -# endif -# ifndef raw_cpu_cmpxchg_4 -# define raw_cpu_cmpxchg_4(pcp, oval, nval) raw_cpu_generic_cmpxchg(pcp, oval, nval) -# endif -# ifndef raw_cpu_cmpxchg_8 -# define raw_cpu_cmpxchg_8(pcp, oval, nval) raw_cpu_generic_cmpxchg(pcp, oval, nval) -# endif # define raw_cpu_cmpxchg(pcp, oval, nval) \ __pcpu_size_call_return2(raw_cpu_cmpxchg_, pcp, oval, nval) - -#define raw_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ -({ \ - int __ret = 0; \ - if (raw_cpu_read(pcp1) == (oval1) && \ - raw_cpu_read(pcp2) == (oval2)) { \ - raw_cpu_write(pcp1, (nval1)); \ - raw_cpu_write(pcp2, (nval2)); \ - __ret = 1; \ - } \ - (__ret); \ -}) - -# ifndef raw_cpu_cmpxchg_double_1 -# define raw_cpu_cmpxchg_double_1(pcp1, pcp2, oval1, oval2, nval1, nval2) \ - raw_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) -# endif -# ifndef raw_cpu_cmpxchg_double_2 -# define raw_cpu_cmpxchg_double_2(pcp1, pcp2, oval1, oval2, nval1, nval2) \ - raw_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) -# endif -# ifndef raw_cpu_cmpxchg_double_4 -# define raw_cpu_cmpxchg_double_4(pcp1, pcp2, oval1, oval2, nval1, nval2) \ - raw_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) -# endif -# ifndef raw_cpu_cmpxchg_double_8 -# define raw_cpu_cmpxchg_double_8(pcp1, pcp2, oval1, oval2, nval1, nval2) \ - raw_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) -# endif # define raw_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ __pcpu_double_call_return_bool(raw_cpu_cmpxchg_double_, (pcp1), (pcp2), (oval1), (oval2), (nval1), (nval2)) -- cgit v1.2.3 From a32f8d8eda8bd49017ac5f88e2b859f1f582557f Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 17 Jun 2014 19:12:39 -0400 Subject: percpu: move {raw|this}_cpu_*() definitions to include/linux/percpu-defs.h We're in the process of moving all percpu accessors and operations to include/linux/percpu-defs.h so that they're available to arch headers without having to include full include/linux/percpu.h which may cause cyclic inclusion dependency. This patch moves {raw|this}_cpu_*() definitions from include/linux/percpu.h to include/linux/percpu-defs.h. The code is moved mostly verbatim; however, raw_cpu_*() are placed above this_cpu_*() which is more conventional as the raw operations may be used to defined other variants. This is pure reorganization. Signed-off-by: Tejun Heo Acked-by: Christoph Lameter --- include/linux/percpu-defs.h | 209 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/percpu.h | 208 ------------------------------------------- 2 files changed, 209 insertions(+), 208 deletions(-) (limited to 'include') diff --git a/include/linux/percpu-defs.h b/include/linux/percpu-defs.h index 94cd90afadac..6710eb9555fa 100644 --- a/include/linux/percpu-defs.h +++ b/include/linux/percpu-defs.h @@ -270,5 +270,214 @@ preempt_enable(); \ } while (0) +/* + * Branching function to split up a function into a set of functions that + * are called for different scalar sizes of the objects handled. + */ + +extern void __bad_size_call_parameter(void); + +#ifdef CONFIG_DEBUG_PREEMPT +extern void __this_cpu_preempt_check(const char *op); +#else +static inline void __this_cpu_preempt_check(const char *op) { } +#endif + +#define __pcpu_size_call_return(stem, variable) \ +({ typeof(variable) pscr_ret__; \ + __verify_pcpu_ptr(&(variable)); \ + switch(sizeof(variable)) { \ + case 1: pscr_ret__ = stem##1(variable);break; \ + case 2: pscr_ret__ = stem##2(variable);break; \ + case 4: pscr_ret__ = stem##4(variable);break; \ + case 8: pscr_ret__ = stem##8(variable);break; \ + default: \ + __bad_size_call_parameter();break; \ + } \ + pscr_ret__; \ +}) + +#define __pcpu_size_call_return2(stem, variable, ...) \ +({ \ + typeof(variable) pscr2_ret__; \ + __verify_pcpu_ptr(&(variable)); \ + switch(sizeof(variable)) { \ + case 1: pscr2_ret__ = stem##1(variable, __VA_ARGS__); break; \ + case 2: pscr2_ret__ = stem##2(variable, __VA_ARGS__); break; \ + case 4: pscr2_ret__ = stem##4(variable, __VA_ARGS__); break; \ + case 8: pscr2_ret__ = stem##8(variable, __VA_ARGS__); break; \ + default: \ + __bad_size_call_parameter(); break; \ + } \ + pscr2_ret__; \ +}) + +/* + * Special handling for cmpxchg_double. cmpxchg_double is passed two + * percpu variables. The first has to be aligned to a double word + * boundary and the second has to follow directly thereafter. + * We enforce this on all architectures even if they don't support + * a double cmpxchg instruction, since it's a cheap requirement, and it + * avoids breaking the requirement for architectures with the instruction. + */ +#define __pcpu_double_call_return_bool(stem, pcp1, pcp2, ...) \ +({ \ + bool pdcrb_ret__; \ + __verify_pcpu_ptr(&pcp1); \ + BUILD_BUG_ON(sizeof(pcp1) != sizeof(pcp2)); \ + VM_BUG_ON((unsigned long)(&pcp1) % (2 * sizeof(pcp1))); \ + VM_BUG_ON((unsigned long)(&pcp2) != \ + (unsigned long)(&pcp1) + sizeof(pcp1)); \ + switch(sizeof(pcp1)) { \ + case 1: pdcrb_ret__ = stem##1(pcp1, pcp2, __VA_ARGS__); break; \ + case 2: pdcrb_ret__ = stem##2(pcp1, pcp2, __VA_ARGS__); break; \ + case 4: pdcrb_ret__ = stem##4(pcp1, pcp2, __VA_ARGS__); break; \ + case 8: pdcrb_ret__ = stem##8(pcp1, pcp2, __VA_ARGS__); break; \ + default: \ + __bad_size_call_parameter(); break; \ + } \ + pdcrb_ret__; \ +}) + +#define __pcpu_size_call(stem, variable, ...) \ +do { \ + __verify_pcpu_ptr(&(variable)); \ + switch(sizeof(variable)) { \ + case 1: stem##1(variable, __VA_ARGS__);break; \ + case 2: stem##2(variable, __VA_ARGS__);break; \ + case 4: stem##4(variable, __VA_ARGS__);break; \ + case 8: stem##8(variable, __VA_ARGS__);break; \ + default: \ + __bad_size_call_parameter();break; \ + } \ +} while (0) + +/* + * this_cpu operations (C) 2008-2013 Christoph Lameter + * + * Optimized manipulation for memory allocated through the per cpu + * allocator or for addresses of per cpu variables. + * + * These operation guarantee exclusivity of access for other operations + * on the *same* processor. The assumption is that per cpu data is only + * accessed by a single processor instance (the current one). + * + * The arch code can provide optimized implementation by defining macros + * for certain scalar sizes. F.e. provide this_cpu_add_2() to provide per + * cpu atomic operations for 2 byte sized RMW actions. If arch code does + * not provide operations for a scalar size then the fallback in the + * generic code will be used. + */ + +/* + * Generic percpu operations for contexts where we do not want to do + * any checks for preemptiosn. + * + * If there is no other protection through preempt disable and/or + * disabling interupts then one of these RMW operations can show unexpected + * behavior because the execution thread was rescheduled on another processor + * or an interrupt occurred and the same percpu variable was modified from + * the interrupt context. + */ +# define raw_cpu_read(pcp) __pcpu_size_call_return(raw_cpu_read_, (pcp)) +# define raw_cpu_write(pcp, val) __pcpu_size_call(raw_cpu_write_, (pcp), (val)) +# define raw_cpu_add(pcp, val) __pcpu_size_call(raw_cpu_add_, (pcp), (val)) +# define raw_cpu_sub(pcp, val) raw_cpu_add((pcp), -(val)) +# define raw_cpu_inc(pcp) raw_cpu_add((pcp), 1) +# define raw_cpu_dec(pcp) raw_cpu_sub((pcp), 1) +# define raw_cpu_and(pcp, val) __pcpu_size_call(raw_cpu_and_, (pcp), (val)) +# define raw_cpu_or(pcp, val) __pcpu_size_call(raw_cpu_or_, (pcp), (val)) +# define raw_cpu_add_return(pcp, val) \ + __pcpu_size_call_return2(raw_cpu_add_return_, pcp, val) +#define raw_cpu_sub_return(pcp, val) raw_cpu_add_return(pcp, -(typeof(pcp))(val)) +#define raw_cpu_inc_return(pcp) raw_cpu_add_return(pcp, 1) +#define raw_cpu_dec_return(pcp) raw_cpu_add_return(pcp, -1) +# define raw_cpu_xchg(pcp, nval) \ + __pcpu_size_call_return2(raw_cpu_xchg_, (pcp), nval) +# define raw_cpu_cmpxchg(pcp, oval, nval) \ + __pcpu_size_call_return2(raw_cpu_cmpxchg_, pcp, oval, nval) +# define raw_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + __pcpu_double_call_return_bool(raw_cpu_cmpxchg_double_, (pcp1), (pcp2), (oval1), (oval2), (nval1), (nval2)) + +/* + * Generic percpu operations for context that are safe from preemption/interrupts. + */ +# define __this_cpu_read(pcp) \ + (__this_cpu_preempt_check("read"),__pcpu_size_call_return(raw_cpu_read_, (pcp))) + +# define __this_cpu_write(pcp, val) \ +do { __this_cpu_preempt_check("write"); \ + __pcpu_size_call(raw_cpu_write_, (pcp), (val)); \ +} while (0) + +# define __this_cpu_add(pcp, val) \ +do { __this_cpu_preempt_check("add"); \ + __pcpu_size_call(raw_cpu_add_, (pcp), (val)); \ +} while (0) + +# define __this_cpu_sub(pcp, val) __this_cpu_add((pcp), -(typeof(pcp))(val)) +# define __this_cpu_inc(pcp) __this_cpu_add((pcp), 1) +# define __this_cpu_dec(pcp) __this_cpu_sub((pcp), 1) + +# define __this_cpu_and(pcp, val) \ +do { __this_cpu_preempt_check("and"); \ + __pcpu_size_call(raw_cpu_and_, (pcp), (val)); \ +} while (0) + +# define __this_cpu_or(pcp, val) \ +do { __this_cpu_preempt_check("or"); \ + __pcpu_size_call(raw_cpu_or_, (pcp), (val)); \ +} while (0) + +# define __this_cpu_add_return(pcp, val) \ + (__this_cpu_preempt_check("add_return"),__pcpu_size_call_return2(raw_cpu_add_return_, pcp, val)) + +#define __this_cpu_sub_return(pcp, val) __this_cpu_add_return(pcp, -(typeof(pcp))(val)) +#define __this_cpu_inc_return(pcp) __this_cpu_add_return(pcp, 1) +#define __this_cpu_dec_return(pcp) __this_cpu_add_return(pcp, -1) + +# define __this_cpu_xchg(pcp, nval) \ + (__this_cpu_preempt_check("xchg"),__pcpu_size_call_return2(raw_cpu_xchg_, (pcp), nval)) + +# define __this_cpu_cmpxchg(pcp, oval, nval) \ + (__this_cpu_preempt_check("cmpxchg"),__pcpu_size_call_return2(raw_cpu_cmpxchg_, pcp, oval, nval)) + +# define __this_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + (__this_cpu_preempt_check("cmpxchg_double"),__pcpu_double_call_return_bool(raw_cpu_cmpxchg_double_, (pcp1), (pcp2), (oval1), (oval2), (nval1), (nval2))) + +/* + * this_cpu_*() operations are used for accesses that must be done in a + * preemption safe way since we know that the context is not preempt + * safe. Interrupts may occur. If the interrupt modifies the variable too + * then RMW actions will not be reliable. + */ +# define this_cpu_read(pcp) __pcpu_size_call_return(this_cpu_read_, (pcp)) +# define this_cpu_write(pcp, val) __pcpu_size_call(this_cpu_write_, (pcp), (val)) +# define this_cpu_add(pcp, val) __pcpu_size_call(this_cpu_add_, (pcp), (val)) +# define this_cpu_sub(pcp, val) this_cpu_add((pcp), -(typeof(pcp))(val)) +# define this_cpu_inc(pcp) this_cpu_add((pcp), 1) +# define this_cpu_dec(pcp) this_cpu_sub((pcp), 1) +# define this_cpu_and(pcp, val) __pcpu_size_call(this_cpu_and_, (pcp), (val)) +# define this_cpu_or(pcp, val) __pcpu_size_call(this_cpu_or_, (pcp), (val)) +# define this_cpu_add_return(pcp, val) __pcpu_size_call_return2(this_cpu_add_return_, pcp, val) +#define this_cpu_sub_return(pcp, val) this_cpu_add_return(pcp, -(typeof(pcp))(val)) +#define this_cpu_inc_return(pcp) this_cpu_add_return(pcp, 1) +#define this_cpu_dec_return(pcp) this_cpu_add_return(pcp, -1) +# define this_cpu_xchg(pcp, nval) \ + __pcpu_size_call_return2(this_cpu_xchg_, (pcp), nval) +# define this_cpu_cmpxchg(pcp, oval, nval) \ + __pcpu_size_call_return2(this_cpu_cmpxchg_, pcp, oval, nval) + +/* + * cmpxchg_double replaces two adjacent scalars at once. The first + * two parameters are per cpu variables which have to be of the same + * size. A truth value is returned to indicate success or failure + * (since a double register result is difficult to handle). There is + * very limited hardware support for these operations, so only certain + * sizes may work. + */ +# define this_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + __pcpu_double_call_return_bool(this_cpu_cmpxchg_double_, (pcp1), (pcp2), (oval1), (oval2), (nval1), (nval2)) + #endif /* __ASSEMBLY__ */ #endif /* _LINUX_PERCPU_DEFS_H */ diff --git a/include/linux/percpu.h b/include/linux/percpu.h index 20b953532596..6f61b61b7996 100644 --- a/include/linux/percpu.h +++ b/include/linux/percpu.h @@ -129,212 +129,4 @@ extern phys_addr_t per_cpu_ptr_to_phys(void *addr); #define alloc_percpu(type) \ (typeof(type) __percpu *)__alloc_percpu(sizeof(type), __alignof__(type)) -/* - * Branching function to split up a function into a set of functions that - * are called for different scalar sizes of the objects handled. - */ - -extern void __bad_size_call_parameter(void); - -#ifdef CONFIG_DEBUG_PREEMPT -extern void __this_cpu_preempt_check(const char *op); -#else -static inline void __this_cpu_preempt_check(const char *op) { } -#endif - -#define __pcpu_size_call_return(stem, variable) \ -({ typeof(variable) pscr_ret__; \ - __verify_pcpu_ptr(&(variable)); \ - switch(sizeof(variable)) { \ - case 1: pscr_ret__ = stem##1(variable);break; \ - case 2: pscr_ret__ = stem##2(variable);break; \ - case 4: pscr_ret__ = stem##4(variable);break; \ - case 8: pscr_ret__ = stem##8(variable);break; \ - default: \ - __bad_size_call_parameter();break; \ - } \ - pscr_ret__; \ -}) - -#define __pcpu_size_call_return2(stem, variable, ...) \ -({ \ - typeof(variable) pscr2_ret__; \ - __verify_pcpu_ptr(&(variable)); \ - switch(sizeof(variable)) { \ - case 1: pscr2_ret__ = stem##1(variable, __VA_ARGS__); break; \ - case 2: pscr2_ret__ = stem##2(variable, __VA_ARGS__); break; \ - case 4: pscr2_ret__ = stem##4(variable, __VA_ARGS__); break; \ - case 8: pscr2_ret__ = stem##8(variable, __VA_ARGS__); break; \ - default: \ - __bad_size_call_parameter(); break; \ - } \ - pscr2_ret__; \ -}) - -/* - * Special handling for cmpxchg_double. cmpxchg_double is passed two - * percpu variables. The first has to be aligned to a double word - * boundary and the second has to follow directly thereafter. - * We enforce this on all architectures even if they don't support - * a double cmpxchg instruction, since it's a cheap requirement, and it - * avoids breaking the requirement for architectures with the instruction. - */ -#define __pcpu_double_call_return_bool(stem, pcp1, pcp2, ...) \ -({ \ - bool pdcrb_ret__; \ - __verify_pcpu_ptr(&pcp1); \ - BUILD_BUG_ON(sizeof(pcp1) != sizeof(pcp2)); \ - VM_BUG_ON((unsigned long)(&pcp1) % (2 * sizeof(pcp1))); \ - VM_BUG_ON((unsigned long)(&pcp2) != \ - (unsigned long)(&pcp1) + sizeof(pcp1)); \ - switch(sizeof(pcp1)) { \ - case 1: pdcrb_ret__ = stem##1(pcp1, pcp2, __VA_ARGS__); break; \ - case 2: pdcrb_ret__ = stem##2(pcp1, pcp2, __VA_ARGS__); break; \ - case 4: pdcrb_ret__ = stem##4(pcp1, pcp2, __VA_ARGS__); break; \ - case 8: pdcrb_ret__ = stem##8(pcp1, pcp2, __VA_ARGS__); break; \ - default: \ - __bad_size_call_parameter(); break; \ - } \ - pdcrb_ret__; \ -}) - -#define __pcpu_size_call(stem, variable, ...) \ -do { \ - __verify_pcpu_ptr(&(variable)); \ - switch(sizeof(variable)) { \ - case 1: stem##1(variable, __VA_ARGS__);break; \ - case 2: stem##2(variable, __VA_ARGS__);break; \ - case 4: stem##4(variable, __VA_ARGS__);break; \ - case 8: stem##8(variable, __VA_ARGS__);break; \ - default: \ - __bad_size_call_parameter();break; \ - } \ -} while (0) - -/* - * this_cpu operations (C) 2008-2013 Christoph Lameter - * - * Optimized manipulation for memory allocated through the per cpu - * allocator or for addresses of per cpu variables. - * - * These operation guarantee exclusivity of access for other operations - * on the *same* processor. The assumption is that per cpu data is only - * accessed by a single processor instance (the current one). - * - * The first group is used for accesses that must be done in a - * preemption safe way since we know that the context is not preempt - * safe. Interrupts may occur. If the interrupt modifies the variable - * too then RMW actions will not be reliable. - * - * The arch code can provide optimized implementation by defining macros - * for certain scalar sizes. F.e. provide this_cpu_add_2() to provide per - * cpu atomic operations for 2 byte sized RMW actions. If arch code does - * not provide operations for a scalar size then the fallback in the - * generic code will be used. - */ - -# define this_cpu_read(pcp) __pcpu_size_call_return(this_cpu_read_, (pcp)) -# define this_cpu_write(pcp, val) __pcpu_size_call(this_cpu_write_, (pcp), (val)) -# define this_cpu_add(pcp, val) __pcpu_size_call(this_cpu_add_, (pcp), (val)) -# define this_cpu_sub(pcp, val) this_cpu_add((pcp), -(typeof(pcp))(val)) -# define this_cpu_inc(pcp) this_cpu_add((pcp), 1) -# define this_cpu_dec(pcp) this_cpu_sub((pcp), 1) -# define this_cpu_and(pcp, val) __pcpu_size_call(this_cpu_and_, (pcp), (val)) -# define this_cpu_or(pcp, val) __pcpu_size_call(this_cpu_or_, (pcp), (val)) -# define this_cpu_add_return(pcp, val) __pcpu_size_call_return2(this_cpu_add_return_, pcp, val) -#define this_cpu_sub_return(pcp, val) this_cpu_add_return(pcp, -(typeof(pcp))(val)) -#define this_cpu_inc_return(pcp) this_cpu_add_return(pcp, 1) -#define this_cpu_dec_return(pcp) this_cpu_add_return(pcp, -1) -# define this_cpu_xchg(pcp, nval) \ - __pcpu_size_call_return2(this_cpu_xchg_, (pcp), nval) -# define this_cpu_cmpxchg(pcp, oval, nval) \ - __pcpu_size_call_return2(this_cpu_cmpxchg_, pcp, oval, nval) - -/* - * cmpxchg_double replaces two adjacent scalars at once. The first - * two parameters are per cpu variables which have to be of the same - * size. A truth value is returned to indicate success or failure - * (since a double register result is difficult to handle). There is - * very limited hardware support for these operations, so only certain - * sizes may work. - */ -# define this_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ - __pcpu_double_call_return_bool(this_cpu_cmpxchg_double_, (pcp1), (pcp2), (oval1), (oval2), (nval1), (nval2)) - -/* - * Generic percpu operations for contexts where we do not want to do - * any checks for preemptiosn. - * - * If there is no other protection through preempt disable and/or - * disabling interupts then one of these RMW operations can show unexpected - * behavior because the execution thread was rescheduled on another processor - * or an interrupt occurred and the same percpu variable was modified from - * the interrupt context. - */ -# define raw_cpu_read(pcp) __pcpu_size_call_return(raw_cpu_read_, (pcp)) -# define raw_cpu_write(pcp, val) __pcpu_size_call(raw_cpu_write_, (pcp), (val)) -# define raw_cpu_add(pcp, val) __pcpu_size_call(raw_cpu_add_, (pcp), (val)) -# define raw_cpu_sub(pcp, val) raw_cpu_add((pcp), -(val)) -# define raw_cpu_inc(pcp) raw_cpu_add((pcp), 1) -# define raw_cpu_dec(pcp) raw_cpu_sub((pcp), 1) -# define raw_cpu_and(pcp, val) __pcpu_size_call(raw_cpu_and_, (pcp), (val)) -# define raw_cpu_or(pcp, val) __pcpu_size_call(raw_cpu_or_, (pcp), (val)) -# define raw_cpu_add_return(pcp, val) \ - __pcpu_size_call_return2(raw_cpu_add_return_, pcp, val) -#define raw_cpu_sub_return(pcp, val) raw_cpu_add_return(pcp, -(typeof(pcp))(val)) -#define raw_cpu_inc_return(pcp) raw_cpu_add_return(pcp, 1) -#define raw_cpu_dec_return(pcp) raw_cpu_add_return(pcp, -1) -# define raw_cpu_xchg(pcp, nval) \ - __pcpu_size_call_return2(raw_cpu_xchg_, (pcp), nval) -# define raw_cpu_cmpxchg(pcp, oval, nval) \ - __pcpu_size_call_return2(raw_cpu_cmpxchg_, pcp, oval, nval) -# define raw_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ - __pcpu_double_call_return_bool(raw_cpu_cmpxchg_double_, (pcp1), (pcp2), (oval1), (oval2), (nval1), (nval2)) - -/* - * Generic percpu operations for context that are safe from preemption/interrupts. - */ -# define __this_cpu_read(pcp) \ - (__this_cpu_preempt_check("read"),__pcpu_size_call_return(raw_cpu_read_, (pcp))) - -# define __this_cpu_write(pcp, val) \ -do { __this_cpu_preempt_check("write"); \ - __pcpu_size_call(raw_cpu_write_, (pcp), (val)); \ -} while (0) - -# define __this_cpu_add(pcp, val) \ -do { __this_cpu_preempt_check("add"); \ - __pcpu_size_call(raw_cpu_add_, (pcp), (val)); \ -} while (0) - -# define __this_cpu_sub(pcp, val) __this_cpu_add((pcp), -(typeof(pcp))(val)) -# define __this_cpu_inc(pcp) __this_cpu_add((pcp), 1) -# define __this_cpu_dec(pcp) __this_cpu_sub((pcp), 1) - -# define __this_cpu_and(pcp, val) \ -do { __this_cpu_preempt_check("and"); \ - __pcpu_size_call(raw_cpu_and_, (pcp), (val)); \ -} while (0) - -# define __this_cpu_or(pcp, val) \ -do { __this_cpu_preempt_check("or"); \ - __pcpu_size_call(raw_cpu_or_, (pcp), (val)); \ -} while (0) - -# define __this_cpu_add_return(pcp, val) \ - (__this_cpu_preempt_check("add_return"),__pcpu_size_call_return2(raw_cpu_add_return_, pcp, val)) - -#define __this_cpu_sub_return(pcp, val) __this_cpu_add_return(pcp, -(typeof(pcp))(val)) -#define __this_cpu_inc_return(pcp) __this_cpu_add_return(pcp, 1) -#define __this_cpu_dec_return(pcp) __this_cpu_add_return(pcp, -1) - -# define __this_cpu_xchg(pcp, nval) \ - (__this_cpu_preempt_check("xchg"),__pcpu_size_call_return2(raw_cpu_xchg_, (pcp), nval)) - -# define __this_cpu_cmpxchg(pcp, oval, nval) \ - (__this_cpu_preempt_check("cmpxchg"),__pcpu_size_call_return2(raw_cpu_cmpxchg_, pcp, oval, nval)) - -# define __this_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ - (__this_cpu_preempt_check("cmpxchg_double"),__pcpu_double_call_return_bool(raw_cpu_cmpxchg_double_, (pcp1), (pcp2), (oval1), (oval2), (nval1), (nval2))) - #endif /* __LINUX_PERCPU_H */ -- cgit v1.2.3 From 9c28278a24c01c0073fb89e53c1d2a605ab9587d Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 17 Jun 2014 19:12:39 -0400 Subject: percpu: reorder macros in percpu header files * In include/asm-generic/percpu.h, collect {raw|_this}_cpu_generic*() macros into one place. They were dispersed through {raw|this}_cpu_*_N() definitions and the visiual inconsistency was making following the code unnecessarily difficult. * In include/linux/percpu-defs.h, move __verify_pcpu_ptr() later in the file so that it's right above accessor definitions where it's actually used. This is pure reorganization. Signed-off-by: Tejun Heo Acked-by: Christoph Lameter --- include/asm-generic/percpu.h | 198 +++++++++++++++++++++---------------------- include/linux/percpu-defs.h | 26 +++--- 2 files changed, 112 insertions(+), 112 deletions(-) (limited to 'include') diff --git a/include/asm-generic/percpu.h b/include/asm-generic/percpu.h index 932ce602128f..2300d989087b 100644 --- a/include/asm-generic/percpu.h +++ b/include/asm-generic/percpu.h @@ -65,6 +65,105 @@ extern void setup_per_cpu_areas(void); #define PER_CPU_DEF_ATTRIBUTES #endif +#define raw_cpu_generic_to_op(pcp, val, op) \ +do { \ + *raw_cpu_ptr(&(pcp)) op val; \ +} while (0) + +#define raw_cpu_generic_add_return(pcp, val) \ +({ \ + raw_cpu_add(pcp, val); \ + raw_cpu_read(pcp); \ +}) + +#define raw_cpu_generic_xchg(pcp, nval) \ +({ typeof(pcp) ret__; \ + ret__ = raw_cpu_read(pcp); \ + raw_cpu_write(pcp, nval); \ + ret__; \ +}) + +#define raw_cpu_generic_cmpxchg(pcp, oval, nval) \ +({ \ + typeof(pcp) ret__; \ + ret__ = raw_cpu_read(pcp); \ + if (ret__ == (oval)) \ + raw_cpu_write(pcp, nval); \ + ret__; \ +}) + +#define raw_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ +({ \ + int __ret = 0; \ + if (raw_cpu_read(pcp1) == (oval1) && \ + raw_cpu_read(pcp2) == (oval2)) { \ + raw_cpu_write(pcp1, (nval1)); \ + raw_cpu_write(pcp2, (nval2)); \ + __ret = 1; \ + } \ + (__ret); \ +}) + +#define _this_cpu_generic_read(pcp) \ +({ typeof(pcp) ret__; \ + preempt_disable(); \ + ret__ = *this_cpu_ptr(&(pcp)); \ + preempt_enable(); \ + ret__; \ +}) + +#define _this_cpu_generic_to_op(pcp, val, op) \ +do { \ + unsigned long flags; \ + raw_local_irq_save(flags); \ + *raw_cpu_ptr(&(pcp)) op val; \ + raw_local_irq_restore(flags); \ +} while (0) + +#define _this_cpu_generic_add_return(pcp, val) \ +({ \ + typeof(pcp) ret__; \ + unsigned long flags; \ + raw_local_irq_save(flags); \ + raw_cpu_add(pcp, val); \ + ret__ = raw_cpu_read(pcp); \ + raw_local_irq_restore(flags); \ + ret__; \ +}) + +#define _this_cpu_generic_xchg(pcp, nval) \ +({ typeof(pcp) ret__; \ + unsigned long flags; \ + raw_local_irq_save(flags); \ + ret__ = raw_cpu_read(pcp); \ + raw_cpu_write(pcp, nval); \ + raw_local_irq_restore(flags); \ + ret__; \ +}) + +#define _this_cpu_generic_cmpxchg(pcp, oval, nval) \ +({ \ + typeof(pcp) ret__; \ + unsigned long flags; \ + raw_local_irq_save(flags); \ + ret__ = raw_cpu_read(pcp); \ + if (ret__ == (oval)) \ + raw_cpu_write(pcp, nval); \ + raw_local_irq_restore(flags); \ + ret__; \ +}) + +#define _this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ +({ \ + int ret__; \ + unsigned long flags; \ + raw_local_irq_save(flags); \ + ret__ = raw_cpu_generic_cmpxchg_double(pcp1, pcp2, \ + oval1, oval2, nval1, nval2); \ + raw_local_irq_restore(flags); \ + ret__; \ +}) + # ifndef raw_cpu_read_1 # define raw_cpu_read_1(pcp) (*raw_cpu_ptr(&(pcp))) # endif @@ -78,11 +177,6 @@ extern void setup_per_cpu_areas(void); # define raw_cpu_read_8(pcp) (*raw_cpu_ptr(&(pcp))) # endif -#define raw_cpu_generic_to_op(pcp, val, op) \ -do { \ - *raw_cpu_ptr(&(pcp)) op val; \ -} while (0) - # ifndef raw_cpu_write_1 # define raw_cpu_write_1(pcp, val) raw_cpu_generic_to_op((pcp), (val), =) # endif @@ -135,12 +229,6 @@ do { \ # define raw_cpu_or_8(pcp, val) raw_cpu_generic_to_op((pcp), (val), |=) # endif -#define raw_cpu_generic_add_return(pcp, val) \ -({ \ - raw_cpu_add(pcp, val); \ - raw_cpu_read(pcp); \ -}) - # ifndef raw_cpu_add_return_1 # define raw_cpu_add_return_1(pcp, val) raw_cpu_generic_add_return(pcp, val) # endif @@ -154,13 +242,6 @@ do { \ # define raw_cpu_add_return_8(pcp, val) raw_cpu_generic_add_return(pcp, val) # endif -#define raw_cpu_generic_xchg(pcp, nval) \ -({ typeof(pcp) ret__; \ - ret__ = raw_cpu_read(pcp); \ - raw_cpu_write(pcp, nval); \ - ret__; \ -}) - # ifndef raw_cpu_xchg_1 # define raw_cpu_xchg_1(pcp, nval) raw_cpu_generic_xchg(pcp, nval) # endif @@ -174,15 +255,6 @@ do { \ # define raw_cpu_xchg_8(pcp, nval) raw_cpu_generic_xchg(pcp, nval) # endif -#define raw_cpu_generic_cmpxchg(pcp, oval, nval) \ -({ \ - typeof(pcp) ret__; \ - ret__ = raw_cpu_read(pcp); \ - if (ret__ == (oval)) \ - raw_cpu_write(pcp, nval); \ - ret__; \ -}) - # ifndef raw_cpu_cmpxchg_1 # define raw_cpu_cmpxchg_1(pcp, oval, nval) raw_cpu_generic_cmpxchg(pcp, oval, nval) # endif @@ -196,18 +268,6 @@ do { \ # define raw_cpu_cmpxchg_8(pcp, oval, nval) raw_cpu_generic_cmpxchg(pcp, oval, nval) # endif -#define raw_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ -({ \ - int __ret = 0; \ - if (raw_cpu_read(pcp1) == (oval1) && \ - raw_cpu_read(pcp2) == (oval2)) { \ - raw_cpu_write(pcp1, (nval1)); \ - raw_cpu_write(pcp2, (nval2)); \ - __ret = 1; \ - } \ - (__ret); \ -}) - # ifndef raw_cpu_cmpxchg_double_1 # define raw_cpu_cmpxchg_double_1(pcp1, pcp2, oval1, oval2, nval1, nval2) \ raw_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) @@ -225,14 +285,6 @@ do { \ raw_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) # endif -#define _this_cpu_generic_read(pcp) \ -({ typeof(pcp) ret__; \ - preempt_disable(); \ - ret__ = *this_cpu_ptr(&(pcp)); \ - preempt_enable(); \ - ret__; \ -}) - # ifndef this_cpu_read_1 # define this_cpu_read_1(pcp) _this_cpu_generic_read(pcp) # endif @@ -246,14 +298,6 @@ do { \ # define this_cpu_read_8(pcp) _this_cpu_generic_read(pcp) # endif -#define _this_cpu_generic_to_op(pcp, val, op) \ -do { \ - unsigned long flags; \ - raw_local_irq_save(flags); \ - *raw_cpu_ptr(&(pcp)) op val; \ - raw_local_irq_restore(flags); \ -} while (0) - # ifndef this_cpu_write_1 # define this_cpu_write_1(pcp, val) _this_cpu_generic_to_op((pcp), (val), =) # endif @@ -306,17 +350,6 @@ do { \ # define this_cpu_or_8(pcp, val) _this_cpu_generic_to_op((pcp), (val), |=) # endif -#define _this_cpu_generic_add_return(pcp, val) \ -({ \ - typeof(pcp) ret__; \ - unsigned long flags; \ - raw_local_irq_save(flags); \ - raw_cpu_add(pcp, val); \ - ret__ = raw_cpu_read(pcp); \ - raw_local_irq_restore(flags); \ - ret__; \ -}) - # ifndef this_cpu_add_return_1 # define this_cpu_add_return_1(pcp, val) _this_cpu_generic_add_return(pcp, val) # endif @@ -330,16 +363,6 @@ do { \ # define this_cpu_add_return_8(pcp, val) _this_cpu_generic_add_return(pcp, val) # endif -#define _this_cpu_generic_xchg(pcp, nval) \ -({ typeof(pcp) ret__; \ - unsigned long flags; \ - raw_local_irq_save(flags); \ - ret__ = raw_cpu_read(pcp); \ - raw_cpu_write(pcp, nval); \ - raw_local_irq_restore(flags); \ - ret__; \ -}) - # ifndef this_cpu_xchg_1 # define this_cpu_xchg_1(pcp, nval) _this_cpu_generic_xchg(pcp, nval) # endif @@ -353,18 +376,6 @@ do { \ # define this_cpu_xchg_8(pcp, nval) _this_cpu_generic_xchg(pcp, nval) # endif -#define _this_cpu_generic_cmpxchg(pcp, oval, nval) \ -({ \ - typeof(pcp) ret__; \ - unsigned long flags; \ - raw_local_irq_save(flags); \ - ret__ = raw_cpu_read(pcp); \ - if (ret__ == (oval)) \ - raw_cpu_write(pcp, nval); \ - raw_local_irq_restore(flags); \ - ret__; \ -}) - # ifndef this_cpu_cmpxchg_1 # define this_cpu_cmpxchg_1(pcp, oval, nval) _this_cpu_generic_cmpxchg(pcp, oval, nval) # endif @@ -378,17 +389,6 @@ do { \ # define this_cpu_cmpxchg_8(pcp, oval, nval) _this_cpu_generic_cmpxchg(pcp, oval, nval) # endif -#define _this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ -({ \ - int ret__; \ - unsigned long flags; \ - raw_local_irq_save(flags); \ - ret__ = raw_cpu_generic_cmpxchg_double(pcp1, pcp2, \ - oval1, oval2, nval1, nval2); \ - raw_local_irq_restore(flags); \ - ret__; \ -}) - # ifndef this_cpu_cmpxchg_double_1 # define this_cpu_cmpxchg_double_1(pcp1, pcp2, oval1, oval2, nval1, nval2) \ _this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) diff --git a/include/linux/percpu-defs.h b/include/linux/percpu-defs.h index 6710eb9555fa..fd0b9ee19ec8 100644 --- a/include/linux/percpu-defs.h +++ b/include/linux/percpu-defs.h @@ -52,19 +52,6 @@ #define __PCPU_DUMMY_ATTRS \ __attribute__((section(".discard"), unused)) -/* - * Macro which verifies @ptr is a percpu pointer without evaluating - * @ptr. This is to be used in percpu accessors to verify that the - * input parameter is a percpu pointer. - * - * + 0 is required in order to convert the pointer type from a - * potential array type to a pointer to a single item of the array. - */ -#define __verify_pcpu_ptr(ptr) do { \ - const void __percpu *__vpp_verify = (typeof((ptr) + 0))NULL; \ - (void)__vpp_verify; \ -} while (0) - /* * s390 and alpha modules require percpu variables to be defined as * weak to force the compiler to generate GOT based external @@ -203,6 +190,19 @@ */ #ifndef __ASSEMBLY__ +/* + * Macro which verifies @ptr is a percpu pointer without evaluating + * @ptr. This is to be used in percpu accessors to verify that the + * input parameter is a percpu pointer. + * + * + 0 is required in order to convert the pointer type from a + * potential array type to a pointer to a single item of the array. + */ +#define __verify_pcpu_ptr(ptr) do { \ + const void __percpu *__vpp_verify = (typeof((ptr) + 0))NULL; \ + (void)__vpp_verify; \ +} while (0) + #ifdef CONFIG_SMP /* -- cgit v1.2.3 From cadb1c4db2d33e0a818f645cd1963a479dab91e2 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 17 Jun 2014 19:12:39 -0400 Subject: percpu: use raw_cpu_*() to define __this_cpu_*() __this_cpu_*() operations are the same as raw_cpu_*() operations except for the added __this_cpu_preempt_check(). Curiously, these were defined using __pcu_size_call_*() instead of being layered on top of raw_cpu_*(). Let's layer them so that __this_cpu_*() are defined in terms of raw_cpu_*(). It's simpler and less error-prone this way. This patch doesn't introduce any functional difference. Signed-off-by: Tejun Heo Acked-by: Christoph Lameter --- include/linux/percpu-defs.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/linux/percpu-defs.h b/include/linux/percpu-defs.h index fd0b9ee19ec8..215917e9a176 100644 --- a/include/linux/percpu-defs.h +++ b/include/linux/percpu-defs.h @@ -403,16 +403,16 @@ do { \ * Generic percpu operations for context that are safe from preemption/interrupts. */ # define __this_cpu_read(pcp) \ - (__this_cpu_preempt_check("read"),__pcpu_size_call_return(raw_cpu_read_, (pcp))) + (__this_cpu_preempt_check("read"),raw_cpu_read(pcp)) # define __this_cpu_write(pcp, val) \ do { __this_cpu_preempt_check("write"); \ - __pcpu_size_call(raw_cpu_write_, (pcp), (val)); \ + raw_cpu_write(pcp, val); \ } while (0) # define __this_cpu_add(pcp, val) \ do { __this_cpu_preempt_check("add"); \ - __pcpu_size_call(raw_cpu_add_, (pcp), (val)); \ + raw_cpu_add(pcp, val); \ } while (0) # define __this_cpu_sub(pcp, val) __this_cpu_add((pcp), -(typeof(pcp))(val)) @@ -421,29 +421,29 @@ do { __this_cpu_preempt_check("add"); \ # define __this_cpu_and(pcp, val) \ do { __this_cpu_preempt_check("and"); \ - __pcpu_size_call(raw_cpu_and_, (pcp), (val)); \ + raw_cpu_and(pcp, val); \ } while (0) # define __this_cpu_or(pcp, val) \ do { __this_cpu_preempt_check("or"); \ - __pcpu_size_call(raw_cpu_or_, (pcp), (val)); \ + raw_cpu_or(pcp, val); \ } while (0) # define __this_cpu_add_return(pcp, val) \ - (__this_cpu_preempt_check("add_return"),__pcpu_size_call_return2(raw_cpu_add_return_, pcp, val)) + (__this_cpu_preempt_check("add_return"),raw_cpu_add_return(pcp, val)) #define __this_cpu_sub_return(pcp, val) __this_cpu_add_return(pcp, -(typeof(pcp))(val)) #define __this_cpu_inc_return(pcp) __this_cpu_add_return(pcp, 1) #define __this_cpu_dec_return(pcp) __this_cpu_add_return(pcp, -1) # define __this_cpu_xchg(pcp, nval) \ - (__this_cpu_preempt_check("xchg"),__pcpu_size_call_return2(raw_cpu_xchg_, (pcp), nval)) + (__this_cpu_preempt_check("xchg"),raw_cpu_xchg(pcp, nval)) # define __this_cpu_cmpxchg(pcp, oval, nval) \ - (__this_cpu_preempt_check("cmpxchg"),__pcpu_size_call_return2(raw_cpu_cmpxchg_, pcp, oval, nval)) + (__this_cpu_preempt_check("cmpxchg"),raw_cpu_cmpxchg(pcp, oval, nval)) # define __this_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ - (__this_cpu_preempt_check("cmpxchg_double"),__pcpu_double_call_return_bool(raw_cpu_cmpxchg_double_, (pcp1), (pcp2), (oval1), (oval2), (nval1), (nval2))) + (__this_cpu_preempt_check("cmpxchg_double"),raw_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2)) /* * this_cpu_*() operations are used for accesses that must be done in a -- cgit v1.2.3 From eba117889ac444bea6e8270049cbaeed48169889 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 17 Jun 2014 19:12:40 -0400 Subject: percpu: preffity percpu header files percpu macros are difficult to read. It's partly because they're fairly complex but also because they simply lack visual and conventional consistency to an unusual degree. The preceding patches tried to organize macro definitions consistently by their roles. This patch makes the following cosmetic changes to improve overall readability. * Use consistent convention for multi-line macro definitions - "do {" or "({" are now put on their own lines and the line continuing '\' are all put on the same column. * Temp variables used inside macro are consistently given "__" prefix. * When a macro argument is passed to another macro or a function, putting extra parenthses around it doesn't help anything. Don't put them. * _this_cpu_generic_*() are renamed to this_cpu_generic_*() so that they're consistent with raw_cpu_generic_*(). * Reorganize raw_cpu_*() and this_cpu_*() definitions so that trivial wrappers are collected in one place after actual operation definitions. * Other misc cleanups including reorganizing comments. All changes in this patch are cosmetic and cause no functional difference. Signed-off-by: Tejun Heo Acked-by: Christoph Lameter --- include/asm-generic/percpu.h | 581 ++++++++++++++++++++++--------------------- include/linux/percpu-defs.h | 253 ++++++++++--------- 2 files changed, 435 insertions(+), 399 deletions(-) (limited to 'include') diff --git a/include/asm-generic/percpu.h b/include/asm-generic/percpu.h index 2300d989087b..4d9f233c4ba8 100644 --- a/include/asm-generic/percpu.h +++ b/include/asm-generic/percpu.h @@ -77,333 +77,344 @@ do { \ }) #define raw_cpu_generic_xchg(pcp, nval) \ -({ typeof(pcp) ret__; \ - ret__ = raw_cpu_read(pcp); \ +({ \ + typeof(pcp) __ret; \ + __ret = raw_cpu_read(pcp); \ raw_cpu_write(pcp, nval); \ - ret__; \ + __ret; \ }) #define raw_cpu_generic_cmpxchg(pcp, oval, nval) \ ({ \ - typeof(pcp) ret__; \ - ret__ = raw_cpu_read(pcp); \ - if (ret__ == (oval)) \ + typeof(pcp) __ret; \ + __ret = raw_cpu_read(pcp); \ + if (__ret == (oval)) \ raw_cpu_write(pcp, nval); \ - ret__; \ + __ret; \ }) -#define raw_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ +#define raw_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ ({ \ int __ret = 0; \ if (raw_cpu_read(pcp1) == (oval1) && \ raw_cpu_read(pcp2) == (oval2)) { \ - raw_cpu_write(pcp1, (nval1)); \ - raw_cpu_write(pcp2, (nval2)); \ + raw_cpu_write(pcp1, nval1); \ + raw_cpu_write(pcp2, nval2); \ __ret = 1; \ } \ (__ret); \ }) -#define _this_cpu_generic_read(pcp) \ -({ typeof(pcp) ret__; \ +#define this_cpu_generic_read(pcp) \ +({ \ + typeof(pcp) __ret; \ preempt_disable(); \ - ret__ = *this_cpu_ptr(&(pcp)); \ + __ret = *this_cpu_ptr(&(pcp)); \ preempt_enable(); \ - ret__; \ + __ret; \ }) -#define _this_cpu_generic_to_op(pcp, val, op) \ +#define this_cpu_generic_to_op(pcp, val, op) \ do { \ - unsigned long flags; \ - raw_local_irq_save(flags); \ + unsigned long __flags; \ + raw_local_irq_save(__flags); \ *raw_cpu_ptr(&(pcp)) op val; \ - raw_local_irq_restore(flags); \ + raw_local_irq_restore(__flags); \ } while (0) -#define _this_cpu_generic_add_return(pcp, val) \ +#define this_cpu_generic_add_return(pcp, val) \ ({ \ - typeof(pcp) ret__; \ - unsigned long flags; \ - raw_local_irq_save(flags); \ - raw_cpu_add(pcp, val); \ - ret__ = raw_cpu_read(pcp); \ - raw_local_irq_restore(flags); \ - ret__; \ + typeof(pcp) __ret; \ + unsigned long __flags; \ + raw_local_irq_save(__flags); \ + raw_cpu_add(pcp, val); \ + __ret = raw_cpu_read(pcp); \ + raw_local_irq_restore(__flags); \ + __ret; \ }) -#define _this_cpu_generic_xchg(pcp, nval) \ -({ typeof(pcp) ret__; \ - unsigned long flags; \ - raw_local_irq_save(flags); \ - ret__ = raw_cpu_read(pcp); \ +#define this_cpu_generic_xchg(pcp, nval) \ +({ \ + typeof(pcp) __ret; \ + unsigned long __flags; \ + raw_local_irq_save(__flags); \ + __ret = raw_cpu_read(pcp); \ raw_cpu_write(pcp, nval); \ - raw_local_irq_restore(flags); \ - ret__; \ + raw_local_irq_restore(__flags); \ + __ret; \ }) -#define _this_cpu_generic_cmpxchg(pcp, oval, nval) \ +#define this_cpu_generic_cmpxchg(pcp, oval, nval) \ ({ \ - typeof(pcp) ret__; \ - unsigned long flags; \ - raw_local_irq_save(flags); \ - ret__ = raw_cpu_read(pcp); \ - if (ret__ == (oval)) \ + typeof(pcp) __ret; \ + unsigned long __flags; \ + raw_local_irq_save(__flags); \ + __ret = raw_cpu_read(pcp); \ + if (__ret == (oval)) \ raw_cpu_write(pcp, nval); \ - raw_local_irq_restore(flags); \ - ret__; \ + raw_local_irq_restore(__flags); \ + __ret; \ }) -#define _this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ +#define this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ ({ \ - int ret__; \ - unsigned long flags; \ - raw_local_irq_save(flags); \ - ret__ = raw_cpu_generic_cmpxchg_double(pcp1, pcp2, \ + int __ret; \ + unsigned long __flags; \ + raw_local_irq_save(__flags); \ + __ret = raw_cpu_generic_cmpxchg_double(pcp1, pcp2, \ oval1, oval2, nval1, nval2); \ - raw_local_irq_restore(flags); \ - ret__; \ + raw_local_irq_restore(__flags); \ + __ret; \ }) -# ifndef raw_cpu_read_1 -# define raw_cpu_read_1(pcp) (*raw_cpu_ptr(&(pcp))) -# endif -# ifndef raw_cpu_read_2 -# define raw_cpu_read_2(pcp) (*raw_cpu_ptr(&(pcp))) -# endif -# ifndef raw_cpu_read_4 -# define raw_cpu_read_4(pcp) (*raw_cpu_ptr(&(pcp))) -# endif -# ifndef raw_cpu_read_8 -# define raw_cpu_read_8(pcp) (*raw_cpu_ptr(&(pcp))) -# endif - -# ifndef raw_cpu_write_1 -# define raw_cpu_write_1(pcp, val) raw_cpu_generic_to_op((pcp), (val), =) -# endif -# ifndef raw_cpu_write_2 -# define raw_cpu_write_2(pcp, val) raw_cpu_generic_to_op((pcp), (val), =) -# endif -# ifndef raw_cpu_write_4 -# define raw_cpu_write_4(pcp, val) raw_cpu_generic_to_op((pcp), (val), =) -# endif -# ifndef raw_cpu_write_8 -# define raw_cpu_write_8(pcp, val) raw_cpu_generic_to_op((pcp), (val), =) -# endif - -# ifndef raw_cpu_add_1 -# define raw_cpu_add_1(pcp, val) raw_cpu_generic_to_op((pcp), (val), +=) -# endif -# ifndef raw_cpu_add_2 -# define raw_cpu_add_2(pcp, val) raw_cpu_generic_to_op((pcp), (val), +=) -# endif -# ifndef raw_cpu_add_4 -# define raw_cpu_add_4(pcp, val) raw_cpu_generic_to_op((pcp), (val), +=) -# endif -# ifndef raw_cpu_add_8 -# define raw_cpu_add_8(pcp, val) raw_cpu_generic_to_op((pcp), (val), +=) -# endif - -# ifndef raw_cpu_and_1 -# define raw_cpu_and_1(pcp, val) raw_cpu_generic_to_op((pcp), (val), &=) -# endif -# ifndef raw_cpu_and_2 -# define raw_cpu_and_2(pcp, val) raw_cpu_generic_to_op((pcp), (val), &=) -# endif -# ifndef raw_cpu_and_4 -# define raw_cpu_and_4(pcp, val) raw_cpu_generic_to_op((pcp), (val), &=) -# endif -# ifndef raw_cpu_and_8 -# define raw_cpu_and_8(pcp, val) raw_cpu_generic_to_op((pcp), (val), &=) -# endif - -# ifndef raw_cpu_or_1 -# define raw_cpu_or_1(pcp, val) raw_cpu_generic_to_op((pcp), (val), |=) -# endif -# ifndef raw_cpu_or_2 -# define raw_cpu_or_2(pcp, val) raw_cpu_generic_to_op((pcp), (val), |=) -# endif -# ifndef raw_cpu_or_4 -# define raw_cpu_or_4(pcp, val) raw_cpu_generic_to_op((pcp), (val), |=) -# endif -# ifndef raw_cpu_or_8 -# define raw_cpu_or_8(pcp, val) raw_cpu_generic_to_op((pcp), (val), |=) -# endif - -# ifndef raw_cpu_add_return_1 -# define raw_cpu_add_return_1(pcp, val) raw_cpu_generic_add_return(pcp, val) -# endif -# ifndef raw_cpu_add_return_2 -# define raw_cpu_add_return_2(pcp, val) raw_cpu_generic_add_return(pcp, val) -# endif -# ifndef raw_cpu_add_return_4 -# define raw_cpu_add_return_4(pcp, val) raw_cpu_generic_add_return(pcp, val) -# endif -# ifndef raw_cpu_add_return_8 -# define raw_cpu_add_return_8(pcp, val) raw_cpu_generic_add_return(pcp, val) -# endif - -# ifndef raw_cpu_xchg_1 -# define raw_cpu_xchg_1(pcp, nval) raw_cpu_generic_xchg(pcp, nval) -# endif -# ifndef raw_cpu_xchg_2 -# define raw_cpu_xchg_2(pcp, nval) raw_cpu_generic_xchg(pcp, nval) -# endif -# ifndef raw_cpu_xchg_4 -# define raw_cpu_xchg_4(pcp, nval) raw_cpu_generic_xchg(pcp, nval) -# endif -# ifndef raw_cpu_xchg_8 -# define raw_cpu_xchg_8(pcp, nval) raw_cpu_generic_xchg(pcp, nval) -# endif - -# ifndef raw_cpu_cmpxchg_1 -# define raw_cpu_cmpxchg_1(pcp, oval, nval) raw_cpu_generic_cmpxchg(pcp, oval, nval) -# endif -# ifndef raw_cpu_cmpxchg_2 -# define raw_cpu_cmpxchg_2(pcp, oval, nval) raw_cpu_generic_cmpxchg(pcp, oval, nval) -# endif -# ifndef raw_cpu_cmpxchg_4 -# define raw_cpu_cmpxchg_4(pcp, oval, nval) raw_cpu_generic_cmpxchg(pcp, oval, nval) -# endif -# ifndef raw_cpu_cmpxchg_8 -# define raw_cpu_cmpxchg_8(pcp, oval, nval) raw_cpu_generic_cmpxchg(pcp, oval, nval) -# endif - -# ifndef raw_cpu_cmpxchg_double_1 -# define raw_cpu_cmpxchg_double_1(pcp1, pcp2, oval1, oval2, nval1, nval2) \ +#ifndef raw_cpu_read_1 +#define raw_cpu_read_1(pcp) (*raw_cpu_ptr(&(pcp))) +#endif +#ifndef raw_cpu_read_2 +#define raw_cpu_read_2(pcp) (*raw_cpu_ptr(&(pcp))) +#endif +#ifndef raw_cpu_read_4 +#define raw_cpu_read_4(pcp) (*raw_cpu_ptr(&(pcp))) +#endif +#ifndef raw_cpu_read_8 +#define raw_cpu_read_8(pcp) (*raw_cpu_ptr(&(pcp))) +#endif + +#ifndef raw_cpu_write_1 +#define raw_cpu_write_1(pcp, val) raw_cpu_generic_to_op(pcp, val, =) +#endif +#ifndef raw_cpu_write_2 +#define raw_cpu_write_2(pcp, val) raw_cpu_generic_to_op(pcp, val, =) +#endif +#ifndef raw_cpu_write_4 +#define raw_cpu_write_4(pcp, val) raw_cpu_generic_to_op(pcp, val, =) +#endif +#ifndef raw_cpu_write_8 +#define raw_cpu_write_8(pcp, val) raw_cpu_generic_to_op(pcp, val, =) +#endif + +#ifndef raw_cpu_add_1 +#define raw_cpu_add_1(pcp, val) raw_cpu_generic_to_op(pcp, val, +=) +#endif +#ifndef raw_cpu_add_2 +#define raw_cpu_add_2(pcp, val) raw_cpu_generic_to_op(pcp, val, +=) +#endif +#ifndef raw_cpu_add_4 +#define raw_cpu_add_4(pcp, val) raw_cpu_generic_to_op(pcp, val, +=) +#endif +#ifndef raw_cpu_add_8 +#define raw_cpu_add_8(pcp, val) raw_cpu_generic_to_op(pcp, val, +=) +#endif + +#ifndef raw_cpu_and_1 +#define raw_cpu_and_1(pcp, val) raw_cpu_generic_to_op(pcp, val, &=) +#endif +#ifndef raw_cpu_and_2 +#define raw_cpu_and_2(pcp, val) raw_cpu_generic_to_op(pcp, val, &=) +#endif +#ifndef raw_cpu_and_4 +#define raw_cpu_and_4(pcp, val) raw_cpu_generic_to_op(pcp, val, &=) +#endif +#ifndef raw_cpu_and_8 +#define raw_cpu_and_8(pcp, val) raw_cpu_generic_to_op(pcp, val, &=) +#endif + +#ifndef raw_cpu_or_1 +#define raw_cpu_or_1(pcp, val) raw_cpu_generic_to_op(pcp, val, |=) +#endif +#ifndef raw_cpu_or_2 +#define raw_cpu_or_2(pcp, val) raw_cpu_generic_to_op(pcp, val, |=) +#endif +#ifndef raw_cpu_or_4 +#define raw_cpu_or_4(pcp, val) raw_cpu_generic_to_op(pcp, val, |=) +#endif +#ifndef raw_cpu_or_8 +#define raw_cpu_or_8(pcp, val) raw_cpu_generic_to_op(pcp, val, |=) +#endif + +#ifndef raw_cpu_add_return_1 +#define raw_cpu_add_return_1(pcp, val) raw_cpu_generic_add_return(pcp, val) +#endif +#ifndef raw_cpu_add_return_2 +#define raw_cpu_add_return_2(pcp, val) raw_cpu_generic_add_return(pcp, val) +#endif +#ifndef raw_cpu_add_return_4 +#define raw_cpu_add_return_4(pcp, val) raw_cpu_generic_add_return(pcp, val) +#endif +#ifndef raw_cpu_add_return_8 +#define raw_cpu_add_return_8(pcp, val) raw_cpu_generic_add_return(pcp, val) +#endif + +#ifndef raw_cpu_xchg_1 +#define raw_cpu_xchg_1(pcp, nval) raw_cpu_generic_xchg(pcp, nval) +#endif +#ifndef raw_cpu_xchg_2 +#define raw_cpu_xchg_2(pcp, nval) raw_cpu_generic_xchg(pcp, nval) +#endif +#ifndef raw_cpu_xchg_4 +#define raw_cpu_xchg_4(pcp, nval) raw_cpu_generic_xchg(pcp, nval) +#endif +#ifndef raw_cpu_xchg_8 +#define raw_cpu_xchg_8(pcp, nval) raw_cpu_generic_xchg(pcp, nval) +#endif + +#ifndef raw_cpu_cmpxchg_1 +#define raw_cpu_cmpxchg_1(pcp, oval, nval) \ + raw_cpu_generic_cmpxchg(pcp, oval, nval) +#endif +#ifndef raw_cpu_cmpxchg_2 +#define raw_cpu_cmpxchg_2(pcp, oval, nval) \ + raw_cpu_generic_cmpxchg(pcp, oval, nval) +#endif +#ifndef raw_cpu_cmpxchg_4 +#define raw_cpu_cmpxchg_4(pcp, oval, nval) \ + raw_cpu_generic_cmpxchg(pcp, oval, nval) +#endif +#ifndef raw_cpu_cmpxchg_8 +#define raw_cpu_cmpxchg_8(pcp, oval, nval) \ + raw_cpu_generic_cmpxchg(pcp, oval, nval) +#endif + +#ifndef raw_cpu_cmpxchg_double_1 +#define raw_cpu_cmpxchg_double_1(pcp1, pcp2, oval1, oval2, nval1, nval2) \ raw_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) -# endif -# ifndef raw_cpu_cmpxchg_double_2 -# define raw_cpu_cmpxchg_double_2(pcp1, pcp2, oval1, oval2, nval1, nval2) \ +#endif +#ifndef raw_cpu_cmpxchg_double_2 +#define raw_cpu_cmpxchg_double_2(pcp1, pcp2, oval1, oval2, nval1, nval2) \ raw_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) -# endif -# ifndef raw_cpu_cmpxchg_double_4 -# define raw_cpu_cmpxchg_double_4(pcp1, pcp2, oval1, oval2, nval1, nval2) \ +#endif +#ifndef raw_cpu_cmpxchg_double_4 +#define raw_cpu_cmpxchg_double_4(pcp1, pcp2, oval1, oval2, nval1, nval2) \ raw_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) -# endif -# ifndef raw_cpu_cmpxchg_double_8 -# define raw_cpu_cmpxchg_double_8(pcp1, pcp2, oval1, oval2, nval1, nval2) \ +#endif +#ifndef raw_cpu_cmpxchg_double_8 +#define raw_cpu_cmpxchg_double_8(pcp1, pcp2, oval1, oval2, nval1, nval2) \ raw_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) -# endif - -# ifndef this_cpu_read_1 -# define this_cpu_read_1(pcp) _this_cpu_generic_read(pcp) -# endif -# ifndef this_cpu_read_2 -# define this_cpu_read_2(pcp) _this_cpu_generic_read(pcp) -# endif -# ifndef this_cpu_read_4 -# define this_cpu_read_4(pcp) _this_cpu_generic_read(pcp) -# endif -# ifndef this_cpu_read_8 -# define this_cpu_read_8(pcp) _this_cpu_generic_read(pcp) -# endif - -# ifndef this_cpu_write_1 -# define this_cpu_write_1(pcp, val) _this_cpu_generic_to_op((pcp), (val), =) -# endif -# ifndef this_cpu_write_2 -# define this_cpu_write_2(pcp, val) _this_cpu_generic_to_op((pcp), (val), =) -# endif -# ifndef this_cpu_write_4 -# define this_cpu_write_4(pcp, val) _this_cpu_generic_to_op((pcp), (val), =) -# endif -# ifndef this_cpu_write_8 -# define this_cpu_write_8(pcp, val) _this_cpu_generic_to_op((pcp), (val), =) -# endif - -# ifndef this_cpu_add_1 -# define this_cpu_add_1(pcp, val) _this_cpu_generic_to_op((pcp), (val), +=) -# endif -# ifndef this_cpu_add_2 -# define this_cpu_add_2(pcp, val) _this_cpu_generic_to_op((pcp), (val), +=) -# endif -# ifndef this_cpu_add_4 -# define this_cpu_add_4(pcp, val) _this_cpu_generic_to_op((pcp), (val), +=) -# endif -# ifndef this_cpu_add_8 -# define this_cpu_add_8(pcp, val) _this_cpu_generic_to_op((pcp), (val), +=) -# endif - -# ifndef this_cpu_and_1 -# define this_cpu_and_1(pcp, val) _this_cpu_generic_to_op((pcp), (val), &=) -# endif -# ifndef this_cpu_and_2 -# define this_cpu_and_2(pcp, val) _this_cpu_generic_to_op((pcp), (val), &=) -# endif -# ifndef this_cpu_and_4 -# define this_cpu_and_4(pcp, val) _this_cpu_generic_to_op((pcp), (val), &=) -# endif -# ifndef this_cpu_and_8 -# define this_cpu_and_8(pcp, val) _this_cpu_generic_to_op((pcp), (val), &=) -# endif - -# ifndef this_cpu_or_1 -# define this_cpu_or_1(pcp, val) _this_cpu_generic_to_op((pcp), (val), |=) -# endif -# ifndef this_cpu_or_2 -# define this_cpu_or_2(pcp, val) _this_cpu_generic_to_op((pcp), (val), |=) -# endif -# ifndef this_cpu_or_4 -# define this_cpu_or_4(pcp, val) _this_cpu_generic_to_op((pcp), (val), |=) -# endif -# ifndef this_cpu_or_8 -# define this_cpu_or_8(pcp, val) _this_cpu_generic_to_op((pcp), (val), |=) -# endif - -# ifndef this_cpu_add_return_1 -# define this_cpu_add_return_1(pcp, val) _this_cpu_generic_add_return(pcp, val) -# endif -# ifndef this_cpu_add_return_2 -# define this_cpu_add_return_2(pcp, val) _this_cpu_generic_add_return(pcp, val) -# endif -# ifndef this_cpu_add_return_4 -# define this_cpu_add_return_4(pcp, val) _this_cpu_generic_add_return(pcp, val) -# endif -# ifndef this_cpu_add_return_8 -# define this_cpu_add_return_8(pcp, val) _this_cpu_generic_add_return(pcp, val) -# endif - -# ifndef this_cpu_xchg_1 -# define this_cpu_xchg_1(pcp, nval) _this_cpu_generic_xchg(pcp, nval) -# endif -# ifndef this_cpu_xchg_2 -# define this_cpu_xchg_2(pcp, nval) _this_cpu_generic_xchg(pcp, nval) -# endif -# ifndef this_cpu_xchg_4 -# define this_cpu_xchg_4(pcp, nval) _this_cpu_generic_xchg(pcp, nval) -# endif -# ifndef this_cpu_xchg_8 -# define this_cpu_xchg_8(pcp, nval) _this_cpu_generic_xchg(pcp, nval) -# endif - -# ifndef this_cpu_cmpxchg_1 -# define this_cpu_cmpxchg_1(pcp, oval, nval) _this_cpu_generic_cmpxchg(pcp, oval, nval) -# endif -# ifndef this_cpu_cmpxchg_2 -# define this_cpu_cmpxchg_2(pcp, oval, nval) _this_cpu_generic_cmpxchg(pcp, oval, nval) -# endif -# ifndef this_cpu_cmpxchg_4 -# define this_cpu_cmpxchg_4(pcp, oval, nval) _this_cpu_generic_cmpxchg(pcp, oval, nval) -# endif -# ifndef this_cpu_cmpxchg_8 -# define this_cpu_cmpxchg_8(pcp, oval, nval) _this_cpu_generic_cmpxchg(pcp, oval, nval) -# endif - -# ifndef this_cpu_cmpxchg_double_1 -# define this_cpu_cmpxchg_double_1(pcp1, pcp2, oval1, oval2, nval1, nval2) \ - _this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) -# endif -# ifndef this_cpu_cmpxchg_double_2 -# define this_cpu_cmpxchg_double_2(pcp1, pcp2, oval1, oval2, nval1, nval2) \ - _this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) -# endif -# ifndef this_cpu_cmpxchg_double_4 -# define this_cpu_cmpxchg_double_4(pcp1, pcp2, oval1, oval2, nval1, nval2) \ - _this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) -# endif -# ifndef this_cpu_cmpxchg_double_8 -# define this_cpu_cmpxchg_double_8(pcp1, pcp2, oval1, oval2, nval1, nval2) \ - _this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) -# endif +#endif + +#ifndef this_cpu_read_1 +#define this_cpu_read_1(pcp) this_cpu_generic_read(pcp) +#endif +#ifndef this_cpu_read_2 +#define this_cpu_read_2(pcp) this_cpu_generic_read(pcp) +#endif +#ifndef this_cpu_read_4 +#define this_cpu_read_4(pcp) this_cpu_generic_read(pcp) +#endif +#ifndef this_cpu_read_8 +#define this_cpu_read_8(pcp) this_cpu_generic_read(pcp) +#endif + +#ifndef this_cpu_write_1 +#define this_cpu_write_1(pcp, val) this_cpu_generic_to_op(pcp, val, =) +#endif +#ifndef this_cpu_write_2 +#define this_cpu_write_2(pcp, val) this_cpu_generic_to_op(pcp, val, =) +#endif +#ifndef this_cpu_write_4 +#define this_cpu_write_4(pcp, val) this_cpu_generic_to_op(pcp, val, =) +#endif +#ifndef this_cpu_write_8 +#define this_cpu_write_8(pcp, val) this_cpu_generic_to_op(pcp, val, =) +#endif + +#ifndef this_cpu_add_1 +#define this_cpu_add_1(pcp, val) this_cpu_generic_to_op(pcp, val, +=) +#endif +#ifndef this_cpu_add_2 +#define this_cpu_add_2(pcp, val) this_cpu_generic_to_op(pcp, val, +=) +#endif +#ifndef this_cpu_add_4 +#define this_cpu_add_4(pcp, val) this_cpu_generic_to_op(pcp, val, +=) +#endif +#ifndef this_cpu_add_8 +#define this_cpu_add_8(pcp, val) this_cpu_generic_to_op(pcp, val, +=) +#endif + +#ifndef this_cpu_and_1 +#define this_cpu_and_1(pcp, val) this_cpu_generic_to_op(pcp, val, &=) +#endif +#ifndef this_cpu_and_2 +#define this_cpu_and_2(pcp, val) this_cpu_generic_to_op(pcp, val, &=) +#endif +#ifndef this_cpu_and_4 +#define this_cpu_and_4(pcp, val) this_cpu_generic_to_op(pcp, val, &=) +#endif +#ifndef this_cpu_and_8 +#define this_cpu_and_8(pcp, val) this_cpu_generic_to_op(pcp, val, &=) +#endif + +#ifndef this_cpu_or_1 +#define this_cpu_or_1(pcp, val) this_cpu_generic_to_op(pcp, val, |=) +#endif +#ifndef this_cpu_or_2 +#define this_cpu_or_2(pcp, val) this_cpu_generic_to_op(pcp, val, |=) +#endif +#ifndef this_cpu_or_4 +#define this_cpu_or_4(pcp, val) this_cpu_generic_to_op(pcp, val, |=) +#endif +#ifndef this_cpu_or_8 +#define this_cpu_or_8(pcp, val) this_cpu_generic_to_op(pcp, val, |=) +#endif + +#ifndef this_cpu_add_return_1 +#define this_cpu_add_return_1(pcp, val) this_cpu_generic_add_return(pcp, val) +#endif +#ifndef this_cpu_add_return_2 +#define this_cpu_add_return_2(pcp, val) this_cpu_generic_add_return(pcp, val) +#endif +#ifndef this_cpu_add_return_4 +#define this_cpu_add_return_4(pcp, val) this_cpu_generic_add_return(pcp, val) +#endif +#ifndef this_cpu_add_return_8 +#define this_cpu_add_return_8(pcp, val) this_cpu_generic_add_return(pcp, val) +#endif + +#ifndef this_cpu_xchg_1 +#define this_cpu_xchg_1(pcp, nval) this_cpu_generic_xchg(pcp, nval) +#endif +#ifndef this_cpu_xchg_2 +#define this_cpu_xchg_2(pcp, nval) this_cpu_generic_xchg(pcp, nval) +#endif +#ifndef this_cpu_xchg_4 +#define this_cpu_xchg_4(pcp, nval) this_cpu_generic_xchg(pcp, nval) +#endif +#ifndef this_cpu_xchg_8 +#define this_cpu_xchg_8(pcp, nval) this_cpu_generic_xchg(pcp, nval) +#endif + +#ifndef this_cpu_cmpxchg_1 +#define this_cpu_cmpxchg_1(pcp, oval, nval) \ + this_cpu_generic_cmpxchg(pcp, oval, nval) +#endif +#ifndef this_cpu_cmpxchg_2 +#define this_cpu_cmpxchg_2(pcp, oval, nval) \ + this_cpu_generic_cmpxchg(pcp, oval, nval) +#endif +#ifndef this_cpu_cmpxchg_4 +#define this_cpu_cmpxchg_4(pcp, oval, nval) \ + this_cpu_generic_cmpxchg(pcp, oval, nval) +#endif +#ifndef this_cpu_cmpxchg_8 +#define this_cpu_cmpxchg_8(pcp, oval, nval) \ + this_cpu_generic_cmpxchg(pcp, oval, nval) +#endif + +#ifndef this_cpu_cmpxchg_double_1 +#define this_cpu_cmpxchg_double_1(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) +#endif +#ifndef this_cpu_cmpxchg_double_2 +#define this_cpu_cmpxchg_double_2(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) +#endif +#ifndef this_cpu_cmpxchg_double_4 +#define this_cpu_cmpxchg_double_4(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) +#endif +#ifndef this_cpu_cmpxchg_double_8 +#define this_cpu_cmpxchg_double_8(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + this_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) +#endif #endif /* _ASM_GENERIC_PERCPU_H_ */ diff --git a/include/linux/percpu-defs.h b/include/linux/percpu-defs.h index 215917e9a176..d8bb6e001c6a 100644 --- a/include/linux/percpu-defs.h +++ b/include/linux/percpu-defs.h @@ -198,7 +198,8 @@ * + 0 is required in order to convert the pointer type from a * potential array type to a pointer to a single item of the array. */ -#define __verify_pcpu_ptr(ptr) do { \ +#define __verify_pcpu_ptr(ptr) \ +do { \ const void __percpu *__vpp_verify = (typeof((ptr) + 0))NULL; \ (void)__vpp_verify; \ } while (0) @@ -210,12 +211,13 @@ * to prevent the compiler from making incorrect assumptions about the * pointer value. The weird cast keeps both GCC and sparse happy. */ -#define SHIFT_PERCPU_PTR(__p, __offset) ({ \ - __verify_pcpu_ptr((__p)); \ +#define SHIFT_PERCPU_PTR(__p, __offset) \ +({ \ + __verify_pcpu_ptr(__p); \ RELOC_HIDE((typeof(*(__p)) __kernel __force *)(__p), (__offset)); \ }) -#define per_cpu_ptr(ptr, cpu) SHIFT_PERCPU_PTR((ptr), per_cpu_offset((cpu))) +#define per_cpu_ptr(ptr, cpu) SHIFT_PERCPU_PTR(ptr, per_cpu_offset(cpu)) #define raw_cpu_ptr(ptr) arch_raw_cpu_ptr(ptr) #ifdef CONFIG_DEBUG_PREEMPT @@ -226,12 +228,13 @@ #else /* CONFIG_SMP */ -#define VERIFY_PERCPU_PTR(__p) ({ \ - __verify_pcpu_ptr((__p)); \ - (typeof(*(__p)) __kernel __force *)(__p); \ +#define VERIFY_PERCPU_PTR(__p) \ +({ \ + __verify_pcpu_ptr(__p); \ + (typeof(*(__p)) __kernel __force *)(__p); \ }) -#define per_cpu_ptr(ptr, cpu) ({ (void)(cpu); VERIFY_PERCPU_PTR((ptr)); }) +#define per_cpu_ptr(ptr, cpu) ({ (void)(cpu); VERIFY_PERCPU_PTR(ptr); }) #define raw_cpu_ptr(ptr) per_cpu_ptr(ptr, 0) #define this_cpu_ptr(ptr) raw_cpu_ptr(ptr) @@ -248,26 +251,32 @@ * Must be an lvalue. Since @var must be a simple identifier, * we force a syntax error here if it isn't. */ -#define get_cpu_var(var) (*({ \ - preempt_disable(); \ - this_cpu_ptr(&var); })) +#define get_cpu_var(var) \ +(*({ \ + preempt_disable(); \ + this_cpu_ptr(&var); \ +})) /* * The weird & is necessary because sparse considers (void)(var) to be * a direct dereference of percpu variable (var). */ -#define put_cpu_var(var) do { \ - (void)&(var); \ - preempt_enable(); \ +#define put_cpu_var(var) \ +do { \ + (void)&(var); \ + preempt_enable(); \ } while (0) -#define get_cpu_ptr(var) ({ \ - preempt_disable(); \ - this_cpu_ptr(var); }) +#define get_cpu_ptr(var) \ +({ \ + preempt_disable(); \ + this_cpu_ptr(var); \ +}) -#define put_cpu_ptr(var) do { \ - (void)(var); \ - preempt_enable(); \ +#define put_cpu_ptr(var) \ +do { \ + (void)(var); \ + preempt_enable(); \ } while (0) /* @@ -284,15 +293,16 @@ static inline void __this_cpu_preempt_check(const char *op) { } #endif #define __pcpu_size_call_return(stem, variable) \ -({ typeof(variable) pscr_ret__; \ +({ \ + typeof(variable) pscr_ret__; \ __verify_pcpu_ptr(&(variable)); \ switch(sizeof(variable)) { \ - case 1: pscr_ret__ = stem##1(variable);break; \ - case 2: pscr_ret__ = stem##2(variable);break; \ - case 4: pscr_ret__ = stem##4(variable);break; \ - case 8: pscr_ret__ = stem##8(variable);break; \ + case 1: pscr_ret__ = stem##1(variable); break; \ + case 2: pscr_ret__ = stem##2(variable); break; \ + case 4: pscr_ret__ = stem##4(variable); break; \ + case 8: pscr_ret__ = stem##8(variable); break; \ default: \ - __bad_size_call_parameter();break; \ + __bad_size_call_parameter(); break; \ } \ pscr_ret__; \ }) @@ -323,11 +333,11 @@ static inline void __this_cpu_preempt_check(const char *op) { } #define __pcpu_double_call_return_bool(stem, pcp1, pcp2, ...) \ ({ \ bool pdcrb_ret__; \ - __verify_pcpu_ptr(&pcp1); \ + __verify_pcpu_ptr(&(pcp1)); \ BUILD_BUG_ON(sizeof(pcp1) != sizeof(pcp2)); \ - VM_BUG_ON((unsigned long)(&pcp1) % (2 * sizeof(pcp1))); \ - VM_BUG_ON((unsigned long)(&pcp2) != \ - (unsigned long)(&pcp1) + sizeof(pcp1)); \ + VM_BUG_ON((unsigned long)(&(pcp1)) % (2 * sizeof(pcp1))); \ + VM_BUG_ON((unsigned long)(&(pcp2)) != \ + (unsigned long)(&(pcp1)) + sizeof(pcp1)); \ switch(sizeof(pcp1)) { \ case 1: pdcrb_ret__ = stem##1(pcp1, pcp2, __VA_ARGS__); break; \ case 2: pdcrb_ret__ = stem##2(pcp1, pcp2, __VA_ARGS__); break; \ @@ -367,117 +377,132 @@ do { \ * cpu atomic operations for 2 byte sized RMW actions. If arch code does * not provide operations for a scalar size then the fallback in the * generic code will be used. + * + * cmpxchg_double replaces two adjacent scalars at once. The first two + * parameters are per cpu variables which have to be of the same size. A + * truth value is returned to indicate success or failure (since a double + * register result is difficult to handle). There is very limited hardware + * support for these operations, so only certain sizes may work. */ /* - * Generic percpu operations for contexts where we do not want to do - * any checks for preemptiosn. + * Operations for contexts where we do not want to do any checks for + * preemptions. Unless strictly necessary, always use [__]this_cpu_*() + * instead. * - * If there is no other protection through preempt disable and/or - * disabling interupts then one of these RMW operations can show unexpected - * behavior because the execution thread was rescheduled on another processor - * or an interrupt occurred and the same percpu variable was modified from - * the interrupt context. + * If there is no other protection through preempt disable and/or disabling + * interupts then one of these RMW operations can show unexpected behavior + * because the execution thread was rescheduled on another processor or an + * interrupt occurred and the same percpu variable was modified from the + * interrupt context. */ -# define raw_cpu_read(pcp) __pcpu_size_call_return(raw_cpu_read_, (pcp)) -# define raw_cpu_write(pcp, val) __pcpu_size_call(raw_cpu_write_, (pcp), (val)) -# define raw_cpu_add(pcp, val) __pcpu_size_call(raw_cpu_add_, (pcp), (val)) -# define raw_cpu_sub(pcp, val) raw_cpu_add((pcp), -(val)) -# define raw_cpu_inc(pcp) raw_cpu_add((pcp), 1) -# define raw_cpu_dec(pcp) raw_cpu_sub((pcp), 1) -# define raw_cpu_and(pcp, val) __pcpu_size_call(raw_cpu_and_, (pcp), (val)) -# define raw_cpu_or(pcp, val) __pcpu_size_call(raw_cpu_or_, (pcp), (val)) -# define raw_cpu_add_return(pcp, val) \ - __pcpu_size_call_return2(raw_cpu_add_return_, pcp, val) -#define raw_cpu_sub_return(pcp, val) raw_cpu_add_return(pcp, -(typeof(pcp))(val)) -#define raw_cpu_inc_return(pcp) raw_cpu_add_return(pcp, 1) -#define raw_cpu_dec_return(pcp) raw_cpu_add_return(pcp, -1) -# define raw_cpu_xchg(pcp, nval) \ - __pcpu_size_call_return2(raw_cpu_xchg_, (pcp), nval) -# define raw_cpu_cmpxchg(pcp, oval, nval) \ +#define raw_cpu_read(pcp) __pcpu_size_call_return(raw_cpu_read_, pcp) +#define raw_cpu_write(pcp, val) __pcpu_size_call(raw_cpu_write_, pcp, val) +#define raw_cpu_add(pcp, val) __pcpu_size_call(raw_cpu_add_, pcp, val) +#define raw_cpu_and(pcp, val) __pcpu_size_call(raw_cpu_and_, pcp, val) +#define raw_cpu_or(pcp, val) __pcpu_size_call(raw_cpu_or_, pcp, val) +#define raw_cpu_add_return(pcp, val) __pcpu_size_call_return2(raw_cpu_add_return_, pcp, val) +#define raw_cpu_xchg(pcp, nval) __pcpu_size_call_return2(raw_cpu_xchg_, pcp, nval) +#define raw_cpu_cmpxchg(pcp, oval, nval) \ __pcpu_size_call_return2(raw_cpu_cmpxchg_, pcp, oval, nval) -# define raw_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ - __pcpu_double_call_return_bool(raw_cpu_cmpxchg_double_, (pcp1), (pcp2), (oval1), (oval2), (nval1), (nval2)) +#define raw_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + __pcpu_double_call_return_bool(raw_cpu_cmpxchg_double_, pcp1, pcp2, oval1, oval2, nval1, nval2) + +#define raw_cpu_sub(pcp, val) raw_cpu_add(pcp, -(val)) +#define raw_cpu_inc(pcp) raw_cpu_add(pcp, 1) +#define raw_cpu_dec(pcp) raw_cpu_sub(pcp, 1) +#define raw_cpu_sub_return(pcp, val) raw_cpu_add_return(pcp, -(typeof(pcp))(val)) +#define raw_cpu_inc_return(pcp) raw_cpu_add_return(pcp, 1) +#define raw_cpu_dec_return(pcp) raw_cpu_add_return(pcp, -1) /* - * Generic percpu operations for context that are safe from preemption/interrupts. + * Operations for contexts that are safe from preemption/interrupts. These + * operations verify that preemption is disabled. */ -# define __this_cpu_read(pcp) \ - (__this_cpu_preempt_check("read"),raw_cpu_read(pcp)) +#define __this_cpu_read(pcp) \ +({ \ + __this_cpu_preempt_check("read"); \ + raw_cpu_read(pcp); \ +}) -# define __this_cpu_write(pcp, val) \ -do { __this_cpu_preempt_check("write"); \ - raw_cpu_write(pcp, val); \ -} while (0) +#define __this_cpu_write(pcp, val) \ +({ \ + __this_cpu_preempt_check("write"); \ + raw_cpu_write(pcp, val); \ +}) -# define __this_cpu_add(pcp, val) \ -do { __this_cpu_preempt_check("add"); \ +#define __this_cpu_add(pcp, val) \ +({ \ + __this_cpu_preempt_check("add"); \ raw_cpu_add(pcp, val); \ -} while (0) - -# define __this_cpu_sub(pcp, val) __this_cpu_add((pcp), -(typeof(pcp))(val)) -# define __this_cpu_inc(pcp) __this_cpu_add((pcp), 1) -# define __this_cpu_dec(pcp) __this_cpu_sub((pcp), 1) +}) -# define __this_cpu_and(pcp, val) \ -do { __this_cpu_preempt_check("and"); \ +#define __this_cpu_and(pcp, val) \ +({ \ + __this_cpu_preempt_check("and"); \ raw_cpu_and(pcp, val); \ -} while (0) +}) -# define __this_cpu_or(pcp, val) \ -do { __this_cpu_preempt_check("or"); \ +#define __this_cpu_or(pcp, val) \ +({ \ + __this_cpu_preempt_check("or"); \ raw_cpu_or(pcp, val); \ -} while (0) +}) -# define __this_cpu_add_return(pcp, val) \ - (__this_cpu_preempt_check("add_return"),raw_cpu_add_return(pcp, val)) +#define __this_cpu_add_return(pcp, val) \ +({ \ + __this_cpu_preempt_check("add_return"); \ + raw_cpu_add_return(pcp, val); \ +}) -#define __this_cpu_sub_return(pcp, val) __this_cpu_add_return(pcp, -(typeof(pcp))(val)) -#define __this_cpu_inc_return(pcp) __this_cpu_add_return(pcp, 1) -#define __this_cpu_dec_return(pcp) __this_cpu_add_return(pcp, -1) +#define __this_cpu_xchg(pcp, nval) \ +({ \ + __this_cpu_preempt_check("xchg"); \ + raw_cpu_xchg(pcp, nval); \ +}) -# define __this_cpu_xchg(pcp, nval) \ - (__this_cpu_preempt_check("xchg"),raw_cpu_xchg(pcp, nval)) +#define __this_cpu_cmpxchg(pcp, oval, nval) \ +({ \ + __this_cpu_preempt_check("cmpxchg"); \ + raw_cpu_cmpxchg(pcp, oval, nval); \ +}) -# define __this_cpu_cmpxchg(pcp, oval, nval) \ - (__this_cpu_preempt_check("cmpxchg"),raw_cpu_cmpxchg(pcp, oval, nval)) +#define __this_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ +({ __this_cpu_preempt_check("cmpxchg_double"); \ + raw_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2); \ +}) -# define __this_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ - (__this_cpu_preempt_check("cmpxchg_double"),raw_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2)) +#define __this_cpu_sub(pcp, val) __this_cpu_add(pcp, -(typeof(pcp))(val)) +#define __this_cpu_inc(pcp) __this_cpu_add(pcp, 1) +#define __this_cpu_dec(pcp) __this_cpu_sub(pcp, 1) +#define __this_cpu_sub_return(pcp, val) __this_cpu_add_return(pcp, -(typeof(pcp))(val)) +#define __this_cpu_inc_return(pcp) __this_cpu_add_return(pcp, 1) +#define __this_cpu_dec_return(pcp) __this_cpu_add_return(pcp, -1) /* - * this_cpu_*() operations are used for accesses that must be done in a - * preemption safe way since we know that the context is not preempt - * safe. Interrupts may occur. If the interrupt modifies the variable too - * then RMW actions will not be reliable. + * Operations with implied preemption protection. These operations can be + * used without worrying about preemption. Note that interrupts may still + * occur while an operation is in progress and if the interrupt modifies + * the variable too then RMW actions may not be reliable. */ -# define this_cpu_read(pcp) __pcpu_size_call_return(this_cpu_read_, (pcp)) -# define this_cpu_write(pcp, val) __pcpu_size_call(this_cpu_write_, (pcp), (val)) -# define this_cpu_add(pcp, val) __pcpu_size_call(this_cpu_add_, (pcp), (val)) -# define this_cpu_sub(pcp, val) this_cpu_add((pcp), -(typeof(pcp))(val)) -# define this_cpu_inc(pcp) this_cpu_add((pcp), 1) -# define this_cpu_dec(pcp) this_cpu_sub((pcp), 1) -# define this_cpu_and(pcp, val) __pcpu_size_call(this_cpu_and_, (pcp), (val)) -# define this_cpu_or(pcp, val) __pcpu_size_call(this_cpu_or_, (pcp), (val)) -# define this_cpu_add_return(pcp, val) __pcpu_size_call_return2(this_cpu_add_return_, pcp, val) +#define this_cpu_read(pcp) __pcpu_size_call_return(this_cpu_read_, pcp) +#define this_cpu_write(pcp, val) __pcpu_size_call(this_cpu_write_, pcp, val) +#define this_cpu_add(pcp, val) __pcpu_size_call(this_cpu_add_, pcp, val) +#define this_cpu_and(pcp, val) __pcpu_size_call(this_cpu_and_, pcp, val) +#define this_cpu_or(pcp, val) __pcpu_size_call(this_cpu_or_, pcp, val) +#define this_cpu_add_return(pcp, val) __pcpu_size_call_return2(this_cpu_add_return_, pcp, val) +#define this_cpu_xchg(pcp, nval) __pcpu_size_call_return2(this_cpu_xchg_, pcp, nval) +#define this_cpu_cmpxchg(pcp, oval, nval) \ + __pcpu_size_call_return2(this_cpu_cmpxchg_, pcp, oval, nval) +#define this_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ + __pcpu_double_call_return_bool(this_cpu_cmpxchg_double_, pcp1, pcp2, oval1, oval2, nval1, nval2) + +#define this_cpu_sub(pcp, val) this_cpu_add(pcp, -(typeof(pcp))(val)) +#define this_cpu_inc(pcp) this_cpu_add(pcp, 1) +#define this_cpu_dec(pcp) this_cpu_sub(pcp, 1) #define this_cpu_sub_return(pcp, val) this_cpu_add_return(pcp, -(typeof(pcp))(val)) #define this_cpu_inc_return(pcp) this_cpu_add_return(pcp, 1) #define this_cpu_dec_return(pcp) this_cpu_add_return(pcp, -1) -# define this_cpu_xchg(pcp, nval) \ - __pcpu_size_call_return2(this_cpu_xchg_, (pcp), nval) -# define this_cpu_cmpxchg(pcp, oval, nval) \ - __pcpu_size_call_return2(this_cpu_cmpxchg_, pcp, oval, nval) - -/* - * cmpxchg_double replaces two adjacent scalars at once. The first - * two parameters are per cpu variables which have to be of the same - * size. A truth value is returned to indicate success or failure - * (since a double register result is difficult to handle). There is - * very limited hardware support for these operations, so only certain - * sizes may work. - */ -# define this_cpu_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \ - __pcpu_double_call_return_bool(this_cpu_cmpxchg_double_, (pcp1), (pcp2), (oval1), (oval2), (nval1), (nval2)) #endif /* __ASSEMBLY__ */ #endif /* _LINUX_PERCPU_DEFS_H */ -- cgit v1.2.3 From 6fbc07bbe2b5a898532f970c5a397f8789ace0d5 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 17 Jun 2014 19:12:40 -0400 Subject: percpu: invoke __verify_pcpu_ptr() from the generic part of accessors and operations __verify_pcpu_ptr() is used to verify that a specified parameter is actually an percpu pointer by percpu accessor and operation implementations. Currently, where it's called isn't clearly defined and we just ensure that it's invoked at least once for all accessors and operations. The lack of clarity on when it should be called isn't nice and given that this is a completely generic issue, there's no reason to make archs worry about it. This patch updates __verify_pcpu_ptr() invocations such that it's always invoked from the final generic wrapper once per access or operation. As this is already the case for {raw|this}_cpu_*() definitions through __pcpu_size_*(), only the {raw|per|this}_cpu_ptr() accessors need to be updated. This change makes it unnecessary for archs to worry about __verify_pcpu_ptr(). x86's arch_raw_cpu_ptr() is updated accordingly. Signed-off-by: Tejun Heo Cc: Christoph Lameter Cc: Thomas Gleixner Cc: Ingo Molnar Cc: "H. Peter Anvin" --- arch/x86/include/asm/percpu.h | 1 - include/linux/percpu-defs.h | 29 +++++++++++++++++++++-------- 2 files changed, 21 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/arch/x86/include/asm/percpu.h b/arch/x86/include/asm/percpu.h index 9bc23f18a6fa..fd472181a1d0 100644 --- a/arch/x86/include/asm/percpu.h +++ b/arch/x86/include/asm/percpu.h @@ -55,7 +55,6 @@ #define arch_raw_cpu_ptr(ptr) \ ({ \ unsigned long tcp_ptr__; \ - __verify_pcpu_ptr(ptr); \ asm volatile("add " __percpu_arg(1) ", %0" \ : "=r" (tcp_ptr__) \ : "m" (this_cpu_off), "0" (ptr)); \ diff --git a/include/linux/percpu-defs.h b/include/linux/percpu-defs.h index d8bb6e001c6a..c93fff16776c 100644 --- a/include/linux/percpu-defs.h +++ b/include/linux/percpu-defs.h @@ -191,9 +191,12 @@ #ifndef __ASSEMBLY__ /* - * Macro which verifies @ptr is a percpu pointer without evaluating - * @ptr. This is to be used in percpu accessors to verify that the - * input parameter is a percpu pointer. + * __verify_pcpu_ptr() verifies @ptr is a percpu pointer without evaluating + * @ptr and is invoked once before a percpu area is accessed by all + * accessors and operations. This is performed in the generic part of + * percpu and arch overrides don't need to worry about it; however, if an + * arch wants to implement an arch-specific percpu accessor or operation, + * it may use __verify_pcpu_ptr() to verify the parameters. * * + 0 is required in order to convert the pointer type from a * potential array type to a pointer to a single item of the array. @@ -212,16 +215,26 @@ do { \ * pointer value. The weird cast keeps both GCC and sparse happy. */ #define SHIFT_PERCPU_PTR(__p, __offset) \ + RELOC_HIDE((typeof(*(__p)) __kernel __force *)(__p), (__offset)) + +#define per_cpu_ptr(ptr, cpu) \ ({ \ - __verify_pcpu_ptr(__p); \ - RELOC_HIDE((typeof(*(__p)) __kernel __force *)(__p), (__offset)); \ + __verify_pcpu_ptr(ptr); \ + SHIFT_PERCPU_PTR((ptr), per_cpu_offset((cpu))); \ }) -#define per_cpu_ptr(ptr, cpu) SHIFT_PERCPU_PTR(ptr, per_cpu_offset(cpu)) -#define raw_cpu_ptr(ptr) arch_raw_cpu_ptr(ptr) +#define raw_cpu_ptr(ptr) \ +({ \ + __verify_pcpu_ptr(ptr); \ + arch_raw_cpu_ptr(ptr); \ +}) #ifdef CONFIG_DEBUG_PREEMPT -#define this_cpu_ptr(ptr) SHIFT_PERCPU_PTR(ptr, my_cpu_offset) +#define this_cpu_ptr(ptr) \ +({ \ + __verify_pcpu_ptr(ptr); \ + SHIFT_PERCPU_PTR(ptr, my_cpu_offset); \ +}) #else #define this_cpu_ptr(ptr) raw_cpu_ptr(ptr) #endif -- cgit v1.2.3 From 735b9ffada0563afa52d0879cf6d630819ee68ad Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Mon, 9 Jun 2014 14:39:50 +0100 Subject: drm: Driver-specific ioctls range from 0x40 to 0x9f DRM_COMMAND_END is 0xa0, so the last driver ioctl is 0x9f, not 0x99. Signed-off-by: Damien Lespiau Reviewed-by: Alex Deucher Signed-off-by: Daniel Vetter --- include/uapi/drm/drm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h index 9abbeb924cbb..b0b855613641 100644 --- a/include/uapi/drm/drm.h +++ b/include/uapi/drm/drm.h @@ -780,7 +780,7 @@ struct drm_prime_handle { /** * Device specific ioctls should only be in their respective headers - * The device specific ioctl range is from 0x40 to 0x99. + * The device specific ioctl range is from 0x40 to 0x9f. * Generic IOCTLS restart at 0xA0. * * \sa drmCommandNone(), drmCommandRead(), drmCommandWrite(), and -- cgit v1.2.3 From 22fdcf02f6e80d64a927f702dd9d631a927d87d4 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 5 Jun 2014 11:31:01 -0400 Subject: lockdep: Revert lockdep check in raw_seqcount_begin() This commit reverts the addition of lockdep checking to raw_seqcount_begin for the following reasons: 1) It violates the naming convention that raw_* functions should not do lockdep checks (a convention that is also followed by the other raw_*_seqcount_begin functions). 2) raw_seqcount_begin does not spin, so it can only be part of an ABBA deadlock in very special circumstances (for instance if a lock is held across the entire raw_seqcount_begin()+read_seqcount_retry() loop while also being taken inside the write_seqcount protected area). 3) It is causing false positives with some existing callers, and there is no non-lockdep alternative for those callers to use. None of the three existing callers (__d_lookup_rcu, netdev_get_name, and the NFS state code) appear to use the function in a manner that is ABBA deadlock prone. Fixes: 1ca7d67cf5d5: seqcount: Add lockdep functionality to seqcount/seqlock Signed-off-by: Trond Myklebust Signed-off-by: Peter Zijlstra Cc: John Stultz Cc: "David S. Miller" Cc: Al Viro Cc: Waiman Long Cc: Stephen Boyd Cc: Linus Torvalds Link: http://lkml.kernel.org/r/CAHQdGtRR6SvEhXiqWo24hoUh9AU9cL82Z8Z-d8-7u951F_d+5g@mail.gmail.com Signed-off-by: Ingo Molnar --- include/linux/seqlock.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/linux/seqlock.h b/include/linux/seqlock.h index 535f158977b9..8cf350325dc6 100644 --- a/include/linux/seqlock.h +++ b/include/linux/seqlock.h @@ -164,8 +164,6 @@ static inline unsigned read_seqcount_begin(const seqcount_t *s) static inline unsigned raw_seqcount_begin(const seqcount_t *s) { unsigned ret = ACCESS_ONCE(s->sequence); - - seqcount_lockdep_reader_access(s); smp_rmb(); return ret & ~1; } -- cgit v1.2.3 From 34ea3d386347cd6de4c2fa2491dd85c9e753e7e4 Mon Sep 17 00:00:00 2001 From: Thomas Wood Date: Thu, 29 May 2014 16:57:41 +0100 Subject: drm: add register and unregister functions for connectors Introduce generic functions to register and unregister connectors. This provides a common place to add and remove associated user space interfaces. Signed-off-by: Thomas Wood Reviewed-by: David Herrmann Signed-off-by: Daniel Vetter --- Documentation/DocBook/drm.tmpl | 6 +++--- drivers/gpu/drm/armada/armada_output.c | 4 ++-- drivers/gpu/drm/ast/ast_mode.c | 4 ++-- drivers/gpu/drm/bridge/ptn3460.c | 2 +- drivers/gpu/drm/drm_crtc.c | 30 ++++++++++++++++++++++++++- drivers/gpu/drm/drm_sysfs.c | 2 -- drivers/gpu/drm/exynos/exynos_dp_core.c | 2 +- drivers/gpu/drm/exynos/exynos_drm_connector.c | 6 +++--- drivers/gpu/drm/exynos/exynos_drm_dpi.c | 4 ++-- drivers/gpu/drm/exynos/exynos_drm_dsi.c | 2 +- drivers/gpu/drm/exynos/exynos_drm_vidi.c | 2 +- drivers/gpu/drm/exynos/exynos_hdmi.c | 2 +- drivers/gpu/drm/gma500/cdv_intel_crt.c | 4 ++-- drivers/gpu/drm/gma500/cdv_intel_dp.c | 4 ++-- drivers/gpu/drm/gma500/cdv_intel_hdmi.c | 4 ++-- drivers/gpu/drm/gma500/cdv_intel_lvds.c | 4 ++-- drivers/gpu/drm/gma500/mdfld_dsi_output.c | 4 ++-- drivers/gpu/drm/gma500/oaktrail_hdmi.c | 2 +- drivers/gpu/drm/gma500/oaktrail_lvds.c | 2 +- drivers/gpu/drm/gma500/psb_intel_lvds.c | 4 ++-- drivers/gpu/drm/gma500/psb_intel_sdvo.c | 4 ++-- drivers/gpu/drm/i915/intel_crt.c | 2 +- drivers/gpu/drm/i915/intel_display.c | 2 +- drivers/gpu/drm/i915/intel_dp.c | 4 ++-- drivers/gpu/drm/i915/intel_dsi.c | 2 +- drivers/gpu/drm/i915/intel_dvo.c | 2 +- drivers/gpu/drm/i915/intel_hdmi.c | 2 +- drivers/gpu/drm/i915/intel_lvds.c | 2 +- drivers/gpu/drm/i915/intel_sdvo.c | 10 ++++----- drivers/gpu/drm/i915/intel_tv.c | 2 +- drivers/gpu/drm/mgag200/mgag200_mode.c | 2 +- drivers/gpu/drm/msm/hdmi/hdmi_connector.c | 4 ++-- drivers/gpu/drm/nouveau/nouveau_connector.c | 4 ++-- drivers/gpu/drm/omapdrm/omap_connector.c | 4 ++-- drivers/gpu/drm/qxl/qxl_display.c | 4 ++-- drivers/gpu/drm/radeon/radeon_connectors.c | 6 +++--- drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c | 4 ++-- drivers/gpu/drm/rcar-du/rcar_du_vgacon.c | 4 ++-- drivers/gpu/drm/shmobile/shmob_drm_crtc.c | 6 +++--- drivers/gpu/drm/tegra/output.c | 4 ++-- drivers/gpu/drm/tilcdc/tilcdc_panel.c | 2 +- drivers/gpu/drm/tilcdc/tilcdc_slave.c | 2 +- drivers/gpu/drm/tilcdc/tilcdc_tfp410.c | 2 +- drivers/gpu/drm/udl/udl_connector.c | 4 ++-- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 2 +- drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 2 +- drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 2 +- drivers/staging/imx-drm/imx-drm-core.c | 6 +++--- include/drm/drm_crtc.h | 2 ++ 49 files changed, 110 insertions(+), 82 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 7df3134ebc0e..b314a42bb18c 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -1610,7 +1610,7 @@ int max_width, max_height; The connector is then registered with a call to drm_connector_init with a pointer to the connector functions and a connector type, and exposed through sysfs with a call to - drm_sysfs_connector_add. + drm_connector_register. Supported connector types are @@ -1768,7 +1768,7 @@ int max_width, max_height; (drm_encoder_cleanup) and connectors (drm_connector_cleanup). Furthermore, connectors that have been added to sysfs must be removed by a call to - drm_sysfs_connector_remove before calling + drm_connector_unregister before calling drm_connector_cleanup. @@ -1813,7 +1813,7 @@ void intel_crt_init(struct drm_device *dev) drm_encoder_helper_add(&intel_output->enc, &intel_crt_helper_funcs); drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); }]]> In the example above (taken from the i915 driver), a CRTC, connector and diff --git a/drivers/gpu/drm/armada/armada_output.c b/drivers/gpu/drm/armada/armada_output.c index d685a5421485..abbc309fe539 100644 --- a/drivers/gpu/drm/armada/armada_output.c +++ b/drivers/gpu/drm/armada/armada_output.c @@ -48,7 +48,7 @@ static void armada_drm_connector_destroy(struct drm_connector *conn) { struct armada_connector *dconn = drm_to_armada_conn(conn); - drm_sysfs_connector_remove(conn); + drm_connector_unregister(conn); drm_connector_cleanup(conn); kfree(dconn); } @@ -141,7 +141,7 @@ int armada_output_create(struct drm_device *dev, if (ret) goto err_conn; - ret = drm_sysfs_connector_add(&dconn->conn); + ret = drm_connector_register(&dconn->conn); if (ret) goto err_sysfs; diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 114aee941d46..9896286ed262 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -829,7 +829,7 @@ static void ast_connector_destroy(struct drm_connector *connector) { struct ast_connector *ast_connector = to_ast_connector(connector); ast_i2c_destroy(ast_connector->i2c); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(connector); } @@ -871,7 +871,7 @@ static int ast_connector_init(struct drm_device *dev) connector->interlace_allowed = 0; connector->doublescan_allowed = 0; - drm_sysfs_connector_add(connector); + drm_connector_register(connector); connector->polled = DRM_CONNECTOR_POLL_CONNECT; diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c index 98fd17ae4916..d466696ed5e8 100644 --- a/drivers/gpu/drm/bridge/ptn3460.c +++ b/drivers/gpu/drm/bridge/ptn3460.c @@ -328,7 +328,7 @@ int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder, } drm_connector_helper_add(&ptn_bridge->connector, &ptn3460_connector_helper_funcs); - drm_sysfs_connector_add(&ptn_bridge->connector); + drm_connector_register(&ptn_bridge->connector); drm_mode_connector_attach_encoder(&ptn_bridge->connector, encoder); return 0; diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index fe94cc10cd35..c50c827cefb6 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -920,6 +920,34 @@ void drm_connector_cleanup(struct drm_connector *connector) } EXPORT_SYMBOL(drm_connector_cleanup); +/** + * drm_connector_register - register a connector + * @connector: the connector to register + * + * Register userspace interfaces for a connector + * + * Returns: + * Zero on success, error code on failure. + */ +int drm_connector_register(struct drm_connector *connector) +{ + return drm_sysfs_connector_add(connector); +} +EXPORT_SYMBOL(drm_connector_register); + +/** + * drm_connector_unregister - unregister a connector + * @connector: the connector to unregister + * + * Unregister userspace interfaces for a connector + */ +void drm_connector_unregister(struct drm_connector *connector) +{ + drm_sysfs_connector_remove(connector); +} +EXPORT_SYMBOL(drm_connector_unregister); + + /** * drm_connector_unplug_all - unregister connector userspace interfaces * @dev: drm device @@ -934,7 +962,7 @@ void drm_connector_unplug_all(struct drm_device *dev) /* taking the mode config mutex ends up in a clash with sysfs */ list_for_each_entry(connector, &dev->mode_config.connector_list, head) - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); } EXPORT_SYMBOL(drm_connector_unplug_all); diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index 369b26278e76..7827dad8fcf4 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -438,7 +438,6 @@ err_out_files: out: return ret; } -EXPORT_SYMBOL(drm_sysfs_connector_add); /** * drm_sysfs_connector_remove - remove an connector device from sysfs @@ -468,7 +467,6 @@ void drm_sysfs_connector_remove(struct drm_connector *connector) device_unregister(connector->kdev); connector->kdev = NULL; } -EXPORT_SYMBOL(drm_sysfs_connector_remove); /** * drm_sysfs_hotplug_event - generate a DRM uevent diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c index a8ffc8c1477b..86dc69d9eabb 100644 --- a/drivers/gpu/drm/exynos/exynos_dp_core.c +++ b/drivers/gpu/drm/exynos/exynos_dp_core.c @@ -1018,7 +1018,7 @@ static int exynos_dp_create_connector(struct exynos_drm_display *display, } drm_connector_helper_add(connector, &exynos_dp_connector_helper_funcs); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); drm_mode_connector_attach_encoder(connector, encoder); return 0; diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.c b/drivers/gpu/drm/exynos/exynos_drm_connector.c index 9a16dbe121d1..25c788832e2e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_connector.c +++ b/drivers/gpu/drm/exynos/exynos_drm_connector.c @@ -185,7 +185,7 @@ static void exynos_drm_connector_destroy(struct drm_connector *connector) struct exynos_drm_connector *exynos_connector = to_exynos_connector(connector); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(exynos_connector); } @@ -230,7 +230,7 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev, drm_connector_init(dev, connector, &exynos_connector_funcs, type); drm_connector_helper_add(connector, &exynos_connector_helper_funcs); - err = drm_sysfs_connector_add(connector); + err = drm_connector_register(connector); if (err) goto err_connector; @@ -250,7 +250,7 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev, return connector; err_sysfs: - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); err_connector: drm_connector_cleanup(connector); kfree(exynos_connector); diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c index 482127f633c5..4693531ebd48 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c @@ -48,7 +48,7 @@ exynos_dpi_detect(struct drm_connector *connector, bool force) static void exynos_dpi_connector_destroy(struct drm_connector *connector) { - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); } @@ -117,7 +117,7 @@ static int exynos_dpi_create_connector(struct exynos_drm_display *display, } drm_connector_helper_add(connector, &exynos_dpi_connector_helper_funcs); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); drm_mode_connector_attach_encoder(connector, encoder); return 0; diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 6302aa64f6c1..2df3592166de 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -1246,7 +1246,7 @@ static int exynos_dsi_create_connector(struct exynos_drm_display *display, } drm_connector_helper_add(connector, &exynos_dsi_connector_helper_funcs); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); drm_mode_connector_attach_encoder(connector, encoder); return 0; diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 2fb8705d6461..9528d81d8004 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -562,7 +562,7 @@ static int vidi_create_connector(struct exynos_drm_display *display, } drm_connector_helper_add(connector, &vidi_connector_helper_funcs); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); drm_mode_connector_attach_encoder(connector, encoder); return 0; diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index c104d0c9b385..dd565c4e5b4d 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -1129,7 +1129,7 @@ static int hdmi_create_connector(struct exynos_drm_display *display, } drm_connector_helper_add(connector, &hdmi_connector_helper_funcs); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); drm_mode_connector_attach_encoder(connector, encoder); return 0; diff --git a/drivers/gpu/drm/gma500/cdv_intel_crt.c b/drivers/gpu/drm/gma500/cdv_intel_crt.c index c18268cd516e..248c33a35ebf 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_crt.c +++ b/drivers/gpu/drm/gma500/cdv_intel_crt.c @@ -192,7 +192,7 @@ static void cdv_intel_crt_destroy(struct drm_connector *connector) struct gma_encoder *gma_encoder = gma_attached_encoder(connector); psb_intel_i2c_destroy(gma_encoder->ddc_bus); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(connector); } @@ -304,7 +304,7 @@ void cdv_intel_crt_init(struct drm_device *dev, drm_connector_helper_add(connector, &cdv_intel_crt_connector_helper_funcs); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); return; failed_ddc: diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c index 9ff30c2efadb..a4cc0e60a1be 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_dp.c +++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c @@ -1713,7 +1713,7 @@ cdv_intel_dp_destroy(struct drm_connector *connector) } } i2c_del_adapter(&intel_dp->adapter); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(connector); } @@ -1847,7 +1847,7 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev connector->interlace_allowed = false; connector->doublescan_allowed = false; - drm_sysfs_connector_add(connector); + drm_connector_register(connector); /* Set up the DDC bus. */ switch (output_reg) { diff --git a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c index b99084b3f706..4268bf210034 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c +++ b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c @@ -248,7 +248,7 @@ static void cdv_hdmi_destroy(struct drm_connector *connector) if (gma_encoder->i2c_bus) psb_intel_i2c_destroy(gma_encoder->i2c_bus); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(connector); } @@ -356,7 +356,7 @@ void cdv_hdmi_init(struct drm_device *dev, hdmi_priv->hdmi_i2c_adapter = &(gma_encoder->i2c_bus->adapter); hdmi_priv->dev = dev; - drm_sysfs_connector_add(connector); + drm_connector_register(connector); return; failed_ddc: diff --git a/drivers/gpu/drm/gma500/cdv_intel_lvds.c b/drivers/gpu/drm/gma500/cdv_intel_lvds.c index 8ecc920fc26d..0b770396548c 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_lvds.c +++ b/drivers/gpu/drm/gma500/cdv_intel_lvds.c @@ -446,7 +446,7 @@ static void cdv_intel_lvds_destroy(struct drm_connector *connector) if (gma_encoder->i2c_bus) psb_intel_i2c_destroy(gma_encoder->i2c_bus); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(connector); } @@ -774,7 +774,7 @@ void cdv_intel_lvds_init(struct drm_device *dev, out: mutex_unlock(&dev->mode_config.mutex); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); return; failed_find: diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_output.c b/drivers/gpu/drm/gma500/mdfld_dsi_output.c index 6e91b20ce2e5..abf2248da61e 100644 --- a/drivers/gpu/drm/gma500/mdfld_dsi_output.c +++ b/drivers/gpu/drm/gma500/mdfld_dsi_output.c @@ -318,7 +318,7 @@ static void mdfld_dsi_connector_destroy(struct drm_connector *connector) if (!dsi_connector) return; - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); sender = dsi_connector->pkg_sender; mdfld_dsi_pkg_sender_destroy(sender); @@ -597,7 +597,7 @@ void mdfld_dsi_output_init(struct drm_device *dev, dsi_config->encoder = encoder; encoder->base.type = (pipe == 0) ? INTEL_OUTPUT_MIPI : INTEL_OUTPUT_MIPI2; - drm_sysfs_connector_add(connector); + drm_connector_register(connector); return; /*TODO: add code to destroy outputs on error*/ diff --git a/drivers/gpu/drm/gma500/oaktrail_hdmi.c b/drivers/gpu/drm/gma500/oaktrail_hdmi.c index cf018ddcc5a6..e6f5c620a0a2 100644 --- a/drivers/gpu/drm/gma500/oaktrail_hdmi.c +++ b/drivers/gpu/drm/gma500/oaktrail_hdmi.c @@ -665,7 +665,7 @@ void oaktrail_hdmi_init(struct drm_device *dev, connector->display_info.subpixel_order = SubPixelHorizontalRGB; connector->interlace_allowed = false; connector->doublescan_allowed = false; - drm_sysfs_connector_add(connector); + drm_connector_register(connector); dev_info(dev->dev, "HDMI initialised.\n"); return; diff --git a/drivers/gpu/drm/gma500/oaktrail_lvds.c b/drivers/gpu/drm/gma500/oaktrail_lvds.c index 9b099468a5db..0d39da6e8b7a 100644 --- a/drivers/gpu/drm/gma500/oaktrail_lvds.c +++ b/drivers/gpu/drm/gma500/oaktrail_lvds.c @@ -404,7 +404,7 @@ void oaktrail_lvds_init(struct drm_device *dev, out: mutex_unlock(&dev->mode_config.mutex); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); return; failed_find: diff --git a/drivers/gpu/drm/gma500/psb_intel_lvds.c b/drivers/gpu/drm/gma500/psb_intel_lvds.c index d7778d0472c1..88aad95bde09 100644 --- a/drivers/gpu/drm/gma500/psb_intel_lvds.c +++ b/drivers/gpu/drm/gma500/psb_intel_lvds.c @@ -563,7 +563,7 @@ void psb_intel_lvds_destroy(struct drm_connector *connector) if (lvds_priv->ddc_bus) psb_intel_i2c_destroy(lvds_priv->ddc_bus); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(connector); } @@ -829,7 +829,7 @@ void psb_intel_lvds_init(struct drm_device *dev, */ out: mutex_unlock(&dev->mode_config.mutex); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); return; failed_find: diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c index deeb0829b129..0be96fdb5e28 100644 --- a/drivers/gpu/drm/gma500/psb_intel_sdvo.c +++ b/drivers/gpu/drm/gma500/psb_intel_sdvo.c @@ -1682,7 +1682,7 @@ static void psb_intel_sdvo_destroy(struct drm_connector *connector) psb_intel_sdvo_connector->tv_format); psb_intel_sdvo_destroy_enhance_property(connector); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(connector); } @@ -2071,7 +2071,7 @@ psb_intel_sdvo_connector_init(struct psb_intel_sdvo_connector *connector, connector->base.base.display_info.subpixel_order = SubPixelHorizontalRGB; gma_connector_attach_encoder(&connector->base, &encoder->base); - drm_sysfs_connector_add(&connector->base.base); + drm_connector_register(&connector->base.base); } static void diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 5a045d3bd77e..8da5ef9f4828 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -869,7 +869,7 @@ void intel_crt_init(struct drm_device *dev) drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); if (!I915_HAS_HOTPLUG(dev)) intel_connector->polled = DRM_CONNECTOR_POLL_CONNECT; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index efd3cf50cb0f..dbabaec6ae2d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -12234,7 +12234,7 @@ void intel_connector_unregister(struct intel_connector *intel_connector) struct drm_connector *connector = &intel_connector->base; intel_panel_destroy_backlight(connector); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); } void intel_modeset_cleanup(struct drm_device *dev) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 52fda950fd2a..99f033f69189 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -4246,7 +4246,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, edp_panel_vdd_work); intel_connector_attach_encoder(intel_connector, intel_encoder); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); if (HAS_DDI(dev)) intel_connector->get_hw_state = intel_ddi_connector_get_hw_state; @@ -4289,7 +4289,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, edp_panel_vdd_off_sync(intel_dp); drm_modeset_unlock(&dev->mode_config.connection_mutex); } - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); return false; } diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c index 02f99d768d49..7c07ee07a8ee 100644 --- a/drivers/gpu/drm/i915/intel_dsi.c +++ b/drivers/gpu/drm/i915/intel_dsi.c @@ -742,7 +742,7 @@ bool intel_dsi_init(struct drm_device *dev) intel_connector_attach_encoder(intel_connector, intel_encoder); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); fixed_mode = dsi->dev_ops->get_modes(&intel_dsi->dev); if (!fixed_mode) { diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index a3631c0a5c28..3fb71a04e14f 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -558,7 +558,7 @@ void intel_dvo_init(struct drm_device *dev) intel_dvo->panel_wants_dither = true; } - drm_sysfs_connector_add(connector); + drm_connector_register(connector); return; } diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index eee2bbec2958..0b603102cb3b 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -1490,7 +1490,7 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, intel_hdmi_add_properties(intel_hdmi, connector); intel_connector_attach_encoder(intel_connector, intel_encoder); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); /* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written * 0xd. Failure to do so will result in spurious interrupts being diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 23126023aeba..4d29a83fd163 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -1097,7 +1097,7 @@ out: DRM_DEBUG_KMS("lid notifier registration failed\n"); lvds_connector->lid_notifier.notifier_call = NULL; } - drm_sysfs_connector_add(connector); + drm_connector_register(connector); intel_panel_init(&intel_connector->panel, fixed_mode, downclock_mode); intel_panel_setup_backlight(connector); diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 6a4d5bc17697..e0be8ae75c15 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -2431,7 +2431,7 @@ intel_sdvo_connector_init(struct intel_sdvo_connector *connector, connector->base.unregister = intel_sdvo_connector_unregister; intel_connector_attach_encoder(&connector->base, &encoder->base); - ret = drm_sysfs_connector_add(drm_connector); + ret = drm_connector_register(drm_connector); if (ret < 0) goto err1; @@ -2444,7 +2444,7 @@ intel_sdvo_connector_init(struct intel_sdvo_connector *connector, return 0; err2: - drm_sysfs_connector_remove(drm_connector); + drm_connector_unregister(drm_connector); err1: drm_connector_cleanup(drm_connector); @@ -2557,7 +2557,7 @@ intel_sdvo_tv_init(struct intel_sdvo *intel_sdvo, int type) return true; err: - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); intel_sdvo_destroy(connector); return false; } @@ -2636,7 +2636,7 @@ intel_sdvo_lvds_init(struct intel_sdvo *intel_sdvo, int device) return true; err: - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); intel_sdvo_destroy(connector); return false; } @@ -2709,7 +2709,7 @@ static void intel_sdvo_output_cleanup(struct intel_sdvo *intel_sdvo) list_for_each_entry_safe(connector, tmp, &dev->mode_config.connector_list, head) { if (intel_attached_encoder(connector) == &intel_sdvo->base) { - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); intel_sdvo_destroy(connector); } } diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 67c6c9a2eb1c..e211eef4b7e4 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1680,5 +1680,5 @@ intel_tv_init(struct drm_device *dev) drm_object_attach_property(&connector->base, dev->mode_config.tv_bottom_margin_property, intel_tv->margin[TV_MARGIN_BOTTOM]); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); } diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c index a034ed408252..f9fe390920f1 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -1621,7 +1621,7 @@ static struct drm_connector *mga_vga_init(struct drm_device *dev) drm_connector_helper_add(connector, &mga_vga_connector_helper_funcs); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); mga_connector->i2c = mgag200_i2c_create(dev); if (!mga_connector->i2c) diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c index e56a6196867c..56c64c14c9b0 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c @@ -298,7 +298,7 @@ static void hdmi_connector_destroy(struct drm_connector *connector) hdp_disable(hdmi_connector); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); hdmi_unreference(hdmi_connector->hdmi); @@ -408,7 +408,7 @@ struct drm_connector *hdmi_connector_init(struct hdmi *hdmi) connector->interlace_allowed = 1; connector->doublescan_allowed = 0; - drm_sysfs_connector_add(connector); + drm_connector_register(connector); ret = hpd_enable(hdmi_connector); if (ret) { diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 1fa222e8f007..680f46d007a0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -104,7 +104,7 @@ nouveau_connector_destroy(struct drm_connector *connector) struct nouveau_connector *nv_connector = nouveau_connector(connector); nouveau_event_ref(NULL, &nv_connector->hpd); kfree(nv_connector->edid); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); if (nv_connector->aux.transfer) drm_dp_aux_unregister(&nv_connector->aux); @@ -1236,6 +1236,6 @@ nouveau_connector_create(struct drm_device *dev, int index) INIT_WORK(&nv_connector->work, nouveau_connector_hotplug_work); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); return connector; } diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c index 86f4ead0441d..36bc5cc80816 100644 --- a/drivers/gpu/drm/omapdrm/omap_connector.c +++ b/drivers/gpu/drm/omapdrm/omap_connector.c @@ -130,7 +130,7 @@ static void omap_connector_destroy(struct drm_connector *connector) struct omap_dss_device *dssdev = omap_connector->dssdev; DBG("%s", omap_connector->dssdev->name); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(omap_connector); @@ -307,7 +307,7 @@ struct drm_connector *omap_connector_init(struct drm_device *dev, connector->interlace_allowed = 1; connector->doublescan_allowed = 0; - drm_sysfs_connector_add(connector); + drm_connector_register(connector); return connector; diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index 5d7ea2461852..b8ced08b6291 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -835,7 +835,7 @@ static void qxl_conn_destroy(struct drm_connector *connector) struct qxl_output *qxl_output = drm_connector_to_qxl_output(connector); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(qxl_output); } @@ -902,7 +902,7 @@ static int qdev_output_init(struct drm_device *dev, int num_output) drm_object_attach_property(&connector->base, qdev->hotplug_mode_update_property, 0); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); return 0; } diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index 933c5c39654d..5b1a611c30eb 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -737,7 +737,7 @@ static void radeon_connector_destroy(struct drm_connector *connector) if (radeon_connector->edid) kfree(radeon_connector->edid); kfree(radeon_connector->con_priv); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(connector); } @@ -2038,7 +2038,7 @@ radeon_add_atom_connector(struct drm_device *dev, connector->polled = DRM_CONNECTOR_POLL_HPD; connector->display_info.subpixel_order = subpixel_order; - drm_sysfs_connector_add(connector); + drm_connector_register(connector); if (has_aux) radeon_dp_aux_init(radeon_connector); @@ -2199,5 +2199,5 @@ radeon_add_legacy_connector(struct drm_device *dev, } else connector->polled = DRM_CONNECTOR_POLL_HPD; connector->display_info.subpixel_order = subpixel_order; - drm_sysfs_connector_add(connector); + drm_connector_register(connector); } diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c index 289048d1c7b2..21426bd234eb 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c @@ -64,7 +64,7 @@ static const struct drm_connector_helper_funcs connector_helper_funcs = { static void rcar_du_lvds_connector_destroy(struct drm_connector *connector) { - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); } @@ -105,7 +105,7 @@ int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu, return ret; drm_connector_helper_add(connector, &connector_helper_funcs); - ret = drm_sysfs_connector_add(connector); + ret = drm_connector_register(connector); if (ret < 0) return ret; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c index ccfe64c7188f..8af3944d31b9 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c @@ -32,7 +32,7 @@ static const struct drm_connector_helper_funcs connector_helper_funcs = { static void rcar_du_vga_connector_destroy(struct drm_connector *connector) { - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); } @@ -70,7 +70,7 @@ int rcar_du_vga_connector_init(struct rcar_du_device *rcdu, return ret; drm_connector_helper_add(connector, &connector_helper_funcs); - ret = drm_sysfs_connector_add(connector); + ret = drm_connector_register(connector); if (ret < 0) return ret; diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c index faf176b2daf9..47875de89010 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c @@ -692,7 +692,7 @@ static void shmob_drm_connector_destroy(struct drm_connector *connector) struct shmob_drm_connector *scon = to_shmob_connector(connector); shmob_drm_backlight_exit(scon); - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); } @@ -726,7 +726,7 @@ int shmob_drm_connector_create(struct shmob_drm_device *sdev, return ret; drm_connector_helper_add(connector, &connector_helper_funcs); - ret = drm_sysfs_connector_add(connector); + ret = drm_connector_register(connector); if (ret < 0) goto err_cleanup; @@ -749,7 +749,7 @@ int shmob_drm_connector_create(struct shmob_drm_device *sdev, err_backlight: shmob_drm_backlight_exit(&sdev->connector); err_sysfs: - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); err_cleanup: drm_connector_cleanup(connector); return ret; diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c index a3e4f1eca6f7..446837e955b6 100644 --- a/drivers/gpu/drm/tegra/output.c +++ b/drivers/gpu/drm/tegra/output.c @@ -105,7 +105,7 @@ static void drm_connector_clear(struct drm_connector *connector) static void tegra_connector_destroy(struct drm_connector *connector) { - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); drm_connector_clear(connector); } @@ -318,7 +318,7 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output) drm_encoder_helper_add(&output->encoder, &encoder_helper_funcs); drm_mode_connector_attach_encoder(&output->connector, &output->encoder); - drm_sysfs_connector_add(&output->connector); + drm_connector_register(&output->connector); output->encoder.possible_crtcs = 0x3; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_panel.c b/drivers/gpu/drm/tilcdc/tilcdc_panel.c index 86c67329b605..0c0bce7e9007 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_panel.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_panel.c @@ -247,7 +247,7 @@ static struct drm_connector *panel_connector_create(struct drm_device *dev, if (ret) goto fail; - drm_sysfs_connector_add(connector); + drm_connector_register(connector); return connector; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_slave.c b/drivers/gpu/drm/tilcdc/tilcdc_slave.c index 595068ba2d5e..c578d144ce6d 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_slave.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_slave.c @@ -261,7 +261,7 @@ static struct drm_connector *slave_connector_create(struct drm_device *dev, if (ret) goto fail; - drm_sysfs_connector_add(connector); + drm_connector_register(connector); return connector; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c index c38b56b268ac..5324dfd0b5ed 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c @@ -261,7 +261,7 @@ static struct drm_connector *tfp410_connector_create(struct drm_device *dev, if (ret) goto fail; - drm_sysfs_connector_add(connector); + drm_connector_register(connector); return connector; diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c index b44d548c56f8..dea38ab6c47d 100644 --- a/drivers/gpu/drm/udl/udl_connector.c +++ b/drivers/gpu/drm/udl/udl_connector.c @@ -124,7 +124,7 @@ static int udl_connector_set_property(struct drm_connector *connector, static void udl_connector_destroy(struct drm_connector *connector) { - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(connector); } @@ -154,7 +154,7 @@ int udl_connector_init(struct drm_device *dev, struct drm_encoder *encoder) drm_connector_init(dev, connector, &udl_connector_funcs, DRM_MODE_CONNECTOR_DVII); drm_connector_helper_add(connector, &udl_connector_helper_funcs); - drm_sysfs_connector_add(connector); + drm_connector_register(connector); drm_mode_connector_attach_encoder(connector, encoder); drm_object_attach_property(&connector->base, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 8f3edc4710f2..1a024e3b7285 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -75,7 +75,7 @@ void vmw_display_unit_cleanup(struct vmw_display_unit *du) vmw_surface_unreference(&du->cursor_surface); if (du->cursor_dmabuf) vmw_dmabuf_unreference(&du->cursor_dmabuf); - drm_sysfs_connector_remove(&du->connector); + drm_connector_unregister(&du->connector); drm_crtc_cleanup(&du->crtc); drm_encoder_cleanup(&du->encoder); drm_connector_cleanup(&du->connector); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index b2b9bd23aeee..15e185ae4c99 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -371,7 +371,7 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) encoder->possible_crtcs = (1 << unit); encoder->possible_clones = 0; - (void) drm_sysfs_connector_add(connector); + (void) drm_connector_register(connector); drm_crtc_init(dev, crtc, &vmw_legacy_crtc_funcs); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index a95d3a0cabe4..b295463a60b3 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -467,7 +467,7 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) encoder->possible_crtcs = (1 << unit); encoder->possible_clones = 0; - (void) drm_sysfs_connector_add(connector); + (void) drm_connector_register(connector); drm_crtc_init(dev, crtc, &vmw_screen_object_crtc_funcs); diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c index def8280d7ee6..6f54ff4f9372 100644 --- a/drivers/staging/imx-drm/imx-drm-core.c +++ b/drivers/staging/imx-drm/imx-drm-core.c @@ -202,7 +202,7 @@ static const struct file_operations imx_drm_driver_fops = { void imx_drm_connector_destroy(struct drm_connector *connector) { - drm_sysfs_connector_remove(connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); } EXPORT_SYMBOL_GPL(imx_drm_connector_destroy); @@ -293,10 +293,10 @@ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags) * userspace will expect to be able to access DRM at this point. */ list_for_each_entry(connector, &drm->mode_config.connector_list, head) { - ret = drm_sysfs_connector_add(connector); + ret = drm_connector_register(connector); if (ret) { dev_err(drm->dev, - "[CONNECTOR:%d:%s] drm_sysfs_connector_add failed: %d\n", + "[CONNECTOR:%d:%s] drm_connector_register failed: %d\n", connector->base.id, connector->name, ret); goto err_unbind; diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 251b75e6bf7a..5512c9968d77 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -878,6 +878,8 @@ extern int drm_connector_init(struct drm_device *dev, struct drm_connector *connector, const struct drm_connector_funcs *funcs, int connector_type); +int drm_connector_register(struct drm_connector *connector); +void drm_connector_unregister(struct drm_connector *connector); extern void drm_connector_cleanup(struct drm_connector *connector); /* helper to unplug all connectors from sysfs for device */ -- cgit v1.2.3 From 30f6570798f6c897df4f1f2c676d803728bfec27 Mon Sep 17 00:00:00 2001 From: Thomas Wood Date: Wed, 18 Jun 2014 17:52:32 +0100 Subject: drm/debugfs: add a "force" file per connector Add a file to debugfs for each connector to enable modification of the "force" connector attribute. This allows connectors to be enabled or disabled for testing and debugging purposes. v2: Add stricter value checking and clean up debugfs_entry if file creation fails in drm_debugfs_connector_add. (David Herrmann) Signed-off-by: Thomas Wood Reviewed-by: Alex Deucher Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 17 ++++++- drivers/gpu/drm/drm_debugfs.c | 114 ++++++++++++++++++++++++++++++++++++++++++ include/drm/drmP.h | 11 ++++ include/drm/drm_crtc.h | 2 + 4 files changed, 143 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index c50c827cefb6..c304e5650bc8 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -881,6 +881,8 @@ int drm_connector_init(struct drm_device *dev, drm_object_attach_property(&connector->base, dev->mode_config.dpms_property, 0); + connector->debugfs_entry = NULL; + out_put: if (ret) drm_mode_object_put(dev, &connector->base); @@ -931,7 +933,19 @@ EXPORT_SYMBOL(drm_connector_cleanup); */ int drm_connector_register(struct drm_connector *connector) { - return drm_sysfs_connector_add(connector); + int ret; + + ret = drm_sysfs_connector_add(connector); + if (ret) + return ret; + + ret = drm_debugfs_connector_add(connector); + if (ret) { + drm_sysfs_connector_remove(connector); + return ret; + } + + return 0; } EXPORT_SYMBOL(drm_connector_register); @@ -944,6 +958,7 @@ EXPORT_SYMBOL(drm_connector_register); void drm_connector_unregister(struct drm_connector *connector) { drm_sysfs_connector_remove(connector); + drm_debugfs_connector_remove(connector); } EXPORT_SYMBOL(drm_connector_unregister); diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index b4b51d46f339..8ab3f3e38643 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -237,5 +237,119 @@ int drm_debugfs_cleanup(struct drm_minor *minor) return 0; } +static int connector_show(struct seq_file *m, void *data) +{ + struct drm_connector *connector = m->private; + const char *status; + + switch (connector->force) { + case DRM_FORCE_ON: + status = "on\n"; + break; + + case DRM_FORCE_ON_DIGITAL: + status = "digital\n"; + break; + + case DRM_FORCE_OFF: + status = "off\n"; + break; + + case DRM_FORCE_UNSPECIFIED: + status = "unspecified\n"; + break; + + default: + return 0; + } + + seq_puts(m, status); + + return 0; +} + +static int connector_open(struct inode *inode, struct file *file) +{ + struct drm_connector *dev = inode->i_private; + + return single_open(file, connector_show, dev); +} + +static ssize_t connector_write(struct file *file, const char __user *ubuf, + size_t len, loff_t *offp) +{ + struct seq_file *m = file->private_data; + struct drm_connector *connector = m->private; + char buf[12]; + + if (len > sizeof(buf) - 1) + return -EINVAL; + + if (copy_from_user(buf, ubuf, len)) + return -EFAULT; + + buf[len] = '\0'; + + if (!strcmp(buf, "on")) + connector->force = DRM_FORCE_ON; + else if (!strcmp(buf, "digital")) + connector->force = DRM_FORCE_ON_DIGITAL; + else if (!strcmp(buf, "off")) + connector->force = DRM_FORCE_OFF; + else if (!strcmp(buf, "unspecified")) + connector->force = DRM_FORCE_UNSPECIFIED; + else + return -EINVAL; + + return len; +} + +static const struct file_operations drm_connector_fops = { + .owner = THIS_MODULE, + .open = connector_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = connector_write +}; + +int drm_debugfs_connector_add(struct drm_connector *connector) +{ + struct drm_minor *minor = connector->dev->primary; + struct dentry *root, *ent; + + if (!minor->debugfs_root) + return -1; + + root = debugfs_create_dir(connector->name, minor->debugfs_root); + if (!root) + return -ENOMEM; + + connector->debugfs_entry = root; + + /* force */ + ent = debugfs_create_file("force", S_IRUGO | S_IWUSR, root, connector, + &drm_connector_fops); + if (!ent) + goto error; + + return 0; + +error: + debugfs_remove_recursive(connector->debugfs_entry); + connector->debugfs_entry = NULL; + return -ENOMEM; +} + +void drm_debugfs_connector_remove(struct drm_connector *connector) +{ + if (!connector->debugfs_entry) + return; + + debugfs_remove_recursive(connector->debugfs_entry); + + connector->debugfs_entry = NULL; +} + #endif /* CONFIG_DEBUG_FS */ diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 8af71a8e2c00..57ecc421b19c 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1419,6 +1419,8 @@ extern int drm_debugfs_create_files(const struct drm_info_list *files, extern int drm_debugfs_remove_files(const struct drm_info_list *files, int count, struct drm_minor *minor); extern int drm_debugfs_cleanup(struct drm_minor *minor); +extern int drm_debugfs_connector_add(struct drm_connector *connector); +extern void drm_debugfs_connector_remove(struct drm_connector *connector); #else static inline int drm_debugfs_init(struct drm_minor *minor, int minor_id, struct dentry *root) @@ -1443,6 +1445,15 @@ static inline int drm_debugfs_cleanup(struct drm_minor *minor) { return 0; } + +static inline int drm_debugfs_connector_add(struct drm_connector *connector) +{ + return 0; +} +static inline void drm_debugfs_connector_remove(struct drm_connector *connector) +{ +} + #endif /* Info file support */ diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 5512c9968d77..d4b0a6a4ad9b 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -545,6 +545,8 @@ struct drm_connector { int audio_latency[2]; int null_edid_counter; /* needed to workaround some HW bugs where we get all 0s */ unsigned bad_edid_counter; + + struct dentry *debugfs_entry; }; /** -- cgit v1.2.3 From 4cf2b28146713d39f78bd62002a8ab00075c63b7 Mon Sep 17 00:00:00 2001 From: Thomas Wood Date: Wed, 18 Jun 2014 17:52:33 +0100 Subject: drm/debugfs: add an "edid_override" file per connector Add a file to debugfs for each connector to allow the EDID to be overridden. v2: Copy ubuf before accessing it and reject invalid length data. (David Herrmann) Ensure override_edid is reset when a new EDID value is written. (David Herrmann) Fix the debugfs file permissions. (David Herrmann) Signed-off-by: Thomas Wood Reviewed-by: Alex Deucher Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 4 +++ drivers/gpu/drm/drm_debugfs.c | 68 ++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_probe_helper.c | 9 ++++- include/drm/drm_crtc.h | 1 + 4 files changed, 81 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index c304e5650bc8..7db6251c38e7 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -3763,6 +3763,10 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector, struct drm_device *dev = connector->dev; int ret, size; + /* ignore requests to set edid when overridden */ + if (connector->override_edid) + return 0; + if (connector->edid_blob_ptr) drm_property_destroy_blob(dev, connector->edid_blob_ptr); diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index 8ab3f3e38643..13bd42923dd4 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -35,6 +35,7 @@ #include #include #include +#include #if defined(CONFIG_DEBUG_FS) @@ -304,6 +305,67 @@ static ssize_t connector_write(struct file *file, const char __user *ubuf, return len; } +static int edid_show(struct seq_file *m, void *data) +{ + struct drm_connector *connector = m->private; + struct drm_property_blob *edid = connector->edid_blob_ptr; + + if (connector->override_edid && edid) + seq_write(m, edid->data, edid->length); + + return 0; +} + +static int edid_open(struct inode *inode, struct file *file) +{ + struct drm_connector *dev = inode->i_private; + + return single_open(file, edid_show, dev); +} + +static ssize_t edid_write(struct file *file, const char __user *ubuf, + size_t len, loff_t *offp) +{ + struct seq_file *m = file->private_data; + struct drm_connector *connector = m->private; + char *buf; + struct edid *edid; + int ret; + + buf = memdup_user(ubuf, len); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + edid = (struct edid *) buf; + + if (len == 5 && !strncmp(buf, "reset", 5)) { + connector->override_edid = false; + ret = drm_mode_connector_update_edid_property(connector, NULL); + } else if (len < EDID_LENGTH || + EDID_LENGTH * (1 + edid->extensions) > len) + ret = -EINVAL; + else { + connector->override_edid = false; + ret = drm_mode_connector_update_edid_property(connector, edid); + if (!ret) + connector->override_edid = true; + } + + kfree(buf); + + return (ret) ? ret : len; +} + +static const struct file_operations drm_edid_fops = { + .owner = THIS_MODULE, + .open = edid_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = edid_write +}; + + static const struct file_operations drm_connector_fops = { .owner = THIS_MODULE, .open = connector_open, @@ -333,6 +395,12 @@ int drm_debugfs_connector_add(struct drm_connector *connector) if (!ent) goto error; + /* edid */ + ent = debugfs_create_file("edid_override", S_IRUGO | S_IWUSR, root, + connector, &drm_edid_fops); + if (!ent) + goto error; + return 0; error: diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index d22676b89cbb..db7d250f7ac7 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -130,7 +130,14 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect count = drm_load_edid_firmware(connector); if (count == 0) #endif - count = (*connector_funcs->get_modes)(connector); + { + if (connector->override_edid) { + struct edid *edid = (struct edid *) connector->edid_blob_ptr->data; + + count = drm_add_edid_modes(connector, edid); + } else + count = (*connector_funcs->get_modes)(connector); + } if (count == 0 && connector->status == connector_status_connected) count = drm_add_modes_noedid(connector, 1024, 768); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index d4b0a6a4ad9b..857bbb1551f8 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -533,6 +533,7 @@ struct drm_connector { /* forced on connector */ enum drm_connector_force force; + bool override_edid; uint32_t encoder_ids[DRM_CONNECTOR_MAX_ENCODER]; struct drm_encoder *encoder; /* currently active encoder */ -- cgit v1.2.3 From f2af74123f8c5a735248547f4286a3adc28633c1 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Tue, 20 May 2014 09:38:03 -1000 Subject: tools: ffs-test: convert to new descriptor format fixing compilation error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit [ac8dde11: “usb: gadget: f_fs: Add flags to descriptors block”] which introduced a new descriptor format for FunctionFS removed the usb_functionfs_descs_head structure, which is still used by ffs-test. tool. Convert ffs-test by converting it to use the new header format. For testing kernels prior to 3.14 (when the new format was introduced) and parsing of the legacy headers in the new kernels, provide a compilation flag to make the tool use the old format. Finally, include information as to when the legacy FunctionFS headers format has been deprecated (which is also when the new one has been introduced). Reported-by: Lad, Prabhakar Signed-off-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- include/uapi/linux/usb/functionfs.h | 2 +- tools/usb/Makefile | 6 +++++- tools/usb/ffs-test.c | 20 ++++++++++++++++++-- 3 files changed, 24 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/usb/functionfs.h b/include/uapi/linux/usb/functionfs.h index 2a4b4a72a4f9..ecb3a31f7ca6 100644 --- a/include/uapi/linux/usb/functionfs.h +++ b/include/uapi/linux/usb/functionfs.h @@ -53,7 +53,7 @@ struct usb_endpoint_descriptor_no_audio { * structure. Any flags that are not recognised cause the whole block to be * rejected with -ENOSYS. * - * Legacy descriptors format: + * Legacy descriptors format (deprecated as of 3.14): * * | off | name | type | description | * |-----+-----------+--------------+--------------------------------------| diff --git a/tools/usb/Makefile b/tools/usb/Makefile index acf2165c04e6..d576b3bac3cf 100644 --- a/tools/usb/Makefile +++ b/tools/usb/Makefile @@ -6,7 +6,11 @@ WARNINGS = -Wall -Wextra CFLAGS = $(WARNINGS) -g -I../include LDFLAGS = $(PTHREAD_LIBS) -all: testusb ffs-test +all: testusb ffs-test ffs-test-legacy + +ffs-test-legacy: ffs-test.c + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) -DUSE_LEGACY_DESC_HEAD + %: %.c $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) diff --git a/tools/usb/ffs-test.c b/tools/usb/ffs-test.c index fe1e66b6ef40..74b353d9eb50 100644 --- a/tools/usb/ffs-test.c +++ b/tools/usb/ffs-test.c @@ -1,5 +1,5 @@ /* - * ffs-test.c.c -- user mode filesystem api for usb composite function + * ffs-test.c -- user mode filesystem api for usb composite function * * Copyright (C) 2010 Samsung Electronics * Author: Michal Nazarewicz @@ -21,6 +21,8 @@ /* $(CROSS_COMPILE)cc -Wall -Wextra -g -o ffs-test ffs-test.c -lpthread */ +/* Uncomment to make the tool use legacy FFS descriptor headers. */ +/* #define USE_LEGACY_DESC_HEAD */ #define _BSD_SOURCE /* for endian.h */ @@ -106,7 +108,15 @@ static void _msg(unsigned level, const char *fmt, ...) /******************** Descriptors and Strings *******************************/ static const struct { - struct usb_functionfs_descs_head header; + struct { + __le32 magic; + __le32 length; +#ifndef USE_LEGACY_DESC_HEAD + __le32 flags; +#endif + __le32 fs_count; + __le32 hs_count; + } __attribute__((packed)) header; struct { struct usb_interface_descriptor intf; struct usb_endpoint_descriptor_no_audio sink; @@ -114,7 +124,13 @@ static const struct { } __attribute__((packed)) fs_descs, hs_descs; } __attribute__((packed)) descriptors = { .header = { +#ifdef USE_LEGACY_DESC_HEAD .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC), +#else + .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2), + .flags = cpu_to_le32(FUNCTIONFS_HAS_FS_DESC | + FUNCTIONFS_HAS_HS_DESC), +#endif .length = cpu_to_le32(sizeof descriptors), .fs_count = 3, .hs_count = 3, -- cgit v1.2.3 From 7d2a01b87f1682fde87461864e6682031bfaa0a9 Mon Sep 17 00:00:00 2001 From: Andreas Noever Date: Tue, 3 Jun 2014 22:04:09 +0200 Subject: PCI: Add pci_fixup_suspend_late quirk pass Add pci_fixup_suspend_late as a new pci_fixup_pass. The pass is called from suspend_noirq and poweroff_noirq. Using the same pass for suspend and hibernate is consistent with resume_early which is called by resume_noirq and restore_noirq. The new quirk pass is required for Thunderbolt support on Apple hardware. Signed-off-by: Andreas Noever Acked-by: Bjorn Helgaas Signed-off-by: Greg Kroah-Hartman --- drivers/pci/pci-driver.c | 18 ++++++++++++++---- drivers/pci/quirks.c | 7 +++++++ include/asm-generic/vmlinux.lds.h | 3 +++ include/linux/pci.h | 12 +++++++++++- 4 files changed, 35 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 3f8e3dbcaa7c..d04c5adafc16 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -582,7 +582,7 @@ static int pci_legacy_suspend_late(struct device *dev, pm_message_t state) WARN_ONCE(pci_dev->current_state != prev, "PCI PM: Device state not saved by %pF\n", drv->suspend_late); - return 0; + goto Fixup; } } @@ -591,6 +591,9 @@ static int pci_legacy_suspend_late(struct device *dev, pm_message_t state) pci_pm_set_unknown_state(pci_dev); +Fixup: + pci_fixup_device(pci_fixup_suspend_late, pci_dev); + return 0; } @@ -734,7 +737,7 @@ static int pci_pm_suspend_noirq(struct device *dev) if (!pm) { pci_save_state(pci_dev); - return 0; + goto Fixup; } if (pm->suspend_noirq) { @@ -751,7 +754,7 @@ static int pci_pm_suspend_noirq(struct device *dev) WARN_ONCE(pci_dev->current_state != prev, "PCI PM: State of device not saved by %pF\n", pm->suspend_noirq); - return 0; + goto Fixup; } } @@ -775,6 +778,9 @@ static int pci_pm_suspend_noirq(struct device *dev) if (pci_dev->class == PCI_CLASS_SERIAL_USB_EHCI) pci_write_config_word(pci_dev, PCI_COMMAND, 0); +Fixup: + pci_fixup_device(pci_fixup_suspend_late, pci_dev); + return 0; } @@ -999,8 +1005,10 @@ static int pci_pm_poweroff_noirq(struct device *dev) if (pci_has_legacy_pm_support(to_pci_dev(dev))) return pci_legacy_suspend_late(dev, PMSG_HIBERNATE); - if (!drv || !drv->pm) + if (!drv || !drv->pm) { + pci_fixup_device(pci_fixup_suspend_late, pci_dev); return 0; + } if (drv->pm->poweroff_noirq) { int error; @@ -1021,6 +1029,8 @@ static int pci_pm_poweroff_noirq(struct device *dev) if (pci_dev->class == PCI_CLASS_SERIAL_USB_EHCI) pci_write_config_word(pci_dev, PCI_COMMAND, 0); + pci_fixup_device(pci_fixup_suspend_late, pci_dev); + if (pcibios_pm_ops.poweroff_noirq) return pcibios_pm_ops.poweroff_noirq(dev); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index d0f69269eb6c..03266af20d5f 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3018,6 +3018,8 @@ extern struct pci_fixup __start_pci_fixups_resume_early[]; extern struct pci_fixup __end_pci_fixups_resume_early[]; extern struct pci_fixup __start_pci_fixups_suspend[]; extern struct pci_fixup __end_pci_fixups_suspend[]; +extern struct pci_fixup __start_pci_fixups_suspend_late[]; +extern struct pci_fixup __end_pci_fixups_suspend_late[]; static bool pci_apply_fixup_final_quirks; @@ -3063,6 +3065,11 @@ void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev) end = __end_pci_fixups_suspend; break; + case pci_fixup_suspend_late: + start = __start_pci_fixups_suspend_late; + end = __end_pci_fixups_suspend_late; + break; + default: /* stupid compiler warning, you would think with an enum... */ return; diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 471ba48c7ae4..47cd98656f9d 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -268,6 +268,9 @@ VMLINUX_SYMBOL(__start_pci_fixups_suspend) = .; \ *(.pci_fixup_suspend) \ VMLINUX_SYMBOL(__end_pci_fixups_suspend) = .; \ + VMLINUX_SYMBOL(__start_pci_fixups_suspend_late) = .; \ + *(.pci_fixup_suspend_late) \ + VMLINUX_SYMBOL(__end_pci_fixups_suspend_late) = .; \ } \ \ /* Built-in firmware blobs */ \ diff --git a/include/linux/pci.h b/include/linux/pci.h index 466bcd111d85..295d3a9d8ffe 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1477,8 +1477,9 @@ enum pci_fixup_pass { pci_fixup_final, /* Final phase of device fixups */ pci_fixup_enable, /* pci_enable_device() time */ pci_fixup_resume, /* pci_device_resume() */ - pci_fixup_suspend, /* pci_device_suspend */ + pci_fixup_suspend, /* pci_device_suspend() */ pci_fixup_resume_early, /* pci_device_resume_early() */ + pci_fixup_suspend_late, /* pci_device_suspend_late() */ }; /* Anonymous variables would be nice... */ @@ -1519,6 +1520,11 @@ enum pci_fixup_pass { DECLARE_PCI_FIXUP_SECTION(.pci_fixup_suspend, \ suspend##hook, vendor, device, class, \ class_shift, hook) +#define DECLARE_PCI_FIXUP_CLASS_SUSPEND_LATE(vendor, device, class, \ + class_shift, hook) \ + DECLARE_PCI_FIXUP_SECTION(.pci_fixup_suspend_late, \ + suspend_late##hook, vendor, device, \ + class, class_shift, hook) #define DECLARE_PCI_FIXUP_EARLY(vendor, device, hook) \ DECLARE_PCI_FIXUP_SECTION(.pci_fixup_early, \ @@ -1544,6 +1550,10 @@ enum pci_fixup_pass { DECLARE_PCI_FIXUP_SECTION(.pci_fixup_suspend, \ suspend##hook, vendor, device, \ PCI_ANY_ID, 0, hook) +#define DECLARE_PCI_FIXUP_SUSPEND_LATE(vendor, device, hook) \ + DECLARE_PCI_FIXUP_SECTION(.pci_fixup_suspend_late, \ + suspend_late##hook, vendor, device, \ + PCI_ANY_ID, 0, hook) #ifdef CONFIG_PCI_QUIRKS void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev); -- cgit v1.2.3 From c9d53c0f2d23c792e4b9cf1551b63de4516f839e Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 11 Jun 2014 14:00:05 +0900 Subject: devres: remove devm_request_and_ioremap() devm_request_and_ioremap() was obsoleted by the commit 7509657 ("lib: devres: Introduce devm_ioremap_resource()") and has been deprecated for a long time. So, let's remove this function. In addition, all usages of devm_request_and_ioremap() are also removed. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-model/devres.txt | 1 - drivers/bus/brcmstb_gisb.c | 6 +- drivers/gpu/drm/armada/armada_crtc.c | 8 +- include/linux/device.h | 2 - lib/devres.c | 28 ------- scripts/coccinelle/api/devm_ioremap_resource.cocci | 90 ---------------------- 6 files changed, 6 insertions(+), 129 deletions(-) delete mode 100644 scripts/coccinelle/api/devm_ioremap_resource.cocci (limited to 'include') diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index 1525e30483fd..de6bc8c325e9 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -278,7 +278,6 @@ IOMAP devm_ioremap_nocache() devm_iounmap() devm_ioremap_resource() : checks resource, requests memory region, ioremaps - devm_request_and_ioremap() : obsoleted by devm_ioremap_resource() pcim_iomap() pcim_iounmap() pcim_iomap_table() : array of mapped addresses indexed by BAR diff --git a/drivers/bus/brcmstb_gisb.c b/drivers/bus/brcmstb_gisb.c index 6159b7752a64..f2cd6a2d40b4 100644 --- a/drivers/bus/brcmstb_gisb.c +++ b/drivers/bus/brcmstb_gisb.c @@ -212,9 +212,9 @@ static int brcmstb_gisb_arb_probe(struct platform_device *pdev) mutex_init(&gdev->lock); INIT_LIST_HEAD(&gdev->next); - gdev->base = devm_request_and_ioremap(&pdev->dev, r); - if (!gdev->base) - return -ENOMEM; + gdev->base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(gdev->base)) + return PTR_ERR(gdev->base); err = devm_request_irq(&pdev->dev, timeout_irq, brcmstb_gisb_timeout_handler, 0, pdev->name, diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index 81c34f949dfc..3aedf9e993e6 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -1039,11 +1039,9 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num, if (ret) return ret; - base = devm_request_and_ioremap(dev->dev, res); - if (!base) { - DRM_ERROR("failed to ioremap register\n"); - return -ENOMEM; - } + base = devm_ioremap_resource(dev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); dcrtc = kzalloc(sizeof(*dcrtc), GFP_KERNEL); if (!dcrtc) { diff --git a/include/linux/device.h b/include/linux/device.h index af424acd393d..921fa0a74df6 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -631,8 +631,6 @@ extern unsigned long devm_get_free_pages(struct device *dev, extern void devm_free_pages(struct device *dev, unsigned long addr); void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res); -void __iomem *devm_request_and_ioremap(struct device *dev, - struct resource *res); /* allows to add/remove a custom action to devres stack */ int devm_add_action(struct device *dev, void (*action)(void *), void *data); diff --git a/lib/devres.c b/lib/devres.c index f562bf6ff71d..6a4aee8a3a7e 100644 --- a/lib/devres.c +++ b/lib/devres.c @@ -142,34 +142,6 @@ void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res) } EXPORT_SYMBOL(devm_ioremap_resource); -/** - * devm_request_and_ioremap() - Check, request region, and ioremap resource - * @dev: Generic device to handle the resource for - * @res: resource to be handled - * - * Takes all necessary steps to ioremap a mem resource. Uses managed device, so - * everything is undone on driver detach. Checks arguments, so you can feed - * it the result from e.g. platform_get_resource() directly. Returns the - * remapped pointer or NULL on error. Usage example: - * - * res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - * base = devm_request_and_ioremap(&pdev->dev, res); - * if (!base) - * return -EADDRNOTAVAIL; - */ -void __iomem *devm_request_and_ioremap(struct device *dev, - struct resource *res) -{ - void __iomem *dest_ptr; - - dest_ptr = devm_ioremap_resource(dev, res); - if (IS_ERR(dest_ptr)) - return NULL; - - return dest_ptr; -} -EXPORT_SYMBOL(devm_request_and_ioremap); - #ifdef CONFIG_HAS_IOPORT_MAP /* * Generic iomap devres diff --git a/scripts/coccinelle/api/devm_ioremap_resource.cocci b/scripts/coccinelle/api/devm_ioremap_resource.cocci deleted file mode 100644 index 495daa3dbf77..000000000000 --- a/scripts/coccinelle/api/devm_ioremap_resource.cocci +++ /dev/null @@ -1,90 +0,0 @@ -virtual patch -virtual report - -@depends on patch@ -expression base, dev, res; -@@ - --base = devm_request_and_ioremap(dev, res); -+base = devm_ioremap_resource(dev, res); - ... - if ( --base == NULL -+IS_ERR(base) - || ...) { -<... -- return ...; -+ return PTR_ERR(base); -...> - } - -@depends on patch@ -expression e, E, ret; -identifier l; -@@ - - e = devm_ioremap_resource(...); - ... - if (IS_ERR(e) || ...) { - ... when any -- ret = E; -+ ret = PTR_ERR(e); - ... -( - return ret; -| - goto l; -) - } - -@depends on patch@ -expression e; -@@ - - e = devm_ioremap_resource(...); - ... - if (IS_ERR(e) || ...) { - ... -- \(dev_dbg\|dev_err\|pr_debug\|pr_err\|DRM_ERROR\)(...); - ... - } - -@depends on patch@ -expression e; -identifier l; -@@ - - e = devm_ioremap_resource(...); - ... - if (IS_ERR(e) || ...) --{ -( - return ...; -| - goto l; -) --} - -@r depends on report@ -expression e; -identifier l; -position p1; -@@ - -*e = devm_request_and_ioremap@p1(...); - ... - if (e == NULL || ...) { - ... -( - return ...; -| - goto l; -) - } - -@script:python depends on r@ -p1 << r.p1; -@@ - -msg = "ERROR: deprecated devm_request_and_ioremap() API used on line %s" % (p1[0].line) -coccilib.report.print_report(p1[0], msg) -- cgit v1.2.3 From 3a494e710367c0a233d86bcde9853781859fc008 Mon Sep 17 00:00:00 2001 From: Haiyang Zhang Date: Thu, 19 Jun 2014 18:34:36 -0700 Subject: hyperv: Add handler for RNDIS_STATUS_NETWORK_CHANGE event The RNDIS_STATUS_NETWORK_CHANGE event is received after the Hyper-V host sleep or hibernation. We refresh network at this time. MS-TFS: 135162 Signed-off-by: Haiyang Zhang Reviewed-by: K. Y. Srinivasan Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 3 ++- drivers/net/hyperv/netvsc_drv.c | 30 ++++++++++++++++++++++++++---- drivers/net/hyperv/rndis_filter.c | 21 +-------------------- include/linux/rndis.h | 1 + 4 files changed, 30 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 6cc37c15e0bf..24441ae832d1 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -170,6 +170,7 @@ struct rndis_device { enum rndis_device_state state; bool link_state; + bool link_change; atomic_t new_req_id; spinlock_t request_lock; @@ -185,7 +186,7 @@ int netvsc_device_remove(struct hv_device *device); int netvsc_send(struct hv_device *device, struct hv_netvsc_packet *packet); void netvsc_linkstatus_callback(struct hv_device *device_obj, - unsigned int status); + struct rndis_message *resp); int netvsc_recv_callback(struct hv_device *device_obj, struct hv_netvsc_packet *packet, struct ndis_tcp_ip_checksum_info *csum_info); diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 4fd71b75e666..9b27ca8c1d39 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -579,8 +579,9 @@ drop: * netvsc_linkstatus_callback - Link up/down notification */ void netvsc_linkstatus_callback(struct hv_device *device_obj, - unsigned int status) + struct rndis_message *resp) { + struct rndis_indicate_status *indicate = &resp->msg.indicate_status; struct net_device *net; struct net_device_context *ndev_ctx; struct netvsc_device *net_device; @@ -589,7 +590,19 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj, net_device = hv_get_drvdata(device_obj); rdev = net_device->extension; - rdev->link_state = status != 1; + switch (indicate->status) { + case RNDIS_STATUS_MEDIA_CONNECT: + rdev->link_state = false; + break; + case RNDIS_STATUS_MEDIA_DISCONNECT: + rdev->link_state = true; + break; + case RNDIS_STATUS_NETWORK_CHANGE: + rdev->link_change = true; + break; + default: + return; + } net = net_device->ndev; @@ -597,7 +610,7 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj, return; ndev_ctx = netdev_priv(net); - if (status == 1) { + if (!rdev->link_state) { schedule_delayed_work(&ndev_ctx->dwork, 0); schedule_delayed_work(&ndev_ctx->dwork, msecs_to_jiffies(20)); } else { @@ -767,7 +780,9 @@ static void netvsc_link_change(struct work_struct *w) struct net_device *net; struct netvsc_device *net_device; struct rndis_device *rdev; - bool notify; + bool notify, refresh = false; + char *argv[] = { "/etc/init.d/network", "restart", NULL }; + char *envp[] = { "HOME=/", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; rtnl_lock(); @@ -782,10 +797,17 @@ static void netvsc_link_change(struct work_struct *w) } else { netif_carrier_on(net); notify = true; + if (rdev->link_change) { + rdev->link_change = false; + refresh = true; + } } rtnl_unlock(); + if (refresh) + call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC); + if (notify) netdev_notify_peers(net); } diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 99c527adae5b..2b86f0b6f6d1 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -320,25 +320,6 @@ static void rndis_filter_receive_response(struct rndis_device *dev, } } -static void rndis_filter_receive_indicate_status(struct rndis_device *dev, - struct rndis_message *resp) -{ - struct rndis_indicate_status *indicate = - &resp->msg.indicate_status; - - if (indicate->status == RNDIS_STATUS_MEDIA_CONNECT) { - netvsc_linkstatus_callback( - dev->net_dev->dev, 1); - } else if (indicate->status == RNDIS_STATUS_MEDIA_DISCONNECT) { - netvsc_linkstatus_callback( - dev->net_dev->dev, 0); - } else { - /* - * TODO: - */ - } -} - /* * Get the Per-Packet-Info with the specified type * return NULL if not found. @@ -464,7 +445,7 @@ int rndis_filter_receive(struct hv_device *dev, case RNDIS_MSG_INDICATE: /* notification msgs */ - rndis_filter_receive_indicate_status(rndis_dev, rndis_msg); + netvsc_linkstatus_callback(dev, rndis_msg); break; default: netdev_err(ndev, diff --git a/include/linux/rndis.h b/include/linux/rndis.h index 0c8dc7195cdb..93c0a64aefa6 100644 --- a/include/linux/rndis.h +++ b/include/linux/rndis.h @@ -65,6 +65,7 @@ #define RNDIS_STATUS_MEDIA_SPECIFIC_INDICATION 0x40010012 #define RNDIS_STATUS_WW_INDICATION RDIA_SPECIFIC_INDICATION #define RNDIS_STATUS_LINK_SPEED_CHANGE 0x40010013L +#define RNDIS_STATUS_NETWORK_CHANGE 0x40010018 #define RNDIS_STATUS_NOT_RESETTABLE 0x80010001 #define RNDIS_STATUS_SOFT_ERRORS 0x80010003 -- cgit v1.2.3 From bb55a4c100abbb8d2a41e732f6362c575a7094e6 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 26 May 2014 15:32:05 +0200 Subject: crypto: api - Move crypto_yield() to algapi.h It makes no sense for crypto_yield() to be defined in scatterwalk.h , move it into algapi.h as it's an internal function to crypto API. Signed-off-by: Marek Vasut Signed-off-by: Herbert Xu --- include/crypto/algapi.h | 6 ++++++ include/crypto/scatterwalk.h | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/crypto/algapi.h b/include/crypto/algapi.h index 016c2f110f63..623a59c1ff5a 100644 --- a/include/crypto/algapi.h +++ b/include/crypto/algapi.h @@ -410,4 +410,10 @@ static inline int crypto_memneq(const void *a, const void *b, size_t size) return __crypto_memneq(a, b, size) != 0UL ? 1 : 0; } +static inline void crypto_yield(u32 flags) +{ + if (flags & CRYPTO_TFM_REQ_MAY_SLEEP) + cond_resched(); +} + #endif /* _CRYPTO_ALGAPI_H */ diff --git a/include/crypto/scatterwalk.h b/include/crypto/scatterwalk.h index 6a626a507b8c..7ef512f8631c 100644 --- a/include/crypto/scatterwalk.h +++ b/include/crypto/scatterwalk.h @@ -25,12 +25,6 @@ #include #include -static inline void crypto_yield(u32 flags) -{ - if (flags & CRYPTO_TFM_REQ_MAY_SLEEP) - cond_resched(); -} - static inline void scatterwalk_sg_chain(struct scatterlist *sg1, int num, struct scatterlist *sg2) { -- cgit v1.2.3 From d656c180de4d3eacd610c414fbcfe2509cf64a99 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 26 May 2014 18:51:48 +0200 Subject: crypto: skcipher - Don't use __crypto_dequeue_request() Use skcipher_givcrypt_cast(crypto_dequeue_request(queue)) instead, which does the same thing in much cleaner way. The skcipher_givcrypt_cast() actually uses container_of() instead of messing around with offsetof() too. Signed-off-by: Marek Vasut Reported-by: Arnd Bergmann Cc: Pantelis Antoniou Signed-off-by: Herbert Xu --- include/crypto/internal/skcipher.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include') diff --git a/include/crypto/internal/skcipher.h b/include/crypto/internal/skcipher.h index 06e8b32d541c..b3a46c515d1b 100644 --- a/include/crypto/internal/skcipher.h +++ b/include/crypto/internal/skcipher.h @@ -81,8 +81,7 @@ static inline int skcipher_enqueue_givcrypt( static inline struct skcipher_givcrypt_request *skcipher_dequeue_givcrypt( struct crypto_queue *queue) { - return __crypto_dequeue_request( - queue, offsetof(struct skcipher_givcrypt_request, creq.base)); + return skcipher_givcrypt_cast(crypto_dequeue_request(queue)); } static inline void *skcipher_givcrypt_reqctx( -- cgit v1.2.3 From 3e16f959b93fd323e1d1a37760e10f39fa3007bb Mon Sep 17 00:00:00 2001 From: Stephan Mueller Date: Sat, 31 May 2014 17:21:48 +0200 Subject: crypto: drbg - header file for DRBG The header file includes the definition of: * DRBG data structures with - struct drbg_state as main structure - struct drbg_core referencing the backend ciphers - struct drbg_state_ops callbach handlers for specific code supporting the Hash, HMAC, CTR DRBG implementations - struct drbg_conc defining a linked list for input data - struct drbg_test_data holding the test "entropy" data for CAVS testing and testmgr.c - struct drbg_gen allowing test data, additional information string and personalization string data to be funneled through the kernel crypto API -- the DRBG requires additional parameters when invoking the reset and random number generation requests than intended by the kernel crypto API * wrapper function to the kernel crypto API functions using struct drbg_gen to pass through all data needed for DRBG * wrapper functions to kernel crypto API functions usable for testing code to inject test_data into the DRBG as needed by CAVS testing and testmgr.c. * DRBG flags required for the operation of the DRBG and for selecting the particular DRBG type and backend cipher * getter functions for data from struct drbg_core Signed-off-by: Stephan Mueller Signed-off-by: Herbert Xu --- include/crypto/drbg.h | 289 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 include/crypto/drbg.h (limited to 'include') diff --git a/include/crypto/drbg.h b/include/crypto/drbg.h new file mode 100644 index 000000000000..b507c5b6020a --- /dev/null +++ b/include/crypto/drbg.h @@ -0,0 +1,289 @@ +/* + * DRBG based on NIST SP800-90A + * + * Copyright Stephan Mueller , 2014 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU General Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +#ifndef _DRBG_H +#define _DRBG_H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Concatenation Helper and string operation helper + * + * SP800-90A requires the concatenation of different data. To avoid copying + * buffers around or allocate additional memory, the following data structure + * is used to point to the original memory with its size. In addition, it + * is used to build a linked list. The linked list defines the concatenation + * of individual buffers. The order of memory block referenced in that + * linked list determines the order of concatenation. + */ +struct drbg_string { + const unsigned char *buf; + size_t len; + struct drbg_string *next; +}; + +static inline void drbg_string_fill(struct drbg_string *string, + const unsigned char *buf, size_t len) +{ + string->buf = buf; + string->len = len; + string->next = NULL; +} + +struct drbg_state; +typedef uint32_t drbg_flag_t; + +struct drbg_core { + drbg_flag_t flags; /* flags for the cipher */ + __u8 statelen; /* maximum state length */ + /* + * maximum length of personalization string or additional input + * string -- exponent for base 2 + */ + __u8 max_addtllen; + /* maximum bits per RNG request -- exponent for base 2*/ + __u8 max_bits; + /* maximum number of requests -- exponent for base 2 */ + __u8 max_req; + __u8 blocklen_bytes; /* block size of output in bytes */ + char cra_name[CRYPTO_MAX_ALG_NAME]; /* mapping to kernel crypto API */ + /* kernel crypto API backend cipher name */ + char backend_cra_name[CRYPTO_MAX_ALG_NAME]; +}; + +struct drbg_state_ops { + int (*update)(struct drbg_state *drbg, struct drbg_string *seed, + int reseed); + int (*generate)(struct drbg_state *drbg, + unsigned char *buf, unsigned int buflen, + struct drbg_string *addtl); + int (*crypto_init)(struct drbg_state *drbg); + int (*crypto_fini)(struct drbg_state *drbg); + +}; + +struct drbg_test_data { + struct drbg_string *testentropy; /* TEST PARAMETER: test entropy */ +}; + +struct drbg_state { + spinlock_t drbg_lock; /* lock around DRBG */ + unsigned char *V; /* internal state 10.1.1.1 1a) */ + /* hash: static value 10.1.1.1 1b) hmac / ctr: key */ + unsigned char *C; + /* Number of RNG requests since last reseed -- 10.1.1.1 1c) */ + size_t reseed_ctr; + /* some memory the DRBG can use for its operation */ + unsigned char *scratchpad; + void *priv_data; /* Cipher handle */ + bool seeded; /* DRBG fully seeded? */ + bool pr; /* Prediction resistance enabled? */ +#ifdef CONFIG_CRYPTO_FIPS + bool fips_primed; /* Continuous test primed? */ + unsigned char *prev; /* FIPS 140-2 continuous test value */ +#endif + const struct drbg_state_ops *d_ops; + const struct drbg_core *core; + struct drbg_test_data *test_data; +}; + +static inline __u8 drbg_statelen(struct drbg_state *drbg) +{ + if (drbg && drbg->core) + return drbg->core->statelen; + return 0; +} + +static inline __u8 drbg_blocklen(struct drbg_state *drbg) +{ + if (drbg && drbg->core) + return drbg->core->blocklen_bytes; + return 0; +} + +static inline __u8 drbg_keylen(struct drbg_state *drbg) +{ + if (drbg && drbg->core) + return (drbg->core->statelen - drbg->core->blocklen_bytes); + return 0; +} + +static inline size_t drbg_max_request_bytes(struct drbg_state *drbg) +{ + /* max_bits is in bits, but buflen is in bytes */ + return (1 << (drbg->core->max_bits - 3)); +} + +static inline size_t drbg_max_addtl(struct drbg_state *drbg) +{ + return (1UL<<(drbg->core->max_addtllen)); +} + +static inline size_t drbg_max_requests(struct drbg_state *drbg) +{ + return (1UL<<(drbg->core->max_req)); +} + +/* + * kernel crypto API input data structure for DRBG generate in case dlen + * is set to 0 + */ +struct drbg_gen { + unsigned char *outbuf; /* output buffer for random numbers */ + unsigned int outlen; /* size of output buffer */ + struct drbg_string *addtl; /* additional information string */ + struct drbg_test_data *test_data; /* test data */ +}; + +/* + * This is a wrapper to the kernel crypto API function of + * crypto_rng_get_bytes() to allow the caller to provide additional data. + * + * @drng DRBG handle -- see crypto_rng_get_bytes + * @outbuf output buffer -- see crypto_rng_get_bytes + * @outlen length of output buffer -- see crypto_rng_get_bytes + * @addtl_input additional information string input buffer + * @addtllen length of additional information string buffer + * + * return + * see crypto_rng_get_bytes + */ +static inline int crypto_drbg_get_bytes_addtl(struct crypto_rng *drng, + unsigned char *outbuf, unsigned int outlen, + struct drbg_string *addtl) +{ + int ret; + struct drbg_gen genbuf; + genbuf.outbuf = outbuf; + genbuf.outlen = outlen; + genbuf.addtl = addtl; + genbuf.test_data = NULL; + ret = crypto_rng_get_bytes(drng, (u8 *)&genbuf, 0); + return ret; +} + +/* + * TEST code + * + * This is a wrapper to the kernel crypto API function of + * crypto_rng_get_bytes() to allow the caller to provide additional data and + * allow furnishing of test_data + * + * @drng DRBG handle -- see crypto_rng_get_bytes + * @outbuf output buffer -- see crypto_rng_get_bytes + * @outlen length of output buffer -- see crypto_rng_get_bytes + * @addtl_input additional information string input buffer + * @addtllen length of additional information string buffer + * @test_data filled test data + * + * return + * see crypto_rng_get_bytes + */ +static inline int crypto_drbg_get_bytes_addtl_test(struct crypto_rng *drng, + unsigned char *outbuf, unsigned int outlen, + struct drbg_string *addtl, + struct drbg_test_data *test_data) +{ + int ret; + struct drbg_gen genbuf; + genbuf.outbuf = outbuf; + genbuf.outlen = outlen; + genbuf.addtl = addtl; + genbuf.test_data = test_data; + ret = crypto_rng_get_bytes(drng, (u8 *)&genbuf, 0); + return ret; +} + +/* + * TEST code + * + * This is a wrapper to the kernel crypto API function of + * crypto_rng_reset() to allow the caller to provide test_data + * + * @drng DRBG handle -- see crypto_rng_reset + * @pers personalization string input buffer + * @perslen length of additional information string buffer + * @test_data filled test data + * + * return + * see crypto_rng_reset + */ +static inline int crypto_drbg_reset_test(struct crypto_rng *drng, + struct drbg_string *pers, + struct drbg_test_data *test_data) +{ + int ret; + struct drbg_gen genbuf; + genbuf.outbuf = NULL; + genbuf.outlen = 0; + genbuf.addtl = pers; + genbuf.test_data = test_data; + ret = crypto_rng_reset(drng, (u8 *)&genbuf, 0); + return ret; +} + +/* DRBG type flags */ +#define DRBG_CTR ((drbg_flag_t)1<<0) +#define DRBG_HMAC ((drbg_flag_t)1<<1) +#define DRBG_HASH ((drbg_flag_t)1<<2) +#define DRBG_TYPE_MASK (DRBG_CTR | DRBG_HMAC | DRBG_HASH) +/* DRBG strength flags */ +#define DRBG_STRENGTH128 ((drbg_flag_t)1<<3) +#define DRBG_STRENGTH192 ((drbg_flag_t)1<<4) +#define DRBG_STRENGTH256 ((drbg_flag_t)1<<5) +#define DRBG_STRENGTH_MASK (DRBG_STRENGTH128 | DRBG_STRENGTH192 | \ + DRBG_STRENGTH256) + +enum drbg_prefixes { + DRBG_PREFIX0 = 0x00, + DRBG_PREFIX1, + DRBG_PREFIX2, + DRBG_PREFIX3 +}; + +#endif /* _DRBG_H */ -- cgit v1.2.3 From 6574e6c64e971c9adb629e81e497afdb52b1c9df Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Mon, 9 Jun 2014 20:59:54 +0300 Subject: crypto: des_3des - add x86-64 assembly implementation Patch adds x86_64 assembly implementation of Triple DES EDE cipher algorithm. Two assembly implementations are provided. First is regular 'one-block at time' encrypt/decrypt function. Second is 'three-blocks at time' function that gains performance increase on out-of-order CPUs. tcrypt test results: Intel Core i5-4570: des3_ede-asm vs des3_ede-generic: size ecb-enc ecb-dec cbc-enc cbc-dec ctr-enc ctr-dec 16B 1.21x 1.22x 1.27x 1.36x 1.25x 1.25x 64B 1.98x 1.96x 1.23x 2.04x 2.01x 2.00x 256B 2.34x 2.37x 1.21x 2.40x 2.38x 2.39x 1024B 2.50x 2.47x 1.22x 2.51x 2.52x 2.51x 8192B 2.51x 2.53x 1.21x 2.56x 2.54x 2.55x Signed-off-by: Jussi Kivilinna Signed-off-by: Herbert Xu --- arch/x86/crypto/Makefile | 2 + arch/x86/crypto/des3_ede-asm_64.S | 805 ++++++++++++++++++++++++++++++++++++++ arch/x86/crypto/des3_ede_glue.c | 509 ++++++++++++++++++++++++ crypto/Kconfig | 13 + crypto/des_generic.c | 22 +- include/crypto/des.h | 3 + 6 files changed, 1349 insertions(+), 5 deletions(-) create mode 100644 arch/x86/crypto/des3_ede-asm_64.S create mode 100644 arch/x86/crypto/des3_ede_glue.c (limited to 'include') diff --git a/arch/x86/crypto/Makefile b/arch/x86/crypto/Makefile index 61d6e281898b..a470de25570f 100644 --- a/arch/x86/crypto/Makefile +++ b/arch/x86/crypto/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_CRYPTO_SALSA20_586) += salsa20-i586.o obj-$(CONFIG_CRYPTO_SERPENT_SSE2_586) += serpent-sse2-i586.o obj-$(CONFIG_CRYPTO_AES_X86_64) += aes-x86_64.o +obj-$(CONFIG_CRYPTO_DES3_EDE_X86_64) += des3_ede-x86_64.o obj-$(CONFIG_CRYPTO_CAMELLIA_X86_64) += camellia-x86_64.o obj-$(CONFIG_CRYPTO_BLOWFISH_X86_64) += blowfish-x86_64.o obj-$(CONFIG_CRYPTO_TWOFISH_X86_64) += twofish-x86_64.o @@ -52,6 +53,7 @@ salsa20-i586-y := salsa20-i586-asm_32.o salsa20_glue.o serpent-sse2-i586-y := serpent-sse2-i586-asm_32.o serpent_sse2_glue.o aes-x86_64-y := aes-x86_64-asm_64.o aes_glue.o +des3_ede-x86_64-y := des3_ede-asm_64.o des3_ede_glue.o camellia-x86_64-y := camellia-x86_64-asm_64.o camellia_glue.o blowfish-x86_64-y := blowfish-x86_64-asm_64.o blowfish_glue.o twofish-x86_64-y := twofish-x86_64-asm_64.o twofish_glue.o diff --git a/arch/x86/crypto/des3_ede-asm_64.S b/arch/x86/crypto/des3_ede-asm_64.S new file mode 100644 index 000000000000..038f6ae87c5e --- /dev/null +++ b/arch/x86/crypto/des3_ede-asm_64.S @@ -0,0 +1,805 @@ +/* + * des3_ede-asm_64.S - x86-64 assembly implementation of 3DES cipher + * + * Copyright © 2014 Jussi Kivilinna + * + * 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 + +.file "des3_ede-asm_64.S" +.text + +#define s1 .L_s1 +#define s2 ((s1) + (64*8)) +#define s3 ((s2) + (64*8)) +#define s4 ((s3) + (64*8)) +#define s5 ((s4) + (64*8)) +#define s6 ((s5) + (64*8)) +#define s7 ((s6) + (64*8)) +#define s8 ((s7) + (64*8)) + +/* register macros */ +#define CTX %rdi + +#define RL0 %r8 +#define RL1 %r9 +#define RL2 %r10 + +#define RL0d %r8d +#define RL1d %r9d +#define RL2d %r10d + +#define RR0 %r11 +#define RR1 %r12 +#define RR2 %r13 + +#define RR0d %r11d +#define RR1d %r12d +#define RR2d %r13d + +#define RW0 %rax +#define RW1 %rbx +#define RW2 %rcx + +#define RW0d %eax +#define RW1d %ebx +#define RW2d %ecx + +#define RW0bl %al +#define RW1bl %bl +#define RW2bl %cl + +#define RW0bh %ah +#define RW1bh %bh +#define RW2bh %ch + +#define RT0 %r15 +#define RT1 %rbp +#define RT2 %r14 +#define RT3 %rdx + +#define RT0d %r15d +#define RT1d %ebp +#define RT2d %r14d +#define RT3d %edx + +/*********************************************************************** + * 1-way 3DES + ***********************************************************************/ +#define do_permutation(a, b, offset, mask) \ + movl a, RT0d; \ + shrl $(offset), RT0d; \ + xorl b, RT0d; \ + andl $(mask), RT0d; \ + xorl RT0d, b; \ + shll $(offset), RT0d; \ + xorl RT0d, a; + +#define expand_to_64bits(val, mask) \ + movl val##d, RT0d; \ + rorl $4, RT0d; \ + shlq $32, RT0; \ + orq RT0, val; \ + andq mask, val; + +#define compress_to_64bits(val) \ + movq val, RT0; \ + shrq $32, RT0; \ + roll $4, RT0d; \ + orl RT0d, val##d; + +#define initial_permutation(left, right) \ + do_permutation(left##d, right##d, 4, 0x0f0f0f0f); \ + do_permutation(left##d, right##d, 16, 0x0000ffff); \ + do_permutation(right##d, left##d, 2, 0x33333333); \ + do_permutation(right##d, left##d, 8, 0x00ff00ff); \ + movabs $0x3f3f3f3f3f3f3f3f, RT3; \ + movl left##d, RW0d; \ + roll $1, right##d; \ + xorl right##d, RW0d; \ + andl $0xaaaaaaaa, RW0d; \ + xorl RW0d, left##d; \ + xorl RW0d, right##d; \ + roll $1, left##d; \ + expand_to_64bits(right, RT3); \ + expand_to_64bits(left, RT3); + +#define final_permutation(left, right) \ + compress_to_64bits(right); \ + compress_to_64bits(left); \ + movl right##d, RW0d; \ + rorl $1, left##d; \ + xorl left##d, RW0d; \ + andl $0xaaaaaaaa, RW0d; \ + xorl RW0d, right##d; \ + xorl RW0d, left##d; \ + rorl $1, right##d; \ + do_permutation(right##d, left##d, 8, 0x00ff00ff); \ + do_permutation(right##d, left##d, 2, 0x33333333); \ + do_permutation(left##d, right##d, 16, 0x0000ffff); \ + do_permutation(left##d, right##d, 4, 0x0f0f0f0f); + +#define round1(n, from, to, load_next_key) \ + xorq from, RW0; \ + \ + movzbl RW0bl, RT0d; \ + movzbl RW0bh, RT1d; \ + shrq $16, RW0; \ + movzbl RW0bl, RT2d; \ + movzbl RW0bh, RT3d; \ + shrq $16, RW0; \ + movq s8(, RT0, 8), RT0; \ + xorq s6(, RT1, 8), to; \ + movzbl RW0bl, RL1d; \ + movzbl RW0bh, RT1d; \ + shrl $16, RW0d; \ + xorq s4(, RT2, 8), RT0; \ + xorq s2(, RT3, 8), to; \ + movzbl RW0bl, RT2d; \ + movzbl RW0bh, RT3d; \ + xorq s7(, RL1, 8), RT0; \ + xorq s5(, RT1, 8), to; \ + xorq s3(, RT2, 8), RT0; \ + load_next_key(n, RW0); \ + xorq RT0, to; \ + xorq s1(, RT3, 8), to; \ + +#define load_next_key(n, RWx) \ + movq (((n) + 1) * 8)(CTX), RWx; + +#define dummy2(a, b) /*_*/ + +#define read_block(io, left, right) \ + movl (io), left##d; \ + movl 4(io), right##d; \ + bswapl left##d; \ + bswapl right##d; + +#define write_block(io, left, right) \ + bswapl left##d; \ + bswapl right##d; \ + movl left##d, (io); \ + movl right##d, 4(io); + +ENTRY(des3_ede_x86_64_crypt_blk) + /* input: + * %rdi: round keys, CTX + * %rsi: dst + * %rdx: src + */ + pushq %rbp; + pushq %rbx; + pushq %r12; + pushq %r13; + pushq %r14; + pushq %r15; + + read_block(%rdx, RL0, RR0); + initial_permutation(RL0, RR0); + + movq (CTX), RW0; + + round1(0, RR0, RL0, load_next_key); + round1(1, RL0, RR0, load_next_key); + round1(2, RR0, RL0, load_next_key); + round1(3, RL0, RR0, load_next_key); + round1(4, RR0, RL0, load_next_key); + round1(5, RL0, RR0, load_next_key); + round1(6, RR0, RL0, load_next_key); + round1(7, RL0, RR0, load_next_key); + round1(8, RR0, RL0, load_next_key); + round1(9, RL0, RR0, load_next_key); + round1(10, RR0, RL0, load_next_key); + round1(11, RL0, RR0, load_next_key); + round1(12, RR0, RL0, load_next_key); + round1(13, RL0, RR0, load_next_key); + round1(14, RR0, RL0, load_next_key); + round1(15, RL0, RR0, load_next_key); + + round1(16+0, RL0, RR0, load_next_key); + round1(16+1, RR0, RL0, load_next_key); + round1(16+2, RL0, RR0, load_next_key); + round1(16+3, RR0, RL0, load_next_key); + round1(16+4, RL0, RR0, load_next_key); + round1(16+5, RR0, RL0, load_next_key); + round1(16+6, RL0, RR0, load_next_key); + round1(16+7, RR0, RL0, load_next_key); + round1(16+8, RL0, RR0, load_next_key); + round1(16+9, RR0, RL0, load_next_key); + round1(16+10, RL0, RR0, load_next_key); + round1(16+11, RR0, RL0, load_next_key); + round1(16+12, RL0, RR0, load_next_key); + round1(16+13, RR0, RL0, load_next_key); + round1(16+14, RL0, RR0, load_next_key); + round1(16+15, RR0, RL0, load_next_key); + + round1(32+0, RR0, RL0, load_next_key); + round1(32+1, RL0, RR0, load_next_key); + round1(32+2, RR0, RL0, load_next_key); + round1(32+3, RL0, RR0, load_next_key); + round1(32+4, RR0, RL0, load_next_key); + round1(32+5, RL0, RR0, load_next_key); + round1(32+6, RR0, RL0, load_next_key); + round1(32+7, RL0, RR0, load_next_key); + round1(32+8, RR0, RL0, load_next_key); + round1(32+9, RL0, RR0, load_next_key); + round1(32+10, RR0, RL0, load_next_key); + round1(32+11, RL0, RR0, load_next_key); + round1(32+12, RR0, RL0, load_next_key); + round1(32+13, RL0, RR0, load_next_key); + round1(32+14, RR0, RL0, load_next_key); + round1(32+15, RL0, RR0, dummy2); + + final_permutation(RR0, RL0); + write_block(%rsi, RR0, RL0); + + popq %r15; + popq %r14; + popq %r13; + popq %r12; + popq %rbx; + popq %rbp; + + ret; +ENDPROC(des3_ede_x86_64_crypt_blk) + +/*********************************************************************** + * 3-way 3DES + ***********************************************************************/ +#define expand_to_64bits(val, mask) \ + movl val##d, RT0d; \ + rorl $4, RT0d; \ + shlq $32, RT0; \ + orq RT0, val; \ + andq mask, val; + +#define compress_to_64bits(val) \ + movq val, RT0; \ + shrq $32, RT0; \ + roll $4, RT0d; \ + orl RT0d, val##d; + +#define initial_permutation3(left, right) \ + do_permutation(left##0d, right##0d, 4, 0x0f0f0f0f); \ + do_permutation(left##0d, right##0d, 16, 0x0000ffff); \ + do_permutation(left##1d, right##1d, 4, 0x0f0f0f0f); \ + do_permutation(left##1d, right##1d, 16, 0x0000ffff); \ + do_permutation(left##2d, right##2d, 4, 0x0f0f0f0f); \ + do_permutation(left##2d, right##2d, 16, 0x0000ffff); \ + \ + do_permutation(right##0d, left##0d, 2, 0x33333333); \ + do_permutation(right##0d, left##0d, 8, 0x00ff00ff); \ + do_permutation(right##1d, left##1d, 2, 0x33333333); \ + do_permutation(right##1d, left##1d, 8, 0x00ff00ff); \ + do_permutation(right##2d, left##2d, 2, 0x33333333); \ + do_permutation(right##2d, left##2d, 8, 0x00ff00ff); \ + \ + movabs $0x3f3f3f3f3f3f3f3f, RT3; \ + \ + movl left##0d, RW0d; \ + roll $1, right##0d; \ + xorl right##0d, RW0d; \ + andl $0xaaaaaaaa, RW0d; \ + xorl RW0d, left##0d; \ + xorl RW0d, right##0d; \ + roll $1, left##0d; \ + expand_to_64bits(right##0, RT3); \ + expand_to_64bits(left##0, RT3); \ + movl left##1d, RW1d; \ + roll $1, right##1d; \ + xorl right##1d, RW1d; \ + andl $0xaaaaaaaa, RW1d; \ + xorl RW1d, left##1d; \ + xorl RW1d, right##1d; \ + roll $1, left##1d; \ + expand_to_64bits(right##1, RT3); \ + expand_to_64bits(left##1, RT3); \ + movl left##2d, RW2d; \ + roll $1, right##2d; \ + xorl right##2d, RW2d; \ + andl $0xaaaaaaaa, RW2d; \ + xorl RW2d, left##2d; \ + xorl RW2d, right##2d; \ + roll $1, left##2d; \ + expand_to_64bits(right##2, RT3); \ + expand_to_64bits(left##2, RT3); + +#define final_permutation3(left, right) \ + compress_to_64bits(right##0); \ + compress_to_64bits(left##0); \ + movl right##0d, RW0d; \ + rorl $1, left##0d; \ + xorl left##0d, RW0d; \ + andl $0xaaaaaaaa, RW0d; \ + xorl RW0d, right##0d; \ + xorl RW0d, left##0d; \ + rorl $1, right##0d; \ + compress_to_64bits(right##1); \ + compress_to_64bits(left##1); \ + movl right##1d, RW1d; \ + rorl $1, left##1d; \ + xorl left##1d, RW1d; \ + andl $0xaaaaaaaa, RW1d; \ + xorl RW1d, right##1d; \ + xorl RW1d, left##1d; \ + rorl $1, right##1d; \ + compress_to_64bits(right##2); \ + compress_to_64bits(left##2); \ + movl right##2d, RW2d; \ + rorl $1, left##2d; \ + xorl left##2d, RW2d; \ + andl $0xaaaaaaaa, RW2d; \ + xorl RW2d, right##2d; \ + xorl RW2d, left##2d; \ + rorl $1, right##2d; \ + \ + do_permutation(right##0d, left##0d, 8, 0x00ff00ff); \ + do_permutation(right##0d, left##0d, 2, 0x33333333); \ + do_permutation(right##1d, left##1d, 8, 0x00ff00ff); \ + do_permutation(right##1d, left##1d, 2, 0x33333333); \ + do_permutation(right##2d, left##2d, 8, 0x00ff00ff); \ + do_permutation(right##2d, left##2d, 2, 0x33333333); \ + \ + do_permutation(left##0d, right##0d, 16, 0x0000ffff); \ + do_permutation(left##0d, right##0d, 4, 0x0f0f0f0f); \ + do_permutation(left##1d, right##1d, 16, 0x0000ffff); \ + do_permutation(left##1d, right##1d, 4, 0x0f0f0f0f); \ + do_permutation(left##2d, right##2d, 16, 0x0000ffff); \ + do_permutation(left##2d, right##2d, 4, 0x0f0f0f0f); + +#define round3(n, from, to, load_next_key, do_movq) \ + xorq from##0, RW0; \ + movzbl RW0bl, RT3d; \ + movzbl RW0bh, RT1d; \ + shrq $16, RW0; \ + xorq s8(, RT3, 8), to##0; \ + xorq s6(, RT1, 8), to##0; \ + movzbl RW0bl, RT3d; \ + movzbl RW0bh, RT1d; \ + shrq $16, RW0; \ + xorq s4(, RT3, 8), to##0; \ + xorq s2(, RT1, 8), to##0; \ + movzbl RW0bl, RT3d; \ + movzbl RW0bh, RT1d; \ + shrl $16, RW0d; \ + xorq s7(, RT3, 8), to##0; \ + xorq s5(, RT1, 8), to##0; \ + movzbl RW0bl, RT3d; \ + movzbl RW0bh, RT1d; \ + load_next_key(n, RW0); \ + xorq s3(, RT3, 8), to##0; \ + xorq s1(, RT1, 8), to##0; \ + xorq from##1, RW1; \ + movzbl RW1bl, RT3d; \ + movzbl RW1bh, RT1d; \ + shrq $16, RW1; \ + xorq s8(, RT3, 8), to##1; \ + xorq s6(, RT1, 8), to##1; \ + movzbl RW1bl, RT3d; \ + movzbl RW1bh, RT1d; \ + shrq $16, RW1; \ + xorq s4(, RT3, 8), to##1; \ + xorq s2(, RT1, 8), to##1; \ + movzbl RW1bl, RT3d; \ + movzbl RW1bh, RT1d; \ + shrl $16, RW1d; \ + xorq s7(, RT3, 8), to##1; \ + xorq s5(, RT1, 8), to##1; \ + movzbl RW1bl, RT3d; \ + movzbl RW1bh, RT1d; \ + do_movq(RW0, RW1); \ + xorq s3(, RT3, 8), to##1; \ + xorq s1(, RT1, 8), to##1; \ + xorq from##2, RW2; \ + movzbl RW2bl, RT3d; \ + movzbl RW2bh, RT1d; \ + shrq $16, RW2; \ + xorq s8(, RT3, 8), to##2; \ + xorq s6(, RT1, 8), to##2; \ + movzbl RW2bl, RT3d; \ + movzbl RW2bh, RT1d; \ + shrq $16, RW2; \ + xorq s4(, RT3, 8), to##2; \ + xorq s2(, RT1, 8), to##2; \ + movzbl RW2bl, RT3d; \ + movzbl RW2bh, RT1d; \ + shrl $16, RW2d; \ + xorq s7(, RT3, 8), to##2; \ + xorq s5(, RT1, 8), to##2; \ + movzbl RW2bl, RT3d; \ + movzbl RW2bh, RT1d; \ + do_movq(RW0, RW2); \ + xorq s3(, RT3, 8), to##2; \ + xorq s1(, RT1, 8), to##2; + +#define __movq(src, dst) \ + movq src, dst; + +ENTRY(des3_ede_x86_64_crypt_blk_3way) + /* input: + * %rdi: ctx, round keys + * %rsi: dst (3 blocks) + * %rdx: src (3 blocks) + */ + + pushq %rbp; + pushq %rbx; + pushq %r12; + pushq %r13; + pushq %r14; + pushq %r15; + + /* load input */ + movl 0 * 4(%rdx), RL0d; + movl 1 * 4(%rdx), RR0d; + movl 2 * 4(%rdx), RL1d; + movl 3 * 4(%rdx), RR1d; + movl 4 * 4(%rdx), RL2d; + movl 5 * 4(%rdx), RR2d; + + bswapl RL0d; + bswapl RR0d; + bswapl RL1d; + bswapl RR1d; + bswapl RL2d; + bswapl RR2d; + + initial_permutation3(RL, RR); + + movq 0(CTX), RW0; + movq RW0, RW1; + movq RW0, RW2; + + round3(0, RR, RL, load_next_key, __movq); + round3(1, RL, RR, load_next_key, __movq); + round3(2, RR, RL, load_next_key, __movq); + round3(3, RL, RR, load_next_key, __movq); + round3(4, RR, RL, load_next_key, __movq); + round3(5, RL, RR, load_next_key, __movq); + round3(6, RR, RL, load_next_key, __movq); + round3(7, RL, RR, load_next_key, __movq); + round3(8, RR, RL, load_next_key, __movq); + round3(9, RL, RR, load_next_key, __movq); + round3(10, RR, RL, load_next_key, __movq); + round3(11, RL, RR, load_next_key, __movq); + round3(12, RR, RL, load_next_key, __movq); + round3(13, RL, RR, load_next_key, __movq); + round3(14, RR, RL, load_next_key, __movq); + round3(15, RL, RR, load_next_key, __movq); + + round3(16+0, RL, RR, load_next_key, __movq); + round3(16+1, RR, RL, load_next_key, __movq); + round3(16+2, RL, RR, load_next_key, __movq); + round3(16+3, RR, RL, load_next_key, __movq); + round3(16+4, RL, RR, load_next_key, __movq); + round3(16+5, RR, RL, load_next_key, __movq); + round3(16+6, RL, RR, load_next_key, __movq); + round3(16+7, RR, RL, load_next_key, __movq); + round3(16+8, RL, RR, load_next_key, __movq); + round3(16+9, RR, RL, load_next_key, __movq); + round3(16+10, RL, RR, load_next_key, __movq); + round3(16+11, RR, RL, load_next_key, __movq); + round3(16+12, RL, RR, load_next_key, __movq); + round3(16+13, RR, RL, load_next_key, __movq); + round3(16+14, RL, RR, load_next_key, __movq); + round3(16+15, RR, RL, load_next_key, __movq); + + round3(32+0, RR, RL, load_next_key, __movq); + round3(32+1, RL, RR, load_next_key, __movq); + round3(32+2, RR, RL, load_next_key, __movq); + round3(32+3, RL, RR, load_next_key, __movq); + round3(32+4, RR, RL, load_next_key, __movq); + round3(32+5, RL, RR, load_next_key, __movq); + round3(32+6, RR, RL, load_next_key, __movq); + round3(32+7, RL, RR, load_next_key, __movq); + round3(32+8, RR, RL, load_next_key, __movq); + round3(32+9, RL, RR, load_next_key, __movq); + round3(32+10, RR, RL, load_next_key, __movq); + round3(32+11, RL, RR, load_next_key, __movq); + round3(32+12, RR, RL, load_next_key, __movq); + round3(32+13, RL, RR, load_next_key, __movq); + round3(32+14, RR, RL, load_next_key, __movq); + round3(32+15, RL, RR, dummy2, dummy2); + + final_permutation3(RR, RL); + + bswapl RR0d; + bswapl RL0d; + bswapl RR1d; + bswapl RL1d; + bswapl RR2d; + bswapl RL2d; + + movl RR0d, 0 * 4(%rsi); + movl RL0d, 1 * 4(%rsi); + movl RR1d, 2 * 4(%rsi); + movl RL1d, 3 * 4(%rsi); + movl RR2d, 4 * 4(%rsi); + movl RL2d, 5 * 4(%rsi); + + popq %r15; + popq %r14; + popq %r13; + popq %r12; + popq %rbx; + popq %rbp; + + ret; +ENDPROC(des3_ede_x86_64_crypt_blk_3way) + +.data +.align 16 +.L_s1: + .quad 0x0010100001010400, 0x0000000000000000 + .quad 0x0000100000010000, 0x0010100001010404 + .quad 0x0010100001010004, 0x0000100000010404 + .quad 0x0000000000000004, 0x0000100000010000 + .quad 0x0000000000000400, 0x0010100001010400 + .quad 0x0010100001010404, 0x0000000000000400 + .quad 0x0010000001000404, 0x0010100001010004 + .quad 0x0010000001000000, 0x0000000000000004 + .quad 0x0000000000000404, 0x0010000001000400 + .quad 0x0010000001000400, 0x0000100000010400 + .quad 0x0000100000010400, 0x0010100001010000 + .quad 0x0010100001010000, 0x0010000001000404 + .quad 0x0000100000010004, 0x0010000001000004 + .quad 0x0010000001000004, 0x0000100000010004 + .quad 0x0000000000000000, 0x0000000000000404 + .quad 0x0000100000010404, 0x0010000001000000 + .quad 0x0000100000010000, 0x0010100001010404 + .quad 0x0000000000000004, 0x0010100001010000 + .quad 0x0010100001010400, 0x0010000001000000 + .quad 0x0010000001000000, 0x0000000000000400 + .quad 0x0010100001010004, 0x0000100000010000 + .quad 0x0000100000010400, 0x0010000001000004 + .quad 0x0000000000000400, 0x0000000000000004 + .quad 0x0010000001000404, 0x0000100000010404 + .quad 0x0010100001010404, 0x0000100000010004 + .quad 0x0010100001010000, 0x0010000001000404 + .quad 0x0010000001000004, 0x0000000000000404 + .quad 0x0000100000010404, 0x0010100001010400 + .quad 0x0000000000000404, 0x0010000001000400 + .quad 0x0010000001000400, 0x0000000000000000 + .quad 0x0000100000010004, 0x0000100000010400 + .quad 0x0000000000000000, 0x0010100001010004 +.L_s2: + .quad 0x0801080200100020, 0x0800080000000000 + .quad 0x0000080000000000, 0x0001080200100020 + .quad 0x0001000000100000, 0x0000000200000020 + .quad 0x0801000200100020, 0x0800080200000020 + .quad 0x0800000200000020, 0x0801080200100020 + .quad 0x0801080000100000, 0x0800000000000000 + .quad 0x0800080000000000, 0x0001000000100000 + .quad 0x0000000200000020, 0x0801000200100020 + .quad 0x0001080000100000, 0x0001000200100020 + .quad 0x0800080200000020, 0x0000000000000000 + .quad 0x0800000000000000, 0x0000080000000000 + .quad 0x0001080200100020, 0x0801000000100000 + .quad 0x0001000200100020, 0x0800000200000020 + .quad 0x0000000000000000, 0x0001080000100000 + .quad 0x0000080200000020, 0x0801080000100000 + .quad 0x0801000000100000, 0x0000080200000020 + .quad 0x0000000000000000, 0x0001080200100020 + .quad 0x0801000200100020, 0x0001000000100000 + .quad 0x0800080200000020, 0x0801000000100000 + .quad 0x0801080000100000, 0x0000080000000000 + .quad 0x0801000000100000, 0x0800080000000000 + .quad 0x0000000200000020, 0x0801080200100020 + .quad 0x0001080200100020, 0x0000000200000020 + .quad 0x0000080000000000, 0x0800000000000000 + .quad 0x0000080200000020, 0x0801080000100000 + .quad 0x0001000000100000, 0x0800000200000020 + .quad 0x0001000200100020, 0x0800080200000020 + .quad 0x0800000200000020, 0x0001000200100020 + .quad 0x0001080000100000, 0x0000000000000000 + .quad 0x0800080000000000, 0x0000080200000020 + .quad 0x0800000000000000, 0x0801000200100020 + .quad 0x0801080200100020, 0x0001080000100000 +.L_s3: + .quad 0x0000002000000208, 0x0000202008020200 + .quad 0x0000000000000000, 0x0000200008020008 + .quad 0x0000002008000200, 0x0000000000000000 + .quad 0x0000202000020208, 0x0000002008000200 + .quad 0x0000200000020008, 0x0000000008000008 + .quad 0x0000000008000008, 0x0000200000020000 + .quad 0x0000202008020208, 0x0000200000020008 + .quad 0x0000200008020000, 0x0000002000000208 + .quad 0x0000000008000000, 0x0000000000000008 + .quad 0x0000202008020200, 0x0000002000000200 + .quad 0x0000202000020200, 0x0000200008020000 + .quad 0x0000200008020008, 0x0000202000020208 + .quad 0x0000002008000208, 0x0000202000020200 + .quad 0x0000200000020000, 0x0000002008000208 + .quad 0x0000000000000008, 0x0000202008020208 + .quad 0x0000002000000200, 0x0000000008000000 + .quad 0x0000202008020200, 0x0000000008000000 + .quad 0x0000200000020008, 0x0000002000000208 + .quad 0x0000200000020000, 0x0000202008020200 + .quad 0x0000002008000200, 0x0000000000000000 + .quad 0x0000002000000200, 0x0000200000020008 + .quad 0x0000202008020208, 0x0000002008000200 + .quad 0x0000000008000008, 0x0000002000000200 + .quad 0x0000000000000000, 0x0000200008020008 + .quad 0x0000002008000208, 0x0000200000020000 + .quad 0x0000000008000000, 0x0000202008020208 + .quad 0x0000000000000008, 0x0000202000020208 + .quad 0x0000202000020200, 0x0000000008000008 + .quad 0x0000200008020000, 0x0000002008000208 + .quad 0x0000002000000208, 0x0000200008020000 + .quad 0x0000202000020208, 0x0000000000000008 + .quad 0x0000200008020008, 0x0000202000020200 +.L_s4: + .quad 0x1008020000002001, 0x1000020800002001 + .quad 0x1000020800002001, 0x0000000800000000 + .quad 0x0008020800002000, 0x1008000800000001 + .quad 0x1008000000000001, 0x1000020000002001 + .quad 0x0000000000000000, 0x0008020000002000 + .quad 0x0008020000002000, 0x1008020800002001 + .quad 0x1000000800000001, 0x0000000000000000 + .quad 0x0008000800000000, 0x1008000000000001 + .quad 0x1000000000000001, 0x0000020000002000 + .quad 0x0008000000000000, 0x1008020000002001 + .quad 0x0000000800000000, 0x0008000000000000 + .quad 0x1000020000002001, 0x0000020800002000 + .quad 0x1008000800000001, 0x1000000000000001 + .quad 0x0000020800002000, 0x0008000800000000 + .quad 0x0000020000002000, 0x0008020800002000 + .quad 0x1008020800002001, 0x1000000800000001 + .quad 0x0008000800000000, 0x1008000000000001 + .quad 0x0008020000002000, 0x1008020800002001 + .quad 0x1000000800000001, 0x0000000000000000 + .quad 0x0000000000000000, 0x0008020000002000 + .quad 0x0000020800002000, 0x0008000800000000 + .quad 0x1008000800000001, 0x1000000000000001 + .quad 0x1008020000002001, 0x1000020800002001 + .quad 0x1000020800002001, 0x0000000800000000 + .quad 0x1008020800002001, 0x1000000800000001 + .quad 0x1000000000000001, 0x0000020000002000 + .quad 0x1008000000000001, 0x1000020000002001 + .quad 0x0008020800002000, 0x1008000800000001 + .quad 0x1000020000002001, 0x0000020800002000 + .quad 0x0008000000000000, 0x1008020000002001 + .quad 0x0000000800000000, 0x0008000000000000 + .quad 0x0000020000002000, 0x0008020800002000 +.L_s5: + .quad 0x0000001000000100, 0x0020001002080100 + .quad 0x0020000002080000, 0x0420001002000100 + .quad 0x0000000000080000, 0x0000001000000100 + .quad 0x0400000000000000, 0x0020000002080000 + .quad 0x0400001000080100, 0x0000000000080000 + .quad 0x0020001002000100, 0x0400001000080100 + .quad 0x0420001002000100, 0x0420000002080000 + .quad 0x0000001000080100, 0x0400000000000000 + .quad 0x0020000002000000, 0x0400000000080000 + .quad 0x0400000000080000, 0x0000000000000000 + .quad 0x0400001000000100, 0x0420001002080100 + .quad 0x0420001002080100, 0x0020001002000100 + .quad 0x0420000002080000, 0x0400001000000100 + .quad 0x0000000000000000, 0x0420000002000000 + .quad 0x0020001002080100, 0x0020000002000000 + .quad 0x0420000002000000, 0x0000001000080100 + .quad 0x0000000000080000, 0x0420001002000100 + .quad 0x0000001000000100, 0x0020000002000000 + .quad 0x0400000000000000, 0x0020000002080000 + .quad 0x0420001002000100, 0x0400001000080100 + .quad 0x0020001002000100, 0x0400000000000000 + .quad 0x0420000002080000, 0x0020001002080100 + .quad 0x0400001000080100, 0x0000001000000100 + .quad 0x0020000002000000, 0x0420000002080000 + .quad 0x0420001002080100, 0x0000001000080100 + .quad 0x0420000002000000, 0x0420001002080100 + .quad 0x0020000002080000, 0x0000000000000000 + .quad 0x0400000000080000, 0x0420000002000000 + .quad 0x0000001000080100, 0x0020001002000100 + .quad 0x0400001000000100, 0x0000000000080000 + .quad 0x0000000000000000, 0x0400000000080000 + .quad 0x0020001002080100, 0x0400001000000100 +.L_s6: + .quad 0x0200000120000010, 0x0204000020000000 + .quad 0x0000040000000000, 0x0204040120000010 + .quad 0x0204000020000000, 0x0000000100000010 + .quad 0x0204040120000010, 0x0004000000000000 + .quad 0x0200040020000000, 0x0004040100000010 + .quad 0x0004000000000000, 0x0200000120000010 + .quad 0x0004000100000010, 0x0200040020000000 + .quad 0x0200000020000000, 0x0000040100000010 + .quad 0x0000000000000000, 0x0004000100000010 + .quad 0x0200040120000010, 0x0000040000000000 + .quad 0x0004040000000000, 0x0200040120000010 + .quad 0x0000000100000010, 0x0204000120000010 + .quad 0x0204000120000010, 0x0000000000000000 + .quad 0x0004040100000010, 0x0204040020000000 + .quad 0x0000040100000010, 0x0004040000000000 + .quad 0x0204040020000000, 0x0200000020000000 + .quad 0x0200040020000000, 0x0000000100000010 + .quad 0x0204000120000010, 0x0004040000000000 + .quad 0x0204040120000010, 0x0004000000000000 + .quad 0x0000040100000010, 0x0200000120000010 + .quad 0x0004000000000000, 0x0200040020000000 + .quad 0x0200000020000000, 0x0000040100000010 + .quad 0x0200000120000010, 0x0204040120000010 + .quad 0x0004040000000000, 0x0204000020000000 + .quad 0x0004040100000010, 0x0204040020000000 + .quad 0x0000000000000000, 0x0204000120000010 + .quad 0x0000000100000010, 0x0000040000000000 + .quad 0x0204000020000000, 0x0004040100000010 + .quad 0x0000040000000000, 0x0004000100000010 + .quad 0x0200040120000010, 0x0000000000000000 + .quad 0x0204040020000000, 0x0200000020000000 + .quad 0x0004000100000010, 0x0200040120000010 +.L_s7: + .quad 0x0002000000200000, 0x2002000004200002 + .quad 0x2000000004000802, 0x0000000000000000 + .quad 0x0000000000000800, 0x2000000004000802 + .quad 0x2002000000200802, 0x0002000004200800 + .quad 0x2002000004200802, 0x0002000000200000 + .quad 0x0000000000000000, 0x2000000004000002 + .quad 0x2000000000000002, 0x0000000004000000 + .quad 0x2002000004200002, 0x2000000000000802 + .quad 0x0000000004000800, 0x2002000000200802 + .quad 0x2002000000200002, 0x0000000004000800 + .quad 0x2000000004000002, 0x0002000004200000 + .quad 0x0002000004200800, 0x2002000000200002 + .quad 0x0002000004200000, 0x0000000000000800 + .quad 0x2000000000000802, 0x2002000004200802 + .quad 0x0002000000200800, 0x2000000000000002 + .quad 0x0000000004000000, 0x0002000000200800 + .quad 0x0000000004000000, 0x0002000000200800 + .quad 0x0002000000200000, 0x2000000004000802 + .quad 0x2000000004000802, 0x2002000004200002 + .quad 0x2002000004200002, 0x2000000000000002 + .quad 0x2002000000200002, 0x0000000004000000 + .quad 0x0000000004000800, 0x0002000000200000 + .quad 0x0002000004200800, 0x2000000000000802 + .quad 0x2002000000200802, 0x0002000004200800 + .quad 0x2000000000000802, 0x2000000004000002 + .quad 0x2002000004200802, 0x0002000004200000 + .quad 0x0002000000200800, 0x0000000000000000 + .quad 0x2000000000000002, 0x2002000004200802 + .quad 0x0000000000000000, 0x2002000000200802 + .quad 0x0002000004200000, 0x0000000000000800 + .quad 0x2000000004000002, 0x0000000004000800 + .quad 0x0000000000000800, 0x2002000000200002 +.L_s8: + .quad 0x0100010410001000, 0x0000010000001000 + .quad 0x0000000000040000, 0x0100010410041000 + .quad 0x0100000010000000, 0x0100010410001000 + .quad 0x0000000400000000, 0x0100000010000000 + .quad 0x0000000400040000, 0x0100000010040000 + .quad 0x0100010410041000, 0x0000010000041000 + .quad 0x0100010010041000, 0x0000010400041000 + .quad 0x0000010000001000, 0x0000000400000000 + .quad 0x0100000010040000, 0x0100000410000000 + .quad 0x0100010010001000, 0x0000010400001000 + .quad 0x0000010000041000, 0x0000000400040000 + .quad 0x0100000410040000, 0x0100010010041000 + .quad 0x0000010400001000, 0x0000000000000000 + .quad 0x0000000000000000, 0x0100000410040000 + .quad 0x0100000410000000, 0x0100010010001000 + .quad 0x0000010400041000, 0x0000000000040000 + .quad 0x0000010400041000, 0x0000000000040000 + .quad 0x0100010010041000, 0x0000010000001000 + .quad 0x0000000400000000, 0x0100000410040000 + .quad 0x0000010000001000, 0x0000010400041000 + .quad 0x0100010010001000, 0x0000000400000000 + .quad 0x0100000410000000, 0x0100000010040000 + .quad 0x0100000410040000, 0x0100000010000000 + .quad 0x0000000000040000, 0x0100010410001000 + .quad 0x0000000000000000, 0x0100010410041000 + .quad 0x0000000400040000, 0x0100000410000000 + .quad 0x0100000010040000, 0x0100010010001000 + .quad 0x0100010410001000, 0x0000000000000000 + .quad 0x0100010410041000, 0x0000010000041000 + .quad 0x0000010000041000, 0x0000010400001000 + .quad 0x0000010400001000, 0x0000000400040000 + .quad 0x0100000010000000, 0x0100010010041000 diff --git a/arch/x86/crypto/des3_ede_glue.c b/arch/x86/crypto/des3_ede_glue.c new file mode 100644 index 000000000000..ebc421543e44 --- /dev/null +++ b/arch/x86/crypto/des3_ede_glue.c @@ -0,0 +1,509 @@ +/* + * Glue Code for assembler optimized version of 3DES + * + * Copyright © 2014 Jussi Kivilinna + * + * CBC & ECB parts based on code (crypto/cbc.c,ecb.c) by: + * Copyright (c) 2006 Herbert Xu + * CTR part based on code (crypto/ctr.c) by: + * (C) Copyright IBM Corp. 2007 - Joy Latten + * + * 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 + +struct des3_ede_x86_ctx { + u32 enc_expkey[DES3_EDE_EXPKEY_WORDS]; + u32 dec_expkey[DES3_EDE_EXPKEY_WORDS]; +}; + +/* regular block cipher functions */ +asmlinkage void des3_ede_x86_64_crypt_blk(const u32 *expkey, u8 *dst, + const u8 *src); + +/* 3-way parallel cipher functions */ +asmlinkage void des3_ede_x86_64_crypt_blk_3way(const u32 *expkey, u8 *dst, + const u8 *src); + +static inline void des3_ede_enc_blk(struct des3_ede_x86_ctx *ctx, u8 *dst, + const u8 *src) +{ + u32 *enc_ctx = ctx->enc_expkey; + + des3_ede_x86_64_crypt_blk(enc_ctx, dst, src); +} + +static inline void des3_ede_dec_blk(struct des3_ede_x86_ctx *ctx, u8 *dst, + const u8 *src) +{ + u32 *dec_ctx = ctx->dec_expkey; + + des3_ede_x86_64_crypt_blk(dec_ctx, dst, src); +} + +static inline void des3_ede_enc_blk_3way(struct des3_ede_x86_ctx *ctx, u8 *dst, + const u8 *src) +{ + u32 *enc_ctx = ctx->enc_expkey; + + des3_ede_x86_64_crypt_blk_3way(enc_ctx, dst, src); +} + +static inline void des3_ede_dec_blk_3way(struct des3_ede_x86_ctx *ctx, u8 *dst, + const u8 *src) +{ + u32 *dec_ctx = ctx->dec_expkey; + + des3_ede_x86_64_crypt_blk_3way(dec_ctx, dst, src); +} + +static void des3_ede_x86_encrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src) +{ + des3_ede_enc_blk(crypto_tfm_ctx(tfm), dst, src); +} + +static void des3_ede_x86_decrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src) +{ + des3_ede_dec_blk(crypto_tfm_ctx(tfm), dst, src); +} + +static int ecb_crypt(struct blkcipher_desc *desc, struct blkcipher_walk *walk, + const u32 *expkey) +{ + unsigned int bsize = DES3_EDE_BLOCK_SIZE; + unsigned int nbytes; + int err; + + err = blkcipher_walk_virt(desc, walk); + + while ((nbytes = walk->nbytes)) { + u8 *wsrc = walk->src.virt.addr; + u8 *wdst = walk->dst.virt.addr; + + /* Process four block batch */ + if (nbytes >= bsize * 3) { + do { + des3_ede_x86_64_crypt_blk_3way(expkey, wdst, + wsrc); + + wsrc += bsize * 3; + wdst += bsize * 3; + nbytes -= bsize * 3; + } while (nbytes >= bsize * 3); + + if (nbytes < bsize) + goto done; + } + + /* Handle leftovers */ + do { + des3_ede_x86_64_crypt_blk(expkey, wdst, wsrc); + + wsrc += bsize; + wdst += bsize; + nbytes -= bsize; + } while (nbytes >= bsize); + +done: + err = blkcipher_walk_done(desc, walk, nbytes); + } + + return err; +} + +static int ecb_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, + struct scatterlist *src, unsigned int nbytes) +{ + struct des3_ede_x86_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); + struct blkcipher_walk walk; + + blkcipher_walk_init(&walk, dst, src, nbytes); + return ecb_crypt(desc, &walk, ctx->enc_expkey); +} + +static int ecb_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, + struct scatterlist *src, unsigned int nbytes) +{ + struct des3_ede_x86_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); + struct blkcipher_walk walk; + + blkcipher_walk_init(&walk, dst, src, nbytes); + return ecb_crypt(desc, &walk, ctx->dec_expkey); +} + +static unsigned int __cbc_encrypt(struct blkcipher_desc *desc, + struct blkcipher_walk *walk) +{ + struct des3_ede_x86_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); + unsigned int bsize = DES3_EDE_BLOCK_SIZE; + unsigned int nbytes = walk->nbytes; + u64 *src = (u64 *)walk->src.virt.addr; + u64 *dst = (u64 *)walk->dst.virt.addr; + u64 *iv = (u64 *)walk->iv; + + do { + *dst = *src ^ *iv; + des3_ede_enc_blk(ctx, (u8 *)dst, (u8 *)dst); + iv = dst; + + src += 1; + dst += 1; + nbytes -= bsize; + } while (nbytes >= bsize); + + *(u64 *)walk->iv = *iv; + return nbytes; +} + +static int cbc_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, + struct scatterlist *src, unsigned int nbytes) +{ + struct blkcipher_walk walk; + int err; + + blkcipher_walk_init(&walk, dst, src, nbytes); + err = blkcipher_walk_virt(desc, &walk); + + while ((nbytes = walk.nbytes)) { + nbytes = __cbc_encrypt(desc, &walk); + err = blkcipher_walk_done(desc, &walk, nbytes); + } + + return err; +} + +static unsigned int __cbc_decrypt(struct blkcipher_desc *desc, + struct blkcipher_walk *walk) +{ + struct des3_ede_x86_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); + unsigned int bsize = DES3_EDE_BLOCK_SIZE; + unsigned int nbytes = walk->nbytes; + u64 *src = (u64 *)walk->src.virt.addr; + u64 *dst = (u64 *)walk->dst.virt.addr; + u64 ivs[3 - 1]; + u64 last_iv; + + /* Start of the last block. */ + src += nbytes / bsize - 1; + dst += nbytes / bsize - 1; + + last_iv = *src; + + /* Process four block batch */ + if (nbytes >= bsize * 3) { + do { + nbytes -= bsize * 3 - bsize; + src -= 3 - 1; + dst -= 3 - 1; + + ivs[0] = src[0]; + ivs[1] = src[1]; + + des3_ede_dec_blk_3way(ctx, (u8 *)dst, (u8 *)src); + + dst[1] ^= ivs[0]; + dst[2] ^= ivs[1]; + + nbytes -= bsize; + if (nbytes < bsize) + goto done; + + *dst ^= *(src - 1); + src -= 1; + dst -= 1; + } while (nbytes >= bsize * 3); + } + + /* Handle leftovers */ + for (;;) { + des3_ede_dec_blk(ctx, (u8 *)dst, (u8 *)src); + + nbytes -= bsize; + if (nbytes < bsize) + break; + + *dst ^= *(src - 1); + src -= 1; + dst -= 1; + } + +done: + *dst ^= *(u64 *)walk->iv; + *(u64 *)walk->iv = last_iv; + + return nbytes; +} + +static int cbc_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, + struct scatterlist *src, unsigned int nbytes) +{ + struct blkcipher_walk walk; + int err; + + blkcipher_walk_init(&walk, dst, src, nbytes); + err = blkcipher_walk_virt(desc, &walk); + + while ((nbytes = walk.nbytes)) { + nbytes = __cbc_decrypt(desc, &walk); + err = blkcipher_walk_done(desc, &walk, nbytes); + } + + return err; +} + +static void ctr_crypt_final(struct des3_ede_x86_ctx *ctx, + struct blkcipher_walk *walk) +{ + u8 *ctrblk = walk->iv; + u8 keystream[DES3_EDE_BLOCK_SIZE]; + u8 *src = walk->src.virt.addr; + u8 *dst = walk->dst.virt.addr; + unsigned int nbytes = walk->nbytes; + + des3_ede_enc_blk(ctx, keystream, ctrblk); + crypto_xor(keystream, src, nbytes); + memcpy(dst, keystream, nbytes); + + crypto_inc(ctrblk, DES3_EDE_BLOCK_SIZE); +} + +static unsigned int __ctr_crypt(struct blkcipher_desc *desc, + struct blkcipher_walk *walk) +{ + struct des3_ede_x86_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); + unsigned int bsize = DES3_EDE_BLOCK_SIZE; + unsigned int nbytes = walk->nbytes; + u64 *src = (u64 *)walk->src.virt.addr; + u64 *dst = (u64 *)walk->dst.virt.addr; + u64 ctrblk = be64_to_cpu(*(__be64 *)walk->iv); + __be64 ctrblocks[3]; + + /* Process four block batch */ + if (nbytes >= bsize * 3) { + do { + /* create ctrblks for parallel encrypt */ + ctrblocks[0] = cpu_to_be64(ctrblk++); + ctrblocks[1] = cpu_to_be64(ctrblk++); + ctrblocks[2] = cpu_to_be64(ctrblk++); + + des3_ede_enc_blk_3way(ctx, (u8 *)ctrblocks, + (u8 *)ctrblocks); + + dst[0] = src[0] ^ ctrblocks[0]; + dst[1] = src[1] ^ ctrblocks[1]; + dst[2] = src[2] ^ ctrblocks[2]; + + src += 3; + dst += 3; + } while ((nbytes -= bsize * 3) >= bsize * 3); + + if (nbytes < bsize) + goto done; + } + + /* Handle leftovers */ + do { + ctrblocks[0] = cpu_to_be64(ctrblk++); + + des3_ede_enc_blk(ctx, (u8 *)ctrblocks, (u8 *)ctrblocks); + + dst[0] = src[0] ^ ctrblocks[0]; + + src += 1; + dst += 1; + } while ((nbytes -= bsize) >= bsize); + +done: + *(__be64 *)walk->iv = cpu_to_be64(ctrblk); + return nbytes; +} + +static int ctr_crypt(struct blkcipher_desc *desc, struct scatterlist *dst, + struct scatterlist *src, unsigned int nbytes) +{ + struct blkcipher_walk walk; + int err; + + blkcipher_walk_init(&walk, dst, src, nbytes); + err = blkcipher_walk_virt_block(desc, &walk, DES3_EDE_BLOCK_SIZE); + + while ((nbytes = walk.nbytes) >= DES3_EDE_BLOCK_SIZE) { + nbytes = __ctr_crypt(desc, &walk); + err = blkcipher_walk_done(desc, &walk, nbytes); + } + + if (walk.nbytes) { + ctr_crypt_final(crypto_blkcipher_ctx(desc->tfm), &walk); + err = blkcipher_walk_done(desc, &walk, 0); + } + + return err; +} + +static int des3_ede_x86_setkey(struct crypto_tfm *tfm, const u8 *key, + unsigned int keylen) +{ + struct des3_ede_x86_ctx *ctx = crypto_tfm_ctx(tfm); + u32 i, j, tmp; + int err; + + /* Generate encryption context using generic implementation. */ + err = __des3_ede_setkey(ctx->enc_expkey, &tfm->crt_flags, key, keylen); + if (err < 0) + return err; + + /* Fix encryption context for this implementation and form decryption + * context. */ + j = DES3_EDE_EXPKEY_WORDS - 2; + for (i = 0; i < DES3_EDE_EXPKEY_WORDS; i += 2, j -= 2) { + tmp = ror32(ctx->enc_expkey[i + 1], 4); + ctx->enc_expkey[i + 1] = tmp; + + ctx->dec_expkey[j + 0] = ctx->enc_expkey[i + 0]; + ctx->dec_expkey[j + 1] = tmp; + } + + return 0; +} + +static struct crypto_alg des3_ede_algs[4] = { { + .cra_name = "des3_ede", + .cra_driver_name = "des3_ede-asm", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_TYPE_CIPHER, + .cra_blocksize = DES3_EDE_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct des3_ede_x86_ctx), + .cra_alignmask = 0, + .cra_module = THIS_MODULE, + .cra_u = { + .cipher = { + .cia_min_keysize = DES3_EDE_KEY_SIZE, + .cia_max_keysize = DES3_EDE_KEY_SIZE, + .cia_setkey = des3_ede_x86_setkey, + .cia_encrypt = des3_ede_x86_encrypt, + .cia_decrypt = des3_ede_x86_decrypt, + } + } +}, { + .cra_name = "ecb(des3_ede)", + .cra_driver_name = "ecb-des3_ede-asm", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_blocksize = DES3_EDE_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct des3_ede_x86_ctx), + .cra_alignmask = 0, + .cra_type = &crypto_blkcipher_type, + .cra_module = THIS_MODULE, + .cra_u = { + .blkcipher = { + .min_keysize = DES3_EDE_KEY_SIZE, + .max_keysize = DES3_EDE_KEY_SIZE, + .setkey = des3_ede_x86_setkey, + .encrypt = ecb_encrypt, + .decrypt = ecb_decrypt, + }, + }, +}, { + .cra_name = "cbc(des3_ede)", + .cra_driver_name = "cbc-des3_ede-asm", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_blocksize = DES3_EDE_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct des3_ede_x86_ctx), + .cra_alignmask = 0, + .cra_type = &crypto_blkcipher_type, + .cra_module = THIS_MODULE, + .cra_u = { + .blkcipher = { + .min_keysize = DES3_EDE_KEY_SIZE, + .max_keysize = DES3_EDE_KEY_SIZE, + .ivsize = DES3_EDE_BLOCK_SIZE, + .setkey = des3_ede_x86_setkey, + .encrypt = cbc_encrypt, + .decrypt = cbc_decrypt, + }, + }, +}, { + .cra_name = "ctr(des3_ede)", + .cra_driver_name = "ctr-des3_ede-asm", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_blocksize = 1, + .cra_ctxsize = sizeof(struct des3_ede_x86_ctx), + .cra_alignmask = 0, + .cra_type = &crypto_blkcipher_type, + .cra_module = THIS_MODULE, + .cra_u = { + .blkcipher = { + .min_keysize = DES3_EDE_KEY_SIZE, + .max_keysize = DES3_EDE_KEY_SIZE, + .ivsize = DES3_EDE_BLOCK_SIZE, + .setkey = des3_ede_x86_setkey, + .encrypt = ctr_crypt, + .decrypt = ctr_crypt, + }, + }, +} }; + +static bool is_blacklisted_cpu(void) +{ + if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) + return false; + + if (boot_cpu_data.x86 == 0x0f) { + /* + * On Pentium 4, des3_ede-x86_64 is slower than generic C + * implementation because use of 64bit rotates (which are really + * slow on P4). Therefore blacklist P4s. + */ + return true; + } + + return false; +} + +static int force; +module_param(force, int, 0); +MODULE_PARM_DESC(force, "Force module load, ignore CPU blacklist"); + +static int __init des3_ede_x86_init(void) +{ + if (!force && is_blacklisted_cpu()) { + pr_info("des3_ede-x86_64: performance on this CPU would be suboptimal: disabling des3_ede-x86_64.\n"); + return -ENODEV; + } + + return crypto_register_algs(des3_ede_algs, ARRAY_SIZE(des3_ede_algs)); +} + +static void __exit des3_ede_x86_fini(void) +{ + crypto_unregister_algs(des3_ede_algs, ARRAY_SIZE(des3_ede_algs)); +} + +module_init(des3_ede_x86_init); +module_exit(des3_ede_x86_fini); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Triple DES EDE Cipher Algorithm, asm optimized"); +MODULE_ALIAS("des3_ede"); +MODULE_ALIAS("des3_ede-asm"); +MODULE_ALIAS("des"); +MODULE_ALIAS("des-asm"); +MODULE_AUTHOR("Jussi Kivilinna "); diff --git a/crypto/Kconfig b/crypto/Kconfig index c9c1cd91031c..025c5108442e 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -1019,6 +1019,19 @@ config CRYPTO_DES_SPARC64 DES cipher algorithm (FIPS 46-2), and Triple DES EDE (FIPS 46-3), optimized using SPARC64 crypto opcodes. +config CRYPTO_DES3_EDE_X86_64 + tristate "Triple DES EDE cipher algorithm (x86-64)" + depends on X86 && 64BIT + select CRYPTO_ALGAPI + select CRYPTO_DES + help + Triple DES EDE (FIPS 46-3) algorithm. + + This module provides implementation of the Triple DES EDE cipher + algorithm that is optimized for x86-64 processors. Two versions of + algorithm are provided; regular processing one input block and + one that processes three blocks parallel. + config CRYPTO_FCRYPT tristate "FCrypt cipher algorithm" select CRYPTO_ALGAPI diff --git a/crypto/des_generic.c b/crypto/des_generic.c index f6cf63f88468..298d464ab7d2 100644 --- a/crypto/des_generic.c +++ b/crypto/des_generic.c @@ -859,13 +859,10 @@ static void des_decrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src) * property. * */ -static int des3_ede_setkey(struct crypto_tfm *tfm, const u8 *key, - unsigned int keylen) +int __des3_ede_setkey(u32 *expkey, u32 *flags, const u8 *key, + unsigned int keylen) { const u32 *K = (const u32 *)key; - struct des3_ede_ctx *dctx = crypto_tfm_ctx(tfm); - u32 *expkey = dctx->expkey; - u32 *flags = &tfm->crt_flags; if (unlikely(!((K[0] ^ K[2]) | (K[1] ^ K[3])) || !((K[2] ^ K[4]) | (K[3] ^ K[5]))) && @@ -880,6 +877,17 @@ static int des3_ede_setkey(struct crypto_tfm *tfm, const u8 *key, return 0; } +EXPORT_SYMBOL_GPL(__des3_ede_setkey); + +static int des3_ede_setkey(struct crypto_tfm *tfm, const u8 *key, + unsigned int keylen) +{ + struct des3_ede_ctx *dctx = crypto_tfm_ctx(tfm); + u32 *flags = &tfm->crt_flags; + u32 *expkey = dctx->expkey; + + return __des3_ede_setkey(expkey, flags, key, keylen); +} static void des3_ede_encrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src) { @@ -945,6 +953,8 @@ static void des3_ede_decrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src) static struct crypto_alg des_algs[2] = { { .cra_name = "des", + .cra_driver_name = "des-generic", + .cra_priority = 100, .cra_flags = CRYPTO_ALG_TYPE_CIPHER, .cra_blocksize = DES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct des_ctx), @@ -958,6 +968,8 @@ static struct crypto_alg des_algs[2] = { { .cia_decrypt = des_decrypt } } }, { .cra_name = "des3_ede", + .cra_driver_name = "des3_ede-generic", + .cra_priority = 100, .cra_flags = CRYPTO_ALG_TYPE_CIPHER, .cra_blocksize = DES3_EDE_BLOCK_SIZE, .cra_ctxsize = sizeof(struct des3_ede_ctx), diff --git a/include/crypto/des.h b/include/crypto/des.h index 2971c6304ade..fc6274c6bb26 100644 --- a/include/crypto/des.h +++ b/include/crypto/des.h @@ -16,4 +16,7 @@ extern unsigned long des_ekey(u32 *pe, const u8 *k); +extern int __des3_ede_setkey(u32 *expkey, u32 *flags, const u8 *key, + unsigned int keylen); + #endif /* __CRYPTO_DES_H */ -- cgit v1.2.3 From 4af4206be2bd1933cae20c2b6fb2058dbc887f7c Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Sun, 13 Apr 2014 20:58:54 +0200 Subject: tracing: Fix syscall_*regfunc() vs copy_process() race syscall_regfunc() and syscall_unregfunc() should set/clear TIF_SYSCALL_TRACEPOINT system-wide, but do_each_thread() can race with copy_process() and miss the new child which was not added to the process/thread lists yet. Change copy_process() to update the child's TIF_SYSCALL_TRACEPOINT under tasklist. Link: http://lkml.kernel.org/p/20140413185854.GB20668@redhat.com Cc: stable@vger.kernel.org # 2.6.33 Fixes: a871bd33a6c0 "tracing: Add syscall tracepoints" Acked-by: Frederic Weisbecker Acked-by: Paul E. McKenney Signed-off-by: Oleg Nesterov Signed-off-by: Steven Rostedt --- include/trace/syscall.h | 15 +++++++++++++++ kernel/fork.c | 2 ++ 2 files changed, 17 insertions(+) (limited to 'include') diff --git a/include/trace/syscall.h b/include/trace/syscall.h index fed853f3d7aa..9674145e2f6a 100644 --- a/include/trace/syscall.h +++ b/include/trace/syscall.h @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -32,4 +33,18 @@ struct syscall_metadata { struct ftrace_event_call *exit_event; }; +#if defined(CONFIG_TRACEPOINTS) && defined(CONFIG_HAVE_SYSCALL_TRACEPOINTS) +static inline void syscall_tracepoint_update(struct task_struct *p) +{ + if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) + set_tsk_thread_flag(p, TIF_SYSCALL_TRACEPOINT); + else + clear_tsk_thread_flag(p, TIF_SYSCALL_TRACEPOINT); +} +#else +static inline void syscall_tracepoint_update(struct task_struct *p) +{ +} +#endif + #endif /* _TRACE_SYSCALL_H */ diff --git a/kernel/fork.c b/kernel/fork.c index d2799d1fc952..6a13c46cd87d 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1487,7 +1487,9 @@ static struct task_struct *copy_process(unsigned long clone_flags, total_forks++; spin_unlock(¤t->sighand->siglock); + syscall_tracepoint_update(p); write_unlock_irq(&tasklist_lock); + proc_fork_connector(p); cgroup_post_fork(p); if (clone_flags & CLONE_THREAD) -- cgit v1.2.3 From 4d4c9cc839a308be3289a361ccba4447ee140552 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 17 Jun 2014 08:59:16 -0400 Subject: tracing: Add __field_struct macro for TRACE_EVENT() Currently the __field() macro in TRACE_EVENT is only good for primitive values, such as integers and pointers, but it fails on complex data types such as structures or unions. This is because the __field() macro determines if the variable is signed or not with the test of: (((type)(-1)) < (type)1) Unfortunately, that fails when type is a structure. Since trace events should support structures as fields a new macro is created for such a case called __field_struct() which acts exactly the same as __field() does but it does not do the signed type check and just uses a constant false for that answer. Cc: Tony Luck Signed-off-by: Steven Rostedt --- include/trace/ftrace.h | 33 ++++++++++++++++++++++++++++++ samples/trace_events/trace-events-sample.h | 3 ++- 2 files changed, 35 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h index 0fd06fef9fac..26b4f2e13275 100644 --- a/include/trace/ftrace.h +++ b/include/trace/ftrace.h @@ -44,6 +44,12 @@ #undef __field_ext #define __field_ext(type, item, filter_type) type item; +#undef __field_struct +#define __field_struct(type, item) type item; + +#undef __field_struct_ext +#define __field_struct_ext(type, item, filter_type) type item; + #undef __array #define __array(type, item, len) type item[len]; @@ -122,6 +128,12 @@ #undef __field_ext #define __field_ext(type, item, filter_type) +#undef __field_struct +#define __field_struct(type, item) + +#undef __field_struct_ext +#define __field_struct_ext(type, item, filter_type) + #undef __array #define __array(type, item, len) @@ -315,9 +327,21 @@ static struct trace_event_functions ftrace_event_type_funcs_##call = { \ if (ret) \ return ret; +#undef __field_struct_ext +#define __field_struct_ext(type, item, filter_type) \ + ret = trace_define_field(event_call, #type, #item, \ + offsetof(typeof(field), item), \ + sizeof(field.item), \ + 0, filter_type); \ + if (ret) \ + return ret; + #undef __field #define __field(type, item) __field_ext(type, item, FILTER_OTHER) +#undef __field_struct +#define __field_struct(type, item) __field_struct_ext(type, item, FILTER_OTHER) + #undef __array #define __array(type, item, len) \ do { \ @@ -379,6 +403,12 @@ ftrace_define_fields_##call(struct ftrace_event_call *event_call) \ #undef __field_ext #define __field_ext(type, item, filter_type) +#undef __field_struct +#define __field_struct(type, item) + +#undef __field_struct_ext +#define __field_struct_ext(type, item, filter_type) + #undef __array #define __array(type, item, len) @@ -550,6 +580,9 @@ static inline notrace int ftrace_get_offsets_##call( \ #undef __field #define __field(type, item) +#undef __field_struct +#define __field_struct(type, item) + #undef __array #define __array(type, item, len) diff --git a/samples/trace_events/trace-events-sample.h b/samples/trace_events/trace-events-sample.h index 6af373236d73..4b0113f73ee9 100644 --- a/samples/trace_events/trace-events-sample.h +++ b/samples/trace_events/trace-events-sample.h @@ -56,7 +56,8 @@ * struct: This defines the way the data will be stored in the ring buffer. * There are currently two types of elements. __field and __array. * a __field is broken up into (type, name). Where type can be any - * type but an array. + * primitive type (integer, long or pointer). __field_struct() can + * be any static complex data value (struct, union, but not an array). * For an array. there are three fields. (type, name, size). The * type of elements in the array, the name of the field and the size * of the array. -- cgit v1.2.3 From c4027faf1dcfc325663464b3f97847358b172c0b Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Wed, 11 Jun 2014 18:14:39 +0800 Subject: ASoC: atmel-ssc: distinguish whether SSC supports fslen ext Add compatible string to distinguish whether SSC supports frame sync length extension. Signed-off-by: Bo Shen Acked-by: Nicolas Ferre Signed-off-by: Mark Brown --- drivers/misc/atmel-ssc.c | 13 +++++++++++++ include/linux/atmel-ssc.h | 1 + 2 files changed, 14 insertions(+) (limited to 'include') diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c index 22de13727641..60843a275abd 100644 --- a/drivers/misc/atmel-ssc.c +++ b/drivers/misc/atmel-ssc.c @@ -83,16 +83,26 @@ EXPORT_SYMBOL(ssc_free); static struct atmel_ssc_platform_data at91rm9200_config = { .use_dma = 0, + .has_fslen_ext = 0, +}; + +static struct atmel_ssc_platform_data at91sam9rl_config = { + .use_dma = 0, + .has_fslen_ext = 1, }; static struct atmel_ssc_platform_data at91sam9g45_config = { .use_dma = 1, + .has_fslen_ext = 1, }; static const struct platform_device_id atmel_ssc_devtypes[] = { { .name = "at91rm9200_ssc", .driver_data = (unsigned long) &at91rm9200_config, + }, { + .name = "at91sam9rl_ssc", + .driver_data = (unsigned long) &at91sam9rl_config, }, { .name = "at91sam9g45_ssc", .driver_data = (unsigned long) &at91sam9g45_config, @@ -106,6 +116,9 @@ static const struct of_device_id atmel_ssc_dt_ids[] = { { .compatible = "atmel,at91rm9200-ssc", .data = &at91rm9200_config, + }, { + .compatible = "atmel,at91sam9rl-ssc", + .data = &at91sam9rl_config, }, { .compatible = "atmel,at91sam9g45-ssc", .data = &at91sam9g45_config, diff --git a/include/linux/atmel-ssc.h b/include/linux/atmel-ssc.h index 571a12ebb018..e8dd40873d55 100644 --- a/include/linux/atmel-ssc.h +++ b/include/linux/atmel-ssc.h @@ -7,6 +7,7 @@ struct atmel_ssc_platform_data { int use_dma; + int has_fslen_ext; }; struct ssc_device { -- cgit v1.2.3 From dfaf535665faa4b5aba4b59633f6b724a467c96e Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Wed, 11 Jun 2014 18:14:40 +0800 Subject: ASoC: atmel_ssc_dai: enable fslen extension feature When SSC work as master, it will generate the frame sync signal. On old SoCs, it only supports frame sync length less or equal to 16bits, on newer SoCs, it supports frame sync length extension, which can support frame size larger than 16 bits. So, add this to make it supports playback 24/32 bits audio clips. Signed-off-by: Bo Shen Acked-by: Nicolas Ferre Signed-off-by: Mark Brown --- include/linux/atmel-ssc.h | 12 ++++++++++++ sound/soc/atmel/atmel_ssc_dai.c | 34 ++++++++++++++++++---------------- 2 files changed, 30 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/linux/atmel-ssc.h b/include/linux/atmel-ssc.h index e8dd40873d55..7c0f6549898b 100644 --- a/include/linux/atmel-ssc.h +++ b/include/linux/atmel-ssc.h @@ -72,6 +72,12 @@ void ssc_free(struct ssc_device *ssc); #define SSC_RFMR_DATNB_OFFSET 8 #define SSC_RFMR_FSEDGE_SIZE 1 #define SSC_RFMR_FSEDGE_OFFSET 24 +/* + * The FSLEN_EXT exist on at91sam9rl, at91sam9g10, + * at91sam9g20, and at91sam9g45 and newer SoCs + */ +#define SSC_RFMR_FSLEN_EXT_SIZE 4 +#define SSC_RFMR_FSLEN_EXT_OFFSET 28 #define SSC_RFMR_FSLEN_SIZE 4 #define SSC_RFMR_FSLEN_OFFSET 16 #define SSC_RFMR_FSOS_SIZE 4 @@ -110,6 +116,12 @@ void ssc_free(struct ssc_device *ssc); #define SSC_TFMR_FSDEN_OFFSET 23 #define SSC_TFMR_FSEDGE_SIZE 1 #define SSC_TFMR_FSEDGE_OFFSET 24 +/* + * The FSLEN_EXT exist on at91sam9rl, at91sam9g10, + * at91sam9g20, and at91sam9g45 and newer SoCs + */ +#define SSC_TFMR_FSLEN_EXT_SIZE 4 +#define SSC_TFMR_FSLEN_EXT_OFFSET 28 #define SSC_TFMR_FSLEN_SIZE 4 #define SSC_TFMR_FSLEN_OFFSET 16 #define SSC_TFMR_FSOS_SIZE 3 diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index de433cfd044c..f403f399808a 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -347,6 +347,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, u32 tfmr, rfmr, tcmr, rcmr; int start_event; int ret; + int fslen, fslen_ext; /* * Currently, there is only one set of dma params for @@ -387,18 +388,6 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - /* - * The SSC only supports up to 16-bit samples in I2S format, due - * to the size of the Frame Mode Register FSLEN field. - */ - if ((ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S - && bits > 16) { - printk(KERN_WARNING - "atmel_ssc_dai: sample size %d " - "is too large for I2S\n", bits); - return -EINVAL; - } - /* * Compute SSC register settings. */ @@ -413,6 +402,17 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, * from the MCK divider, and the BCLK signal * is output on the SSC TK line. */ + + if (bits > 16 && !ssc->pdata->has_fslen_ext) { + dev_err(dai->dev, + "sample size %d is too large for SSC device\n", + bits); + return -EINVAL; + } + + fslen_ext = (bits - 1) / 16; + fslen = (bits - 1) % 16; + rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) | SSC_BF(RCMR_STTDLY, START_DELAY) | SSC_BF(RCMR_START, SSC_START_FALLING_RF) @@ -420,9 +420,10 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, | SSC_BF(RCMR_CKO, SSC_CKO_NONE) | SSC_BF(RCMR_CKS, SSC_CKS_DIV); - rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) + rfmr = SSC_BF(RFMR_FSLEN_EXT, fslen_ext) + | SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE) - | SSC_BF(RFMR_FSLEN, (bits - 1)) + | SSC_BF(RFMR_FSLEN, fslen) | SSC_BF(RFMR_DATNB, (channels - 1)) | SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_LOOP, 0) @@ -435,10 +436,11 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, | SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS) | SSC_BF(TCMR_CKS, SSC_CKS_DIV); - tfmr = SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) + tfmr = SSC_BF(TFMR_FSLEN_EXT, fslen_ext) + | SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(TFMR_FSDEN, 0) | SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE) - | SSC_BF(TFMR_FSLEN, (bits - 1)) + | SSC_BF(TFMR_FSLEN, fslen) | SSC_BF(TFMR_DATNB, (channels - 1)) | SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATDEF, 0) -- cgit v1.2.3 From 94f99c875c109e51decf0d8c25ec2c946db20c56 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 16 Jun 2014 18:13:01 +0200 Subject: ASoC: Move name_prefix from CODEC to component Move the name_prefix from the CODEC struct to the component struct. This will eventually allow to specify prefixes for all types of components. It is also necessary to make the DAPM code component type independent (i.e. a DAPM context does not need to know whether it belongs to a CODEC or a platform or something else). Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 2 +- sound/soc/soc-core.c | 12 ++++++------ sound/soc/soc-dapm.c | 36 ++++++++++++++++++++++-------------- 3 files changed, 29 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index ed9e2d7e5fdc..e1cce0042f8a 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -682,6 +682,7 @@ struct snd_soc_component_driver { struct snd_soc_component { const char *name; int id; + const char *name_prefix; struct device *dev; unsigned int active; @@ -710,7 +711,6 @@ struct snd_soc_component { /* SoC Audio Codec device */ struct snd_soc_codec { const char *name; - const char *name_prefix; int id; struct device *dev; const struct snd_soc_codec_driver *driver; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index b87d7d882e6d..ba822e927187 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1108,7 +1108,7 @@ static void soc_remove_dai_links(struct snd_soc_card *card) } static void soc_set_name_prefix(struct snd_soc_card *card, - struct snd_soc_codec *codec) + struct snd_soc_component *component) { int i; @@ -1117,11 +1117,11 @@ static void soc_set_name_prefix(struct snd_soc_card *card, for (i = 0; i < card->num_configs; i++) { struct snd_soc_codec_conf *map = &card->codec_conf[i]; - if (map->of_node && codec->dev->of_node != map->of_node) + if (map->of_node && component->dev->of_node != map->of_node) continue; - if (map->dev_name && strcmp(codec->name, map->dev_name)) + if (map->dev_name && strcmp(component->name, map->dev_name)) continue; - codec->name_prefix = map->name_prefix; + component->name_prefix = map->name_prefix; break; } } @@ -1135,7 +1135,7 @@ static int soc_probe_codec(struct snd_soc_card *card, codec->card = card; codec->dapm.card = card; - soc_set_name_prefix(card, codec); + soc_set_name_prefix(card, &codec->component); if (!try_module_get(codec->dev->driver->owner)) return -ENODEV; @@ -2403,7 +2403,7 @@ int snd_soc_add_codec_controls(struct snd_soc_codec *codec, struct snd_card *card = codec->card->snd_card; return snd_soc_add_controls(card, codec->dev, controls, num_controls, - codec->name_prefix, &codec->component); + codec->component.name_prefix, &codec->component); } EXPORT_SYMBOL_GPL(snd_soc_add_codec_controls); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index a74b9bf23d9f..2f29b289a333 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -375,6 +375,13 @@ static void dapm_reset(struct snd_soc_card *card) } } +static const char *soc_dapm_prefix(struct snd_soc_dapm_context *dapm) +{ + if (!dapm->component) + return NULL; + return dapm->component->name_prefix; +} + static int soc_widget_read(struct snd_soc_dapm_widget *w, int reg, unsigned int *value) { @@ -570,11 +577,7 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, const char *name; int ret; - if (dapm->codec) - prefix = dapm->codec->name_prefix; - else - prefix = NULL; - + prefix = soc_dapm_prefix(dapm); if (prefix) prefix_len = strlen(prefix) + 1; else @@ -2371,14 +2374,16 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, const char *source; char prefixed_sink[80]; char prefixed_source[80]; + const char *prefix; int ret; - if (dapm->codec && dapm->codec->name_prefix) { + prefix = soc_dapm_prefix(dapm); + if (prefix) { snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s", - dapm->codec->name_prefix, route->sink); + prefix, route->sink); sink = prefixed_sink; snprintf(prefixed_source, sizeof(prefixed_source), "%s %s", - dapm->codec->name_prefix, route->source); + prefix, route->source); source = prefixed_source; } else { sink = route->sink; @@ -2439,6 +2444,7 @@ static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm, const char *source; char prefixed_sink[80]; char prefixed_source[80]; + const char *prefix; if (route->control) { dev_err(dapm->dev, @@ -2446,12 +2452,13 @@ static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm, return -EINVAL; } - if (dapm->codec && dapm->codec->name_prefix) { + prefix = soc_dapm_prefix(dapm); + if (prefix) { snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s", - dapm->codec->name_prefix, route->sink); + prefix, route->sink); sink = prefixed_sink; snprintf(prefixed_source, sizeof(prefixed_source), "%s %s", - dapm->codec->name_prefix, route->source); + prefix, route->source); source = prefixed_source; } else { sink = route->sink; @@ -2968,6 +2975,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_widget *widget) { struct snd_soc_dapm_widget *w; + const char *prefix; int ret; if ((w = dapm_cnew_widget(widget)) == NULL) @@ -3008,9 +3016,9 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, break; } - if (dapm->codec && dapm->codec->name_prefix) - w->name = kasprintf(GFP_KERNEL, "%s %s", - dapm->codec->name_prefix, widget->name); + prefix = soc_dapm_prefix(dapm); + if (prefix) + w->name = kasprintf(GFP_KERNEL, "%s %s", prefix, widget->name); else w->name = kasprintf(GFP_KERNEL, "%s", widget->name); -- cgit v1.2.3 From f4333203ec933f9272c90c7add01774ec2cf94d3 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 16 Jun 2014 18:13:02 +0200 Subject: ASoC: Move name and id from CODEC/platform to component The component struct already has a name and id field which are initialized to the same values as the same fields in the CODEC and platform structs. So remove them from the CODEC and platform structs and used the ones from the component struct instead. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 4 ---- include/trace/events/asoc.h | 6 ++--- sound/soc/codecs/tlv320dac33.c | 2 +- sound/soc/soc-cache.c | 7 +++--- sound/soc/soc-compress.c | 8 ++++--- sound/soc/soc-core.c | 53 ++++++++++++++++-------------------------- sound/soc/soc-dapm.c | 8 +++---- sound/soc/soc-pcm.c | 4 ++-- 8 files changed, 38 insertions(+), 54 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index e1cce0042f8a..f64bf9452466 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -710,8 +710,6 @@ struct snd_soc_component { /* SoC Audio Codec device */ struct snd_soc_codec { - const char *name; - int id; struct device *dev; const struct snd_soc_codec_driver *driver; @@ -848,8 +846,6 @@ struct snd_soc_platform_driver { }; struct snd_soc_platform { - const char *name; - int id; struct device *dev; const struct snd_soc_platform_driver *driver; diff --git a/include/trace/events/asoc.h b/include/trace/events/asoc.h index c75c795a377b..0194a641e4e2 100644 --- a/include/trace/events/asoc.h +++ b/include/trace/events/asoc.h @@ -296,17 +296,17 @@ TRACE_EVENT(snd_soc_cache_sync, TP_ARGS(codec, type, status), TP_STRUCT__entry( - __string( name, codec->name ) + __string( name, codec->component.name) __string( status, status ) __string( type, type ) __field( int, id ) ), TP_fast_assign( - __assign_str(name, codec->name); + __assign_str(name, codec->component.name); __assign_str(status, status); __assign_str(type, type); - __entry->id = codec->id; + __entry->id = codec->component.id; ), TP_printk("codec=%s.%d type=%s status=%s", __get_str(name), diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index df3a7506c023..ff006cc95520 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -1404,7 +1404,7 @@ static int dac33_soc_probe(struct snd_soc_codec *codec) if (dac33->irq >= 0) { ret = request_irq(dac33->irq, dac33_interrupt_handler, IRQF_TRIGGER_RISING, - codec->name, codec); + codec->component.name, codec); if (ret < 0) { dev_err(codec->dev, "Could not request IRQ%d (%d)\n", dac33->irq, ret); diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index 00e70b6c7da2..a9f82b5aba9d 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -78,7 +78,7 @@ int snd_soc_cache_init(struct snd_soc_codec *codec) mutex_init(&codec->cache_rw_mutex); dev_dbg(codec->dev, "ASoC: Initializing cache for %s codec\n", - codec->name); + codec->component.name); if (codec_drv->reg_cache_default) codec->reg_cache = kmemdup(codec_drv->reg_cache_default, @@ -98,8 +98,7 @@ int snd_soc_cache_init(struct snd_soc_codec *codec) int snd_soc_cache_exit(struct snd_soc_codec *codec) { dev_dbg(codec->dev, "ASoC: Destroying cache for %s codec\n", - codec->name); - + codec->component.name); kfree(codec->reg_cache); codec->reg_cache = NULL; return 0; @@ -192,7 +191,7 @@ int snd_soc_cache_sync(struct snd_soc_codec *codec) return 0; dev_dbg(codec->dev, "ASoC: Syncing cache for %s codec\n", - codec->name); + codec->component.name); trace_snd_soc_cache_sync(codec, name, "start"); ret = snd_soc_flat_cache_sync(codec); if (!ret) diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 10f7f1da2aca..f96fb96b2678 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -37,7 +37,8 @@ static int soc_compr_open(struct snd_compr_stream *cstream) if (platform->driver->compr_ops && platform->driver->compr_ops->open) { ret = platform->driver->compr_ops->open(cstream); if (ret < 0) { - pr_err("compress asoc: can't open platform %s\n", platform->name); + pr_err("compress asoc: can't open platform %s\n", + platform->component.name); goto out; } } @@ -84,7 +85,8 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) if (platform->driver->compr_ops && platform->driver->compr_ops->open) { ret = platform->driver->compr_ops->open(cstream); if (ret < 0) { - pr_err("compress asoc: can't open platform %s\n", platform->name); + pr_err("compress asoc: can't open platform %s\n", + platform->component.name); goto out; } } @@ -680,7 +682,7 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) ret = snd_compress_new(rtd->card->snd_card, num, direction, compr); if (ret < 0) { pr_err("compress asoc: can't create compress for codec %s\n", - codec->name); + codec->component.name); goto compr_err; } diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index ba822e927187..b8cc88a9f947 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -274,7 +274,7 @@ static void soc_init_codec_debugfs(struct snd_soc_codec *codec) { struct dentry *debugfs_card_root = codec->card->debugfs_card_root; - codec->debugfs_codec_root = debugfs_create_dir(codec->name, + codec->debugfs_codec_root = debugfs_create_dir(codec->component.name, debugfs_card_root); if (!codec->debugfs_codec_root) { dev_warn(codec->dev, @@ -306,8 +306,8 @@ static void soc_init_platform_debugfs(struct snd_soc_platform *platform) { struct dentry *debugfs_card_root = platform->card->debugfs_card_root; - platform->debugfs_platform_root = debugfs_create_dir(platform->name, - debugfs_card_root); + platform->debugfs_platform_root = debugfs_create_dir( + platform->component.name, debugfs_card_root); if (!platform->debugfs_platform_root) { dev_warn(platform->dev, "ASoC: Failed to create platform debugfs directory\n"); @@ -335,7 +335,7 @@ static ssize_t codec_list_read_file(struct file *file, char __user *user_buf, list_for_each_entry(codec, &codec_list, list) { len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n", - codec->name); + codec->component.name); if (len >= 0) ret += len; if (ret > PAGE_SIZE) { @@ -406,7 +406,7 @@ static ssize_t platform_list_read_file(struct file *file, list_for_each_entry(platform, &platform_list, list) { len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n", - platform->name); + platform->component.name); if (len >= 0) ret += len; if (ret > PAGE_SIZE) { @@ -528,7 +528,7 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec) codec->ac97->dev.release = soc_ac97_device_release; dev_set_name(&codec->ac97->dev, "%d-%d:%s", - codec->card->snd_card->number, 0, codec->name); + codec->card->snd_card->number, 0, codec->component.name); err = device_register(&codec->ac97->dev); if (err < 0) { dev_err(codec->dev, "ASoC: Can't register ac97 bus\n"); @@ -857,7 +857,7 @@ static struct snd_soc_codec *soc_find_codec(const struct device_node *codec_of_n if (codec->dev->of_node != codec_of_node) continue; } else { - if (strcmp(codec->name, codec_name)) + if (strcmp(codec->component.name, codec_name)) continue; } @@ -945,7 +945,7 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num) dai_link->platform_of_node) continue; } else { - if (strcmp(platform->name, platform_name)) + if (strcmp(platform->component.name, platform_name)) continue; } @@ -1177,7 +1177,7 @@ static int soc_probe_codec(struct snd_soc_card *card, WARN(codec->dapm.idle_bias_off && codec->dapm.bias_level != SND_SOC_BIAS_OFF, "codec %s can not start from non-off bias with idle_bias_off==1\n", - codec->name); + codec->component.name); } if (driver->controls) @@ -1647,7 +1647,8 @@ static struct snd_soc_codec *soc_find_matching_codec(struct snd_soc_card *card, if (aux_dev->codec_of_node && (codec->dev->of_node != aux_dev->codec_of_node)) continue; - if (aux_dev->codec_name && strcmp(codec->name, aux_dev->codec_name)) + if (aux_dev->codec_name && + strcmp(codec->component.name, aux_dev->codec_name)) continue; return codec; } @@ -4131,11 +4132,6 @@ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform, { int ret; - /* create platform component name */ - platform->name = fmt_single_name(dev, &platform->id); - if (platform->name == NULL) - return -ENOMEM; - platform->dev = dev; platform->driver = platform_drv; platform->dapm.dev = dev; @@ -4161,7 +4157,8 @@ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform, list_add(&platform->list, &platform_list); mutex_unlock(&client_mutex); - dev_dbg(dev, "ASoC: Registered platform '%s'\n", platform->name); + dev_dbg(dev, "ASoC: Registered platform '%s'\n", + platform->component.name); return 0; } @@ -4205,8 +4202,7 @@ void snd_soc_remove_platform(struct snd_soc_platform *platform) mutex_unlock(&client_mutex); dev_dbg(platform->dev, "ASoC: Unregistered platform '%s'\n", - platform->name); - kfree(platform->name); + platform->component.name); } EXPORT_SYMBOL_GPL(snd_soc_remove_platform); @@ -4312,13 +4308,6 @@ int snd_soc_register_codec(struct device *dev, if (codec == NULL) return -ENOMEM; - /* create CODEC component name */ - codec->name = fmt_single_name(dev, &codec->id); - if (codec->name == NULL) { - ret = -ENOMEM; - goto fail_codec; - } - if (codec_drv->write) codec->component.write = snd_soc_codec_drv_write; if (codec_drv->read) @@ -4368,19 +4357,17 @@ int snd_soc_register_codec(struct device *dev, codec, dai_drv, num_dai, false); if (ret < 0) { dev_err(codec->dev, "ASoC: Failed to regster component: %d\n", ret); - goto fail_codec_name; + goto fail_codec; } - dev_dbg(codec->dev, "ASoC: Registered codec '%s'\n", codec->name); + dev_dbg(codec->dev, "ASoC: Registered codec '%s'\n", + codec->component.name); return 0; -fail_codec_name: +fail_codec: mutex_lock(&client_mutex); list_del(&codec->list); mutex_unlock(&client_mutex); - - kfree(codec->name); -fail_codec: kfree(codec); return ret; } @@ -4408,10 +4395,10 @@ found: list_del(&codec->list); mutex_unlock(&client_mutex); - dev_dbg(codec->dev, "ASoC: Unregistered codec '%s'\n", codec->name); + dev_dbg(codec->dev, "ASoC: Unregistered codec '%s'\n", + codec->component.name); snd_soc_cache_exit(codec); - kfree(codec->name); kfree(codec); } EXPORT_SYMBOL_GPL(snd_soc_unregister_codec); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 2f29b289a333..074e9ce136f8 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3400,8 +3400,8 @@ void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card) source = cpu_dai->playback_widget; sink = codec_dai->playback_widget; dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n", - cpu_dai->codec->name, source->name, - codec_dai->platform->name, sink->name); + cpu_dai->component->name, source->name, + codec_dai->component->name, sink->name); snd_soc_dapm_add_path(&card->dapm, source, sink, NULL, NULL); @@ -3412,8 +3412,8 @@ void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card) source = codec_dai->capture_widget; sink = cpu_dai->capture_widget; dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n", - codec_dai->codec->name, source->name, - cpu_dai->platform->name, sink->name); + codec_dai->component->name, source->name, + cpu_dai->component->name, sink->name); snd_soc_dapm_add_path(&card->dapm, source, sink, NULL, NULL); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 54d18f22a33e..9b78bb619bec 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -376,7 +376,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) ret = platform->driver->ops->open(substream); if (ret < 0) { dev_err(platform->dev, "ASoC: can't open platform" - " %s: %d\n", platform->name, ret); + " %s: %d\n", platform->component.name, ret); goto platform_err; } } @@ -707,7 +707,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, ret = platform->driver->ops->hw_params(substream, params); if (ret < 0) { dev_err(platform->dev, "ASoC: %s hw params failed: %d\n", - platform->name, ret); + platform->component.name, ret); goto platform_err; } } -- cgit v1.2.3 From c051b21f71d1ffdfd7ad406a1ef5ede5e5f974c5 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 22 May 2014 03:25:50 +0000 Subject: rtmutex: Confine deadlock logic to futex The deadlock logic is only required for futexes. Remove the extra arguments for the public functions and also for the futex specific ones which get always called with deadlock detection enabled. Signed-off-by: Thomas Gleixner Reviewed-by: Steven Rostedt --- include/linux/rtmutex.h | 6 ++--- kernel/futex.c | 10 +++---- kernel/locking/rtmutex.c | 59 +++++++++++++++++++++-------------------- kernel/locking/rtmutex_common.h | 7 +++-- 4 files changed, 40 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/include/linux/rtmutex.h b/include/linux/rtmutex.h index 3aed8d737e1a..1abba5ce2a2f 100644 --- a/include/linux/rtmutex.h +++ b/include/linux/rtmutex.h @@ -90,11 +90,9 @@ extern void __rt_mutex_init(struct rt_mutex *lock, const char *name); extern void rt_mutex_destroy(struct rt_mutex *lock); extern void rt_mutex_lock(struct rt_mutex *lock); -extern int rt_mutex_lock_interruptible(struct rt_mutex *lock, - int detect_deadlock); +extern int rt_mutex_lock_interruptible(struct rt_mutex *lock); extern int rt_mutex_timed_lock(struct rt_mutex *lock, - struct hrtimer_sleeper *timeout, - int detect_deadlock); + struct hrtimer_sleeper *timeout); extern int rt_mutex_trylock(struct rt_mutex *lock); diff --git a/kernel/futex.c b/kernel/futex.c index b632b5f3f094..e5c6c404be1d 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -1718,7 +1718,7 @@ retry_private: this->pi_state = pi_state; ret = rt_mutex_start_proxy_lock(&pi_state->pi_mutex, this->rt_waiter, - this->task, 1); + this->task); if (ret == 1) { /* We got the lock. */ requeue_pi_wake_futex(this, &key2, hb2); @@ -2337,9 +2337,9 @@ retry_private: /* * Block on the PI mutex: */ - if (!trylock) - ret = rt_mutex_timed_lock(&q.pi_state->pi_mutex, to, 1); - else { + if (!trylock) { + ret = rt_mutex_timed_futex_lock(&q.pi_state->pi_mutex, to); + } else { ret = rt_mutex_trylock(&q.pi_state->pi_mutex); /* Fixup the trylock return value: */ ret = ret ? 0 : -EWOULDBLOCK; @@ -2669,7 +2669,7 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags, */ WARN_ON(!q.pi_state); pi_mutex = &q.pi_state->pi_mutex; - ret = rt_mutex_finish_proxy_lock(pi_mutex, to, &rt_waiter, 1); + ret = rt_mutex_finish_proxy_lock(pi_mutex, to, &rt_waiter); debug_rt_mutex_free_waiter(&rt_waiter); spin_lock(q.lock_ptr); diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c index 1e8fdabb19de..32906482edd1 100644 --- a/kernel/locking/rtmutex.c +++ b/kernel/locking/rtmutex.c @@ -1228,16 +1228,15 @@ rt_mutex_slowunlock(struct rt_mutex *lock) */ static inline int rt_mutex_fastlock(struct rt_mutex *lock, int state, - int detect_deadlock, int (*slowfn)(struct rt_mutex *lock, int state, struct hrtimer_sleeper *timeout, int detect_deadlock)) { - if (!detect_deadlock && likely(rt_mutex_cmpxchg(lock, NULL, current))) { + if (likely(rt_mutex_cmpxchg(lock, NULL, current))) { rt_mutex_deadlock_account_lock(lock, current); return 0; } else - return slowfn(lock, state, NULL, detect_deadlock); + return slowfn(lock, state, NULL, 0); } static inline int @@ -1284,54 +1283,59 @@ void __sched rt_mutex_lock(struct rt_mutex *lock) { might_sleep(); - rt_mutex_fastlock(lock, TASK_UNINTERRUPTIBLE, 0, rt_mutex_slowlock); + rt_mutex_fastlock(lock, TASK_UNINTERRUPTIBLE, rt_mutex_slowlock); } EXPORT_SYMBOL_GPL(rt_mutex_lock); /** * rt_mutex_lock_interruptible - lock a rt_mutex interruptible * - * @lock: the rt_mutex to be locked - * @detect_deadlock: deadlock detection on/off + * @lock: the rt_mutex to be locked * * Returns: - * 0 on success - * -EINTR when interrupted by a signal - * -EDEADLK when the lock would deadlock (when deadlock detection is on) + * 0 on success + * -EINTR when interrupted by a signal */ -int __sched rt_mutex_lock_interruptible(struct rt_mutex *lock, - int detect_deadlock) +int __sched rt_mutex_lock_interruptible(struct rt_mutex *lock) { might_sleep(); - return rt_mutex_fastlock(lock, TASK_INTERRUPTIBLE, - detect_deadlock, rt_mutex_slowlock); + return rt_mutex_fastlock(lock, TASK_INTERRUPTIBLE, rt_mutex_slowlock); } EXPORT_SYMBOL_GPL(rt_mutex_lock_interruptible); +/* + * Futex variant with full deadlock detection. + */ +int rt_mutex_timed_futex_lock(struct rt_mutex *lock, + struct hrtimer_sleeper *timeout) +{ + might_sleep(); + + return rt_mutex_timed_fastlock(lock, TASK_INTERRUPTIBLE, timeout, 1, + rt_mutex_slowlock); +} + /** * rt_mutex_timed_lock - lock a rt_mutex interruptible * the timeout structure is provided * by the caller * - * @lock: the rt_mutex to be locked + * @lock: the rt_mutex to be locked * @timeout: timeout structure or NULL (no timeout) - * @detect_deadlock: deadlock detection on/off * * Returns: - * 0 on success - * -EINTR when interrupted by a signal + * 0 on success + * -EINTR when interrupted by a signal * -ETIMEDOUT when the timeout expired - * -EDEADLK when the lock would deadlock (when deadlock detection is on) */ int -rt_mutex_timed_lock(struct rt_mutex *lock, struct hrtimer_sleeper *timeout, - int detect_deadlock) +rt_mutex_timed_lock(struct rt_mutex *lock, struct hrtimer_sleeper *timeout) { might_sleep(); - return rt_mutex_timed_fastlock(lock, TASK_INTERRUPTIBLE, timeout, - detect_deadlock, rt_mutex_slowlock); + return rt_mutex_timed_fastlock(lock, TASK_INTERRUPTIBLE, timeout, 0, + rt_mutex_slowlock); } EXPORT_SYMBOL_GPL(rt_mutex_timed_lock); @@ -1437,7 +1441,6 @@ void rt_mutex_proxy_unlock(struct rt_mutex *lock, * @lock: the rt_mutex to take * @waiter: the pre-initialized rt_mutex_waiter * @task: the task to prepare - * @detect_deadlock: perform deadlock detection (1) or not (0) * * Returns: * 0 - task blocked on lock @@ -1448,7 +1451,7 @@ void rt_mutex_proxy_unlock(struct rt_mutex *lock, */ int rt_mutex_start_proxy_lock(struct rt_mutex *lock, struct rt_mutex_waiter *waiter, - struct task_struct *task, int detect_deadlock) + struct task_struct *task) { int ret; @@ -1506,22 +1509,20 @@ struct task_struct *rt_mutex_next_owner(struct rt_mutex *lock) * rt_mutex_finish_proxy_lock() - Complete lock acquisition * @lock: the rt_mutex we were woken on * @to: the timeout, null if none. hrtimer should already have - * been started. + * been started. * @waiter: the pre-initialized rt_mutex_waiter - * @detect_deadlock: perform deadlock detection (1) or not (0) * * Complete the lock acquisition started our behalf by another thread. * * Returns: * 0 - success - * <0 - error, one of -EINTR, -ETIMEDOUT, or -EDEADLK + * <0 - error, one of -EINTR, -ETIMEDOUT * * Special API call for PI-futex requeue support */ int rt_mutex_finish_proxy_lock(struct rt_mutex *lock, struct hrtimer_sleeper *to, - struct rt_mutex_waiter *waiter, - int detect_deadlock) + struct rt_mutex_waiter *waiter) { int ret; diff --git a/kernel/locking/rtmutex_common.h b/kernel/locking/rtmutex_common.h index 7431a9c86f35..cd3ec209d0c8 100644 --- a/kernel/locking/rtmutex_common.h +++ b/kernel/locking/rtmutex_common.h @@ -111,12 +111,11 @@ extern void rt_mutex_proxy_unlock(struct rt_mutex *lock, struct task_struct *proxy_owner); extern int rt_mutex_start_proxy_lock(struct rt_mutex *lock, struct rt_mutex_waiter *waiter, - struct task_struct *task, - int detect_deadlock); + struct task_struct *task); extern int rt_mutex_finish_proxy_lock(struct rt_mutex *lock, struct hrtimer_sleeper *to, - struct rt_mutex_waiter *waiter, - int detect_deadlock); + struct rt_mutex_waiter *waiter); +extern int rt_mutex_timed_futex_lock(struct rt_mutex *l, struct hrtimer_sleeper *to); #ifdef CONFIG_DEBUG_RT_MUTEXES # include "rtmutex-debug.h" -- cgit v1.2.3 From 7df3788410e674423375b88e2d95c46e4015f5f5 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 16 Jun 2014 18:13:04 +0200 Subject: ASoC: Auto disconnect pins from all DAPM contexts Currently only pins in CODEC DAPM contexts are automatically marked as non-connected if the card has the fully_routed flag set. This makes sense since widgets which qualify for auto-disconnection are only found in CODEC DAPM contexts. But with componentisation this is going to change, so consider all widgets for auto-disconnection. Also it is probably faster to walk the widgets list only once rather than once for each CODEC. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 2 +- sound/soc/soc-core.c | 3 +-- sound/soc/soc-dapm.c | 27 +++++++++++---------------- 3 files changed, 13 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 6b59471cdf44..8db627cc2fbe 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -431,7 +431,7 @@ int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, const char *pin); int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, const char *pin); -void snd_soc_dapm_auto_nc_codec_pins(struct snd_soc_codec *codec); +void snd_soc_dapm_auto_nc_pins(struct snd_soc_card *card); /* Mostly internal - should not normally be used */ void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 0944da66538d..bca8a7150a4a 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1928,8 +1928,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) } if (card->fully_routed) - list_for_each_entry(codec, &card->codec_dev_list, card_list) - snd_soc_dapm_auto_nc_codec_pins(codec); + snd_soc_dapm_auto_nc_pins(card); snd_soc_dapm_new_widgets(card); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 074e9ce136f8..3ccbf9ba04b2 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3763,36 +3763,31 @@ static bool snd_soc_dapm_widget_in_card_paths(struct snd_soc_card *card, } /** - * snd_soc_dapm_auto_nc_codec_pins - call snd_soc_dapm_nc_pin for unused pins - * @codec: The codec whose pins should be processed + * snd_soc_dapm_auto_nc_pins - call snd_soc_dapm_nc_pin for unused pins + * @card: The card whose pins should be processed * - * Automatically call snd_soc_dapm_nc_pin() for any external pins in the codec - * which are unused. Pins are used if they are connected externally to the - * codec, whether that be to some other device, or a loop-back connection to - * the codec itself. + * Automatically call snd_soc_dapm_nc_pin() for any external pins in the card + * which are unused. Pins are used if they are connected externally to a + * component, whether that be to some other device, or a loop-back connection to + * the component itself. */ -void snd_soc_dapm_auto_nc_codec_pins(struct snd_soc_codec *codec) +void snd_soc_dapm_auto_nc_pins(struct snd_soc_card *card) { - struct snd_soc_card *card = codec->card; - struct snd_soc_dapm_context *dapm = &codec->dapm; struct snd_soc_dapm_widget *w; - dev_dbg(codec->dev, "ASoC: Auto NC: DAPMs: card:%p codec:%p\n", - &card->dapm, &codec->dapm); + dev_dbg(card->dev, "ASoC: Auto NC: DAPMs: card:%p\n", &card->dapm); list_for_each_entry(w, &card->widgets, list) { - if (w->dapm != dapm) - continue; switch (w->id) { case snd_soc_dapm_input: case snd_soc_dapm_output: case snd_soc_dapm_micbias: - dev_dbg(codec->dev, "ASoC: Auto NC: Checking widget %s\n", + dev_dbg(card->dev, "ASoC: Auto NC: Checking widget %s\n", w->name); if (!snd_soc_dapm_widget_in_card_paths(card, w)) { - dev_dbg(codec->dev, + dev_dbg(card->dev, "... Not in map; disabling\n"); - snd_soc_dapm_nc_pin(dapm, w->name); + snd_soc_dapm_nc_pin(w->dapm, w->name); } break; default: -- cgit v1.2.3 From 68f831c2724ab72c0088471b2ed1dc99e81948ef Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 16 Jun 2014 18:13:05 +0200 Subject: ASoC: Add a set_bias_level() callback to the DAPM context struct Currently the DAPM code directly looks at the CODEC driver struct to get a handle to the set_bias_level() callback. This patch adds a new set_bias_level() callback to the DAPM context struct. The DAPM code will use this new callback instead of the CODEC callback. For CODECs the new callback is set up to call the CODEC specific set_bias_level callback(). Not looking directly at the CODEC driver struct will allow non CODEC DAPM contexts to implement a set_bias_level() callback. This is also similar to how the seq_notifier() and stream_event() callbacks are currently handled. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 2 ++ sound/soc/soc-core.c | 10 ++++++++++ sound/soc/soc-dapm.c | 11 +++-------- 3 files changed, 15 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 8db627cc2fbe..3a5c4f969c04 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -601,6 +601,8 @@ struct snd_soc_dapm_context { struct list_head list; int (*stream_event)(struct snd_soc_dapm_context *dapm, int event); + int (*set_bias_level)(struct snd_soc_dapm_context *dapm, + enum snd_soc_bias_level level); #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_dapm; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index bca8a7150a4a..10e13c43bc54 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4285,6 +4285,14 @@ static int snd_soc_codec_drv_read(struct snd_soc_component *component, return 0; } +static int snd_soc_codec_set_bias_level(struct snd_soc_dapm_context *dapm, + enum snd_soc_bias_level level) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); + + return codec->driver->set_bias_level(codec, level); +} + /** * snd_soc_register_codec - Register a codec with the ASoC core * @@ -4322,6 +4330,8 @@ int snd_soc_register_codec(struct device *dev, codec->dapm.component = &codec->component; codec->dapm.seq_notifier = codec_drv->seq_notifier; codec->dapm.stream_event = codec_drv->stream_event; + if (codec_drv->set_bias_level) + codec->dapm.set_bias_level = snd_soc_codec_set_bias_level; codec->dev = dev; codec->driver = codec_drv; codec->component.val_bytes = codec_drv->reg_word_size; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index fab1a8813abf..6c94a6b3fce7 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -427,15 +427,10 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm, if (ret != 0) goto out; - if (dapm->codec) { - if (dapm->codec->driver->set_bias_level) - ret = dapm->codec->driver->set_bias_level(dapm->codec, - level); - else - dapm->bias_level = level; - } else if (!card || dapm != &card->dapm) { + if (dapm->set_bias_level) + ret = dapm->set_bias_level(dapm, level); + else if (!card || dapm != &card->dapm) dapm->bias_level = level; - } if (ret != 0) goto out; -- cgit v1.2.3 From ce0fc93ae56e2ba50ff8c220d69e4e860e889320 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 16 Jun 2014 18:13:06 +0200 Subject: ASoC: Add DAPM support at the component level This patch adds full DAPM support at the component level. Previously there was only full DAPM support for CODECs and partial DAPM support (e.g. no Mixers nor MUXs) for platforms. Having DAPM support at the component level will allow all types of components to use DAPM and also help in consolidating the DAPM support between CODECs and platforms. Since the DAPM context is directly embedded into the snd_soc_codec and snd_soc_platform struct and the 'dapm' field is directly referenced in a lot of drivers moving the field just right now is not possible without causing code churn. The approach this patch takes is to add two new fields to the component struct. One field which is the pointer to the actual DAPM context used by the component and one DAPM context that will be used as the default if no other context was specified. For CODECs and platforms the pointer is initialized to point to the CODEC or platform DAPM context. All generic code when referencing a component's DAPM struct will go via the pointer. This will make it possible to eventually seamlessly move the DAPM context from snd_soc_codec and snd_soc_platform struct over once all direct references have been eliminated. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 2 + include/sound/soc.h | 30 ++++++++++++++ sound/soc/soc-core.c | 19 ++++++--- sound/soc/soc-dapm.c | 100 ++++++++++++++++++++++++++++++----------------- 4 files changed, 110 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 3a5c4f969c04..e292683ee694 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -441,6 +441,8 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, struct snd_soc_dapm_widget_list **list); struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol); +struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm( + struct snd_kcontrol *kcontrol); /* dapm widget types */ enum snd_soc_dapm_type { diff --git a/include/sound/soc.h b/include/sound/soc.h index f64bf9452466..a21dfecba56b 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -706,6 +706,10 @@ struct snd_soc_component { int val_bytes; struct mutex io_mutex; + + /* Don't use these, use snd_soc_component_get_dapm() */ + struct snd_soc_dapm_context dapm; + struct snd_soc_dapm_context *dapm_ptr; }; /* SoC Audio Codec device */ @@ -1160,6 +1164,21 @@ static inline struct snd_soc_platform *snd_soc_component_to_platform( return container_of(component, struct snd_soc_platform, component); } +/** + * snd_soc_dapm_to_component() - Casts a DAPM context to the component it is + * embedded in + * @dapm: The DAPM context to cast to the component + * + * This function must only be used on DAPM contexts that are known to be part of + * a component (e.g. in a component driver). Otherwise the behavior is + * undefined. + */ +static inline struct snd_soc_component *snd_soc_dapm_to_component( + struct snd_soc_dapm_context *dapm) +{ + return container_of(dapm, struct snd_soc_component, dapm); +} + /** * snd_soc_dapm_to_codec() - Casts a DAPM context to the CODEC it is embedded in * @dapm: The DAPM context to cast to the CODEC @@ -1187,6 +1206,17 @@ static inline struct snd_soc_platform *snd_soc_dapm_to_platform( return container_of(dapm, struct snd_soc_platform, dapm); } +/** + * snd_soc_component_get_dapm() - Returns the DAPM context associated with a + * component + * @component: The component for which to get the DAPM context + */ +static inline struct snd_soc_dapm_context *snd_soc_component_get_dapm( + struct snd_soc_component *component) +{ + return component->dapm_ptr; +} + /* codec IO */ unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg); int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 10e13c43bc54..f519a9f7571c 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3997,6 +3997,8 @@ err: static int snd_soc_component_initialize(struct snd_soc_component *component, const struct snd_soc_component_driver *driver, struct device *dev) { + struct snd_soc_dapm_context *dapm; + component->name = fmt_single_name(dev, &component->id); if (!component->name) { dev_err(dev, "ASoC: Failed to allocate name\n"); @@ -4006,6 +4008,14 @@ static int snd_soc_component_initialize(struct snd_soc_component *component, component->dev = dev; component->driver = driver; + if (!component->dapm_ptr) + component->dapm_ptr = &component->dapm; + + dapm = component->dapm_ptr; + dapm->dev = dev; + dapm->component = component; + dapm->bias_level = SND_SOC_BIAS_OFF; + INIT_LIST_HEAD(&component->dai_list); mutex_init(&component->io_mutex); @@ -4131,6 +4141,8 @@ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform, { int ret; + platform->component.dapm_ptr = &platform->dapm; + ret = snd_soc_component_initialize(&platform->component, &platform_drv->component_driver, dev); if (ret) @@ -4138,9 +4150,7 @@ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform, platform->dev = dev; platform->driver = platform_drv; - platform->dapm.dev = dev; platform->dapm.platform = platform; - platform->dapm.component = &platform->component; platform->dapm.stream_event = platform_drv->stream_event; if (platform_drv->write) platform->component.write = snd_soc_platform_drv_write; @@ -4314,6 +4324,8 @@ int snd_soc_register_codec(struct device *dev, if (codec == NULL) return -ENOMEM; + codec->component.dapm_ptr = &codec->dapm; + ret = snd_soc_component_initialize(&codec->component, &codec_drv->component_driver, dev); if (ret) @@ -4324,10 +4336,7 @@ int snd_soc_register_codec(struct device *dev, if (codec_drv->read) codec->component.read = snd_soc_codec_drv_read; codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time; - codec->dapm.bias_level = SND_SOC_BIAS_OFF; - codec->dapm.dev = dev; codec->dapm.codec = codec; - codec->dapm.component = &codec->component; codec->dapm.seq_notifier = codec_drv->seq_notifier; codec->dapm.stream_event = codec_drv->stream_event; if (codec_drv->set_bias_level) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 6c94a6b3fce7..4702b926a6a0 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -349,13 +349,28 @@ static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol, return true; } +/** + * snd_soc_dapm_kcontrol_dapm() - Returns the dapm context associated to a + * kcontrol + * @kcontrol: The kcontrol + * + * Note: This function must only be used on kcontrols that are known to have + * been registered for a CODEC. Otherwise the behaviour is undefined. + */ +struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm( + struct snd_kcontrol *kcontrol) +{ + return dapm_kcontrol_get_wlist(kcontrol)->widgets[0]->dapm; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_dapm); + /** * snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol * @kcontrol: The kcontrol */ struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol) { - return dapm_kcontrol_get_wlist(kcontrol)->widgets[0]->codec; + return snd_soc_dapm_to_codec(snd_soc_dapm_kcontrol_dapm(kcontrol)); } EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_codec); @@ -382,23 +397,31 @@ static const char *soc_dapm_prefix(struct snd_soc_dapm_context *dapm) return dapm->component->name_prefix; } -static int soc_widget_read(struct snd_soc_dapm_widget *w, int reg, +static int soc_dapm_read(struct snd_soc_dapm_context *dapm, int reg, unsigned int *value) { - if (!w->dapm->component) + if (!dapm->component) return -EIO; - return snd_soc_component_read(w->dapm->component, reg, value); + return snd_soc_component_read(dapm->component, reg, value); } -static int soc_widget_update_bits(struct snd_soc_dapm_widget *w, +static int soc_dapm_update_bits(struct snd_soc_dapm_context *dapm, int reg, unsigned int mask, unsigned int value) { - if (!w->dapm->component) + if (!dapm->component) return -EIO; - return snd_soc_component_update_bits_async(w->dapm->component, reg, + return snd_soc_component_update_bits_async(dapm->component, reg, mask, value); } +static int soc_dapm_test_bits(struct snd_soc_dapm_context *dapm, + int reg, unsigned int mask, unsigned int value) +{ + if (!dapm->component) + return -EIO; + return snd_soc_component_test_bits(dapm->component, reg, mask, value); +} + static void soc_dapm_async_complete(struct snd_soc_dapm_context *dapm) { if (dapm->component) @@ -454,7 +477,7 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, int i; if (e->reg != SND_SOC_NOPM) { - soc_widget_read(dest, e->reg, &val); + soc_dapm_read(dapm, e->reg, &val); val = (val >> e->shift_l) & e->mask; item = snd_soc_enum_val_to_item(e, val); } else { @@ -498,7 +521,7 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w, unsigned int val; if (reg != SND_SOC_NOPM) { - soc_widget_read(w, reg, &val); + soc_dapm_read(w->dapm, reg, &val); val = (val >> shift) & mask; if (invert) val = max - val; @@ -1306,16 +1329,18 @@ static void dapm_seq_check_event(struct snd_soc_card *card, static void dapm_seq_run_coalesced(struct snd_soc_card *card, struct list_head *pending) { + struct snd_soc_dapm_context *dapm; struct snd_soc_dapm_widget *w; int reg; unsigned int value = 0; unsigned int mask = 0; - reg = list_first_entry(pending, struct snd_soc_dapm_widget, - power_list)->reg; + w = list_first_entry(pending, struct snd_soc_dapm_widget, power_list); + reg = w->reg; + dapm = w->dapm; list_for_each_entry(w, pending, power_list) { - WARN_ON(reg != w->reg); + WARN_ON(reg != w->reg || dapm != w->dapm); w->power = w->new_power; mask |= w->mask << w->shift; @@ -1324,7 +1349,7 @@ static void dapm_seq_run_coalesced(struct snd_soc_card *card, else value |= w->off_val << w->shift; - pop_dbg(w->dapm->dev, card->pop_time, + pop_dbg(dapm->dev, card->pop_time, "pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n", w->name, reg, value, mask); @@ -1337,14 +1362,12 @@ static void dapm_seq_run_coalesced(struct snd_soc_card *card, /* Any widget will do, they should all be updating the * same register. */ - w = list_first_entry(pending, struct snd_soc_dapm_widget, - power_list); - pop_dbg(w->dapm->dev, card->pop_time, + pop_dbg(dapm->dev, card->pop_time, "pop test : Applying 0x%x/0x%x to %x in %dms\n", value, mask, reg, card->pop_time); pop_wait(card->pop_time); - soc_widget_update_bits(w, reg, mask, value); + soc_dapm_update_bits(dapm, reg, mask, value); } list_for_each_entry(w, pending, power_list) { @@ -1490,7 +1513,8 @@ static void dapm_widget_update(struct snd_soc_card *card) if (!w) return; - ret = soc_widget_update_bits(w, update->reg, update->mask, update->val); + ret = soc_dapm_update_bits(w->dapm, update->reg, update->mask, + update->val); if (ret < 0) dev_err(w->dapm->dev, "ASoC: %s DAPM update failed: %d\n", w->name, ret); @@ -2672,7 +2696,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) /* Read the initial power state from the device */ if (w->reg >= 0) { - soc_widget_read(w, w->reg, &val); + soc_dapm_read(w->dapm, w->reg, &val); val = val >> w->shift; val &= w->mask; if (val == w->on_val) @@ -2703,8 +2727,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets); int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); - struct snd_soc_card *card = codec->card; + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_card *card = dapm->card; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; int reg = mc->reg; @@ -2713,17 +2737,20 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; unsigned int val; + int ret = 0; if (snd_soc_volsw_is_stereo(mc)) - dev_warn(codec->dapm.dev, + dev_warn(dapm->dev, "ASoC: Control '%s' is stereo, which is not supported\n", kcontrol->id.name); mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - if (dapm_kcontrol_is_powered(kcontrol) && reg != SND_SOC_NOPM) - val = (snd_soc_read(codec, reg) >> shift) & mask; - else + if (dapm_kcontrol_is_powered(kcontrol) && reg != SND_SOC_NOPM) { + ret = soc_dapm_read(dapm, reg, &val); + val = (val >> shift) & mask; + } else { val = dapm_kcontrol_get_value(kcontrol); + } mutex_unlock(&card->dapm_mutex); if (invert) @@ -2731,7 +2758,7 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, else ucontrol->value.integer.value[0] = val; - return 0; + return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw); @@ -2747,8 +2774,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw); int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); - struct snd_soc_card *card = codec->card; + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_card *card = dapm->card; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; int reg = mc->reg; @@ -2762,7 +2789,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, int ret = 0; if (snd_soc_volsw_is_stereo(mc)) - dev_warn(codec->dapm.dev, + dev_warn(dapm->dev, "ASoC: Control '%s' is stereo, which is not supported\n", kcontrol->id.name); @@ -2780,7 +2807,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, mask = mask << shift; val = val << shift; - reg_change = snd_soc_test_bits(codec, reg, mask, val); + reg_change = soc_dapm_test_bits(dapm, reg, mask, val); } if (change || reg_change) { @@ -2819,12 +2846,13 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw); int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int reg_val, val; + int ret = 0; if (e->reg != SND_SOC_NOPM) - reg_val = snd_soc_read(codec, e->reg); + ret = soc_dapm_read(dapm, e->reg, ®_val); else reg_val = dapm_kcontrol_get_value(kcontrol); @@ -2836,7 +2864,7 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, ucontrol->value.enumerated.item[1] = val; } - return 0; + return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double); @@ -2852,8 +2880,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double); int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); - struct snd_soc_card *card = codec->card; + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_card *card = dapm->card; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int *item = ucontrol->value.enumerated.item; unsigned int val, change; @@ -2876,7 +2904,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); if (e->reg != SND_SOC_NOPM) - change = snd_soc_test_bits(codec, e->reg, mask, val); + change = soc_dapm_test_bits(dapm, e->reg, mask, val); else change = dapm_kcontrol_set_value(kcontrol, val); -- cgit v1.2.3 From bc9af9fa9b89cb74eed020066d882abf238fad69 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 16 Jun 2014 18:13:07 +0200 Subject: ASoC: Use component DAPM context for platforms The snd_soc_platform dapm field is not accessed outside of the ASoC core. Switch it over to using the snd_soc_component DAPM context. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 4 +--- sound/soc/soc-core.c | 25 ++++++++++++------------- 2 files changed, 13 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index a21dfecba56b..84ebc079c92f 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -862,8 +862,6 @@ struct snd_soc_platform { struct snd_soc_component component; - struct snd_soc_dapm_context dapm; - #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_platform_root; #endif @@ -1203,7 +1201,7 @@ static inline struct snd_soc_codec *snd_soc_dapm_to_codec( static inline struct snd_soc_platform *snd_soc_dapm_to_platform( struct snd_soc_dapm_context *dapm) { - return container_of(dapm, struct snd_soc_platform, dapm); + return snd_soc_component_to_platform(snd_soc_dapm_to_component(dapm)); } /** diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index f519a9f7571c..711e99c43be3 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -314,7 +314,7 @@ static void soc_init_platform_debugfs(struct snd_soc_platform *platform) return; } - snd_soc_dapm_debugfs_init(&platform->dapm, + snd_soc_dapm_debugfs_init(&platform->component.dapm, platform->debugfs_platform_root); } @@ -974,7 +974,7 @@ static int soc_remove_platform(struct snd_soc_platform *platform) } /* Make sure all DAPM widgets are freed */ - snd_soc_dapm_free(&platform->dapm); + snd_soc_dapm_free(&platform->component.dapm); soc_cleanup_platform_debugfs(platform); platform->probed = 0; @@ -1210,7 +1210,7 @@ static int soc_probe_platform(struct snd_soc_card *card, struct snd_soc_dai *dai; platform->card = card; - platform->dapm.card = card; + platform->component.dapm.card = card; if (!try_module_get(platform->dev->driver->owner)) return -ENODEV; @@ -1218,7 +1218,7 @@ static int soc_probe_platform(struct snd_soc_card *card, soc_init_platform_debugfs(platform); if (driver->dapm_widgets) - snd_soc_dapm_new_controls(&platform->dapm, + snd_soc_dapm_new_controls(&platform->component.dapm, driver->dapm_widgets, driver->num_dapm_widgets); /* Create DAPM widgets for each DAI stream */ @@ -1226,10 +1226,11 @@ static int soc_probe_platform(struct snd_soc_card *card, if (component->dev != platform->dev) continue; list_for_each_entry(dai, &component->dai_list, list) - snd_soc_dapm_new_dai_widgets(&platform->dapm, dai); + snd_soc_dapm_new_dai_widgets(&platform->component.dapm, + dai); } - platform->dapm.idle_bias_off = 1; + platform->component.dapm.idle_bias_off = 1; if (driver->probe) { ret = driver->probe(platform); @@ -1244,13 +1245,13 @@ static int soc_probe_platform(struct snd_soc_card *card, snd_soc_add_platform_controls(platform, driver->controls, driver->num_controls); if (driver->dapm_routes) - snd_soc_dapm_add_routes(&platform->dapm, driver->dapm_routes, - driver->num_dapm_routes); + snd_soc_dapm_add_routes(&platform->component.dapm, + driver->dapm_routes, driver->num_dapm_routes); /* mark platform as probed and add to card platform list */ platform->probed = 1; list_add(&platform->card_list, &card->platform_dev_list); - list_add(&platform->dapm.list, &card->dapm_list); + list_add(&platform->component.dapm.list, &card->dapm_list); return 0; @@ -4141,8 +4142,6 @@ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform, { int ret; - platform->component.dapm_ptr = &platform->dapm; - ret = snd_soc_component_initialize(&platform->component, &platform_drv->component_driver, dev); if (ret) @@ -4150,8 +4149,8 @@ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform, platform->dev = dev; platform->driver = platform_drv; - platform->dapm.platform = platform; - platform->dapm.stream_event = platform_drv->stream_event; + platform->component.dapm.platform = platform; + platform->component.dapm.stream_event = platform_drv->stream_event; if (platform_drv->write) platform->component.write = snd_soc_platform_drv_write; if (platform_drv->read) -- cgit v1.2.3 From 14e8bdebfbc1d5c8804b3520233b2d4e516056bc Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 16 Jun 2014 18:13:08 +0200 Subject: ASoC: Add component level stream_event() and seq_notifier() support This patch adds stream_event() and seq_notifier() callbacks similar to those found in the snd_soc_codec_driver and snd_soc_platform driver struct to the snd_soc_component_driver struct. This is meant to unify the handling of these callbacks across different types of components and will eventually allow their removal from the CODEC and platfrom driver structs. The new callbacks are slightly different from the old ones in that they take a snd_soc_component as a parameter rather than a snd_soc_dapm_context. This was done since otherwise casting from the DAPM context to the component would typically be the first thing to do in the callback. And the interface becomes slightly cleaner by passing a snd_soc_component to all callbacks in the snd_soc_component_driver struct. The patch also already removes the stream_event() callback from the snd_soc_codec_driver and snd_soc_platform_driver structs as it is currently unused. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 9 +++------ sound/soc/soc-core.c | 25 ++++++++++++++++++++++--- 2 files changed, 25 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 84ebc079c92f..9a5b4f6fe847 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -677,6 +677,9 @@ struct snd_soc_component_driver { int (*of_xlate_dai_name)(struct snd_soc_component *component, struct of_phandle_args *args, const char **dai_name); + void (*seq_notifier)(struct snd_soc_component *, enum snd_soc_dapm_type, + int subseq); + int (*stream_event)(struct snd_soc_component *, int event); }; struct snd_soc_component { @@ -792,9 +795,6 @@ struct snd_soc_codec_driver { void (*seq_notifier)(struct snd_soc_dapm_context *, enum snd_soc_dapm_type, int); - /* codec stream completion event */ - int (*stream_event)(struct snd_soc_dapm_context *dapm, int event); - bool ignore_pmdown_time; /* Doesn't benefit from pmdown delay */ /* probe ordering - for components with runtime dependencies */ @@ -836,9 +836,6 @@ struct snd_soc_platform_driver { /* platform stream compress ops */ const struct snd_compr_ops *compr_ops; - /* platform stream completion event */ - int (*stream_event)(struct snd_soc_dapm_context *dapm, int event); - /* probe ordering - for components with runtime dependencies */ int probe_order; int remove_order; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 711e99c43be3..5fe732fdfd59 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3995,6 +3995,22 @@ err: return ret; } +static void snd_soc_component_seq_notifier(struct snd_soc_dapm_context *dapm, + enum snd_soc_dapm_type type, int subseq) +{ + struct snd_soc_component *component = dapm->component; + + component->driver->seq_notifier(component, type, subseq); +} + +static int snd_soc_component_stream_event(struct snd_soc_dapm_context *dapm, + int event) +{ + struct snd_soc_component *component = dapm->component; + + return component->driver->stream_event(component, event); +} + static int snd_soc_component_initialize(struct snd_soc_component *component, const struct snd_soc_component_driver *driver, struct device *dev) { @@ -4016,6 +4032,10 @@ static int snd_soc_component_initialize(struct snd_soc_component *component, dapm->dev = dev; dapm->component = component; dapm->bias_level = SND_SOC_BIAS_OFF; + if (driver->seq_notifier) + dapm->seq_notifier = snd_soc_component_seq_notifier; + if (driver->stream_event) + dapm->stream_event = snd_soc_component_stream_event; INIT_LIST_HEAD(&component->dai_list); mutex_init(&component->io_mutex); @@ -4150,7 +4170,6 @@ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform, platform->dev = dev; platform->driver = platform_drv; platform->component.dapm.platform = platform; - platform->component.dapm.stream_event = platform_drv->stream_event; if (platform_drv->write) platform->component.write = snd_soc_platform_drv_write; if (platform_drv->read) @@ -4336,8 +4355,8 @@ int snd_soc_register_codec(struct device *dev, codec->component.read = snd_soc_codec_drv_read; codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time; codec->dapm.codec = codec; - codec->dapm.seq_notifier = codec_drv->seq_notifier; - codec->dapm.stream_event = codec_drv->stream_event; + if (codec_drv->seq_notifier) + codec->dapm.seq_notifier = codec_drv->seq_notifier; if (codec_drv->set_bias_level) codec->dapm.set_bias_level = snd_soc_codec_set_bias_level; codec->dev = dev; -- cgit v1.2.3 From 9420d97b3f8672478696ae9c3e33051243e1f4a0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 16 Jun 2014 18:13:10 +0200 Subject: ASoC: dapm: Remove DAI DAPM context The DAI DAPM context was added in commit be09ad90 ("ASoC: core: Add platform DAI widget mapping") and the only user was removed again in commit ae10e7e8f ("ASoC: core: Only add platform DAI widgets once."). Now that we have a per component DAPM context it is unlikely that we'll need the DAI DAPM context again. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dai.h | 1 - sound/soc/soc-core.c | 12 +----------- 2 files changed, 1 insertion(+), 12 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 688f2ba8009f..031be2ab75d0 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -257,7 +257,6 @@ struct snd_soc_dai { struct snd_soc_dapm_widget *playback_widget; struct snd_soc_dapm_widget *capture_widget; - struct snd_soc_dapm_context dapm; /* DAI DMA data */ void *playback_dma_data; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 5fe732fdfd59..995a4b7f87eb 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1048,11 +1048,8 @@ static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order) cpu_dai->name, err); } cpu_dai->probed = 0; - - if (!cpu_dai->codec) { - snd_soc_dapm_free(&cpu_dai->dapm); + if (!cpu_dai->codec) module_put(cpu_dai->dev->driver->owner); - } } } @@ -1510,11 +1507,8 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) if (!cpu_dai->probed && cpu_dai->driver->probe_order == order) { if (!cpu_dai->codec) { - cpu_dai->dapm.card = card; if (!try_module_get(cpu_dai->dev->driver->owner)) return -ENODEV; - - list_add(&cpu_dai->dapm.list, &card->dapm_list); } if (cpu_dai->driver->probe) { @@ -3975,13 +3969,9 @@ static int snd_soc_register_dais(struct snd_soc_component *component, dai->component = component; dai->dev = dev; dai->driver = &dai_drv[i]; - dai->dapm.dev = dev; if (!dai->driver->ops) dai->driver->ops = &null_dai_ops; - if (!dai->codec) - dai->dapm.idle_bias_off = 1; - list_add(&dai->list, &component->dai_list); dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name); -- cgit v1.2.3 From 88a8fe3df65fa0229b04f1c03411062230091cdd Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 16 Jun 2014 18:13:11 +0200 Subject: ASoC: dapm: Remove platform field from widget and dapm context struct The platform field in the snd_soc_dapm_widget and snd_soc_dapm_context structs is now unused can be removed. New code that wants to get the platform for a widget or dapm context should use snd_soc_dapm_to_platform(w->dapm) or snd_soc_dapm_to_platform(dapm). Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 2 -- sound/soc/soc-core.c | 1 - sound/soc/soc-dapm.c | 1 - 3 files changed, 4 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index e292683ee694..aac04ff84eea 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -526,7 +526,6 @@ struct snd_soc_dapm_widget { const char *name; /* widget name */ const char *sname; /* stream name */ struct snd_soc_codec *codec; - struct snd_soc_platform *platform; struct list_head list; struct snd_soc_dapm_context *dapm; @@ -595,7 +594,6 @@ struct snd_soc_dapm_context { struct device *dev; /* from parent - for debug */ struct snd_soc_component *component; /* parent component */ struct snd_soc_codec *codec; /* parent codec */ - struct snd_soc_platform *platform; /* parent platform */ struct snd_soc_card *card; /* parent card */ /* used during DAPM updates */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 995a4b7f87eb..19c1e9588e05 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4159,7 +4159,6 @@ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform, platform->dev = dev; platform->driver = platform_drv; - platform->component.dapm.platform = platform; if (platform_drv->write) platform->component.write = snd_soc_platform_drv_write; if (platform_drv->read) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 4702b926a6a0..4bf08cffd1f1 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3097,7 +3097,6 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, w->dapm = dapm; w->codec = dapm->codec; - w->platform = dapm->platform; INIT_LIST_HEAD(&w->sources); INIT_LIST_HEAD(&w->sinks); INIT_LIST_HEAD(&w->list); -- cgit v1.2.3 From 2b8f2a28eac1d35a432705d269f02bdaeba9be8f Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 18 Jun 2014 11:01:41 +0200 Subject: net: phylib: add link_change_notify callback to phy device Add a notify callback to inform phy drivers when the core is about to do its link adjustment. No change for drivers that do not implement this callback. Signed-off-by: Daniel Mack Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 3 +++ include/linux/phy.h | 9 +++++++++ 2 files changed, 12 insertions(+) (limited to 'include') diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 3bc079a67a3d..f7c61812ea4a 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -720,6 +720,9 @@ void phy_state_machine(struct work_struct *work) mutex_lock(&phydev->lock); + if (phydev->drv->link_change_notify) + phydev->drv->link_change_notify(phydev); + switch (phydev->state) { case PHY_DOWN: case PHY_STARTING: diff --git a/include/linux/phy.h b/include/linux/phy.h index 864ddafad8cc..68041446c450 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -536,6 +536,15 @@ struct phy_driver { /* See set_wol, but for checking whether Wake on LAN is enabled. */ void (*get_wol)(struct phy_device *dev, struct ethtool_wolinfo *wol); + /* + * Called to inform a PHY device driver when the core is about to + * change the link state. This callback is supposed to be used as + * fixup hook for drivers that need to take action when the link + * state changes. Drivers are by no means allowed to mess with the + * PHY device structure in their implementations. + */ + void (*link_change_notify)(struct phy_device *dev); + struct device_driver driver; }; #define to_phy_driver(d) container_of(d, struct phy_driver, driver) -- cgit v1.2.3 From 03187c72db60e20354aca6802bc5cc3e42c1d6e1 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 8 Jun 2014 22:47:10 +0800 Subject: regulator: ab8500: Remove ab8500_regulator_debug_init/exit() CONFIG_REGULATOR_AB8500_DEBUG is always not defined. ab8500_regulator_debug_init() is not called at all now, ab8500_regulator_debug_exit() simply return 0, thus remove them. Signed-off-by: Axel Lin Reviewed-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/regulator/ab8500.c | 13 ------------- include/linux/regulator/ab8500.h | 14 -------------- 2 files changed, 27 deletions(-) (limited to 'include') diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c index 88da8626a57a..1fda14e12ea8 100644 --- a/drivers/regulator/ab8500.c +++ b/drivers/regulator/ab8500.c @@ -3071,21 +3071,8 @@ static int ab8500_regulator_probe(struct platform_device *pdev) return 0; } -static int ab8500_regulator_remove(struct platform_device *pdev) -{ - int err; - - /* remove regulator debug */ - err = ab8500_regulator_debug_exit(pdev); - if (err) - return err; - - return 0; -} - static struct platform_driver ab8500_regulator_driver = { .probe = ab8500_regulator_probe, - .remove = ab8500_regulator_remove, .driver = { .name = "ab8500-regulator", .owner = THIS_MODULE, diff --git a/include/linux/regulator/ab8500.h b/include/linux/regulator/ab8500.h index 75307447cef9..d8ecefaf63ca 100644 --- a/include/linux/regulator/ab8500.h +++ b/include/linux/regulator/ab8500.h @@ -322,18 +322,4 @@ struct ab8500_regulator_platform_data { struct regulator_init_data *ext_regulator; }; -#ifdef CONFIG_REGULATOR_AB8500_DEBUG -int ab8500_regulator_debug_init(struct platform_device *pdev); -int ab8500_regulator_debug_exit(struct platform_device *pdev); -#else -static inline int ab8500_regulator_debug_init(struct platform_device *pdev) -{ - return 0; -} -static inline int ab8500_regulator_debug_exit(struct platform_device *pdev) -{ - return 0; -} -#endif - #endif -- cgit v1.2.3 From e567bf7112518824830978d644dfb5a991e67d54 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Sun, 22 Jun 2014 16:32:48 -0600 Subject: Revert "block: add __init to elv_register" This reverts commit b5097e956a4d2919ee248d6481e4204c5568ed5c. The original commit is buggy, we do use the registration functions at runtime, for instance when loading IO schedulers through sysfs. Reported-by: Damien Wyart --- block/elevator.c | 2 +- include/linux/elevator.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/block/elevator.c b/block/elevator.c index 34bded18910e..24c28b659bb3 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -825,7 +825,7 @@ void elv_unregister_queue(struct request_queue *q) } EXPORT_SYMBOL(elv_unregister_queue); -int __init elv_register(struct elevator_type *e) +int elv_register(struct elevator_type *e) { char *def = ""; diff --git a/include/linux/elevator.h b/include/linux/elevator.h index e2a6bd7fb133..45a91474487d 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h @@ -143,7 +143,7 @@ extern void elv_drain_elevator(struct request_queue *); * io scheduler registration */ extern void __init load_default_elevator_module(void); -extern int __init elv_register(struct elevator_type *); +extern int elv_register(struct elevator_type *); extern void elv_unregister(struct elevator_type *); /* -- cgit v1.2.3 From 0da6bc8cc3417a5e452efb886ff2c61e72b743d6 Mon Sep 17 00:00:00 2001 From: Varka Bhadram Date: Fri, 20 Jun 2014 17:47:15 +0530 Subject: ieee802154: cc2520: adds driver for TI CC2520 radio This patch adds the driver support for the cc2520 radio. Driver support: - Tx and Rx of IEEE-802.15.4 packets - Energy Detection on channel - Setting the Channel for the radio. [b/w 11 - 26 channels] - Start and Stop the radio - h/w address filtering Signed-off-by: Varka Bhadram Signed-off-by: David S. Miller --- drivers/net/ieee802154/cc2520.c | 1039 +++++++++++++++++++++++++++++++++++++++ include/linux/spi/cc2520.h | 26 + 2 files changed, 1065 insertions(+) create mode 100644 drivers/net/ieee802154/cc2520.c create mode 100644 include/linux/spi/cc2520.h (limited to 'include') diff --git a/drivers/net/ieee802154/cc2520.c b/drivers/net/ieee802154/cc2520.c new file mode 100644 index 000000000000..8a5ac7ab2300 --- /dev/null +++ b/drivers/net/ieee802154/cc2520.c @@ -0,0 +1,1039 @@ +/* Driver for TI CC2520 802.15.4 Wireless-PAN Networking controller + * + * Copyright (C) 2014 Varka Bhadram + * Md.Jamal Mohiuddin + * P Sowjanya + * + * 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 + +#define SPI_COMMAND_BUFFER 3 +#define HIGH 1 +#define LOW 0 +#define STATE_IDLE 0 +#define RSSI_VALID 0 +#define RSSI_OFFSET 78 + +#define CC2520_RAM_SIZE 640 +#define CC2520_FIFO_SIZE 128 + +#define CC2520RAM_TXFIFO 0x100 +#define CC2520RAM_RXFIFO 0x180 +#define CC2520RAM_IEEEADDR 0x3EA +#define CC2520RAM_PANID 0x3F2 +#define CC2520RAM_SHORTADDR 0x3F4 + +#define CC2520_FREG_MASK 0x3F + +/* status byte values */ +#define CC2520_STATUS_XOSC32M_STABLE (1 << 7) +#define CC2520_STATUS_RSSI_VALID (1 << 6) +#define CC2520_STATUS_TX_UNDERFLOW (1 << 3) + +/* IEEE-802.15.4 defined constants (2.4 GHz logical channels) */ +#define CC2520_MINCHANNEL 11 +#define CC2520_MAXCHANNEL 26 +#define CC2520_CHANNEL_SPACING 5 + +/* command strobes */ +#define CC2520_CMD_SNOP 0x00 +#define CC2520_CMD_IBUFLD 0x02 +#define CC2520_CMD_SIBUFEX 0x03 +#define CC2520_CMD_SSAMPLECCA 0x04 +#define CC2520_CMD_SRES 0x0f +#define CC2520_CMD_MEMORY_MASK 0x0f +#define CC2520_CMD_MEMORY_READ 0x10 +#define CC2520_CMD_MEMORY_WRITE 0x20 +#define CC2520_CMD_RXBUF 0x30 +#define CC2520_CMD_RXBUFCP 0x38 +#define CC2520_CMD_RXBUFMOV 0x32 +#define CC2520_CMD_TXBUF 0x3A +#define CC2520_CMD_TXBUFCP 0x3E +#define CC2520_CMD_RANDOM 0x3C +#define CC2520_CMD_SXOSCON 0x40 +#define CC2520_CMD_STXCAL 0x41 +#define CC2520_CMD_SRXON 0x42 +#define CC2520_CMD_STXON 0x43 +#define CC2520_CMD_STXONCCA 0x44 +#define CC2520_CMD_SRFOFF 0x45 +#define CC2520_CMD_SXOSCOFF 0x46 +#define CC2520_CMD_SFLUSHRX 0x47 +#define CC2520_CMD_SFLUSHTX 0x48 +#define CC2520_CMD_SACK 0x49 +#define CC2520_CMD_SACKPEND 0x4A +#define CC2520_CMD_SNACK 0x4B +#define CC2520_CMD_SRXMASKBITSET 0x4C +#define CC2520_CMD_SRXMASKBITCLR 0x4D +#define CC2520_CMD_RXMASKAND 0x4E +#define CC2520_CMD_RXMASKOR 0x4F +#define CC2520_CMD_MEMCP 0x50 +#define CC2520_CMD_MEMCPR 0x52 +#define CC2520_CMD_MEMXCP 0x54 +#define CC2520_CMD_MEMXWR 0x56 +#define CC2520_CMD_BCLR 0x58 +#define CC2520_CMD_BSET 0x59 +#define CC2520_CMD_CTR_UCTR 0x60 +#define CC2520_CMD_CBCMAC 0x64 +#define CC2520_CMD_UCBCMAC 0x66 +#define CC2520_CMD_CCM 0x68 +#define CC2520_CMD_UCCM 0x6A +#define CC2520_CMD_ECB 0x70 +#define CC2520_CMD_ECBO 0x72 +#define CC2520_CMD_ECBX 0x74 +#define CC2520_CMD_INC 0x78 +#define CC2520_CMD_ABORT 0x7F +#define CC2520_CMD_REGISTER_READ 0x80 +#define CC2520_CMD_REGISTER_WRITE 0xC0 + +/* status registers */ +#define CC2520_CHIPID 0x40 +#define CC2520_VERSION 0x42 +#define CC2520_EXTCLOCK 0x44 +#define CC2520_MDMCTRL0 0x46 +#define CC2520_MDMCTRL1 0x47 +#define CC2520_FREQEST 0x48 +#define CC2520_RXCTRL 0x4A +#define CC2520_FSCTRL 0x4C +#define CC2520_FSCAL0 0x4E +#define CC2520_FSCAL1 0x4F +#define CC2520_FSCAL2 0x50 +#define CC2520_FSCAL3 0x51 +#define CC2520_AGCCTRL0 0x52 +#define CC2520_AGCCTRL1 0x53 +#define CC2520_AGCCTRL2 0x54 +#define CC2520_AGCCTRL3 0x55 +#define CC2520_ADCTEST0 0x56 +#define CC2520_ADCTEST1 0x57 +#define CC2520_ADCTEST2 0x58 +#define CC2520_MDMTEST0 0x5A +#define CC2520_MDMTEST1 0x5B +#define CC2520_DACTEST0 0x5C +#define CC2520_DACTEST1 0x5D +#define CC2520_ATEST 0x5E +#define CC2520_DACTEST2 0x5F +#define CC2520_PTEST0 0x60 +#define CC2520_PTEST1 0x61 +#define CC2520_RESERVED 0x62 +#define CC2520_DPUBIST 0x7A +#define CC2520_ACTBIST 0x7C +#define CC2520_RAMBIST 0x7E + +/* frame registers */ +#define CC2520_FRMFILT0 0x00 +#define CC2520_FRMFILT1 0x01 +#define CC2520_SRCMATCH 0x02 +#define CC2520_SRCSHORTEN0 0x04 +#define CC2520_SRCSHORTEN1 0x05 +#define CC2520_SRCSHORTEN2 0x06 +#define CC2520_SRCEXTEN0 0x08 +#define CC2520_SRCEXTEN1 0x09 +#define CC2520_SRCEXTEN2 0x0A +#define CC2520_FRMCTRL0 0x0C +#define CC2520_FRMCTRL1 0x0D +#define CC2520_RXENABLE0 0x0E +#define CC2520_RXENABLE1 0x0F +#define CC2520_EXCFLAG0 0x10 +#define CC2520_EXCFLAG1 0x11 +#define CC2520_EXCFLAG2 0x12 +#define CC2520_EXCMASKA0 0x14 +#define CC2520_EXCMASKA1 0x15 +#define CC2520_EXCMASKA2 0x16 +#define CC2520_EXCMASKB0 0x18 +#define CC2520_EXCMASKB1 0x19 +#define CC2520_EXCMASKB2 0x1A +#define CC2520_EXCBINDX0 0x1C +#define CC2520_EXCBINDX1 0x1D +#define CC2520_EXCBINDY0 0x1E +#define CC2520_EXCBINDY1 0x1F +#define CC2520_GPIOCTRL0 0x20 +#define CC2520_GPIOCTRL1 0x21 +#define CC2520_GPIOCTRL2 0x22 +#define CC2520_GPIOCTRL3 0x23 +#define CC2520_GPIOCTRL4 0x24 +#define CC2520_GPIOCTRL5 0x25 +#define CC2520_GPIOPOLARITY 0x26 +#define CC2520_GPIOCTRL 0x28 +#define CC2520_DPUCON 0x2A +#define CC2520_DPUSTAT 0x2C +#define CC2520_FREQCTRL 0x2E +#define CC2520_FREQTUNE 0x2F +#define CC2520_TXPOWER 0x30 +#define CC2520_TXCTRL 0x31 +#define CC2520_FSMSTAT0 0x32 +#define CC2520_FSMSTAT1 0x33 +#define CC2520_FIFOPCTRL 0x34 +#define CC2520_FSMCTRL 0x35 +#define CC2520_CCACTRL0 0x36 +#define CC2520_CCACTRL1 0x37 +#define CC2520_RSSI 0x38 +#define CC2520_RSSISTAT 0x39 +#define CC2520_RXFIRST 0x3C +#define CC2520_RXFIFOCNT 0x3E +#define CC2520_TXFIFOCNT 0x3F + +/* Driver private information */ +struct cc2520_private { + struct spi_device *spi; /* SPI device structure */ + struct ieee802154_dev *dev; /* IEEE-802.15.4 device */ + u8 *buf; /* SPI TX/Rx data buffer */ + struct mutex buffer_mutex; /* SPI buffer mutex */ + bool is_tx; /* Flag for sync b/w Tx and Rx */ + int fifo_pin; /* FIFO GPIO pin number */ + struct work_struct fifop_irqwork;/* Workqueue for FIFOP */ + spinlock_t lock; /* Lock for is_tx*/ + struct completion tx_complete; /* Work completion for Tx */ +}; + +/* Generic Functions */ +static int +cc2520_cmd_strobe(struct cc2520_private *priv, u8 cmd) +{ + int ret; + u8 status = 0xff; + struct spi_message msg; + struct spi_transfer xfer = { + .len = 0, + .tx_buf = priv->buf, + .rx_buf = priv->buf, + }; + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + mutex_lock(&priv->buffer_mutex); + priv->buf[xfer.len++] = cmd; + dev_vdbg(&priv->spi->dev, + "command strobe buf[0] = %02x\n", + priv->buf[0]); + + ret = spi_sync(priv->spi, &msg); + if (!ret) + status = priv->buf[0]; + dev_vdbg(&priv->spi->dev, + "buf[0] = %02x\n", priv->buf[0]); + mutex_unlock(&priv->buffer_mutex); + + return ret; +} + +static int +cc2520_get_status(struct cc2520_private *priv, u8 *status) +{ + int ret; + struct spi_message msg; + struct spi_transfer xfer = { + .len = 0, + .tx_buf = priv->buf, + .rx_buf = priv->buf, + }; + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + mutex_lock(&priv->buffer_mutex); + priv->buf[xfer.len++] = CC2520_CMD_SNOP; + dev_vdbg(&priv->spi->dev, + "get status command buf[0] = %02x\n", priv->buf[0]); + + ret = spi_sync(priv->spi, &msg); + if (!ret) + *status = priv->buf[0]; + dev_vdbg(&priv->spi->dev, + "buf[0] = %02x\n", priv->buf[0]); + mutex_unlock(&priv->buffer_mutex); + + return ret; +} + +static int +cc2520_write_register(struct cc2520_private *priv, u8 reg, u8 value) +{ + int status; + struct spi_message msg; + struct spi_transfer xfer = { + .len = 0, + .tx_buf = priv->buf, + .rx_buf = priv->buf, + }; + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + mutex_lock(&priv->buffer_mutex); + + if (reg <= CC2520_FREG_MASK) { + priv->buf[xfer.len++] = CC2520_CMD_REGISTER_WRITE | reg; + priv->buf[xfer.len++] = value; + } else { + priv->buf[xfer.len++] = CC2520_CMD_MEMORY_WRITE; + priv->buf[xfer.len++] = reg; + priv->buf[xfer.len++] = value; + } + status = spi_sync(priv->spi, &msg); + if (msg.status) + status = msg.status; + + mutex_unlock(&priv->buffer_mutex); + + return status; +} + +static int +cc2520_write_ram(struct cc2520_private *priv, u16 reg, u8 len, u8 *data) +{ + int status; + struct spi_message msg; + struct spi_transfer xfer_head = { + .len = 0, + .tx_buf = priv->buf, + .rx_buf = priv->buf, + }; + + struct spi_transfer xfer_buf = { + .len = len, + .tx_buf = data, + }; + + mutex_lock(&priv->buffer_mutex); + priv->buf[xfer_head.len++] = (CC2520_CMD_MEMORY_WRITE | + ((reg >> 8) & 0xff)); + priv->buf[xfer_head.len++] = reg & 0xff; + + spi_message_init(&msg); + spi_message_add_tail(&xfer_head, &msg); + spi_message_add_tail(&xfer_buf, &msg); + + status = spi_sync(priv->spi, &msg); + dev_dbg(&priv->spi->dev, "spi status = %d\n", status); + if (msg.status) + status = msg.status; + + mutex_unlock(&priv->buffer_mutex); + return status; +} + +static int +cc2520_read_register(struct cc2520_private *priv, u8 reg, u8 *data) +{ + int status; + struct spi_message msg; + struct spi_transfer xfer1 = { + .len = 0, + .tx_buf = priv->buf, + .rx_buf = priv->buf, + }; + + struct spi_transfer xfer2 = { + .len = 1, + .rx_buf = data, + }; + + spi_message_init(&msg); + spi_message_add_tail(&xfer1, &msg); + spi_message_add_tail(&xfer2, &msg); + + mutex_lock(&priv->buffer_mutex); + priv->buf[xfer1.len++] = CC2520_CMD_MEMORY_READ; + priv->buf[xfer1.len++] = reg; + + status = spi_sync(priv->spi, &msg); + dev_dbg(&priv->spi->dev, + "spi status = %d\n", status); + if (msg.status) + status = msg.status; + + mutex_unlock(&priv->buffer_mutex); + + return status; +} + +static int +cc2520_write_txfifo(struct cc2520_private *priv, u8 *data, u8 len) +{ + int status; + + /* length byte must include FCS even + * if it is calculated in the hardware + */ + int len_byte = len + 2; + + struct spi_message msg; + + struct spi_transfer xfer_head = { + .len = 0, + .tx_buf = priv->buf, + .rx_buf = priv->buf, + }; + struct spi_transfer xfer_len = { + .len = 1, + .tx_buf = &len_byte, + }; + struct spi_transfer xfer_buf = { + .len = len, + .tx_buf = data, + }; + + spi_message_init(&msg); + spi_message_add_tail(&xfer_head, &msg); + spi_message_add_tail(&xfer_len, &msg); + spi_message_add_tail(&xfer_buf, &msg); + + mutex_lock(&priv->buffer_mutex); + priv->buf[xfer_head.len++] = CC2520_CMD_TXBUF; + dev_vdbg(&priv->spi->dev, + "TX_FIFO cmd buf[0] = %02x\n", priv->buf[0]); + + status = spi_sync(priv->spi, &msg); + dev_vdbg(&priv->spi->dev, "status = %d\n", status); + if (msg.status) + status = msg.status; + dev_vdbg(&priv->spi->dev, "status = %d\n", status); + dev_vdbg(&priv->spi->dev, "buf[0] = %02x\n", priv->buf[0]); + mutex_unlock(&priv->buffer_mutex); + + return status; +} + +static int +cc2520_read_rxfifo(struct cc2520_private *priv, u8 *data, u8 len, u8 *lqi) +{ + int status; + struct spi_message msg; + + struct spi_transfer xfer_head = { + .len = 0, + .tx_buf = priv->buf, + .rx_buf = priv->buf, + }; + struct spi_transfer xfer_buf = { + .len = len, + .rx_buf = data, + }; + + spi_message_init(&msg); + spi_message_add_tail(&xfer_head, &msg); + spi_message_add_tail(&xfer_buf, &msg); + + mutex_lock(&priv->buffer_mutex); + priv->buf[xfer_head.len++] = CC2520_CMD_RXBUF; + + dev_vdbg(&priv->spi->dev, "read rxfifo buf[0] = %02x\n", priv->buf[0]); + dev_vdbg(&priv->spi->dev, "buf[1] = %02x\n", priv->buf[1]); + + status = spi_sync(priv->spi, &msg); + dev_vdbg(&priv->spi->dev, "status = %d\n", status); + if (msg.status) + status = msg.status; + dev_vdbg(&priv->spi->dev, "status = %d\n", status); + dev_vdbg(&priv->spi->dev, + "return status buf[0] = %02x\n", priv->buf[0]); + dev_vdbg(&priv->spi->dev, "length buf[1] = %02x\n", priv->buf[1]); + + mutex_unlock(&priv->buffer_mutex); + + return status; +} + +static int cc2520_start(struct ieee802154_dev *dev) +{ + return cc2520_cmd_strobe(dev->priv, CC2520_CMD_SRXON); +} + +static void cc2520_stop(struct ieee802154_dev *dev) +{ + cc2520_cmd_strobe(dev->priv, CC2520_CMD_SRFOFF); +} + +static int +cc2520_tx(struct ieee802154_dev *dev, struct sk_buff *skb) +{ + struct cc2520_private *priv = dev->priv; + unsigned long flags; + int rc; + u8 status = 0; + + rc = cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHTX); + if (rc) + goto err_tx; + + rc = cc2520_write_txfifo(priv, skb->data, skb->len); + if (rc) + goto err_tx; + + rc = cc2520_get_status(priv, &status); + if (rc) + goto err_tx; + + if (status & CC2520_STATUS_TX_UNDERFLOW) { + dev_err(&priv->spi->dev, "cc2520 tx underflow exception\n"); + goto err_tx; + } + + spin_lock_irqsave(&priv->lock, flags); + BUG_ON(priv->is_tx); + priv->is_tx = 1; + spin_unlock_irqrestore(&priv->lock, flags); + + rc = cc2520_cmd_strobe(priv, CC2520_CMD_STXONCCA); + if (rc) + goto err; + + rc = wait_for_completion_interruptible(&priv->tx_complete); + if (rc < 0) + goto err; + + cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHTX); + cc2520_cmd_strobe(priv, CC2520_CMD_SRXON); + + return rc; +err: + spin_lock_irqsave(&priv->lock, flags); + priv->is_tx = 0; + spin_unlock_irqrestore(&priv->lock, flags); +err_tx: + return rc; +} + + +static int cc2520_rx(struct cc2520_private *priv) +{ + u8 len = 0, lqi = 0, bytes = 1; + struct sk_buff *skb; + + cc2520_read_rxfifo(priv, &len, bytes, &lqi); + + if (len < 2 || len > IEEE802154_MTU) + return -EINVAL; + + skb = alloc_skb(len, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + if (cc2520_read_rxfifo(priv, skb_put(skb, len), len, &lqi)) { + dev_dbg(&priv->spi->dev, "frame reception failed\n"); + kfree_skb(skb); + return -EINVAL; + } + + skb_trim(skb, skb->len - 2); + + ieee802154_rx_irqsafe(priv->dev, skb, lqi); + + dev_vdbg(&priv->spi->dev, "RXFIFO: %x %x\n", len, lqi); + + return 0; +} + +static int +cc2520_ed(struct ieee802154_dev *dev, u8 *level) +{ + struct cc2520_private *priv = dev->priv; + u8 status = 0xff; + u8 rssi; + int ret; + + ret = cc2520_read_register(priv , CC2520_RSSISTAT, &status); + if (ret) + return ret; + + if (status != RSSI_VALID) + return -EINVAL; + + ret = cc2520_read_register(priv , CC2520_RSSI, &rssi); + if (ret) + return ret; + + /* level = RSSI(rssi) - OFFSET [dBm] : offset is 76dBm */ + *level = rssi - RSSI_OFFSET; + + return 0; +} + +static int +cc2520_set_channel(struct ieee802154_dev *dev, int page, int channel) +{ + struct cc2520_private *priv = dev->priv; + int ret; + + might_sleep(); + dev_dbg(&priv->spi->dev, "trying to set channel\n"); + + BUG_ON(page != 0); + BUG_ON(channel < CC2520_MINCHANNEL); + BUG_ON(channel > CC2520_MAXCHANNEL); + + ret = cc2520_write_register(priv, CC2520_FREQCTRL, + 11 + 5*(channel - 11)); + + return ret; +} + +static int +cc2520_filter(struct ieee802154_dev *dev, + struct ieee802154_hw_addr_filt *filt, unsigned long changed) +{ + struct cc2520_private *priv = dev->priv; + + if (changed & IEEE802515_AFILT_PANID_CHANGED) { + u16 panid = le16_to_cpu(filt->pan_id); + + dev_vdbg(&priv->spi->dev, + "cc2520_filter called for pan id\n"); + cc2520_write_ram(priv, CC2520RAM_PANID, + sizeof(panid), (u8 *)&panid); + } + + if (changed & IEEE802515_AFILT_IEEEADDR_CHANGED) { + dev_vdbg(&priv->spi->dev, + "cc2520_filter called for IEEE addr\n"); + cc2520_write_ram(priv, CC2520RAM_IEEEADDR, + sizeof(filt->ieee_addr), + (u8 *)&filt->ieee_addr); + } + + if (changed & IEEE802515_AFILT_SADDR_CHANGED) { + u16 addr = le16_to_cpu(filt->short_addr); + + dev_vdbg(&priv->spi->dev, + "cc2520_filter called for saddr\n"); + cc2520_write_ram(priv, CC2520RAM_SHORTADDR, + sizeof(addr), (u8 *)&addr); + } + + if (changed & IEEE802515_AFILT_PANC_CHANGED) { + dev_vdbg(&priv->spi->dev, + "cc2520_filter called for panc change\n"); + if (filt->pan_coord) + cc2520_write_register(priv, CC2520_FRMFILT0, 0x02); + else + cc2520_write_register(priv, CC2520_FRMFILT0, 0x00); + } + + return 0; +} + +static struct ieee802154_ops cc2520_ops = { + .owner = THIS_MODULE, + .start = cc2520_start, + .stop = cc2520_stop, + .xmit = cc2520_tx, + .ed = cc2520_ed, + .set_channel = cc2520_set_channel, + .set_hw_addr_filt = cc2520_filter, +}; + +static int cc2520_register(struct cc2520_private *priv) +{ + int ret = -ENOMEM; + + priv->dev = ieee802154_alloc_device(sizeof(*priv), &cc2520_ops); + if (!priv->dev) + goto err_ret; + + priv->dev->priv = priv; + priv->dev->parent = &priv->spi->dev; + priv->dev->extra_tx_headroom = 0; + + /* We do support only 2.4 Ghz */ + priv->dev->phy->channels_supported[0] = 0x7FFF800; + priv->dev->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AACK; + + dev_vdbg(&priv->spi->dev, "registered cc2520\n"); + ret = ieee802154_register_device(priv->dev); + if (ret) + goto err_free_device; + + return 0; + +err_free_device: + ieee802154_free_device(priv->dev); +err_ret: + return ret; +} + +static void cc2520_fifop_irqwork(struct work_struct *work) +{ + struct cc2520_private *priv + = container_of(work, struct cc2520_private, fifop_irqwork); + + dev_dbg(&priv->spi->dev, "fifop interrupt received\n"); + + if (gpio_get_value(priv->fifo_pin)) + cc2520_rx(priv); + else + dev_dbg(&priv->spi->dev, "rxfifo overflow\n"); + + cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHRX); + cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHRX); +} + +static irqreturn_t cc2520_fifop_isr(int irq, void *data) +{ + struct cc2520_private *priv = data; + + schedule_work(&priv->fifop_irqwork); + + return IRQ_HANDLED; +} + +static irqreturn_t cc2520_sfd_isr(int irq, void *data) +{ + struct cc2520_private *priv = data; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + if (priv->is_tx) { + priv->is_tx = 0; + spin_unlock_irqrestore(&priv->lock, flags); + dev_dbg(&priv->spi->dev, "SFD for TX\n"); + complete(&priv->tx_complete); + } else { + spin_unlock_irqrestore(&priv->lock, flags); + dev_dbg(&priv->spi->dev, "SFD for RX\n"); + } + + return IRQ_HANDLED; +} + +static int cc2520_hw_init(struct cc2520_private *priv) +{ + u8 status = 0, state = 0xff; + int ret; + int timeout = 100; + + ret = cc2520_read_register(priv, CC2520_FSMSTAT1, &state); + if (ret) + goto err_ret; + + if (state != STATE_IDLE) + return -EINVAL; + + do { + ret = cc2520_get_status(priv, &status); + if (ret) + goto err_ret; + + if (timeout-- <= 0) { + dev_err(&priv->spi->dev, "oscillator start failed!\n"); + return ret; + } + udelay(1); + } while (!(status & CC2520_STATUS_XOSC32M_STABLE)); + + dev_vdbg(&priv->spi->dev, "oscillator brought up\n"); + + /* Registers default value: section 28.1 in Datasheet */ + ret = cc2520_write_register(priv, CC2520_TXPOWER, 0xF7); + if (ret) + goto err_ret; + + ret = cc2520_write_register(priv, CC2520_CCACTRL0, 0x1A); + if (ret) + goto err_ret; + + ret = cc2520_write_register(priv, CC2520_MDMCTRL0, 0x85); + if (ret) + goto err_ret; + + ret = cc2520_write_register(priv, CC2520_MDMCTRL1, 0x14); + if (ret) + goto err_ret; + + ret = cc2520_write_register(priv, CC2520_RXCTRL, 0x3f); + if (ret) + goto err_ret; + + ret = cc2520_write_register(priv, CC2520_FSCTRL, 0x5a); + if (ret) + goto err_ret; + + ret = cc2520_write_register(priv, CC2520_FSCAL1, 0x2b); + if (ret) + goto err_ret; + + ret = cc2520_write_register(priv, CC2520_AGCCTRL1, 0x11); + if (ret) + goto err_ret; + + ret = cc2520_write_register(priv, CC2520_ADCTEST0, 0x10); + if (ret) + goto err_ret; + + ret = cc2520_write_register(priv, CC2520_ADCTEST1, 0x0e); + if (ret) + goto err_ret; + + ret = cc2520_write_register(priv, CC2520_ADCTEST2, 0x03); + if (ret) + goto err_ret; + + ret = cc2520_write_register(priv, CC2520_FRMCTRL0, 0x60); + if (ret) + goto err_ret; + + ret = cc2520_write_register(priv, CC2520_FRMCTRL1, 0x03); + if (ret) + goto err_ret; + + ret = cc2520_write_register(priv, CC2520_FRMFILT0, 0x00); + if (ret) + goto err_ret; + + ret = cc2520_write_register(priv, CC2520_FIFOPCTRL, 127); + if (ret) + goto err_ret; + + return 0; + +err_ret: + return ret; +} + +static struct cc2520_platform_data * +cc2520_get_platform_data(struct spi_device *spi) +{ + struct cc2520_platform_data *pdata; + struct device_node *np = spi->dev.of_node; + struct cc2520_private *priv = spi_get_drvdata(spi); + + if (!np) + return spi->dev.platform_data; + + pdata = devm_kzalloc(&spi->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + goto done; + + pdata->fifo = of_get_named_gpio(np, "fifo-gpio", 0); + priv->fifo_pin = pdata->fifo; + + pdata->fifop = of_get_named_gpio(np, "fifop-gpio", 0); + + pdata->sfd = of_get_named_gpio(np, "sfd-gpio", 0); + pdata->cca = of_get_named_gpio(np, "cca-gpio", 0); + pdata->vreg = of_get_named_gpio(np, "vreg-gpio", 0); + pdata->reset = of_get_named_gpio(np, "reset-gpio", 0); + + spi->dev.platform_data = pdata; + +done: + return pdata; +} + +static int cc2520_probe(struct spi_device *spi) +{ + struct cc2520_private *priv; + struct pinctrl *pinctrl; + struct cc2520_platform_data *pdata; + int ret; + + priv = devm_kzalloc(&spi->dev, + sizeof(struct cc2520_private), GFP_KERNEL); + if (!priv) { + ret = -ENOMEM; + goto err_ret; + } + + spi_set_drvdata(spi, priv); + + pinctrl = devm_pinctrl_get_select_default(&spi->dev); + if (IS_ERR(pinctrl)) + dev_warn(&spi->dev, + "pinctrl pins are not configured"); + + pdata = cc2520_get_platform_data(spi); + if (!pdata) { + dev_err(&spi->dev, "no platform data\n"); + return -EINVAL; + } + + priv->spi = spi; + + priv->buf = devm_kzalloc(&spi->dev, + SPI_COMMAND_BUFFER, GFP_KERNEL); + if (!priv->buf) { + ret = -ENOMEM; + goto err_ret; + } + + mutex_init(&priv->buffer_mutex); + INIT_WORK(&priv->fifop_irqwork, cc2520_fifop_irqwork); + spin_lock_init(&priv->lock); + init_completion(&priv->tx_complete); + + /* Request all the gpio's */ + if (!gpio_is_valid(pdata->fifo)) { + dev_err(&spi->dev, "fifo gpio is not valid\n"); + ret = -EINVAL; + goto err_hw_init; + } + + ret = devm_gpio_request_one(&spi->dev, pdata->fifo, + GPIOF_IN, "fifo"); + if (ret) + goto err_hw_init; + + if (!gpio_is_valid(pdata->cca)) { + dev_err(&spi->dev, "cca gpio is not valid\n"); + ret = -EINVAL; + goto err_hw_init; + } + + ret = devm_gpio_request_one(&spi->dev, pdata->cca, + GPIOF_IN, "cca"); + if (ret) + goto err_hw_init; + + if (!gpio_is_valid(pdata->fifop)) { + dev_err(&spi->dev, "fifop gpio is not valid\n"); + ret = -EINVAL; + goto err_hw_init; + } + + ret = devm_gpio_request_one(&spi->dev, pdata->fifop, + GPIOF_IN, "fifop"); + if (ret) + goto err_hw_init; + + if (!gpio_is_valid(pdata->sfd)) { + dev_err(&spi->dev, "sfd gpio is not valid\n"); + ret = -EINVAL; + goto err_hw_init; + } + + ret = devm_gpio_request_one(&spi->dev, pdata->sfd, + GPIOF_IN, "sfd"); + if (ret) + goto err_hw_init; + + if (!gpio_is_valid(pdata->reset)) { + dev_err(&spi->dev, "reset gpio is not valid\n"); + ret = -EINVAL; + goto err_hw_init; + } + + ret = devm_gpio_request_one(&spi->dev, pdata->reset, + GPIOF_OUT_INIT_LOW, "reset"); + if (ret) + goto err_hw_init; + + if (!gpio_is_valid(pdata->vreg)) { + dev_err(&spi->dev, "vreg gpio is not valid\n"); + ret = -EINVAL; + goto err_hw_init; + } + + ret = devm_gpio_request_one(&spi->dev, pdata->vreg, + GPIOF_OUT_INIT_LOW, "vreg"); + if (ret) + goto err_hw_init; + + + gpio_set_value(pdata->vreg, HIGH); + usleep_range(100, 150); + + gpio_set_value(pdata->reset, HIGH); + usleep_range(200, 250); + + ret = cc2520_hw_init(priv); + if (ret) + goto err_hw_init; + + /* Set up fifop interrupt */ + ret = devm_request_irq(&spi->dev, + gpio_to_irq(pdata->fifop), + cc2520_fifop_isr, + IRQF_TRIGGER_RISING, + dev_name(&spi->dev), + priv); + if (ret) { + dev_err(&spi->dev, "could not get fifop irq\n"); + goto err_hw_init; + } + + /* Set up sfd interrupt */ + ret = devm_request_irq(&spi->dev, + gpio_to_irq(pdata->sfd), + cc2520_sfd_isr, + IRQF_TRIGGER_FALLING, + dev_name(&spi->dev), + priv); + if (ret) { + dev_err(&spi->dev, "could not get sfd irq\n"); + goto err_hw_init; + } + + ret = cc2520_register(priv); + if (ret) + goto err_hw_init; + + return 0; + +err_hw_init: + mutex_destroy(&priv->buffer_mutex); + flush_work(&priv->fifop_irqwork); + +err_ret: + return ret; +} + +static int cc2520_remove(struct spi_device *spi) +{ + struct cc2520_private *priv = spi_get_drvdata(spi); + + mutex_destroy(&priv->buffer_mutex); + flush_work(&priv->fifop_irqwork); + + ieee802154_unregister_device(priv->dev); + ieee802154_free_device(priv->dev); + + return 0; +} + +static const struct spi_device_id cc2520_ids[] = { + {"cc2520", }, + {}, +}; +MODULE_DEVICE_TABLE(spi, cc2520_ids); + +static const struct of_device_id cc2520_of_ids[] = { + {.compatible = "ti,cc2520", }, + {}, +}; +MODULE_DEVICE_TABLE(of, cc2520_of_ids); + +/* SPI driver structure */ +static struct spi_driver cc2520_driver = { + .driver = { + .name = "cc2520", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(cc2520_of_ids), + }, + .id_table = cc2520_ids, + .probe = cc2520_probe, + .remove = cc2520_remove, +}; +module_spi_driver(cc2520_driver); + +MODULE_AUTHOR("Varka Bhadram "); +MODULE_DESCRIPTION("CC2520 Transceiver Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/spi/cc2520.h b/include/linux/spi/cc2520.h new file mode 100644 index 000000000000..85b8ee67e937 --- /dev/null +++ b/include/linux/spi/cc2520.h @@ -0,0 +1,26 @@ +/* Header file for cc2520 radio driver + * + * Copyright (C) 2014 Varka Bhadram + * Md.Jamal Mohiuddin + * P Sowjanya + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#ifndef __CC2520_H +#define __CC2520_H + +struct cc2520_platform_data { + int fifo; + int fifop; + int cca; + int sfd; + int reset; + int vreg; +}; + +#endif -- cgit v1.2.3 From b7ffbd7ef6751f6cde73082346e365738daf00d2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 4 Jun 2014 17:31:56 +0200 Subject: cfg80211: make ethtool the driver's responsibility Currently, cfg80211 tries to implement ethtool, but that doesn't really scale well, with all the different operations. Make the lower-level driver responsible for it, which currently only has an effect on mac80211. It will similarly not scale well at that level though, since mac80211 also has many drivers. To cleanly implement this in mac80211, introduce a new file and move some code to appropriate places. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 29 +--- net/mac80211/Makefile | 1 + net/mac80211/cfg.c | 340 --------------------------------------------- net/mac80211/ethtool.c | 244 ++++++++++++++++++++++++++++++++ net/mac80211/ieee80211_i.h | 2 + net/mac80211/iface.c | 2 + net/mac80211/sta_info.c | 134 ++++++++++++++++++ net/mac80211/sta_info.h | 2 + net/wireless/core.c | 3 - net/wireless/ethtool.c | 86 +----------- net/wireless/ethtool.h | 6 - net/wireless/rdev-ops.h | 48 ------- net/wireless/trace.h | 49 ------- 13 files changed, 391 insertions(+), 555 deletions(-) create mode 100644 net/mac80211/ethtool.c delete mode 100644 net/wireless/ethtool.h (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index e46c437944f7..29cb4b2bee5a 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2266,10 +2266,6 @@ struct cfg80211_qos_map { * * @get_antenna: Get current antenna configuration from device (tx_ant, rx_ant). * - * @set_ringparam: Set tx and rx ring sizes. - * - * @get_ringparam: Get tx and rx ring current and maximum sizes. - * * @tdls_mgmt: Transmit a TDLS management frame. * @tdls_oper: Perform a high-level TDLS operation (e.g. TDLS link setup). * @@ -2278,16 +2274,6 @@ struct cfg80211_qos_map { * * @set_noack_map: Set the NoAck Map for the TIDs. * - * @get_et_sset_count: Ethtool API to get string-set count. - * See @ethtool_ops.get_sset_count - * - * @get_et_stats: Ethtool API to get a set of u64 stats. - * See @ethtool_ops.get_ethtool_stats - * - * @get_et_strings: Ethtool API to get a set of strings to describe stats - * and perhaps other supported types of ethtool data-sets. - * See @ethtool_ops.get_strings - * * @get_channel: Get the current operating channel for the virtual interface. * For monitor interfaces, it should return %NULL unless there's a single * current monitoring channel. @@ -2503,10 +2489,6 @@ struct cfg80211_ops { int (*set_antenna)(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant); int (*get_antenna)(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant); - int (*set_ringparam)(struct wiphy *wiphy, u32 tx, u32 rx); - void (*get_ringparam)(struct wiphy *wiphy, - u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max); - int (*sched_scan_start)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_sched_scan_request *request); @@ -2529,13 +2511,6 @@ struct cfg80211_ops { struct net_device *dev, u16 noack_map); - 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, - struct ethtool_stats *stats, u64 *data); - void (*get_et_strings)(struct wiphy *wiphy, struct net_device *dev, - u32 sset, u8 *data); - int (*get_channel)(struct wiphy *wiphy, struct wireless_dev *wdev, struct cfg80211_chan_def *chandef); @@ -4843,6 +4818,10 @@ void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev, */ void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy); + +/* ethtool helper */ +void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info); + /* Logging, debugging and troubleshooting/diagnostic helpers. */ /* wiphy_printk helpers, similar to dev_printk */ diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index 4409bf506594..7273d2796dd1 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -17,6 +17,7 @@ mac80211-y := \ aes_ccm.o \ aes_cmac.o \ cfg.o \ + ethtool.o \ rx.o \ spectmgmt.o \ tx.o \ diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index f8d065480ba9..b6d73c14e1ae 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -468,326 +468,6 @@ void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo) rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH; } -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 rate_control_ref *ref = local->rate_ctrl; - struct timespec uptime; - u64 packets = 0; - u32 thr = 0; - int i, ac; - - sinfo->generation = sdata->local->sta_generation; - - sinfo->filled = STATION_INFO_INACTIVE_TIME | - STATION_INFO_RX_BYTES64 | - STATION_INFO_TX_BYTES64 | - STATION_INFO_RX_PACKETS | - STATION_INFO_TX_PACKETS | - STATION_INFO_TX_RETRIES | - STATION_INFO_TX_FAILED | - STATION_INFO_TX_BITRATE | - STATION_INFO_RX_BITRATE | - STATION_INFO_RX_DROP_MISC | - STATION_INFO_BSS_PARAM | - STATION_INFO_CONNECTED_TIME | - STATION_INFO_STA_FLAGS | - STATION_INFO_BEACON_LOSS_COUNT; - - do_posix_clock_monotonic_gettime(&uptime); - sinfo->connected_time = uptime.tv_sec - sta->last_connected; - - sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx); - sinfo->tx_bytes = 0; - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { - sinfo->tx_bytes += sta->tx_bytes[ac]; - packets += sta->tx_packets[ac]; - } - sinfo->tx_packets = packets; - sinfo->rx_bytes = sta->rx_bytes; - sinfo->rx_packets = sta->rx_packets; - sinfo->tx_retries = sta->tx_retry_count; - sinfo->tx_failed = sta->tx_retry_failed; - sinfo->rx_dropped_misc = sta->rx_dropped; - sinfo->beacon_loss_count = sta->beacon_loss_count; - - 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; - 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); - } - if (sta->chains) { - sinfo->filled |= STATION_INFO_CHAIN_SIGNAL | - STATION_INFO_CHAIN_SIGNAL_AVG; - - sinfo->chains = sta->chains; - for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) { - sinfo->chain_signal[i] = sta->chain_signal_last[i]; - sinfo->chain_signal_avg[i] = - (s8) -ewma_read(&sta->chain_signal_avg[i]); - } - } - - sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate); - sta_set_rate_info_rx(sta, &sinfo->rxrate); - - if (ieee80211_vif_is_mesh(&sdata->vif)) { -#ifdef CONFIG_MAC80211_MESH - sinfo->filled |= STATION_INFO_LLID | - STATION_INFO_PLID | - STATION_INFO_PLINK_STATE | - STATION_INFO_LOCAL_PM | - STATION_INFO_PEER_PM | - STATION_INFO_NONPEER_PM; - - sinfo->llid = sta->llid; - sinfo->plid = sta->plid; - sinfo->plink_state = sta->plink_state; - if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) { - sinfo->filled |= STATION_INFO_T_OFFSET; - sinfo->t_offset = sta->t_offset; - } - sinfo->local_pm = sta->local_pm; - sinfo->peer_pm = sta->peer_pm; - sinfo->nonpeer_pm = sta->nonpeer_pm; -#endif - } - - sinfo->bss_param.flags = 0; - if (sdata->vif.bss_conf.use_cts_prot) - sinfo->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT; - if (sdata->vif.bss_conf.use_short_preamble) - sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE; - if (sdata->vif.bss_conf.use_short_slot) - sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME; - sinfo->bss_param.dtim_period = sdata->local->hw.conf.ps_dtim_period; - sinfo->bss_param.beacon_interval = sdata->vif.bss_conf.beacon_int; - - sinfo->sta_flags.set = 0; - sinfo->sta_flags.mask = BIT(NL80211_STA_FLAG_AUTHORIZED) | - BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) | - BIT(NL80211_STA_FLAG_WME) | - BIT(NL80211_STA_FLAG_MFP) | - BIT(NL80211_STA_FLAG_AUTHENTICATED) | - BIT(NL80211_STA_FLAG_ASSOCIATED) | - BIT(NL80211_STA_FLAG_TDLS_PEER); - if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) - sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHORIZED); - if (test_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE)) - sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE); - if (test_sta_flag(sta, WLAN_STA_WME)) - sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_WME); - if (test_sta_flag(sta, WLAN_STA_MFP)) - sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_MFP); - if (test_sta_flag(sta, WLAN_STA_AUTH)) - sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHENTICATED); - if (test_sta_flag(sta, WLAN_STA_ASSOC)) - sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_ASSOCIATED); - if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) - sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER); - - /* check if the driver has a SW RC implementation */ - if (ref && ref->ops->get_expected_throughput) - thr = ref->ops->get_expected_throughput(sta->rate_ctrl_priv); - else - thr = drv_get_expected_throughput(local, &sta->sta); - - if (thr != 0) { - sinfo->filled |= STATION_INFO_EXPECTED_THROUGHPUT; - sinfo->expected_throughput = thr; - } -} - -static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = { - "rx_packets", "rx_bytes", - "rx_duplicates", "rx_fragments", "rx_dropped", - "tx_packets", "tx_bytes", "tx_fragments", - "tx_filtered", "tx_retry_failed", "tx_retries", - "beacon_loss", "sta_state", "txrate", "rxrate", "signal", - "channel", "noise", "ch_time", "ch_time_busy", - "ch_time_ext_busy", "ch_time_rx", "ch_time_tx" -}; -#define STA_STATS_LEN ARRAY_SIZE(ieee80211_gstrings_sta_stats) - -static int ieee80211_get_et_sset_count(struct wiphy *wiphy, - struct net_device *dev, - int sset) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - int rv = 0; - - if (sset == ETH_SS_STATS) - rv += STA_STATS_LEN; - - rv += drv_get_et_sset_count(sdata, sset); - - if (rv == 0) - return -EOPNOTSUPP; - return rv; -} - -static void ieee80211_get_et_stats(struct wiphy *wiphy, - struct net_device *dev, - struct ethtool_stats *stats, - u64 *data) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - struct ieee80211_chanctx_conf *chanctx_conf; - struct ieee80211_channel *channel; - struct sta_info *sta; - struct ieee80211_local *local = sdata->local; - struct station_info sinfo; - struct survey_info survey; - int i, q; -#define STA_STATS_SURVEY_LEN 7 - - memset(data, 0, sizeof(u64) * STA_STATS_LEN); - -#define ADD_STA_STATS(sta) \ - do { \ - data[i++] += sta->rx_packets; \ - data[i++] += sta->rx_bytes; \ - data[i++] += sta->num_duplicates; \ - data[i++] += sta->rx_fragments; \ - data[i++] += sta->rx_dropped; \ - \ - data[i++] += sinfo.tx_packets; \ - data[i++] += sinfo.tx_bytes; \ - data[i++] += sta->tx_fragments; \ - data[i++] += sta->tx_filtered_count; \ - data[i++] += sta->tx_retry_failed; \ - data[i++] += sta->tx_retry_count; \ - data[i++] += sta->beacon_loss_count; \ - } while (0) - - /* For Managed stations, find the single station based on BSSID - * and use that. For interface types, iterate through all available - * stations and add stats for any station that is assigned to this - * network device. - */ - - mutex_lock(&local->sta_mtx); - - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - sta = sta_info_get_bss(sdata, sdata->u.mgd.bssid); - - if (!(sta && !WARN_ON(sta->sdata->dev != dev))) - goto do_survey; - - sinfo.filled = 0; - sta_set_sinfo(sta, &sinfo); - - i = 0; - ADD_STA_STATS(sta); - - data[i++] = sta->sta_state; - - - if (sinfo.filled & STATION_INFO_TX_BITRATE) - data[i] = 100000 * - cfg80211_calculate_bitrate(&sinfo.txrate); - i++; - if (sinfo.filled & STATION_INFO_RX_BITRATE) - data[i] = 100000 * - cfg80211_calculate_bitrate(&sinfo.rxrate); - i++; - - if (sinfo.filled & STATION_INFO_SIGNAL_AVG) - data[i] = (u8)sinfo.signal_avg; - i++; - } else { - list_for_each_entry(sta, &local->sta_list, list) { - /* Make sure this station belongs to the proper dev */ - if (sta->sdata->dev != dev) - continue; - - sinfo.filled = 0; - sta_set_sinfo(sta, &sinfo); - i = 0; - ADD_STA_STATS(sta); - } - } - -do_survey: - i = STA_STATS_LEN - STA_STATS_SURVEY_LEN; - /* Get survey stats for current channel */ - survey.filled = 0; - - rcu_read_lock(); - chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); - if (chanctx_conf) - channel = chanctx_conf->def.chan; - else - channel = NULL; - rcu_read_unlock(); - - if (channel) { - q = 0; - do { - survey.filled = 0; - if (drv_get_survey(local, q, &survey) != 0) { - survey.filled = 0; - break; - } - q++; - } while (channel != survey.channel); - } - - if (survey.filled) - data[i++] = survey.channel->center_freq; - else - data[i++] = 0; - if (survey.filled & SURVEY_INFO_NOISE_DBM) - data[i++] = (u8)survey.noise; - else - data[i++] = -1LL; - if (survey.filled & SURVEY_INFO_CHANNEL_TIME) - data[i++] = survey.channel_time; - else - data[i++] = -1LL; - if (survey.filled & SURVEY_INFO_CHANNEL_TIME_BUSY) - data[i++] = survey.channel_time_busy; - else - data[i++] = -1LL; - if (survey.filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY) - data[i++] = survey.channel_time_ext_busy; - else - data[i++] = -1LL; - if (survey.filled & SURVEY_INFO_CHANNEL_TIME_RX) - data[i++] = survey.channel_time_rx; - else - data[i++] = -1LL; - if (survey.filled & SURVEY_INFO_CHANNEL_TIME_TX) - data[i++] = survey.channel_time_tx; - else - data[i++] = -1LL; - - mutex_unlock(&local->sta_mtx); - - if (WARN_ON(i != STA_STATS_LEN)) - return; - - drv_get_et_stats(sdata, stats, &(data[STA_STATS_LEN])); -} - -static void ieee80211_get_et_strings(struct wiphy *wiphy, - struct net_device *dev, - u32 sset, u8 *data) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - int sz_sta_stats = 0; - - if (sset == ETH_SS_STATS) { - sz_sta_stats = sizeof(ieee80211_gstrings_sta_stats); - memcpy(data, ieee80211_gstrings_sta_stats, sz_sta_stats); - } - drv_get_et_strings(sdata, sset, &(data[sz_sta_stats])); -} - static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev, int idx, u8 *mac, struct station_info *sinfo) { @@ -3597,21 +3277,6 @@ static int ieee80211_get_antenna(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant) return drv_get_antenna(local, tx_ant, rx_ant); } -static int ieee80211_set_ringparam(struct wiphy *wiphy, u32 tx, u32 rx) -{ - struct ieee80211_local *local = wiphy_priv(wiphy); - - return drv_set_ringparam(local, tx, rx); -} - -static void ieee80211_get_ringparam(struct wiphy *wiphy, - u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max) -{ - struct ieee80211_local *local = wiphy_priv(wiphy); - - drv_get_ringparam(local, tx, tx_max, rx, rx_max); -} - static int ieee80211_set_rekey_data(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_gtk_rekey_data *data) @@ -3843,8 +3508,6 @@ const struct cfg80211_ops mac80211_config_ops = { .mgmt_frame_register = ieee80211_mgmt_frame_register, .set_antenna = ieee80211_set_antenna, .get_antenna = ieee80211_get_antenna, - .set_ringparam = ieee80211_set_ringparam, - .get_ringparam = ieee80211_get_ringparam, .set_rekey_data = ieee80211_set_rekey_data, .tdls_oper = ieee80211_tdls_oper, .tdls_mgmt = ieee80211_tdls_mgmt, @@ -3853,9 +3516,6 @@ const struct cfg80211_ops mac80211_config_ops = { #ifdef CONFIG_PM .set_wakeup = ieee80211_set_wakeup, #endif - .get_et_sset_count = ieee80211_get_et_sset_count, - .get_et_stats = ieee80211_get_et_stats, - .get_et_strings = ieee80211_get_et_strings, .get_channel = ieee80211_cfg_get_channel, .start_radar_detection = ieee80211_start_radar_detection, .channel_switch = ieee80211_channel_switch, diff --git a/net/mac80211/ethtool.c b/net/mac80211/ethtool.c new file mode 100644 index 000000000000..ebfc8091557b --- /dev/null +++ b/net/mac80211/ethtool.c @@ -0,0 +1,244 @@ +/* + * mac80211 ethtool hooks for cfg80211 + * + * Copied from cfg.c - originally + * Copyright 2006-2010 Johannes Berg + * Copyright 2014 Intel Corporation (Author: Johannes Berg) + * + * This file is GPLv2 as found in COPYING. + */ +#include +#include +#include "ieee80211_i.h" +#include "sta_info.h" +#include "driver-ops.h" + +static int ieee80211_set_ringparam(struct net_device *dev, + struct ethtool_ringparam *rp) +{ + struct ieee80211_local *local = wiphy_priv(dev->ieee80211_ptr->wiphy); + + if (rp->rx_mini_pending != 0 || rp->rx_jumbo_pending != 0) + return -EINVAL; + + return drv_set_ringparam(local, rp->tx_pending, rp->rx_pending); +} + +static void ieee80211_get_ringparam(struct net_device *dev, + struct ethtool_ringparam *rp) +{ + struct ieee80211_local *local = wiphy_priv(dev->ieee80211_ptr->wiphy); + + memset(rp, 0, sizeof(*rp)); + + drv_get_ringparam(local, &rp->tx_pending, &rp->tx_max_pending, + &rp->rx_pending, &rp->rx_max_pending); +} + +static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = { + "rx_packets", "rx_bytes", + "rx_duplicates", "rx_fragments", "rx_dropped", + "tx_packets", "tx_bytes", "tx_fragments", + "tx_filtered", "tx_retry_failed", "tx_retries", + "beacon_loss", "sta_state", "txrate", "rxrate", "signal", + "channel", "noise", "ch_time", "ch_time_busy", + "ch_time_ext_busy", "ch_time_rx", "ch_time_tx" +}; +#define STA_STATS_LEN ARRAY_SIZE(ieee80211_gstrings_sta_stats) + +static int ieee80211_get_sset_count(struct net_device *dev, int sset) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + int rv = 0; + + if (sset == ETH_SS_STATS) + rv += STA_STATS_LEN; + + rv += drv_get_et_sset_count(sdata, sset); + + if (rv == 0) + return -EOPNOTSUPP; + return rv; +} + +static void ieee80211_get_stats(struct net_device *dev, + struct ethtool_stats *stats, + u64 *data) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_channel *channel; + struct sta_info *sta; + struct ieee80211_local *local = sdata->local; + struct station_info sinfo; + struct survey_info survey; + int i, q; +#define STA_STATS_SURVEY_LEN 7 + + memset(data, 0, sizeof(u64) * STA_STATS_LEN); + +#define ADD_STA_STATS(sta) \ + do { \ + data[i++] += sta->rx_packets; \ + data[i++] += sta->rx_bytes; \ + data[i++] += sta->num_duplicates; \ + data[i++] += sta->rx_fragments; \ + data[i++] += sta->rx_dropped; \ + \ + data[i++] += sinfo.tx_packets; \ + data[i++] += sinfo.tx_bytes; \ + data[i++] += sta->tx_fragments; \ + data[i++] += sta->tx_filtered_count; \ + data[i++] += sta->tx_retry_failed; \ + data[i++] += sta->tx_retry_count; \ + data[i++] += sta->beacon_loss_count; \ + } while (0) + + /* For Managed stations, find the single station based on BSSID + * and use that. For interface types, iterate through all available + * stations and add stats for any station that is assigned to this + * network device. + */ + + mutex_lock(&local->sta_mtx); + + if (sdata->vif.type == NL80211_IFTYPE_STATION) { + sta = sta_info_get_bss(sdata, sdata->u.mgd.bssid); + + if (!(sta && !WARN_ON(sta->sdata->dev != dev))) + goto do_survey; + + sinfo.filled = 0; + sta_set_sinfo(sta, &sinfo); + + i = 0; + ADD_STA_STATS(sta); + + data[i++] = sta->sta_state; + + + if (sinfo.filled & STATION_INFO_TX_BITRATE) + data[i] = 100000 * + cfg80211_calculate_bitrate(&sinfo.txrate); + i++; + if (sinfo.filled & STATION_INFO_RX_BITRATE) + data[i] = 100000 * + cfg80211_calculate_bitrate(&sinfo.rxrate); + i++; + + if (sinfo.filled & STATION_INFO_SIGNAL_AVG) + data[i] = (u8)sinfo.signal_avg; + i++; + } else { + list_for_each_entry(sta, &local->sta_list, list) { + /* Make sure this station belongs to the proper dev */ + if (sta->sdata->dev != dev) + continue; + + sinfo.filled = 0; + sta_set_sinfo(sta, &sinfo); + i = 0; + ADD_STA_STATS(sta); + } + } + +do_survey: + i = STA_STATS_LEN - STA_STATS_SURVEY_LEN; + /* Get survey stats for current channel */ + survey.filled = 0; + + rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (chanctx_conf) + channel = chanctx_conf->def.chan; + else + channel = NULL; + rcu_read_unlock(); + + if (channel) { + q = 0; + do { + survey.filled = 0; + if (drv_get_survey(local, q, &survey) != 0) { + survey.filled = 0; + break; + } + q++; + } while (channel != survey.channel); + } + + if (survey.filled) + data[i++] = survey.channel->center_freq; + else + data[i++] = 0; + if (survey.filled & SURVEY_INFO_NOISE_DBM) + data[i++] = (u8)survey.noise; + else + data[i++] = -1LL; + if (survey.filled & SURVEY_INFO_CHANNEL_TIME) + data[i++] = survey.channel_time; + else + data[i++] = -1LL; + if (survey.filled & SURVEY_INFO_CHANNEL_TIME_BUSY) + data[i++] = survey.channel_time_busy; + else + data[i++] = -1LL; + if (survey.filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY) + data[i++] = survey.channel_time_ext_busy; + else + data[i++] = -1LL; + if (survey.filled & SURVEY_INFO_CHANNEL_TIME_RX) + data[i++] = survey.channel_time_rx; + else + data[i++] = -1LL; + if (survey.filled & SURVEY_INFO_CHANNEL_TIME_TX) + data[i++] = survey.channel_time_tx; + else + data[i++] = -1LL; + + mutex_unlock(&local->sta_mtx); + + if (WARN_ON(i != STA_STATS_LEN)) + return; + + drv_get_et_stats(sdata, stats, &(data[STA_STATS_LEN])); +} + +static void ieee80211_get_strings(struct net_device *dev, u32 sset, u8 *data) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + int sz_sta_stats = 0; + + if (sset == ETH_SS_STATS) { + sz_sta_stats = sizeof(ieee80211_gstrings_sta_stats); + memcpy(data, ieee80211_gstrings_sta_stats, sz_sta_stats); + } + drv_get_et_strings(sdata, sset, &(data[sz_sta_stats])); +} + +static int ieee80211_get_regs_len(struct net_device *dev) +{ + return 0; +} + +static void ieee80211_get_regs(struct net_device *dev, + struct ethtool_regs *regs, + void *data) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + regs->version = wdev->wiphy->hw_version; + regs->len = 0; +} + +const struct ethtool_ops ieee80211_ethtool_ops = { + .get_drvinfo = cfg80211_get_drvinfo, + .get_regs_len = ieee80211_get_regs_len, + .get_regs = ieee80211_get_regs, + .get_link = ethtool_op_get_link, + .get_ringparam = ieee80211_get_ringparam, + .set_ringparam = ieee80211_set_ringparam, + .get_strings = ieee80211_get_strings, + .get_ethtool_stats = ieee80211_get_stats, + .get_sset_count = ieee80211_get_sset_count, +}; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index ac9836e0aab3..4372d48b718f 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1847,6 +1847,8 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, enum nl80211_tdls_operation oper); +extern const struct ethtool_ops ieee80211_ethtool_ops; + #ifdef CONFIG_MAC80211_NOINLINE #define debug_noinline noinline #else diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 388b863e821c..db5afc7faa22 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1705,6 +1705,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, ndev->features |= local->hw.netdev_features; + netdev_set_default_ethtool_ops(ndev, &ieee80211_ethtool_ops); + ret = register_netdevice(ndev); if (ret) { free_netdev(ndev); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index ae7c16ad5f22..6fe48f64d0e4 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1719,3 +1719,137 @@ u8 sta_info_tx_streams(struct sta_info *sta) return ((ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK) >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1; } + +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 rate_control_ref *ref = local->rate_ctrl; + struct timespec uptime; + u64 packets = 0; + u32 thr = 0; + int i, ac; + + sinfo->generation = sdata->local->sta_generation; + + sinfo->filled = STATION_INFO_INACTIVE_TIME | + STATION_INFO_RX_BYTES64 | + STATION_INFO_TX_BYTES64 | + STATION_INFO_RX_PACKETS | + STATION_INFO_TX_PACKETS | + STATION_INFO_TX_RETRIES | + STATION_INFO_TX_FAILED | + STATION_INFO_TX_BITRATE | + STATION_INFO_RX_BITRATE | + STATION_INFO_RX_DROP_MISC | + STATION_INFO_BSS_PARAM | + STATION_INFO_CONNECTED_TIME | + STATION_INFO_STA_FLAGS | + STATION_INFO_BEACON_LOSS_COUNT; + + do_posix_clock_monotonic_gettime(&uptime); + sinfo->connected_time = uptime.tv_sec - sta->last_connected; + + sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx); + sinfo->tx_bytes = 0; + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + sinfo->tx_bytes += sta->tx_bytes[ac]; + packets += sta->tx_packets[ac]; + } + sinfo->tx_packets = packets; + sinfo->rx_bytes = sta->rx_bytes; + sinfo->rx_packets = sta->rx_packets; + sinfo->tx_retries = sta->tx_retry_count; + sinfo->tx_failed = sta->tx_retry_failed; + sinfo->rx_dropped_misc = sta->rx_dropped; + sinfo->beacon_loss_count = sta->beacon_loss_count; + + 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; + 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); + } + if (sta->chains) { + sinfo->filled |= STATION_INFO_CHAIN_SIGNAL | + STATION_INFO_CHAIN_SIGNAL_AVG; + + sinfo->chains = sta->chains; + for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) { + sinfo->chain_signal[i] = sta->chain_signal_last[i]; + sinfo->chain_signal_avg[i] = + (s8) -ewma_read(&sta->chain_signal_avg[i]); + } + } + + sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate); + sta_set_rate_info_rx(sta, &sinfo->rxrate); + + if (ieee80211_vif_is_mesh(&sdata->vif)) { +#ifdef CONFIG_MAC80211_MESH + sinfo->filled |= STATION_INFO_LLID | + STATION_INFO_PLID | + STATION_INFO_PLINK_STATE | + STATION_INFO_LOCAL_PM | + STATION_INFO_PEER_PM | + STATION_INFO_NONPEER_PM; + + sinfo->llid = sta->llid; + sinfo->plid = sta->plid; + sinfo->plink_state = sta->plink_state; + if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) { + sinfo->filled |= STATION_INFO_T_OFFSET; + sinfo->t_offset = sta->t_offset; + } + sinfo->local_pm = sta->local_pm; + sinfo->peer_pm = sta->peer_pm; + sinfo->nonpeer_pm = sta->nonpeer_pm; +#endif + } + + sinfo->bss_param.flags = 0; + if (sdata->vif.bss_conf.use_cts_prot) + sinfo->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT; + if (sdata->vif.bss_conf.use_short_preamble) + sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE; + if (sdata->vif.bss_conf.use_short_slot) + sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME; + sinfo->bss_param.dtim_period = sdata->local->hw.conf.ps_dtim_period; + sinfo->bss_param.beacon_interval = sdata->vif.bss_conf.beacon_int; + + sinfo->sta_flags.set = 0; + sinfo->sta_flags.mask = BIT(NL80211_STA_FLAG_AUTHORIZED) | + BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) | + BIT(NL80211_STA_FLAG_WME) | + BIT(NL80211_STA_FLAG_MFP) | + BIT(NL80211_STA_FLAG_AUTHENTICATED) | + BIT(NL80211_STA_FLAG_ASSOCIATED) | + BIT(NL80211_STA_FLAG_TDLS_PEER); + if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) + sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHORIZED); + if (test_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE)) + sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE); + if (test_sta_flag(sta, WLAN_STA_WME)) + sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_WME); + if (test_sta_flag(sta, WLAN_STA_MFP)) + sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_MFP); + if (test_sta_flag(sta, WLAN_STA_AUTH)) + sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHENTICATED); + if (test_sta_flag(sta, WLAN_STA_ASSOC)) + sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_ASSOCIATED); + if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) + sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER); + + /* check if the driver has a SW RC implementation */ + if (ref && ref->ops->get_expected_throughput) + thr = ref->ops->get_expected_throughput(sta->rate_ctrl_priv); + else + thr = drv_get_expected_throughput(local, &sta->sta); + + if (thr != 0) { + sinfo->filled |= STATION_INFO_EXPECTED_THROUGHPUT; + sinfo->expected_throughput = thr; + } +} diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index fa7ce6f8593b..2a04361b2162 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -627,6 +627,8 @@ void sta_set_rate_info_tx(struct sta_info *sta, struct rate_info *rinfo); void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo); +void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo); + void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, unsigned long exp_time); u8 sta_info_tx_streams(struct sta_info *sta); diff --git a/net/wireless/core.c b/net/wireless/core.c index a1c40654dd9b..afee5e0455ea 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -25,7 +25,6 @@ #include "sysfs.h" #include "debugfs.h" #include "wext-compat.h" -#include "ethtool.h" #include "rdev-ops.h" /* name for sysfs, %d is appended */ @@ -927,8 +926,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, /* allow mac80211 to determine the timeout */ wdev->ps_timeout = -1; - netdev_set_default_ethtool_ops(dev, &cfg80211_ethtool_ops); - if ((wdev->iftype == NL80211_IFTYPE_STATION || wdev->iftype == NL80211_IFTYPE_P2P_CLIENT || wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr) diff --git a/net/wireless/ethtool.c b/net/wireless/ethtool.c index d4860bfc020e..e9e91298c70d 100644 --- a/net/wireless/ethtool.c +++ b/net/wireless/ethtool.c @@ -1,11 +1,9 @@ #include #include #include "core.h" -#include "ethtool.h" #include "rdev-ops.h" -static void cfg80211_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) +void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct wireless_dev *wdev = dev->ieee80211_ptr; @@ -23,84 +21,4 @@ static void cfg80211_get_drvinfo(struct net_device *dev, strlcpy(info->bus_info, dev_name(wiphy_dev(wdev->wiphy)), sizeof(info->bus_info)); } - -static int cfg80211_get_regs_len(struct net_device *dev) -{ - /* For now, return 0... */ - return 0; -} - -static void cfg80211_get_regs(struct net_device *dev, struct ethtool_regs *regs, - void *data) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - regs->version = wdev->wiphy->hw_version; - regs->len = 0; -} - -static void cfg80211_get_ringparam(struct net_device *dev, - struct ethtool_ringparam *rp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); - - memset(rp, 0, sizeof(*rp)); - - if (rdev->ops->get_ringparam) - rdev_get_ringparam(rdev, &rp->tx_pending, &rp->tx_max_pending, - &rp->rx_pending, &rp->rx_max_pending); -} - -static int cfg80211_set_ringparam(struct net_device *dev, - struct ethtool_ringparam *rp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); - - if (rp->rx_mini_pending != 0 || rp->rx_jumbo_pending != 0) - return -EINVAL; - - if (rdev->ops->set_ringparam) - return rdev_set_ringparam(rdev, rp->tx_pending, rp->rx_pending); - - return -ENOTSUPP; -} - -static int cfg80211_get_sset_count(struct net_device *dev, int sset) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); - if (rdev->ops->get_et_sset_count) - return rdev_get_et_sset_count(rdev, dev, sset); - return -EOPNOTSUPP; -} - -static void cfg80211_get_stats(struct net_device *dev, - struct ethtool_stats *stats, u64 *data) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); - if (rdev->ops->get_et_stats) - rdev_get_et_stats(rdev, dev, stats, data); -} - -static void cfg80211_get_strings(struct net_device *dev, u32 sset, u8 *data) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); - if (rdev->ops->get_et_strings) - rdev_get_et_strings(rdev, dev, sset, data); -} - -const struct ethtool_ops cfg80211_ethtool_ops = { - .get_drvinfo = cfg80211_get_drvinfo, - .get_regs_len = cfg80211_get_regs_len, - .get_regs = cfg80211_get_regs, - .get_link = ethtool_op_get_link, - .get_ringparam = cfg80211_get_ringparam, - .set_ringparam = cfg80211_set_ringparam, - .get_strings = cfg80211_get_strings, - .get_ethtool_stats = cfg80211_get_stats, - .get_sset_count = cfg80211_get_sset_count, -}; +EXPORT_SYMBOL(cfg80211_get_drvinfo); diff --git a/net/wireless/ethtool.h b/net/wireless/ethtool.h deleted file mode 100644 index 695ecad20bd6..000000000000 --- a/net/wireless/ethtool.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __CFG80211_ETHTOOL__ -#define __CFG80211_ETHTOOL__ - -extern const struct ethtool_ops cfg80211_ethtool_ops; - -#endif /* __CFG80211_ETHTOOL__ */ diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index d95bbe348138..f552b0abbd70 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -714,25 +714,6 @@ static inline int rdev_get_antenna(struct cfg80211_registered_device *rdev, return ret; } -static inline int rdev_set_ringparam(struct cfg80211_registered_device *rdev, - u32 tx, u32 rx) -{ - int ret; - trace_rdev_set_ringparam(&rdev->wiphy, tx, rx); - ret = rdev->ops->set_ringparam(&rdev->wiphy, tx, rx); - trace_rdev_return_int(&rdev->wiphy, ret); - return ret; -} - -static inline void rdev_get_ringparam(struct cfg80211_registered_device *rdev, - u32 *tx, u32 *tx_max, u32 *rx, - u32 *rx_max) -{ - trace_rdev_get_ringparam(&rdev->wiphy); - rdev->ops->get_ringparam(&rdev->wiphy, tx, tx_max, rx, rx_max); - trace_rdev_return_void_tx_rx(&rdev->wiphy, *tx, *tx_max, *rx, *rx_max); -} - static inline int rdev_sched_scan_start(struct cfg80211_registered_device *rdev, struct net_device *dev, @@ -815,35 +796,6 @@ static inline int rdev_set_noack_map(struct cfg80211_registered_device *rdev, return ret; } -static inline int -rdev_get_et_sset_count(struct cfg80211_registered_device *rdev, - struct net_device *dev, int sset) -{ - int ret; - trace_rdev_get_et_sset_count(&rdev->wiphy, dev, sset); - ret = rdev->ops->get_et_sset_count(&rdev->wiphy, dev, sset); - trace_rdev_return_int(&rdev->wiphy, ret); - return ret; -} - -static inline void rdev_get_et_stats(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct ethtool_stats *stats, u64 *data) -{ - trace_rdev_get_et_stats(&rdev->wiphy, dev); - rdev->ops->get_et_stats(&rdev->wiphy, dev, stats, data); - trace_rdev_return_void(&rdev->wiphy); -} - -static inline void rdev_get_et_strings(struct cfg80211_registered_device *rdev, - struct net_device *dev, u32 sset, - u8 *data) -{ - trace_rdev_get_et_strings(&rdev->wiphy, dev, sset); - rdev->ops->get_et_strings(&rdev->wiphy, dev, sset, data); - trace_rdev_return_void(&rdev->wiphy); -} - static inline int rdev_get_channel(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 560ed77084e9..174559aade57 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -298,11 +298,6 @@ DEFINE_EVENT(wiphy_only_evt, rdev_return_void, TP_ARGS(wiphy) ); -DEFINE_EVENT(wiphy_only_evt, rdev_get_ringparam, - TP_PROTO(struct wiphy *wiphy), - TP_ARGS(wiphy) -); - DEFINE_EVENT(wiphy_only_evt, rdev_get_antenna, TP_PROTO(struct wiphy *wiphy), TP_ARGS(wiphy) @@ -580,11 +575,6 @@ DEFINE_EVENT(wiphy_netdev_evt, rdev_stop_ap, TP_ARGS(wiphy, netdev) ); -DEFINE_EVENT(wiphy_netdev_evt, rdev_get_et_stats, - TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), - TP_ARGS(wiphy, netdev) -); - DEFINE_EVENT(wiphy_netdev_evt, rdev_sched_scan_stop, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), TP_ARGS(wiphy, netdev) @@ -1439,11 +1429,6 @@ DECLARE_EVENT_CLASS(tx_rx_evt, WIPHY_PR_ARG, __entry->tx, __entry->rx) ); -DEFINE_EVENT(tx_rx_evt, rdev_set_ringparam, - TP_PROTO(struct wiphy *wiphy, u32 tx, u32 rx), - TP_ARGS(wiphy, rx, tx) -); - DEFINE_EVENT(tx_rx_evt, rdev_set_antenna, TP_PROTO(struct wiphy *wiphy, u32 tx, u32 rx), TP_ARGS(wiphy, rx, tx) @@ -1725,40 +1710,6 @@ TRACE_EVENT(rdev_set_noack_map, WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->noack_map) ); -TRACE_EVENT(rdev_get_et_sset_count, - TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int sset), - TP_ARGS(wiphy, netdev, sset), - TP_STRUCT__entry( - WIPHY_ENTRY - NETDEV_ENTRY - __field(int, sset) - ), - TP_fast_assign( - WIPHY_ASSIGN; - NETDEV_ASSIGN; - __entry->sset = sset; - ), - TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", sset: %d", - WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->sset) -); - -TRACE_EVENT(rdev_get_et_strings, - TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u32 sset), - TP_ARGS(wiphy, netdev, sset), - TP_STRUCT__entry( - WIPHY_ENTRY - NETDEV_ENTRY - __field(u32, sset) - ), - TP_fast_assign( - WIPHY_ASSIGN; - NETDEV_ASSIGN; - __entry->sset = sset; - ), - TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", sset: %u", - WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->sset) -); - DEFINE_EVENT(wiphy_wdev_evt, rdev_get_channel, TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev), TP_ARGS(wiphy, wdev) -- cgit v1.2.3 From cddd02489f52ccf635ed65931214729a23b93cd6 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Sun, 22 Jun 2014 01:29:15 +0200 Subject: hrtimer: Store cpu-number in struct hrtimer_cpu_base In lowres mode, hrtimers are serviced by the tick instead of a clock event. Now it works well as long as the tick stays periodic but we must also make sure that the hrtimers are serviced in dynticks mode. Part of that job consist in kicking a dynticks hrtimer target in order to make it reconsider the next tick to schedule to correctly handle the hrtimer's expiring time. And that part isn't handled by the hrtimers subsystem. To prepare for fixing this, we need __hrtimer_start_range_ns() to be able to resolve the CPU target associated to a hrtimer's object 'cpu_base' so that the kick can be centralized there. So lets store it in the 'struct hrtimer_cpu_base' to resolve the CPU without overhead. It is set once at CPU's online notification. Signed-off-by: Viresh Kumar Signed-off-by: Frederic Weisbecker Link: http://lkml.kernel.org/r/1403393357-2070-4-git-send-email-fweisbec@gmail.com Signed-off-by: Thomas Gleixner --- include/linux/hrtimer.h | 2 ++ kernel/time/hrtimer.c | 1 + 2 files changed, 3 insertions(+) (limited to 'include') diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index e7a8d3fa91d5..bb4ffff31c69 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -165,6 +165,7 @@ enum hrtimer_base_type { * struct hrtimer_cpu_base - the per cpu clock bases * @lock: lock protecting the base and associated clock bases * and timers + * @cpu: cpu number * @active_bases: Bitfield to mark bases with active timers * @clock_was_set: Indicates that clock was set from irq context. * @expires_next: absolute time of the next event which was scheduled @@ -179,6 +180,7 @@ enum hrtimer_base_type { */ struct hrtimer_cpu_base { raw_spinlock_t lock; + unsigned int cpu; unsigned int active_bases; unsigned int clock_was_set; #ifdef CONFIG_HIGH_RES_TIMERS diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c index 3ab28993f6e0..0e32d4e7583f 100644 --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c @@ -1680,6 +1680,7 @@ static void init_hrtimers_cpu(int cpu) timerqueue_init_head(&cpu_base->clock_base[i].active); } + cpu_base->cpu = cpu; hrtimer_init_hres(cpu_base); } -- cgit v1.2.3 From 2da38e0c9465b89518b29328daeb7da0ca1690b7 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 19 Jun 2014 14:41:00 +0530 Subject: ALSA: compress: fix the struct alignment to 4 bytes In 64bit systems the compiler can default align to 8bytes causing mis-match with 32bit usermode. Avoid this is future by ensuring all the structures shared with usermode are packed and aligned to 4 bytes irrespective of arch used [coding style fixes by tiwai] Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- include/uapi/sound/compress_offload.h | 14 +++++++------- include/uapi/sound/compress_params.h | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/uapi/sound/compress_offload.h b/include/uapi/sound/compress_offload.h index 21eed488783f..1964026b5e09 100644 --- a/include/uapi/sound/compress_offload.h +++ b/include/uapi/sound/compress_offload.h @@ -39,7 +39,7 @@ struct snd_compressed_buffer { __u32 fragment_size; __u32 fragments; -}; +} __attribute__((packed, aligned(4))); /** * struct snd_compr_params: compressed stream params @@ -51,7 +51,7 @@ struct snd_compr_params { struct snd_compressed_buffer buffer; struct snd_codec codec; __u8 no_wake_mode; -}; +} __attribute__((packed, aligned(4))); /** * struct snd_compr_tstamp: timestamp descriptor @@ -70,7 +70,7 @@ struct snd_compr_tstamp { __u32 pcm_frames; __u32 pcm_io_frames; __u32 sampling_rate; -}; +} __attribute__((packed, aligned(4))); /** * struct snd_compr_avail: avail descriptor @@ -80,7 +80,7 @@ struct snd_compr_tstamp { struct snd_compr_avail { __u64 avail; struct snd_compr_tstamp tstamp; -} __attribute__((packed)); +} __attribute__((packed, aligned(4))); enum snd_compr_direction { SND_COMPRESS_PLAYBACK = 0, @@ -107,7 +107,7 @@ struct snd_compr_caps { __u32 max_fragments; __u32 codecs[MAX_NUM_CODECS]; __u32 reserved[11]; -}; +} __attribute__((packed, aligned(4))); /** * struct snd_compr_codec_caps: query capability of codec @@ -119,7 +119,7 @@ struct snd_compr_codec_caps { __u32 codec; __u32 num_descriptors; struct snd_codec_desc descriptor[MAX_NUM_CODEC_DESCRIPTORS]; -}; +} __attribute__((packed, aligned(4))); /** * @SNDRV_COMPRESS_ENCODER_PADDING: no of samples appended by the encoder at the @@ -140,7 +140,7 @@ enum { struct snd_compr_metadata { __u32 key; __u32 value[8]; -}; +} __attribute__((packed, aligned(4))); /** * compress path ioctl definitions diff --git a/include/uapi/sound/compress_params.h b/include/uapi/sound/compress_params.h index 165e7059de75..d9bd9ca0d5b0 100644 --- a/include/uapi/sound/compress_params.h +++ b/include/uapi/sound/compress_params.h @@ -268,7 +268,7 @@ struct snd_enc_vorbis { __u32 max_bit_rate; __u32 min_bit_rate; __u32 downmix; -}; +} __attribute__((packed, aligned(4))); /** @@ -284,7 +284,7 @@ struct snd_enc_real { __u32 quant_bits; __u32 start_region; __u32 num_regions; -}; +} __attribute__((packed, aligned(4))); /** * struct snd_enc_flac @@ -308,12 +308,12 @@ struct snd_enc_real { struct snd_enc_flac { __u32 num; __u32 gain; -}; +} __attribute__((packed, aligned(4))); struct snd_enc_generic { __u32 bw; /* encoder bandwidth */ __s32 reserved[15]; -}; +} __attribute__((packed, aligned(4))); union snd_codec_options { struct snd_enc_wma wma; @@ -321,7 +321,7 @@ union snd_codec_options { struct snd_enc_real real; struct snd_enc_flac flac; struct snd_enc_generic generic; -}; +} __attribute__((packed, aligned(4))); /** struct snd_codec_desc - description of codec capabilities * @max_ch: Maximum number of audio channels @@ -358,7 +358,7 @@ struct snd_codec_desc { __u32 formats; __u32 min_buffer; __u32 reserved[15]; -}; +} __attribute__((packed, aligned(4))); /** struct snd_codec * @id: Identifies the supported audio encoder/decoder. @@ -399,6 +399,6 @@ struct snd_codec { __u32 align; union snd_codec_options options; __u32 reserved[3]; -}; +} __attribute__((packed, aligned(4))); #endif -- cgit v1.2.3 From 027d7c2a26ad637f14c72f401dd8da0bb6df20c8 Mon Sep 17 00:00:00 2001 From: Keerthy Date: Wed, 18 Jun 2014 15:28:54 +0530 Subject: mfd: palmas: Add tps65917 specific definitions and enums Add tps65917 specific definitions and enums. Acked-by: Lee Jones Signed-off-by: Keerthy Signed-off-by: Mark Brown --- include/linux/mfd/palmas.h | 793 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 793 insertions(+) (limited to 'include') diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h index 3420e09e2e20..f760a07ab76e 100644 --- a/include/linux/mfd/palmas.h +++ b/include/linux/mfd/palmas.h @@ -30,6 +30,8 @@ #define PALMAS_CHIP_ID 0xC035 #define PALMAS_CHIP_CHARGER_ID 0xC036 +#define TPS65917_RESERVED -1 + #define is_palmas(a) (((a) == PALMAS_CHIP_OLD_ID) || \ ((a) == PALMAS_CHIP_ID)) #define is_palmas_charger(a) ((a) == PALMAS_CHIP_CHARGER_ID) @@ -184,6 +186,27 @@ enum palmas_regulators { PALMAS_NUM_REGS, }; +enum tps65917_regulators { + /* SMPS regulators */ + TPS65917_REG_SMPS1, + TPS65917_REG_SMPS2, + TPS65917_REG_SMPS3, + TPS65917_REG_SMPS4, + TPS65917_REG_SMPS5, + /* LDO regulators */ + TPS65917_REG_LDO1, + TPS65917_REG_LDO2, + TPS65917_REG_LDO3, + TPS65917_REG_LDO4, + TPS65917_REG_LDO5, + TPS65917_REG_REGEN1, + TPS65917_REG_REGEN2, + TPS65917_REG_REGEN3, + + /* Total number of regulators */ + TPS65917_NUM_REGS, +}; + /* External controll signal name */ enum { PALMAS_EXT_CONTROL_ENABLE1 = 0x1, @@ -228,6 +251,24 @@ enum palmas_external_requestor_id { PALMAS_EXTERNAL_REQSTR_ID_MAX, }; +enum tps65917_external_requestor_id { + TPS65917_EXTERNAL_REQSTR_ID_REGEN1, + TPS65917_EXTERNAL_REQSTR_ID_REGEN2, + TPS65917_EXTERNAL_REQSTR_ID_REGEN3, + TPS65917_EXTERNAL_REQSTR_ID_SMPS1, + TPS65917_EXTERNAL_REQSTR_ID_SMPS2, + TPS65917_EXTERNAL_REQSTR_ID_SMPS3, + TPS65917_EXTERNAL_REQSTR_ID_SMPS4, + TPS65917_EXTERNAL_REQSTR_ID_SMPS5, + TPS65917_EXTERNAL_REQSTR_ID_LDO1, + TPS65917_EXTERNAL_REQSTR_ID_LDO2, + TPS65917_EXTERNAL_REQSTR_ID_LDO3, + TPS65917_EXTERNAL_REQSTR_ID_LDO4, + TPS65917_EXTERNAL_REQSTR_ID_LDO5, + /* Last entry */ + TPS65917_EXTERNAL_REQSTR_ID_MAX, +}; + struct palmas_pmic_platform_data { /* An array of pointers to regulator init data indexed by regulator * ID @@ -349,6 +390,48 @@ struct palmas_gpadc_result { #define PALMAS_MAX_CHANNELS 16 +/* Define the tps65917 IRQ numbers */ +enum tps65917_irqs { + /* INT1 registers */ + TPS65917_RESERVED1, + TPS65917_PWRON_IRQ, + TPS65917_LONG_PRESS_KEY_IRQ, + TPS65917_RESERVED2, + TPS65917_PWRDOWN_IRQ, + TPS65917_HOTDIE_IRQ, + TPS65917_VSYS_MON_IRQ, + TPS65917_RESERVED3, + /* INT2 registers */ + TPS65917_RESERVED4, + TPS65917_OTP_ERROR_IRQ, + TPS65917_WDT_IRQ, + TPS65917_RESERVED5, + TPS65917_RESET_IN_IRQ, + TPS65917_FSD_IRQ, + TPS65917_SHORT_IRQ, + TPS65917_RESERVED6, + /* INT3 registers */ + TPS65917_GPADC_AUTO_0_IRQ, + TPS65917_GPADC_AUTO_1_IRQ, + TPS65917_GPADC_EOC_SW_IRQ, + TPS65917_RESREVED6, + TPS65917_RESERVED7, + TPS65917_RESERVED8, + TPS65917_RESERVED9, + TPS65917_VBUS_IRQ, + /* INT4 registers */ + TPS65917_GPIO_0_IRQ, + TPS65917_GPIO_1_IRQ, + TPS65917_GPIO_2_IRQ, + TPS65917_GPIO_3_IRQ, + TPS65917_GPIO_4_IRQ, + TPS65917_GPIO_5_IRQ, + TPS65917_GPIO_6_IRQ, + TPS65917_RESERVED10, + /* Total Number IRQs */ + TPS65917_NUM_IRQ, +}; + /* Define the palmas IRQ numbers */ enum palmas_irqs { /* INT1 registers */ @@ -400,6 +483,7 @@ struct palmas_pmic { int smps123; int smps457; + int smps12; int range[PALMAS_REG_SMPS10_OUT1]; unsigned int ramp_delay[PALMAS_REG_SMPS10_OUT1]; @@ -2871,6 +2955,715 @@ enum usb_irq_events { #define PALMAS_GPADC_TRIM15 0x0E #define PALMAS_GPADC_TRIM16 0x0F +/* TPS65917 Interrupt registers */ + +/* Registers for function INTERRUPT */ +#define TPS65917_INT1_STATUS 0x00 +#define TPS65917_INT1_MASK 0x01 +#define TPS65917_INT1_LINE_STATE 0x02 +#define TPS65917_INT2_STATUS 0x05 +#define TPS65917_INT2_MASK 0x06 +#define TPS65917_INT2_LINE_STATE 0x07 +#define TPS65917_INT3_STATUS 0x0A +#define TPS65917_INT3_MASK 0x0B +#define TPS65917_INT3_LINE_STATE 0x0C +#define TPS65917_INT4_STATUS 0x0F +#define TPS65917_INT4_MASK 0x10 +#define TPS65917_INT4_LINE_STATE 0x11 +#define TPS65917_INT4_EDGE_DETECT1 0x12 +#define TPS65917_INT4_EDGE_DETECT2 0x13 +#define TPS65917_INT_CTRL 0x14 + +/* Bit definitions for INT1_STATUS */ +#define TPS65917_INT1_STATUS_VSYS_MON 0x40 +#define TPS65917_INT1_STATUS_VSYS_MON_SHIFT 0x06 +#define TPS65917_INT1_STATUS_HOTDIE 0x20 +#define TPS65917_INT1_STATUS_HOTDIE_SHIFT 0x05 +#define TPS65917_INT1_STATUS_PWRDOWN 0x10 +#define TPS65917_INT1_STATUS_PWRDOWN_SHIFT 0x04 +#define TPS65917_INT1_STATUS_LONG_PRESS_KEY 0x04 +#define TPS65917_INT1_STATUS_LONG_PRESS_KEY_SHIFT 0x02 +#define TPS65917_INT1_STATUS_PWRON 0x02 +#define TPS65917_INT1_STATUS_PWRON_SHIFT 0x01 + +/* Bit definitions for INT1_MASK */ +#define TPS65917_INT1_MASK_VSYS_MON 0x40 +#define TPS65917_INT1_MASK_VSYS_MON_SHIFT 0x06 +#define TPS65917_INT1_MASK_HOTDIE 0x20 +#define TPS65917_INT1_MASK_HOTDIE_SHIFT 0x05 +#define TPS65917_INT1_MASK_PWRDOWN 0x10 +#define TPS65917_INT1_MASK_PWRDOWN_SHIFT 0x04 +#define TPS65917_INT1_MASK_LONG_PRESS_KEY 0x04 +#define TPS65917_INT1_MASK_LONG_PRESS_KEY_SHIFT 0x02 +#define TPS65917_INT1_MASK_PWRON 0x02 +#define TPS65917_INT1_MASK_PWRON_SHIFT 0x01 + +/* Bit definitions for INT1_LINE_STATE */ +#define TPS65917_INT1_LINE_STATE_VSYS_MON 0x40 +#define TPS65917_INT1_LINE_STATE_VSYS_MON_SHIFT 0x06 +#define TPS65917_INT1_LINE_STATE_HOTDIE 0x20 +#define TPS65917_INT1_LINE_STATE_HOTDIE_SHIFT 0x05 +#define TPS65917_INT1_LINE_STATE_PWRDOWN 0x10 +#define TPS65917_INT1_LINE_STATE_PWRDOWN_SHIFT 0x04 +#define TPS65917_INT1_LINE_STATE_LONG_PRESS_KEY 0x04 +#define TPS65917_INT1_LINE_STATE_LONG_PRESS_KEY_SHIFT 0x02 +#define TPS65917_INT1_LINE_STATE_PWRON 0x02 +#define TPS65917_INT1_LINE_STATE_PWRON_SHIFT 0x01 + +/* Bit definitions for INT2_STATUS */ +#define TPS65917_INT2_STATUS_SHORT 0x40 +#define TPS65917_INT2_STATUS_SHORT_SHIFT 0x06 +#define TPS65917_INT2_STATUS_FSD 0x20 +#define TPS65917_INT2_STATUS_FSD_SHIFT 0x05 +#define TPS65917_INT2_STATUS_RESET_IN 0x10 +#define TPS65917_INT2_STATUS_RESET_IN_SHIFT 0x04 +#define TPS65917_INT2_STATUS_WDT 0x04 +#define TPS65917_INT2_STATUS_WDT_SHIFT 0x02 +#define TPS65917_INT2_STATUS_OTP_ERROR 0x02 +#define TPS65917_INT2_STATUS_OTP_ERROR_SHIFT 0x01 + +/* Bit definitions for INT2_MASK */ +#define TPS65917_INT2_MASK_SHORT 0x40 +#define TPS65917_INT2_MASK_SHORT_SHIFT 0x06 +#define TPS65917_INT2_MASK_FSD 0x20 +#define TPS65917_INT2_MASK_FSD_SHIFT 0x05 +#define TPS65917_INT2_MASK_RESET_IN 0x10 +#define TPS65917_INT2_MASK_RESET_IN_SHIFT 0x04 +#define TPS65917_INT2_MASK_WDT 0x04 +#define TPS65917_INT2_MASK_WDT_SHIFT 0x02 +#define TPS65917_INT2_MASK_OTP_ERROR_TIMER 0x02 +#define TPS65917_INT2_MASK_OTP_ERROR_SHIFT 0x01 + +/* Bit definitions for INT2_LINE_STATE */ +#define TPS65917_INT2_LINE_STATE_SHORT 0x40 +#define TPS65917_INT2_LINE_STATE_SHORT_SHIFT 0x06 +#define TPS65917_INT2_LINE_STATE_FSD 0x20 +#define TPS65917_INT2_LINE_STATE_FSD_SHIFT 0x05 +#define TPS65917_INT2_LINE_STATE_RESET_IN 0x10 +#define TPS65917_INT2_LINE_STATE_RESET_IN_SHIFT 0x04 +#define TPS65917_INT2_LINE_STATE_WDT 0x04 +#define TPS65917_INT2_LINE_STATE_WDT_SHIFT 0x02 +#define TPS65917_INT2_LINE_STATE_OTP_ERROR 0x02 +#define TPS65917_INT2_LINE_STATE_OTP_ERROR_SHIFT 0x01 + +/* Bit definitions for INT3_STATUS */ +#define TPS65917_INT3_STATUS_VBUS 0x80 +#define TPS65917_INT3_STATUS_VBUS_SHIFT 0x07 +#define TPS65917_INT3_STATUS_GPADC_EOC_SW 0x04 +#define TPS65917_INT3_STATUS_GPADC_EOC_SW_SHIFT 0x02 +#define TPS65917_INT3_STATUS_GPADC_AUTO_1 0x02 +#define TPS65917_INT3_STATUS_GPADC_AUTO_1_SHIFT 0x01 +#define TPS65917_INT3_STATUS_GPADC_AUTO_0 0x01 +#define TPS65917_INT3_STATUS_GPADC_AUTO_0_SHIFT 0x00 + +/* Bit definitions for INT3_MASK */ +#define TPS65917_INT3_MASK_VBUS 0x80 +#define TPS65917_INT3_MASK_VBUS_SHIFT 0x07 +#define TPS65917_INT3_MASK_GPADC_EOC_SW 0x04 +#define TPS65917_INT3_MASK_GPADC_EOC_SW_SHIFT 0x02 +#define TPS65917_INT3_MASK_GPADC_AUTO_1 0x02 +#define TPS65917_INT3_MASK_GPADC_AUTO_1_SHIFT 0x01 +#define TPS65917_INT3_MASK_GPADC_AUTO_0 0x01 +#define TPS65917_INT3_MASK_GPADC_AUTO_0_SHIFT 0x00 + +/* Bit definitions for INT3_LINE_STATE */ +#define TPS65917_INT3_LINE_STATE_VBUS 0x80 +#define TPS65917_INT3_LINE_STATE_VBUS_SHIFT 0x07 +#define TPS65917_INT3_LINE_STATE_GPADC_EOC_SW 0x04 +#define TPS65917_INT3_LINE_STATE_GPADC_EOC_SW_SHIFT 0x02 +#define TPS65917_INT3_LINE_STATE_GPADC_AUTO_1 0x02 +#define TPS65917_INT3_LINE_STATE_GPADC_AUTO_1_SHIFT 0x01 +#define TPS65917_INT3_LINE_STATE_GPADC_AUTO_0 0x01 +#define TPS65917_INT3_LINE_STATE_GPADC_AUTO_0_SHIFT 0x00 + +/* Bit definitions for INT4_STATUS */ +#define TPS65917_INT4_STATUS_GPIO_6 0x40 +#define TPS65917_INT4_STATUS_GPIO_6_SHIFT 0x06 +#define TPS65917_INT4_STATUS_GPIO_5 0x20 +#define TPS65917_INT4_STATUS_GPIO_5_SHIFT 0x05 +#define TPS65917_INT4_STATUS_GPIO_4 0x10 +#define TPS65917_INT4_STATUS_GPIO_4_SHIFT 0x04 +#define TPS65917_INT4_STATUS_GPIO_3 0x08 +#define TPS65917_INT4_STATUS_GPIO_3_SHIFT 0x03 +#define TPS65917_INT4_STATUS_GPIO_2 0x04 +#define TPS65917_INT4_STATUS_GPIO_2_SHIFT 0x02 +#define TPS65917_INT4_STATUS_GPIO_1 0x02 +#define TPS65917_INT4_STATUS_GPIO_1_SHIFT 0x01 +#define TPS65917_INT4_STATUS_GPIO_0 0x01 +#define TPS65917_INT4_STATUS_GPIO_0_SHIFT 0x00 + +/* Bit definitions for INT4_MASK */ +#define TPS65917_INT4_MASK_GPIO_6 0x40 +#define TPS65917_INT4_MASK_GPIO_6_SHIFT 0x06 +#define TPS65917_INT4_MASK_GPIO_5 0x20 +#define TPS65917_INT4_MASK_GPIO_5_SHIFT 0x05 +#define TPS65917_INT4_MASK_GPIO_4 0x10 +#define TPS65917_INT4_MASK_GPIO_4_SHIFT 0x04 +#define TPS65917_INT4_MASK_GPIO_3 0x08 +#define TPS65917_INT4_MASK_GPIO_3_SHIFT 0x03 +#define TPS65917_INT4_MASK_GPIO_2 0x04 +#define TPS65917_INT4_MASK_GPIO_2_SHIFT 0x02 +#define TPS65917_INT4_MASK_GPIO_1 0x02 +#define TPS65917_INT4_MASK_GPIO_1_SHIFT 0x01 +#define TPS65917_INT4_MASK_GPIO_0 0x01 +#define TPS65917_INT4_MASK_GPIO_0_SHIFT 0x00 + +/* Bit definitions for INT4_LINE_STATE */ +#define TPS65917_INT4_LINE_STATE_GPIO_6 0x40 +#define TPS65917_INT4_LINE_STATE_GPIO_6_SHIFT 0x06 +#define TPS65917_INT4_LINE_STATE_GPIO_5 0x20 +#define TPS65917_INT4_LINE_STATE_GPIO_5_SHIFT 0x05 +#define TPS65917_INT4_LINE_STATE_GPIO_4 0x10 +#define TPS65917_INT4_LINE_STATE_GPIO_4_SHIFT 0x04 +#define TPS65917_INT4_LINE_STATE_GPIO_3 0x08 +#define TPS65917_INT4_LINE_STATE_GPIO_3_SHIFT 0x03 +#define TPS65917_INT4_LINE_STATE_GPIO_2 0x04 +#define TPS65917_INT4_LINE_STATE_GPIO_2_SHIFT 0x02 +#define TPS65917_INT4_LINE_STATE_GPIO_1 0x02 +#define TPS65917_INT4_LINE_STATE_GPIO_1_SHIFT 0x01 +#define TPS65917_INT4_LINE_STATE_GPIO_0 0x01 +#define TPS65917_INT4_LINE_STATE_GPIO_0_SHIFT 0x00 + +/* Bit definitions for INT4_EDGE_DETECT1 */ +#define TPS65917_INT4_EDGE_DETECT1_GPIO_3_RISING 0x80 +#define TPS65917_INT4_EDGE_DETECT1_GPIO_3_RISING_SHIFT 0x07 +#define TPS65917_INT4_EDGE_DETECT1_GPIO_3_FALLING 0x40 +#define TPS65917_INT4_EDGE_DETECT1_GPIO_3_FALLING_SHIFT 0x06 +#define TPS65917_INT4_EDGE_DETECT1_GPIO_2_RISING 0x20 +#define TPS65917_INT4_EDGE_DETECT1_GPIO_2_RISING_SHIFT 0x05 +#define TPS65917_INT4_EDGE_DETECT1_GPIO_2_FALLING 0x10 +#define TPS65917_INT4_EDGE_DETECT1_GPIO_2_FALLING_SHIFT 0x04 +#define TPS65917_INT4_EDGE_DETECT1_GPIO_1_RISING 0x08 +#define TPS65917_INT4_EDGE_DETECT1_GPIO_1_RISING_SHIFT 0x03 +#define TPS65917_INT4_EDGE_DETECT1_GPIO_1_FALLING 0x04 +#define TPS65917_INT4_EDGE_DETECT1_GPIO_1_FALLING_SHIFT 0x02 +#define TPS65917_INT4_EDGE_DETECT1_GPIO_0_RISING 0x02 +#define TPS65917_INT4_EDGE_DETECT1_GPIO_0_RISING_SHIFT 0x01 +#define TPS65917_INT4_EDGE_DETECT1_GPIO_0_FALLING 0x01 +#define TPS65917_INT4_EDGE_DETECT1_GPIO_0_FALLING_SHIFT 0x00 + +/* Bit definitions for INT4_EDGE_DETECT2 */ +#define TPS65917_INT4_EDGE_DETECT2_GPIO_6_RISING 0x20 +#define TPS65917_INT4_EDGE_DETECT2_GPIO_6_RISING_SHIFT 0x05 +#define TPS65917_INT4_EDGE_DETECT2_GPIO_6_FALLING 0x10 +#define TPS65917_INT4_EDGE_DETECT2_GPIO_6_FALLING_SHIFT 0x04 +#define TPS65917_INT4_EDGE_DETECT2_GPIO_5_RISING 0x08 +#define TPS65917_INT4_EDGE_DETECT2_GPIO_5_RISING_SHIFT 0x03 +#define TPS65917_INT4_EDGE_DETECT2_GPIO_5_FALLING 0x04 +#define TPS65917_INT4_EDGE_DETECT2_GPIO_5_FALLING_SHIFT 0x02 +#define TPS65917_INT4_EDGE_DETECT2_GPIO_4_RISING 0x02 +#define TPS65917_INT4_EDGE_DETECT2_GPIO_4_RISING_SHIFT 0x01 +#define TPS65917_INT4_EDGE_DETECT2_GPIO_4_FALLING 0x01 +#define TPS65917_INT4_EDGE_DETECT2_GPIO_4_FALLING_SHIFT 0x00 + +/* Bit definitions for INT_CTRL */ +#define TPS65917_INT_CTRL_INT_PENDING 0x04 +#define TPS65917_INT_CTRL_INT_PENDING_SHIFT 0x02 +#define TPS65917_INT_CTRL_INT_CLEAR 0x01 +#define TPS65917_INT_CTRL_INT_CLEAR_SHIFT 0x00 + +/* TPS65917 SMPS Registers */ + +/* Registers for function SMPS */ +#define TPS65917_SMPS1_CTRL 0x00 +#define TPS65917_SMPS1_FORCE 0x02 +#define TPS65917_SMPS1_VOLTAGE 0x03 +#define TPS65917_SMPS2_CTRL 0x04 +#define TPS65917_SMPS2_FORCE 0x06 +#define TPS65917_SMPS2_VOLTAGE 0x07 +#define TPS65917_SMPS3_CTRL 0x0C +#define TPS65917_SMPS3_FORCE 0x0E +#define TPS65917_SMPS3_VOLTAGE 0x0F +#define TPS65917_SMPS4_CTRL 0x10 +#define TPS65917_SMPS4_VOLTAGE 0x13 +#define TPS65917_SMPS5_CTRL 0x18 +#define TPS65917_SMPS5_VOLTAGE 0x1B +#define TPS65917_SMPS_CTRL 0x24 +#define TPS65917_SMPS_PD_CTRL 0x25 +#define TPS65917_SMPS_THERMAL_EN 0x27 +#define TPS65917_SMPS_THERMAL_STATUS 0x28 +#define TPS65917_SMPS_SHORT_STATUS 0x29 +#define TPS65917_SMPS_NEGATIVE_CURRENT_LIMIT_EN 0x2A +#define TPS65917_SMPS_POWERGOOD_MASK1 0x2B +#define TPS65917_SMPS_POWERGOOD_MASK2 0x2C + +/* Bit definitions for SMPS1_CTRL */ +#define TPS65917_SMPS1_CTRL_WR_S 0x80 +#define TPS65917_SMPS1_CTRL_WR_S_SHIFT 0x07 +#define TPS65917_SMPS1_CTRL_ROOF_FLOOR_EN 0x40 +#define TPS65917_SMPS1_CTRL_ROOF_FLOOR_EN_SHIFT 0x06 +#define TPS65917_SMPS1_CTRL_STATUS_MASK 0x30 +#define TPS65917_SMPS1_CTRL_STATUS_SHIFT 0x04 +#define TPS65917_SMPS1_CTRL_MODE_SLEEP_MASK 0x0C +#define TPS65917_SMPS1_CTRL_MODE_SLEEP_SHIFT 0x02 +#define TPS65917_SMPS1_CTRL_MODE_ACTIVE_MASK 0x03 +#define TPS65917_SMPS1_CTRL_MODE_ACTIVE_SHIFT 0x00 + +/* Bit definitions for SMPS1_FORCE */ +#define TPS65917_SMPS1_FORCE_CMD 0x80 +#define TPS65917_SMPS1_FORCE_CMD_SHIFT 0x07 +#define TPS65917_SMPS1_FORCE_VSEL_MASK 0x7F +#define TPS65917_SMPS1_FORCE_VSEL_SHIFT 0x00 + +/* Bit definitions for SMPS1_VOLTAGE */ +#define TPS65917_SMPS1_VOLTAGE_RANGE 0x80 +#define TPS65917_SMPS1_VOLTAGE_RANGE_SHIFT 0x07 +#define TPS65917_SMPS1_VOLTAGE_VSEL_MASK 0x7F +#define TPS65917_SMPS1_VOLTAGE_VSEL_SHIFT 0x00 + +/* Bit definitions for SMPS2_CTRL */ +#define TPS65917_SMPS2_CTRL_WR_S 0x80 +#define TPS65917_SMPS2_CTRL_WR_S_SHIFT 0x07 +#define TPS65917_SMPS2_CTRL_ROOF_FLOOR_EN 0x40 +#define TPS65917_SMPS2_CTRL_ROOF_FLOOR_EN_SHIFT 0x06 +#define TPS65917_SMPS2_CTRL_STATUS_MASK 0x30 +#define TPS65917_SMPS2_CTRL_STATUS_SHIFT 0x04 +#define TPS65917_SMPS2_CTRL_MODE_SLEEP_MASK 0x0C +#define TPS65917_SMPS2_CTRL_MODE_SLEEP_SHIFT 0x02 +#define TPS65917_SMPS2_CTRL_MODE_ACTIVE_MASK 0x03 +#define TPS65917_SMPS2_CTRL_MODE_ACTIVE_SHIFT 0x00 + +/* Bit definitions for SMPS2_FORCE */ +#define TPS65917_SMPS2_FORCE_CMD 0x80 +#define TPS65917_SMPS2_FORCE_CMD_SHIFT 0x07 +#define TPS65917_SMPS2_FORCE_VSEL_MASK 0x7F +#define TPS65917_SMPS2_FORCE_VSEL_SHIFT 0x00 + +/* Bit definitions for SMPS2_VOLTAGE */ +#define TPS65917_SMPS2_VOLTAGE_RANGE 0x80 +#define TPS65917_SMPS2_VOLTAGE_RANGE_SHIFT 0x07 +#define TPS65917_SMPS2_VOLTAGE_VSEL_MASK 0x7F +#define TPS65917_SMPS2_VOLTAGE_VSEL_SHIFT 0x00 + +/* Bit definitions for SMPS3_CTRL */ +#define TPS65917_SMPS3_CTRL_WR_S 0x80 +#define TPS65917_SMPS3_CTRL_WR_S_SHIFT 0x07 +#define TPS65917_SMPS3_CTRL_ROOF_FLOOR_EN 0x40 +#define TPS65917_SMPS3_CTRL_ROOF_FLOOR_EN_SHIFT 0x06 +#define TPS65917_SMPS3_CTRL_STATUS_MASK 0x30 +#define TPS65917_SMPS3_CTRL_STATUS_SHIFT 0x04 +#define TPS65917_SMPS3_CTRL_MODE_SLEEP_MASK 0x0C +#define TPS65917_SMPS3_CTRL_MODE_SLEEP_SHIFT 0x02 +#define TPS65917_SMPS3_CTRL_MODE_ACTIVE_MASK 0x03 +#define TPS65917_SMPS3_CTRL_MODE_ACTIVE_SHIFT 0x00 + +/* Bit definitions for SMPS3_FORCE */ +#define TPS65917_SMPS3_FORCE_CMD 0x80 +#define TPS65917_SMPS3_FORCE_CMD_SHIFT 0x07 +#define TPS65917_SMPS3_FORCE_VSEL_MASK 0x7F +#define TPS65917_SMPS3_FORCE_VSEL_SHIFT 0x00 + +/* Bit definitions for SMPS3_VOLTAGE */ +#define TPS65917_SMPS3_VOLTAGE_RANGE 0x80 +#define TPS65917_SMPS3_VOLTAGE_RANGE_SHIFT 0x07 +#define TPS65917_SMPS3_VOLTAGE_VSEL_MASK 0x7F +#define TPS65917_SMPS3_VOLTAGE_VSEL_SHIFT 0x00 + +/* Bit definitions for SMPS4_CTRL */ +#define TPS65917_SMPS4_CTRL_WR_S 0x80 +#define TPS65917_SMPS4_CTRL_WR_S_SHIFT 0x07 +#define TPS65917_SMPS4_CTRL_ROOF_FLOOR_EN 0x40 +#define TPS65917_SMPS4_CTRL_ROOF_FLOOR_EN_SHIFT 0x06 +#define TPS65917_SMPS4_CTRL_STATUS_MASK 0x30 +#define TPS65917_SMPS4_CTRL_STATUS_SHIFT 0x04 +#define TPS65917_SMPS4_CTRL_MODE_SLEEP_MASK 0x0C +#define TPS65917_SMPS4_CTRL_MODE_SLEEP_SHIFT 0x02 +#define TPS65917_SMPS4_CTRL_MODE_ACTIVE_MASK 0x03 +#define TPS65917_SMPS4_CTRL_MODE_ACTIVE_SHIFT 0x00 + +/* Bit definitions for SMPS4_VOLTAGE */ +#define TPS65917_SMPS4_VOLTAGE_RANGE 0x80 +#define TPS65917_SMPS4_VOLTAGE_RANGE_SHIFT 0x07 +#define TPS65917_SMPS4_VOLTAGE_VSEL_MASK 0x7F +#define TPS65917_SMPS4_VOLTAGE_VSEL_SHIFT 0x00 + +/* Bit definitions for SMPS5_CTRL */ +#define TPS65917_SMPS5_CTRL_WR_S 0x80 +#define TPS65917_SMPS5_CTRL_WR_S_SHIFT 0x07 +#define TPS65917_SMPS5_CTRL_ROOF_FLOOR_EN 0x40 +#define TPS65917_SMPS5_CTRL_ROOF_FLOOR_EN_SHIFT 0x06 +#define TPS65917_SMPS5_CTRL_STATUS_MASK 0x30 +#define TPS65917_SMPS5_CTRL_STATUS_SHIFT 0x04 +#define TPS65917_SMPS5_CTRL_MODE_SLEEP_MASK 0x0C +#define TPS65917_SMPS5_CTRL_MODE_SLEEP_SHIFT 0x02 +#define TPS65917_SMPS5_CTRL_MODE_ACTIVE_MASK 0x03 +#define TPS65917_SMPS5_CTRL_MODE_ACTIVE_SHIFT 0x00 + +/* Bit definitions for SMPS5_VOLTAGE */ +#define TPS65917_SMPS5_VOLTAGE_RANGE 0x80 +#define TPS65917_SMPS5_VOLTAGE_RANGE_SHIFT 0x07 +#define TPS65917_SMPS5_VOLTAGE_VSEL_MASK 0x7F +#define TPS65917_SMPS5_VOLTAGE_VSEL_SHIFT 0x00 + +/* Bit definitions for SMPS_CTRL */ +#define TPS65917_SMPS_CTRL_SMPS1_SMPS12_EN 0x10 +#define TPS65917_SMPS_CTRL_SMPS1_SMPS12_EN_SHIFT 0x04 +#define TPS65917_SMPS_CTRL_SMPS12_PHASE_CTRL 0x03 +#define TPS65917_SMPS_CTRL_SMPS12_PHASE_CTRL_SHIFT 0x00 + +/* Bit definitions for SMPS_PD_CTRL */ +#define TPS65917_SMPS_PD_CTRL_SMPS5 0x40 +#define TPS65917_SMPS_PD_CTRL_SMPS5_SHIFT 0x06 +#define TPS65917_SMPS_PD_CTRL_SMPS4 0x10 +#define TPS65917_SMPS_PD_CTRL_SMPS4_SHIFT 0x04 +#define TPS65917_SMPS_PD_CTRL_SMPS3 0x08 +#define TPS65917_SMPS_PD_CTRL_SMPS3_SHIFT 0x03 +#define TPS65917_SMPS_PD_CTRL_SMPS2 0x02 +#define TPS65917_SMPS_PD_CTRL_SMPS2_SHIFT 0x01 +#define TPS65917_SMPS_PD_CTRL_SMPS1 0x01 +#define TPS65917_SMPS_PD_CTRL_SMPS1_SHIFT 0x00 + +/* Bit definitions for SMPS_THERMAL_EN */ +#define TPS65917_SMPS_THERMAL_EN_SMPS5 0x40 +#define TPS65917_SMPS_THERMAL_EN_SMPS5_SHIFT 0x06 +#define TPS65917_SMPS_THERMAL_EN_SMPS3 0x08 +#define TPS65917_SMPS_THERMAL_EN_SMPS3_SHIFT 0x03 +#define TPS65917_SMPS_THERMAL_EN_SMPS12 0x01 +#define TPS65917_SMPS_THERMAL_EN_SMPS12_SHIFT 0x00 + +/* Bit definitions for SMPS_THERMAL_STATUS */ +#define TPS65917_SMPS_THERMAL_STATUS_SMPS5 0x40 +#define TPS65917_SMPS_THERMAL_STATUS_SMPS5_SHIFT 0x06 +#define TPS65917_SMPS_THERMAL_STATUS_SMPS3 0x08 +#define TPS65917_SMPS_THERMAL_STATUS_SMPS3_SHIFT 0x03 +#define TPS65917_SMPS_THERMAL_STATUS_SMPS12 0x01 +#define TPS65917_SMPS_THERMAL_STATUS_SMPS12_SHIFT 0x00 + +/* Bit definitions for SMPS_SHORT_STATUS */ +#define TPS65917_SMPS_SHORT_STATUS_SMPS5 0x40 +#define TPS65917_SMPS_SHORT_STATUS_SMPS5_SHIFT 0x06 +#define TPS65917_SMPS_SHORT_STATUS_SMPS4 0x10 +#define TPS65917_SMPS_SHORT_STATUS_SMPS4_SHIFT 0x04 +#define TPS65917_SMPS_SHORT_STATUS_SMPS3 0x08 +#define TPS65917_SMPS_SHORT_STATUS_SMPS3_SHIFT 0x03 +#define TPS65917_SMPS_SHORT_STATUS_SMPS2 0x02 +#define TPS65917_SMPS_SHORT_STATUS_SMPS2_SHIFT 0x01 +#define TPS65917_SMPS_SHORT_STATUS_SMPS1 0x01 +#define TPS65917_SMPS_SHORT_STATUS_SMPS1_SHIFT 0x00 + +/* Bit definitions for SMPS_NEGATIVE_CURRENT_LIMIT_EN */ +#define TPS65917_SMPS_NEGATIVE_CURRENT_LIMIT_EN_SMPS5 0x40 +#define TPS65917_SMPS_NEGATIVE_CURRENT_LIMIT_EN_SMPS5_SHIFT 0x06 +#define TPS65917_SMPS_NEGATIVE_CURRENT_LIMIT_EN_SMPS4 0x10 +#define TPS65917_SMPS_NEGATIVE_CURRENT_LIMIT_EN_SMPS4_SHIFT 0x04 +#define TPS65917_SMPS_NEGATIVE_CURRENT_LIMIT_EN_SMPS3 0x08 +#define TPS65917_SMPS_NEGATIVE_CURRENT_LIMIT_EN_SMPS3_SHIFT 0x03 +#define TPS65917_SMPS_NEGATIVE_CURRENT_LIMIT_EN_SMPS2 0x02 +#define TPS65917_SMPS_NEGATIVE_CURRENT_LIMIT_EN_SMPS2_SHIFT 0x01 +#define TPS65917_SMPS_NEGATIVE_CURRENT_LIMIT_EN_SMPS1 0x01 +#define TPS65917_SMPS_NEGATIVE_CURRENT_LIMIT_EN_SMPS1_SHIFT 0x00 + +/* Bit definitions for SMPS_POWERGOOD_MASK1 */ +#define TPS65917_SMPS_POWERGOOD_MASK1_SMPS5 0x40 +#define TPS65917_SMPS_POWERGOOD_MASK1_SMPS5_SHIFT 0x06 +#define TPS65917_SMPS_POWERGOOD_MASK1_SMPS4 0x10 +#define TPS65917_SMPS_POWERGOOD_MASK1_SMPS4_SHIFT 0x04 +#define TPS65917_SMPS_POWERGOOD_MASK1_SMPS3 0x08 +#define TPS65917_SMPS_POWERGOOD_MASK1_SMPS3_SHIFT 0x03 +#define TPS65917_SMPS_POWERGOOD_MASK1_SMPS2 0x02 +#define TPS65917_SMPS_POWERGOOD_MASK1_SMPS2_SHIFT 0x01 +#define TPS65917_SMPS_POWERGOOD_MASK1_SMPS1 0x01 +#define TPS65917_SMPS_POWERGOOD_MASK1_SMPS1_SHIFT 0x00 + +/* Bit definitions for SMPS_POWERGOOD_MASK2 */ +#define TPS65917_SMPS_POWERGOOD_MASK2_POWERGOOD_TYPE_SELECT 0x80 +#define TPS65917_SMPS_POWERGOOD_MASK2_POWERGOOD_TYPE_SELECT_SHIFT 0x07 +#define TPS65917_SMPS_POWERGOOD_MASK2_OVC_ALARM_SHIFT 0x10 +#define TPS65917_SMPS_POWERGOOD_MASK2_OVC_ALARM 0x04 + +/* Bit definitions for SMPS_PLL_CTRL */ + +#define TPS65917_SMPS_PLL_CTRL_PLL_EN_PLL_BYPASS_SHIFT 0x08 +#define TPS65917_SMPS_PLL_CTRL_PLL_PLL_EN_BYPASS 0x03 +#define TPS65917_SMPS_PLL_CTRL_PLL_PLL_BYPASS_CLK_SHIFT 0x04 +#define TPS65917_SMPS_PLL_CTRL_PLL_PLL_BYPASS_CLK 0x02 + +/* Registers for function LDO */ +#define TPS65917_LDO1_CTRL 0x00 +#define TPS65917_LDO1_VOLTAGE 0x01 +#define TPS65917_LDO2_CTRL 0x02 +#define TPS65917_LDO2_VOLTAGE 0x03 +#define TPS65917_LDO3_CTRL 0x04 +#define TPS65917_LDO3_VOLTAGE 0x05 +#define TPS65917_LDO4_CTRL 0x0E +#define TPS65917_LDO4_VOLTAGE 0x0F +#define TPS65917_LDO5_CTRL 0x12 +#define TPS65917_LDO5_VOLTAGE 0x13 +#define TPS65917_LDO_PD_CTRL1 0x1B +#define TPS65917_LDO_PD_CTRL2 0x1C +#define TPS65917_LDO_SHORT_STATUS1 0x1D +#define TPS65917_LDO_SHORT_STATUS2 0x1E +#define TPS65917_LDO_PD_CTRL3 0x2D +#define TPS65917_LDO_SHORT_STATUS3 0x2E + +/* Bit definitions for LDO1_CTRL */ +#define TPS65917_LDO1_CTRL_WR_S 0x80 +#define TPS65917_LDO1_CTRL_WR_S_SHIFT 0x07 +#define TPS65917_LDO1_CTRL_BYPASS_EN 0x40 +#define TPS65917_LDO1_CTRL_BYPASS_EN_SHIFT 0x06 +#define TPS65917_LDO1_CTRL_STATUS 0x10 +#define TPS65917_LDO1_CTRL_STATUS_SHIFT 0x04 +#define TPS65917_LDO1_CTRL_MODE_SLEEP 0x04 +#define TPS65917_LDO1_CTRL_MODE_SLEEP_SHIFT 0x02 +#define TPS65917_LDO1_CTRL_MODE_ACTIVE 0x01 +#define TPS65917_LDO1_CTRL_MODE_ACTIVE_SHIFT 0x00 + +/* Bit definitions for LDO1_VOLTAGE */ +#define TPS65917_LDO1_VOLTAGE_VSEL_MASK 0x2F +#define TPS65917_LDO1_VOLTAGE_VSEL_SHIFT 0x00 + +/* Bit definitions for LDO2_CTRL */ +#define TPS65917_LDO2_CTRL_WR_S 0x80 +#define TPS65917_LDO2_CTRL_WR_S_SHIFT 0x07 +#define TPS65917_LDO2_CTRL_BYPASS_EN 0x40 +#define TPS65917_LDO2_CTRL_BYPASS_EN_SHIFT 0x06 +#define TPS65917_LDO2_CTRL_STATUS 0x10 +#define TPS65917_LDO2_CTRL_STATUS_SHIFT 0x04 +#define TPS65917_LDO2_CTRL_MODE_SLEEP 0x04 +#define TPS65917_LDO2_CTRL_MODE_SLEEP_SHIFT 0x02 +#define TPS65917_LDO2_CTRL_MODE_ACTIVE 0x01 +#define TPS65917_LDO2_CTRL_MODE_ACTIVE_SHIFT 0x00 + +/* Bit definitions for LDO2_VOLTAGE */ +#define TPS65917_LDO2_VOLTAGE_VSEL_MASK 0x2F +#define TPS65917_LDO2_VOLTAGE_VSEL_SHIFT 0x00 + +/* Bit definitions for LDO3_CTRL */ +#define TPS65917_LDO3_CTRL_WR_S 0x80 +#define TPS65917_LDO3_CTRL_WR_S_SHIFT 0x07 +#define TPS65917_LDO3_CTRL_STATUS 0x10 +#define TPS65917_LDO3_CTRL_STATUS_SHIFT 0x04 +#define TPS65917_LDO3_CTRL_MODE_SLEEP 0x04 +#define TPS65917_LDO3_CTRL_MODE_SLEEP_SHIFT 0x02 +#define TPS65917_LDO3_CTRL_MODE_ACTIVE 0x01 +#define TPS65917_LDO3_CTRL_MODE_ACTIVE_SHIFT 0x00 + +/* Bit definitions for LDO3_VOLTAGE */ +#define TPS65917_LDO3_VOLTAGE_VSEL_MASK 0x2F +#define TPS65917_LDO3_VOLTAGE_VSEL_SHIFT 0x00 + +/* Bit definitions for LDO4_CTRL */ +#define TPS65917_LDO4_CTRL_WR_S 0x80 +#define TPS65917_LDO4_CTRL_WR_S_SHIFT 0x07 +#define TPS65917_LDO4_CTRL_STATUS 0x10 +#define TPS65917_LDO4_CTRL_STATUS_SHIFT 0x04 +#define TPS65917_LDO4_CTRL_MODE_SLEEP 0x04 +#define TPS65917_LDO4_CTRL_MODE_SLEEP_SHIFT 0x02 +#define TPS65917_LDO4_CTRL_MODE_ACTIVE 0x01 +#define TPS65917_LDO4_CTRL_MODE_ACTIVE_SHIFT 0x00 + +/* Bit definitions for LDO4_VOLTAGE */ +#define TPS65917_LDO4_VOLTAGE_VSEL_MASK 0x2F +#define TPS65917_LDO4_VOLTAGE_VSEL_SHIFT 0x00 + +/* Bit definitions for LDO5_CTRL */ +#define TPS65917_LDO5_CTRL_WR_S 0x80 +#define TPS65917_LDO5_CTRL_WR_S_SHIFT 0x07 +#define TPS65917_LDO5_CTRL_STATUS 0x10 +#define TPS65917_LDO5_CTRL_STATUS_SHIFT 0x04 +#define TPS65917_LDO5_CTRL_MODE_SLEEP 0x04 +#define TPS65917_LDO5_CTRL_MODE_SLEEP_SHIFT 0x02 +#define TPS65917_LDO5_CTRL_MODE_ACTIVE 0x01 +#define TPS65917_LDO5_CTRL_MODE_ACTIVE_SHIFT 0x00 + +/* Bit definitions for LDO5_VOLTAGE */ +#define TPS65917_LDO5_VOLTAGE_VSEL_MASK 0x2F +#define TPS65917_LDO5_VOLTAGE_VSEL_SHIFT 0x00 + +/* Bit definitions for LDO_PD_CTRL1 */ +#define TPS65917_LDO_PD_CTRL1_LDO4 0x80 +#define TPS65917_LDO_PD_CTRL1_LDO4_SHIFT 0x07 +#define TPS65917_LDO_PD_CTRL1_LDO2 0x02 +#define TPS65917_LDO_PD_CTRL1_LDO2_SHIFT 0x01 +#define TPS65917_LDO_PD_CTRL1_LDO1 0x01 +#define TPS65917_LDO_PD_CTRL1_LDO1_SHIFT 0x00 + +/* Bit definitions for LDO_PD_CTRL2 */ +#define TPS65917_LDO_PD_CTRL2_LDO3 0x04 +#define TPS65917_LDO_PD_CTRL2_LDO3_SHIFT 0x02 +#define TPS65917_LDO_PD_CTRL2_LDO5 0x02 +#define TPS65917_LDO_PD_CTRL2_LDO5_SHIFT 0x01 + +/* Bit definitions for LDO_PD_CTRL3 */ +#define TPS65917_LDO_PD_CTRL2_LDOVANA 0x80 +#define TPS65917_LDO_PD_CTRL2_LDOVANA_SHIFT 0x07 + +/* Bit definitions for LDO_SHORT_STATUS1 */ +#define TPS65917_LDO_SHORT_STATUS1_LDO4 0x80 +#define TPS65917_LDO_SHORT_STATUS1_LDO4_SHIFT 0x07 +#define TPS65917_LDO_SHORT_STATUS1_LDO2 0x02 +#define TPS65917_LDO_SHORT_STATUS1_LDO2_SHIFT 0x01 +#define TPS65917_LDO_SHORT_STATUS1_LDO1 0x01 +#define TPS65917_LDO_SHORT_STATUS1_LDO1_SHIFT 0x00 + +/* Bit definitions for LDO_SHORT_STATUS2 */ +#define TPS65917_LDO_SHORT_STATUS2_LDO3 0x04 +#define TPS65917_LDO_SHORT_STATUS2_LDO3_SHIFT 0x02 +#define TPS65917_LDO_SHORT_STATUS2_LDO5 0x02 +#define TPS65917_LDO_SHORT_STATUS2_LDO5_SHIFT 0x01 + +/* Bit definitions for LDO_SHORT_STATUS2 */ +#define TPS65917_LDO_SHORT_STATUS2_LDOVANA 0x80 +#define TPS65917_LDO_SHORT_STATUS2_LDOVANA_SHIFT 0x07 + +/* Bit definitions for REGEN1_CTRL */ +#define TPS65917_REGEN1_CTRL_STATUS 0x10 +#define TPS65917_REGEN1_CTRL_STATUS_SHIFT 0x04 +#define TPS65917_REGEN1_CTRL_MODE_SLEEP 0x04 +#define TPS65917_REGEN1_CTRL_MODE_SLEEP_SHIFT 0x02 +#define TPS65917_REGEN1_CTRL_MODE_ACTIVE 0x01 +#define TPS65917_REGEN1_CTRL_MODE_ACTIVE_SHIFT 0x00 + +/* Bit definitions for PLLEN_CTRL */ +#define TPS65917_PLLEN_CTRL_STATUS 0x10 +#define TPS65917_PLLEN_CTRL_STATUS_SHIFT 0x04 +#define TPS65917_PLLEN_CTRL_MODE_SLEEP 0x04 +#define TPS65917_PLLEN_CTRL_MODE_SLEEP_SHIFT 0x02 +#define TPS65917_PLLEN_CTRL_MODE_ACTIVE 0x01 +#define TPS65917_PLLEN_CTRL_MODE_ACTIVE_SHIFT 0x00 + +/* Bit definitions for REGEN2_CTRL */ +#define TPS65917_REGEN2_CTRL_STATUS 0x10 +#define TPS65917_REGEN2_CTRL_STATUS_SHIFT 0x04 +#define TPS65917_REGEN2_CTRL_MODE_SLEEP 0x04 +#define TPS65917_REGEN2_CTRL_MODE_SLEEP_SHIFT 0x02 +#define TPS65917_REGEN2_CTRL_MODE_ACTIVE 0x01 +#define TPS65917_REGEN2_CTRL_MODE_ACTIVE_SHIFT 0x00 + +/* Bit definitions for NSLEEP_RES_ASSIGN */ +#define TPS65917_NSLEEP_RES_ASSIGN_PLL_EN 0x08 +#define TPS65917_NSLEEP_RES_ASSIGN_PLL_EN_SHIFT 0x03 +#define TPS65917_NSLEEP_RES_ASSIGN_REGEN3 0x04 +#define TPS65917_NSLEEP_RES_ASSIGN_REGEN3_SHIFT 0x02 +#define TPS65917_NSLEEP_RES_ASSIGN_REGEN2 0x02 +#define TPS65917_NSLEEP_RES_ASSIGN_REGEN2_SHIFT 0x01 +#define TPS65917_NSLEEP_RES_ASSIGN_REGEN1 0x01 +#define TPS65917_NSLEEP_RES_ASSIGN_REGEN1_SHIFT 0x00 + +/* Bit definitions for NSLEEP_SMPS_ASSIGN */ +#define TPS65917_NSLEEP_SMPS_ASSIGN_SMPS5 0x40 +#define TPS65917_NSLEEP_SMPS_ASSIGN_SMPS5_SHIFT 0x06 +#define TPS65917_NSLEEP_SMPS_ASSIGN_SMPS4 0x10 +#define TPS65917_NSLEEP_SMPS_ASSIGN_SMPS4_SHIFT 0x04 +#define TPS65917_NSLEEP_SMPS_ASSIGN_SMPS3 0x08 +#define TPS65917_NSLEEP_SMPS_ASSIGN_SMPS3_SHIFT 0x03 +#define TPS65917_NSLEEP_SMPS_ASSIGN_SMPS2 0x02 +#define TPS65917_NSLEEP_SMPS_ASSIGN_SMPS2_SHIFT 0x01 +#define TPS65917_NSLEEP_SMPS_ASSIGN_SMPS1 0x01 +#define TPS65917_NSLEEP_SMPS_ASSIGN_SMPS1_SHIFT 0x00 + +/* Bit definitions for NSLEEP_LDO_ASSIGN1 */ +#define TPS65917_NSLEEP_LDO_ASSIGN1_LDO4 0x80 +#define TPS65917_NSLEEP_LDO_ASSIGN1_LDO4_SHIFT 0x07 +#define TPS65917_NSLEEP_LDO_ASSIGN1_LDO2 0x02 +#define TPS65917_NSLEEP_LDO_ASSIGN1_LDO2_SHIFT 0x01 +#define TPS65917_NSLEEP_LDO_ASSIGN1_LDO1 0x01 +#define TPS65917_NSLEEP_LDO_ASSIGN1_LDO1_SHIFT 0x00 + +/* Bit definitions for NSLEEP_LDO_ASSIGN2 */ +#define TPS65917_NSLEEP_LDO_ASSIGN2_LDO3 0x04 +#define TPS65917_NSLEEP_LDO_ASSIGN2_LDO3_SHIFT 0x02 +#define TPS65917_NSLEEP_LDO_ASSIGN2_LDO5 0x02 +#define TPS65917_NSLEEP_LDO_ASSIGN2_LDO5_SHIFT 0x01 + +/* Bit definitions for ENABLE1_RES_ASSIGN */ +#define TPS65917_ENABLE1_RES_ASSIGN_PLLEN 0x08 +#define TPS65917_ENABLE1_RES_ASSIGN_PLLEN_SHIFT 0x03 +#define TPS65917_ENABLE1_RES_ASSIGN_REGEN3 0x04 +#define TPS65917_ENABLE1_RES_ASSIGN_REGEN3_SHIFT 0x02 +#define TPS65917_ENABLE1_RES_ASSIGN_REGEN2 0x02 +#define TPS65917_ENABLE1_RES_ASSIGN_REGEN2_SHIFT 0x01 +#define TPS65917_ENABLE1_RES_ASSIGN_REGEN1 0x01 +#define TPS65917_ENABLE1_RES_ASSIGN_REGEN1_SHIFT 0x00 + +/* Bit definitions for ENABLE1_SMPS_ASSIGN */ +#define TPS65917_ENABLE1_SMPS_ASSIGN_SMPS5 0x40 +#define TPS65917_ENABLE1_SMPS_ASSIGN_SMPS5_SHIFT 0x06 +#define TPS65917_ENABLE1_SMPS_ASSIGN_SMPS4 0x10 +#define TPS65917_ENABLE1_SMPS_ASSIGN_SMPS4_SHIFT 0x04 +#define TPS65917_ENABLE1_SMPS_ASSIGN_SMPS3 0x08 +#define TPS65917_ENABLE1_SMPS_ASSIGN_SMPS3_SHIFT 0x03 +#define TPS65917_ENABLE1_SMPS_ASSIGN_SMPS2 0x02 +#define TPS65917_ENABLE1_SMPS_ASSIGN_SMPS2_SHIFT 0x01 +#define TPS65917_ENABLE1_SMPS_ASSIGN_SMPS1 0x01 +#define TPS65917_ENABLE1_SMPS_ASSIGN_SMPS1_SHIFT 0x00 + +/* Bit definitions for ENABLE1_LDO_ASSIGN1 */ +#define TPS65917_ENABLE1_LDO_ASSIGN1_LDO4 0x80 +#define TPS65917_ENABLE1_LDO_ASSIGN1_LDO4_SHIFT 0x07 +#define TPS65917_ENABLE1_LDO_ASSIGN1_LDO2 0x02 +#define TPS65917_ENABLE1_LDO_ASSIGN1_LDO2_SHIFT 0x01 +#define TPS65917_ENABLE1_LDO_ASSIGN1_LDO1 0x01 +#define TPS65917_ENABLE1_LDO_ASSIGN1_LDO1_SHIFT 0x00 + +/* Bit definitions for ENABLE1_LDO_ASSIGN2 */ +#define TPS65917_ENABLE1_LDO_ASSIGN2_LDO3 0x04 +#define TPS65917_ENABLE1_LDO_ASSIGN2_LDO3_SHIFT 0x02 +#define TPS65917_ENABLE1_LDO_ASSIGN2_LDO5 0x02 +#define TPS65917_ENABLE1_LDO_ASSIGN2_LDO5_SHIFT 0x01 + +/* Bit definitions for ENABLE2_RES_ASSIGN */ +#define TPS65917_ENABLE2_RES_ASSIGN_PLLEN 0x08 +#define TPS65917_ENABLE2_RES_ASSIGN_PLLEN_SHIFT 0x03 +#define TPS65917_ENABLE2_RES_ASSIGN_REGEN3 0x04 +#define TPS65917_ENABLE2_RES_ASSIGN_REGEN3_SHIFT 0x02 +#define TPS65917_ENABLE2_RES_ASSIGN_REGEN2 0x02 +#define TPS65917_ENABLE2_RES_ASSIGN_REGEN2_SHIFT 0x01 +#define TPS65917_ENABLE2_RES_ASSIGN_REGEN1 0x01 +#define TPS65917_ENABLE2_RES_ASSIGN_REGEN1_SHIFT 0x00 + +/* Bit definitions for ENABLE2_SMPS_ASSIGN */ +#define TPS65917_ENABLE2_SMPS_ASSIGN_SMPS5 0x40 +#define TPS65917_ENABLE2_SMPS_ASSIGN_SMPS5_SHIFT 0x06 +#define TPS65917_ENABLE2_SMPS_ASSIGN_SMPS4 0x10 +#define TPS65917_ENABLE2_SMPS_ASSIGN_SMPS4_SHIFT 0x04 +#define TPS65917_ENABLE2_SMPS_ASSIGN_SMPS3 0x08 +#define TPS65917_ENABLE2_SMPS_ASSIGN_SMPS3_SHIFT 0x03 +#define TPS65917_ENABLE2_SMPS_ASSIGN_SMPS2 0x02 +#define TPS65917_ENABLE2_SMPS_ASSIGN_SMPS2_SHIFT 0x01 +#define TPS65917_ENABLE2_SMPS_ASSIGN_SMPS1 0x01 +#define TPS65917_ENABLE2_SMPS_ASSIGN_SMPS1_SHIFT 0x00 + +/* Bit definitions for ENABLE2_LDO_ASSIGN1 */ +#define TPS65917_ENABLE2_LDO_ASSIGN1_LDO4 0x80 +#define TPS65917_ENABLE2_LDO_ASSIGN1_LDO4_SHIFT 0x07 +#define TPS65917_ENABLE2_LDO_ASSIGN1_LDO2 0x02 +#define TPS65917_ENABLE2_LDO_ASSIGN1_LDO2_SHIFT 0x01 +#define TPS65917_ENABLE2_LDO_ASSIGN1_LDO1 0x01 +#define TPS65917_ENABLE2_LDO_ASSIGN1_LDO1_SHIFT 0x00 + +/* Bit definitions for ENABLE2_LDO_ASSIGN2 */ +#define TPS65917_ENABLE2_LDO_ASSIGN2_LDO3 0x04 +#define TPS65917_ENABLE2_LDO_ASSIGN2_LDO3_SHIFT 0x02 +#define TPS65917_ENABLE2_LDO_ASSIGN2_LDO5 0x02 +#define TPS65917_ENABLE2_LDO_ASSIGN2_LDO5_SHIFT 0x01 + +/* Bit definitions for REGEN3_CTRL */ +#define TPS65917_REGEN3_CTRL_STATUS 0x10 +#define TPS65917_REGEN3_CTRL_STATUS_SHIFT 0x04 +#define TPS65917_REGEN3_CTRL_MODE_SLEEP 0x04 +#define TPS65917_REGEN3_CTRL_MODE_SLEEP_SHIFT 0x02 +#define TPS65917_REGEN3_CTRL_MODE_ACTIVE 0x01 +#define TPS65917_REGEN3_CTRL_MODE_ACTIVE_SHIFT 0x00 + +/* Registers for function RESOURCE */ +#define TPS65917_REGEN1_CTRL 0x2 +#define TPS65917_PLLEN_CTRL 0x3 +#define TPS65917_NSLEEP_RES_ASSIGN 0x6 +#define TPS65917_NSLEEP_SMPS_ASSIGN 0x7 +#define TPS65917_NSLEEP_LDO_ASSIGN1 0x8 +#define TPS65917_NSLEEP_LDO_ASSIGN2 0x9 +#define TPS65917_ENABLE1_RES_ASSIGN 0xA +#define TPS65917_ENABLE1_SMPS_ASSIGN 0xB +#define TPS65917_ENABLE1_LDO_ASSIGN1 0xC +#define TPS65917_ENABLE1_LDO_ASSIGN2 0xD +#define TPS65917_ENABLE2_RES_ASSIGN 0xE +#define TPS65917_ENABLE2_SMPS_ASSIGN 0xF +#define TPS65917_ENABLE2_LDO_ASSIGN1 0x10 +#define TPS65917_ENABLE2_LDO_ASSIGN2 0x11 +#define TPS65917_REGEN2_CTRL 0x12 +#define TPS65917_REGEN3_CTRL 0x13 + static inline int palmas_read(struct palmas *palmas, unsigned int base, unsigned int reg, unsigned int *val) { -- cgit v1.2.3 From 9f057dc1d01383e6975e515ba8e661d2aeb6bdfc Mon Sep 17 00:00:00 2001 From: Keerthy Date: Wed, 18 Jun 2014 15:28:56 +0530 Subject: regulator: palmas: Shift the reg_info structure definition to the header file Shift the reg_info structure definition to the header file. Acked-by: Lee Jones Signed-off-by: Keerthy Signed-off-by: Mark Brown --- drivers/regulator/palmas-regulator.c | 9 --------- include/linux/mfd/palmas.h | 9 +++++++++ 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c index b982f0ff4e01..1cf462f90df5 100644 --- a/drivers/regulator/palmas-regulator.c +++ b/drivers/regulator/palmas-regulator.c @@ -27,15 +27,6 @@ #include #include -struct regs_info { - char *name; - char *sname; - u8 vsel_addr; - u8 ctrl_addr; - u8 tstep_addr; - int sleep_id; -}; - static const struct regulator_linear_range smps_low_ranges[] = { REGULATOR_LINEAR_RANGE(0, 0x0, 0x0, 0), REGULATOR_LINEAR_RANGE(500000, 0x1, 0x6, 0), diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h index f760a07ab76e..c123666c70f7 100644 --- a/include/linux/mfd/palmas.h +++ b/include/linux/mfd/palmas.h @@ -88,6 +88,15 @@ struct palmas { u8 pwm_muxed; }; +struct regs_info { + char *name; + char *sname; + u8 vsel_addr; + u8 ctrl_addr; + u8 tstep_addr; + int sleep_id; +}; + struct palmas_gpadc_platform_data { /* Channel 3 current source is only enabled during conversion */ int ch3_current; -- cgit v1.2.3 From 7ec70c73c9e93556fd19bb6bdfbbd089d9db438b Mon Sep 17 00:00:00 2001 From: Keerthy Date: Wed, 18 Jun 2014 15:28:57 +0530 Subject: mfd: palmas: shift the palmas_sleep_requestor_info structure definition to the header file shift the palmas_sleep_requestor_info structure definition to the header file. Acked-by: Lee Jones Signed-off-by: Keerthy Signed-off-by: Mark Brown --- drivers/mfd/palmas.c | 10 ---------- include/linux/mfd/palmas.h | 10 ++++++++++ 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/mfd/palmas.c b/drivers/mfd/palmas.c index 6e1786187dd8..c12759d1bd7c 100644 --- a/drivers/mfd/palmas.c +++ b/drivers/mfd/palmas.c @@ -25,16 +25,6 @@ #include #include -#define PALMAS_EXT_REQ (PALMAS_EXT_CONTROL_ENABLE1 | \ - PALMAS_EXT_CONTROL_ENABLE2 | \ - PALMAS_EXT_CONTROL_NSLEEP) - -struct palmas_sleep_requestor_info { - int id; - int reg_offset; - int bit_pos; -}; - #define EXTERNAL_REQUESTOR(_id, _offset, _pos) \ [PALMAS_EXTERNAL_REQSTR_ID_##_id] = { \ .id = PALMAS_EXTERNAL_REQSTR_ID_##_id, \ diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h index c123666c70f7..0136e583f7f8 100644 --- a/include/linux/mfd/palmas.h +++ b/include/linux/mfd/palmas.h @@ -88,6 +88,16 @@ struct palmas { u8 pwm_muxed; }; +#define PALMAS_EXT_REQ (PALMAS_EXT_CONTROL_ENABLE1 | \ + PALMAS_EXT_CONTROL_ENABLE2 | \ + PALMAS_EXT_CONTROL_NSLEEP) + +struct palmas_sleep_requestor_info { + int id; + int reg_offset; + int bit_pos; +}; + struct regs_info { char *name; char *sname; -- cgit v1.2.3 From fe40b173441e4519347395825d15d2c5386494c8 Mon Sep 17 00:00:00 2001 From: Keerthy Date: Wed, 18 Jun 2014 15:28:58 +0530 Subject: regulator: palmas: Add palmas_pmic_driver_data structure Add palmas_pmic_driver_data structure. Signed-off-by: Keerthy Signed-off-by: Mark Brown --- include/linux/mfd/palmas.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'include') diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h index 0136e583f7f8..1a045ba32c13 100644 --- a/include/linux/mfd/palmas.h +++ b/include/linux/mfd/palmas.h @@ -53,6 +53,8 @@ struct palmas_pmic; struct palmas_gpadc; struct palmas_resource; struct palmas_usb; +struct palmas_pmic_driver_data; +struct palmas_pmic_platform_data; enum palmas_usb_state { PALMAS_USB_STATE_DISCONNECT, @@ -76,6 +78,8 @@ struct palmas { struct mutex irq_lock; struct regmap_irq_chip_data *irq_data; + struct palmas_pmic_driver_data *pmic_ddata; + /* Child Devices */ struct palmas_pmic *pmic; struct palmas_gpadc *gpadc; @@ -107,6 +111,27 @@ struct regs_info { int sleep_id; }; +struct palmas_pmic_driver_data { + int smps_start; + int smps_end; + int ldo_begin; + int ldo_end; + int max_reg; + struct regs_info *palmas_regs_info; + struct of_regulator_match *palmas_matches; + struct palmas_sleep_requestor_info *sleep_req_info; + int (*smps_register)(struct palmas_pmic *pmic, + struct palmas_pmic_driver_data *ddata, + struct palmas_pmic_platform_data *pdata, + const char *pdev_name, + struct regulator_config config); + int (*ldo_register)(struct palmas_pmic *pmic, + struct palmas_pmic_driver_data *ddata, + struct palmas_pmic_platform_data *pdata, + const char *pdev_name, + struct regulator_config config); +}; + struct palmas_gpadc_platform_data { /* Channel 3 current source is only enabled during conversion */ int ch3_current; -- cgit v1.2.3 From 31fa97c5defca3895dc6c823096d7ba59df76125 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 11 Jun 2014 17:18:21 +0300 Subject: cfg80211: pass TDLS initiator in tdls_mgmt operations The TDLS initiator is set once during link setup. If determines the address ordering in the link identifier IE. Fix dependent drivers - mwifiex and mac80211. Signed-off-by: Arik Nemtsov Signed-off-by: Johannes Berg --- drivers/net/wireless/mwifiex/cfg80211.c | 3 ++- include/net/cfg80211.h | 2 +- include/uapi/linux/nl80211.h | 5 +++++ net/mac80211/ieee80211_i.h | 3 ++- net/mac80211/tdls.c | 3 ++- net/wireless/nl80211.c | 4 ++++ net/wireless/rdev-ops.h | 6 +++--- net/wireless/trace.h | 10 +++++++--- 8 files changed, 26 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index e95dec91a561..149d2e693bdd 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -2631,7 +2631,8 @@ static int mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capability, - const u8 *extra_ies, size_t extra_ies_len) + bool initiator, const u8 *extra_ies, + size_t extra_ies_len) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); int ret; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 29cb4b2bee5a..b9eeae3990cf 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2500,7 +2500,7 @@ struct cfg80211_ops { int (*tdls_mgmt)(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capability, - const u8 *buf, size_t len); + bool initiator, const u8 *buf, size_t len); int (*tdls_oper)(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, enum nl80211_tdls_operation oper); diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index be9519b52bb1..f1db15b9c041 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1591,6 +1591,9 @@ enum nl80211_commands { * creation then the new interface will be owned by the netlink socket * that created it and will be destroyed when the socket is closed * + * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is + * the TDLS link initiator. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1931,6 +1934,8 @@ enum nl80211_attrs { NL80211_ATTR_CSA_C_OFFSETS_TX, NL80211_ATTR_MAX_CSA_COUNTERS, + NL80211_ATTR_TDLS_INITIATOR, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index fc687d2a7518..75f79c168e90 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1867,7 +1867,8 @@ int ieee80211_max_num_channels(struct ieee80211_local *local); int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capability, - const u8 *extra_ies, size_t extra_ies_len); + bool initiator, const u8 *extra_ies, + size_t extra_ies_len); int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, enum nl80211_tdls_operation oper); diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index cafcbde70018..0b3ca2ce7ea4 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -299,7 +299,8 @@ fail: int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capability, - const u8 *extra_ies, size_t extra_ies_len) + bool initiator, const u8 *extra_ies, + size_t extra_ies_len) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ba4f1723c83a..8f46b8ffbcf6 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -337,6 +337,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_TDLS_OPERATION] = { .type = NLA_U8 }, [NL80211_ATTR_TDLS_SUPPORT] = { .type = NLA_FLAG }, [NL80211_ATTR_TDLS_EXTERNAL_SETUP] = { .type = NLA_FLAG }, + [NL80211_ATTR_TDLS_INITIATOR] = { .type = NLA_FLAG }, [NL80211_ATTR_DONT_WAIT_FOR_ACK] = { .type = NLA_FLAG }, [NL80211_ATTR_PROBE_RESP] = { .type = NLA_BINARY, .len = IEEE80211_MAX_DATA_LEN }, @@ -7365,6 +7366,7 @@ static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info) u32 peer_capability = 0; u16 status_code; u8 *peer; + bool initiator; if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) || !rdev->ops->tdls_mgmt) @@ -7381,12 +7383,14 @@ static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info) action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]); status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]); dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]); + initiator = nla_get_flag(info->attrs[NL80211_ATTR_TDLS_INITIATOR]); if (info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY]) peer_capability = nla_get_u32(info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY]); return rdev_tdls_mgmt(rdev, dev, peer, action_code, dialog_token, status_code, peer_capability, + initiator, nla_data(info->attrs[NL80211_ATTR_IE]), nla_len(info->attrs[NL80211_ATTR_IE])); } diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index f552b0abbd70..56c2240c30ce 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -751,15 +751,15 @@ static inline int rdev_tdls_mgmt(struct cfg80211_registered_device *rdev, struct net_device *dev, u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capability, - const u8 *buf, size_t len) + bool initiator, const u8 *buf, size_t len) { int ret; trace_rdev_tdls_mgmt(&rdev->wiphy, dev, peer, action_code, dialog_token, status_code, peer_capability, - buf, len); + initiator, buf, len); ret = rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code, dialog_token, status_code, peer_capability, - buf, len); + initiator, buf, len); trace_rdev_return_int(&rdev->wiphy, ret); return ret; } diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 174559aade57..85474ee501eb 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -1454,9 +1454,9 @@ TRACE_EVENT(rdev_tdls_mgmt, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capability, - const u8 *buf, size_t len), + bool initiator, const u8 *buf, size_t len), TP_ARGS(wiphy, netdev, peer, action_code, dialog_token, status_code, - peer_capability, buf, len), + peer_capability, initiator, buf, len), TP_STRUCT__entry( WIPHY_ENTRY NETDEV_ENTRY @@ -1465,6 +1465,7 @@ TRACE_EVENT(rdev_tdls_mgmt, __field(u8, dialog_token) __field(u16, status_code) __field(u32, peer_capability) + __field(bool, initiator) __dynamic_array(u8, buf, len) ), TP_fast_assign( @@ -1475,13 +1476,16 @@ TRACE_EVENT(rdev_tdls_mgmt, __entry->dialog_token = dialog_token; __entry->status_code = status_code; __entry->peer_capability = peer_capability; + __entry->initiator = initiator; memcpy(__get_dynamic_array(buf), buf, len); ), TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT ", action_code: %u, " - "dialog_token: %u, status_code: %u, peer_capability: %u buf: %#.2x ", + "dialog_token: %u, status_code: %u, peer_capability: %u " + "initiator: %s buf: %#.2x ", WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->action_code, __entry->dialog_token, __entry->status_code, __entry->peer_capability, + BOOL_TO_STR(__entry->initiator), ((u8 *)__get_dynamic_array(buf))[0]) ); -- cgit v1.2.3 From c887f0d3a03283cb6fe2c32aae62229bebd3fa32 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 11 Jun 2014 17:18:25 +0300 Subject: mac80211: add API to request TDLS operation from userspace Write a mac80211 to the cfg80211 API for requesting a userspace TDLS operation. Define TDLS specific reason codes that can be used here. Signed-off-by: Arik Nemtsov Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 3 +++ include/net/mac80211.h | 13 +++++++++++++ net/mac80211/tdls.c | 17 +++++++++++++++++ 3 files changed, 33 insertions(+) (limited to 'include') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 6bff13f74050..75d17e15da33 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1621,6 +1621,9 @@ enum ieee80211_reasoncode { WLAN_REASON_INVALID_RSN_IE_CAP = 22, WLAN_REASON_IEEE8021X_FAILED = 23, WLAN_REASON_CIPHER_SUITE_REJECTED = 24, + /* TDLS (802.11z) */ + WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE = 25, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED = 26, /* 802.11e */ WLAN_REASON_DISASSOC_UNSPECIFIED_QOS = 32, WLAN_REASON_DISASSOC_QAP_NO_BANDWIDTH = 33, diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 421b6ecb4b2c..8d876dc8b299 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -4815,4 +4815,17 @@ int ieee80211_parse_p2p_noa(const struct ieee80211_p2p_noa_attr *attr, */ void ieee80211_update_p2p_noa(struct ieee80211_noa_data *data, u32 tsf); +/** + * ieee80211_tdls_oper - request userspace to perform a TDLS operation + * @vif: virtual interface + * @peer: the peer's destination address + * @oper: the requested TDLS operation + * @reason_code: reason code for the operation, valid for TDLS teardown + * @gfp: allocation flags + * + * See cfg80211_tdls_oper_request(). + */ +void ieee80211_tdls_oper_request(struct ieee80211_vif *vif, const u8 *peer, + enum nl80211_tdls_operation oper, + u16 reason_code, gfp_t gfp); #endif /* MAC80211_H */ diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 0ba7e4c029c8..6f3a3ad0cb7c 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -8,6 +8,7 @@ */ #include +#include #include "ieee80211_i.h" /* give usermode some time for retries in setting up the TDLS session */ @@ -514,3 +515,19 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, mutex_unlock(&local->mtx); return ret; } + +void ieee80211_tdls_oper_request(struct ieee80211_vif *vif, const u8 *peer, + enum nl80211_tdls_operation oper, + u16 reason_code, gfp_t gfp) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + + if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc) { + sdata_err(sdata, "Discarding TDLS oper %d - not STA or disconnected\n", + oper); + return; + } + + cfg80211_tdls_oper_request(sdata->dev, peer, oper, reason_code, gfp); +} +EXPORT_SYMBOL(ieee80211_tdls_oper_request); -- cgit v1.2.3 From ee10f2c779b28c1d6e87ac3e1bbb1aa8b62fa891 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 11 Jun 2014 17:18:27 +0300 Subject: mac80211: protect TDLS discovery session After sending a TDLS discovery-request, we expect a reply to arrive on the AP's channel. We must stay on the channel (no PSM, scan, etc.), since a TDLS setup-response is a direct packet not buffered by the AP. Add a new mac80211 driver callback to allow discovery session protection. Signed-off-by: Arik Nemtsov Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- include/net/mac80211.h | 12 ++++++++++++ net/mac80211/driver-ops.h | 16 ++++++++++++++++ net/mac80211/tdls.c | 10 +++++++++- net/mac80211/trace.h | 7 +++++++ 4 files changed, 44 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 8d876dc8b299..18c2bdbaf988 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2764,6 +2764,15 @@ enum ieee80211_roc_type { * mac80211 will transmit the frame right away. * The callback is optional and can (should!) sleep. * + * @mgd_protect_tdls_discover: Protect a TDLS discovery session. After sending + * a TDLS discovery-request, we expect a reply to arrive on the AP's + * channel. We must stay on the channel (no PSM, scan, etc.), since a TDLS + * setup-response is a direct packet not buffered by the AP. + * mac80211 will call this function just before the transmission of a TDLS + * discovery-request. The recommended period of protection is at least + * 2 * (DTIM period). + * The callback is optional and can sleep. + * * @add_chanctx: Notifies device driver about new channel context creation. * @remove_chanctx: Notifies device driver about channel context destruction. * @change_chanctx: Notifies device driver about channel context changes that @@ -2981,6 +2990,9 @@ struct ieee80211_ops { void (*mgd_prepare_tx)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); + void (*mgd_protect_tdls_discover)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); + int (*add_chanctx)(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx); void (*remove_chanctx)(struct ieee80211_hw *hw, diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index bd782dcffcc7..2265bd7a44ba 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -970,6 +970,22 @@ static inline void drv_mgd_prepare_tx(struct ieee80211_local *local, trace_drv_return_void(local); } +static inline void +drv_mgd_protect_tdls_discover(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) +{ + might_sleep(); + + if (!check_sdata_in_driver(sdata)) + return; + WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION); + + trace_drv_mgd_protect_tdls_discover(local, sdata); + if (local->ops->mgd_protect_tdls_discover) + local->ops->mgd_protect_tdls_discover(&local->hw, &sdata->vif); + trace_drv_return_void(local); +} + static inline int drv_add_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx *ctx) { diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index b87e369561f4..f7185338a0fa 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -10,6 +10,7 @@ #include #include #include "ieee80211_i.h" +#include "driver-ops.h" /* give usermode some time for retries in setting up the TDLS session */ #define TDLS_PEER_SETUP_TIMEOUT (15 * HZ) @@ -442,8 +443,15 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, peer_capability, initiator, extra_ies, extra_ies_len); break; - case WLAN_TDLS_SETUP_CONFIRM: case WLAN_TDLS_DISCOVERY_REQUEST: + /* + * Protect the discovery so we can hear the TDLS discovery + * response frame. It is transmitted directly and not buffered + * by the AP. + */ + drv_mgd_protect_tdls_discover(sdata->local, sdata); + /* fall-through */ + case WLAN_TDLS_SETUP_CONFIRM: case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: /* no special handling */ ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index cfe1a0688b5c..02ac535d1274 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -1330,6 +1330,13 @@ DEFINE_EVENT(local_sdata_evt, drv_mgd_prepare_tx, TP_ARGS(local, sdata) ); +DEFINE_EVENT(local_sdata_evt, drv_mgd_protect_tdls_discover, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + + TP_ARGS(local, sdata) +); + DECLARE_EVENT_CLASS(local_chanctx, TP_PROTO(struct ieee80211_local *local, struct ieee80211_chanctx *ctx), -- cgit v1.2.3 From f15a5cf912f05b572d1f9f3772fba019643f4837 Mon Sep 17 00:00:00 2001 From: Kinglong Mee Date: Tue, 10 Jun 2014 18:29:39 +0800 Subject: SUNRPC/NFSD: Change to type of bool for rq_usedeferral and rq_splice_ok rq_usedeferral and rq_splice_ok are used as 0 and 1, just defined to bool. Signed-off-by: Kinglong Mee Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4proc.c | 4 ++-- include/linux/sunrpc/svc.h | 4 ++-- net/sunrpc/auth_gss/svcauth_gss.c | 2 +- net/sunrpc/svc.c | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index baa3803f0811..be6734060d2a 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -1298,7 +1298,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, * Don't use the deferral mechanism for NFSv4; compounds make it * too hard to avoid non-idempotency problems. */ - rqstp->rq_usedeferral = 0; + rqstp->rq_usedeferral = false; /* * According to RFC3010, this takes precedence over all other errors. @@ -1417,7 +1417,7 @@ encode_op: BUG_ON(cstate->replay_owner); out: /* Reset deferral mechanism for RPC deferrals */ - rqstp->rq_usedeferral = 1; + rqstp->rq_usedeferral = true; dprintk("nfsv4 compound returned %d\n", ntohl(status)); return status; } diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 1bc7cd05b22e..cf61ecd148e0 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -236,7 +236,7 @@ struct svc_rqst { struct svc_cred rq_cred; /* auth info */ void * rq_xprt_ctxt; /* transport specific context ptr */ struct svc_deferred_req*rq_deferred; /* deferred request we are replaying */ - int rq_usedeferral; /* use deferral */ + bool rq_usedeferral; /* use deferral */ size_t rq_xprt_hlen; /* xprt header len */ struct xdr_buf rq_arg; @@ -277,7 +277,7 @@ struct svc_rqst { struct auth_domain * rq_gssclient; /* "gss/"-style peer info */ int rq_cachetype; struct svc_cacherep * rq_cacherep; /* cache info */ - int rq_splice_ok; /* turned off in gss privacy + bool rq_splice_ok; /* turned off in gss privacy * to prevent encrypting page * cache pages */ wait_queue_head_t rq_wait; /* synchronization */ diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 4ce5eccec1f6..c548ab213f76 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -886,7 +886,7 @@ unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gs u32 priv_len, maj_stat; int pad, saved_len, remaining_len, offset; - rqstp->rq_splice_ok = 0; + rqstp->rq_splice_ok = false; priv_len = svc_getnl(&buf->head[0]); if (rqstp->rq_deferred) { diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 5de6801cd924..1db5007ddbce 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -1086,9 +1086,9 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv) goto err_short_len; /* Will be turned off only in gss privacy case: */ - rqstp->rq_splice_ok = 1; + rqstp->rq_splice_ok = true; /* Will be turned off only when NFSv4 Sessions are used */ - rqstp->rq_usedeferral = 1; + rqstp->rq_usedeferral = true; rqstp->rq_dropme = false; /* Setup reply header */ -- cgit v1.2.3 From 0a2409aad38e97b1db55e6515b990be7b17060f6 Mon Sep 17 00:00:00 2001 From: "Chen, Gong" Date: Wed, 11 Jun 2014 13:57:27 -0700 Subject: trace, AER: Move trace into unified interface AER uses a separate trace interface by now. To make it consistent, move it into unified RAS trace interface. Signed-off-by: Chen, Gong Acked-by: Borislav Petkov Signed-off-by: Tony Luck --- drivers/pci/pcie/aer/Kconfig | 1 + drivers/pci/pcie/aer/aerdrv_errprint.c | 4 +- include/ras/ras_event.h | 64 ++++++++++++++++++++++++++++ include/trace/events/ras.h | 77 ---------------------------------- 4 files changed, 66 insertions(+), 80 deletions(-) delete mode 100644 include/trace/events/ras.h (limited to 'include') diff --git a/drivers/pci/pcie/aer/Kconfig b/drivers/pci/pcie/aer/Kconfig index 50e94e02378a..389440228c1d 100644 --- a/drivers/pci/pcie/aer/Kconfig +++ b/drivers/pci/pcie/aer/Kconfig @@ -5,6 +5,7 @@ config PCIEAER boolean "Root Port Advanced Error Reporting support" depends on PCIEPORTBUS + select RAS default y help This enables PCI Express Root Port Advanced Error Reporting diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c index 36ed31b52198..35d06e177917 100644 --- a/drivers/pci/pcie/aer/aerdrv_errprint.c +++ b/drivers/pci/pcie/aer/aerdrv_errprint.c @@ -22,9 +22,7 @@ #include #include "aerdrv.h" - -#define CREATE_TRACE_POINTS -#include +#include #define AER_AGENT_RECEIVER 0 #define AER_AGENT_REQUESTER 1 diff --git a/include/ras/ras_event.h b/include/ras/ras_event.h index 21cdb0b7b0fb..acbcbb88eaaa 100644 --- a/include/ras/ras_event.h +++ b/include/ras/ras_event.h @@ -8,6 +8,7 @@ #include #include #include +#include /* * Hardware Events Report @@ -94,6 +95,69 @@ TRACE_EVENT(mc_event, __get_str(driver_detail)) ); +/* + * PCIe AER Trace event + * + * These events are generated when hardware detects a corrected or + * uncorrected event on a PCIe device. The event report has + * the following structure: + * + * char * dev_name - The name of the slot where the device resides + * ([domain:]bus:device.function). + * u32 status - Either the correctable or uncorrectable register + * indicating what error or errors have been seen + * u8 severity - error severity 0:NONFATAL 1:FATAL 2:CORRECTED + */ + +#define aer_correctable_errors \ + {BIT(0), "Receiver Error"}, \ + {BIT(6), "Bad TLP"}, \ + {BIT(7), "Bad DLLP"}, \ + {BIT(8), "RELAY_NUM Rollover"}, \ + {BIT(12), "Replay Timer Timeout"}, \ + {BIT(13), "Advisory Non-Fatal"} + +#define aer_uncorrectable_errors \ + {BIT(4), "Data Link Protocol"}, \ + {BIT(12), "Poisoned TLP"}, \ + {BIT(13), "Flow Control Protocol"}, \ + {BIT(14), "Completion Timeout"}, \ + {BIT(15), "Completer Abort"}, \ + {BIT(16), "Unexpected Completion"}, \ + {BIT(17), "Receiver Overflow"}, \ + {BIT(18), "Malformed TLP"}, \ + {BIT(19), "ECRC"}, \ + {BIT(20), "Unsupported Request"} + +TRACE_EVENT(aer_event, + TP_PROTO(const char *dev_name, + const u32 status, + const u8 severity), + + TP_ARGS(dev_name, status, severity), + + TP_STRUCT__entry( + __string( dev_name, dev_name ) + __field( u32, status ) + __field( u8, severity ) + ), + + TP_fast_assign( + __assign_str(dev_name, dev_name); + __entry->status = status; + __entry->severity = severity; + ), + + TP_printk("%s PCIe Bus Error: severity=%s, %s\n", + __get_str(dev_name), + __entry->severity == AER_CORRECTABLE ? "Corrected" : + __entry->severity == AER_FATAL ? + "Fatal" : "Uncorrected, non-fatal", + __entry->severity == AER_CORRECTABLE ? + __print_flags(__entry->status, "|", aer_correctable_errors) : + __print_flags(__entry->status, "|", aer_uncorrectable_errors)) +); + #endif /* _TRACE_HW_EVENT_MC_H */ /* This part must be outside protection */ diff --git a/include/trace/events/ras.h b/include/trace/events/ras.h deleted file mode 100644 index 1c875ad1ee5f..000000000000 --- a/include/trace/events/ras.h +++ /dev/null @@ -1,77 +0,0 @@ -#undef TRACE_SYSTEM -#define TRACE_SYSTEM ras - -#if !defined(_TRACE_AER_H) || defined(TRACE_HEADER_MULTI_READ) -#define _TRACE_AER_H - -#include -#include - - -/* - * PCIe AER Trace event - * - * These events are generated when hardware detects a corrected or - * uncorrected event on a PCIe device. The event report has - * the following structure: - * - * char * dev_name - The name of the slot where the device resides - * ([domain:]bus:device.function). - * u32 status - Either the correctable or uncorrectable register - * indicating what error or errors have been seen - * u8 severity - error severity 0:NONFATAL 1:FATAL 2:CORRECTED - */ - -#define aer_correctable_errors \ - {BIT(0), "Receiver Error"}, \ - {BIT(6), "Bad TLP"}, \ - {BIT(7), "Bad DLLP"}, \ - {BIT(8), "RELAY_NUM Rollover"}, \ - {BIT(12), "Replay Timer Timeout"}, \ - {BIT(13), "Advisory Non-Fatal"} - -#define aer_uncorrectable_errors \ - {BIT(4), "Data Link Protocol"}, \ - {BIT(12), "Poisoned TLP"}, \ - {BIT(13), "Flow Control Protocol"}, \ - {BIT(14), "Completion Timeout"}, \ - {BIT(15), "Completer Abort"}, \ - {BIT(16), "Unexpected Completion"}, \ - {BIT(17), "Receiver Overflow"}, \ - {BIT(18), "Malformed TLP"}, \ - {BIT(19), "ECRC"}, \ - {BIT(20), "Unsupported Request"} - -TRACE_EVENT(aer_event, - TP_PROTO(const char *dev_name, - const u32 status, - const u8 severity), - - TP_ARGS(dev_name, status, severity), - - TP_STRUCT__entry( - __string( dev_name, dev_name ) - __field( u32, status ) - __field( u8, severity ) - ), - - TP_fast_assign( - __assign_str(dev_name, dev_name); - __entry->status = status; - __entry->severity = severity; - ), - - TP_printk("%s PCIe Bus Error: severity=%s, %s\n", - __get_str(dev_name), - __entry->severity == AER_CORRECTABLE ? "Corrected" : - __entry->severity == AER_FATAL ? - "Fatal" : "Uncorrected, non-fatal", - __entry->severity == AER_CORRECTABLE ? - __print_flags(__entry->status, "|", aer_correctable_errors) : - __print_flags(__entry->status, "|", aer_uncorrectable_errors)) -); - -#endif /* _TRACE_AER_H */ - -/* This part must be outside protection */ -#include -- cgit v1.2.3 From 546a9d8519ed137b2804a3f5a3659003039dd49c Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 19 Jun 2014 14:57:10 -0700 Subject: rcu: Export debug_init_rcu_head() and and debug_init_rcu_head() Currently, call_rcu() relies on implicit allocation and initialization for the debug-objects handling of RCU callbacks. If you hammer the kernel hard enough with Sasha's modified version of trinity, you can end up with the sl*b allocators recursing into themselves via this implicit call_rcu() allocation. This commit therefore exports the debug_init_rcu_head() and debug_rcu_head_free() functions, which permits the allocators to allocated and pre-initialize the debug-objects information, so that there no longer any need for call_rcu() to do that initialization, which in turn prevents the recursion into the memory allocators. Reported-by: Sasha Levin Suggested-by: Thomas Gleixner Signed-off-by: Paul E. McKenney Acked-by: Thomas Gleixner Looks-good-to: Christoph Lameter --- include/linux/rcupdate.h | 10 ++++++++++ kernel/rcu/update.c | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 5a75d19aa661..13bbfbde41b9 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -358,9 +358,19 @@ void wait_rcu_gp(call_rcu_func_t crf); * initialization. */ #ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD +void init_rcu_head(struct rcu_head *head); +void destroy_rcu_head(struct rcu_head *head); void init_rcu_head_on_stack(struct rcu_head *head); void destroy_rcu_head_on_stack(struct rcu_head *head); #else /* !CONFIG_DEBUG_OBJECTS_RCU_HEAD */ +static inline void init_rcu_head(struct rcu_head *head) +{ +} + +static inline void destroy_rcu_head(struct rcu_head *head) +{ +} + static inline void init_rcu_head_on_stack(struct rcu_head *head) { } diff --git a/kernel/rcu/update.c b/kernel/rcu/update.c index a2aeb4df0f60..0fb691e63ce6 100644 --- a/kernel/rcu/update.c +++ b/kernel/rcu/update.c @@ -200,12 +200,12 @@ void wait_rcu_gp(call_rcu_func_t crf) EXPORT_SYMBOL_GPL(wait_rcu_gp); #ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD -static inline void debug_init_rcu_head(struct rcu_head *head) +void init_rcu_head(struct rcu_head *head) { debug_object_init(head, &rcuhead_debug_descr); } -static inline void debug_rcu_head_free(struct rcu_head *head) +void destroy_rcu_head(struct rcu_head *head) { debug_object_free(head, &rcuhead_debug_descr); } -- cgit v1.2.3 From 4a81e8328d3791a4f99bf5b436d050f6dc5ffea3 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 20 Jun 2014 16:49:01 -0700 Subject: rcu: Reduce overhead of cond_resched() checks for RCU Commit ac1bea85781e (Make cond_resched() report RCU quiescent states) fixed a problem where a CPU looping in the kernel with but one runnable task would give RCU CPU stall warnings, even if the in-kernel loop contained cond_resched() calls. Unfortunately, in so doing, it introduced performance regressions in Anton Blanchard's will-it-scale "open1" test. The problem appears to be not so much the increased cond_resched() path length as an increase in the rate at which grace periods complete, which increased per-update grace-period overhead. This commit takes a different approach to fixing this bug, mainly by moving the RCU-visible quiescent state from cond_resched() to rcu_note_context_switch(), and by further reducing the check to a simple non-zero test of a single per-CPU variable. However, this approach requires that the force-quiescent-state processing send resched IPIs to the offending CPUs. These will be sent only once the grace period has reached an age specified by the boot/sysfs parameter rcutree.jiffies_till_sched_qs, or once the grace period reaches an age halfway to the point at which RCU CPU stall warnings will be emitted, whichever comes first. Reported-by: Dave Hansen Signed-off-by: Paul E. McKenney Cc: Andi Kleen Cc: Christoph Lameter Cc: Mike Galbraith Cc: Eric Dumazet Reviewed-by: Josh Triplett [ paulmck: Made rcu_momentary_dyntick_idle() as suggested by the ktest build robot. Also fixed smp_mb() comment as noted by Oleg Nesterov. ] Merge with e552592e (Reduce overhead of cond_resched() checks for RCU) Signed-off-by: Paul E. McKenney --- Documentation/kernel-parameters.txt | 6 ++ include/linux/rcupdate.h | 36 ---------- kernel/rcu/tree.c | 140 ++++++++++++++++++++++++++++-------- kernel/rcu/tree.h | 6 +- kernel/rcu/tree_plugin.h | 2 +- kernel/rcu/update.c | 18 ----- kernel/sched/core.c | 7 +- 7 files changed, 125 insertions(+), 90 deletions(-) (limited to 'include') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 6eaa9cdb7094..910c3829f81d 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2785,6 +2785,12 @@ bytes respectively. Such letter suffixes can also be entirely omitted. leaf rcu_node structure. Useful for very large systems. + rcutree.jiffies_till_sched_qs= [KNL] + Set required age in jiffies for a + given grace period before RCU starts + soliciting quiescent-state help from + rcu_note_context_switch(). + rcutree.jiffies_till_first_fqs= [KNL] Set delay from grace-period initialization to first attempt to force quiescent states. diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 13bbfbde41b9..6a94cc8b1ca0 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -44,7 +44,6 @@ #include #include #include -#include #include extern int rcu_expedited; /* for sysctl */ @@ -299,41 +298,6 @@ static inline void rcu_user_hooks_switch(struct task_struct *prev, bool __rcu_is_watching(void); #endif /* #if defined(CONFIG_DEBUG_LOCK_ALLOC) || defined(CONFIG_RCU_TRACE) || defined(CONFIG_SMP) */ -/* - * Hooks for cond_resched() and friends to avoid RCU CPU stall warnings. - */ - -#define RCU_COND_RESCHED_LIM 256 /* ms vs. 100s of ms. */ -DECLARE_PER_CPU(int, rcu_cond_resched_count); -void rcu_resched(void); - -/* - * Is it time to report RCU quiescent states? - * - * Note unsynchronized access to rcu_cond_resched_count. Yes, we might - * increment some random CPU's count, and possibly also load the result from - * yet another CPU's count. We might even clobber some other CPU's attempt - * to zero its counter. This is all OK because the goal is not precision, - * but rather reasonable amortization of rcu_note_context_switch() overhead - * and extremely high probability of avoiding RCU CPU stall warnings. - * Note that this function has to be preempted in just the wrong place, - * many thousands of times in a row, for anything bad to happen. - */ -static inline bool rcu_should_resched(void) -{ - return raw_cpu_inc_return(rcu_cond_resched_count) >= - RCU_COND_RESCHED_LIM; -} - -/* - * Report quiscent states to RCU if it is time to do so. - */ -static inline void rcu_cond_resched(void) -{ - if (unlikely(rcu_should_resched())) - rcu_resched(); -} - /* * Infrastructure to implement the synchronize_() primitives in * TREE_RCU and rcu_barrier_() primitives in TINY_RCU. diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index f1ba77363fbb..625d0b0cd75a 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -206,6 +206,70 @@ void rcu_bh_qs(int cpu) rdp->passed_quiesce = 1; } +static DEFINE_PER_CPU(int, rcu_sched_qs_mask); + +static DEFINE_PER_CPU(struct rcu_dynticks, rcu_dynticks) = { + .dynticks_nesting = DYNTICK_TASK_EXIT_IDLE, + .dynticks = ATOMIC_INIT(1), +#ifdef CONFIG_NO_HZ_FULL_SYSIDLE + .dynticks_idle_nesting = DYNTICK_TASK_NEST_VALUE, + .dynticks_idle = ATOMIC_INIT(1), +#endif /* #ifdef CONFIG_NO_HZ_FULL_SYSIDLE */ +}; + +/* + * Let the RCU core know that this CPU has gone through the scheduler, + * which is a quiescent state. This is called when the need for a + * quiescent state is urgent, so we burn an atomic operation and full + * memory barriers to let the RCU core know about it, regardless of what + * this CPU might (or might not) do in the near future. + * + * We inform the RCU core by emulating a zero-duration dyntick-idle + * period, which we in turn do by incrementing the ->dynticks counter + * by two. + */ +static void rcu_momentary_dyntick_idle(void) +{ + unsigned long flags; + struct rcu_data *rdp; + struct rcu_dynticks *rdtp; + int resched_mask; + struct rcu_state *rsp; + + local_irq_save(flags); + + /* + * Yes, we can lose flag-setting operations. This is OK, because + * the flag will be set again after some delay. + */ + resched_mask = raw_cpu_read(rcu_sched_qs_mask); + raw_cpu_write(rcu_sched_qs_mask, 0); + + /* Find the flavor that needs a quiescent state. */ + for_each_rcu_flavor(rsp) { + rdp = raw_cpu_ptr(rsp->rda); + if (!(resched_mask & rsp->flavor_mask)) + continue; + smp_mb(); /* rcu_sched_qs_mask before cond_resched_completed. */ + if (ACCESS_ONCE(rdp->mynode->completed) != + ACCESS_ONCE(rdp->cond_resched_completed)) + continue; + + /* + * Pretend to be momentarily idle for the quiescent state. + * This allows the grace-period kthread to record the + * quiescent state, with no need for this CPU to do anything + * further. + */ + rdtp = this_cpu_ptr(&rcu_dynticks); + smp_mb__before_atomic(); /* Earlier stuff before QS. */ + atomic_add(2, &rdtp->dynticks); /* QS. */ + smp_mb__after_atomic(); /* Later stuff after QS. */ + break; + } + local_irq_restore(flags); +} + /* * Note a context switch. This is a quiescent state for RCU-sched, * and requires special handling for preemptible RCU. @@ -216,19 +280,12 @@ void rcu_note_context_switch(int cpu) trace_rcu_utilization(TPS("Start context switch")); rcu_sched_qs(cpu); rcu_preempt_note_context_switch(cpu); + if (unlikely(raw_cpu_read(rcu_sched_qs_mask))) + rcu_momentary_dyntick_idle(); trace_rcu_utilization(TPS("End context switch")); } EXPORT_SYMBOL_GPL(rcu_note_context_switch); -static DEFINE_PER_CPU(struct rcu_dynticks, rcu_dynticks) = { - .dynticks_nesting = DYNTICK_TASK_EXIT_IDLE, - .dynticks = ATOMIC_INIT(1), -#ifdef CONFIG_NO_HZ_FULL_SYSIDLE - .dynticks_idle_nesting = DYNTICK_TASK_NEST_VALUE, - .dynticks_idle = ATOMIC_INIT(1), -#endif /* #ifdef CONFIG_NO_HZ_FULL_SYSIDLE */ -}; - static long blimit = 10; /* Maximum callbacks per rcu_do_batch. */ static long qhimark = 10000; /* If this many pending, ignore blimit. */ static long qlowmark = 100; /* Once only this many pending, use blimit. */ @@ -243,6 +300,13 @@ static ulong jiffies_till_next_fqs = ULONG_MAX; module_param(jiffies_till_first_fqs, ulong, 0644); module_param(jiffies_till_next_fqs, ulong, 0644); +/* + * How long the grace period must be before we start recruiting + * quiescent-state help from rcu_note_context_switch(). + */ +static ulong jiffies_till_sched_qs = HZ / 20; +module_param(jiffies_till_sched_qs, ulong, 0644); + static bool rcu_start_gp_advanced(struct rcu_state *rsp, struct rcu_node *rnp, struct rcu_data *rdp); static void force_qs_rnp(struct rcu_state *rsp, @@ -853,6 +917,7 @@ static int rcu_implicit_dynticks_qs(struct rcu_data *rdp, bool *isidle, unsigned long *maxj) { unsigned int curr; + int *rcrmp; unsigned int snap; curr = (unsigned int)atomic_add_return(0, &rdp->dynticks->dynticks); @@ -893,27 +958,43 @@ static int rcu_implicit_dynticks_qs(struct rcu_data *rdp, } /* - * There is a possibility that a CPU in adaptive-ticks state - * might run in the kernel with the scheduling-clock tick disabled - * for an extended time period. Invoke rcu_kick_nohz_cpu() to - * force the CPU to restart the scheduling-clock tick in this - * CPU is in this state. - */ - rcu_kick_nohz_cpu(rdp->cpu); - - /* - * Alternatively, the CPU might be running in the kernel - * for an extended period of time without a quiescent state. - * Attempt to force the CPU through the scheduler to gain the - * needed quiescent state, but only if the grace period has gone - * on for an uncommonly long time. If there are many stuck CPUs, - * we will beat on the first one until it gets unstuck, then move - * to the next. Only do this for the primary flavor of RCU. + * A CPU running for an extended time within the kernel can + * delay RCU grace periods. When the CPU is in NO_HZ_FULL mode, + * even context-switching back and forth between a pair of + * in-kernel CPU-bound tasks cannot advance grace periods. + * So if the grace period is old enough, make the CPU pay attention. + * Note that the unsynchronized assignments to the per-CPU + * rcu_sched_qs_mask variable are safe. Yes, setting of + * bits can be lost, but they will be set again on the next + * force-quiescent-state pass. So lost bit sets do not result + * in incorrect behavior, merely in a grace period lasting + * a few jiffies longer than it might otherwise. Because + * there are at most four threads involved, and because the + * updates are only once every few jiffies, the probability of + * lossage (and thus of slight grace-period extension) is + * quite low. + * + * Note that if the jiffies_till_sched_qs boot/sysfs parameter + * is set too high, we override with half of the RCU CPU stall + * warning delay. */ - if (rdp->rsp == rcu_state_p && + rcrmp = &per_cpu(rcu_sched_qs_mask, rdp->cpu); + if (ULONG_CMP_GE(jiffies, + rdp->rsp->gp_start + jiffies_till_sched_qs) || ULONG_CMP_GE(jiffies, rdp->rsp->jiffies_resched)) { - rdp->rsp->jiffies_resched += 5; - resched_cpu(rdp->cpu); + if (!(ACCESS_ONCE(*rcrmp) & rdp->rsp->flavor_mask)) { + ACCESS_ONCE(rdp->cond_resched_completed) = + ACCESS_ONCE(rdp->mynode->completed); + smp_mb(); /* ->cond_resched_completed before *rcrmp. */ + ACCESS_ONCE(*rcrmp) = + ACCESS_ONCE(*rcrmp) + rdp->rsp->flavor_mask; + resched_cpu(rdp->cpu); /* Force CPU into scheduler. */ + rdp->rsp->jiffies_resched += 5; /* Enable beating. */ + } else if (ULONG_CMP_GE(jiffies, rdp->rsp->jiffies_resched)) { + /* Time to beat on that CPU again! */ + resched_cpu(rdp->cpu); /* Force CPU into scheduler. */ + rdp->rsp->jiffies_resched += 5; /* Re-enable beating. */ + } } return 0; @@ -3491,6 +3572,7 @@ static void __init rcu_init_one(struct rcu_state *rsp, "rcu_node_fqs_1", "rcu_node_fqs_2", "rcu_node_fqs_3" }; /* Match MAX_RCU_LVLS */ + static u8 fl_mask = 0x1; int cpustride = 1; int i; int j; @@ -3509,6 +3591,8 @@ static void __init rcu_init_one(struct rcu_state *rsp, for (i = 1; i < rcu_num_lvls; i++) rsp->level[i] = rsp->level[i - 1] + rsp->levelcnt[i - 1]; rcu_init_levelspread(rsp); + rsp->flavor_mask = fl_mask; + fl_mask <<= 1; /* Initialize the elements themselves, starting from the leaves. */ diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h index bf2c1e669691..0f69a79c5b7d 100644 --- a/kernel/rcu/tree.h +++ b/kernel/rcu/tree.h @@ -307,6 +307,9 @@ struct rcu_data { /* 4) reasons this CPU needed to be kicked by force_quiescent_state */ unsigned long dynticks_fqs; /* Kicked due to dynticks idle. */ unsigned long offline_fqs; /* Kicked due to being offline. */ + unsigned long cond_resched_completed; + /* Grace period that needs help */ + /* from cond_resched(). */ /* 5) __rcu_pending() statistics. */ unsigned long n_rcu_pending; /* rcu_pending() calls since boot. */ @@ -392,6 +395,7 @@ struct rcu_state { struct rcu_node *level[RCU_NUM_LVLS]; /* Hierarchy levels. */ u32 levelcnt[MAX_RCU_LVLS + 1]; /* # nodes in each level. */ u8 levelspread[RCU_NUM_LVLS]; /* kids/node in each level. */ + u8 flavor_mask; /* bit in flavor mask. */ struct rcu_data __percpu *rda; /* pointer of percu rcu_data. */ void (*call)(struct rcu_head *head, /* call_rcu() flavor. */ void (*func)(struct rcu_head *head)); @@ -563,7 +567,7 @@ static bool rcu_nocb_need_deferred_wakeup(struct rcu_data *rdp); static void do_nocb_deferred_wakeup(struct rcu_data *rdp); static void rcu_boot_init_nocb_percpu_data(struct rcu_data *rdp); static void rcu_spawn_nocb_kthreads(struct rcu_state *rsp); -static void rcu_kick_nohz_cpu(int cpu); +static void __maybe_unused rcu_kick_nohz_cpu(int cpu); static bool init_nocb_callback_list(struct rcu_data *rdp); static void rcu_sysidle_enter(struct rcu_dynticks *rdtp, int irq); static void rcu_sysidle_exit(struct rcu_dynticks *rdtp, int irq); diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index cbc2c45265e2..02ac0fb186b8 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -2404,7 +2404,7 @@ static bool init_nocb_callback_list(struct rcu_data *rdp) * if an adaptive-ticks CPU is failing to respond to the current grace * period and has not be idle from an RCU perspective, kick it. */ -static void rcu_kick_nohz_cpu(int cpu) +static void __maybe_unused rcu_kick_nohz_cpu(int cpu) { #ifdef CONFIG_NO_HZ_FULL if (tick_nohz_full_cpu(cpu)) diff --git a/kernel/rcu/update.c b/kernel/rcu/update.c index 0fb691e63ce6..bc7883570530 100644 --- a/kernel/rcu/update.c +++ b/kernel/rcu/update.c @@ -350,21 +350,3 @@ static int __init check_cpu_stall_init(void) early_initcall(check_cpu_stall_init); #endif /* #ifdef CONFIG_RCU_STALL_COMMON */ - -/* - * Hooks for cond_resched() and friends to avoid RCU CPU stall warnings. - */ - -DEFINE_PER_CPU(int, rcu_cond_resched_count); - -/* - * Report a set of RCU quiescent states, for use by cond_resched() - * and friends. Out of line due to being called infrequently. - */ -void rcu_resched(void) -{ - preempt_disable(); - __this_cpu_write(rcu_cond_resched_count, 0); - rcu_note_context_switch(smp_processor_id()); - preempt_enable(); -} diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 3bdf01b494fe..bc1638b33449 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -4147,7 +4147,6 @@ static void __cond_resched(void) int __sched _cond_resched(void) { - rcu_cond_resched(); if (should_resched()) { __cond_resched(); return 1; @@ -4166,18 +4165,15 @@ EXPORT_SYMBOL(_cond_resched); */ int __cond_resched_lock(spinlock_t *lock) { - bool need_rcu_resched = rcu_should_resched(); int resched = should_resched(); int ret = 0; lockdep_assert_held(lock); - if (spin_needbreak(lock) || resched || need_rcu_resched) { + if (spin_needbreak(lock) || resched) { spin_unlock(lock); if (resched) __cond_resched(); - else if (unlikely(need_rcu_resched)) - rcu_resched(); else cpu_relax(); ret = 1; @@ -4191,7 +4187,6 @@ int __sched __cond_resched_softirq(void) { BUG_ON(!in_softirq()); - rcu_cond_resched(); /* BH disabled OK, just recording QSes. */ if (should_resched()) { local_bh_enable(); __cond_resched(); -- cgit v1.2.3 From e0f31d8498676fda36289603a054d0d490aa2679 Mon Sep 17 00:00:00 2001 From: Govindarajulu Varadarajan <_govind@gmx.com> Date: Mon, 23 Jun 2014 16:07:58 +0530 Subject: flow_keys: Record IP layer protocol in skb_flow_dissect() skb_flow_dissect() dissects only transport header type in ip_proto. It dose not give any information about IPv4 or IPv6. This patch adds new member, n_proto, to struct flow_keys. Which records the IP layer type. i.e IPv4 or IPv6. This can be used in netdev->ndo_rx_flow_steer driver function to dissect flow. Adding new member to flow_keys increases the struct size by around 4 bytes. This causes BUILD_BUG_ON(sizeof(qcb->data) < sz); to fail in qdisc_cb_private_validate() So increase data size by 4 Signed-off-by: Govindarajulu Varadarajan <_govind@gmx.com> Signed-off-by: David S. Miller --- include/net/flow_keys.h | 14 ++++++++++++++ include/net/sch_generic.h | 2 +- net/core/flow_dissector.c | 1 + 3 files changed, 16 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/flow_keys.h b/include/net/flow_keys.h index 7e64bd8bbda9..fbefdca5e283 100644 --- a/include/net/flow_keys.h +++ b/include/net/flow_keys.h @@ -1,6 +1,19 @@ #ifndef _NET_FLOW_KEYS_H #define _NET_FLOW_KEYS_H +/* struct flow_keys: + * @src: source ip address in case of IPv4 + * For IPv6 it contains 32bit hash of src address + * @dst: destination ip address in case of IPv4 + * For IPv6 it contains 32bit hash of dst address + * @ports: port numbers of Transport header + * port16[0]: src port number + * port16[1]: dst port number + * @thoff: Transport header offset + * @n_proto: Network header protocol (eg. IPv4/IPv6) + * @ip_proto: Transport header protocol (eg. TCP/UDP) + * All the members, except thoff, are in network byte order. + */ struct flow_keys { /* (src,dst) must be grouped, in the same way than in IP header */ __be32 src; @@ -10,6 +23,7 @@ struct flow_keys { __be16 port16[2]; }; u16 thoff; + u16 n_proto; u8 ip_proto; }; diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 624f9857c83e..a3cfb8ebeb53 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -231,7 +231,7 @@ struct qdisc_skb_cb { unsigned int pkt_len; u16 slave_dev_queue_mapping; u16 _pad; - unsigned char data[20]; + unsigned char data[24]; }; static inline void qdisc_cb_private_validate(const struct sk_buff *skb, int sz) diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 107ed12a5323..c2b53c1b21d2 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -175,6 +175,7 @@ ipv6: break; } + flow->n_proto = proto; flow->ip_proto = ip_proto; flow->ports = skb_flow_get_ports(skb, nhoff, ip_proto); flow->thoff = (u16) nhoff; -- cgit v1.2.3 From b3acc56bfe1287c6b666e80edc70b89eea2a1a80 Mon Sep 17 00:00:00 2001 From: Petr Tesarik Date: Mon, 23 Jun 2014 13:22:03 -0700 Subject: kexec: save PG_head_mask in VMCOREINFO To allow filtering of huge pages, makedumpfile must be able to identify them in the dump. This can be done by checking the appropriate page flag, so communicate its value to makedumpfile through the VMCOREINFO interface. There's only one small catch. Depending on how many page flags are available on a given architecture, this bit can be called PG_head or PG_compound. I sent a similar patch back in 2012, but Eric Biederman did not like using an #ifdef. So, this time I'm adding a common symbol (PG_head_mask) instead. See https://lkml.org/lkml/2012/11/28/91 for the previous version. Signed-off-by: Petr Tesarik Acked-by: Vivek Goyal Cc: Eric Biederman Cc: Paul Mackerras Cc: Fengguang Wu Cc: Benjamin Herrenschmidt Cc: Shaohua Li Cc: Alexey Kardashevskiy Cc: Sasha Levin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/page-flags.h | 3 +++ kernel/kexec.c | 1 + 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index 3c545b48aeab..8304959ad336 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -360,6 +360,9 @@ static inline void ClearPageCompound(struct page *page) ClearPageHead(page); } #endif + +#define PG_head_mask ((1L << PG_head)) + #else /* * Reduce page flag use as much as possible by overlapping diff --git a/kernel/kexec.c b/kernel/kexec.c index 6748688813d0..369f41a94124 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -1617,6 +1617,7 @@ static int __init crash_save_vmcoreinfo_init(void) #ifdef CONFIG_MEMORY_FAILURE VMCOREINFO_NUMBER(PG_hwpoison); #endif + VMCOREINFO_NUMBER(PG_head_mask); VMCOREINFO_NUMBER(PAGE_BUDDY_MAPCOUNT_VALUE); arch_crash_save_vmcoreinfo(); -- cgit v1.2.3 From f3aca3d09525f87731ba6b892c9b010570bc54b4 Mon Sep 17 00:00:00 2001 From: Aaron Tomlin Date: Mon, 23 Jun 2014 13:22:05 -0700 Subject: nmi: provide the option to issue an NMI back trace to every cpu but current Sometimes it is preferred not to use the trigger_all_cpu_backtrace() routine when one wants to avoid capturing a back trace for current. For instance if one was previously captured recently. This patch provides a new routine namely trigger_allbutself_cpu_backtrace() which offers the flexibility to issue an NMI to every cpu but current and capture a back trace accordingly. Patch x86 and sparc to support new routine. [dzickus@redhat.com: add stub in #else clause] [dzickus@redhat.com: don't print message in single processor case, wrap with get/put_cpu based on Oleg's suggestion] [sfr@canb.auug.org.au: undo C99ism] Signed-off-by: Aaron Tomlin Signed-off-by: Don Zickus Acked-by: David S. Miller Cc: Mateusz Guzik Cc: Oleg Nesterov Signed-off-by: Stephen Rothwell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/sparc/include/asm/irq_64.h | 2 +- arch/sparc/kernel/process_64.c | 18 ++++++++++++------ arch/x86/include/asm/irq.h | 2 +- arch/x86/kernel/apic/hw_nmi.c | 18 ++++++++++++++---- include/linux/nmi.h | 11 ++++++++++- 5 files changed, 38 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/arch/sparc/include/asm/irq_64.h b/arch/sparc/include/asm/irq_64.h index 375cffcf7dbd..91d219381306 100644 --- a/arch/sparc/include/asm/irq_64.h +++ b/arch/sparc/include/asm/irq_64.h @@ -89,7 +89,7 @@ static inline unsigned long get_softint(void) return retval; } -void arch_trigger_all_cpu_backtrace(void); +void arch_trigger_all_cpu_backtrace(bool); #define arch_trigger_all_cpu_backtrace arch_trigger_all_cpu_backtrace extern void *hardirq_stack[NR_CPUS]; diff --git a/arch/sparc/kernel/process_64.c b/arch/sparc/kernel/process_64.c index b2988f25e230..027e09986194 100644 --- a/arch/sparc/kernel/process_64.c +++ b/arch/sparc/kernel/process_64.c @@ -239,7 +239,7 @@ static void __global_reg_poll(struct global_reg_snapshot *gp) } } -void arch_trigger_all_cpu_backtrace(void) +void arch_trigger_all_cpu_backtrace(bool include_self) { struct thread_info *tp = current_thread_info(); struct pt_regs *regs = get_irq_regs(); @@ -251,16 +251,22 @@ void arch_trigger_all_cpu_backtrace(void) spin_lock_irqsave(&global_cpu_snapshot_lock, flags); - memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot)); - this_cpu = raw_smp_processor_id(); - __global_reg_self(tp, regs, this_cpu); + memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot)); + + if (include_self) + __global_reg_self(tp, regs, this_cpu); smp_fetch_global_regs(); for_each_online_cpu(cpu) { - struct global_reg_snapshot *gp = &global_cpu_snapshot[cpu].reg; + struct global_reg_snapshot *gp; + + if (!include_self && cpu == this_cpu) + continue; + + gp = &global_cpu_snapshot[cpu].reg; __global_reg_poll(gp); @@ -292,7 +298,7 @@ void arch_trigger_all_cpu_backtrace(void) static void sysrq_handle_globreg(int key) { - arch_trigger_all_cpu_backtrace(); + arch_trigger_all_cpu_backtrace(true); } static struct sysrq_key_op sparc_globalreg_op = { diff --git a/arch/x86/include/asm/irq.h b/arch/x86/include/asm/irq.h index cb6cfcd034cf..a80cbb88ea91 100644 --- a/arch/x86/include/asm/irq.h +++ b/arch/x86/include/asm/irq.h @@ -43,7 +43,7 @@ extern int vector_used_by_percpu_irq(unsigned int vector); extern void init_ISA_irqs(void); #ifdef CONFIG_X86_LOCAL_APIC -void arch_trigger_all_cpu_backtrace(void); +void arch_trigger_all_cpu_backtrace(bool); #define arch_trigger_all_cpu_backtrace arch_trigger_all_cpu_backtrace #endif diff --git a/arch/x86/kernel/apic/hw_nmi.c b/arch/x86/kernel/apic/hw_nmi.c index c3fcb5de5083..6a1e71bde323 100644 --- a/arch/x86/kernel/apic/hw_nmi.c +++ b/arch/x86/kernel/apic/hw_nmi.c @@ -33,31 +33,41 @@ static DECLARE_BITMAP(backtrace_mask, NR_CPUS) __read_mostly; /* "in progress" flag of arch_trigger_all_cpu_backtrace */ static unsigned long backtrace_flag; -void arch_trigger_all_cpu_backtrace(void) +void arch_trigger_all_cpu_backtrace(bool include_self) { int i; + int cpu = get_cpu(); - if (test_and_set_bit(0, &backtrace_flag)) + if (test_and_set_bit(0, &backtrace_flag)) { /* * If there is already a trigger_all_cpu_backtrace() in progress * (backtrace_flag == 1), don't output double cpu dump infos. */ + put_cpu(); return; + } cpumask_copy(to_cpumask(backtrace_mask), cpu_online_mask); + if (!include_self) + cpumask_clear_cpu(cpu, to_cpumask(backtrace_mask)); - printk(KERN_INFO "sending NMI to all CPUs:\n"); - apic->send_IPI_all(NMI_VECTOR); + if (!cpumask_empty(to_cpumask(backtrace_mask))) { + pr_info("sending NMI to %s CPUs:\n", + (include_self ? "all" : "other")); + apic->send_IPI_mask(to_cpumask(backtrace_mask), NMI_VECTOR); + } /* Wait for up to 10 seconds for all CPUs to do the backtrace */ for (i = 0; i < 10 * 1000; i++) { if (cpumask_empty(to_cpumask(backtrace_mask))) break; mdelay(1); + touch_softlockup_watchdog(); } clear_bit(0, &backtrace_flag); smp_mb__after_atomic(); + put_cpu(); } static int diff --git a/include/linux/nmi.h b/include/linux/nmi.h index 6a45fb583ff1..a17ab6398d7c 100644 --- a/include/linux/nmi.h +++ b/include/linux/nmi.h @@ -32,15 +32,24 @@ static inline void touch_nmi_watchdog(void) #ifdef arch_trigger_all_cpu_backtrace static inline bool trigger_all_cpu_backtrace(void) { - arch_trigger_all_cpu_backtrace(); + arch_trigger_all_cpu_backtrace(true); return true; } +static inline bool trigger_allbutself_cpu_backtrace(void) +{ + arch_trigger_all_cpu_backtrace(false); + return true; +} #else static inline bool trigger_all_cpu_backtrace(void) { return false; } +static inline bool trigger_allbutself_cpu_backtrace(void) +{ + return false; +} #endif #ifdef CONFIG_LOCKUP_DETECTOR -- cgit v1.2.3 From ed235875e2ca983197831337a986f0517074e1a0 Mon Sep 17 00:00:00 2001 From: Aaron Tomlin Date: Mon, 23 Jun 2014 13:22:05 -0700 Subject: kernel/watchdog.c: print traces for all cpus on lockup detection A 'softlockup' is defined as a bug that causes the kernel to loop in kernel mode for more than a predefined period to time, without giving other tasks a chance to run. Currently, upon detection of this condition by the per-cpu watchdog task, debug information (including a stack trace) is sent to the system log. On some occasions, we have observed that the "victim" rather than the actual "culprit" (i.e. the owner/holder of the contended resource) is reported to the user. Often this information has proven to be insufficient to assist debugging efforts. To avoid loss of useful debug information, for architectures which support NMI, this patch makes it possible to improve soft lockup reporting. This is accomplished by issuing an NMI to each cpu to obtain a stack trace. If NMI is not supported we just revert back to the old method. A sysctl and boot-time parameter is available to toggle this feature. [dzickus@redhat.com: add CONFIG_SMP in certain areas] [akpm@linux-foundation.org: additional CONFIG_SMP=n optimisations] [mq@suse.cz: fix warning] Signed-off-by: Aaron Tomlin Signed-off-by: Don Zickus Cc: David S. Miller Cc: Mateusz Guzik Cc: Oleg Nesterov Signed-off-by: Jan Moskyto Matejka Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/kernel-parameters.txt | 5 +++++ Documentation/sysctl/kernel.txt | 17 ++++++++++++++++ include/linux/nmi.h | 1 + kernel/sysctl.c | 11 +++++++++++ kernel/watchdog.c | 39 +++++++++++++++++++++++++++++++++++++ 5 files changed, 73 insertions(+) (limited to 'include') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 884904975d0b..c1b9aa8c5a52 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -3130,6 +3130,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted. [KNL] Should the soft-lockup detector generate panics. Format: + softlockup_all_cpu_backtrace= + [KNL] Should the soft-lockup detector generate + backtraces on all cpus. + Format: + sonypi.*= [HW] Sony Programmable I/O Control Device driver See Documentation/laptops/sonypi.txt diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index 708bb7f1b7e0..c14374e71775 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -75,6 +75,7 @@ show up in /proc/sys/kernel: - shmall - shmmax [ sysv ipc ] - shmmni +- softlockup_all_cpu_backtrace - stop-a [ SPARC only ] - sysrq ==> Documentation/sysrq.txt - sysctl_writes_strict @@ -783,6 +784,22 @@ via the /proc/sys interface: ============================================================== +softlockup_all_cpu_backtrace: + +This value controls the soft lockup detector thread's behavior +when a soft lockup condition is detected as to whether or not +to gather further debug information. If enabled, each cpu will +be issued an NMI and instructed to capture stack trace. + +This feature is only applicable for architectures which support +NMI. + +0: do nothing. This is the default behavior. + +1: on detection capture more debug information. + +============================================================== + tainted: Non-zero if the kernel has been tainted. Numeric values, which diff --git a/include/linux/nmi.h b/include/linux/nmi.h index a17ab6398d7c..447775ee2c4b 100644 --- a/include/linux/nmi.h +++ b/include/linux/nmi.h @@ -57,6 +57,7 @@ int hw_nmi_is_cpu_stuck(struct pt_regs *); u64 hw_nmi_get_sample_period(int watchdog_thresh); extern int watchdog_user_enabled; extern int watchdog_thresh; +extern int sysctl_softlockup_all_cpu_backtrace; struct ctl_table; extern int proc_dowatchdog(struct ctl_table *, int , void __user *, size_t *, loff_t *); diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 075d1903138f..75b22e22a72c 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -860,6 +860,17 @@ static struct ctl_table kern_table[] = { .extra1 = &zero, .extra2 = &one, }, +#ifdef CONFIG_SMP + { + .procname = "softlockup_all_cpu_backtrace", + .data = &sysctl_softlockup_all_cpu_backtrace, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &one, + }, +#endif /* CONFIG_SMP */ { .procname = "nmi_watchdog", .data = &watchdog_user_enabled, diff --git a/kernel/watchdog.c b/kernel/watchdog.c index 30e482240dae..c3319bd1b040 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c @@ -31,6 +31,12 @@ int watchdog_user_enabled = 1; int __read_mostly watchdog_thresh = 10; +#ifdef CONFIG_SMP +int __read_mostly sysctl_softlockup_all_cpu_backtrace; +#else +#define sysctl_softlockup_all_cpu_backtrace 0 +#endif + static int __read_mostly watchdog_running; static u64 __read_mostly sample_period; @@ -47,6 +53,7 @@ static DEFINE_PER_CPU(bool, watchdog_nmi_touch); static DEFINE_PER_CPU(unsigned long, hrtimer_interrupts_saved); static DEFINE_PER_CPU(struct perf_event *, watchdog_ev); #endif +static unsigned long soft_lockup_nmi_warn; /* boot commands */ /* @@ -95,6 +102,15 @@ static int __init nosoftlockup_setup(char *str) } __setup("nosoftlockup", nosoftlockup_setup); /* */ +#ifdef CONFIG_SMP +static int __init softlockup_all_cpu_backtrace_setup(char *str) +{ + sysctl_softlockup_all_cpu_backtrace = + !!simple_strtol(str, NULL, 0); + return 1; +} +__setup("softlockup_all_cpu_backtrace=", softlockup_all_cpu_backtrace_setup); +#endif /* * Hard-lockup warnings should be triggered after just a few seconds. Soft- @@ -271,6 +287,7 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer) unsigned long touch_ts = __this_cpu_read(watchdog_touch_ts); struct pt_regs *regs = get_irq_regs(); int duration; + int softlockup_all_cpu_backtrace = sysctl_softlockup_all_cpu_backtrace; /* kick the hardlockup detector */ watchdog_interrupt_count(); @@ -317,6 +334,17 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer) if (__this_cpu_read(soft_watchdog_warn) == true) return HRTIMER_RESTART; + if (softlockup_all_cpu_backtrace) { + /* Prevent multiple soft-lockup reports if one cpu is already + * engaged in dumping cpu back traces + */ + if (test_and_set_bit(0, &soft_lockup_nmi_warn)) { + /* Someone else will report us. Let's give up */ + __this_cpu_write(soft_watchdog_warn, true); + return HRTIMER_RESTART; + } + } + printk(KERN_EMERG "BUG: soft lockup - CPU#%d stuck for %us! [%s:%d]\n", smp_processor_id(), duration, current->comm, task_pid_nr(current)); @@ -327,6 +355,17 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer) else dump_stack(); + if (softlockup_all_cpu_backtrace) { + /* Avoid generating two back traces for current + * given that one is already made above + */ + trigger_allbutself_cpu_backtrace(); + + clear_bit(0, &soft_lockup_nmi_warn); + /* Barrier to sync with other cpus */ + smp_mb__after_atomic(); + } + if (softlockup_panic) panic("softlockup: hung tasks"); __this_cpu_write(soft_watchdog_warn, true); -- cgit v1.2.3 From fb7023e0e248a33cb00d0a9cdce0bcedaa1ad284 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Tue, 10 Jun 2014 10:09:52 -0700 Subject: drm/i915: BDW: Adding Reserved PCI IDs. These PCI IDs are reserved on BSpec and can be used at any time in the future. So let's add this now in order to avoid issues that we already faced on previous platforms, like finding out about new ids when user reported accelaration weren't enabled. Cc: stable@vger.kernel.org Reviewed-by: Ben Widawsky Signed-off-by: Rodrigo Vivi Signed-off-by: Jani Nikula --- include/drm/i915_pciids.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/drm/i915_pciids.h b/include/drm/i915_pciids.h index 0572035673f3..a70d45647898 100644 --- a/include/drm/i915_pciids.h +++ b/include/drm/i915_pciids.h @@ -237,13 +237,21 @@ #define INTEL_BDW_GT3D_IDS(info) \ _INTEL_BDW_D_IDS(3, info) +#define INTEL_BDW_RSVDM_IDS(info) \ + _INTEL_BDW_M_IDS(4, info) + +#define INTEL_BDW_RSVDD_IDS(info) \ + _INTEL_BDW_D_IDS(4, info) + #define INTEL_BDW_M_IDS(info) \ INTEL_BDW_GT12M_IDS(info), \ - INTEL_BDW_GT3M_IDS(info) + INTEL_BDW_GT3M_IDS(info), \ + INTEL_BDW_RSVDM_IDS(info) #define INTEL_BDW_D_IDS(info) \ INTEL_BDW_GT12D_IDS(info), \ - INTEL_BDW_GT3D_IDS(info) + INTEL_BDW_GT3D_IDS(info), \ + INTEL_BDW_RSVDD_IDS(info) #define INTEL_CHV_IDS(info) \ INTEL_VGA_DEVICE(0x22b0, info), \ -- cgit v1.2.3 From 3a4b0eda8e4b27e6aca86f9f4d327c1070815e30 Mon Sep 17 00:00:00 2001 From: Gu Zheng Date: Tue, 24 Jun 2014 18:10:26 +0800 Subject: bio: remove unused macro bip_vec_idx() Macro bip_vec_idx() was used by bio integrity originally, but no longer used now. So remove it. Signed-off-by: Gu Zheng Signed-off-by: Jens Axboe --- include/linux/bio.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'include') diff --git a/include/linux/bio.h b/include/linux/bio.h index 5a645769f020..f91decbca96b 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -644,10 +644,6 @@ struct biovec_slab { #if defined(CONFIG_BLK_DEV_INTEGRITY) - - -#define bip_vec_idx(bip, idx) (&(bip->bip_vec[(idx)])) - #define bip_for_each_vec(bvl, bip, iter) \ for_each_bvec(bvl, (bip)->bip_vec, iter, (bip)->bip_iter) -- cgit v1.2.3 From 66cb45aa41315d1d9972cada354fbdf7870d7714 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 24 Jun 2014 16:22:24 -0600 Subject: block: add support for limiting gaps in SG lists Another restriction inherited for NVMe - those devices don't support SG lists that have "gaps" in them. Gaps refers to cases where the previous SG entry doesn't end on a page boundary. For NVMe, all SG entries must start at offset 0 (except the first) and end on a page boundary (except the last). Signed-off-by: Jens Axboe --- block/bio.c | 8 ++++++++ block/blk-merge.c | 10 ++++++++++ include/linux/bio.h | 9 +++++++++ include/linux/blkdev.h | 1 + 4 files changed, 28 insertions(+) (limited to 'include') diff --git a/block/bio.c b/block/bio.c index 8c2e55e39a1b..0ec61c9e536c 100644 --- a/block/bio.c +++ b/block/bio.c @@ -746,6 +746,14 @@ static int __bio_add_page(struct request_queue *q, struct bio *bio, struct page goto done; } + + /* + * If the queue doesn't support SG gaps and adding this + * offset would create a gap, disallow it. + */ + if (q->queue_flags & (1 << QUEUE_FLAG_SG_GAPS) && + bvec_gap_to_prev(prev, offset)) + return 0; } if (bio->bi_vcnt >= bio->bi_max_vecs) diff --git a/block/blk-merge.c b/block/blk-merge.c index b3bf0df0f4c2..54535831f1e1 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -568,6 +568,8 @@ int blk_attempt_req_merge(struct request_queue *q, struct request *rq, bool blk_rq_merge_ok(struct request *rq, struct bio *bio) { + struct request_queue *q = rq->q; + if (!rq_mergeable(rq) || !bio_mergeable(bio)) return false; @@ -591,6 +593,14 @@ bool blk_rq_merge_ok(struct request *rq, struct bio *bio) !blk_write_same_mergeable(rq->bio, bio)) return false; + if (q->queue_flags & (1 << QUEUE_FLAG_SG_GAPS)) { + struct bio_vec *bprev; + + bprev = &rq->biotail->bi_io_vec[bio->bi_vcnt - 1]; + if (bvec_gap_to_prev(bprev, bio->bi_io_vec[0].bv_offset)) + return false; + } + return true; } diff --git a/include/linux/bio.h b/include/linux/bio.h index f91decbca96b..d2633ee099d9 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -186,6 +186,15 @@ static inline void *bio_data(struct bio *bio) #define BIOVEC_SEG_BOUNDARY(q, b1, b2) \ __BIO_SEG_BOUNDARY(bvec_to_phys((b1)), bvec_to_phys((b2)) + (b2)->bv_len, queue_segment_boundary((q))) +/* + * Check if adding a bio_vec after bprv with offset would create a gap in + * the SG list. Most drivers don't care about this, but some do. + */ +static inline bool bvec_gap_to_prev(struct bio_vec *bprv, unsigned int offset) +{ + return offset || ((bprv->bv_offset + bprv->bv_len) & (PAGE_SIZE - 1)); +} + #define bio_io_error(bio) bio_endio((bio), -EIO) /* diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 713f8b62b435..8699bcf5f099 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -512,6 +512,7 @@ struct request_queue { #define QUEUE_FLAG_DEAD 19 /* queue tear-down finished */ #define QUEUE_FLAG_INIT_DONE 20 /* queue is initialized */ #define QUEUE_FLAG_NO_SG_MERGE 21 /* don't attempt to merge SG segments*/ +#define QUEUE_FLAG_SG_GAPS 22 /* queue doesn't support SG gaps */ #define QUEUE_FLAG_DEFAULT ((1 << QUEUE_FLAG_IO_STAT) | \ (1 << QUEUE_FLAG_STACKABLE) | \ -- cgit v1.2.3 From 1e7f3a485922211b6e4a082ebc6bf05810b0b6ea Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Mon, 9 Jun 2014 11:48:33 -0400 Subject: nfs: move nfs_pgio_data and remove nfs_rw_header nfs_rw_header was used to allocate an nfs_pgio_header along with an nfs_pgio_data, because a _header would need at least one _data. Now there is only ever one nfs_pgio_data for each nfs_pgio_header -- move it to nfs_pgio_header and get rid of nfs_rw_header. Reviewed-by: Christoph Hellwig Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust --- fs/nfs/direct.c | 8 ++--- fs/nfs/internal.h | 6 ++-- fs/nfs/pagelist.c | 94 +++++++++++++++--------------------------------- fs/nfs/pnfs.c | 24 ++++++------- fs/nfs/read.c | 6 ++-- fs/nfs/write.c | 10 +++--- include/linux/nfs_page.h | 4 +-- include/linux/nfs_xdr.h | 38 +++++++++----------- 8 files changed, 71 insertions(+), 119 deletions(-) (limited to 'include') diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 8f98138cbc43..179de67ca907 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -148,8 +148,8 @@ static void nfs_direct_set_hdr_verf(struct nfs_direct_req *dreq, { struct nfs_writeverf *verfp; - verfp = nfs_direct_select_verf(dreq, hdr->data->ds_clp, - hdr->data->ds_idx); + verfp = nfs_direct_select_verf(dreq, hdr->data.ds_clp, + hdr->data.ds_idx); WARN_ON_ONCE(verfp->committed >= 0); memcpy(verfp, &hdr->verf, sizeof(struct nfs_writeverf)); WARN_ON_ONCE(verfp->committed < 0); @@ -169,8 +169,8 @@ static int nfs_direct_set_or_cmp_hdr_verf(struct nfs_direct_req *dreq, { struct nfs_writeverf *verfp; - verfp = nfs_direct_select_verf(dreq, hdr->data->ds_clp, - hdr->data->ds_idx); + verfp = nfs_direct_select_verf(dreq, hdr->data.ds_clp, + hdr->data.ds_idx); if (verfp->committed < 0) { nfs_direct_set_hdr_verf(dreq, hdr); return 0; diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 82ddbf46660e..5cda049c8f9b 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -238,9 +238,9 @@ void nfs_set_pgio_error(struct nfs_pgio_header *hdr, int error, loff_t pos); int nfs_iocounter_wait(struct nfs_io_counter *c); extern const struct nfs_pageio_ops nfs_pgio_rw_ops; -struct nfs_rw_header *nfs_rw_header_alloc(const struct nfs_rw_ops *); -void nfs_rw_header_free(struct nfs_pgio_header *); -void nfs_pgio_data_release(struct nfs_pgio_data *); +struct nfs_pgio_header *nfs_pgio_header_alloc(const struct nfs_rw_ops *); +void nfs_pgio_header_free(struct nfs_pgio_header *); +void nfs_pgio_data_destroy(struct nfs_pgio_data *); int nfs_generic_pgio(struct nfs_pageio_descriptor *, struct nfs_pgio_header *); int nfs_initiate_pgio(struct rpc_clnt *, struct nfs_pgio_data *, const struct rpc_call_ops *, int, int); diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index b6ee3a6ee96d..e4cde476562f 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -452,95 +452,61 @@ size_t nfs_generic_pg_test(struct nfs_pageio_descriptor *desc, } EXPORT_SYMBOL_GPL(nfs_generic_pg_test); -static inline struct nfs_rw_header *NFS_RW_HEADER(struct nfs_pgio_header *hdr) +struct nfs_pgio_header *nfs_pgio_header_alloc(const struct nfs_rw_ops *ops) { - return container_of(hdr, struct nfs_rw_header, header); -} - -/** - * nfs_rw_header_alloc - Allocate a header for a read or write - * @ops: Read or write function vector - */ -struct nfs_rw_header *nfs_rw_header_alloc(const struct nfs_rw_ops *ops) -{ - struct nfs_rw_header *header = ops->rw_alloc_header(); - - if (header) { - struct nfs_pgio_header *hdr = &header->header; + struct nfs_pgio_header *hdr = ops->rw_alloc_header(); + if (hdr) { INIT_LIST_HEAD(&hdr->pages); spin_lock_init(&hdr->lock); atomic_set(&hdr->refcnt, 0); hdr->rw_ops = ops; } - return header; + return hdr; } -EXPORT_SYMBOL_GPL(nfs_rw_header_alloc); +EXPORT_SYMBOL_GPL(nfs_pgio_header_alloc); /* - * nfs_rw_header_free - Free a read or write header + * nfs_pgio_header_free - Free a read or write header * @hdr: The header to free */ -void nfs_rw_header_free(struct nfs_pgio_header *hdr) +void nfs_pgio_header_free(struct nfs_pgio_header *hdr) { - hdr->rw_ops->rw_free_header(NFS_RW_HEADER(hdr)); + hdr->rw_ops->rw_free_header(hdr); } -EXPORT_SYMBOL_GPL(nfs_rw_header_free); +EXPORT_SYMBOL_GPL(nfs_pgio_header_free); /** * nfs_pgio_data_alloc - Allocate pageio data * @hdr: The header making a request * @pagecount: Number of pages to create */ -static struct nfs_pgio_data *nfs_pgio_data_alloc(struct nfs_pgio_header *hdr, - unsigned int pagecount) +static bool nfs_pgio_data_init(struct nfs_pgio_header *hdr, + unsigned int pagecount) { - struct nfs_pgio_data *data, *prealloc; - - prealloc = &NFS_RW_HEADER(hdr)->rpc_data; - if (prealloc->header == NULL) - data = prealloc; - else - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) - goto out; - - if (nfs_pgarray_set(&data->pages, pagecount)) { - data->header = hdr; + if (nfs_pgarray_set(&hdr->data.pages, pagecount)) { + hdr->data.header = hdr; atomic_inc(&hdr->refcnt); - } else { - if (data != prealloc) - kfree(data); - data = NULL; + return true; } -out: - return data; + return false; } /** - * nfs_pgio_data_release - Properly free pageio data - * @data: The data to release + * nfs_pgio_data_destroy - Properly free pageio data + * @data: The data to destroy */ -void nfs_pgio_data_release(struct nfs_pgio_data *data) +void nfs_pgio_data_destroy(struct nfs_pgio_data *data) { struct nfs_pgio_header *hdr = data->header; - struct nfs_rw_header *pageio_header = NFS_RW_HEADER(hdr); put_nfs_open_context(data->args.context); if (data->pages.pagevec != data->pages.page_array) kfree(data->pages.pagevec); - if (data == &pageio_header->rpc_data) { - data->header = NULL; - data = NULL; - } if (atomic_dec_and_test(&hdr->refcnt)) hdr->completion_ops->completion(hdr); - /* Note: we only free the rpc_task after callbacks are done. - * See the comment in rpc_free_task() for why - */ - kfree(data); } -EXPORT_SYMBOL_GPL(nfs_pgio_data_release); +EXPORT_SYMBOL_GPL(nfs_pgio_data_destroy); /** * nfs_pgio_rpcsetup - Set up arguments for a pageio call @@ -655,8 +621,7 @@ static int nfs_pgio_error(struct nfs_pageio_descriptor *desc, struct nfs_pgio_header *hdr) { set_bit(NFS_IOHDR_REDO, &hdr->flags); - nfs_pgio_data_release(hdr->data); - hdr->data = NULL; + nfs_pgio_data_destroy(&hdr->data); desc->pg_completion_ops->error_cleanup(&desc->pg_list); return -ENOMEM; } @@ -670,7 +635,7 @@ static void nfs_pgio_release(void *calldata) struct nfs_pgio_data *data = calldata; if (data->header->rw_ops->rw_release) data->header->rw_ops->rw_release(data); - nfs_pgio_data_release(data); + nfs_pgio_data_destroy(data); } /** @@ -746,11 +711,11 @@ int nfs_generic_pgio(struct nfs_pageio_descriptor *desc, struct list_head *head = &desc->pg_list; struct nfs_commit_info cinfo; - data = nfs_pgio_data_alloc(hdr, nfs_page_array_len(desc->pg_base, - desc->pg_count)); - if (!data) + if (!nfs_pgio_data_init(hdr, nfs_page_array_len(desc->pg_base, + desc->pg_count))) return nfs_pgio_error(desc, hdr); + data = &hdr->data; nfs_init_cinfo(&cinfo, desc->pg_inode, desc->pg_dreq); pages = data->pages.pagevec; while (!list_empty(head)) { @@ -766,7 +731,6 @@ int nfs_generic_pgio(struct nfs_pageio_descriptor *desc, /* Set up the argument struct */ nfs_pgio_rpcsetup(data, desc->pg_count, 0, desc->pg_ioflags, &cinfo); - hdr->data = data; desc->pg_rpc_callops = &nfs_pgio_common_ops; return 0; } @@ -774,22 +738,20 @@ EXPORT_SYMBOL_GPL(nfs_generic_pgio); static int nfs_generic_pg_pgios(struct nfs_pageio_descriptor *desc) { - struct nfs_rw_header *rw_hdr; struct nfs_pgio_header *hdr; int ret; - rw_hdr = nfs_rw_header_alloc(desc->pg_rw_ops); - if (!rw_hdr) { + hdr = nfs_pgio_header_alloc(desc->pg_rw_ops); + if (!hdr) { desc->pg_completion_ops->error_cleanup(&desc->pg_list); return -ENOMEM; } - hdr = &rw_hdr->header; - nfs_pgheader_init(desc, hdr, nfs_rw_header_free); + nfs_pgheader_init(desc, hdr, nfs_pgio_header_free); atomic_inc(&hdr->refcnt); ret = nfs_generic_pgio(desc, hdr); if (ret == 0) ret = nfs_initiate_pgio(NFS_CLIENT(hdr->inode), - hdr->data, desc->pg_rpc_callops, + &hdr->data, desc->pg_rpc_callops, desc->pg_ioflags, 0); if (atomic_dec_and_test(&hdr->refcnt)) hdr->completion_ops->completion(hdr); diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index 6fdcd233d6f7..067104cce181 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -1546,7 +1546,7 @@ pnfs_write_through_mds(struct nfs_pageio_descriptor *desc, nfs_pageio_reset_write_mds(desc); desc->pg_recoalesce = 1; } - nfs_pgio_data_release(data); + nfs_pgio_data_destroy(data); } static enum pnfs_try_status @@ -1575,7 +1575,7 @@ static void pnfs_do_write(struct nfs_pageio_descriptor *desc, struct nfs_pgio_header *hdr, int how) { - struct nfs_pgio_data *data = hdr->data; + struct nfs_pgio_data *data = &hdr->data; const struct rpc_call_ops *call_ops = desc->pg_rpc_callops; struct pnfs_layout_segment *lseg = desc->pg_lseg; enum pnfs_try_status trypnfs; @@ -1590,25 +1590,23 @@ pnfs_do_write(struct nfs_pageio_descriptor *desc, static void pnfs_writehdr_free(struct nfs_pgio_header *hdr) { pnfs_put_lseg(hdr->lseg); - nfs_rw_header_free(hdr); + nfs_pgio_header_free(hdr); } EXPORT_SYMBOL_GPL(pnfs_writehdr_free); int pnfs_generic_pg_writepages(struct nfs_pageio_descriptor *desc) { - struct nfs_rw_header *whdr; struct nfs_pgio_header *hdr; int ret; - whdr = nfs_rw_header_alloc(desc->pg_rw_ops); - if (!whdr) { + hdr = nfs_pgio_header_alloc(desc->pg_rw_ops); + if (!hdr) { desc->pg_completion_ops->error_cleanup(&desc->pg_list); pnfs_put_lseg(desc->pg_lseg); desc->pg_lseg = NULL; return -ENOMEM; } - hdr = &whdr->header; nfs_pgheader_init(desc, hdr, pnfs_writehdr_free); hdr->lseg = pnfs_get_lseg(desc->pg_lseg); atomic_inc(&hdr->refcnt); @@ -1696,7 +1694,7 @@ pnfs_read_through_mds(struct nfs_pageio_descriptor *desc, nfs_pageio_reset_read_mds(desc); desc->pg_recoalesce = 1; } - nfs_pgio_data_release(data); + nfs_pgio_data_destroy(data); } /* @@ -1727,7 +1725,7 @@ pnfs_try_to_read_data(struct nfs_pgio_data *rdata, static void pnfs_do_read(struct nfs_pageio_descriptor *desc, struct nfs_pgio_header *hdr) { - struct nfs_pgio_data *data = hdr->data; + struct nfs_pgio_data *data = &hdr->data; const struct rpc_call_ops *call_ops = desc->pg_rpc_callops; struct pnfs_layout_segment *lseg = desc->pg_lseg; enum pnfs_try_status trypnfs; @@ -1742,26 +1740,24 @@ pnfs_do_read(struct nfs_pageio_descriptor *desc, struct nfs_pgio_header *hdr) static void pnfs_readhdr_free(struct nfs_pgio_header *hdr) { pnfs_put_lseg(hdr->lseg); - nfs_rw_header_free(hdr); + nfs_pgio_header_free(hdr); } EXPORT_SYMBOL_GPL(pnfs_readhdr_free); int pnfs_generic_pg_readpages(struct nfs_pageio_descriptor *desc) { - struct nfs_rw_header *rhdr; struct nfs_pgio_header *hdr; int ret; - rhdr = nfs_rw_header_alloc(desc->pg_rw_ops); - if (!rhdr) { + hdr = nfs_pgio_header_alloc(desc->pg_rw_ops); + if (!hdr) { desc->pg_completion_ops->error_cleanup(&desc->pg_list); ret = -ENOMEM; pnfs_put_lseg(desc->pg_lseg); desc->pg_lseg = NULL; return ret; } - hdr = &rhdr->header; nfs_pgheader_init(desc, hdr, pnfs_readhdr_free); hdr->lseg = pnfs_get_lseg(desc->pg_lseg); atomic_inc(&hdr->refcnt); diff --git a/fs/nfs/read.c b/fs/nfs/read.c index e818a475ca64..d9df4ab3737b 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -33,12 +33,12 @@ static const struct nfs_rw_ops nfs_rw_read_ops; static struct kmem_cache *nfs_rdata_cachep; -static struct nfs_rw_header *nfs_readhdr_alloc(void) +static struct nfs_pgio_header *nfs_readhdr_alloc(void) { return kmem_cache_zalloc(nfs_rdata_cachep, GFP_KERNEL); } -static void nfs_readhdr_free(struct nfs_rw_header *rhdr) +static void nfs_readhdr_free(struct nfs_pgio_header *rhdr) { kmem_cache_free(nfs_rdata_cachep, rhdr); } @@ -404,7 +404,7 @@ out: int __init nfs_init_readpagecache(void) { nfs_rdata_cachep = kmem_cache_create("nfs_read_data", - sizeof(struct nfs_rw_header), + sizeof(struct nfs_pgio_header), 0, SLAB_HWCACHE_ALIGN, NULL); if (nfs_rdata_cachep == NULL) diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 98ff061ccaf3..d694952f0071 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -70,18 +70,18 @@ void nfs_commit_free(struct nfs_commit_data *p) } EXPORT_SYMBOL_GPL(nfs_commit_free); -static struct nfs_rw_header *nfs_writehdr_alloc(void) +static struct nfs_pgio_header *nfs_writehdr_alloc(void) { - struct nfs_rw_header *p = mempool_alloc(nfs_wdata_mempool, GFP_NOIO); + struct nfs_pgio_header *p = mempool_alloc(nfs_wdata_mempool, GFP_NOIO); if (p) memset(p, 0, sizeof(*p)); return p; } -static void nfs_writehdr_free(struct nfs_rw_header *whdr) +static void nfs_writehdr_free(struct nfs_pgio_header *hdr) { - mempool_free(whdr, nfs_wdata_mempool); + mempool_free(hdr, nfs_wdata_mempool); } static void nfs_context_set_write_error(struct nfs_open_context *ctx, int error) @@ -1655,7 +1655,7 @@ int nfs_migrate_page(struct address_space *mapping, struct page *newpage, int __init nfs_init_writepagecache(void) { nfs_wdata_cachep = kmem_cache_create("nfs_write_data", - sizeof(struct nfs_rw_header), + sizeof(struct nfs_pgio_header), 0, SLAB_HWCACHE_ALIGN, NULL); if (nfs_wdata_cachep == NULL) diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h index 7d9096d95d4a..43592651cd5a 100644 --- a/include/linux/nfs_page.h +++ b/include/linux/nfs_page.h @@ -62,8 +62,8 @@ struct nfs_pageio_ops { struct nfs_rw_ops { const fmode_t rw_mode; - struct nfs_rw_header *(*rw_alloc_header)(void); - void (*rw_free_header)(struct nfs_rw_header *); + struct nfs_pgio_header *(*rw_alloc_header)(void); + void (*rw_free_header)(struct nfs_pgio_header *); void (*rw_release)(struct nfs_pgio_data *); int (*rw_done)(struct rpc_task *, struct nfs_pgio_data *, struct inode *); void (*rw_result)(struct rpc_task *, struct nfs_pgio_data *); diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 9a1396e70310..e1c9437e8aac 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1257,13 +1257,27 @@ enum { NFS_IOHDR_NEED_RESCHED, }; -struct nfs_pgio_data; +struct nfs_pgio_data { + struct nfs_pgio_header *header; + struct list_head list; + struct rpc_task task; + struct nfs_fattr fattr; + struct nfs_writeverf verf; /* Used for writes */ + struct nfs_pgio_args args; /* argument struct */ + struct nfs_pgio_res res; /* result struct */ + unsigned long timestamp; /* For lease renewal */ + int (*pgio_done_cb)(struct rpc_task *task, struct nfs_pgio_data *data); + __u64 mds_offset; /* Filelayout dense stripe */ + struct nfs_page_array pages; + struct nfs_client *ds_clp; /* pNFS data server */ + int ds_idx; /* ds index if ds_clp is set */ +}; struct nfs_pgio_header { struct inode *inode; struct rpc_cred *cred; struct list_head pages; - struct nfs_pgio_data *data; + struct nfs_pgio_data data; atomic_t refcnt; struct nfs_page *req; struct nfs_writeverf verf; /* Used for writes */ @@ -1283,26 +1297,6 @@ struct nfs_pgio_header { unsigned long flags; }; -struct nfs_pgio_data { - struct nfs_pgio_header *header; - struct rpc_task task; - struct nfs_fattr fattr; - struct nfs_writeverf verf; /* Used for writes */ - struct nfs_pgio_args args; /* argument struct */ - struct nfs_pgio_res res; /* result struct */ - unsigned long timestamp; /* For lease renewal */ - int (*pgio_done_cb) (struct rpc_task *task, struct nfs_pgio_data *data); - __u64 mds_offset; /* Filelayout dense stripe */ - struct nfs_page_array pages; - struct nfs_client *ds_clp; /* pNFS data server */ - int ds_idx; /* ds index if ds_clp is set */ -}; - -struct nfs_rw_header { - struct nfs_pgio_header header; - struct nfs_pgio_data rpc_data; -}; - struct nfs_mds_commit_info { atomic_t rpcs_out; unsigned long ncommit; -- cgit v1.2.3 From 823b0c9d9800e712374cda89ac3565bd29f6701b Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Mon, 9 Jun 2014 11:48:34 -0400 Subject: nfs: rename members of nfs_pgio_data Rename "verf" to "writeverf" and "pages" to "page_array" to prepare for merge of nfs_pgio_data and nfs_pgio_header. Reviewed-by: Christoph Hellwig Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust --- fs/nfs/blocklayout/blocklayout.c | 17 ++++++++++------- fs/nfs/objlayout/objlayout.c | 4 ++-- fs/nfs/pagelist.c | 12 ++++++------ fs/nfs/write.c | 9 +++++---- include/linux/nfs_xdr.h | 4 ++-- 5 files changed, 25 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/fs/nfs/blocklayout/blocklayout.c b/fs/nfs/blocklayout/blocklayout.c index 9b431f44fad9..36b01cef849e 100644 --- a/fs/nfs/blocklayout/blocklayout.c +++ b/fs/nfs/blocklayout/blocklayout.c @@ -258,7 +258,8 @@ bl_read_pagelist(struct nfs_pgio_data *rdata) const bool is_dio = (header->dreq != NULL); dprintk("%s enter nr_pages %u offset %lld count %u\n", __func__, - rdata->pages.npages, f_offset, (unsigned int)rdata->args.count); + rdata->page_array.npages, f_offset, + (unsigned int)rdata->args.count); par = alloc_parallel(rdata); if (!par) @@ -268,7 +269,7 @@ bl_read_pagelist(struct nfs_pgio_data *rdata) isect = (sector_t) (f_offset >> SECTOR_SHIFT); /* Code assumes extents are page-aligned */ - for (i = pg_index; i < rdata->pages.npages; i++) { + for (i = pg_index; i < rdata->page_array.npages; i++) { if (!extent_length) { /* We've used up the previous extent */ bl_put_extent(be); @@ -317,7 +318,8 @@ bl_read_pagelist(struct nfs_pgio_data *rdata) struct pnfs_block_extent *be_read; be_read = (hole && cow_read) ? cow_read : be; - bio = do_add_page_to_bio(bio, rdata->pages.npages - i, + bio = do_add_page_to_bio(bio, + rdata->page_array.npages - i, READ, isect, pages[i], be_read, bl_end_io_read, par, @@ -446,7 +448,7 @@ static void bl_end_par_io_write(void *data, int num_se) } wdata->task.tk_status = wdata->header->pnfs_error; - wdata->verf.committed = NFS_FILE_SYNC; + wdata->writeverf.committed = NFS_FILE_SYNC; INIT_WORK(&wdata->task.u.tk_work, bl_write_cleanup); schedule_work(&wdata->task.u.tk_work); } @@ -699,7 +701,7 @@ bl_write_pagelist(struct nfs_pgio_data *wdata, int sync) dprintk("pnfsblock nonblock aligned DIO writes. Resend MDS\n"); goto out_mds; } - /* At this point, wdata->pages is a (sequential) list of nfs_pages. + /* At this point, wdata->page_aray is a (sequential) list of nfs_pages. * We want to write each, and if there is an error set pnfs_error * to have it redone using nfs. */ @@ -791,7 +793,7 @@ next_page: /* Middle pages */ pg_index = wdata->args.pgbase >> PAGE_CACHE_SHIFT; - for (i = pg_index; i < wdata->pages.npages; i++) { + for (i = pg_index; i < wdata->page_array.npages; i++) { if (!extent_length) { /* We've used up the previous extent */ bl_put_extent(be); @@ -862,7 +864,8 @@ next_page: } - bio = do_add_page_to_bio(bio, wdata->pages.npages - i, WRITE, + bio = do_add_page_to_bio(bio, wdata->page_array.npages - i, + WRITE, isect, pages[i], be, bl_end_io_write, par, pg_offset, pg_len); diff --git a/fs/nfs/objlayout/objlayout.c b/fs/nfs/objlayout/objlayout.c index 765d3f54e986..31fed91a8bac 100644 --- a/fs/nfs/objlayout/objlayout.c +++ b/fs/nfs/objlayout/objlayout.c @@ -329,7 +329,7 @@ objlayout_write_done(struct objlayout_io_res *oir, ssize_t status, bool sync) oir->status = wdata->task.tk_status = status; if (status >= 0) { wdata->res.count = status; - wdata->verf.committed = oir->committed; + wdata->writeverf.committed = oir->committed; } else { wdata->header->pnfs_error = status; } @@ -337,7 +337,7 @@ objlayout_write_done(struct objlayout_io_res *oir, ssize_t status, bool sync) /* must not use oir after this point */ dprintk("%s: Return status %zd committed %d sync=%d\n", __func__, - status, wdata->verf.committed, sync); + status, wdata->writeverf.committed, sync); if (sync) pnfs_ld_write_done(wdata); diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index e4cde476562f..5e70918f6c95 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -484,7 +484,7 @@ EXPORT_SYMBOL_GPL(nfs_pgio_header_free); static bool nfs_pgio_data_init(struct nfs_pgio_header *hdr, unsigned int pagecount) { - if (nfs_pgarray_set(&hdr->data.pages, pagecount)) { + if (nfs_pgarray_set(&hdr->data.page_array, pagecount)) { hdr->data.header = hdr; atomic_inc(&hdr->refcnt); return true; @@ -501,8 +501,8 @@ void nfs_pgio_data_destroy(struct nfs_pgio_data *data) struct nfs_pgio_header *hdr = data->header; put_nfs_open_context(data->args.context); - if (data->pages.pagevec != data->pages.page_array) - kfree(data->pages.pagevec); + if (data->page_array.pagevec != data->page_array.page_array) + kfree(data->page_array.pagevec); if (atomic_dec_and_test(&hdr->refcnt)) hdr->completion_ops->completion(hdr); } @@ -530,7 +530,7 @@ static void nfs_pgio_rpcsetup(struct nfs_pgio_data *data, /* pnfs_set_layoutcommit needs this */ data->mds_offset = data->args.offset; data->args.pgbase = req->wb_pgbase + offset; - data->args.pages = data->pages.pagevec; + data->args.pages = data->page_array.pagevec; data->args.count = count; data->args.context = get_nfs_open_context(req->wb_context); data->args.lock_context = req->wb_lock_context; @@ -548,7 +548,7 @@ static void nfs_pgio_rpcsetup(struct nfs_pgio_data *data, data->res.fattr = &data->fattr; data->res.count = count; data->res.eof = 0; - data->res.verf = &data->verf; + data->res.verf = &data->writeverf; nfs_fattr_init(&data->fattr); } @@ -717,7 +717,7 @@ int nfs_generic_pgio(struct nfs_pageio_descriptor *desc, data = &hdr->data; nfs_init_cinfo(&cinfo, desc->pg_inode, desc->pg_dreq); - pages = data->pages.pagevec; + pages = data->page_array.pagevec; while (!list_empty(head)) { req = nfs_list_entry(head->next); nfs_list_remove_request(req); diff --git a/fs/nfs/write.c b/fs/nfs/write.c index d694952f0071..6afe0f679420 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -598,9 +598,9 @@ nfs_clear_request_commit(struct nfs_page *req) static inline int nfs_write_need_commit(struct nfs_pgio_data *data) { - if (data->verf.committed == NFS_DATA_SYNC) + if (data->writeverf.committed == NFS_DATA_SYNC) return data->header->lseg == NULL; - return data->verf.committed != NFS_FILE_SYNC; + return data->writeverf.committed != NFS_FILE_SYNC; } #else @@ -1095,8 +1095,9 @@ static void nfs_writeback_release_common(struct nfs_pgio_data *data) if (test_bit(NFS_IOHDR_NEED_RESCHED, &hdr->flags)) ; /* Do nothing */ else if (!test_and_set_bit(NFS_IOHDR_NEED_COMMIT, &hdr->flags)) - memcpy(&hdr->verf, &data->verf, sizeof(hdr->verf)); - else if (memcmp(&hdr->verf, &data->verf, sizeof(hdr->verf))) + memcpy(&hdr->verf, &data->writeverf, sizeof(hdr->verf)); + else if (memcmp(&hdr->verf, &data->writeverf, + sizeof(hdr->verf))) set_bit(NFS_IOHDR_NEED_RESCHED, &hdr->flags); spin_unlock(&hdr->lock); } diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index e1c9437e8aac..bb18dba1aefe 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1262,13 +1262,13 @@ struct nfs_pgio_data { struct list_head list; struct rpc_task task; struct nfs_fattr fattr; - struct nfs_writeverf verf; /* Used for writes */ + struct nfs_writeverf writeverf; /* Used for writes */ struct nfs_pgio_args args; /* argument struct */ struct nfs_pgio_res res; /* result struct */ unsigned long timestamp; /* For lease renewal */ int (*pgio_done_cb)(struct rpc_task *task, struct nfs_pgio_data *data); __u64 mds_offset; /* Filelayout dense stripe */ - struct nfs_page_array pages; + struct nfs_page_array page_array; struct nfs_client *ds_clp; /* pNFS data server */ int ds_idx; /* ds index if ds_clp is set */ }; -- cgit v1.2.3 From d45f60c67848b9f19160692581d78e5b4757a000 Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Mon, 9 Jun 2014 11:48:35 -0400 Subject: nfs: merge nfs_pgio_data into _header struct nfs_pgio_data only exists as a member of nfs_pgio_header, but is passed around everywhere, because there used to be multiple _data structs per _header. Many of these functions then use the _data to find a pointer to the _header. This patch cleans this up by merging the nfs_pgio_data structure into nfs_pgio_header and passing nfs_pgio_header around instead. Reviewed-by: Christoph Hellwig Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust --- fs/nfs/blocklayout/blocklayout.c | 98 +++++++++++----------- fs/nfs/direct.c | 8 +- fs/nfs/filelayout/filelayout.c | 170 +++++++++++++++++++-------------------- fs/nfs/internal.h | 6 +- fs/nfs/nfs3proc.c | 21 ++--- fs/nfs/nfs4_fs.h | 6 +- fs/nfs/nfs4proc.c | 105 ++++++++++++------------ fs/nfs/nfs4trace.h | 28 +++---- fs/nfs/objlayout/objio_osd.c | 24 +++--- fs/nfs/objlayout/objlayout.c | 81 +++++++++---------- fs/nfs/objlayout/objlayout.h | 8 +- fs/nfs/pagelist.c | 120 +++++++++++++-------------- fs/nfs/pnfs.c | 80 ++++++++---------- fs/nfs/pnfs.h | 10 +-- fs/nfs/proc.c | 27 ++++--- fs/nfs/read.c | 42 +++++----- fs/nfs/write.c | 56 ++++++------- include/linux/nfs_page.h | 9 ++- include/linux/nfs_xdr.h | 43 +++++----- 19 files changed, 460 insertions(+), 482 deletions(-) (limited to 'include') diff --git a/fs/nfs/blocklayout/blocklayout.c b/fs/nfs/blocklayout/blocklayout.c index 36b01cef849e..c3ccfe440390 100644 --- a/fs/nfs/blocklayout/blocklayout.c +++ b/fs/nfs/blocklayout/blocklayout.c @@ -210,8 +210,7 @@ static void bl_end_io_read(struct bio *bio, int err) SetPageUptodate(bvec->bv_page); if (err) { - struct nfs_pgio_data *rdata = par->data; - struct nfs_pgio_header *header = rdata->header; + struct nfs_pgio_header *header = par->data; if (!header->pnfs_error) header->pnfs_error = -EIO; @@ -224,44 +223,44 @@ static void bl_end_io_read(struct bio *bio, int err) static void bl_read_cleanup(struct work_struct *work) { struct rpc_task *task; - struct nfs_pgio_data *rdata; + struct nfs_pgio_header *hdr; dprintk("%s enter\n", __func__); task = container_of(work, struct rpc_task, u.tk_work); - rdata = container_of(task, struct nfs_pgio_data, task); - pnfs_ld_read_done(rdata); + hdr = container_of(task, struct nfs_pgio_header, task); + pnfs_ld_read_done(hdr); } static void bl_end_par_io_read(void *data, int unused) { - struct nfs_pgio_data *rdata = data; + struct nfs_pgio_header *hdr = data; - rdata->task.tk_status = rdata->header->pnfs_error; - INIT_WORK(&rdata->task.u.tk_work, bl_read_cleanup); - schedule_work(&rdata->task.u.tk_work); + hdr->task.tk_status = hdr->pnfs_error; + INIT_WORK(&hdr->task.u.tk_work, bl_read_cleanup); + schedule_work(&hdr->task.u.tk_work); } static enum pnfs_try_status -bl_read_pagelist(struct nfs_pgio_data *rdata) +bl_read_pagelist(struct nfs_pgio_header *hdr) { - struct nfs_pgio_header *header = rdata->header; + struct nfs_pgio_header *header = hdr; int i, hole; struct bio *bio = NULL; struct pnfs_block_extent *be = NULL, *cow_read = NULL; sector_t isect, extent_length = 0; struct parallel_io *par; - loff_t f_offset = rdata->args.offset; - size_t bytes_left = rdata->args.count; + loff_t f_offset = hdr->args.offset; + size_t bytes_left = hdr->args.count; unsigned int pg_offset, pg_len; - struct page **pages = rdata->args.pages; - int pg_index = rdata->args.pgbase >> PAGE_CACHE_SHIFT; + struct page **pages = hdr->args.pages; + int pg_index = hdr->args.pgbase >> PAGE_CACHE_SHIFT; const bool is_dio = (header->dreq != NULL); dprintk("%s enter nr_pages %u offset %lld count %u\n", __func__, - rdata->page_array.npages, f_offset, - (unsigned int)rdata->args.count); + hdr->page_array.npages, f_offset, + (unsigned int)hdr->args.count); - par = alloc_parallel(rdata); + par = alloc_parallel(hdr); if (!par) goto use_mds; par->pnfs_callback = bl_end_par_io_read; @@ -269,7 +268,7 @@ bl_read_pagelist(struct nfs_pgio_data *rdata) isect = (sector_t) (f_offset >> SECTOR_SHIFT); /* Code assumes extents are page-aligned */ - for (i = pg_index; i < rdata->page_array.npages; i++) { + for (i = pg_index; i < hdr->page_array.npages; i++) { if (!extent_length) { /* We've used up the previous extent */ bl_put_extent(be); @@ -319,7 +318,7 @@ bl_read_pagelist(struct nfs_pgio_data *rdata) be_read = (hole && cow_read) ? cow_read : be; bio = do_add_page_to_bio(bio, - rdata->page_array.npages - i, + hdr->page_array.npages - i, READ, isect, pages[i], be_read, bl_end_io_read, par, @@ -334,10 +333,10 @@ bl_read_pagelist(struct nfs_pgio_data *rdata) extent_length -= PAGE_CACHE_SECTORS; } if ((isect << SECTOR_SHIFT) >= header->inode->i_size) { - rdata->res.eof = 1; - rdata->res.count = header->inode->i_size - rdata->args.offset; + hdr->res.eof = 1; + hdr->res.count = header->inode->i_size - hdr->args.offset; } else { - rdata->res.count = (isect << SECTOR_SHIFT) - rdata->args.offset; + hdr->res.count = (isect << SECTOR_SHIFT) - hdr->args.offset; } out: bl_put_extent(be); @@ -392,8 +391,7 @@ static void bl_end_io_write_zero(struct bio *bio, int err) } if (unlikely(err)) { - struct nfs_pgio_data *data = par->data; - struct nfs_pgio_header *header = data->header; + struct nfs_pgio_header *header = par->data; if (!header->pnfs_error) header->pnfs_error = -EIO; @@ -407,8 +405,7 @@ static void bl_end_io_write(struct bio *bio, int err) { struct parallel_io *par = bio->bi_private; const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); - struct nfs_pgio_data *data = par->data; - struct nfs_pgio_header *header = data->header; + struct nfs_pgio_header *header = par->data; if (!uptodate) { if (!header->pnfs_error) @@ -425,32 +422,32 @@ static void bl_end_io_write(struct bio *bio, int err) static void bl_write_cleanup(struct work_struct *work) { struct rpc_task *task; - struct nfs_pgio_data *wdata; + struct nfs_pgio_header *hdr; dprintk("%s enter\n", __func__); task = container_of(work, struct rpc_task, u.tk_work); - wdata = container_of(task, struct nfs_pgio_data, task); - if (likely(!wdata->header->pnfs_error)) { + hdr = container_of(task, struct nfs_pgio_header, task); + if (likely(!hdr->pnfs_error)) { /* Marks for LAYOUTCOMMIT */ - mark_extents_written(BLK_LSEG2EXT(wdata->header->lseg), - wdata->args.offset, wdata->args.count); + mark_extents_written(BLK_LSEG2EXT(hdr->lseg), + hdr->args.offset, hdr->args.count); } - pnfs_ld_write_done(wdata); + pnfs_ld_write_done(hdr); } /* Called when last of bios associated with a bl_write_pagelist call finishes */ static void bl_end_par_io_write(void *data, int num_se) { - struct nfs_pgio_data *wdata = data; + struct nfs_pgio_header *hdr = data; - if (unlikely(wdata->header->pnfs_error)) { - bl_free_short_extents(&BLK_LSEG2EXT(wdata->header->lseg)->bl_inval, + if (unlikely(hdr->pnfs_error)) { + bl_free_short_extents(&BLK_LSEG2EXT(hdr->lseg)->bl_inval, num_se); } - wdata->task.tk_status = wdata->header->pnfs_error; - wdata->writeverf.committed = NFS_FILE_SYNC; - INIT_WORK(&wdata->task.u.tk_work, bl_write_cleanup); - schedule_work(&wdata->task.u.tk_work); + hdr->task.tk_status = hdr->pnfs_error; + hdr->writeverf.committed = NFS_FILE_SYNC; + INIT_WORK(&hdr->task.u.tk_work, bl_write_cleanup); + schedule_work(&hdr->task.u.tk_work); } /* FIXME STUB - mark intersection of layout and page as bad, so is not @@ -675,18 +672,17 @@ check_page: } static enum pnfs_try_status -bl_write_pagelist(struct nfs_pgio_data *wdata, int sync) +bl_write_pagelist(struct nfs_pgio_header *header, int sync) { - struct nfs_pgio_header *header = wdata->header; int i, ret, npg_zero, pg_index, last = 0; struct bio *bio = NULL; struct pnfs_block_extent *be = NULL, *cow_read = NULL; sector_t isect, last_isect = 0, extent_length = 0; struct parallel_io *par = NULL; - loff_t offset = wdata->args.offset; - size_t count = wdata->args.count; + loff_t offset = header->args.offset; + size_t count = header->args.count; unsigned int pg_offset, pg_len, saved_len; - struct page **pages = wdata->args.pages; + struct page **pages = header->args.pages; struct page *page; pgoff_t index; u64 temp; @@ -701,11 +697,11 @@ bl_write_pagelist(struct nfs_pgio_data *wdata, int sync) dprintk("pnfsblock nonblock aligned DIO writes. Resend MDS\n"); goto out_mds; } - /* At this point, wdata->page_aray is a (sequential) list of nfs_pages. + /* At this point, header->page_aray is a (sequential) list of nfs_pages. * We want to write each, and if there is an error set pnfs_error * to have it redone using nfs. */ - par = alloc_parallel(wdata); + par = alloc_parallel(header); if (!par) goto out_mds; par->pnfs_callback = bl_end_par_io_write; @@ -792,8 +788,8 @@ next_page: bio = bl_submit_bio(WRITE, bio); /* Middle pages */ - pg_index = wdata->args.pgbase >> PAGE_CACHE_SHIFT; - for (i = pg_index; i < wdata->page_array.npages; i++) { + pg_index = header->args.pgbase >> PAGE_CACHE_SHIFT; + for (i = pg_index; i < header->page_array.npages; i++) { if (!extent_length) { /* We've used up the previous extent */ bl_put_extent(be); @@ -864,7 +860,7 @@ next_page: } - bio = do_add_page_to_bio(bio, wdata->page_array.npages - i, + bio = do_add_page_to_bio(bio, header->page_array.npages - i, WRITE, isect, pages[i], be, bl_end_io_write, par, @@ -893,7 +889,7 @@ next_page: } write_done: - wdata->res.count = wdata->args.count; + header->res.count = header->args.count; out: bl_put_extent(be); bl_put_extent(cow_read); diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 179de67ca907..6c4c867ee04c 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -148,8 +148,8 @@ static void nfs_direct_set_hdr_verf(struct nfs_direct_req *dreq, { struct nfs_writeverf *verfp; - verfp = nfs_direct_select_verf(dreq, hdr->data.ds_clp, - hdr->data.ds_idx); + verfp = nfs_direct_select_verf(dreq, hdr->ds_clp, + hdr->ds_idx); WARN_ON_ONCE(verfp->committed >= 0); memcpy(verfp, &hdr->verf, sizeof(struct nfs_writeverf)); WARN_ON_ONCE(verfp->committed < 0); @@ -169,8 +169,8 @@ static int nfs_direct_set_or_cmp_hdr_verf(struct nfs_direct_req *dreq, { struct nfs_writeverf *verfp; - verfp = nfs_direct_select_verf(dreq, hdr->data.ds_clp, - hdr->data.ds_idx); + verfp = nfs_direct_select_verf(dreq, hdr->ds_clp, + hdr->ds_idx); if (verfp->committed < 0) { nfs_direct_set_hdr_verf(dreq, hdr); return 0; diff --git a/fs/nfs/filelayout/filelayout.c b/fs/nfs/filelayout/filelayout.c index d2eba1c13b7e..537e7f7a0b48 100644 --- a/fs/nfs/filelayout/filelayout.c +++ b/fs/nfs/filelayout/filelayout.c @@ -84,19 +84,18 @@ filelayout_get_dserver_offset(struct pnfs_layout_segment *lseg, loff_t offset) BUG(); } -static void filelayout_reset_write(struct nfs_pgio_data *data) +static void filelayout_reset_write(struct nfs_pgio_header *hdr) { - struct nfs_pgio_header *hdr = data->header; - struct rpc_task *task = &data->task; + struct rpc_task *task = &hdr->task; if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags)) { dprintk("%s Reset task %5u for i/o through MDS " "(req %s/%llu, %u bytes @ offset %llu)\n", __func__, - data->task.tk_pid, + hdr->task.tk_pid, hdr->inode->i_sb->s_id, (unsigned long long)NFS_FILEID(hdr->inode), - data->args.count, - (unsigned long long)data->args.offset); + hdr->args.count, + (unsigned long long)hdr->args.offset); task->tk_status = pnfs_write_done_resend_to_mds(hdr->inode, &hdr->pages, @@ -105,19 +104,18 @@ static void filelayout_reset_write(struct nfs_pgio_data *data) } } -static void filelayout_reset_read(struct nfs_pgio_data *data) +static void filelayout_reset_read(struct nfs_pgio_header *hdr) { - struct nfs_pgio_header *hdr = data->header; - struct rpc_task *task = &data->task; + struct rpc_task *task = &hdr->task; if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags)) { dprintk("%s Reset task %5u for i/o through MDS " "(req %s/%llu, %u bytes @ offset %llu)\n", __func__, - data->task.tk_pid, + hdr->task.tk_pid, hdr->inode->i_sb->s_id, (unsigned long long)NFS_FILEID(hdr->inode), - data->args.count, - (unsigned long long)data->args.offset); + hdr->args.count, + (unsigned long long)hdr->args.offset); task->tk_status = pnfs_read_done_resend_to_mds(hdr->inode, &hdr->pages, @@ -243,18 +241,17 @@ wait_on_recovery: /* NFS_PROTO call done callback routines */ static int filelayout_read_done_cb(struct rpc_task *task, - struct nfs_pgio_data *data) + struct nfs_pgio_header *hdr) { - struct nfs_pgio_header *hdr = data->header; int err; - trace_nfs4_pnfs_read(data, task->tk_status); - err = filelayout_async_handle_error(task, data->args.context->state, - data->ds_clp, hdr->lseg); + trace_nfs4_pnfs_read(hdr, task->tk_status); + err = filelayout_async_handle_error(task, hdr->args.context->state, + hdr->ds_clp, hdr->lseg); switch (err) { case -NFS4ERR_RESET_TO_MDS: - filelayout_reset_read(data); + filelayout_reset_read(hdr); return task->tk_status; case -EAGAIN: rpc_restart_call_prepare(task); @@ -270,15 +267,14 @@ static int filelayout_read_done_cb(struct rpc_task *task, * rfc5661 is not clear about which credential should be used. */ static void -filelayout_set_layoutcommit(struct nfs_pgio_data *wdata) +filelayout_set_layoutcommit(struct nfs_pgio_header *hdr) { - struct nfs_pgio_header *hdr = wdata->header; if (FILELAYOUT_LSEG(hdr->lseg)->commit_through_mds || - wdata->res.verf->committed == NFS_FILE_SYNC) + hdr->res.verf->committed == NFS_FILE_SYNC) return; - pnfs_set_layoutcommit(wdata); + pnfs_set_layoutcommit(hdr); dprintk("%s inode %lu pls_end_pos %lu\n", __func__, hdr->inode->i_ino, (unsigned long) NFS_I(hdr->inode)->layout->plh_lwb); } @@ -305,83 +301,82 @@ filelayout_reset_to_mds(struct pnfs_layout_segment *lseg) */ static void filelayout_read_prepare(struct rpc_task *task, void *data) { - struct nfs_pgio_data *rdata = data; + struct nfs_pgio_header *hdr = data; - if (unlikely(test_bit(NFS_CONTEXT_BAD, &rdata->args.context->flags))) { + if (unlikely(test_bit(NFS_CONTEXT_BAD, &hdr->args.context->flags))) { rpc_exit(task, -EIO); return; } - if (filelayout_reset_to_mds(rdata->header->lseg)) { + if (filelayout_reset_to_mds(hdr->lseg)) { dprintk("%s task %u reset io to MDS\n", __func__, task->tk_pid); - filelayout_reset_read(rdata); + filelayout_reset_read(hdr); rpc_exit(task, 0); return; } - rdata->pgio_done_cb = filelayout_read_done_cb; + hdr->pgio_done_cb = filelayout_read_done_cb; - if (nfs41_setup_sequence(rdata->ds_clp->cl_session, - &rdata->args.seq_args, - &rdata->res.seq_res, + if (nfs41_setup_sequence(hdr->ds_clp->cl_session, + &hdr->args.seq_args, + &hdr->res.seq_res, task)) return; - if (nfs4_set_rw_stateid(&rdata->args.stateid, rdata->args.context, - rdata->args.lock_context, FMODE_READ) == -EIO) + if (nfs4_set_rw_stateid(&hdr->args.stateid, hdr->args.context, + hdr->args.lock_context, FMODE_READ) == -EIO) rpc_exit(task, -EIO); /* lost lock, terminate I/O */ } static void filelayout_read_call_done(struct rpc_task *task, void *data) { - struct nfs_pgio_data *rdata = data; + struct nfs_pgio_header *hdr = data; dprintk("--> %s task->tk_status %d\n", __func__, task->tk_status); - if (test_bit(NFS_IOHDR_REDO, &rdata->header->flags) && + if (test_bit(NFS_IOHDR_REDO, &hdr->flags) && task->tk_status == 0) { - nfs41_sequence_done(task, &rdata->res.seq_res); + nfs41_sequence_done(task, &hdr->res.seq_res); return; } /* Note this may cause RPC to be resent */ - rdata->header->mds_ops->rpc_call_done(task, data); + hdr->mds_ops->rpc_call_done(task, data); } static void filelayout_read_count_stats(struct rpc_task *task, void *data) { - struct nfs_pgio_data *rdata = data; + struct nfs_pgio_header *hdr = data; - rpc_count_iostats(task, NFS_SERVER(rdata->header->inode)->client->cl_metrics); + rpc_count_iostats(task, NFS_SERVER(hdr->inode)->client->cl_metrics); } static void filelayout_read_release(void *data) { - struct nfs_pgio_data *rdata = data; - struct pnfs_layout_hdr *lo = rdata->header->lseg->pls_layout; + struct nfs_pgio_header *hdr = data; + struct pnfs_layout_hdr *lo = hdr->lseg->pls_layout; filelayout_fenceme(lo->plh_inode, lo); - nfs_put_client(rdata->ds_clp); - rdata->header->mds_ops->rpc_release(data); + nfs_put_client(hdr->ds_clp); + hdr->mds_ops->rpc_release(data); } static int filelayout_write_done_cb(struct rpc_task *task, - struct nfs_pgio_data *data) + struct nfs_pgio_header *hdr) { - struct nfs_pgio_header *hdr = data->header; int err; - trace_nfs4_pnfs_write(data, task->tk_status); - err = filelayout_async_handle_error(task, data->args.context->state, - data->ds_clp, hdr->lseg); + trace_nfs4_pnfs_write(hdr, task->tk_status); + err = filelayout_async_handle_error(task, hdr->args.context->state, + hdr->ds_clp, hdr->lseg); switch (err) { case -NFS4ERR_RESET_TO_MDS: - filelayout_reset_write(data); + filelayout_reset_write(hdr); return task->tk_status; case -EAGAIN: rpc_restart_call_prepare(task); return -EAGAIN; } - filelayout_set_layoutcommit(data); + filelayout_set_layoutcommit(hdr); return 0; } @@ -419,57 +414,57 @@ static int filelayout_commit_done_cb(struct rpc_task *task, static void filelayout_write_prepare(struct rpc_task *task, void *data) { - struct nfs_pgio_data *wdata = data; + struct nfs_pgio_header *hdr = data; - if (unlikely(test_bit(NFS_CONTEXT_BAD, &wdata->args.context->flags))) { + if (unlikely(test_bit(NFS_CONTEXT_BAD, &hdr->args.context->flags))) { rpc_exit(task, -EIO); return; } - if (filelayout_reset_to_mds(wdata->header->lseg)) { + if (filelayout_reset_to_mds(hdr->lseg)) { dprintk("%s task %u reset io to MDS\n", __func__, task->tk_pid); - filelayout_reset_write(wdata); + filelayout_reset_write(hdr); rpc_exit(task, 0); return; } - if (nfs41_setup_sequence(wdata->ds_clp->cl_session, - &wdata->args.seq_args, - &wdata->res.seq_res, + if (nfs41_setup_sequence(hdr->ds_clp->cl_session, + &hdr->args.seq_args, + &hdr->res.seq_res, task)) return; - if (nfs4_set_rw_stateid(&wdata->args.stateid, wdata->args.context, - wdata->args.lock_context, FMODE_WRITE) == -EIO) + if (nfs4_set_rw_stateid(&hdr->args.stateid, hdr->args.context, + hdr->args.lock_context, FMODE_WRITE) == -EIO) rpc_exit(task, -EIO); /* lost lock, terminate I/O */ } static void filelayout_write_call_done(struct rpc_task *task, void *data) { - struct nfs_pgio_data *wdata = data; + struct nfs_pgio_header *hdr = data; - if (test_bit(NFS_IOHDR_REDO, &wdata->header->flags) && + if (test_bit(NFS_IOHDR_REDO, &hdr->flags) && task->tk_status == 0) { - nfs41_sequence_done(task, &wdata->res.seq_res); + nfs41_sequence_done(task, &hdr->res.seq_res); return; } /* Note this may cause RPC to be resent */ - wdata->header->mds_ops->rpc_call_done(task, data); + hdr->mds_ops->rpc_call_done(task, data); } static void filelayout_write_count_stats(struct rpc_task *task, void *data) { - struct nfs_pgio_data *wdata = data; + struct nfs_pgio_header *hdr = data; - rpc_count_iostats(task, NFS_SERVER(wdata->header->inode)->client->cl_metrics); + rpc_count_iostats(task, NFS_SERVER(hdr->inode)->client->cl_metrics); } static void filelayout_write_release(void *data) { - struct nfs_pgio_data *wdata = data; - struct pnfs_layout_hdr *lo = wdata->header->lseg->pls_layout; + struct nfs_pgio_header *hdr = data; + struct pnfs_layout_hdr *lo = hdr->lseg->pls_layout; filelayout_fenceme(lo->plh_inode, lo); - nfs_put_client(wdata->ds_clp); - wdata->header->mds_ops->rpc_release(data); + nfs_put_client(hdr->ds_clp); + hdr->mds_ops->rpc_release(data); } static void filelayout_commit_prepare(struct rpc_task *task, void *data) @@ -529,19 +524,18 @@ static const struct rpc_call_ops filelayout_commit_call_ops = { }; static enum pnfs_try_status -filelayout_read_pagelist(struct nfs_pgio_data *data) +filelayout_read_pagelist(struct nfs_pgio_header *hdr) { - struct nfs_pgio_header *hdr = data->header; struct pnfs_layout_segment *lseg = hdr->lseg; struct nfs4_pnfs_ds *ds; struct rpc_clnt *ds_clnt; - loff_t offset = data->args.offset; + loff_t offset = hdr->args.offset; u32 j, idx; struct nfs_fh *fh; dprintk("--> %s ino %lu pgbase %u req %Zu@%llu\n", __func__, hdr->inode->i_ino, - data->args.pgbase, (size_t)data->args.count, offset); + hdr->args.pgbase, (size_t)hdr->args.count, offset); /* Retrieve the correct rpc_client for the byte range */ j = nfs4_fl_calc_j_index(lseg, offset); @@ -559,30 +553,29 @@ filelayout_read_pagelist(struct nfs_pgio_data *data) /* No multipath support. Use first DS */ atomic_inc(&ds->ds_clp->cl_count); - data->ds_clp = ds->ds_clp; - data->ds_idx = idx; + hdr->ds_clp = ds->ds_clp; + hdr->ds_idx = idx; fh = nfs4_fl_select_ds_fh(lseg, j); if (fh) - data->args.fh = fh; + hdr->args.fh = fh; - data->args.offset = filelayout_get_dserver_offset(lseg, offset); - data->mds_offset = offset; + hdr->args.offset = filelayout_get_dserver_offset(lseg, offset); + hdr->mds_offset = offset; /* Perform an asynchronous read to ds */ - nfs_initiate_pgio(ds_clnt, data, + nfs_initiate_pgio(ds_clnt, hdr, &filelayout_read_call_ops, 0, RPC_TASK_SOFTCONN); return PNFS_ATTEMPTED; } /* Perform async writes. */ static enum pnfs_try_status -filelayout_write_pagelist(struct nfs_pgio_data *data, int sync) +filelayout_write_pagelist(struct nfs_pgio_header *hdr, int sync) { - struct nfs_pgio_header *hdr = data->header; struct pnfs_layout_segment *lseg = hdr->lseg; struct nfs4_pnfs_ds *ds; struct rpc_clnt *ds_clnt; - loff_t offset = data->args.offset; + loff_t offset = hdr->args.offset; u32 j, idx; struct nfs_fh *fh; @@ -598,21 +591,20 @@ filelayout_write_pagelist(struct nfs_pgio_data *data, int sync) return PNFS_NOT_ATTEMPTED; dprintk("%s ino %lu sync %d req %Zu@%llu DS: %s cl_count %d\n", - __func__, hdr->inode->i_ino, sync, (size_t) data->args.count, + __func__, hdr->inode->i_ino, sync, (size_t) hdr->args.count, offset, ds->ds_remotestr, atomic_read(&ds->ds_clp->cl_count)); - data->pgio_done_cb = filelayout_write_done_cb; + hdr->pgio_done_cb = filelayout_write_done_cb; atomic_inc(&ds->ds_clp->cl_count); - data->ds_clp = ds->ds_clp; - data->ds_idx = idx; + hdr->ds_clp = ds->ds_clp; + hdr->ds_idx = idx; fh = nfs4_fl_select_ds_fh(lseg, j); if (fh) - data->args.fh = fh; - - data->args.offset = filelayout_get_dserver_offset(lseg, offset); + hdr->args.fh = fh; + hdr->args.offset = filelayout_get_dserver_offset(lseg, offset); /* Perform an asynchronous write */ - nfs_initiate_pgio(ds_clnt, data, + nfs_initiate_pgio(ds_clnt, hdr, &filelayout_write_call_ops, sync, RPC_TASK_SOFTCONN); return PNFS_ATTEMPTED; diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 5cda049c8f9b..3f3aedd2e8c9 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -240,9 +240,9 @@ int nfs_iocounter_wait(struct nfs_io_counter *c); extern const struct nfs_pageio_ops nfs_pgio_rw_ops; struct nfs_pgio_header *nfs_pgio_header_alloc(const struct nfs_rw_ops *); void nfs_pgio_header_free(struct nfs_pgio_header *); -void nfs_pgio_data_destroy(struct nfs_pgio_data *); +void nfs_pgio_data_destroy(struct nfs_pgio_header *); int nfs_generic_pgio(struct nfs_pageio_descriptor *, struct nfs_pgio_header *); -int nfs_initiate_pgio(struct rpc_clnt *, struct nfs_pgio_data *, +int nfs_initiate_pgio(struct rpc_clnt *, struct nfs_pgio_header *, const struct rpc_call_ops *, int, int); static inline void nfs_iocounter_init(struct nfs_io_counter *c) @@ -481,7 +481,7 @@ static inline void nfs_inode_dio_wait(struct inode *inode) extern ssize_t nfs_dreq_bytes_left(struct nfs_direct_req *dreq); /* nfs4proc.c */ -extern void __nfs4_read_done_cb(struct nfs_pgio_data *); +extern void __nfs4_read_done_cb(struct nfs_pgio_header *); extern struct nfs_client *nfs4_init_client(struct nfs_client *clp, const struct rpc_timeout *timeparms, const char *ip_addr); diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index e7daa42bbc86..854959db0e5d 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -795,41 +795,44 @@ nfs3_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle, return status; } -static int nfs3_read_done(struct rpc_task *task, struct nfs_pgio_data *data) +static int nfs3_read_done(struct rpc_task *task, struct nfs_pgio_header *hdr) { - struct inode *inode = data->header->inode; + struct inode *inode = hdr->inode; if (nfs3_async_handle_jukebox(task, inode)) return -EAGAIN; nfs_invalidate_atime(inode); - nfs_refresh_inode(inode, &data->fattr); + nfs_refresh_inode(inode, &hdr->fattr); return 0; } -static void nfs3_proc_read_setup(struct nfs_pgio_data *data, struct rpc_message *msg) +static void nfs3_proc_read_setup(struct nfs_pgio_header *hdr, + struct rpc_message *msg) { msg->rpc_proc = &nfs3_procedures[NFS3PROC_READ]; } -static int nfs3_proc_pgio_rpc_prepare(struct rpc_task *task, struct nfs_pgio_data *data) +static int nfs3_proc_pgio_rpc_prepare(struct rpc_task *task, + struct nfs_pgio_header *hdr) { rpc_call_start(task); return 0; } -static int nfs3_write_done(struct rpc_task *task, struct nfs_pgio_data *data) +static int nfs3_write_done(struct rpc_task *task, struct nfs_pgio_header *hdr) { - struct inode *inode = data->header->inode; + struct inode *inode = hdr->inode; if (nfs3_async_handle_jukebox(task, inode)) return -EAGAIN; if (task->tk_status >= 0) - nfs_post_op_update_inode_force_wcc(inode, data->res.fattr); + nfs_post_op_update_inode_force_wcc(inode, hdr->res.fattr); return 0; } -static void nfs3_proc_write_setup(struct nfs_pgio_data *data, struct rpc_message *msg) +static void nfs3_proc_write_setup(struct nfs_pgio_header *hdr, + struct rpc_message *msg) { msg->rpc_proc = &nfs3_procedures[NFS3PROC_WRITE]; } diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index ba2affa51941..b8ea4a26998c 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -337,11 +337,11 @@ nfs4_state_protect(struct nfs_client *clp, unsigned long sp4_mode, */ static inline void nfs4_state_protect_write(struct nfs_client *clp, struct rpc_clnt **clntp, - struct rpc_message *msg, struct nfs_pgio_data *wdata) + struct rpc_message *msg, struct nfs_pgio_header *hdr) { if (_nfs4_state_protect(clp, NFS_SP4_MACH_CRED_WRITE, clntp, msg) && !test_bit(NFS_SP4_MACH_CRED_COMMIT, &clp->cl_sp4_flags)) - wdata->args.stable = NFS_FILE_SYNC; + hdr->args.stable = NFS_FILE_SYNC; } #else /* CONFIG_NFS_v4_1 */ static inline struct nfs4_session *nfs4_get_session(const struct nfs_server *server) @@ -369,7 +369,7 @@ nfs4_state_protect(struct nfs_client *clp, unsigned long sp4_flags, static inline void nfs4_state_protect_write(struct nfs_client *clp, struct rpc_clnt **clntp, - struct rpc_message *msg, struct nfs_pgio_data *wdata) + struct rpc_message *msg, struct nfs_pgio_header *hdr) { } #endif /* CONFIG_NFS_V4_1 */ diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 4bf3d97cc5a0..b0e5705599bf 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4033,24 +4033,25 @@ static bool nfs4_error_stateid_expired(int err) return false; } -void __nfs4_read_done_cb(struct nfs_pgio_data *data) +void __nfs4_read_done_cb(struct nfs_pgio_header *hdr) { - nfs_invalidate_atime(data->header->inode); + nfs_invalidate_atime(hdr->inode); } -static int nfs4_read_done_cb(struct rpc_task *task, struct nfs_pgio_data *data) +static int nfs4_read_done_cb(struct rpc_task *task, struct nfs_pgio_header *hdr) { - struct nfs_server *server = NFS_SERVER(data->header->inode); + struct nfs_server *server = NFS_SERVER(hdr->inode); - trace_nfs4_read(data, task->tk_status); - if (nfs4_async_handle_error(task, server, data->args.context->state) == -EAGAIN) { + trace_nfs4_read(hdr, task->tk_status); + if (nfs4_async_handle_error(task, server, + hdr->args.context->state) == -EAGAIN) { rpc_restart_call_prepare(task); return -EAGAIN; } - __nfs4_read_done_cb(data); + __nfs4_read_done_cb(hdr); if (task->tk_status > 0) - renew_lease(server, data->timestamp); + renew_lease(server, hdr->timestamp); return 0; } @@ -4068,54 +4069,59 @@ static bool nfs4_read_stateid_changed(struct rpc_task *task, return true; } -static int nfs4_read_done(struct rpc_task *task, struct nfs_pgio_data *data) +static int nfs4_read_done(struct rpc_task *task, struct nfs_pgio_header *hdr) { dprintk("--> %s\n", __func__); - if (!nfs4_sequence_done(task, &data->res.seq_res)) + if (!nfs4_sequence_done(task, &hdr->res.seq_res)) return -EAGAIN; - if (nfs4_read_stateid_changed(task, &data->args)) + if (nfs4_read_stateid_changed(task, &hdr->args)) return -EAGAIN; - return data->pgio_done_cb ? data->pgio_done_cb(task, data) : - nfs4_read_done_cb(task, data); + return hdr->pgio_done_cb ? hdr->pgio_done_cb(task, hdr) : + nfs4_read_done_cb(task, hdr); } -static void nfs4_proc_read_setup(struct nfs_pgio_data *data, struct rpc_message *msg) +static void nfs4_proc_read_setup(struct nfs_pgio_header *hdr, + struct rpc_message *msg) { - data->timestamp = jiffies; - data->pgio_done_cb = nfs4_read_done_cb; + hdr->timestamp = jiffies; + hdr->pgio_done_cb = nfs4_read_done_cb; msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ]; - nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0); + nfs4_init_sequence(&hdr->args.seq_args, &hdr->res.seq_res, 0); } -static int nfs4_proc_pgio_rpc_prepare(struct rpc_task *task, struct nfs_pgio_data *data) +static int nfs4_proc_pgio_rpc_prepare(struct rpc_task *task, + struct nfs_pgio_header *hdr) { - if (nfs4_setup_sequence(NFS_SERVER(data->header->inode), - &data->args.seq_args, - &data->res.seq_res, + if (nfs4_setup_sequence(NFS_SERVER(hdr->inode), + &hdr->args.seq_args, + &hdr->res.seq_res, task)) return 0; - if (nfs4_set_rw_stateid(&data->args.stateid, data->args.context, - data->args.lock_context, data->header->rw_ops->rw_mode) == -EIO) + if (nfs4_set_rw_stateid(&hdr->args.stateid, hdr->args.context, + hdr->args.lock_context, + hdr->rw_ops->rw_mode) == -EIO) return -EIO; - if (unlikely(test_bit(NFS_CONTEXT_BAD, &data->args.context->flags))) + if (unlikely(test_bit(NFS_CONTEXT_BAD, &hdr->args.context->flags))) return -EIO; return 0; } -static int nfs4_write_done_cb(struct rpc_task *task, struct nfs_pgio_data *data) +static int nfs4_write_done_cb(struct rpc_task *task, + struct nfs_pgio_header *hdr) { - struct inode *inode = data->header->inode; + struct inode *inode = hdr->inode; - trace_nfs4_write(data, task->tk_status); - if (nfs4_async_handle_error(task, NFS_SERVER(inode), data->args.context->state) == -EAGAIN) { + trace_nfs4_write(hdr, task->tk_status); + if (nfs4_async_handle_error(task, NFS_SERVER(inode), + hdr->args.context->state) == -EAGAIN) { rpc_restart_call_prepare(task); return -EAGAIN; } if (task->tk_status >= 0) { - renew_lease(NFS_SERVER(inode), data->timestamp); - nfs_post_op_update_inode_force_wcc(inode, &data->fattr); + renew_lease(NFS_SERVER(inode), hdr->timestamp); + nfs_post_op_update_inode_force_wcc(inode, &hdr->fattr); } return 0; } @@ -4134,23 +4140,21 @@ static bool nfs4_write_stateid_changed(struct rpc_task *task, return true; } -static int nfs4_write_done(struct rpc_task *task, struct nfs_pgio_data *data) +static int nfs4_write_done(struct rpc_task *task, struct nfs_pgio_header *hdr) { - if (!nfs4_sequence_done(task, &data->res.seq_res)) + if (!nfs4_sequence_done(task, &hdr->res.seq_res)) return -EAGAIN; - if (nfs4_write_stateid_changed(task, &data->args)) + if (nfs4_write_stateid_changed(task, &hdr->args)) return -EAGAIN; - return data->pgio_done_cb ? data->pgio_done_cb(task, data) : - nfs4_write_done_cb(task, data); + return hdr->pgio_done_cb ? hdr->pgio_done_cb(task, hdr) : + nfs4_write_done_cb(task, hdr); } static -bool nfs4_write_need_cache_consistency_data(const struct nfs_pgio_data *data) +bool nfs4_write_need_cache_consistency_data(struct nfs_pgio_header *hdr) { - const struct nfs_pgio_header *hdr = data->header; - /* Don't request attributes for pNFS or O_DIRECT writes */ - if (data->ds_clp != NULL || hdr->dreq != NULL) + if (hdr->ds_clp != NULL || hdr->dreq != NULL) return false; /* Otherwise, request attributes if and only if we don't hold * a delegation @@ -4158,23 +4162,24 @@ bool nfs4_write_need_cache_consistency_data(const struct nfs_pgio_data *data) return nfs4_have_delegation(hdr->inode, FMODE_READ) == 0; } -static void nfs4_proc_write_setup(struct nfs_pgio_data *data, struct rpc_message *msg) +static void nfs4_proc_write_setup(struct nfs_pgio_header *hdr, + struct rpc_message *msg) { - struct nfs_server *server = NFS_SERVER(data->header->inode); + struct nfs_server *server = NFS_SERVER(hdr->inode); - if (!nfs4_write_need_cache_consistency_data(data)) { - data->args.bitmask = NULL; - data->res.fattr = NULL; + if (!nfs4_write_need_cache_consistency_data(hdr)) { + hdr->args.bitmask = NULL; + hdr->res.fattr = NULL; } else - data->args.bitmask = server->cache_consistency_bitmask; + hdr->args.bitmask = server->cache_consistency_bitmask; - if (!data->pgio_done_cb) - data->pgio_done_cb = nfs4_write_done_cb; - data->res.server = server; - data->timestamp = jiffies; + if (!hdr->pgio_done_cb) + hdr->pgio_done_cb = nfs4_write_done_cb; + hdr->res.server = server; + hdr->timestamp = jiffies; msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_WRITE]; - nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 1); + nfs4_init_sequence(&hdr->args.seq_args, &hdr->res.seq_res, 1); } static void nfs4_proc_commit_rpc_prepare(struct rpc_task *task, struct nfs_commit_data *data) diff --git a/fs/nfs/nfs4trace.h b/fs/nfs/nfs4trace.h index 0a744f3a86f6..1c32adbe728d 100644 --- a/fs/nfs/nfs4trace.h +++ b/fs/nfs/nfs4trace.h @@ -932,11 +932,11 @@ DEFINE_NFS4_IDMAP_EVENT(nfs4_map_gid_to_group); DECLARE_EVENT_CLASS(nfs4_read_event, TP_PROTO( - const struct nfs_pgio_data *data, + const struct nfs_pgio_header *hdr, int error ), - TP_ARGS(data, error), + TP_ARGS(hdr, error), TP_STRUCT__entry( __field(dev_t, dev) @@ -948,12 +948,12 @@ DECLARE_EVENT_CLASS(nfs4_read_event, ), TP_fast_assign( - const struct inode *inode = data->header->inode; + const struct inode *inode = hdr->inode; __entry->dev = inode->i_sb->s_dev; __entry->fileid = NFS_FILEID(inode); __entry->fhandle = nfs_fhandle_hash(NFS_FH(inode)); - __entry->offset = data->args.offset; - __entry->count = data->args.count; + __entry->offset = hdr->args.offset; + __entry->count = hdr->args.count; __entry->error = error; ), @@ -972,10 +972,10 @@ DECLARE_EVENT_CLASS(nfs4_read_event, #define DEFINE_NFS4_READ_EVENT(name) \ DEFINE_EVENT(nfs4_read_event, name, \ TP_PROTO( \ - const struct nfs_pgio_data *data, \ + const struct nfs_pgio_header *hdr, \ int error \ ), \ - TP_ARGS(data, error)) + TP_ARGS(hdr, error)) DEFINE_NFS4_READ_EVENT(nfs4_read); #ifdef CONFIG_NFS_V4_1 DEFINE_NFS4_READ_EVENT(nfs4_pnfs_read); @@ -983,11 +983,11 @@ DEFINE_NFS4_READ_EVENT(nfs4_pnfs_read); DECLARE_EVENT_CLASS(nfs4_write_event, TP_PROTO( - const struct nfs_pgio_data *data, + const struct nfs_pgio_header *hdr, int error ), - TP_ARGS(data, error), + TP_ARGS(hdr, error), TP_STRUCT__entry( __field(dev_t, dev) @@ -999,12 +999,12 @@ DECLARE_EVENT_CLASS(nfs4_write_event, ), TP_fast_assign( - const struct inode *inode = data->header->inode; + const struct inode *inode = hdr->inode; __entry->dev = inode->i_sb->s_dev; __entry->fileid = NFS_FILEID(inode); __entry->fhandle = nfs_fhandle_hash(NFS_FH(inode)); - __entry->offset = data->args.offset; - __entry->count = data->args.count; + __entry->offset = hdr->args.offset; + __entry->count = hdr->args.count; __entry->error = error; ), @@ -1024,10 +1024,10 @@ DECLARE_EVENT_CLASS(nfs4_write_event, #define DEFINE_NFS4_WRITE_EVENT(name) \ DEFINE_EVENT(nfs4_write_event, name, \ TP_PROTO( \ - const struct nfs_pgio_data *data, \ + const struct nfs_pgio_header *hdr, \ int error \ ), \ - TP_ARGS(data, error)) + TP_ARGS(hdr, error)) DEFINE_NFS4_WRITE_EVENT(nfs4_write); #ifdef CONFIG_NFS_V4_1 DEFINE_NFS4_WRITE_EVENT(nfs4_pnfs_write); diff --git a/fs/nfs/objlayout/objio_osd.c b/fs/nfs/objlayout/objio_osd.c index 611320753db2..ae05278b3761 100644 --- a/fs/nfs/objlayout/objio_osd.c +++ b/fs/nfs/objlayout/objio_osd.c @@ -439,22 +439,21 @@ static void _read_done(struct ore_io_state *ios, void *private) objlayout_read_done(&objios->oir, status, objios->sync); } -int objio_read_pagelist(struct nfs_pgio_data *rdata) +int objio_read_pagelist(struct nfs_pgio_header *hdr) { - struct nfs_pgio_header *hdr = rdata->header; struct objio_state *objios; int ret; ret = objio_alloc_io_state(NFS_I(hdr->inode)->layout, true, - hdr->lseg, rdata->args.pages, rdata->args.pgbase, - rdata->args.offset, rdata->args.count, rdata, + hdr->lseg, hdr->args.pages, hdr->args.pgbase, + hdr->args.offset, hdr->args.count, hdr, GFP_KERNEL, &objios); if (unlikely(ret)) return ret; objios->ios->done = _read_done; dprintk("%s: offset=0x%llx length=0x%x\n", __func__, - rdata->args.offset, rdata->args.count); + hdr->args.offset, hdr->args.count); ret = ore_read(objios->ios); if (unlikely(ret)) objio_free_result(&objios->oir); @@ -487,11 +486,11 @@ static void _write_done(struct ore_io_state *ios, void *private) static struct page *__r4w_get_page(void *priv, u64 offset, bool *uptodate) { struct objio_state *objios = priv; - struct nfs_pgio_data *wdata = objios->oir.rpcdata; - struct address_space *mapping = wdata->header->inode->i_mapping; + struct nfs_pgio_header *hdr = objios->oir.rpcdata; + struct address_space *mapping = hdr->inode->i_mapping; pgoff_t index = offset / PAGE_SIZE; struct page *page; - loff_t i_size = i_size_read(wdata->header->inode); + loff_t i_size = i_size_read(hdr->inode); if (offset >= i_size) { *uptodate = true; @@ -531,15 +530,14 @@ static const struct _ore_r4w_op _r4w_op = { .put_page = &__r4w_put_page, }; -int objio_write_pagelist(struct nfs_pgio_data *wdata, int how) +int objio_write_pagelist(struct nfs_pgio_header *hdr, int how) { - struct nfs_pgio_header *hdr = wdata->header; struct objio_state *objios; int ret; ret = objio_alloc_io_state(NFS_I(hdr->inode)->layout, false, - hdr->lseg, wdata->args.pages, wdata->args.pgbase, - wdata->args.offset, wdata->args.count, wdata, GFP_NOFS, + hdr->lseg, hdr->args.pages, hdr->args.pgbase, + hdr->args.offset, hdr->args.count, hdr, GFP_NOFS, &objios); if (unlikely(ret)) return ret; @@ -551,7 +549,7 @@ int objio_write_pagelist(struct nfs_pgio_data *wdata, int how) objios->ios->done = _write_done; dprintk("%s: offset=0x%llx length=0x%x\n", __func__, - wdata->args.offset, wdata->args.count); + hdr->args.offset, hdr->args.count); ret = ore_write(objios->ios); if (unlikely(ret)) { objio_free_result(&objios->oir); diff --git a/fs/nfs/objlayout/objlayout.c b/fs/nfs/objlayout/objlayout.c index 31fed91a8bac..86312787cee6 100644 --- a/fs/nfs/objlayout/objlayout.c +++ b/fs/nfs/objlayout/objlayout.c @@ -229,36 +229,36 @@ objlayout_io_set_result(struct objlayout_io_res *oir, unsigned index, static void _rpc_read_complete(struct work_struct *work) { struct rpc_task *task; - struct nfs_pgio_data *rdata; + struct nfs_pgio_header *hdr; dprintk("%s enter\n", __func__); task = container_of(work, struct rpc_task, u.tk_work); - rdata = container_of(task, struct nfs_pgio_data, task); + hdr = container_of(task, struct nfs_pgio_header, task); - pnfs_ld_read_done(rdata); + pnfs_ld_read_done(hdr); } void objlayout_read_done(struct objlayout_io_res *oir, ssize_t status, bool sync) { - struct nfs_pgio_data *rdata = oir->rpcdata; + struct nfs_pgio_header *hdr = oir->rpcdata; - oir->status = rdata->task.tk_status = status; + oir->status = hdr->task.tk_status = status; if (status >= 0) - rdata->res.count = status; + hdr->res.count = status; else - rdata->header->pnfs_error = status; + hdr->pnfs_error = status; objlayout_iodone(oir); /* must not use oir after this point */ dprintk("%s: Return status=%zd eof=%d sync=%d\n", __func__, - status, rdata->res.eof, sync); + status, hdr->res.eof, sync); if (sync) - pnfs_ld_read_done(rdata); + pnfs_ld_read_done(hdr); else { - INIT_WORK(&rdata->task.u.tk_work, _rpc_read_complete); - schedule_work(&rdata->task.u.tk_work); + INIT_WORK(&hdr->task.u.tk_work, _rpc_read_complete); + schedule_work(&hdr->task.u.tk_work); } } @@ -266,12 +266,11 @@ objlayout_read_done(struct objlayout_io_res *oir, ssize_t status, bool sync) * Perform sync or async reads. */ enum pnfs_try_status -objlayout_read_pagelist(struct nfs_pgio_data *rdata) +objlayout_read_pagelist(struct nfs_pgio_header *hdr) { - struct nfs_pgio_header *hdr = rdata->header; struct inode *inode = hdr->inode; - loff_t offset = rdata->args.offset; - size_t count = rdata->args.count; + loff_t offset = hdr->args.offset; + size_t count = hdr->args.count; int err; loff_t eof; @@ -279,23 +278,23 @@ objlayout_read_pagelist(struct nfs_pgio_data *rdata) if (unlikely(offset + count > eof)) { if (offset >= eof) { err = 0; - rdata->res.count = 0; - rdata->res.eof = 1; + hdr->res.count = 0; + hdr->res.eof = 1; /*FIXME: do we need to call pnfs_ld_read_done() */ goto out; } count = eof - offset; } - rdata->res.eof = (offset + count) >= eof; - _fix_verify_io_params(hdr->lseg, &rdata->args.pages, - &rdata->args.pgbase, - rdata->args.offset, rdata->args.count); + hdr->res.eof = (offset + count) >= eof; + _fix_verify_io_params(hdr->lseg, &hdr->args.pages, + &hdr->args.pgbase, + hdr->args.offset, hdr->args.count); dprintk("%s: inode(%lx) offset 0x%llx count 0x%Zx eof=%d\n", - __func__, inode->i_ino, offset, count, rdata->res.eof); + __func__, inode->i_ino, offset, count, hdr->res.eof); - err = objio_read_pagelist(rdata); + err = objio_read_pagelist(hdr); out: if (unlikely(err)) { hdr->pnfs_error = err; @@ -312,38 +311,38 @@ objlayout_read_pagelist(struct nfs_pgio_data *rdata) static void _rpc_write_complete(struct work_struct *work) { struct rpc_task *task; - struct nfs_pgio_data *wdata; + struct nfs_pgio_header *hdr; dprintk("%s enter\n", __func__); task = container_of(work, struct rpc_task, u.tk_work); - wdata = container_of(task, struct nfs_pgio_data, task); + hdr = container_of(task, struct nfs_pgio_header, task); - pnfs_ld_write_done(wdata); + pnfs_ld_write_done(hdr); } void objlayout_write_done(struct objlayout_io_res *oir, ssize_t status, bool sync) { - struct nfs_pgio_data *wdata = oir->rpcdata; + struct nfs_pgio_header *hdr = oir->rpcdata; - oir->status = wdata->task.tk_status = status; + oir->status = hdr->task.tk_status = status; if (status >= 0) { - wdata->res.count = status; - wdata->writeverf.committed = oir->committed; + hdr->res.count = status; + hdr->writeverf.committed = oir->committed; } else { - wdata->header->pnfs_error = status; + hdr->pnfs_error = status; } objlayout_iodone(oir); /* must not use oir after this point */ dprintk("%s: Return status %zd committed %d sync=%d\n", __func__, - status, wdata->writeverf.committed, sync); + status, hdr->writeverf.committed, sync); if (sync) - pnfs_ld_write_done(wdata); + pnfs_ld_write_done(hdr); else { - INIT_WORK(&wdata->task.u.tk_work, _rpc_write_complete); - schedule_work(&wdata->task.u.tk_work); + INIT_WORK(&hdr->task.u.tk_work, _rpc_write_complete); + schedule_work(&hdr->task.u.tk_work); } } @@ -351,17 +350,15 @@ objlayout_write_done(struct objlayout_io_res *oir, ssize_t status, bool sync) * Perform sync or async writes. */ enum pnfs_try_status -objlayout_write_pagelist(struct nfs_pgio_data *wdata, - int how) +objlayout_write_pagelist(struct nfs_pgio_header *hdr, int how) { - struct nfs_pgio_header *hdr = wdata->header; int err; - _fix_verify_io_params(hdr->lseg, &wdata->args.pages, - &wdata->args.pgbase, - wdata->args.offset, wdata->args.count); + _fix_verify_io_params(hdr->lseg, &hdr->args.pages, + &hdr->args.pgbase, + hdr->args.offset, hdr->args.count); - err = objio_write_pagelist(wdata, how); + err = objio_write_pagelist(hdr, how); if (unlikely(err)) { hdr->pnfs_error = err; dprintk("%s: Returned Error %d\n", __func__, err); diff --git a/fs/nfs/objlayout/objlayout.h b/fs/nfs/objlayout/objlayout.h index 01e041029a6c..fd13f1d2f136 100644 --- a/fs/nfs/objlayout/objlayout.h +++ b/fs/nfs/objlayout/objlayout.h @@ -119,8 +119,8 @@ extern void objio_free_lseg(struct pnfs_layout_segment *lseg); */ extern void objio_free_result(struct objlayout_io_res *oir); -extern int objio_read_pagelist(struct nfs_pgio_data *rdata); -extern int objio_write_pagelist(struct nfs_pgio_data *wdata, int how); +extern int objio_read_pagelist(struct nfs_pgio_header *rdata); +extern int objio_write_pagelist(struct nfs_pgio_header *wdata, int how); /* * callback API @@ -168,10 +168,10 @@ extern struct pnfs_layout_segment *objlayout_alloc_lseg( extern void objlayout_free_lseg(struct pnfs_layout_segment *); extern enum pnfs_try_status objlayout_read_pagelist( - struct nfs_pgio_data *); + struct nfs_pgio_header *); extern enum pnfs_try_status objlayout_write_pagelist( - struct nfs_pgio_data *, + struct nfs_pgio_header *, int how); extern void objlayout_encode_layoutcommit( diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index 5e70918f6c95..ecb3d4cdbc85 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -484,8 +484,7 @@ EXPORT_SYMBOL_GPL(nfs_pgio_header_free); static bool nfs_pgio_data_init(struct nfs_pgio_header *hdr, unsigned int pagecount) { - if (nfs_pgarray_set(&hdr->data.page_array, pagecount)) { - hdr->data.header = hdr; + if (nfs_pgarray_set(&hdr->page_array, pagecount)) { atomic_inc(&hdr->refcnt); return true; } @@ -493,16 +492,14 @@ static bool nfs_pgio_data_init(struct nfs_pgio_header *hdr, } /** - * nfs_pgio_data_destroy - Properly free pageio data - * @data: The data to destroy + * nfs_pgio_data_destroy - Properly release pageio data + * @hdr: The header with data to destroy */ -void nfs_pgio_data_destroy(struct nfs_pgio_data *data) +void nfs_pgio_data_destroy(struct nfs_pgio_header *hdr) { - struct nfs_pgio_header *hdr = data->header; - - put_nfs_open_context(data->args.context); - if (data->page_array.pagevec != data->page_array.page_array) - kfree(data->page_array.pagevec); + put_nfs_open_context(hdr->args.context); + if (hdr->page_array.pagevec != hdr->page_array.page_array) + kfree(hdr->page_array.pagevec); if (atomic_dec_and_test(&hdr->refcnt)) hdr->completion_ops->completion(hdr); } @@ -510,31 +507,31 @@ EXPORT_SYMBOL_GPL(nfs_pgio_data_destroy); /** * nfs_pgio_rpcsetup - Set up arguments for a pageio call - * @data: The pageio data + * @hdr: The pageio hdr * @count: Number of bytes to read * @offset: Initial offset * @how: How to commit data (writes only) * @cinfo: Commit information for the call (writes only) */ -static void nfs_pgio_rpcsetup(struct nfs_pgio_data *data, +static void nfs_pgio_rpcsetup(struct nfs_pgio_header *hdr, unsigned int count, unsigned int offset, int how, struct nfs_commit_info *cinfo) { - struct nfs_page *req = data->header->req; + struct nfs_page *req = hdr->req; /* Set up the RPC argument and reply structs - * NB: take care not to mess about with data->commit et al. */ + * NB: take care not to mess about with hdr->commit et al. */ - data->args.fh = NFS_FH(data->header->inode); - data->args.offset = req_offset(req) + offset; + hdr->args.fh = NFS_FH(hdr->inode); + hdr->args.offset = req_offset(req) + offset; /* pnfs_set_layoutcommit needs this */ - data->mds_offset = data->args.offset; - data->args.pgbase = req->wb_pgbase + offset; - data->args.pages = data->page_array.pagevec; - data->args.count = count; - data->args.context = get_nfs_open_context(req->wb_context); - data->args.lock_context = req->wb_lock_context; - data->args.stable = NFS_UNSTABLE; + hdr->mds_offset = hdr->args.offset; + hdr->args.pgbase = req->wb_pgbase + offset; + hdr->args.pages = hdr->page_array.pagevec; + hdr->args.count = count; + hdr->args.context = get_nfs_open_context(req->wb_context); + hdr->args.lock_context = req->wb_lock_context; + hdr->args.stable = NFS_UNSTABLE; switch (how & (FLUSH_STABLE | FLUSH_COND_STABLE)) { case 0: break; @@ -542,59 +539,60 @@ static void nfs_pgio_rpcsetup(struct nfs_pgio_data *data, if (nfs_reqs_to_commit(cinfo)) break; default: - data->args.stable = NFS_FILE_SYNC; + hdr->args.stable = NFS_FILE_SYNC; } - data->res.fattr = &data->fattr; - data->res.count = count; - data->res.eof = 0; - data->res.verf = &data->writeverf; - nfs_fattr_init(&data->fattr); + hdr->res.fattr = &hdr->fattr; + hdr->res.count = count; + hdr->res.eof = 0; + hdr->res.verf = &hdr->writeverf; + nfs_fattr_init(&hdr->fattr); } /** - * nfs_pgio_prepare - Prepare pageio data to go over the wire + * nfs_pgio_prepare - Prepare pageio hdr to go over the wire * @task: The current task - * @calldata: pageio data to prepare + * @calldata: pageio header to prepare */ static void nfs_pgio_prepare(struct rpc_task *task, void *calldata) { - struct nfs_pgio_data *data = calldata; + struct nfs_pgio_header *hdr = calldata; int err; - err = NFS_PROTO(data->header->inode)->pgio_rpc_prepare(task, data); + err = NFS_PROTO(hdr->inode)->pgio_rpc_prepare(task, hdr); if (err) rpc_exit(task, err); } -int nfs_initiate_pgio(struct rpc_clnt *clnt, struct nfs_pgio_data *data, +int nfs_initiate_pgio(struct rpc_clnt *clnt, struct nfs_pgio_header *hdr, const struct rpc_call_ops *call_ops, int how, int flags) { + struct inode *inode = hdr->inode; struct rpc_task *task; struct rpc_message msg = { - .rpc_argp = &data->args, - .rpc_resp = &data->res, - .rpc_cred = data->header->cred, + .rpc_argp = &hdr->args, + .rpc_resp = &hdr->res, + .rpc_cred = hdr->cred, }; struct rpc_task_setup task_setup_data = { .rpc_client = clnt, - .task = &data->task, + .task = &hdr->task, .rpc_message = &msg, .callback_ops = call_ops, - .callback_data = data, + .callback_data = hdr, .workqueue = nfsiod_workqueue, .flags = RPC_TASK_ASYNC | flags, }; int ret = 0; - data->header->rw_ops->rw_initiate(data, &msg, &task_setup_data, how); + hdr->rw_ops->rw_initiate(hdr, &msg, &task_setup_data, how); dprintk("NFS: %5u initiated pgio call " "(req %s/%llu, %u bytes @ offset %llu)\n", - data->task.tk_pid, - data->header->inode->i_sb->s_id, - (unsigned long long)NFS_FILEID(data->header->inode), - data->args.count, - (unsigned long long)data->args.offset); + hdr->task.tk_pid, + inode->i_sb->s_id, + (unsigned long long)NFS_FILEID(inode), + hdr->args.count, + (unsigned long long)hdr->args.offset); task = rpc_run_task(&task_setup_data); if (IS_ERR(task)) { @@ -621,21 +619,21 @@ static int nfs_pgio_error(struct nfs_pageio_descriptor *desc, struct nfs_pgio_header *hdr) { set_bit(NFS_IOHDR_REDO, &hdr->flags); - nfs_pgio_data_destroy(&hdr->data); + nfs_pgio_data_destroy(hdr); desc->pg_completion_ops->error_cleanup(&desc->pg_list); return -ENOMEM; } /** * nfs_pgio_release - Release pageio data - * @calldata: The pageio data to release + * @calldata: The pageio header to release */ static void nfs_pgio_release(void *calldata) { - struct nfs_pgio_data *data = calldata; - if (data->header->rw_ops->rw_release) - data->header->rw_ops->rw_release(data); - nfs_pgio_data_destroy(data); + struct nfs_pgio_header *hdr = calldata; + if (hdr->rw_ops->rw_release) + hdr->rw_ops->rw_release(hdr); + nfs_pgio_data_destroy(hdr); } /** @@ -676,22 +674,22 @@ EXPORT_SYMBOL_GPL(nfs_pageio_init); /** * nfs_pgio_result - Basic pageio error handling * @task: The task that ran - * @calldata: Pageio data to check + * @calldata: Pageio header to check */ static void nfs_pgio_result(struct rpc_task *task, void *calldata) { - struct nfs_pgio_data *data = calldata; - struct inode *inode = data->header->inode; + struct nfs_pgio_header *hdr = calldata; + struct inode *inode = hdr->inode; dprintk("NFS: %s: %5u, (status %d)\n", __func__, task->tk_pid, task->tk_status); - if (data->header->rw_ops->rw_done(task, data, inode) != 0) + if (hdr->rw_ops->rw_done(task, hdr, inode) != 0) return; if (task->tk_status < 0) - nfs_set_pgio_error(data->header, task->tk_status, data->args.offset); + nfs_set_pgio_error(hdr, task->tk_status, hdr->args.offset); else - data->header->rw_ops->rw_result(task, data); + hdr->rw_ops->rw_result(task, hdr); } /* @@ -707,7 +705,6 @@ int nfs_generic_pgio(struct nfs_pageio_descriptor *desc, { struct nfs_page *req; struct page **pages; - struct nfs_pgio_data *data; struct list_head *head = &desc->pg_list; struct nfs_commit_info cinfo; @@ -715,9 +712,8 @@ int nfs_generic_pgio(struct nfs_pageio_descriptor *desc, desc->pg_count))) return nfs_pgio_error(desc, hdr); - data = &hdr->data; nfs_init_cinfo(&cinfo, desc->pg_inode, desc->pg_dreq); - pages = data->page_array.pagevec; + pages = hdr->page_array.pagevec; while (!list_empty(head)) { req = nfs_list_entry(head->next); nfs_list_remove_request(req); @@ -730,7 +726,7 @@ int nfs_generic_pgio(struct nfs_pageio_descriptor *desc, desc->pg_ioflags &= ~FLUSH_COND_STABLE; /* Set up the argument struct */ - nfs_pgio_rpcsetup(data, desc->pg_count, 0, desc->pg_ioflags, &cinfo); + nfs_pgio_rpcsetup(hdr, desc->pg_count, 0, desc->pg_ioflags, &cinfo); desc->pg_rpc_callops = &nfs_pgio_common_ops; return 0; } @@ -751,7 +747,7 @@ static int nfs_generic_pg_pgios(struct nfs_pageio_descriptor *desc) ret = nfs_generic_pgio(desc, hdr); if (ret == 0) ret = nfs_initiate_pgio(NFS_CLIENT(hdr->inode), - &hdr->data, desc->pg_rpc_callops, + hdr, desc->pg_rpc_callops, desc->pg_ioflags, 0); if (atomic_dec_and_test(&hdr->refcnt)) hdr->completion_ops->completion(hdr); diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index 067104cce181..ecc911347750 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -1502,9 +1502,8 @@ int pnfs_write_done_resend_to_mds(struct inode *inode, } EXPORT_SYMBOL_GPL(pnfs_write_done_resend_to_mds); -static void pnfs_ld_handle_write_error(struct nfs_pgio_data *data) +static void pnfs_ld_handle_write_error(struct nfs_pgio_header *hdr) { - struct nfs_pgio_header *hdr = data->header; dprintk("pnfs write error = %d\n", hdr->pnfs_error); if (NFS_SERVER(hdr->inode)->pnfs_curr_ld->flags & @@ -1512,7 +1511,7 @@ static void pnfs_ld_handle_write_error(struct nfs_pgio_data *data) pnfs_return_layout(hdr->inode); } if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags)) - data->task.tk_status = pnfs_write_done_resend_to_mds(hdr->inode, + hdr->task.tk_status = pnfs_write_done_resend_to_mds(hdr->inode, &hdr->pages, hdr->completion_ops, hdr->dreq); @@ -1521,41 +1520,36 @@ static void pnfs_ld_handle_write_error(struct nfs_pgio_data *data) /* * Called by non rpc-based layout drivers */ -void pnfs_ld_write_done(struct nfs_pgio_data *data) +void pnfs_ld_write_done(struct nfs_pgio_header *hdr) { - struct nfs_pgio_header *hdr = data->header; - - trace_nfs4_pnfs_write(data, hdr->pnfs_error); + trace_nfs4_pnfs_write(hdr, hdr->pnfs_error); if (!hdr->pnfs_error) { - pnfs_set_layoutcommit(data); - hdr->mds_ops->rpc_call_done(&data->task, data); + pnfs_set_layoutcommit(hdr); + hdr->mds_ops->rpc_call_done(&hdr->task, hdr); } else - pnfs_ld_handle_write_error(data); - hdr->mds_ops->rpc_release(data); + pnfs_ld_handle_write_error(hdr); + hdr->mds_ops->rpc_release(hdr); } EXPORT_SYMBOL_GPL(pnfs_ld_write_done); static void pnfs_write_through_mds(struct nfs_pageio_descriptor *desc, - struct nfs_pgio_data *data) + struct nfs_pgio_header *hdr) { - struct nfs_pgio_header *hdr = data->header; - if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags)) { list_splice_tail_init(&hdr->pages, &desc->pg_list); nfs_pageio_reset_write_mds(desc); desc->pg_recoalesce = 1; } - nfs_pgio_data_destroy(data); + nfs_pgio_data_destroy(hdr); } static enum pnfs_try_status -pnfs_try_to_write_data(struct nfs_pgio_data *wdata, +pnfs_try_to_write_data(struct nfs_pgio_header *hdr, const struct rpc_call_ops *call_ops, struct pnfs_layout_segment *lseg, int how) { - struct nfs_pgio_header *hdr = wdata->header; struct inode *inode = hdr->inode; enum pnfs_try_status trypnfs; struct nfs_server *nfss = NFS_SERVER(inode); @@ -1563,8 +1557,8 @@ pnfs_try_to_write_data(struct nfs_pgio_data *wdata, hdr->mds_ops = call_ops; dprintk("%s: Writing ino:%lu %u@%llu (how %d)\n", __func__, - inode->i_ino, wdata->args.count, wdata->args.offset, how); - trypnfs = nfss->pnfs_curr_ld->write_pagelist(wdata, how); + inode->i_ino, hdr->args.count, hdr->args.offset, how); + trypnfs = nfss->pnfs_curr_ld->write_pagelist(hdr, how); if (trypnfs != PNFS_NOT_ATTEMPTED) nfs_inc_stats(inode, NFSIOS_PNFS_WRITE); dprintk("%s End (trypnfs:%d)\n", __func__, trypnfs); @@ -1575,15 +1569,14 @@ static void pnfs_do_write(struct nfs_pageio_descriptor *desc, struct nfs_pgio_header *hdr, int how) { - struct nfs_pgio_data *data = &hdr->data; const struct rpc_call_ops *call_ops = desc->pg_rpc_callops; struct pnfs_layout_segment *lseg = desc->pg_lseg; enum pnfs_try_status trypnfs; desc->pg_lseg = NULL; - trypnfs = pnfs_try_to_write_data(data, call_ops, lseg, how); + trypnfs = pnfs_try_to_write_data(hdr, call_ops, lseg, how); if (trypnfs == PNFS_NOT_ATTEMPTED) - pnfs_write_through_mds(desc, data); + pnfs_write_through_mds(desc, hdr); pnfs_put_lseg(lseg); } @@ -1650,17 +1643,15 @@ int pnfs_read_done_resend_to_mds(struct inode *inode, } EXPORT_SYMBOL_GPL(pnfs_read_done_resend_to_mds); -static void pnfs_ld_handle_read_error(struct nfs_pgio_data *data) +static void pnfs_ld_handle_read_error(struct nfs_pgio_header *hdr) { - struct nfs_pgio_header *hdr = data->header; - dprintk("pnfs read error = %d\n", hdr->pnfs_error); if (NFS_SERVER(hdr->inode)->pnfs_curr_ld->flags & PNFS_LAYOUTRET_ON_ERROR) { pnfs_return_layout(hdr->inode); } if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags)) - data->task.tk_status = pnfs_read_done_resend_to_mds(hdr->inode, + hdr->task.tk_status = pnfs_read_done_resend_to_mds(hdr->inode, &hdr->pages, hdr->completion_ops, hdr->dreq); @@ -1669,43 +1660,38 @@ static void pnfs_ld_handle_read_error(struct nfs_pgio_data *data) /* * Called by non rpc-based layout drivers */ -void pnfs_ld_read_done(struct nfs_pgio_data *data) +void pnfs_ld_read_done(struct nfs_pgio_header *hdr) { - struct nfs_pgio_header *hdr = data->header; - - trace_nfs4_pnfs_read(data, hdr->pnfs_error); + trace_nfs4_pnfs_read(hdr, hdr->pnfs_error); if (likely(!hdr->pnfs_error)) { - __nfs4_read_done_cb(data); - hdr->mds_ops->rpc_call_done(&data->task, data); + __nfs4_read_done_cb(hdr); + hdr->mds_ops->rpc_call_done(&hdr->task, hdr); } else - pnfs_ld_handle_read_error(data); - hdr->mds_ops->rpc_release(data); + pnfs_ld_handle_read_error(hdr); + hdr->mds_ops->rpc_release(hdr); } EXPORT_SYMBOL_GPL(pnfs_ld_read_done); static void pnfs_read_through_mds(struct nfs_pageio_descriptor *desc, - struct nfs_pgio_data *data) + struct nfs_pgio_header *hdr) { - struct nfs_pgio_header *hdr = data->header; - if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags)) { list_splice_tail_init(&hdr->pages, &desc->pg_list); nfs_pageio_reset_read_mds(desc); desc->pg_recoalesce = 1; } - nfs_pgio_data_destroy(data); + nfs_pgio_data_destroy(hdr); } /* * Call the appropriate parallel I/O subsystem read function. */ static enum pnfs_try_status -pnfs_try_to_read_data(struct nfs_pgio_data *rdata, +pnfs_try_to_read_data(struct nfs_pgio_header *hdr, const struct rpc_call_ops *call_ops, struct pnfs_layout_segment *lseg) { - struct nfs_pgio_header *hdr = rdata->header; struct inode *inode = hdr->inode; struct nfs_server *nfss = NFS_SERVER(inode); enum pnfs_try_status trypnfs; @@ -1713,9 +1699,9 @@ pnfs_try_to_read_data(struct nfs_pgio_data *rdata, hdr->mds_ops = call_ops; dprintk("%s: Reading ino:%lu %u@%llu\n", - __func__, inode->i_ino, rdata->args.count, rdata->args.offset); + __func__, inode->i_ino, hdr->args.count, hdr->args.offset); - trypnfs = nfss->pnfs_curr_ld->read_pagelist(rdata); + trypnfs = nfss->pnfs_curr_ld->read_pagelist(hdr); if (trypnfs != PNFS_NOT_ATTEMPTED) nfs_inc_stats(inode, NFSIOS_PNFS_READ); dprintk("%s End (trypnfs:%d)\n", __func__, trypnfs); @@ -1725,15 +1711,14 @@ pnfs_try_to_read_data(struct nfs_pgio_data *rdata, static void pnfs_do_read(struct nfs_pageio_descriptor *desc, struct nfs_pgio_header *hdr) { - struct nfs_pgio_data *data = &hdr->data; const struct rpc_call_ops *call_ops = desc->pg_rpc_callops; struct pnfs_layout_segment *lseg = desc->pg_lseg; enum pnfs_try_status trypnfs; desc->pg_lseg = NULL; - trypnfs = pnfs_try_to_read_data(data, call_ops, lseg); + trypnfs = pnfs_try_to_read_data(hdr, call_ops, lseg); if (trypnfs == PNFS_NOT_ATTEMPTED) - pnfs_read_through_mds(desc, data); + pnfs_read_through_mds(desc, hdr); pnfs_put_lseg(lseg); } @@ -1816,12 +1801,11 @@ void pnfs_set_lo_fail(struct pnfs_layout_segment *lseg) EXPORT_SYMBOL_GPL(pnfs_set_lo_fail); void -pnfs_set_layoutcommit(struct nfs_pgio_data *wdata) +pnfs_set_layoutcommit(struct nfs_pgio_header *hdr) { - struct nfs_pgio_header *hdr = wdata->header; struct inode *inode = hdr->inode; struct nfs_inode *nfsi = NFS_I(inode); - loff_t end_pos = wdata->mds_offset + wdata->res.count; + loff_t end_pos = hdr->mds_offset + hdr->res.count; bool mark_as_dirty = false; spin_lock(&inode->i_lock); diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index 4fb309a2b4c4..a4a58be94064 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h @@ -113,8 +113,8 @@ struct pnfs_layoutdriver_type { * Return PNFS_ATTEMPTED to indicate the layout code has attempted * I/O, else return PNFS_NOT_ATTEMPTED to fall back to normal NFS */ - enum pnfs_try_status (*read_pagelist) (struct nfs_pgio_data *nfs_data); - enum pnfs_try_status (*write_pagelist) (struct nfs_pgio_data *nfs_data, int how); + enum pnfs_try_status (*read_pagelist)(struct nfs_pgio_header *); + enum pnfs_try_status (*write_pagelist)(struct nfs_pgio_header *, int); void (*free_deviceid_node) (struct nfs4_deviceid_node *); @@ -213,13 +213,13 @@ bool pnfs_roc(struct inode *ino); void pnfs_roc_release(struct inode *ino); void pnfs_roc_set_barrier(struct inode *ino, u32 barrier); bool pnfs_roc_drain(struct inode *ino, u32 *barrier, struct rpc_task *task); -void pnfs_set_layoutcommit(struct nfs_pgio_data *wdata); +void pnfs_set_layoutcommit(struct nfs_pgio_header *); void pnfs_cleanup_layoutcommit(struct nfs4_layoutcommit_data *data); int pnfs_layoutcommit_inode(struct inode *inode, bool sync); int _pnfs_return_layout(struct inode *); int pnfs_commit_and_return_layout(struct inode *); -void pnfs_ld_write_done(struct nfs_pgio_data *); -void pnfs_ld_read_done(struct nfs_pgio_data *); +void pnfs_ld_write_done(struct nfs_pgio_header *); +void pnfs_ld_read_done(struct nfs_pgio_header *); struct pnfs_layout_segment *pnfs_update_layout(struct inode *ino, struct nfs_open_context *ctx, loff_t pos, diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index c171ce1a8a30..b09cc23d6f43 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -578,46 +578,49 @@ nfs_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle, return 0; } -static int nfs_read_done(struct rpc_task *task, struct nfs_pgio_data *data) +static int nfs_read_done(struct rpc_task *task, struct nfs_pgio_header *hdr) { - struct inode *inode = data->header->inode; + struct inode *inode = hdr->inode; nfs_invalidate_atime(inode); if (task->tk_status >= 0) { - nfs_refresh_inode(inode, data->res.fattr); + nfs_refresh_inode(inode, hdr->res.fattr); /* Emulate the eof flag, which isn't normally needed in NFSv2 * as it is guaranteed to always return the file attributes */ - if (data->args.offset + data->res.count >= data->res.fattr->size) - data->res.eof = 1; + if (hdr->args.offset + hdr->res.count >= hdr->res.fattr->size) + hdr->res.eof = 1; } return 0; } -static void nfs_proc_read_setup(struct nfs_pgio_data *data, struct rpc_message *msg) +static void nfs_proc_read_setup(struct nfs_pgio_header *hdr, + struct rpc_message *msg) { msg->rpc_proc = &nfs_procedures[NFSPROC_READ]; } -static int nfs_proc_pgio_rpc_prepare(struct rpc_task *task, struct nfs_pgio_data *data) +static int nfs_proc_pgio_rpc_prepare(struct rpc_task *task, + struct nfs_pgio_header *hdr) { rpc_call_start(task); return 0; } -static int nfs_write_done(struct rpc_task *task, struct nfs_pgio_data *data) +static int nfs_write_done(struct rpc_task *task, struct nfs_pgio_header *hdr) { - struct inode *inode = data->header->inode; + struct inode *inode = hdr->inode; if (task->tk_status >= 0) - nfs_post_op_update_inode_force_wcc(inode, data->res.fattr); + nfs_post_op_update_inode_force_wcc(inode, hdr->res.fattr); return 0; } -static void nfs_proc_write_setup(struct nfs_pgio_data *data, struct rpc_message *msg) +static void nfs_proc_write_setup(struct nfs_pgio_header *hdr, + struct rpc_message *msg) { /* Note: NFSv2 ignores @stable and always uses NFS_FILE_SYNC */ - data->args.stable = NFS_FILE_SYNC; + hdr->args.stable = NFS_FILE_SYNC; msg->rpc_proc = &nfs_procedures[NFSPROC_WRITE]; } diff --git a/fs/nfs/read.c b/fs/nfs/read.c index d9df4ab3737b..b1532b73fea3 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -172,14 +172,15 @@ out: hdr->release(hdr); } -static void nfs_initiate_read(struct nfs_pgio_data *data, struct rpc_message *msg, +static void nfs_initiate_read(struct nfs_pgio_header *hdr, + struct rpc_message *msg, struct rpc_task_setup *task_setup_data, int how) { - struct inode *inode = data->header->inode; + struct inode *inode = hdr->inode; int swap_flags = IS_SWAPFILE(inode) ? NFS_RPC_SWAPFLAGS : 0; task_setup_data->flags |= swap_flags; - NFS_PROTO(inode)->read_setup(data, msg); + NFS_PROTO(inode)->read_setup(hdr, msg); } static void @@ -203,14 +204,15 @@ static const struct nfs_pgio_completion_ops nfs_async_read_completion_ops = { * This is the callback from RPC telling us whether a reply was * received or some error occurred (timeout or socket shutdown). */ -static int nfs_readpage_done(struct rpc_task *task, struct nfs_pgio_data *data, +static int nfs_readpage_done(struct rpc_task *task, + struct nfs_pgio_header *hdr, struct inode *inode) { - int status = NFS_PROTO(inode)->read_done(task, data); + int status = NFS_PROTO(inode)->read_done(task, hdr); if (status != 0) return status; - nfs_add_stats(inode, NFSIOS_SERVERREADBYTES, data->res.count); + nfs_add_stats(inode, NFSIOS_SERVERREADBYTES, hdr->res.count); if (task->tk_status == -ESTALE) { set_bit(NFS_INO_STALE, &NFS_I(inode)->flags); @@ -219,34 +221,34 @@ static int nfs_readpage_done(struct rpc_task *task, struct nfs_pgio_data *data, return 0; } -static void nfs_readpage_retry(struct rpc_task *task, struct nfs_pgio_data *data) +static void nfs_readpage_retry(struct rpc_task *task, + struct nfs_pgio_header *hdr) { - struct nfs_pgio_args *argp = &data->args; - struct nfs_pgio_res *resp = &data->res; + struct nfs_pgio_args *argp = &hdr->args; + struct nfs_pgio_res *resp = &hdr->res; /* This is a short read! */ - nfs_inc_stats(data->header->inode, NFSIOS_SHORTREAD); + nfs_inc_stats(hdr->inode, NFSIOS_SHORTREAD); /* Has the server at least made some progress? */ if (resp->count == 0) { - nfs_set_pgio_error(data->header, -EIO, argp->offset); + nfs_set_pgio_error(hdr, -EIO, argp->offset); return; } - /* Yes, so retry the read at the end of the data */ - data->mds_offset += resp->count; + /* Yes, so retry the read at the end of the hdr */ + hdr->mds_offset += resp->count; argp->offset += resp->count; argp->pgbase += resp->count; argp->count -= resp->count; rpc_restart_call_prepare(task); } -static void nfs_readpage_result(struct rpc_task *task, struct nfs_pgio_data *data) +static void nfs_readpage_result(struct rpc_task *task, + struct nfs_pgio_header *hdr) { - struct nfs_pgio_header *hdr = data->header; - - if (data->res.eof) { + if (hdr->res.eof) { loff_t bound; - bound = data->args.offset + data->res.count; + bound = hdr->args.offset + hdr->res.count; spin_lock(&hdr->lock); if (bound < hdr->io_start + hdr->good_bytes) { set_bit(NFS_IOHDR_EOF, &hdr->flags); @@ -254,8 +256,8 @@ static void nfs_readpage_result(struct rpc_task *task, struct nfs_pgio_data *dat hdr->good_bytes = bound - hdr->io_start; } spin_unlock(&hdr->lock); - } else if (data->res.count != data->args.count) - nfs_readpage_retry(task, data); + } else if (hdr->res.count != hdr->args.count) + nfs_readpage_retry(task, hdr); } /* diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 6afe0f679420..6a2d0986a3a3 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -596,11 +596,11 @@ nfs_clear_request_commit(struct nfs_page *req) } static inline -int nfs_write_need_commit(struct nfs_pgio_data *data) +int nfs_write_need_commit(struct nfs_pgio_header *hdr) { - if (data->writeverf.committed == NFS_DATA_SYNC) - return data->header->lseg == NULL; - return data->writeverf.committed != NFS_FILE_SYNC; + if (hdr->writeverf.committed == NFS_DATA_SYNC) + return hdr->lseg == NULL; + return hdr->writeverf.committed != NFS_FILE_SYNC; } #else @@ -627,7 +627,7 @@ nfs_clear_request_commit(struct nfs_page *req) } static inline -int nfs_write_need_commit(struct nfs_pgio_data *data) +int nfs_write_need_commit(struct nfs_pgio_header *hdr) { return 0; } @@ -1013,17 +1013,18 @@ static int flush_task_priority(int how) return RPC_PRIORITY_NORMAL; } -static void nfs_initiate_write(struct nfs_pgio_data *data, struct rpc_message *msg, +static void nfs_initiate_write(struct nfs_pgio_header *hdr, + struct rpc_message *msg, struct rpc_task_setup *task_setup_data, int how) { - struct inode *inode = data->header->inode; + struct inode *inode = hdr->inode; int priority = flush_task_priority(how); task_setup_data->priority = priority; - NFS_PROTO(inode)->write_setup(data, msg); + NFS_PROTO(inode)->write_setup(hdr, msg); nfs4_state_protect_write(NFS_SERVER(inode)->nfs_client, - &task_setup_data->rpc_client, msg, data); + &task_setup_data->rpc_client, msg, hdr); } /* If a nfs_flush_* function fails, it should remove reqs from @head and @@ -1085,19 +1086,17 @@ void nfs_commit_prepare(struct rpc_task *task, void *calldata) NFS_PROTO(data->inode)->commit_rpc_prepare(task, data); } -static void nfs_writeback_release_common(struct nfs_pgio_data *data) +static void nfs_writeback_release_common(struct nfs_pgio_header *hdr) { - struct nfs_pgio_header *hdr = data->header; - int status = data->task.tk_status; + int status = hdr->task.tk_status; - if ((status >= 0) && nfs_write_need_commit(data)) { + if ((status >= 0) && nfs_write_need_commit(hdr)) { spin_lock(&hdr->lock); if (test_bit(NFS_IOHDR_NEED_RESCHED, &hdr->flags)) ; /* Do nothing */ else if (!test_and_set_bit(NFS_IOHDR_NEED_COMMIT, &hdr->flags)) - memcpy(&hdr->verf, &data->writeverf, sizeof(hdr->verf)); - else if (memcmp(&hdr->verf, &data->writeverf, - sizeof(hdr->verf))) + memcpy(&hdr->verf, &hdr->writeverf, sizeof(hdr->verf)); + else if (memcmp(&hdr->verf, &hdr->writeverf, sizeof(hdr->verf))) set_bit(NFS_IOHDR_NEED_RESCHED, &hdr->flags); spin_unlock(&hdr->lock); } @@ -1131,7 +1130,8 @@ static int nfs_should_remove_suid(const struct inode *inode) /* * This function is called when the WRITE call is complete. */ -static int nfs_writeback_done(struct rpc_task *task, struct nfs_pgio_data *data, +static int nfs_writeback_done(struct rpc_task *task, + struct nfs_pgio_header *hdr, struct inode *inode) { int status; @@ -1143,13 +1143,14 @@ static int nfs_writeback_done(struct rpc_task *task, struct nfs_pgio_data *data, * another writer had changed the file, but some applications * depend on tighter cache coherency when writing. */ - status = NFS_PROTO(inode)->write_done(task, data); + status = NFS_PROTO(inode)->write_done(task, hdr); if (status != 0) return status; - nfs_add_stats(inode, NFSIOS_SERVERWRITTENBYTES, data->res.count); + nfs_add_stats(inode, NFSIOS_SERVERWRITTENBYTES, hdr->res.count); #if IS_ENABLED(CONFIG_NFS_V3) || IS_ENABLED(CONFIG_NFS_V4) - if (data->res.verf->committed < data->args.stable && task->tk_status >= 0) { + if (hdr->res.verf->committed < hdr->args.stable && + task->tk_status >= 0) { /* We tried a write call, but the server did not * commit data to stable storage even though we * requested it. @@ -1165,7 +1166,7 @@ static int nfs_writeback_done(struct rpc_task *task, struct nfs_pgio_data *data, dprintk("NFS: faulty NFS server %s:" " (committed = %d) != (stable = %d)\n", NFS_SERVER(inode)->nfs_client->cl_hostname, - data->res.verf->committed, data->args.stable); + hdr->res.verf->committed, hdr->args.stable); complain = jiffies + 300 * HZ; } } @@ -1180,16 +1181,17 @@ static int nfs_writeback_done(struct rpc_task *task, struct nfs_pgio_data *data, /* * This function is called when the WRITE call is complete. */ -static void nfs_writeback_result(struct rpc_task *task, struct nfs_pgio_data *data) +static void nfs_writeback_result(struct rpc_task *task, + struct nfs_pgio_header *hdr) { - struct nfs_pgio_args *argp = &data->args; - struct nfs_pgio_res *resp = &data->res; + struct nfs_pgio_args *argp = &hdr->args; + struct nfs_pgio_res *resp = &hdr->res; if (resp->count < argp->count) { static unsigned long complain; /* This a short write! */ - nfs_inc_stats(data->header->inode, NFSIOS_SHORTWRITE); + nfs_inc_stats(hdr->inode, NFSIOS_SHORTWRITE); /* Has the server at least made some progress? */ if (resp->count == 0) { @@ -1199,14 +1201,14 @@ static void nfs_writeback_result(struct rpc_task *task, struct nfs_pgio_data *da argp->count); complain = jiffies + 300 * HZ; } - nfs_set_pgio_error(data->header, -EIO, argp->offset); + nfs_set_pgio_error(hdr, -EIO, argp->offset); task->tk_status = -EIO; return; } /* Was this an NFSv2 write or an NFSv3 stable write? */ if (resp->verf->committed != NFS_UNSTABLE) { /* Resend from where the server left off */ - data->mds_offset += resp->count; + hdr->mds_offset += resp->count; argp->offset += resp->count; argp->pgbase += resp->count; argp->count -= resp->count; diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h index 43592651cd5a..d0fae7b78252 100644 --- a/include/linux/nfs_page.h +++ b/include/linux/nfs_page.h @@ -64,10 +64,11 @@ struct nfs_rw_ops { const fmode_t rw_mode; struct nfs_pgio_header *(*rw_alloc_header)(void); void (*rw_free_header)(struct nfs_pgio_header *); - void (*rw_release)(struct nfs_pgio_data *); - int (*rw_done)(struct rpc_task *, struct nfs_pgio_data *, struct inode *); - void (*rw_result)(struct rpc_task *, struct nfs_pgio_data *); - void (*rw_initiate)(struct nfs_pgio_data *, struct rpc_message *, + void (*rw_release)(struct nfs_pgio_header *); + int (*rw_done)(struct rpc_task *, struct nfs_pgio_header *, + struct inode *); + void (*rw_result)(struct rpc_task *, struct nfs_pgio_header *); + void (*rw_initiate)(struct nfs_pgio_header *, struct rpc_message *, struct rpc_task_setup *, int); }; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index bb18dba1aefe..efeaf7690b51 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1257,27 +1257,10 @@ enum { NFS_IOHDR_NEED_RESCHED, }; -struct nfs_pgio_data { - struct nfs_pgio_header *header; - struct list_head list; - struct rpc_task task; - struct nfs_fattr fattr; - struct nfs_writeverf writeverf; /* Used for writes */ - struct nfs_pgio_args args; /* argument struct */ - struct nfs_pgio_res res; /* result struct */ - unsigned long timestamp; /* For lease renewal */ - int (*pgio_done_cb)(struct rpc_task *task, struct nfs_pgio_data *data); - __u64 mds_offset; /* Filelayout dense stripe */ - struct nfs_page_array page_array; - struct nfs_client *ds_clp; /* pNFS data server */ - int ds_idx; /* ds index if ds_clp is set */ -}; - struct nfs_pgio_header { struct inode *inode; struct rpc_cred *cred; struct list_head pages; - struct nfs_pgio_data data; atomic_t refcnt; struct nfs_page *req; struct nfs_writeverf verf; /* Used for writes */ @@ -1295,6 +1278,21 @@ struct nfs_pgio_header { int error; /* merge with pnfs_error */ unsigned long good_bytes; /* boundary of good data */ unsigned long flags; + + /* + * rpc data + */ + struct rpc_task task; + struct nfs_fattr fattr; + struct nfs_writeverf writeverf; /* Used for writes */ + struct nfs_pgio_args args; /* argument struct */ + struct nfs_pgio_res res; /* result struct */ + unsigned long timestamp; /* For lease renewal */ + int (*pgio_done_cb)(struct rpc_task *, struct nfs_pgio_header *); + __u64 mds_offset; /* Filelayout dense stripe */ + struct nfs_page_array page_array; + struct nfs_client *ds_clp; /* pNFS data server */ + int ds_idx; /* ds index if ds_clp is set */ }; struct nfs_mds_commit_info { @@ -1426,11 +1424,12 @@ struct nfs_rpc_ops { struct nfs_pathconf *); int (*set_capabilities)(struct nfs_server *, struct nfs_fh *); int (*decode_dirent)(struct xdr_stream *, struct nfs_entry *, int); - int (*pgio_rpc_prepare)(struct rpc_task *, struct nfs_pgio_data *); - void (*read_setup) (struct nfs_pgio_data *, struct rpc_message *); - int (*read_done) (struct rpc_task *, struct nfs_pgio_data *); - void (*write_setup) (struct nfs_pgio_data *, struct rpc_message *); - int (*write_done) (struct rpc_task *, struct nfs_pgio_data *); + int (*pgio_rpc_prepare)(struct rpc_task *, + struct nfs_pgio_header *); + void (*read_setup)(struct nfs_pgio_header *, struct rpc_message *); + int (*read_done)(struct rpc_task *, struct nfs_pgio_header *); + void (*write_setup)(struct nfs_pgio_header *, struct rpc_message *); + int (*write_done)(struct rpc_task *, struct nfs_pgio_header *); void (*commit_setup) (struct nfs_commit_data *, struct rpc_message *); void (*commit_rpc_prepare)(struct rpc_task *, struct nfs_commit_data *); int (*commit_done) (struct rpc_task *, struct nfs_commit_data *); -- cgit v1.2.3 From c65e6254ca4db1584c5bf5f228ee26556477a9fd Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Mon, 9 Jun 2014 11:48:36 -0400 Subject: nfs: remove unused writeverf code Remove duplicate writeverf structure from merge of nfs_pgio_header and nfs_pgio_data and remove writeverf related flags and logic to handle more than one RPC per nfs_pgio_header. Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust --- fs/nfs/blocklayout/blocklayout.c | 2 +- fs/nfs/direct.c | 25 ++++++++----------------- fs/nfs/internal.h | 1 + fs/nfs/objlayout/objlayout.c | 4 ++-- fs/nfs/pagelist.c | 2 +- fs/nfs/write.c | 27 +++++---------------------- include/linux/nfs_xdr.h | 3 --- 7 files changed, 18 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/fs/nfs/blocklayout/blocklayout.c b/fs/nfs/blocklayout/blocklayout.c index c3ccfe440390..04ac32b339f8 100644 --- a/fs/nfs/blocklayout/blocklayout.c +++ b/fs/nfs/blocklayout/blocklayout.c @@ -445,7 +445,7 @@ static void bl_end_par_io_write(void *data, int num_se) } hdr->task.tk_status = hdr->pnfs_error; - hdr->writeverf.committed = NFS_FILE_SYNC; + hdr->verf.committed = NFS_FILE_SYNC; INIT_WORK(&hdr->task.u.tk_work, bl_write_cleanup); schedule_work(&hdr->task.u.tk_work); } diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 6c4c867ee04c..2a3293a5dda0 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -715,7 +715,7 @@ static void nfs_direct_write_completion(struct nfs_pgio_header *hdr) { struct nfs_direct_req *dreq = hdr->dreq; struct nfs_commit_info cinfo; - int bit = -1; + bool request_commit = false; struct nfs_page *req = nfs_list_entry(hdr->pages.next); if (test_bit(NFS_IOHDR_REDO, &hdr->flags)) @@ -729,27 +729,20 @@ static void nfs_direct_write_completion(struct nfs_pgio_header *hdr) dreq->flags = 0; dreq->error = hdr->error; } - if (dreq->error != 0) - bit = NFS_IOHDR_ERROR; - else { + if (dreq->error == 0) { dreq->count += hdr->good_bytes; - if (test_bit(NFS_IOHDR_NEED_RESCHED, &hdr->flags)) { - dreq->flags = NFS_ODIRECT_RESCHED_WRITES; - bit = NFS_IOHDR_NEED_RESCHED; - } else if (test_bit(NFS_IOHDR_NEED_COMMIT, &hdr->flags)) { + if (nfs_write_need_commit(hdr)) { if (dreq->flags == NFS_ODIRECT_RESCHED_WRITES) - bit = NFS_IOHDR_NEED_RESCHED; + request_commit = true; else if (dreq->flags == 0) { nfs_direct_set_hdr_verf(dreq, hdr); - bit = NFS_IOHDR_NEED_COMMIT; + request_commit = true; dreq->flags = NFS_ODIRECT_DO_COMMIT; } else if (dreq->flags == NFS_ODIRECT_DO_COMMIT) { - if (nfs_direct_set_or_cmp_hdr_verf(dreq, hdr)) { + request_commit = true; + if (nfs_direct_set_or_cmp_hdr_verf(dreq, hdr)) dreq->flags = NFS_ODIRECT_RESCHED_WRITES; - bit = NFS_IOHDR_NEED_RESCHED; - } else - bit = NFS_IOHDR_NEED_COMMIT; } } } @@ -760,9 +753,7 @@ static void nfs_direct_write_completion(struct nfs_pgio_header *hdr) req = nfs_list_entry(hdr->pages.next); nfs_list_remove_request(req); - switch (bit) { - case NFS_IOHDR_NEED_RESCHED: - case NFS_IOHDR_NEED_COMMIT: + if (request_commit) { kref_get(&req->wb_kref); nfs_mark_request_commit(req, hdr->lseg, &cinfo); do_destroy = false; diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 3f3aedd2e8c9..da36257628c5 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -441,6 +441,7 @@ int nfs_scan_commit(struct inode *inode, struct list_head *dst, void nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg, struct nfs_commit_info *cinfo); +int nfs_write_need_commit(struct nfs_pgio_header *); int nfs_generic_commit_list(struct inode *inode, struct list_head *head, int how, struct nfs_commit_info *cinfo); void nfs_retry_commit(struct list_head *page_list, diff --git a/fs/nfs/objlayout/objlayout.c b/fs/nfs/objlayout/objlayout.c index 86312787cee6..697a16d11fac 100644 --- a/fs/nfs/objlayout/objlayout.c +++ b/fs/nfs/objlayout/objlayout.c @@ -328,7 +328,7 @@ objlayout_write_done(struct objlayout_io_res *oir, ssize_t status, bool sync) oir->status = hdr->task.tk_status = status; if (status >= 0) { hdr->res.count = status; - hdr->writeverf.committed = oir->committed; + hdr->verf.committed = oir->committed; } else { hdr->pnfs_error = status; } @@ -336,7 +336,7 @@ objlayout_write_done(struct objlayout_io_res *oir, ssize_t status, bool sync) /* must not use oir after this point */ dprintk("%s: Return status %zd committed %d sync=%d\n", __func__, - status, hdr->writeverf.committed, sync); + status, hdr->verf.committed, sync); if (sync) pnfs_ld_write_done(hdr); diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index ecb3d4cdbc85..7dd0d5f101a4 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -545,7 +545,7 @@ static void nfs_pgio_rpcsetup(struct nfs_pgio_header *hdr, hdr->res.fattr = &hdr->fattr; hdr->res.count = count; hdr->res.eof = 0; - hdr->res.verf = &hdr->writeverf; + hdr->res.verf = &hdr->verf; nfs_fattr_init(&hdr->fattr); } diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 6a2d0986a3a3..8534ee5c207d 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -595,12 +595,11 @@ nfs_clear_request_commit(struct nfs_page *req) } } -static inline int nfs_write_need_commit(struct nfs_pgio_header *hdr) { - if (hdr->writeverf.committed == NFS_DATA_SYNC) + if (hdr->verf.committed == NFS_DATA_SYNC) return hdr->lseg == NULL; - return hdr->writeverf.committed != NFS_FILE_SYNC; + return hdr->verf.committed != NFS_FILE_SYNC; } #else @@ -626,7 +625,6 @@ nfs_clear_request_commit(struct nfs_page *req) { } -static inline int nfs_write_need_commit(struct nfs_pgio_header *hdr) { return 0; @@ -654,11 +652,7 @@ static void nfs_write_completion(struct nfs_pgio_header *hdr) nfs_context_set_write_error(req->wb_context, hdr->error); goto remove_req; } - if (test_bit(NFS_IOHDR_NEED_RESCHED, &hdr->flags)) { - nfs_mark_request_dirty(req); - goto next; - } - if (test_bit(NFS_IOHDR_NEED_COMMIT, &hdr->flags)) { + if (nfs_write_need_commit(hdr)) { memcpy(&req->wb_verf, &hdr->verf.verifier, sizeof(req->wb_verf)); nfs_mark_request_commit(req, hdr->lseg, &cinfo); goto next; @@ -668,7 +662,7 @@ remove_req: next: nfs_unlock_request(req); nfs_end_page_writeback(req); - do_destroy = !test_bit(NFS_IOHDR_NEED_COMMIT, &hdr->flags); + do_destroy = !nfs_write_need_commit(hdr); nfs_release_request(req); } out: @@ -1088,18 +1082,7 @@ void nfs_commit_prepare(struct rpc_task *task, void *calldata) static void nfs_writeback_release_common(struct nfs_pgio_header *hdr) { - int status = hdr->task.tk_status; - - if ((status >= 0) && nfs_write_need_commit(hdr)) { - spin_lock(&hdr->lock); - if (test_bit(NFS_IOHDR_NEED_RESCHED, &hdr->flags)) - ; /* Do nothing */ - else if (!test_and_set_bit(NFS_IOHDR_NEED_COMMIT, &hdr->flags)) - memcpy(&hdr->verf, &hdr->writeverf, sizeof(hdr->verf)); - else if (memcmp(&hdr->verf, &hdr->writeverf, sizeof(hdr->verf))) - set_bit(NFS_IOHDR_NEED_RESCHED, &hdr->flags); - spin_unlock(&hdr->lock); - } + /* do nothing! */ } /* diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index efeaf7690b51..e1b7b3b7c40f 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1253,8 +1253,6 @@ enum { NFS_IOHDR_ERROR = 0, NFS_IOHDR_EOF, NFS_IOHDR_REDO, - NFS_IOHDR_NEED_COMMIT, - NFS_IOHDR_NEED_RESCHED, }; struct nfs_pgio_header { @@ -1284,7 +1282,6 @@ struct nfs_pgio_header { */ struct rpc_task task; struct nfs_fattr fattr; - struct nfs_writeverf writeverf; /* Used for writes */ struct nfs_pgio_args args; /* argument struct */ struct nfs_pgio_res res; /* result struct */ unsigned long timestamp; /* For lease renewal */ -- cgit v1.2.3 From 4714fb51fd03a14d8c73001438283e7f7b752f1e Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Mon, 9 Jun 2014 11:48:37 -0400 Subject: nfs: remove pgio_header refcount, related cleanup The refcounting on nfs_pgio_header was related to there being (possibly) more than one nfs_pgio_data. Now that nfs_pgio_data has been merged into nfs_pgio_header, there is no reason to do this ref counting. Just call the completion callback on nfs_pgio_release/nfs_pgio_error. Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust --- fs/nfs/pagelist.c | 36 +++++++++++------------------------- fs/nfs/pnfs.c | 6 ------ include/linux/nfs_xdr.h | 1 - 3 files changed, 11 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index 7dd0d5f101a4..580fc0c982e6 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -459,7 +459,6 @@ struct nfs_pgio_header *nfs_pgio_header_alloc(const struct nfs_rw_ops *ops) if (hdr) { INIT_LIST_HEAD(&hdr->pages); spin_lock_init(&hdr->lock); - atomic_set(&hdr->refcnt, 0); hdr->rw_ops = ops; } return hdr; @@ -477,31 +476,18 @@ void nfs_pgio_header_free(struct nfs_pgio_header *hdr) EXPORT_SYMBOL_GPL(nfs_pgio_header_free); /** - * nfs_pgio_data_alloc - Allocate pageio data - * @hdr: The header making a request - * @pagecount: Number of pages to create - */ -static bool nfs_pgio_data_init(struct nfs_pgio_header *hdr, - unsigned int pagecount) -{ - if (nfs_pgarray_set(&hdr->page_array, pagecount)) { - atomic_inc(&hdr->refcnt); - return true; - } - return false; -} - -/** - * nfs_pgio_data_destroy - Properly release pageio data - * @hdr: The header with data to destroy + * nfs_pgio_data_destroy - make @hdr suitable for reuse + * + * Frees memory and releases refs from nfs_generic_pgio, so that it may + * be called again. + * + * @hdr: A header that has had nfs_generic_pgio called */ void nfs_pgio_data_destroy(struct nfs_pgio_header *hdr) { put_nfs_open_context(hdr->args.context); if (hdr->page_array.pagevec != hdr->page_array.page_array) kfree(hdr->page_array.pagevec); - if (atomic_dec_and_test(&hdr->refcnt)) - hdr->completion_ops->completion(hdr); } EXPORT_SYMBOL_GPL(nfs_pgio_data_destroy); @@ -620,6 +606,7 @@ static int nfs_pgio_error(struct nfs_pageio_descriptor *desc, { set_bit(NFS_IOHDR_REDO, &hdr->flags); nfs_pgio_data_destroy(hdr); + hdr->completion_ops->completion(hdr); desc->pg_completion_ops->error_cleanup(&desc->pg_list); return -ENOMEM; } @@ -634,6 +621,7 @@ static void nfs_pgio_release(void *calldata) if (hdr->rw_ops->rw_release) hdr->rw_ops->rw_release(hdr); nfs_pgio_data_destroy(hdr); + hdr->completion_ops->completion(hdr); } /** @@ -707,9 +695,10 @@ int nfs_generic_pgio(struct nfs_pageio_descriptor *desc, struct page **pages; struct list_head *head = &desc->pg_list; struct nfs_commit_info cinfo; + unsigned int pagecount; - if (!nfs_pgio_data_init(hdr, nfs_page_array_len(desc->pg_base, - desc->pg_count))) + pagecount = nfs_page_array_len(desc->pg_base, desc->pg_count); + if (!nfs_pgarray_set(&hdr->page_array, pagecount)) return nfs_pgio_error(desc, hdr); nfs_init_cinfo(&cinfo, desc->pg_inode, desc->pg_dreq); @@ -743,14 +732,11 @@ static int nfs_generic_pg_pgios(struct nfs_pageio_descriptor *desc) return -ENOMEM; } nfs_pgheader_init(desc, hdr, nfs_pgio_header_free); - atomic_inc(&hdr->refcnt); ret = nfs_generic_pgio(desc, hdr); if (ret == 0) ret = nfs_initiate_pgio(NFS_CLIENT(hdr->inode), hdr, desc->pg_rpc_callops, desc->pg_ioflags, 0); - if (atomic_dec_and_test(&hdr->refcnt)) - hdr->completion_ops->completion(hdr); return ret; } diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index ecc911347750..ecbed4632d11 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -1602,15 +1602,12 @@ pnfs_generic_pg_writepages(struct nfs_pageio_descriptor *desc) } nfs_pgheader_init(desc, hdr, pnfs_writehdr_free); hdr->lseg = pnfs_get_lseg(desc->pg_lseg); - atomic_inc(&hdr->refcnt); ret = nfs_generic_pgio(desc, hdr); if (ret != 0) { pnfs_put_lseg(desc->pg_lseg); desc->pg_lseg = NULL; } else pnfs_do_write(desc, hdr, desc->pg_ioflags); - if (atomic_dec_and_test(&hdr->refcnt)) - hdr->completion_ops->completion(hdr); return ret; } EXPORT_SYMBOL_GPL(pnfs_generic_pg_writepages); @@ -1745,15 +1742,12 @@ pnfs_generic_pg_readpages(struct nfs_pageio_descriptor *desc) } nfs_pgheader_init(desc, hdr, pnfs_readhdr_free); hdr->lseg = pnfs_get_lseg(desc->pg_lseg); - atomic_inc(&hdr->refcnt); ret = nfs_generic_pgio(desc, hdr); if (ret != 0) { pnfs_put_lseg(desc->pg_lseg); desc->pg_lseg = NULL; } else pnfs_do_read(desc, hdr); - if (atomic_dec_and_test(&hdr->refcnt)) - hdr->completion_ops->completion(hdr); return ret; } EXPORT_SYMBOL_GPL(pnfs_generic_pg_readpages); diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index e1b7b3b7c40f..81cbbf313272 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1259,7 +1259,6 @@ struct nfs_pgio_header { struct inode *inode; struct rpc_cred *cred; struct list_head pages; - atomic_t refcnt; struct nfs_page *req; struct nfs_writeverf verf; /* Used for writes */ struct pnfs_layout_segment *lseg; -- cgit v1.2.3 From 53113ad35e4b9ce82d949c7c67c7b666fad5d907 Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Mon, 9 Jun 2014 11:48:38 -0400 Subject: pnfs: clean up *_resend_to_mds Clean up pnfs_read_done_resend_to_mds and pnfs_write_done_resend_to_mds: - instead of passing all arguments from a nfs_pgio_header, just pass the header - share the common code Reviewed-by: Christoph Hellwig Signed-off-by: Weston Andros Adamson Signed-off-by: Trond Myklebust --- fs/nfs/filelayout/filelayout.c | 10 ++----- fs/nfs/pagelist.c | 32 +++++++++++++++++++++ fs/nfs/pnfs.c | 63 ++++++------------------------------------ fs/nfs/pnfs.h | 8 ++---- include/linux/nfs_page.h | 2 ++ 5 files changed, 47 insertions(+), 68 deletions(-) (limited to 'include') diff --git a/fs/nfs/filelayout/filelayout.c b/fs/nfs/filelayout/filelayout.c index 537e7f7a0b48..504d58a51d35 100644 --- a/fs/nfs/filelayout/filelayout.c +++ b/fs/nfs/filelayout/filelayout.c @@ -97,10 +97,7 @@ static void filelayout_reset_write(struct nfs_pgio_header *hdr) hdr->args.count, (unsigned long long)hdr->args.offset); - task->tk_status = pnfs_write_done_resend_to_mds(hdr->inode, - &hdr->pages, - hdr->completion_ops, - hdr->dreq); + task->tk_status = pnfs_write_done_resend_to_mds(hdr); } } @@ -117,10 +114,7 @@ static void filelayout_reset_read(struct nfs_pgio_header *hdr) hdr->args.count, (unsigned long long)hdr->args.offset); - task->tk_status = pnfs_read_done_resend_to_mds(hdr->inode, - &hdr->pages, - hdr->completion_ops, - hdr->dreq); + task->tk_status = pnfs_read_done_resend_to_mds(hdr); } } diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index 580fc0c982e6..9c6c55359394 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -949,6 +949,38 @@ int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc, } EXPORT_SYMBOL_GPL(nfs_pageio_add_request); +/* + * nfs_pageio_resend - Transfer requests to new descriptor and resend + * @hdr - the pgio header to move request from + * @desc - the pageio descriptor to add requests to + * + * Try to move each request (nfs_page) from @hdr to @desc then attempt + * to send them. + * + * Returns 0 on success and < 0 on error. + */ +int nfs_pageio_resend(struct nfs_pageio_descriptor *desc, + struct nfs_pgio_header *hdr) +{ + LIST_HEAD(failed); + + desc->pg_dreq = hdr->dreq; + while (!list_empty(&hdr->pages)) { + struct nfs_page *req = nfs_list_entry(hdr->pages.next); + + nfs_list_remove_request(req); + if (!nfs_pageio_add_request(desc, req)) + nfs_list_add_request(req, &failed); + } + nfs_pageio_complete(desc); + if (!list_empty(&failed)) { + list_move(&failed, &hdr->pages); + return -EIO; + } + return 0; +} +EXPORT_SYMBOL_GPL(nfs_pageio_resend); + /** * nfs_pageio_complete - Complete I/O on an nfs_pageio_descriptor * @desc: pointer to io descriptor diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index ecbed4632d11..83ff8a05485a 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -1470,35 +1470,14 @@ pnfs_generic_pg_test(struct nfs_pageio_descriptor *pgio, struct nfs_page *prev, } EXPORT_SYMBOL_GPL(pnfs_generic_pg_test); -int pnfs_write_done_resend_to_mds(struct inode *inode, - struct list_head *head, - const struct nfs_pgio_completion_ops *compl_ops, - struct nfs_direct_req *dreq) +int pnfs_write_done_resend_to_mds(struct nfs_pgio_header *hdr) { struct nfs_pageio_descriptor pgio; - LIST_HEAD(failed); /* Resend all requests through the MDS */ - nfs_pageio_init_write(&pgio, inode, FLUSH_STABLE, true, compl_ops); - pgio.pg_dreq = dreq; - while (!list_empty(head)) { - struct nfs_page *req = nfs_list_entry(head->next); - - nfs_list_remove_request(req); - if (!nfs_pageio_add_request(&pgio, req)) - nfs_list_add_request(req, &failed); - } - nfs_pageio_complete(&pgio); - - if (!list_empty(&failed)) { - /* For some reason our attempt to resend pages. Mark the - * overall send request as having failed, and let - * nfs_writeback_release_full deal with the error. - */ - list_move(&failed, head); - return -EIO; - } - return 0; + nfs_pageio_init_write(&pgio, hdr->inode, FLUSH_STABLE, true, + hdr->completion_ops); + return nfs_pageio_resend(&pgio, hdr); } EXPORT_SYMBOL_GPL(pnfs_write_done_resend_to_mds); @@ -1511,10 +1490,7 @@ static void pnfs_ld_handle_write_error(struct nfs_pgio_header *hdr) pnfs_return_layout(hdr->inode); } if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags)) - hdr->task.tk_status = pnfs_write_done_resend_to_mds(hdr->inode, - &hdr->pages, - hdr->completion_ops, - hdr->dreq); + hdr->task.tk_status = pnfs_write_done_resend_to_mds(hdr); } /* @@ -1612,31 +1588,13 @@ pnfs_generic_pg_writepages(struct nfs_pageio_descriptor *desc) } EXPORT_SYMBOL_GPL(pnfs_generic_pg_writepages); -int pnfs_read_done_resend_to_mds(struct inode *inode, - struct list_head *head, - const struct nfs_pgio_completion_ops *compl_ops, - struct nfs_direct_req *dreq) +int pnfs_read_done_resend_to_mds(struct nfs_pgio_header *hdr) { struct nfs_pageio_descriptor pgio; - LIST_HEAD(failed); /* Resend all requests through the MDS */ - nfs_pageio_init_read(&pgio, inode, true, compl_ops); - pgio.pg_dreq = dreq; - while (!list_empty(head)) { - struct nfs_page *req = nfs_list_entry(head->next); - - nfs_list_remove_request(req); - if (!nfs_pageio_add_request(&pgio, req)) - nfs_list_add_request(req, &failed); - } - nfs_pageio_complete(&pgio); - - if (!list_empty(&failed)) { - list_move(&failed, head); - return -EIO; - } - return 0; + nfs_pageio_init_read(&pgio, hdr->inode, true, hdr->completion_ops); + return nfs_pageio_resend(&pgio, hdr); } EXPORT_SYMBOL_GPL(pnfs_read_done_resend_to_mds); @@ -1648,10 +1606,7 @@ static void pnfs_ld_handle_read_error(struct nfs_pgio_header *hdr) pnfs_return_layout(hdr->inode); } if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags)) - hdr->task.tk_status = pnfs_read_done_resend_to_mds(hdr->inode, - &hdr->pages, - hdr->completion_ops, - hdr->dreq); + hdr->task.tk_status = pnfs_read_done_resend_to_mds(hdr); } /* diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index a4a58be94064..27ddecd3847f 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h @@ -228,12 +228,8 @@ struct pnfs_layout_segment *pnfs_update_layout(struct inode *ino, gfp_t gfp_flags); void nfs4_deviceid_mark_client_invalid(struct nfs_client *clp); -int pnfs_read_done_resend_to_mds(struct inode *inode, struct list_head *head, - const struct nfs_pgio_completion_ops *compl_ops, - struct nfs_direct_req *dreq); -int pnfs_write_done_resend_to_mds(struct inode *inode, struct list_head *head, - const struct nfs_pgio_completion_ops *compl_ops, - struct nfs_direct_req *dreq); +int pnfs_read_done_resend_to_mds(struct nfs_pgio_header *); +int pnfs_write_done_resend_to_mds(struct nfs_pgio_header *); struct nfs4_threshold *pnfs_mdsthreshold_alloc(void); /* nfs4_deviceid_flags */ diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h index d0fae7b78252..4b48548e700e 100644 --- a/include/linux/nfs_page.h +++ b/include/linux/nfs_page.h @@ -112,6 +112,8 @@ extern void nfs_pageio_init(struct nfs_pageio_descriptor *desc, int how); extern int nfs_pageio_add_request(struct nfs_pageio_descriptor *, struct nfs_page *); +extern int nfs_pageio_resend(struct nfs_pageio_descriptor *, + struct nfs_pgio_header *); extern void nfs_pageio_complete(struct nfs_pageio_descriptor *desc); extern void nfs_pageio_cond_complete(struct nfs_pageio_descriptor *, pgoff_t); extern size_t nfs_generic_pg_test(struct nfs_pageio_descriptor *desc, -- cgit v1.2.3 From c56ef6725068c0ce499e517409c0da226ef51b08 Mon Sep 17 00:00:00 2001 From: David Spinadel Date: Wed, 5 Feb 2014 15:21:13 +0200 Subject: mac80211: support more than one band in scan request Some drivers (such as iwlmvm) can handle multiple bands in a single HW scan request. Add a HW flag to indicate that the driver support this. To hold the required data, create a separate structure for HW scan request that holds cfg scan request and data about different parts of the scan IEs. As this changes the mac80211 API, update all drivers using it to use the correct new function type/argument. Signed-off-by: David Spinadel Signed-off-by: Johannes Berg --- drivers/net/wireless/at76c50x-usb.c | 3 +- drivers/net/wireless/ath/ath10k/mac.c | 3 +- drivers/net/wireless/cw1200/scan.c | 3 +- drivers/net/wireless/cw1200/scan.h | 2 +- drivers/net/wireless/iwlegacy/common.c | 3 +- drivers/net/wireless/iwlegacy/common.h | 2 +- drivers/net/wireless/iwlwifi/dvm/mac80211.c | 3 +- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 3 +- drivers/net/wireless/mac80211_hwsim.c | 3 +- drivers/net/wireless/ti/wl1251/main.c | 3 +- drivers/net/wireless/ti/wlcore/main.c | 3 +- include/net/mac80211.h | 39 ++++++++++- net/mac80211/driver-ops.h | 2 +- net/mac80211/ieee80211_i.h | 9 ++- net/mac80211/scan.c | 85 +++++++++++++++------- net/mac80211/util.c | 105 +++++++++++++++++++--------- 16 files changed, 198 insertions(+), 73 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c index d48776e4f343..334c2ece855a 100644 --- a/drivers/net/wireless/at76c50x-usb.c +++ b/drivers/net/wireless/at76c50x-usb.c @@ -1955,8 +1955,9 @@ static void at76_dwork_hw_scan(struct work_struct *work) static int at76_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct cfg80211_scan_request *req) + struct ieee80211_scan_request *hw_req) { + struct cfg80211_scan_request *req = &hw_req->req; struct at76_priv *priv = hw->priv; struct at76_req_scan scan; u8 *ssid = NULL; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index a21080028c54..b8314a534972 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3137,10 +3137,11 @@ exit: static int ath10k_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct cfg80211_scan_request *req) + struct ieee80211_scan_request *hw_req) { struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct cfg80211_scan_request *req = &hw_req->req; struct wmi_start_scan_arg arg; int ret = 0; int i; diff --git a/drivers/net/wireless/cw1200/scan.c b/drivers/net/wireless/cw1200/scan.c index 9afcd4ce3368..b2fb6c632092 100644 --- a/drivers/net/wireless/cw1200/scan.c +++ b/drivers/net/wireless/cw1200/scan.c @@ -53,9 +53,10 @@ static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan) int cw1200_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct cfg80211_scan_request *req) + struct ieee80211_scan_request *hw_req) { struct cw1200_common *priv = hw->priv; + struct cfg80211_scan_request *req = &hw_req->req; struct wsm_template_frame frame = { .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, }; diff --git a/drivers/net/wireless/cw1200/scan.h b/drivers/net/wireless/cw1200/scan.h index 5a8296ccfa82..cc75459e5784 100644 --- a/drivers/net/wireless/cw1200/scan.h +++ b/drivers/net/wireless/cw1200/scan.h @@ -41,7 +41,7 @@ struct cw1200_scan { int cw1200_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct cfg80211_scan_request *req); + struct ieee80211_scan_request *hw_req); void cw1200_scan_work(struct work_struct *work); void cw1200_scan_timeout(struct work_struct *work); void cw1200_clear_recent_scan_work(struct work_struct *work); diff --git a/drivers/net/wireless/iwlegacy/common.c b/drivers/net/wireless/iwlegacy/common.c index ecc674627e6e..03de7467aecf 100644 --- a/drivers/net/wireless/iwlegacy/common.c +++ b/drivers/net/wireless/iwlegacy/common.c @@ -1572,8 +1572,9 @@ il_scan_initiate(struct il_priv *il, struct ieee80211_vif *vif) int il_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct cfg80211_scan_request *req) + struct ieee80211_scan_request *hw_req) { + struct cfg80211_scan_request *req = &hw_req->req; struct il_priv *il = hw->priv; int ret; diff --git a/drivers/net/wireless/iwlegacy/common.h b/drivers/net/wireless/iwlegacy/common.h index ea5c0f863c4e..5b972798bdff 100644 --- a/drivers/net/wireless/iwlegacy/common.h +++ b/drivers/net/wireless/iwlegacy/common.h @@ -1787,7 +1787,7 @@ int il_scan_cancel(struct il_priv *il); int il_scan_cancel_timeout(struct il_priv *il, unsigned long ms); void il_force_scan_end(struct il_priv *il); int il_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct cfg80211_scan_request *req); + struct ieee80211_scan_request *hw_req); void il_internal_short_hw_scan(struct il_priv *il); int il_force_reset(struct il_priv *il, bool external); u16 il_fill_probe_req(struct il_priv *il, struct ieee80211_mgmt *frame, diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index 29af7b51e370..afb98f4fdaf3 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -1495,9 +1495,10 @@ static int iwlagn_mac_change_interface(struct ieee80211_hw *hw, static int iwlagn_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct cfg80211_scan_request *req) + struct ieee80211_scan_request *hw_req) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct cfg80211_scan_request *req = &hw_req->req; int ret; IWL_DEBUG_MAC80211(priv, "enter\n"); diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 7215f5980186..4dc2e05f49ce 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -1537,9 +1537,10 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw, static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct cfg80211_scan_request *req) + struct ieee80211_scan_request *hw_req) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct cfg80211_scan_request *req = &hw_req->req; int ret; if (req->n_channels == 0 || req->n_channels > MAX_NUM_SCAN_CHANNELS) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 06a0722164da..eba51460a5de 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1736,9 +1736,10 @@ static void hw_scan_work(struct work_struct *work) static int mac80211_hwsim_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct cfg80211_scan_request *req) + struct ieee80211_scan_request *hw_req) { struct mac80211_hwsim_data *hwsim = hw->priv; + struct cfg80211_scan_request *req = &hw_req->req; mutex_lock(&hwsim->mutex); if (WARN_ON(hwsim->tmp_chan || hwsim->hw_scan_request)) { diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c index 4e782f18ae34..38234851457e 100644 --- a/drivers/net/wireless/ti/wl1251/main.c +++ b/drivers/net/wireless/ti/wl1251/main.c @@ -991,8 +991,9 @@ out: static int wl1251_op_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct cfg80211_scan_request *req) + struct ieee80211_scan_request *hw_req) { + struct cfg80211_scan_request *req = &hw_req->req; struct wl1251 *wl = hw->priv; struct sk_buff *skb; size_t ssid_len = 0; diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 3d6028e62750..e5ffb8b91dd4 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -3540,8 +3540,9 @@ out: static int wl1271_op_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct cfg80211_scan_request *req) + struct ieee80211_scan_request *hw_req) { + struct cfg80211_scan_request *req = &hw_req->req; struct wl1271 *wl = hw->priv; int ret; u8 *ssid = NULL; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 18c2bdbaf988..9536ee3e22a9 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -768,6 +768,26 @@ struct ieee80211_sched_scan_ies { size_t len[IEEE80211_NUM_BANDS]; }; +/** + * struct ieee80211_scan_ies - descriptors for different blocks of IEs + * + * This structure is used to point to different blocks of IEs in HW scan. + * These blocks contain the IEs passed by userspace and the ones generated + * by mac80211. + * + * @ies: pointers to band specific IEs. + * @len: lengths of band_specific IEs. + * @common_ies: IEs for all bands (especially vendor specific ones) + * @common_ie_len: length of the common_ies + */ +struct ieee80211_scan_ies { + const u8 *ies[IEEE80211_NUM_BANDS]; + size_t len[IEEE80211_NUM_BANDS]; + const u8 *common_ies; + size_t common_ie_len; +}; + + static inline struct ieee80211_tx_info *IEEE80211_SKB_CB(struct sk_buff *skb) { return (struct ieee80211_tx_info *)skb->cb; @@ -1606,6 +1626,9 @@ struct ieee80211_tx_control { * on single-channel hardware. It can also be used as an * optimization in certain channel switch cases with * multi-channel. + * + * @IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS: The HW supports scanning on all bands + * in one command, mac80211 doesn't have to run separate scans per band. */ enum ieee80211_hw_flags { IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, @@ -1638,6 +1661,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_SUPPORTS_HT_CCK_RATES = 1<<27, IEEE80211_HW_CHANCTX_STA_CSA = 1<<28, IEEE80211_HW_CHANGE_RUNNING_CHANCTX = 1<<29, + IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS = 1<<30, }; /** @@ -1763,6 +1787,19 @@ struct ieee80211_hw { const struct ieee80211_cipher_scheme *cipher_schemes; }; +/** + * struct ieee80211_scan_request - hw scan request + * + * @ies: pointers different parts of IEs (in req.ie) + * @req: cfg80211 request. + */ +struct ieee80211_scan_request { + struct ieee80211_scan_ies ies; + + /* Keep last */ + struct cfg80211_scan_request req; +}; + /** * wiphy_to_ieee80211_hw - return a mac80211 driver hw struct from a wiphy * @@ -2874,7 +2911,7 @@ struct ieee80211_ops { void (*set_default_unicast_key)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int idx); int (*hw_scan)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct cfg80211_scan_request *req); + struct ieee80211_scan_request *req); void (*cancel_hw_scan)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); int (*sched_scan_start)(struct ieee80211_hw *hw, diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 2265bd7a44ba..faa0d90f6e80 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -314,7 +314,7 @@ static inline void drv_update_tkip_key(struct ieee80211_local *local, static inline int drv_hw_scan(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, - struct cfg80211_scan_request *req) + struct ieee80211_scan_request *req) { int ret; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 6c8089429892..f88bd1659cde 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1152,7 +1152,8 @@ struct ieee80211_local { unsigned long scanning; struct cfg80211_ssid scan_ssid; struct cfg80211_scan_request *int_scan_req; - struct cfg80211_scan_request *scan_req, *hw_scan_req; + struct cfg80211_scan_request *scan_req; + struct ieee80211_scan_request *hw_scan_req; struct cfg80211_chan_def scan_chandef; enum ieee80211_band hw_scan_band; int scan_channel_idx; @@ -1756,8 +1757,10 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, const u8 *bssid, u16 stype, u16 reason, bool send_frame, u8 *frame_buf); int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, - size_t buffer_len, const u8 *ie, size_t ie_len, - enum ieee80211_band band, u32 rate_mask, + size_t buffer_len, + struct ieee80211_scan_ies *ie_desc, + const u8 *ie, size_t ie_len, + u8 bands_used, u32 *rate_masks, struct cfg80211_chan_def *chandef); struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, u32 ratemask, diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index f40661eb75b5..116959e070d0 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -235,38 +235,51 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local) { struct cfg80211_scan_request *req = local->scan_req; struct cfg80211_chan_def chandef; - enum ieee80211_band band; + u8 bands_used = 0; int i, ielen, n_chans; if (test_bit(SCAN_HW_CANCELLED, &local->scanning)) return false; - do { - if (local->hw_scan_band == IEEE80211_NUM_BANDS) - return false; - - band = local->hw_scan_band; - n_chans = 0; + if (local->hw.flags & IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS) { for (i = 0; i < req->n_channels; i++) { - if (req->channels[i]->band == band) { - local->hw_scan_req->channels[n_chans] = + local->hw_scan_req->req.channels[i] = req->channels[i]; + bands_used |= BIT(req->channels[i]->band); + } + + n_chans = req->n_channels; + } else { + do { + if (local->hw_scan_band == IEEE80211_NUM_BANDS) + return false; + + n_chans = 0; + + for (i = 0; i < req->n_channels; i++) { + if (req->channels[i]->band != + local->hw_scan_band) + continue; + local->hw_scan_req->req.channels[n_chans] = req->channels[i]; n_chans++; + bands_used |= BIT(req->channels[i]->band); } - } - local->hw_scan_band++; - } while (!n_chans); + local->hw_scan_band++; + } while (!n_chans); + } - local->hw_scan_req->n_channels = n_chans; + local->hw_scan_req->req.n_channels = n_chans; ieee80211_prepare_scan_chandef(&chandef, req->scan_width); - ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie, + ielen = ieee80211_build_preq_ies(local, + (u8 *)local->hw_scan_req->req.ie, local->hw_scan_ies_bufsize, - req->ie, req->ie_len, band, - req->rates[band], &chandef); - local->hw_scan_req->ie_len = ielen; - local->hw_scan_req->no_cck = req->no_cck; + &local->hw_scan_req->ies, + req->ie, req->ie_len, + bands_used, req->rates, &chandef); + local->hw_scan_req->req.ie_len = ielen; + local->hw_scan_req->req.no_cck = req->no_cck; return true; } @@ -291,7 +304,9 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) if (WARN_ON(!local->scan_req)) return; - if (hw_scan && !aborted && ieee80211_prep_hw_scan(local)) { + if (hw_scan && !aborted && + !(local->hw.flags & IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS) && + ieee80211_prep_hw_scan(local)) { int rc; rc = drv_hw_scan(local, @@ -473,6 +488,21 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, u8 *ies; local->hw_scan_ies_bufsize = local->scan_ies_len + req->ie_len; + + if (local->hw.flags & IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS) { + int i, n_bands = 0; + u8 bands_counted = 0; + + for (i = 0; i < req->n_channels; i++) { + if (bands_counted & BIT(req->channels[i]->band)) + continue; + bands_counted |= BIT(req->channels[i]->band); + n_bands++; + } + + local->hw_scan_ies_bufsize *= n_bands; + } + local->hw_scan_req = kmalloc( sizeof(*local->hw_scan_req) + req->n_channels * sizeof(req->channels[0]) + @@ -480,13 +510,13 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, if (!local->hw_scan_req) return -ENOMEM; - local->hw_scan_req->ssids = req->ssids; - local->hw_scan_req->n_ssids = req->n_ssids; + local->hw_scan_req->req.ssids = req->ssids; + local->hw_scan_req->req.n_ssids = req->n_ssids; ies = (u8 *)local->hw_scan_req + sizeof(*local->hw_scan_req) + req->n_channels * sizeof(req->channels[0]); - local->hw_scan_req->ie = ies; - local->hw_scan_req->flags = req->flags; + local->hw_scan_req->req.ie = ies; + local->hw_scan_req->req.flags = req->flags; local->hw_scan_band = 0; @@ -976,6 +1006,7 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, struct ieee80211_sched_scan_ies sched_scan_ies = {}; struct cfg80211_chan_def chandef; int ret, i, iebufsz; + struct ieee80211_scan_ies dummy_ie_desc; iebufsz = local->scan_ies_len + req->ie_len; @@ -985,6 +1016,8 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, return -ENOTSUPP; for (i = 0; i < IEEE80211_NUM_BANDS; i++) { + u32 rate_masks[IEEE80211_NUM_BANDS] = {}; + if (!local->hw.wiphy->bands[i]) continue; @@ -995,11 +1028,13 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, } ieee80211_prepare_scan_chandef(&chandef, req->scan_width); + rate_masks[i] = (u32) -1; sched_scan_ies.len[i] = ieee80211_build_preq_ies(local, sched_scan_ies.ie[i], - iebufsz, req->ie, req->ie_len, - i, (u32) -1, &chandef); + iebufsz, &dummy_ie_desc, + req->ie, req->ie_len, BIT(i), + rate_masks, &chandef); } ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 42d448d765b4..e31458201278 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1219,14 +1219,17 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, } } -int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, - size_t buffer_len, const u8 *ie, size_t ie_len, - enum ieee80211_band band, u32 rate_mask, - struct cfg80211_chan_def *chandef) +static int ieee80211_build_preq_ies_band(struct ieee80211_local *local, + u8 *buffer, size_t buffer_len, + const u8 *ie, size_t ie_len, + enum ieee80211_band band, + u32 rate_mask, + struct cfg80211_chan_def *chandef, + size_t *offset) { struct ieee80211_supported_band *sband; u8 *pos = buffer, *end = buffer + buffer_len; - size_t offset = 0, noffset; + size_t noffset; int supp_rates_len, i; u8 rates[32]; int num_rates; @@ -1234,6 +1237,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, int shift; u32 rate_flags; + *offset = 0; + sband = local->hw.wiphy->bands[band]; if (WARN_ON_ONCE(!sband)) return 0; @@ -1272,12 +1277,12 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, noffset = ieee80211_ie_split(ie, ie_len, before_extrates, ARRAY_SIZE(before_extrates), - offset); - if (end - pos < noffset - offset) + *offset); + if (end - pos < noffset - *offset) goto out_err; - memcpy(pos, ie + offset, noffset - offset); - pos += noffset - offset; - offset = noffset; + memcpy(pos, ie + *offset, noffset - *offset); + pos += noffset - *offset; + *offset = noffset; } ext_rates_len = num_rates - supp_rates_len; @@ -1311,12 +1316,12 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, }; noffset = ieee80211_ie_split(ie, ie_len, before_ht, ARRAY_SIZE(before_ht), - offset); - if (end - pos < noffset - offset) + *offset); + if (end - pos < noffset - *offset) goto out_err; - memcpy(pos, ie + offset, noffset - offset); - pos += noffset - offset; - offset = noffset; + memcpy(pos, ie + *offset, noffset - *offset); + pos += noffset - *offset; + *offset = noffset; } if (sband->ht_cap.ht_supported) { @@ -1351,12 +1356,12 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, }; noffset = ieee80211_ie_split(ie, ie_len, before_vht, ARRAY_SIZE(before_vht), - offset); - if (end - pos < noffset - offset) + *offset); + if (end - pos < noffset - *offset) goto out_err; - memcpy(pos, ie + offset, noffset - offset); - pos += noffset - offset; - offset = noffset; + memcpy(pos, ie + *offset, noffset - *offset); + pos += noffset - *offset; + *offset = noffset; } if (sband->vht_cap.vht_supported) { @@ -1366,21 +1371,54 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, sband->vht_cap.cap); } - /* add any remaining custom IEs */ - if (ie && ie_len) { - noffset = ie_len; - if (end - pos < noffset - offset) - goto out_err; - memcpy(pos, ie + offset, noffset - offset); - pos += noffset - offset; - } - return pos - buffer; out_err: WARN_ONCE(1, "not enough space for preq IEs\n"); return pos - buffer; } +int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, + size_t buffer_len, + struct ieee80211_scan_ies *ie_desc, + const u8 *ie, size_t ie_len, + u8 bands_used, u32 *rate_masks, + struct cfg80211_chan_def *chandef) +{ + size_t pos = 0, old_pos = 0, custom_ie_offset = 0; + int i; + + memset(ie_desc, 0, sizeof(*ie_desc)); + + for (i = 0; i < IEEE80211_NUM_BANDS; i++) { + if (bands_used & BIT(i)) { + pos += ieee80211_build_preq_ies_band(local, + buffer + pos, + buffer_len - pos, + ie, ie_len, i, + rate_masks[i], + chandef, + &custom_ie_offset); + ie_desc->ies[i] = buffer + old_pos; + ie_desc->len[i] = pos - old_pos; + old_pos = pos; + } + } + + /* add any remaining custom IEs */ + if (ie && ie_len) { + if (WARN_ONCE(buffer_len - pos < ie_len - custom_ie_offset, + "not enough space for preq custom IEs\n")) + return pos; + memcpy(buffer + pos, ie + custom_ie_offset, + ie_len - custom_ie_offset); + ie_desc->common_ies = buffer + pos; + ie_desc->common_ie_len = ie_len - custom_ie_offset; + pos += ie_len - custom_ie_offset; + } + + return pos; +}; + struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, u32 ratemask, struct ieee80211_channel *chan, @@ -1393,6 +1431,8 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb; struct ieee80211_mgmt *mgmt; int ies_len; + u32 rate_masks[IEEE80211_NUM_BANDS] = {}; + struct ieee80211_scan_ies dummy_ie_desc; /* * Do not send DS Channel parameter for directed probe requests @@ -1410,10 +1450,11 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, if (!skb) return NULL; + rate_masks[chan->band] = ratemask; ies_len = ieee80211_build_preq_ies(local, skb_tail_pointer(skb), - skb_tailroom(skb), - ie, ie_len, chan->band, - ratemask, &chandef); + skb_tailroom(skb), &dummy_ie_desc, + ie, ie_len, BIT(chan->band), + rate_masks, &chandef); skb_put(skb, ies_len); if (dst) { -- cgit v1.2.3 From 633e27132625a0692440c4db58b901fb3cb67c55 Mon Sep 17 00:00:00 2001 From: David Spinadel Date: Thu, 6 Feb 2014 16:15:23 +0200 Subject: mac80211: split sched scan IEs Split sched scan IEs to band specific and not band specific blocks. Common IEs blocks may be sent to the FW once per command, instead of per band. This allows optimization of size of the command, which may be required by some drivers (eg. iwlmvm with newer firmware version). As this changes the mac80211 API, update all drivers to use the new version correctly, even if they don't (yet) make use of the split data. Signed-off-by: David Spinadel Reviewed-by: Alexander Bondar Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 2 +- drivers/net/wireless/iwlwifi/mvm/mvm.h | 2 +- drivers/net/wireless/iwlwifi/mvm/scan.c | 27 +++++++++++------ drivers/net/wireless/ti/wl12xx/scan.c | 20 +++++++----- drivers/net/wireless/ti/wl12xx/scan.h | 2 +- drivers/net/wireless/ti/wl18xx/scan.c | 16 +++++++--- drivers/net/wireless/ti/wl18xx/scan.h | 2 +- drivers/net/wireless/ti/wlcore/cmd.c | 11 ++++--- drivers/net/wireless/ti/wlcore/cmd.h | 3 +- drivers/net/wireless/ti/wlcore/main.c | 2 +- drivers/net/wireless/ti/wlcore/scan.h | 2 +- drivers/net/wireless/ti/wlcore/wlcore.h | 2 +- include/net/mac80211.h | 23 +++----------- net/mac80211/driver-ops.h | 2 +- net/mac80211/scan.c | 47 +++++++++++++++-------------- 15 files changed, 88 insertions(+), 75 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 4dc2e05f49ce..7dd363dd3ad3 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -1828,7 +1828,7 @@ static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw, static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies) + struct ieee80211_scan_ies *ies) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); int ret; diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index fcc6c29482d0..97630f9a7cb9 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -854,7 +854,7 @@ int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm, int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies); + struct ieee80211_scan_ies *ies); int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm, struct cfg80211_sched_scan_request *req); int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index 4b6c7d4bd199..3206fa097255 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -204,7 +204,8 @@ static void iwl_mvm_scan_fill_channels(struct iwl_scan_cmd *cmd, */ static u16 iwl_mvm_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta, int n_ssids, const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len, + const u8 *band_ie, int band_ie_len, + const u8 *common_ie, int common_ie_len, int left) { int len = 0; @@ -244,12 +245,19 @@ static u16 iwl_mvm_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta, len += ssid_len + 2; - if (WARN_ON(left < ie_len)) + if (WARN_ON(left < band_ie_len + common_ie_len)) return len; - if (ie && ie_len) { - memcpy(pos, ie, ie_len); - len += ie_len; + if (band_ie && band_ie_len) { + memcpy(pos, band_ie, band_ie_len); + pos += band_ie_len; + len += band_ie_len; + } + + if (common_ie && common_ie_len) { + memcpy(pos, common_ie, common_ie_len); + pos += common_ie_len; + len += common_ie_len; } return (u16)len; @@ -382,7 +390,7 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm, (struct ieee80211_mgmt *)cmd->data, vif->addr, req->n_ssids, ssid, ssid_len, - req->ie, req->ie_len, + req->ie, req->ie_len, NULL, 0, mvm->fw->ucode_capa.max_probe_length)); iwl_mvm_scan_fill_channels(cmd, req, basic_ssid, ¶ms); @@ -561,7 +569,7 @@ int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm, static void iwl_scan_offload_build_tx_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct ieee80211_sched_scan_ies *ies, + struct ieee80211_scan_ies *ies, enum ieee80211_band band, struct iwl_tx_cmd *cmd, u8 *data) @@ -577,7 +585,8 @@ static void iwl_scan_offload_build_tx_cmd(struct iwl_mvm *mvm, cmd_len = iwl_mvm_fill_probe_req((struct ieee80211_mgmt *)data, vif->addr, 1, NULL, 0, - ies->ie[band], ies->len[band], + ies->ies[band], ies->len[band], + ies->common_ies, ies->common_ie_len, SCAN_OFFLOAD_PROBE_REQ_SIZE); cmd->len = cpu_to_le16(cmd_len); } @@ -735,7 +744,7 @@ static void iwl_build_channel_cfg(struct iwl_mvm *mvm, int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies) + struct ieee80211_scan_ies *ies) { int band_2ghz = mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels; int band_5ghz = mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels; diff --git a/drivers/net/wireless/ti/wl12xx/scan.c b/drivers/net/wireless/ti/wl12xx/scan.c index 7541bd1a4a4b..0c0d5cd98514 100644 --- a/drivers/net/wireless/ti/wl12xx/scan.c +++ b/drivers/net/wireless/ti/wl12xx/scan.c @@ -156,7 +156,7 @@ static int wl1271_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif, cmd->params.role_id, band, wl->scan.ssid, wl->scan.ssid_len, wl->scan.req->ie, - wl->scan.req->ie_len, false); + wl->scan.req->ie_len, NULL, 0, false); if (ret < 0) { wl1271_error("PROBE request template failed"); goto out; @@ -317,7 +317,7 @@ static void wl12xx_adjust_channels(struct wl1271_cmd_sched_scan_config *cmd, int wl1271_scan_sched_scan_config(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies) + struct ieee80211_scan_ies *ies) { struct wl1271_cmd_sched_scan_config *cfg = NULL; struct wlcore_scan_channels *cfg_channels = NULL; @@ -378,8 +378,11 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl, wlvif->role_id, band, req->ssids[0].ssid, req->ssids[0].ssid_len, - ies->ie[band], - ies->len[band], true); + ies->ies[band], + ies->len[band], + ies->common_ies, + ies->common_ie_len, + true); if (ret < 0) { wl1271_error("2.4GHz PROBE request template failed"); goto out; @@ -392,8 +395,11 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl, wlvif->role_id, band, req->ssids[0].ssid, req->ssids[0].ssid_len, - ies->ie[band], - ies->len[band], true); + ies->ies[band], + ies->len[band], + ies->common_ies, + ies->common_ie_len, + true); if (ret < 0) { wl1271_error("5GHz PROBE request template failed"); goto out; @@ -449,7 +455,7 @@ out_free: int wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies) + struct ieee80211_scan_ies *ies) { int ret; diff --git a/drivers/net/wireless/ti/wl12xx/scan.h b/drivers/net/wireless/ti/wl12xx/scan.h index 264af7ac2785..427f9af85a00 100644 --- a/drivers/net/wireless/ti/wl12xx/scan.h +++ b/drivers/net/wireless/ti/wl12xx/scan.h @@ -135,6 +135,6 @@ int wl12xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif); void wl12xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies); + struct ieee80211_scan_ies *ies); void wl12xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif); #endif diff --git a/drivers/net/wireless/ti/wl18xx/scan.c b/drivers/net/wireless/ti/wl18xx/scan.c index 2b642f8c9266..98666f235a12 100644 --- a/drivers/net/wireless/ti/wl18xx/scan.c +++ b/drivers/net/wireless/ti/wl18xx/scan.c @@ -113,6 +113,8 @@ static int wl18xx_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif, req->ssids ? req->ssids[0].ssid_len : 0, req->ie, req->ie_len, + NULL, + 0, false); if (ret < 0) { wl1271_error("2.4GHz PROBE request template failed"); @@ -128,6 +130,8 @@ static int wl18xx_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif, req->ssids ? req->ssids[0].ssid_len : 0, req->ie, req->ie_len, + NULL, + 0, false); if (ret < 0) { wl1271_error("5GHz PROBE request template failed"); @@ -161,7 +165,7 @@ static int wl18xx_scan_sched_scan_config(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies) + struct ieee80211_scan_ies *ies) { struct wl18xx_cmd_scan_params *cmd; struct wlcore_scan_channels *cmd_channels = NULL; @@ -237,8 +241,10 @@ int wl18xx_scan_sched_scan_config(struct wl1271 *wl, cmd->role_id, band, req->ssids ? req->ssids[0].ssid : NULL, req->ssids ? req->ssids[0].ssid_len : 0, - ies->ie[band], + ies->ies[band], ies->len[band], + ies->common_ies, + ies->common_ie_len, true); if (ret < 0) { wl1271_error("2.4GHz PROBE request template failed"); @@ -252,8 +258,10 @@ int wl18xx_scan_sched_scan_config(struct wl1271 *wl, cmd->role_id, band, req->ssids ? req->ssids[0].ssid : NULL, req->ssids ? req->ssids[0].ssid_len : 0, - ies->ie[band], + ies->ies[band], ies->len[band], + ies->common_ies, + ies->common_ie_len, true); if (ret < 0) { wl1271_error("5GHz PROBE request template failed"); @@ -277,7 +285,7 @@ out: int wl18xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies) + struct ieee80211_scan_ies *ies) { return wl18xx_scan_sched_scan_config(wl, wlvif, req, ies); } diff --git a/drivers/net/wireless/ti/wl18xx/scan.h b/drivers/net/wireless/ti/wl18xx/scan.h index eadee42689d1..2e636aa5dba9 100644 --- a/drivers/net/wireless/ti/wl18xx/scan.h +++ b/drivers/net/wireless/ti/wl18xx/scan.h @@ -122,6 +122,6 @@ int wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif); void wl18xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl18xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies); + struct ieee80211_scan_ies *ies); void wl18xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif); #endif diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index 40dc30f4faaa..e269c0a57017 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -1124,7 +1124,8 @@ out: int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id, u8 band, const u8 *ssid, size_t ssid_len, - const u8 *ie, size_t ie_len, bool sched_scan) + const u8 *ie0, size_t ie0_len, const u8 *ie1, + size_t ie1_len, bool sched_scan) { struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); struct sk_buff *skb; @@ -1136,13 +1137,15 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, wl1271_debug(DEBUG_SCAN, "build probe request band %d", band); skb = ieee80211_probereq_get(wl->hw, vif, ssid, ssid_len, - ie_len); + ie0_len + ie1_len); if (!skb) { ret = -ENOMEM; goto out; } - if (ie_len) - memcpy(skb_put(skb, ie_len), ie, ie_len); + if (ie0_len) + memcpy(skb_put(skb, ie0_len), ie0, ie0_len); + if (ie1_len) + memcpy(skb_put(skb, ie1_len), ie1, ie1_len); if (sched_scan && (wl->quirks & WLCORE_QUIRK_DUAL_PROBE_TMPL)) { diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h index b084830a61cf..6788d7356ca5 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.h +++ b/drivers/net/wireless/ti/wlcore/cmd.h @@ -64,7 +64,8 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, struct wl12xx_vif *wlvif, int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id, u8 band, const u8 *ssid, size_t ssid_len, - const u8 *ie, size_t ie_len, bool sched_scan); + const u8 *ie, size_t ie_len, const u8 *common_ie, + size_t common_ie_len, bool sched_scan); struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct sk_buff *skb); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index e5ffb8b91dd4..48f83868f9cb 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -3637,7 +3637,7 @@ out: static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies) + struct ieee80211_scan_ies *ies) { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); diff --git a/drivers/net/wireless/ti/wlcore/scan.h b/drivers/net/wireless/ti/wlcore/scan.h index a6ab24b5c0f9..4dadd0c62cde 100644 --- a/drivers/net/wireless/ti/wlcore/scan.h +++ b/drivers/net/wireless/ti/wlcore/scan.h @@ -37,7 +37,7 @@ void wl1271_scan_complete_work(struct work_struct *work); int wl1271_scan_sched_scan_config(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies); + struct ieee80211_scan_ies *ies); int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif); void wlcore_scan_sched_scan_results(struct wl1271 *wl); diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 95a54504f0cc..71320509b56d 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -95,7 +95,7 @@ struct wlcore_ops { int (*scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif); int (*sched_scan_start)(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies); + struct ieee80211_scan_ies *ies); void (*sched_scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif); int (*get_spare_blocks)(struct wl1271 *wl, bool is_gem); int (*set_key)(struct wl1271 *wl, enum set_key_cmd cmd, diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 9536ee3e22a9..545d2fa179c4 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -753,27 +753,12 @@ struct ieee80211_tx_info { }; }; -/** - * struct ieee80211_sched_scan_ies - scheduled scan IEs - * - * This structure is used to pass the appropriate IEs to be used in scheduled - * scans for all bands. It contains both the IEs passed from the userspace - * and the ones generated by mac80211. - * - * @ie: array with the IEs for each supported band - * @len: array with the total length of the IEs for each band - */ -struct ieee80211_sched_scan_ies { - u8 *ie[IEEE80211_NUM_BANDS]; - size_t len[IEEE80211_NUM_BANDS]; -}; - /** * struct ieee80211_scan_ies - descriptors for different blocks of IEs * - * This structure is used to point to different blocks of IEs in HW scan. - * These blocks contain the IEs passed by userspace and the ones generated - * by mac80211. + * This structure is used to point to different blocks of IEs in HW scan + * and scheduled scan. These blocks contain the IEs passed by userspace + * and the ones generated by mac80211. * * @ies: pointers to band specific IEs. * @len: lengths of band_specific IEs. @@ -2917,7 +2902,7 @@ struct ieee80211_ops { int (*sched_scan_start)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies); + struct ieee80211_scan_ies *ies); int (*sched_scan_stop)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); void (*sw_scan_start)(struct ieee80211_hw *hw); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index faa0d90f6e80..11423958116a 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -346,7 +346,7 @@ static inline int drv_sched_scan_start(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct cfg80211_sched_scan_request *req, - struct ieee80211_sched_scan_ies *ies) + struct ieee80211_scan_ies *ies) { int ret; diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 116959e070d0..a0a938145dcc 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -1003,10 +1003,13 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, struct cfg80211_sched_scan_request *req) { struct ieee80211_local *local = sdata->local; - struct ieee80211_sched_scan_ies sched_scan_ies = {}; + struct ieee80211_scan_ies sched_scan_ies = {}; struct cfg80211_chan_def chandef; - int ret, i, iebufsz; - struct ieee80211_scan_ies dummy_ie_desc; + int ret, i, iebufsz, num_bands = 0; + u32 rate_masks[IEEE80211_NUM_BANDS] = {}; + u8 bands_used = 0; + u8 *ie; + size_t len; iebufsz = local->scan_ies_len + req->ie_len; @@ -1016,37 +1019,35 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, return -ENOTSUPP; for (i = 0; i < IEEE80211_NUM_BANDS; i++) { - u32 rate_masks[IEEE80211_NUM_BANDS] = {}; - - if (!local->hw.wiphy->bands[i]) - continue; - - sched_scan_ies.ie[i] = kzalloc(iebufsz, GFP_KERNEL); - if (!sched_scan_ies.ie[i]) { - ret = -ENOMEM; - goto out_free; + if (local->hw.wiphy->bands[i]) { + bands_used |= BIT(i); + rate_masks[i] = (u32) -1; + num_bands++; } + } - ieee80211_prepare_scan_chandef(&chandef, req->scan_width); - rate_masks[i] = (u32) -1; - - sched_scan_ies.len[i] = - ieee80211_build_preq_ies(local, sched_scan_ies.ie[i], - iebufsz, &dummy_ie_desc, - req->ie, req->ie_len, BIT(i), - rate_masks, &chandef); + ie = kzalloc(num_bands * iebufsz, GFP_KERNEL); + if (!ie) { + ret = -ENOMEM; + goto out; } + ieee80211_prepare_scan_chandef(&chandef, req->scan_width); + + len = ieee80211_build_preq_ies(local, ie, num_bands * iebufsz, + &sched_scan_ies, req->ie, + req->ie_len, bands_used, + rate_masks, &chandef); + ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies); if (ret == 0) { rcu_assign_pointer(local->sched_scan_sdata, sdata); local->sched_scan_req = req; } -out_free: - while (i > 0) - kfree(sched_scan_ies.ie[--i]); + kfree(ie); +out: if (ret) { /* Clean in case of failure after HW restart or upon resume. */ RCU_INIT_POINTER(local->sched_scan_sdata, NULL); -- cgit v1.2.3 From 5bcae31d9cb1ebfad3ad5a3eea04c8cdc329a04f Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 25 Jun 2014 12:35:06 +0200 Subject: mac80211: implement multi-vif in-place reservations Multi-vif in-place reservations happen when it is impossible to allocate more channel contexts as indicated by interface combinations. Such reservations are not finalized until all assigned interfaces are ready. This still doesn't handle all possible cases (i.e. degradation of number of channels) properly. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- include/net/mac80211.h | 8 +- net/mac80211/chan.c | 765 ++++++++++++++++++++++++++++++++++++++++----- net/mac80211/ieee80211_i.h | 26 +- net/mac80211/util.c | 9 +- 4 files changed, 716 insertions(+), 92 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 545d2fa179c4..9ce5cb17ed82 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1606,12 +1606,6 @@ struct ieee80211_tx_control { * is not enabled the default action is to disconnect when getting the * CSA frame. * - * @IEEE80211_HW_CHANGE_RUNNING_CHANCTX: The hardware can change a - * channel context on-the-fly. This is needed for channel switch - * on single-channel hardware. It can also be used as an - * optimization in certain channel switch cases with - * multi-channel. - * * @IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS: The HW supports scanning on all bands * in one command, mac80211 doesn't have to run separate scans per band. */ @@ -1645,7 +1639,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_TIMING_BEACON_ONLY = 1<<26, IEEE80211_HW_SUPPORTS_HT_CCK_RATES = 1<<27, IEEE80211_HW_CHANCTX_STA_CSA = 1<<28, - IEEE80211_HW_CHANGE_RUNNING_CHANCTX = 1<<29, + /* bit 29 unused */ IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS = 1<<30, }; diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index a310e33972de..0e4302bb5b34 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -63,6 +63,20 @@ static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local) return ieee80211_num_chanctx(local) < ieee80211_max_num_channels(local); } +static struct ieee80211_chanctx * +ieee80211_vif_get_chanctx(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_chanctx_conf *conf; + + conf = rcu_dereference_protected(sdata->vif.chanctx_conf, + lockdep_is_held(&local->chanctx_mtx)); + if (!conf) + return NULL; + + return container_of(conf, struct ieee80211_chanctx, conf); +} + static const struct cfg80211_chan_def * ieee80211_chanctx_reserved_chandef(struct ieee80211_local *local, struct ieee80211_chanctx *ctx, @@ -160,6 +174,9 @@ ieee80211_find_reservation_chanctx(struct ieee80211_local *local, return NULL; list_for_each_entry(ctx, &local->chanctx_list, list) { + if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) + continue; + if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) continue; @@ -347,6 +364,9 @@ ieee80211_find_chanctx(struct ieee80211_local *local, list_for_each_entry(ctx, &local->chanctx_list, list) { const struct cfg80211_chan_def *compat; + if (ctx->replace_state != IEEE80211_CHANCTX_REPLACE_NONE) + continue; + if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) continue; @@ -622,6 +642,7 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) struct ieee80211_local *local = sdata->local; struct ieee80211_chanctx_conf *conf; struct ieee80211_chanctx *ctx; + bool use_reserved_switch = false; lockdep_assert_held(&local->chanctx_mtx); @@ -632,12 +653,23 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) ctx = container_of(conf, struct ieee80211_chanctx, conf); - if (sdata->reserved_chanctx) + if (sdata->reserved_chanctx) { + if (sdata->reserved_chanctx->replace_state == + IEEE80211_CHANCTX_REPLACES_OTHER && + ieee80211_chanctx_num_reserved(local, + sdata->reserved_chanctx) > 1) + use_reserved_switch = true; + ieee80211_vif_unreserve_chanctx(sdata); + } ieee80211_assign_vif_chanctx(sdata, NULL); if (ieee80211_chanctx_refcount(local, ctx) == 0) ieee80211_free_chanctx(local, ctx); + + /* Unreserving may ready an in-place reservation. */ + if (use_reserved_switch) + ieee80211_vif_use_reserved_switch(local); } void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, @@ -905,8 +937,25 @@ int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata) list_del(&sdata->reserved_chanctx_list); sdata->reserved_chanctx = NULL; - if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) - ieee80211_free_chanctx(sdata->local, ctx); + if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) { + if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) { + if (WARN_ON(!ctx->replace_ctx)) + return -EINVAL; + + WARN_ON(ctx->replace_ctx->replace_state != + IEEE80211_CHANCTX_WILL_BE_REPLACED); + WARN_ON(ctx->replace_ctx->replace_ctx != ctx); + + ctx->replace_ctx->replace_ctx = NULL; + ctx->replace_ctx->replace_state = + IEEE80211_CHANCTX_REPLACE_NONE; + + list_del_rcu(&ctx->list); + kfree_rcu(ctx, rcu_head); + } else { + ieee80211_free_chanctx(sdata->local, ctx); + } + } return 0; } @@ -917,40 +966,84 @@ int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata, bool radar_required) { struct ieee80211_local *local = sdata->local; - struct ieee80211_chanctx_conf *conf; - struct ieee80211_chanctx *new_ctx, *curr_ctx; - int ret = 0; + struct ieee80211_chanctx *new_ctx, *curr_ctx, *ctx; - mutex_lock(&local->chanctx_mtx); - - conf = rcu_dereference_protected(sdata->vif.chanctx_conf, - lockdep_is_held(&local->chanctx_mtx)); - if (!conf) { - ret = -EINVAL; - goto out; - } + lockdep_assert_held(&local->chanctx_mtx); - curr_ctx = container_of(conf, struct ieee80211_chanctx, conf); + curr_ctx = ieee80211_vif_get_chanctx(sdata); + if (curr_ctx && local->use_chanctx && !local->ops->switch_vif_chanctx) + return -ENOTSUPP; new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode); if (!new_ctx) { - if (ieee80211_chanctx_refcount(local, curr_ctx) == 1 && - (local->hw.flags & IEEE80211_HW_CHANGE_RUNNING_CHANCTX)) { - /* if we're the only users of the chanctx and - * the driver supports changing a running - * context, reserve our current context - */ - new_ctx = curr_ctx; - } else if (ieee80211_can_create_new_chanctx(local)) { - /* create a new context and reserve it */ + if (ieee80211_can_create_new_chanctx(local)) { new_ctx = ieee80211_new_chanctx(local, chandef, mode); - if (IS_ERR(new_ctx)) { - ret = PTR_ERR(new_ctx); - goto out; - } + if (IS_ERR(new_ctx)) + return PTR_ERR(new_ctx); } else { - ret = -EBUSY; - goto out; + if (!curr_ctx || + (curr_ctx->replace_state == + IEEE80211_CHANCTX_WILL_BE_REPLACED) || + !list_empty(&curr_ctx->reserved_vifs)) { + /* + * Another vif already requested this context + * for a reservation. Find another one hoping + * all vifs assigned to it will also switch + * soon enough. + * + * TODO: This needs a little more work as some + * cases (more than 2 chanctx capable devices) + * may fail which could otherwise succeed + * provided some channel context juggling was + * performed. + * + * Consider ctx1..3, vif1..6, each ctx has 2 + * vifs. vif1 and vif2 from ctx1 request new + * different chandefs starting 2 in-place + * reserations with ctx4 and ctx5 replacing + * ctx1 and ctx2 respectively. Next vif5 and + * vif6 from ctx3 reserve ctx4. If vif3 and + * vif4 remain on ctx2 as they are then this + * fails unless `replace_ctx` from ctx5 is + * replaced with ctx3. + */ + list_for_each_entry(ctx, &local->chanctx_list, + list) { + if (ctx->replace_state != + IEEE80211_CHANCTX_REPLACE_NONE) + continue; + + if (!list_empty(&ctx->reserved_vifs)) + continue; + + curr_ctx = ctx; + break; + } + } + + /* + * If that's true then all available contexts already + * have reservations and cannot be used. + */ + if (!curr_ctx || + (curr_ctx->replace_state == + IEEE80211_CHANCTX_WILL_BE_REPLACED) || + !list_empty(&curr_ctx->reserved_vifs)) + return -EBUSY; + + new_ctx = ieee80211_alloc_chanctx(local, chandef, mode); + if (!new_ctx) + return -ENOMEM; + + new_ctx->replace_ctx = curr_ctx; + new_ctx->replace_state = + IEEE80211_CHANCTX_REPLACES_OTHER; + + curr_ctx->replace_ctx = new_ctx; + curr_ctx->replace_state = + IEEE80211_CHANCTX_WILL_BE_REPLACED; + + list_add_rcu(&new_ctx->list, &local->chanctx_list); } } @@ -958,82 +1051,567 @@ int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata, sdata->reserved_chanctx = new_ctx; sdata->reserved_chandef = *chandef; sdata->reserved_radar_required = radar_required; -out: - mutex_unlock(&local->chanctx_mtx); - return ret; + sdata->reserved_ready = false; + + return 0; } -int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata, - u32 *changed) +static int +ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; - struct ieee80211_chanctx *ctx; - struct ieee80211_chanctx *old_ctx; - struct ieee80211_chanctx_conf *conf; - int ret; - u32 tmp_changed = *changed; - - /* TODO: need to recheck if the chandef is usable etc.? */ + struct ieee80211_vif_chanctx_switch vif_chsw[1] = {}; + struct ieee80211_chanctx *old_ctx, *new_ctx; + const struct cfg80211_chan_def *chandef; + u32 changed = 0; + int err; lockdep_assert_held(&local->mtx); + lockdep_assert_held(&local->chanctx_mtx); - mutex_lock(&local->chanctx_mtx); + new_ctx = sdata->reserved_chanctx; + old_ctx = ieee80211_vif_get_chanctx(sdata); - ctx = sdata->reserved_chanctx; - if (WARN_ON(!ctx)) { - ret = -EINVAL; - goto out; - } + if (WARN_ON(!sdata->reserved_ready)) + return -EBUSY; - conf = rcu_dereference_protected(sdata->vif.chanctx_conf, - lockdep_is_held(&local->chanctx_mtx)); - if (!conf) { - ret = -EINVAL; - goto out; + if (WARN_ON(!new_ctx)) + return -EINVAL; + + if (WARN_ON(!old_ctx)) + return -EINVAL; + + if (WARN_ON(new_ctx->replace_state == + IEEE80211_CHANCTX_REPLACES_OTHER)) + return -EINVAL; + + chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx, + &sdata->reserved_chandef); + if (WARN_ON(!chandef)) + return -EINVAL; + + vif_chsw[0].vif = &sdata->vif; + vif_chsw[0].old_ctx = &old_ctx->conf; + vif_chsw[0].new_ctx = &new_ctx->conf; + + list_del(&sdata->reserved_chanctx_list); + sdata->reserved_chanctx = NULL; + + err = drv_switch_vif_chanctx(local, vif_chsw, 1, + CHANCTX_SWMODE_REASSIGN_VIF); + if (err) { + if (ieee80211_chanctx_refcount(local, new_ctx) == 0) + ieee80211_free_chanctx(local, new_ctx); + + return err; } - old_ctx = container_of(conf, struct ieee80211_chanctx, conf); + list_move(&sdata->assigned_chanctx_list, &new_ctx->assigned_vifs); + rcu_assign_pointer(sdata->vif.chanctx_conf, &new_ctx->conf); + + if (sdata->vif.type == NL80211_IFTYPE_AP) + __ieee80211_vif_copy_chanctx_to_vlans(sdata, false); + + if (ieee80211_chanctx_refcount(local, old_ctx) == 0) + ieee80211_free_chanctx(local, old_ctx); if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width) - tmp_changed |= BSS_CHANGED_BANDWIDTH; + changed = BSS_CHANGED_BANDWIDTH; sdata->vif.bss_conf.chandef = sdata->reserved_chandef; - /* unref our reservation */ - sdata->reserved_chanctx = NULL; - sdata->radar_required = sdata->reserved_radar_required; + if (changed) + ieee80211_bss_info_change_notify(sdata, changed); + + return err; +} + +static int +ieee80211_vif_use_reserved_assign(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_chanctx *old_ctx, *new_ctx; + const struct cfg80211_chan_def *chandef; + int err; + + old_ctx = ieee80211_vif_get_chanctx(sdata); + new_ctx = sdata->reserved_chanctx; + + if (WARN_ON(!sdata->reserved_ready)) + return -EINVAL; + + if (WARN_ON(old_ctx)) + return -EINVAL; + + if (WARN_ON(!new_ctx)) + return -EINVAL; + + if (WARN_ON(new_ctx->replace_state == + IEEE80211_CHANCTX_REPLACES_OTHER)) + return -EINVAL; + + chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx, + &sdata->reserved_chandef); + if (WARN_ON(!chandef)) + return -EINVAL; + list_del(&sdata->reserved_chanctx_list); + sdata->reserved_chanctx = NULL; - if (old_ctx == ctx) { - /* This is our own context, just change it */ - ret = __ieee80211_vif_change_channel(sdata, old_ctx, - &tmp_changed); - if (ret) - goto out; - } else { - ret = ieee80211_assign_vif_chanctx(sdata, ctx); - if (ieee80211_chanctx_refcount(local, old_ctx) == 0) - ieee80211_free_chanctx(local, old_ctx); - if (ret) { - /* if assign fails refcount stays the same */ - if (ieee80211_chanctx_refcount(local, ctx) == 0) - ieee80211_free_chanctx(local, ctx); + err = ieee80211_assign_vif_chanctx(sdata, new_ctx); + if (err) { + if (ieee80211_chanctx_refcount(local, new_ctx) == 0) + ieee80211_free_chanctx(local, new_ctx); + + goto out; + } + +out: + return err; +} + +static bool +ieee80211_vif_has_in_place_reservation(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_chanctx *old_ctx, *new_ctx; + + lockdep_assert_held(&sdata->local->chanctx_mtx); + + new_ctx = sdata->reserved_chanctx; + old_ctx = ieee80211_vif_get_chanctx(sdata); + + if (!old_ctx) + return false; + + if (WARN_ON(!new_ctx)) + return false; + + if (old_ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED) + return false; + + if (new_ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) + return false; + + return true; +} + +static int ieee80211_chsw_switch_hwconf(struct ieee80211_local *local, + struct ieee80211_chanctx *new_ctx) +{ + const struct cfg80211_chan_def *chandef; + + lockdep_assert_held(&local->mtx); + lockdep_assert_held(&local->chanctx_mtx); + + chandef = ieee80211_chanctx_reserved_chandef(local, new_ctx, NULL); + if (WARN_ON(!chandef)) + return -EINVAL; + + local->hw.conf.radar_enabled = new_ctx->conf.radar_enabled; + local->_oper_chandef = *chandef; + ieee80211_hw_config(local, 0); + + return 0; +} + +static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local, + int n_vifs) +{ + struct ieee80211_vif_chanctx_switch *vif_chsw; + struct ieee80211_sub_if_data *sdata; + struct ieee80211_chanctx *ctx, *old_ctx; + int i, err; + + lockdep_assert_held(&local->mtx); + lockdep_assert_held(&local->chanctx_mtx); + + vif_chsw = kzalloc(sizeof(vif_chsw[0]) * n_vifs, GFP_KERNEL); + if (!vif_chsw) + return -ENOMEM; + + i = 0; + list_for_each_entry(ctx, &local->chanctx_list, list) { + if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) + continue; + + if (WARN_ON(!ctx->replace_ctx)) { + err = -EINVAL; goto out; } - if (sdata->vif.type == NL80211_IFTYPE_AP) - __ieee80211_vif_copy_chanctx_to_vlans(sdata, false); + list_for_each_entry(sdata, &ctx->reserved_vifs, + reserved_chanctx_list) { + if (!ieee80211_vif_has_in_place_reservation( + sdata)) + continue; + + old_ctx = ieee80211_vif_get_chanctx(sdata); + vif_chsw[i].vif = &sdata->vif; + vif_chsw[i].old_ctx = &old_ctx->conf; + vif_chsw[i].new_ctx = &ctx->conf; + + i++; + } } - *changed = tmp_changed; + err = drv_switch_vif_chanctx(local, vif_chsw, n_vifs, + CHANCTX_SWMODE_SWAP_CONTEXTS); - ieee80211_recalc_chanctx_chantype(local, ctx); - ieee80211_recalc_smps_chanctx(local, ctx); - ieee80211_recalc_radar_chanctx(local, ctx); - ieee80211_recalc_chanctx_min_def(local, ctx); out: - mutex_unlock(&local->chanctx_mtx); - return ret; + kfree(vif_chsw); + return err; +} + +static int ieee80211_chsw_switch_ctxs(struct ieee80211_local *local) +{ + struct ieee80211_chanctx *ctx; + int err; + + lockdep_assert_held(&local->mtx); + lockdep_assert_held(&local->chanctx_mtx); + + list_for_each_entry(ctx, &local->chanctx_list, list) { + if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) + continue; + + if (!list_empty(&ctx->replace_ctx->assigned_vifs)) + continue; + + ieee80211_del_chanctx(local, ctx->replace_ctx); + err = ieee80211_add_chanctx(local, ctx); + if (err) + goto err; + } + + return 0; + +err: + WARN_ON(ieee80211_add_chanctx(local, ctx)); + list_for_each_entry_continue_reverse(ctx, &local->chanctx_list, list) { + if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) + continue; + + if (!list_empty(&ctx->replace_ctx->assigned_vifs)) + continue; + + ieee80211_del_chanctx(local, ctx); + WARN_ON(ieee80211_add_chanctx(local, ctx->replace_ctx)); + } + + return err; +} + +int +ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) +{ + struct ieee80211_sub_if_data *sdata, *sdata_tmp; + struct ieee80211_chanctx *ctx, *ctx_tmp, *old_ctx; + struct ieee80211_chanctx *new_ctx = NULL; + int i, err, n_assigned, n_reserved, n_ready; + int n_ctx = 0, n_vifs_switch = 0, n_vifs_assign = 0, n_vifs_ctxless = 0; + + lockdep_assert_held(&local->mtx); + lockdep_assert_held(&local->chanctx_mtx); + + /* + * If there are 2 independent pairs of channel contexts performing + * cross-switch of their vifs this code will still wait until both are + * ready even though it could be possible to switch one before the + * other is ready. + * + * For practical reasons and code simplicity just do a single huge + * switch. + */ + + /* + * Verify if the reservation is still feasible. + * - if it's not then disconnect + * - if it is but not all vifs necessary are ready then defer + */ + + list_for_each_entry(ctx, &local->chanctx_list, list) { + if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) + continue; + + if (WARN_ON(!ctx->replace_ctx)) { + err = -EINVAL; + goto err; + } + + if (!local->use_chanctx) + new_ctx = ctx; + + n_ctx++; + + n_assigned = 0; + n_reserved = 0; + n_ready = 0; + + list_for_each_entry(sdata, &ctx->replace_ctx->assigned_vifs, + assigned_chanctx_list) { + n_assigned++; + if (sdata->reserved_chanctx) { + n_reserved++; + if (sdata->reserved_ready) + n_ready++; + } + } + + if (n_assigned != n_reserved) { + if (n_ready == n_reserved) { + wiphy_info(local->hw.wiphy, + "channel context reservation cannot be finalized because some interfaces aren't switching\n"); + err = -EBUSY; + goto err; + } + + return -EAGAIN; + } + + ctx->conf.radar_enabled = false; + list_for_each_entry(sdata, &ctx->reserved_vifs, + reserved_chanctx_list) { + if (ieee80211_vif_has_in_place_reservation(sdata) && + !sdata->reserved_ready) + return -EAGAIN; + + old_ctx = ieee80211_vif_get_chanctx(sdata); + if (old_ctx) { + if (old_ctx->replace_state == + IEEE80211_CHANCTX_WILL_BE_REPLACED) + n_vifs_switch++; + else + n_vifs_assign++; + } else { + n_vifs_ctxless++; + } + + if (sdata->reserved_radar_required) + ctx->conf.radar_enabled = true; + } + } + + if (WARN_ON(n_ctx == 0) || + WARN_ON(n_vifs_switch == 0 && + n_vifs_assign == 0 && + n_vifs_ctxless == 0) || + WARN_ON(n_ctx > 1 && !local->use_chanctx) || + WARN_ON(!new_ctx && !local->use_chanctx)) { + err = -EINVAL; + goto err; + } + + /* + * All necessary vifs are ready. Perform the switch now depending on + * reservations and driver capabilities. + */ + + if (local->use_chanctx) { + if (n_vifs_switch > 0) { + err = ieee80211_chsw_switch_vifs(local, n_vifs_switch); + if (err) + goto err; + } + + if (n_vifs_assign > 0 || n_vifs_ctxless > 0) { + err = ieee80211_chsw_switch_ctxs(local); + if (err) + goto err; + } + } else { + err = ieee80211_chsw_switch_hwconf(local, new_ctx); + if (err) + goto err; + } + + /* + * Update all structures, values and pointers to point to new channel + * context(s). + */ + + i = 0; + list_for_each_entry(ctx, &local->chanctx_list, list) { + if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) + continue; + + if (WARN_ON(!ctx->replace_ctx)) { + err = -EINVAL; + goto err; + } + + list_for_each_entry(sdata, &ctx->reserved_vifs, + reserved_chanctx_list) { + u32 changed = 0; + + if (!ieee80211_vif_has_in_place_reservation(sdata)) + continue; + + rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf); + + if (sdata->vif.type == NL80211_IFTYPE_AP) + __ieee80211_vif_copy_chanctx_to_vlans(sdata, + false); + + sdata->radar_required = sdata->reserved_radar_required; + + if (sdata->vif.bss_conf.chandef.width != + sdata->reserved_chandef.width) + changed = BSS_CHANGED_BANDWIDTH; + + sdata->vif.bss_conf.chandef = sdata->reserved_chandef; + if (changed) + ieee80211_bss_info_change_notify(sdata, + changed); + + ieee80211_recalc_txpower(sdata); + } + + ieee80211_recalc_chanctx_chantype(local, ctx); + ieee80211_recalc_smps_chanctx(local, ctx); + ieee80211_recalc_radar_chanctx(local, ctx); + ieee80211_recalc_chanctx_min_def(local, ctx); + + list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, + reserved_chanctx_list) { + if (ieee80211_vif_get_chanctx(sdata) != ctx) + continue; + + list_del(&sdata->reserved_chanctx_list); + list_move(&sdata->assigned_chanctx_list, + &new_ctx->assigned_vifs); + sdata->reserved_chanctx = NULL; + } + + /* + * This context might have been a dependency for an already + * ready re-assign reservation interface that was deferred. Do + * not propagate error to the caller though. The in-place + * reservation for originally requested interface has already + * succeeded at this point. + */ + list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, + reserved_chanctx_list) { + if (WARN_ON(ieee80211_vif_has_in_place_reservation( + sdata))) + continue; + + if (WARN_ON(sdata->reserved_chanctx != ctx)) + continue; + + if (!sdata->reserved_ready) + continue; + + if (ieee80211_vif_get_chanctx(sdata)) + err = ieee80211_vif_use_reserved_reassign( + sdata); + else + err = ieee80211_vif_use_reserved_assign(sdata); + + if (err) { + sdata_info(sdata, + "failed to finalize (re-)assign reservation (err=%d)\n", + err); + ieee80211_vif_unreserve_chanctx(sdata); + cfg80211_stop_iface(local->hw.wiphy, + &sdata->wdev, + GFP_KERNEL); + } + } + } + + /* + * Finally free old contexts + */ + + list_for_each_entry_safe(ctx, ctx_tmp, &local->chanctx_list, list) { + if (ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED) + continue; + + ctx->replace_ctx->replace_ctx = NULL; + ctx->replace_ctx->replace_state = + IEEE80211_CHANCTX_REPLACE_NONE; + + list_del_rcu(&ctx->list); + kfree_rcu(ctx, rcu_head); + } + + return 0; + +err: + list_for_each_entry(ctx, &local->chanctx_list, list) { + if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) + continue; + + list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, + reserved_chanctx_list) + ieee80211_vif_unreserve_chanctx(sdata); + } + + return err; +} + +int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_chanctx *new_ctx; + struct ieee80211_chanctx *old_ctx; + int err; + + lockdep_assert_held(&local->mtx); + lockdep_assert_held(&local->chanctx_mtx); + + new_ctx = sdata->reserved_chanctx; + old_ctx = ieee80211_vif_get_chanctx(sdata); + + if (WARN_ON(!new_ctx)) + return -EINVAL; + + if (WARN_ON(new_ctx->replace_state == + IEEE80211_CHANCTX_WILL_BE_REPLACED)) + return -EINVAL; + + if (WARN_ON(sdata->reserved_ready)) + return -EINVAL; + + sdata->reserved_ready = true; + + if (new_ctx->replace_state == IEEE80211_CHANCTX_REPLACE_NONE) { + if (old_ctx) + err = ieee80211_vif_use_reserved_reassign(sdata); + else + err = ieee80211_vif_use_reserved_assign(sdata); + + if (err) + return err; + } + + /* + * In-place reservation may need to be finalized now either if: + * a) sdata is taking part in the swapping itself and is the last one + * b) sdata has switched with a re-assign reservation to an existing + * context readying in-place switching of old_ctx + * + * In case of (b) do not propagate the error up because the requested + * sdata already switched successfully. Just spill an extra warning. + * The ieee80211_vif_use_reserved_switch() already stops all necessary + * interfaces upon failure. + */ + if ((old_ctx && + old_ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) || + new_ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) { + err = ieee80211_vif_use_reserved_switch(local); + if (err && err != -EAGAIN) { + if (new_ctx->replace_state == + IEEE80211_CHANCTX_REPLACES_OTHER) + return err; + + wiphy_info(local->hw.wiphy, + "depending in-place reservation failed (err=%d)\n", + err); + } + } + + return 0; } int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, @@ -1043,6 +1621,7 @@ int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct ieee80211_chanctx_conf *conf; struct ieee80211_chanctx *ctx; + const struct cfg80211_chan_def *compat; int ret; if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, @@ -1069,11 +1648,33 @@ int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, } ctx = container_of(conf, struct ieee80211_chanctx, conf); - if (!cfg80211_chandef_compatible(&conf->def, chandef)) { + + compat = cfg80211_chandef_compatible(&conf->def, chandef); + if (!compat) { ret = -EINVAL; goto out; } + switch (ctx->replace_state) { + case IEEE80211_CHANCTX_REPLACE_NONE: + if (!ieee80211_chanctx_reserved_chandef(local, ctx, compat)) { + ret = -EBUSY; + goto out; + } + break; + case IEEE80211_CHANCTX_WILL_BE_REPLACED: + /* TODO: Perhaps the bandwith change could be treated as a + * reservation itself? */ + ret = -EBUSY; + goto out; + case IEEE80211_CHANCTX_REPLACES_OTHER: + /* channel context that is going to replace another channel + * context doesn't really exist and shouldn't be assigned + * anywhere yet */ + WARN_ON(1); + break; + } + sdata->vif.bss_conf.chandef = *chandef; ieee80211_recalc_chanctx_chantype(local, ctx); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index f88bd1659cde..c26955f6c14d 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -701,6 +701,24 @@ enum ieee80211_chanctx_mode { IEEE80211_CHANCTX_EXCLUSIVE }; +/** + * enum ieee80211_chanctx_replace_state - channel context replacement state + * + * This is used for channel context in-place reservations that require channel + * context switch/swap. + * + * @IEEE80211_CHANCTX_REPLACE_NONE: no replacement is taking place + * @IEEE80211_CHANCTX_WILL_BE_REPLACED: this channel context will be replaced + * by a (not yet registered) channel context pointed by %replace_ctx. + * @IEEE80211_CHANCTX_REPLACES_OTHER: this (not yet registered) channel context + * replaces an existing channel context pointed to by %replace_ctx. + */ +enum ieee80211_chanctx_replace_state { + IEEE80211_CHANCTX_REPLACE_NONE, + IEEE80211_CHANCTX_WILL_BE_REPLACED, + IEEE80211_CHANCTX_REPLACES_OTHER, +}; + struct ieee80211_chanctx { struct list_head list; struct rcu_head rcu_head; @@ -708,6 +726,9 @@ struct ieee80211_chanctx { struct list_head assigned_vifs; struct list_head reserved_vifs; + enum ieee80211_chanctx_replace_state replace_state; + struct ieee80211_chanctx *replace_ctx; + enum ieee80211_chanctx_mode mode; bool driver_present; @@ -778,6 +799,7 @@ struct ieee80211_sub_if_data { struct ieee80211_chanctx *reserved_chanctx; struct cfg80211_chan_def reserved_chandef; bool reserved_radar_required; + bool reserved_ready; /* used to reconfigure hardware SM PS */ struct work_struct recalc_smps; @@ -1820,9 +1842,9 @@ ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata, enum ieee80211_chanctx_mode mode, bool radar_required); int __must_check -ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata, - u32 *changed); +ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata); int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata); +int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local); int __must_check ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/util.c b/net/mac80211/util.c index e31458201278..1b42aa16ec03 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1698,7 +1698,9 @@ int ieee80211_reconfig(struct ieee80211_local *local) if (local->use_chanctx) { mutex_lock(&local->chanctx_mtx); list_for_each_entry(ctx, &local->chanctx_list, list) - WARN_ON(drv_add_chanctx(local, ctx)); + if (ctx->replace_state != + IEEE80211_CHANCTX_REPLACES_OTHER) + WARN_ON(drv_add_chanctx(local, ctx)); mutex_unlock(&local->chanctx_mtx); list_for_each_entry(sdata, &local->interfaces, list) { @@ -2972,6 +2974,8 @@ int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, num[iftype] = 1; list_for_each_entry(ctx, &local->chanctx_list, list) { + if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) + continue; if (ctx->conf.radar_enabled) radar_detect |= BIT(ctx->conf.def.width); if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) { @@ -3030,6 +3034,9 @@ int ieee80211_max_num_channels(struct ieee80211_local *local) lockdep_assert_held(&local->chanctx_mtx); list_for_each_entry(ctx, &local->chanctx_list, list) { + if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) + continue; + num_different_channels++; if (ctx->conf.radar_enabled) -- cgit v1.2.3 From 97dc94f1d933c9df2c0b327066ea130c0e92083f Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 25 Jun 2014 12:35:10 +0200 Subject: cfg80211: remove channel_switch combination check Driver is now responsible for veryfing if the switch is possible. Since this is inherently tricky driver may decide to disconnect an interface later with cfg80211_stop_iface(). This doesn't mean driver can accept everything. It should do it's best to verify requests and reject them as soon as possible. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 7 ++++++- net/wireless/nl80211.c | 11 ----------- 2 files changed, 6 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index b9eeae3990cf..0a080c4de275 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2301,7 +2301,12 @@ struct cfg80211_qos_map { * reliability. This operation can not fail. * @set_coalesce: Set coalesce parameters. * - * @channel_switch: initiate channel-switch procedure (with CSA) + * @channel_switch: initiate channel-switch procedure (with CSA). Driver is + * responsible for veryfing if the switch is possible. Since this is + * inherently tricky driver may decide to disconnect an interface later + * with cfg80211_stop_iface(). This doesn't mean driver can accept + * everything. It should do it's best to verify requests and reject them + * as soon as possible. * * @set_qos_map: Set QoS mapping information to the driver * diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 8f46b8ffbcf6..c10295138eb5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6013,17 +6013,6 @@ skip_beacons: params.radar_required = true; } - /* TODO: I left this here for now. With channel switch, the - * verification is a bit more complicated, because we only do - * it later when the channel switch really happens. - */ - err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, - params.chandef.chan, - CHAN_MODE_SHARED, - radar_detect_width); - if (err) - return err; - if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX]) params.block_tx = true; -- cgit v1.2.3 From 9500507c61381ceda4edbefa7361a4d26f54eb17 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 10 Jun 2014 23:12:56 +0200 Subject: netfilter: conntrack: remove timer from ecache extension This brings the (per-conntrack) ecache extension back to 24 bytes in size (was 152 byte on x86_64 with lockdep on). When event delivery fails, re-delivery is attempted via work queue. Redelivery is attempted at least every 0.1 seconds, but can happen more frequently if userspace is not congested. The nf_ct_release_dying_list() function is removed. With this patch, ownership of the to-be-redelivered conntracks (on-dying-list-with-DYING-bit not yet set) is with the work queue, which will release the references once event is out. Joint work with Pablo Neira Ayuso. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_ecache.h | 26 +++++++- include/net/netns/conntrack.h | 6 +- net/netfilter/nf_conntrack_core.c | 68 +++----------------- net/netfilter/nf_conntrack_ecache.c | 96 ++++++++++++++++++++++++++--- 4 files changed, 124 insertions(+), 72 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack_ecache.h b/include/net/netfilter/nf_conntrack_ecache.h index 0e3d08e4b1d3..57c880378443 100644 --- a/include/net/netfilter/nf_conntrack_ecache.h +++ b/include/net/netfilter/nf_conntrack_ecache.h @@ -18,7 +18,6 @@ struct nf_conntrack_ecache { u16 ctmask; /* bitmask of ct events to be delivered */ u16 expmask; /* bitmask of expect events to be delivered */ u32 portid; /* netlink portid of destroyer */ - struct timer_list timeout; }; static inline struct nf_conntrack_ecache * @@ -216,8 +215,23 @@ void nf_conntrack_ecache_pernet_fini(struct net *net); int nf_conntrack_ecache_init(void); void nf_conntrack_ecache_fini(void); -#else /* CONFIG_NF_CONNTRACK_EVENTS */ +static inline void nf_conntrack_ecache_delayed_work(struct net *net) +{ + if (!delayed_work_pending(&net->ct.ecache_dwork)) { + schedule_delayed_work(&net->ct.ecache_dwork, HZ); + net->ct.ecache_dwork_pending = true; + } +} + +static inline void nf_conntrack_ecache_work(struct net *net) +{ + if (net->ct.ecache_dwork_pending) { + net->ct.ecache_dwork_pending = false; + mod_delayed_work(system_wq, &net->ct.ecache_dwork, 0); + } +} +#else /* CONFIG_NF_CONNTRACK_EVENTS */ static inline void nf_conntrack_event_cache(enum ip_conntrack_events event, struct nf_conn *ct) {} static inline int nf_conntrack_eventmask_report(unsigned int eventmask, @@ -255,6 +269,14 @@ static inline int nf_conntrack_ecache_init(void) static inline void nf_conntrack_ecache_fini(void) { } + +static inline void nf_conntrack_ecache_delayed_work(struct net *net) +{ +} + +static inline void nf_conntrack_ecache_work(struct net *net) +{ +} #endif /* CONFIG_NF_CONNTRACK_EVENTS */ #endif /*_NF_CONNTRACK_ECACHE_H*/ diff --git a/include/net/netns/conntrack.h b/include/net/netns/conntrack.h index 773cce308bc6..29d6a94db54d 100644 --- a/include/net/netns/conntrack.h +++ b/include/net/netns/conntrack.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -73,6 +74,10 @@ struct ct_pcpu { struct netns_ct { atomic_t count; unsigned int expect_count; +#ifdef CONFIG_NF_CONNTRACK_EVENTS + struct delayed_work ecache_dwork; + bool ecache_dwork_pending; +#endif #ifdef CONFIG_SYSCTL struct ctl_table_header *sysctl_header; struct ctl_table_header *acct_sysctl_header; @@ -82,7 +87,6 @@ struct netns_ct { #endif char *slabname; unsigned int sysctl_log_invalid; /* Log invalid packets */ - unsigned int sysctl_events_retry_timeout; int sysctl_events; int sysctl_acct; int sysctl_auto_assign_helper; diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 1f4f954c4b47..de88c4ab5146 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -352,40 +352,6 @@ static void nf_ct_delete_from_lists(struct nf_conn *ct) local_bh_enable(); } -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 */ - ecache->timeout.expires = jiffies + - (prandom_u32() % net->ct.sysctl_events_retry_timeout); - add_timer(&ecache->timeout); - return; - } - /* we've got the event delivered, now it's dying */ - set_bit(IPS_DYING_BIT, &ct->status); - nf_ct_put(ct); -} - -static void nf_ct_dying_timeout(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); - - /* set a new timer to retry event delivery */ - setup_timer(&ecache->timeout, death_by_event, (unsigned long)ct); - ecache->timeout.expires = jiffies + - (prandom_u32() % net->ct.sysctl_events_retry_timeout); - add_timer(&ecache->timeout); -} - bool nf_ct_delete(struct nf_conn *ct, u32 portid, int report) { struct nf_conn_tstamp *tstamp; @@ -394,15 +360,20 @@ bool nf_ct_delete(struct nf_conn *ct, u32 portid, int report) if (tstamp && tstamp->stop == 0) tstamp->stop = ktime_to_ns(ktime_get_real()); - if (!nf_ct_is_dying(ct) && - unlikely(nf_conntrack_event_report(IPCT_DESTROY, ct, - portid, report) < 0)) { + if (nf_ct_is_dying(ct)) + goto delete; + + if (nf_conntrack_event_report(IPCT_DESTROY, ct, + portid, report) < 0) { /* destroy event was not delivered */ nf_ct_delete_from_lists(ct); - nf_ct_dying_timeout(ct); + nf_conntrack_ecache_delayed_work(nf_ct_net(ct)); return false; } + + nf_conntrack_ecache_work(nf_ct_net(ct)); set_bit(IPS_DYING_BIT, &ct->status); + delete: nf_ct_delete_from_lists(ct); nf_ct_put(ct); return true; @@ -1464,26 +1435,6 @@ void nf_conntrack_flush_report(struct net *net, u32 portid, int report) } EXPORT_SYMBOL_GPL(nf_conntrack_flush_report); -static void nf_ct_release_dying_list(struct net *net) -{ - struct nf_conntrack_tuple_hash *h; - struct nf_conn *ct; - struct hlist_nulls_node *n; - int cpu; - - for_each_possible_cpu(cpu) { - struct ct_pcpu *pcpu = per_cpu_ptr(net->ct.pcpu_lists, cpu); - - spin_lock_bh(&pcpu->lock); - hlist_nulls_for_each_entry(h, n, &pcpu->dying, hnnode) { - ct = nf_ct_tuplehash_to_ctrack(h); - /* never fails to remove them, no listeners at this point */ - nf_ct_kill(ct); - } - spin_unlock_bh(&pcpu->lock); - } -} - static int untrack_refs(void) { int cnt = 0, cpu; @@ -1548,7 +1499,6 @@ i_see_dead_people: busy = 0; list_for_each_entry(net, net_exit_list, exit_list) { nf_ct_iterate_cleanup(net, kill_all, NULL, 0, 0); - nf_ct_release_dying_list(net); if (atomic_read(&net->ct.count) != 0) busy = 1; } diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c index 1df176146567..4e78c57b818f 100644 --- a/net/netfilter/nf_conntrack_ecache.c +++ b/net/netfilter/nf_conntrack_ecache.c @@ -29,6 +29,90 @@ static DEFINE_MUTEX(nf_ct_ecache_mutex); +#define ECACHE_RETRY_WAIT (HZ/10) + +enum retry_state { + STATE_CONGESTED, + STATE_RESTART, + STATE_DONE, +}; + +static enum retry_state ecache_work_evict_list(struct ct_pcpu *pcpu) +{ + struct nf_conn *refs[16]; + struct nf_conntrack_tuple_hash *h; + struct hlist_nulls_node *n; + unsigned int evicted = 0; + enum retry_state ret = STATE_DONE; + + spin_lock(&pcpu->lock); + + hlist_nulls_for_each_entry(h, n, &pcpu->dying, hnnode) { + struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(h); + + if (nf_ct_is_dying(ct)) + continue; + + if (nf_conntrack_event(IPCT_DESTROY, ct)) { + ret = STATE_CONGESTED; + break; + } + + /* we've got the event delivered, now it's dying */ + set_bit(IPS_DYING_BIT, &ct->status); + refs[evicted] = ct; + + if (++evicted >= ARRAY_SIZE(refs)) { + ret = STATE_RESTART; + break; + } + } + + spin_unlock(&pcpu->lock); + + /* can't _put while holding lock */ + while (evicted) + nf_ct_put(refs[--evicted]); + + return ret; +} + +static void ecache_work(struct work_struct *work) +{ + struct netns_ct *ctnet = + container_of(work, struct netns_ct, ecache_dwork.work); + int cpu, delay = -1; + struct ct_pcpu *pcpu; + + local_bh_disable(); + + for_each_possible_cpu(cpu) { + enum retry_state ret; + + pcpu = per_cpu_ptr(ctnet->pcpu_lists, cpu); + + ret = ecache_work_evict_list(pcpu); + + switch (ret) { + case STATE_CONGESTED: + delay = ECACHE_RETRY_WAIT; + goto out; + case STATE_RESTART: + delay = 0; + break; + case STATE_DONE: + break; + } + } + + out: + local_bh_enable(); + + ctnet->ecache_dwork_pending = delay > 0; + if (delay >= 0) + schedule_delayed_work(&ctnet->ecache_dwork, delay); +} + /* deliver cached events and clear cache entry - must be called with locally * disabled softirqs */ void nf_ct_deliver_cached_events(struct nf_conn *ct) @@ -157,7 +241,6 @@ EXPORT_SYMBOL_GPL(nf_ct_expect_unregister_notifier); #define NF_CT_EVENTS_DEFAULT 1 static int nf_ct_events __read_mostly = NF_CT_EVENTS_DEFAULT; -static int nf_ct_events_retry_timeout __read_mostly = 15*HZ; #ifdef CONFIG_SYSCTL static struct ctl_table event_sysctl_table[] = { @@ -168,13 +251,6 @@ static struct ctl_table event_sysctl_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, - { - .procname = "nf_conntrack_events_retry_timeout", - .data = &init_net.ct.sysctl_events_retry_timeout, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = proc_dointvec_jiffies, - }, {} }; #endif /* CONFIG_SYSCTL */ @@ -196,7 +272,6 @@ static int nf_conntrack_event_init_sysctl(struct net *net) goto out; table[0].data = &net->ct.sysctl_events; - table[1].data = &net->ct.sysctl_events_retry_timeout; /* Don't export sysctls to unprivileged users */ if (net->user_ns != &init_user_ns) @@ -238,12 +313,13 @@ static void nf_conntrack_event_fini_sysctl(struct net *net) int nf_conntrack_ecache_pernet_init(struct net *net) { net->ct.sysctl_events = nf_ct_events; - net->ct.sysctl_events_retry_timeout = nf_ct_events_retry_timeout; + INIT_DELAYED_WORK(&net->ct.ecache_dwork, ecache_work); return nf_conntrack_event_init_sysctl(net); } void nf_conntrack_ecache_pernet_fini(struct net *net) { + cancel_delayed_work_sync(&net->ct.ecache_dwork); nf_conntrack_event_fini_sysctl(net); } -- cgit v1.2.3 From 7200135bc1e61f1437dc326ae2ef2f310c50b4eb Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 16 Jun 2014 13:01:52 +0200 Subject: netfilter: kill ulog targets This has been marked as deprecated for quite some time and the NFLOG target replacement has been also available since 2006. Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter_bridge/Kbuild | 1 - include/uapi/linux/netfilter_bridge/ebt_ulog.h | 38 -- include/uapi/linux/netfilter_ipv4/Kbuild | 1 - include/uapi/linux/netfilter_ipv4/ipt_ULOG.h | 49 --- net/bridge/netfilter/Kconfig | 16 - net/bridge/netfilter/ebt_ulog.c | 393 ------------------- net/ipv4/netfilter/Kconfig | 19 - net/ipv4/netfilter/ipt_ULOG.c | 498 ------------------------- 8 files changed, 1015 deletions(-) delete mode 100644 include/uapi/linux/netfilter_bridge/ebt_ulog.h delete mode 100644 include/uapi/linux/netfilter_ipv4/ipt_ULOG.h delete mode 100644 net/bridge/netfilter/ebt_ulog.c delete mode 100644 net/ipv4/netfilter/ipt_ULOG.c (limited to 'include') diff --git a/include/uapi/linux/netfilter_bridge/Kbuild b/include/uapi/linux/netfilter_bridge/Kbuild index 348717c3a22f..0fbad8ef96de 100644 --- a/include/uapi/linux/netfilter_bridge/Kbuild +++ b/include/uapi/linux/netfilter_bridge/Kbuild @@ -14,6 +14,5 @@ header-y += ebt_nflog.h header-y += ebt_pkttype.h header-y += ebt_redirect.h header-y += ebt_stp.h -header-y += ebt_ulog.h header-y += ebt_vlan.h header-y += ebtables.h diff --git a/include/uapi/linux/netfilter_bridge/ebt_ulog.h b/include/uapi/linux/netfilter_bridge/ebt_ulog.h deleted file mode 100644 index 89a6becb5269..000000000000 --- a/include/uapi/linux/netfilter_bridge/ebt_ulog.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef _EBT_ULOG_H -#define _EBT_ULOG_H - -#include - -#define EBT_ULOG_DEFAULT_NLGROUP 0 -#define EBT_ULOG_DEFAULT_QTHRESHOLD 1 -#define EBT_ULOG_MAXNLGROUPS 32 /* hardcoded netlink max */ -#define EBT_ULOG_PREFIX_LEN 32 -#define EBT_ULOG_MAX_QLEN 50 -#define EBT_ULOG_WATCHER "ulog" -#define EBT_ULOG_VERSION 1 - -struct ebt_ulog_info { - __u32 nlgroup; - unsigned int cprange; - unsigned int qthreshold; - char prefix[EBT_ULOG_PREFIX_LEN]; -}; - -typedef struct ebt_ulog_packet_msg { - int version; - char indev[IFNAMSIZ]; - char outdev[IFNAMSIZ]; - char physindev[IFNAMSIZ]; - char physoutdev[IFNAMSIZ]; - char prefix[EBT_ULOG_PREFIX_LEN]; - struct timeval stamp; - unsigned long mark; - unsigned int hook; - size_t data_len; - /* The complete packet, including Ethernet header and perhaps - * the VLAN header is appended */ - unsigned char data[0] __attribute__ - ((aligned (__alignof__(struct ebt_ulog_info)))); -} ebt_ulog_packet_msg_t; - -#endif /* _EBT_ULOG_H */ diff --git a/include/uapi/linux/netfilter_ipv4/Kbuild b/include/uapi/linux/netfilter_ipv4/Kbuild index fb008437dde1..ecb291df390e 100644 --- a/include/uapi/linux/netfilter_ipv4/Kbuild +++ b/include/uapi/linux/netfilter_ipv4/Kbuild @@ -5,7 +5,6 @@ header-y += ipt_ECN.h header-y += ipt_LOG.h header-y += ipt_REJECT.h header-y += ipt_TTL.h -header-y += ipt_ULOG.h header-y += ipt_ah.h header-y += ipt_ecn.h header-y += ipt_ttl.h diff --git a/include/uapi/linux/netfilter_ipv4/ipt_ULOG.h b/include/uapi/linux/netfilter_ipv4/ipt_ULOG.h deleted file mode 100644 index 417aad280bcc..000000000000 --- a/include/uapi/linux/netfilter_ipv4/ipt_ULOG.h +++ /dev/null @@ -1,49 +0,0 @@ -/* Header file for IP tables userspace logging, Version 1.8 - * - * (C) 2000-2002 by Harald Welte - * - * Distributed under the terms of GNU GPL */ - -#ifndef _IPT_ULOG_H -#define _IPT_ULOG_H - -#ifndef NETLINK_NFLOG -#define NETLINK_NFLOG 5 -#endif - -#define ULOG_DEFAULT_NLGROUP 1 -#define ULOG_DEFAULT_QTHRESHOLD 1 - -#define ULOG_MAC_LEN 80 -#define ULOG_PREFIX_LEN 32 - -#define ULOG_MAX_QLEN 50 -/* Why 50? Well... there is a limit imposed by the slab cache 131000 - * bytes. So the multipart netlink-message has to be < 131000 bytes. - * Assuming a standard ethernet-mtu of 1500, we could define this up - * to 80... but even 50 seems to be big enough. */ - -/* private data structure for each rule with a ULOG target */ -struct ipt_ulog_info { - unsigned int nl_group; - size_t copy_range; - size_t qthreshold; - char prefix[ULOG_PREFIX_LEN]; -}; - -/* Format of the ULOG packets passed through netlink */ -typedef struct ulog_packet_msg { - unsigned long mark; - long timestamp_sec; - long timestamp_usec; - unsigned int hook; - char indev_name[IFNAMSIZ]; - char outdev_name[IFNAMSIZ]; - size_t data_len; - char prefix[ULOG_PREFIX_LEN]; - unsigned char mac_len; - unsigned char mac[ULOG_MAC_LEN]; - unsigned char payload[0]; -} ulog_packet_msg_t; - -#endif /*_IPT_ULOG_H*/ diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig index 629dc77874a9..3a76ac7b7141 100644 --- a/net/bridge/netfilter/Kconfig +++ b/net/bridge/netfilter/Kconfig @@ -202,22 +202,6 @@ config BRIDGE_EBT_LOG To compile it as a module, choose M here. If unsure, say N. -config BRIDGE_EBT_ULOG - tristate "ebt: ulog support (OBSOLETE)" - help - This option enables the old bridge-specific "ebt_ulog" implementation - which has been obsoleted by the new "nfnetlink_log" code (see - CONFIG_NETFILTER_NETLINK_LOG). - - This option adds the ulog watcher, that you can use in any rule - in any ebtables table. The packet is passed to a userspace - logging daemon using netlink multicast sockets. This differs - from the log watcher in the sense that the complete packet is - sent to userspace instead of a descriptive text and that - netlink multicast sockets are used instead of the syslog. - - To compile it as a module, choose M here. If unsure, say N. - config BRIDGE_EBT_NFLOG tristate "ebt: nflog support" help diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c deleted file mode 100644 index 7c470c371e14..000000000000 --- a/net/bridge/netfilter/ebt_ulog.c +++ /dev/null @@ -1,393 +0,0 @@ -/* - * netfilter module for userspace bridged Ethernet frames logging daemons - * - * Authors: - * Bart De Schuymer - * Harald Welte - * - * November, 2004 - * - * Based on ipt_ULOG.c, which is - * (C) 2000-2002 by Harald Welte - * - * This module accepts two parameters: - * - * nlbufsiz: - * The parameter specifies how big the buffer for each netlink multicast - * group is. e.g. If you say nlbufsiz=8192, up to eight kb of packets will - * get accumulated in the kernel until they are sent to userspace. It is - * NOT possible to allocate more than 128kB, and it is strongly discouraged, - * because atomically allocating 128kB inside the network rx softirq is not - * reliable. Please also keep in mind that this buffer size is allocated for - * each nlgroup you are using, so the total kernel memory usage increases - * by that factor. - * - * flushtimeout: - * Specify, after how many hundredths of a second the queue should be - * flushed even if it is not full yet. - * - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../br_private.h" - -static unsigned int nlbufsiz = NLMSG_GOODSIZE; -module_param(nlbufsiz, uint, 0600); -MODULE_PARM_DESC(nlbufsiz, "netlink buffer size (number of bytes) " - "(defaults to 4096)"); - -static unsigned int flushtimeout = 10; -module_param(flushtimeout, uint, 0600); -MODULE_PARM_DESC(flushtimeout, "buffer flush timeout (hundredths ofa second) " - "(defaults to 10)"); - -typedef struct { - unsigned int qlen; /* number of nlmsgs' in the skb */ - struct nlmsghdr *lastnlh; /* netlink header of last msg in skb */ - struct sk_buff *skb; /* the pre-allocated skb */ - struct timer_list timer; /* the timer function */ - spinlock_t lock; /* the per-queue lock */ -} ebt_ulog_buff_t; - -static int ebt_ulog_net_id __read_mostly; -struct ebt_ulog_net { - unsigned int nlgroup[EBT_ULOG_MAXNLGROUPS]; - ebt_ulog_buff_t ulog_buffers[EBT_ULOG_MAXNLGROUPS]; - struct sock *ebtulognl; -}; - -static struct ebt_ulog_net *ebt_ulog_pernet(struct net *net) -{ - return net_generic(net, ebt_ulog_net_id); -} - -/* send one ulog_buff_t to userspace */ -static void ulog_send(struct ebt_ulog_net *ebt, unsigned int nlgroup) -{ - ebt_ulog_buff_t *ub = &ebt->ulog_buffers[nlgroup]; - - del_timer(&ub->timer); - - if (!ub->skb) - return; - - /* last nlmsg needs NLMSG_DONE */ - if (ub->qlen > 1) - ub->lastnlh->nlmsg_type = NLMSG_DONE; - - NETLINK_CB(ub->skb).dst_group = nlgroup + 1; - netlink_broadcast(ebt->ebtulognl, ub->skb, 0, nlgroup + 1, GFP_ATOMIC); - - ub->qlen = 0; - ub->skb = NULL; -} - -/* timer function to flush queue in flushtimeout time */ -static void ulog_timer(unsigned long data) -{ - struct ebt_ulog_net *ebt = container_of((void *)data, - struct ebt_ulog_net, - nlgroup[*(unsigned int *)data]); - - ebt_ulog_buff_t *ub = &ebt->ulog_buffers[*(unsigned int *)data]; - spin_lock_bh(&ub->lock); - if (ub->skb) - ulog_send(ebt, *(unsigned int *)data); - spin_unlock_bh(&ub->lock); -} - -static struct sk_buff *ulog_alloc_skb(unsigned int size) -{ - struct sk_buff *skb; - unsigned int n; - - n = max(size, nlbufsiz); - skb = alloc_skb(n, GFP_ATOMIC | __GFP_NOWARN); - if (!skb) { - if (n > size) { - /* try to allocate only as much as we need for - * current packet */ - skb = alloc_skb(size, GFP_ATOMIC); - if (!skb) - pr_debug("cannot even allocate buffer of size %ub\n", - size); - } - } - - return skb; -} - -static void ebt_ulog_packet(struct net *net, unsigned int hooknr, - const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct ebt_ulog_info *uloginfo, - const char *prefix) -{ - ebt_ulog_packet_msg_t *pm; - size_t size, copy_len; - struct nlmsghdr *nlh; - struct ebt_ulog_net *ebt = ebt_ulog_pernet(net); - unsigned int group = uloginfo->nlgroup; - ebt_ulog_buff_t *ub = &ebt->ulog_buffers[group]; - spinlock_t *lock = &ub->lock; - ktime_t kt; - - if ((uloginfo->cprange == 0) || - (uloginfo->cprange > skb->len + ETH_HLEN)) - copy_len = skb->len + ETH_HLEN; - else - copy_len = uloginfo->cprange; - - size = nlmsg_total_size(sizeof(*pm) + copy_len); - if (size > nlbufsiz) { - pr_debug("Size %Zd needed, but nlbufsiz=%d\n", size, nlbufsiz); - return; - } - - spin_lock_bh(lock); - - if (!ub->skb) { - if (!(ub->skb = ulog_alloc_skb(size))) - goto unlock; - } else if (size > skb_tailroom(ub->skb)) { - ulog_send(ebt, group); - - if (!(ub->skb = ulog_alloc_skb(size))) - goto unlock; - } - - nlh = nlmsg_put(ub->skb, 0, ub->qlen, 0, - size - NLMSG_ALIGN(sizeof(*nlh)), 0); - if (!nlh) { - kfree_skb(ub->skb); - ub->skb = NULL; - goto unlock; - } - ub->qlen++; - - pm = nlmsg_data(nlh); - memset(pm, 0, sizeof(*pm)); - - /* Fill in the ulog data */ - pm->version = EBT_ULOG_VERSION; - kt = ktime_get_real(); - pm->stamp = ktime_to_timeval(kt); - if (ub->qlen == 1) - ub->skb->tstamp = kt; - pm->data_len = copy_len; - pm->mark = skb->mark; - pm->hook = hooknr; - if (uloginfo->prefix != NULL) - strcpy(pm->prefix, uloginfo->prefix); - - if (in) { - strcpy(pm->physindev, in->name); - /* If in isn't a bridge, then physindev==indev */ - if (br_port_exists(in)) - /* rcu_read_lock()ed by nf_hook_slow */ - strcpy(pm->indev, br_port_get_rcu(in)->br->dev->name); - else - strcpy(pm->indev, in->name); - } - - if (out) { - /* If out exists, then out is a bridge port */ - strcpy(pm->physoutdev, out->name); - /* rcu_read_lock()ed by nf_hook_slow */ - strcpy(pm->outdev, br_port_get_rcu(out)->br->dev->name); - } - - if (skb_copy_bits(skb, -ETH_HLEN, pm->data, copy_len) < 0) - BUG(); - - if (ub->qlen > 1) - ub->lastnlh->nlmsg_flags |= NLM_F_MULTI; - - ub->lastnlh = nlh; - - if (ub->qlen >= uloginfo->qthreshold) - ulog_send(ebt, group); - else if (!timer_pending(&ub->timer)) { - ub->timer.expires = jiffies + flushtimeout * HZ / 100; - add_timer(&ub->timer); - } - -unlock: - spin_unlock_bh(lock); -} - -/* this function is registered with the netfilter core */ -static void ebt_log_packet(struct net *net, u_int8_t pf, unsigned int hooknum, - const struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, const struct nf_loginfo *li, - const char *prefix) -{ - struct ebt_ulog_info loginfo; - - if (!li || li->type != NF_LOG_TYPE_ULOG) { - loginfo.nlgroup = EBT_ULOG_DEFAULT_NLGROUP; - loginfo.cprange = 0; - loginfo.qthreshold = EBT_ULOG_DEFAULT_QTHRESHOLD; - loginfo.prefix[0] = '\0'; - } else { - loginfo.nlgroup = li->u.ulog.group; - loginfo.cprange = li->u.ulog.copy_len; - loginfo.qthreshold = li->u.ulog.qthreshold; - strlcpy(loginfo.prefix, prefix, sizeof(loginfo.prefix)); - } - - ebt_ulog_packet(net, hooknum, skb, in, out, &loginfo, prefix); -} - -static unsigned int -ebt_ulog_tg(struct sk_buff *skb, const struct xt_action_param *par) -{ - struct net *net = dev_net(par->in ? par->in : par->out); - - ebt_ulog_packet(net, par->hooknum, skb, par->in, par->out, - par->targinfo, NULL); - return EBT_CONTINUE; -} - -static int ebt_ulog_tg_check(const struct xt_tgchk_param *par) -{ - struct ebt_ulog_info *uloginfo = par->targinfo; - - if (!par->net->xt.ebt_ulog_warn_deprecated) { - pr_info("ebt_ulog is deprecated and it will be removed soon, " - "use ebt_nflog instead\n"); - par->net->xt.ebt_ulog_warn_deprecated = true; - } - - if (uloginfo->nlgroup > 31) - return -EINVAL; - - uloginfo->prefix[EBT_ULOG_PREFIX_LEN - 1] = '\0'; - - if (uloginfo->qthreshold > EBT_ULOG_MAX_QLEN) - uloginfo->qthreshold = EBT_ULOG_MAX_QLEN; - - return 0; -} - -static struct xt_target ebt_ulog_tg_reg __read_mostly = { - .name = "ulog", - .revision = 0, - .family = NFPROTO_BRIDGE, - .target = ebt_ulog_tg, - .checkentry = ebt_ulog_tg_check, - .targetsize = sizeof(struct ebt_ulog_info), - .me = THIS_MODULE, -}; - -static struct nf_logger ebt_ulog_logger __read_mostly = { - .name = "ebt_ulog", - .logfn = &ebt_log_packet, - .me = THIS_MODULE, -}; - -static int __net_init ebt_ulog_net_init(struct net *net) -{ - int i; - struct ebt_ulog_net *ebt = ebt_ulog_pernet(net); - - struct netlink_kernel_cfg cfg = { - .groups = EBT_ULOG_MAXNLGROUPS, - }; - - /* initialize ulog_buffers */ - for (i = 0; i < EBT_ULOG_MAXNLGROUPS; i++) { - ebt->nlgroup[i] = i; - setup_timer(&ebt->ulog_buffers[i].timer, ulog_timer, - (unsigned long)&ebt->nlgroup[i]); - spin_lock_init(&ebt->ulog_buffers[i].lock); - } - - ebt->ebtulognl = netlink_kernel_create(net, NETLINK_NFLOG, &cfg); - if (!ebt->ebtulognl) - return -ENOMEM; - - nf_log_set(net, NFPROTO_BRIDGE, &ebt_ulog_logger); - return 0; -} - -static void __net_exit ebt_ulog_net_fini(struct net *net) -{ - int i; - struct ebt_ulog_net *ebt = ebt_ulog_pernet(net); - - nf_log_unset(net, &ebt_ulog_logger); - for (i = 0; i < EBT_ULOG_MAXNLGROUPS; i++) { - ebt_ulog_buff_t *ub = &ebt->ulog_buffers[i]; - del_timer(&ub->timer); - - if (ub->skb) { - kfree_skb(ub->skb); - ub->skb = NULL; - } - } - netlink_kernel_release(ebt->ebtulognl); -} - -static struct pernet_operations ebt_ulog_net_ops = { - .init = ebt_ulog_net_init, - .exit = ebt_ulog_net_fini, - .id = &ebt_ulog_net_id, - .size = sizeof(struct ebt_ulog_net), -}; - -static int __init ebt_ulog_init(void) -{ - int ret; - - if (nlbufsiz >= 128*1024) { - pr_warn("Netlink buffer has to be <= 128kB," - "please try a smaller nlbufsiz parameter.\n"); - return -EINVAL; - } - - ret = register_pernet_subsys(&ebt_ulog_net_ops); - if (ret) - goto out_pernet; - - ret = xt_register_target(&ebt_ulog_tg_reg); - if (ret) - goto out_target; - - nf_log_register(NFPROTO_BRIDGE, &ebt_ulog_logger); - - return 0; - -out_target: - unregister_pernet_subsys(&ebt_ulog_net_ops); -out_pernet: - return ret; -} - -static void __exit ebt_ulog_fini(void) -{ - nf_log_unregister(&ebt_ulog_logger); - xt_unregister_target(&ebt_ulog_tg_reg); - unregister_pernet_subsys(&ebt_ulog_net_ops); -} - -module_init(ebt_ulog_init); -module_exit(ebt_ulog_fini); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Bart De Schuymer "); -MODULE_DESCRIPTION("Ebtables: Packet logging to netlink using ULOG"); diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index a26ce035e3fa..730faacbc5f8 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig @@ -159,25 +159,6 @@ config IP_NF_TARGET_SYNPROXY To compile it as a module, choose M here. If unsure, say N. -config IP_NF_TARGET_ULOG - tristate "ULOG target support (obsolete)" - default m if NETFILTER_ADVANCED=n - ---help--- - - This option enables the old IPv4-only "ipt_ULOG" implementation - which has been obsoleted by the new "nfnetlink_log" code (see - CONFIG_NETFILTER_NETLINK_LOG). - - This option adds a `ULOG' target, which allows you to create rules in - any iptables table. The packet is passed to a userspace logging - daemon using netlink multicast sockets; unlike the LOG target - which can only be viewed through syslog. - - The appropriate userspace logging daemon (ulogd) may be obtained from - - - To compile it as a module, choose M here. If unsure, say N. - # NAT + specific targets: nf_conntrack config NF_NAT_IPV4 tristate "IPv4 NAT" diff --git a/net/ipv4/netfilter/ipt_ULOG.c b/net/ipv4/netfilter/ipt_ULOG.c deleted file mode 100644 index 9cb993cd224b..000000000000 --- a/net/ipv4/netfilter/ipt_ULOG.c +++ /dev/null @@ -1,498 +0,0 @@ -/* - * netfilter module for userspace packet logging daemons - * - * (C) 2000-2004 by Harald Welte - * (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team - * (C) 2005-2007 Patrick McHardy - * - * 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 module accepts two parameters: - * - * nlbufsiz: - * The parameter specifies how big the buffer for each netlink multicast - * group is. e.g. If you say nlbufsiz=8192, up to eight kb of packets will - * get accumulated in the kernel until they are sent to userspace. It is - * NOT possible to allocate more than 128kB, and it is strongly discouraged, - * because atomically allocating 128kB inside the network rx softirq is not - * reliable. Please also keep in mind that this buffer size is allocated for - * each nlgroup you are using, so the total kernel memory usage increases - * by that factor. - * - * Actually you should use nlbufsiz a bit smaller than PAGE_SIZE, since - * nlbufsiz is used with alloc_skb, which adds another - * sizeof(struct skb_shared_info). Use NLMSG_GOODSIZE instead. - * - * flushtimeout: - * Specify, after how many hundredths of a second the queue should be - * flushed even if it is not full yet. - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Harald Welte "); -MODULE_DESCRIPTION("Xtables: packet logging to netlink using ULOG"); -MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_NFLOG); - -#define ULOG_NL_EVENT 111 /* Harald's favorite number */ -#define ULOG_MAXNLGROUPS 32 /* numer of nlgroups */ - -static unsigned int nlbufsiz = NLMSG_GOODSIZE; -module_param(nlbufsiz, uint, 0400); -MODULE_PARM_DESC(nlbufsiz, "netlink buffer size"); - -static unsigned int flushtimeout = 10; -module_param(flushtimeout, uint, 0600); -MODULE_PARM_DESC(flushtimeout, "buffer flush timeout (hundredths of a second)"); - -static bool nflog = true; -module_param(nflog, bool, 0400); -MODULE_PARM_DESC(nflog, "register as internal netfilter logging module"); - -/* global data structures */ - -typedef struct { - unsigned int qlen; /* number of nlmsgs' in the skb */ - struct nlmsghdr *lastnlh; /* netlink header of last msg in skb */ - struct sk_buff *skb; /* the pre-allocated skb */ - struct timer_list timer; /* the timer function */ -} ulog_buff_t; - -static int ulog_net_id __read_mostly; -struct ulog_net { - unsigned int nlgroup[ULOG_MAXNLGROUPS]; - ulog_buff_t ulog_buffers[ULOG_MAXNLGROUPS]; - struct sock *nflognl; - spinlock_t lock; -}; - -static struct ulog_net *ulog_pernet(struct net *net) -{ - return net_generic(net, ulog_net_id); -} - -/* send one ulog_buff_t to userspace */ -static void ulog_send(struct ulog_net *ulog, unsigned int nlgroupnum) -{ - ulog_buff_t *ub = &ulog->ulog_buffers[nlgroupnum]; - - pr_debug("ulog_send: timer is deleting\n"); - del_timer(&ub->timer); - - if (!ub->skb) { - pr_debug("ulog_send: nothing to send\n"); - return; - } - - /* last nlmsg needs NLMSG_DONE */ - if (ub->qlen > 1) - ub->lastnlh->nlmsg_type = NLMSG_DONE; - - NETLINK_CB(ub->skb).dst_group = nlgroupnum + 1; - pr_debug("throwing %d packets to netlink group %u\n", - ub->qlen, nlgroupnum + 1); - netlink_broadcast(ulog->nflognl, ub->skb, 0, nlgroupnum + 1, - GFP_ATOMIC); - - ub->qlen = 0; - ub->skb = NULL; - ub->lastnlh = NULL; -} - - -/* timer function to flush queue in flushtimeout time */ -static void ulog_timer(unsigned long data) -{ - unsigned int groupnum = *((unsigned int *)data); - struct ulog_net *ulog = container_of((void *)data, - struct ulog_net, - nlgroup[groupnum]); - pr_debug("timer function called, calling ulog_send\n"); - - /* lock to protect against somebody modifying our structure - * from ipt_ulog_target at the same time */ - spin_lock_bh(&ulog->lock); - ulog_send(ulog, groupnum); - spin_unlock_bh(&ulog->lock); -} - -static struct sk_buff *ulog_alloc_skb(unsigned int size) -{ - struct sk_buff *skb; - unsigned int n; - - /* alloc skb which should be big enough for a whole - * multipart message. WARNING: has to be <= 131000 - * due to slab allocator restrictions */ - - n = max(size, nlbufsiz); - skb = alloc_skb(n, GFP_ATOMIC | __GFP_NOWARN); - if (!skb) { - if (n > size) { - /* try to allocate only as much as we need for - * current packet */ - - skb = alloc_skb(size, GFP_ATOMIC); - if (!skb) - pr_debug("cannot even allocate %ub\n", size); - } - } - - return skb; -} - -static void ipt_ulog_packet(struct net *net, - unsigned int hooknum, - const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct ipt_ulog_info *loginfo, - const char *prefix) -{ - ulog_buff_t *ub; - ulog_packet_msg_t *pm; - size_t size, copy_len; - struct nlmsghdr *nlh; - struct timeval tv; - struct ulog_net *ulog = ulog_pernet(net); - - /* ffs == find first bit set, necessary because userspace - * is already shifting groupnumber, but we need unshifted. - * ffs() returns [1..32], we need [0..31] */ - unsigned int groupnum = ffs(loginfo->nl_group) - 1; - - /* calculate the size of the skb needed */ - if (loginfo->copy_range == 0 || loginfo->copy_range > skb->len) - copy_len = skb->len; - else - copy_len = loginfo->copy_range; - - size = nlmsg_total_size(sizeof(*pm) + copy_len); - - ub = &ulog->ulog_buffers[groupnum]; - - spin_lock_bh(&ulog->lock); - - if (!ub->skb) { - if (!(ub->skb = ulog_alloc_skb(size))) - goto alloc_failure; - } else if (ub->qlen >= loginfo->qthreshold || - size > skb_tailroom(ub->skb)) { - /* either the queue len is too high or we don't have - * enough room in nlskb left. send it to userspace. */ - - ulog_send(ulog, groupnum); - - if (!(ub->skb = ulog_alloc_skb(size))) - goto alloc_failure; - } - - pr_debug("qlen %d, qthreshold %Zu\n", ub->qlen, loginfo->qthreshold); - - nlh = nlmsg_put(ub->skb, 0, ub->qlen, ULOG_NL_EVENT, - sizeof(*pm)+copy_len, 0); - if (!nlh) { - pr_debug("error during nlmsg_put\n"); - goto out_unlock; - } - ub->qlen++; - - pm = nlmsg_data(nlh); - memset(pm, 0, sizeof(*pm)); - - /* We might not have a timestamp, get one */ - if (skb->tstamp.tv64 == 0) - __net_timestamp((struct sk_buff *)skb); - - /* copy hook, prefix, timestamp, payload, etc. */ - pm->data_len = copy_len; - tv = ktime_to_timeval(skb->tstamp); - put_unaligned(tv.tv_sec, &pm->timestamp_sec); - put_unaligned(tv.tv_usec, &pm->timestamp_usec); - put_unaligned(skb->mark, &pm->mark); - pm->hook = hooknum; - if (prefix != NULL) { - strncpy(pm->prefix, prefix, sizeof(pm->prefix) - 1); - pm->prefix[sizeof(pm->prefix) - 1] = '\0'; - } - else if (loginfo->prefix[0] != '\0') - strncpy(pm->prefix, loginfo->prefix, sizeof(pm->prefix)); - - if (in && in->hard_header_len > 0 && - skb->mac_header != skb->network_header && - in->hard_header_len <= ULOG_MAC_LEN) { - memcpy(pm->mac, skb_mac_header(skb), in->hard_header_len); - pm->mac_len = in->hard_header_len; - } else - pm->mac_len = 0; - - if (in) - strncpy(pm->indev_name, in->name, sizeof(pm->indev_name)); - - if (out) - strncpy(pm->outdev_name, out->name, sizeof(pm->outdev_name)); - - /* copy_len <= skb->len, so can't fail. */ - if (skb_copy_bits(skb, 0, pm->payload, copy_len) < 0) - BUG(); - - /* check if we are building multi-part messages */ - if (ub->qlen > 1) - ub->lastnlh->nlmsg_flags |= NLM_F_MULTI; - - ub->lastnlh = nlh; - - /* if timer isn't already running, start it */ - if (!timer_pending(&ub->timer)) { - ub->timer.expires = jiffies + flushtimeout * HZ / 100; - add_timer(&ub->timer); - } - - /* if threshold is reached, send message to userspace */ - if (ub->qlen >= loginfo->qthreshold) { - if (loginfo->qthreshold > 1) - nlh->nlmsg_type = NLMSG_DONE; - ulog_send(ulog, groupnum); - } -out_unlock: - spin_unlock_bh(&ulog->lock); - - return; - -alloc_failure: - pr_debug("Error building netlink message\n"); - spin_unlock_bh(&ulog->lock); -} - -static unsigned int -ulog_tg(struct sk_buff *skb, const struct xt_action_param *par) -{ - struct net *net = dev_net(par->in ? par->in : par->out); - - ipt_ulog_packet(net, par->hooknum, skb, par->in, par->out, - par->targinfo, NULL); - return XT_CONTINUE; -} - -static void ipt_logfn(struct net *net, - u_int8_t pf, - unsigned int hooknum, - const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct nf_loginfo *li, - const char *prefix) -{ - struct ipt_ulog_info loginfo; - - if (!li || li->type != NF_LOG_TYPE_ULOG) { - loginfo.nl_group = ULOG_DEFAULT_NLGROUP; - loginfo.copy_range = 0; - loginfo.qthreshold = ULOG_DEFAULT_QTHRESHOLD; - loginfo.prefix[0] = '\0'; - } else { - loginfo.nl_group = li->u.ulog.group; - loginfo.copy_range = li->u.ulog.copy_len; - loginfo.qthreshold = li->u.ulog.qthreshold; - strlcpy(loginfo.prefix, prefix, sizeof(loginfo.prefix)); - } - - ipt_ulog_packet(net, hooknum, skb, in, out, &loginfo, prefix); -} - -static int ulog_tg_check(const struct xt_tgchk_param *par) -{ - const struct ipt_ulog_info *loginfo = par->targinfo; - - if (!par->net->xt.ulog_warn_deprecated) { - pr_info("ULOG is deprecated and it will be removed soon, " - "use NFLOG instead\n"); - par->net->xt.ulog_warn_deprecated = true; - } - - if (loginfo->prefix[sizeof(loginfo->prefix) - 1] != '\0') { - pr_debug("prefix not null-terminated\n"); - return -EINVAL; - } - if (loginfo->qthreshold > ULOG_MAX_QLEN) { - pr_debug("queue threshold %Zu > MAX_QLEN\n", - loginfo->qthreshold); - return -EINVAL; - } - return 0; -} - -#ifdef CONFIG_COMPAT -struct compat_ipt_ulog_info { - compat_uint_t nl_group; - compat_size_t copy_range; - compat_size_t qthreshold; - char prefix[ULOG_PREFIX_LEN]; -}; - -static void ulog_tg_compat_from_user(void *dst, const void *src) -{ - const struct compat_ipt_ulog_info *cl = src; - struct ipt_ulog_info l = { - .nl_group = cl->nl_group, - .copy_range = cl->copy_range, - .qthreshold = cl->qthreshold, - }; - - memcpy(l.prefix, cl->prefix, sizeof(l.prefix)); - memcpy(dst, &l, sizeof(l)); -} - -static int ulog_tg_compat_to_user(void __user *dst, const void *src) -{ - const struct ipt_ulog_info *l = src; - struct compat_ipt_ulog_info cl = { - .nl_group = l->nl_group, - .copy_range = l->copy_range, - .qthreshold = l->qthreshold, - }; - - memcpy(cl.prefix, l->prefix, sizeof(cl.prefix)); - return copy_to_user(dst, &cl, sizeof(cl)) ? -EFAULT : 0; -} -#endif /* CONFIG_COMPAT */ - -static struct xt_target ulog_tg_reg __read_mostly = { - .name = "ULOG", - .family = NFPROTO_IPV4, - .target = ulog_tg, - .targetsize = sizeof(struct ipt_ulog_info), - .checkentry = ulog_tg_check, -#ifdef CONFIG_COMPAT - .compatsize = sizeof(struct compat_ipt_ulog_info), - .compat_from_user = ulog_tg_compat_from_user, - .compat_to_user = ulog_tg_compat_to_user, -#endif - .me = THIS_MODULE, -}; - -static struct nf_logger ipt_ulog_logger __read_mostly = { - .name = "ipt_ULOG", - .logfn = ipt_logfn, - .me = THIS_MODULE, -}; - -static int __net_init ulog_tg_net_init(struct net *net) -{ - int i; - struct ulog_net *ulog = ulog_pernet(net); - struct netlink_kernel_cfg cfg = { - .groups = ULOG_MAXNLGROUPS, - }; - - spin_lock_init(&ulog->lock); - /* initialize ulog_buffers */ - for (i = 0; i < ULOG_MAXNLGROUPS; i++) { - ulog->nlgroup[i] = i; - setup_timer(&ulog->ulog_buffers[i].timer, ulog_timer, - (unsigned long)&ulog->nlgroup[i]); - } - - ulog->nflognl = netlink_kernel_create(net, NETLINK_NFLOG, &cfg); - if (!ulog->nflognl) - return -ENOMEM; - - if (nflog) - nf_log_set(net, NFPROTO_IPV4, &ipt_ulog_logger); - - return 0; -} - -static void __net_exit ulog_tg_net_exit(struct net *net) -{ - ulog_buff_t *ub; - int i; - struct ulog_net *ulog = ulog_pernet(net); - - if (nflog) - nf_log_unset(net, &ipt_ulog_logger); - - netlink_kernel_release(ulog->nflognl); - - /* remove pending timers and free allocated skb's */ - for (i = 0; i < ULOG_MAXNLGROUPS; i++) { - ub = &ulog->ulog_buffers[i]; - pr_debug("timer is deleting\n"); - del_timer(&ub->timer); - - if (ub->skb) { - kfree_skb(ub->skb); - ub->skb = NULL; - } - } -} - -static struct pernet_operations ulog_tg_net_ops = { - .init = ulog_tg_net_init, - .exit = ulog_tg_net_exit, - .id = &ulog_net_id, - .size = sizeof(struct ulog_net), -}; - -static int __init ulog_tg_init(void) -{ - int ret; - pr_debug("init module\n"); - - if (nlbufsiz > 128*1024) { - pr_warn("Netlink buffer has to be <= 128kB\n"); - return -EINVAL; - } - - ret = register_pernet_subsys(&ulog_tg_net_ops); - if (ret) - goto out_pernet; - - ret = xt_register_target(&ulog_tg_reg); - if (ret < 0) - goto out_target; - - if (nflog) - nf_log_register(NFPROTO_IPV4, &ipt_ulog_logger); - - return 0; - -out_target: - unregister_pernet_subsys(&ulog_tg_net_ops); -out_pernet: - return ret; -} - -static void __exit ulog_tg_exit(void) -{ - pr_debug("cleanup_module\n"); - if (nflog) - nf_log_unregister(&ipt_ulog_logger); - xt_unregister_target(&ulog_tg_reg); - unregister_pernet_subsys(&ulog_tg_net_ops); -} - -module_init(ulog_tg_init); -module_exit(ulog_tg_exit); -- cgit v1.2.3 From 5962815a6a56566318a60dc53ff8789b7e6ec71f Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 18 Jun 2014 19:24:30 +0200 Subject: netfilter: nf_log: use an array of loggers instead of list Now that legacy ulog targets are not available anymore in the tree, we can have up to two possible loggers: 1) The plain text logging via kernel logging ring. 2) The nfnetlink_log infrastructure which delivers log messages to userspace. This patch replaces the list of loggers by an array of two pointers per family for each possible logger and it also introduces a new field to the nf_logger structure which indicates the position in the logger array (based on the logger type). This prepares a follow up patch that consolidates the nf_log_packet() interface by allowing to specify the logger as parameter. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_log.h | 15 ++++++++----- net/bridge/netfilter/ebt_log.c | 1 + net/netfilter/nf_log.c | 50 ++++++++++++++++++++---------------------- net/netfilter/nfnetlink_log.c | 1 + net/netfilter/xt_LOG.c | 2 ++ 5 files changed, 37 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_log.h b/include/net/netfilter/nf_log.h index 99eac12d040b..06b4c6b07f52 100644 --- a/include/net/netfilter/nf_log.h +++ b/include/net/netfilter/nf_log.h @@ -12,8 +12,11 @@ #define NF_LOG_UID 0x08 /* Log UID owning local socket */ #define NF_LOG_MASK 0x0f -#define NF_LOG_TYPE_LOG 0x01 -#define NF_LOG_TYPE_ULOG 0x02 +enum nf_log_type { + NF_LOG_TYPE_LOG = 0, + NF_LOG_TYPE_ULOG, + NF_LOG_TYPE_MAX +}; struct nf_loginfo { u_int8_t type; @@ -40,10 +43,10 @@ typedef void nf_logfn(struct net *net, const char *prefix); struct nf_logger { - struct module *me; - nf_logfn *logfn; - char *name; - struct list_head list[NFPROTO_NUMPROTO]; + char *name; + enum nf_log_type type; + nf_logfn *logfn; + struct module *me; }; /* Function to register/unregister log function. */ diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c index 5322a36867a3..0577477aacd8 100644 --- a/net/bridge/netfilter/ebt_log.c +++ b/net/bridge/netfilter/ebt_log.c @@ -207,6 +207,7 @@ static struct xt_target ebt_log_tg_reg __read_mostly = { static struct nf_logger ebt_log_logger __read_mostly = { .name = "ebt_log", + .type = NF_LOG_TYPE_LOG, .logfn = &ebt_log_packet, .me = THIS_MODULE, }; diff --git a/net/netfilter/nf_log.c b/net/netfilter/nf_log.c index 85296d4eac0e..7a29a3a46172 100644 --- a/net/netfilter/nf_log.c +++ b/net/netfilter/nf_log.c @@ -16,16 +16,22 @@ #define NF_LOG_PREFIXLEN 128 #define NFLOGGER_NAME_LEN 64 -static struct list_head nf_loggers_l[NFPROTO_NUMPROTO] __read_mostly; +static struct nf_logger __rcu *loggers[NFPROTO_NUMPROTO][NF_LOG_TYPE_MAX] __read_mostly; static DEFINE_MUTEX(nf_log_mutex); static struct nf_logger *__find_logger(int pf, const char *str_logger) { - struct nf_logger *t; + struct nf_logger *log; + int i; + + for (i = 0; i < NF_LOG_TYPE_MAX; i++) { + if (loggers[pf][i] == NULL) + continue; - list_for_each_entry(t, &nf_loggers_l[pf], list[pf]) { - if (!strnicmp(str_logger, t->name, strlen(t->name))) - return t; + log = rcu_dereference_protected(loggers[pf][i], + lockdep_is_held(&nf_log_mutex)); + if (!strnicmp(str_logger, log->name, strlen(log->name))) + return log; } return NULL; @@ -73,17 +79,14 @@ int nf_log_register(u_int8_t pf, struct nf_logger *logger) if (pf >= ARRAY_SIZE(init_net.nf.nf_loggers)) return -EINVAL; - for (i = 0; i < ARRAY_SIZE(logger->list); i++) - INIT_LIST_HEAD(&logger->list[i]); - mutex_lock(&nf_log_mutex); if (pf == NFPROTO_UNSPEC) { for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) - list_add_tail(&(logger->list[i]), &(nf_loggers_l[i])); + rcu_assign_pointer(loggers[i][logger->type], logger); } else { /* register at end of list to honor first register win */ - list_add_tail(&logger->list[pf], &nf_loggers_l[pf]); + rcu_assign_pointer(loggers[pf][logger->type], logger); } mutex_unlock(&nf_log_mutex); @@ -98,7 +101,7 @@ void nf_log_unregister(struct nf_logger *logger) mutex_lock(&nf_log_mutex); for (i = 0; i < NFPROTO_NUMPROTO; i++) - list_del(&logger->list[i]); + RCU_INIT_POINTER(loggers[i][logger->type], NULL); mutex_unlock(&nf_log_mutex); } EXPORT_SYMBOL(nf_log_unregister); @@ -188,8 +191,7 @@ static int seq_show(struct seq_file *s, void *v) { loff_t *pos = v; const struct nf_logger *logger; - struct nf_logger *t; - int ret; + int i, ret; struct net *net = seq_file_net(s); logger = rcu_dereference_protected(net->nf.nf_loggers[*pos], @@ -203,11 +205,16 @@ static int seq_show(struct seq_file *s, void *v) if (ret < 0) return ret; - list_for_each_entry(t, &nf_loggers_l[*pos], list[*pos]) { - ret = seq_printf(s, "%s", t->name); + for (i = 0; i < NF_LOG_TYPE_MAX; i++) { + if (loggers[*pos][i] == NULL) + continue; + + logger = rcu_dereference_protected(loggers[*pos][i], + lockdep_is_held(&nf_log_mutex)); + ret = seq_printf(s, "%s", logger->name); if (ret < 0) return ret; - if (&t->list[*pos] != nf_loggers_l[*pos].prev) { + if (i == 0 && loggers[*pos][i + 1] != NULL) { ret = seq_printf(s, ","); if (ret < 0) return ret; @@ -389,14 +396,5 @@ static struct pernet_operations nf_log_net_ops = { int __init netfilter_log_init(void) { - int i, ret; - - ret = register_pernet_subsys(&nf_log_net_ops); - if (ret < 0) - return ret; - - for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) - INIT_LIST_HEAD(&(nf_loggers_l[i])); - - return 0; + return register_pernet_subsys(&nf_log_net_ops); } diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index d292c8d286eb..160bb8ea9923 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -773,6 +773,7 @@ nfulnl_recv_unsupp(struct sock *ctnl, struct sk_buff *skb, static struct nf_logger nfulnl_logger __read_mostly = { .name = "nfnetlink_log", + .type = NF_LOG_TYPE_ULOG, .logfn = &nfulnl_log_packet, .me = THIS_MODULE, }; diff --git a/net/netfilter/xt_LOG.c b/net/netfilter/xt_LOG.c index 5ab24843370a..e668d9797cf3 100644 --- a/net/netfilter/xt_LOG.c +++ b/net/netfilter/xt_LOG.c @@ -896,6 +896,7 @@ static struct xt_target log_tg_regs[] __read_mostly = { static struct nf_logger ipt_log_logger __read_mostly = { .name = "ipt_LOG", + .type = NF_LOG_TYPE_LOG, .logfn = &ipt_log_packet, .me = THIS_MODULE, }; @@ -903,6 +904,7 @@ static struct nf_logger ipt_log_logger __read_mostly = { #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) static struct nf_logger ip6t_log_logger __read_mostly = { .name = "ip6t_LOG", + .type = NF_LOG_TYPE_LOG, .logfn = &ip6t_log_packet, .me = THIS_MODULE, }; -- cgit v1.2.3 From 27fd8d90c996caa480ed6777eaaf21d9e5166cc3 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 19 Jun 2014 12:37:58 +0200 Subject: netfilter: nf_log: move log buffering to core logging This patch moves Eric Dumazet's log buffer implementation from the xt_log.h header file to the core net/netfilter/nf_log.c. This also includes the renaming of the structure and functions to avoid possible undesired namespace clashes. This change allows us to use it from the arp and bridge packet logging implementation in follow up patches. --- include/net/netfilter/nf_log.h | 6 + include/net/netfilter/xt_log.h | 54 -------- net/netfilter/nf_log.c | 57 ++++++++ net/netfilter/xt_LOG.c | 301 +++++++++++++++++++++-------------------- 4 files changed, 217 insertions(+), 201 deletions(-) delete mode 100644 include/net/netfilter/xt_log.h (limited to 'include') diff --git a/include/net/netfilter/nf_log.h b/include/net/netfilter/nf_log.h index 06b4c6b07f52..aaec845de106 100644 --- a/include/net/netfilter/nf_log.h +++ b/include/net/netfilter/nf_log.h @@ -72,4 +72,10 @@ void nf_log_packet(struct net *net, const struct nf_loginfo *li, const char *fmt, ...); +struct nf_log_buf; + +struct nf_log_buf *nf_log_buf_open(void); +__printf(2, 3) int nf_log_buf_add(struct nf_log_buf *m, const char *f, ...); +void nf_log_buf_close(struct nf_log_buf *m); + #endif /* _NF_LOG_H */ diff --git a/include/net/netfilter/xt_log.h b/include/net/netfilter/xt_log.h deleted file mode 100644 index 9d9756cca013..000000000000 --- a/include/net/netfilter/xt_log.h +++ /dev/null @@ -1,54 +0,0 @@ -#define S_SIZE (1024 - (sizeof(unsigned int) + 1)) - -struct sbuff { - unsigned int count; - char buf[S_SIZE + 1]; -}; -static struct sbuff emergency, *emergency_ptr = &emergency; - -static __printf(2, 3) int sb_add(struct sbuff *m, const char *f, ...) -{ - va_list args; - int len; - - if (likely(m->count < S_SIZE)) { - va_start(args, f); - len = vsnprintf(m->buf + m->count, S_SIZE - m->count, f, args); - va_end(args); - if (likely(m->count + len < S_SIZE)) { - m->count += len; - return 0; - } - } - m->count = S_SIZE; - printk_once(KERN_ERR KBUILD_MODNAME " please increase S_SIZE\n"); - return -1; -} - -static struct sbuff *sb_open(void) -{ - struct sbuff *m = kmalloc(sizeof(*m), GFP_ATOMIC); - - if (unlikely(!m)) { - local_bh_disable(); - do { - m = xchg(&emergency_ptr, NULL); - } while (!m); - } - m->count = 0; - return m; -} - -static void sb_close(struct sbuff *m) -{ - m->buf[m->count] = 0; - printk("%s\n", m->buf); - - if (likely(m != &emergency)) - kfree(m); - else { - emergency_ptr = m; - local_bh_enable(); - } -} - diff --git a/net/netfilter/nf_log.c b/net/netfilter/nf_log.c index 7a29a3a46172..0b6b2c874199 100644 --- a/net/netfilter/nf_log.c +++ b/net/netfilter/nf_log.c @@ -157,6 +157,63 @@ void nf_log_packet(struct net *net, } EXPORT_SYMBOL(nf_log_packet); +#define S_SIZE (1024 - (sizeof(unsigned int) + 1)) + +struct nf_log_buf { + unsigned int count; + char buf[S_SIZE + 1]; +}; +static struct nf_log_buf emergency, *emergency_ptr = &emergency; + +__printf(2, 3) int nf_log_buf_add(struct nf_log_buf *m, const char *f, ...) +{ + va_list args; + int len; + + if (likely(m->count < S_SIZE)) { + va_start(args, f); + len = vsnprintf(m->buf + m->count, S_SIZE - m->count, f, args); + va_end(args); + if (likely(m->count + len < S_SIZE)) { + m->count += len; + return 0; + } + } + m->count = S_SIZE; + printk_once(KERN_ERR KBUILD_MODNAME " please increase S_SIZE\n"); + return -1; +} +EXPORT_SYMBOL_GPL(nf_log_buf_add); + +struct nf_log_buf *nf_log_buf_open(void) +{ + struct nf_log_buf *m = kmalloc(sizeof(*m), GFP_ATOMIC); + + if (unlikely(!m)) { + local_bh_disable(); + do { + m = xchg(&emergency_ptr, NULL); + } while (!m); + } + m->count = 0; + return m; +} +EXPORT_SYMBOL_GPL(nf_log_buf_open); + +void nf_log_buf_close(struct nf_log_buf *m) +{ + m->buf[m->count] = 0; + printk("%s\n", m->buf); + + if (likely(m != &emergency)) + kfree(m); + else { + emergency_ptr = m; + local_bh_enable(); + } +} +EXPORT_SYMBOL_GPL(nf_log_buf_close); + #ifdef CONFIG_PROC_FS static void *seq_start(struct seq_file *seq, loff_t *pos) { diff --git a/net/netfilter/xt_LOG.c b/net/netfilter/xt_LOG.c index e668d9797cf3..649b85fc5463 100644 --- a/net/netfilter/xt_LOG.c +++ b/net/netfilter/xt_LOG.c @@ -27,7 +27,6 @@ #include #include #include -#include static struct nf_loginfo default_loginfo = { .type = NF_LOG_TYPE_LOG, @@ -39,7 +38,7 @@ static struct nf_loginfo default_loginfo = { }, }; -static int dump_udp_header(struct sbuff *m, const struct sk_buff *skb, +static int dump_udp_header(struct nf_log_buf *m, const struct sk_buff *skb, u8 proto, int fragment, unsigned int offset) { struct udphdr _udph; @@ -47,9 +46,9 @@ static int dump_udp_header(struct sbuff *m, const struct sk_buff *skb, if (proto == IPPROTO_UDP) /* Max length: 10 "PROTO=UDP " */ - sb_add(m, "PROTO=UDP "); + nf_log_buf_add(m, "PROTO=UDP "); else /* Max length: 14 "PROTO=UDPLITE " */ - sb_add(m, "PROTO=UDPLITE "); + nf_log_buf_add(m, "PROTO=UDPLITE "); if (fragment) goto out; @@ -57,20 +56,20 @@ static int dump_udp_header(struct sbuff *m, const struct sk_buff *skb, /* Max length: 25 "INCOMPLETE [65535 bytes] " */ uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph); if (uh == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - offset); + nf_log_buf_add(m, "INCOMPLETE [%u bytes] ", skb->len - offset); return 1; } /* Max length: 20 "SPT=65535 DPT=65535 " */ - sb_add(m, "SPT=%u DPT=%u LEN=%u ", ntohs(uh->source), ntohs(uh->dest), - ntohs(uh->len)); + nf_log_buf_add(m, "SPT=%u DPT=%u LEN=%u ", + ntohs(uh->source), ntohs(uh->dest), ntohs(uh->len)); out: return 0; } -static int dump_tcp_header(struct sbuff *m, const struct sk_buff *skb, +static int dump_tcp_header(struct nf_log_buf *m, const struct sk_buff *skb, u8 proto, int fragment, unsigned int offset, unsigned int logflags) { @@ -78,7 +77,7 @@ static int dump_tcp_header(struct sbuff *m, const struct sk_buff *skb, const struct tcphdr *th; /* Max length: 10 "PROTO=TCP " */ - sb_add(m, "PROTO=TCP "); + nf_log_buf_add(m, "PROTO=TCP "); if (fragment) return 0; @@ -86,40 +85,43 @@ static int dump_tcp_header(struct sbuff *m, const struct sk_buff *skb, /* Max length: 25 "INCOMPLETE [65535 bytes] " */ th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph); if (th == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - offset); + nf_log_buf_add(m, "INCOMPLETE [%u bytes] ", skb->len - offset); return 1; } /* Max length: 20 "SPT=65535 DPT=65535 " */ - sb_add(m, "SPT=%u DPT=%u ", ntohs(th->source), ntohs(th->dest)); + nf_log_buf_add(m, "SPT=%u DPT=%u ", + ntohs(th->source), ntohs(th->dest)); /* Max length: 30 "SEQ=4294967295 ACK=4294967295 " */ - if (logflags & XT_LOG_TCPSEQ) - sb_add(m, "SEQ=%u ACK=%u ", ntohl(th->seq), ntohl(th->ack_seq)); + if (logflags & XT_LOG_TCPSEQ) { + nf_log_buf_add(m, "SEQ=%u ACK=%u ", + ntohl(th->seq), ntohl(th->ack_seq)); + } /* Max length: 13 "WINDOW=65535 " */ - sb_add(m, "WINDOW=%u ", ntohs(th->window)); + nf_log_buf_add(m, "WINDOW=%u ", ntohs(th->window)); /* Max length: 9 "RES=0x3C " */ - sb_add(m, "RES=0x%02x ", (u_int8_t)(ntohl(tcp_flag_word(th) & + nf_log_buf_add(m, "RES=0x%02x ", (u_int8_t)(ntohl(tcp_flag_word(th) & TCP_RESERVED_BITS) >> 22)); /* Max length: 32 "CWR ECE URG ACK PSH RST SYN FIN " */ if (th->cwr) - sb_add(m, "CWR "); + nf_log_buf_add(m, "CWR "); if (th->ece) - sb_add(m, "ECE "); + nf_log_buf_add(m, "ECE "); if (th->urg) - sb_add(m, "URG "); + nf_log_buf_add(m, "URG "); if (th->ack) - sb_add(m, "ACK "); + nf_log_buf_add(m, "ACK "); if (th->psh) - sb_add(m, "PSH "); + nf_log_buf_add(m, "PSH "); if (th->rst) - sb_add(m, "RST "); + nf_log_buf_add(m, "RST "); if (th->syn) - sb_add(m, "SYN "); + nf_log_buf_add(m, "SYN "); if (th->fin) - sb_add(m, "FIN "); + nf_log_buf_add(m, "FIN "); /* Max length: 11 "URGP=65535 " */ - sb_add(m, "URGP=%u ", ntohs(th->urg_ptr)); + nf_log_buf_add(m, "URGP=%u ", ntohs(th->urg_ptr)); if ((logflags & XT_LOG_TCPOPT) && th->doff*4 > sizeof(struct tcphdr)) { u_int8_t _opt[60 - sizeof(struct tcphdr)]; @@ -130,22 +132,22 @@ static int dump_tcp_header(struct sbuff *m, const struct sk_buff *skb, op = skb_header_pointer(skb, offset + sizeof(struct tcphdr), optsize, _opt); if (op == NULL) { - sb_add(m, "OPT (TRUNCATED)"); + nf_log_buf_add(m, "OPT (TRUNCATED)"); return 1; } /* Max length: 127 "OPT (" 15*4*2chars ") " */ - sb_add(m, "OPT ("); + nf_log_buf_add(m, "OPT ("); for (i = 0; i < optsize; i++) - sb_add(m, "%02X", op[i]); + nf_log_buf_add(m, "%02X", op[i]); - sb_add(m, ") "); + nf_log_buf_add(m, ") "); } return 0; } -static void dump_sk_uid_gid(struct sbuff *m, struct sock *sk) +static void dump_sk_uid_gid(struct nf_log_buf *m, struct sock *sk) { if (!sk || sk->sk_state == TCP_TIME_WAIT) return; @@ -153,7 +155,7 @@ static void dump_sk_uid_gid(struct sbuff *m, struct sock *sk) read_lock_bh(&sk->sk_callback_lock); if (sk->sk_socket && sk->sk_socket->file) { const struct cred *cred = sk->sk_socket->file->f_cred; - sb_add(m, "UID=%u GID=%u ", + nf_log_buf_add(m, "UID=%u GID=%u ", from_kuid_munged(&init_user_ns, cred->fsuid), from_kgid_munged(&init_user_ns, cred->fsgid)); } @@ -161,10 +163,9 @@ static void dump_sk_uid_gid(struct sbuff *m, struct sock *sk) } /* One level of recursion won't kill us */ -static void dump_ipv4_packet(struct sbuff *m, - const struct nf_loginfo *info, - const struct sk_buff *skb, - unsigned int iphoff) +static void dump_ipv4_packet(struct nf_log_buf *m, + const struct nf_loginfo *info, + const struct sk_buff *skb, unsigned int iphoff) { struct iphdr _iph; const struct iphdr *ih; @@ -177,32 +178,32 @@ static void dump_ipv4_packet(struct sbuff *m, ih = skb_header_pointer(skb, iphoff, sizeof(_iph), &_iph); if (ih == NULL) { - sb_add(m, "TRUNCATED"); + nf_log_buf_add(m, "TRUNCATED"); return; } /* Important fields: * TOS, len, DF/MF, fragment offset, TTL, src, dst, options. */ /* Max length: 40 "SRC=255.255.255.255 DST=255.255.255.255 " */ - sb_add(m, "SRC=%pI4 DST=%pI4 ", + nf_log_buf_add(m, "SRC=%pI4 DST=%pI4 ", &ih->saddr, &ih->daddr); /* Max length: 46 "LEN=65535 TOS=0xFF PREC=0xFF TTL=255 ID=65535 " */ - sb_add(m, "LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ", + nf_log_buf_add(m, "LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ", ntohs(ih->tot_len), ih->tos & IPTOS_TOS_MASK, ih->tos & IPTOS_PREC_MASK, ih->ttl, ntohs(ih->id)); /* Max length: 6 "CE DF MF " */ if (ntohs(ih->frag_off) & IP_CE) - sb_add(m, "CE "); + nf_log_buf_add(m, "CE "); if (ntohs(ih->frag_off) & IP_DF) - sb_add(m, "DF "); + nf_log_buf_add(m, "DF "); if (ntohs(ih->frag_off) & IP_MF) - sb_add(m, "MF "); + nf_log_buf_add(m, "MF "); /* Max length: 11 "FRAG:65535 " */ if (ntohs(ih->frag_off) & IP_OFFSET) - sb_add(m, "FRAG:%u ", ntohs(ih->frag_off) & IP_OFFSET); + nf_log_buf_add(m, "FRAG:%u ", ntohs(ih->frag_off) & IP_OFFSET); if ((logflags & XT_LOG_IPOPT) && ih->ihl * 4 > sizeof(struct iphdr)) { @@ -214,15 +215,15 @@ static void dump_ipv4_packet(struct sbuff *m, op = skb_header_pointer(skb, iphoff+sizeof(_iph), optsize, _opt); if (op == NULL) { - sb_add(m, "TRUNCATED"); + nf_log_buf_add(m, "TRUNCATED"); return; } /* Max length: 127 "OPT (" 15*4*2chars ") " */ - sb_add(m, "OPT ("); + nf_log_buf_add(m, "OPT ("); for (i = 0; i < optsize; i++) - sb_add(m, "%02X", op[i]); - sb_add(m, ") "); + nf_log_buf_add(m, "%02X", op[i]); + nf_log_buf_add(m, ") "); } switch (ih->protocol) { @@ -261,7 +262,7 @@ static void dump_ipv4_packet(struct sbuff *m, [ICMP_ADDRESSREPLY] = 12 }; /* Max length: 11 "PROTO=ICMP " */ - sb_add(m, "PROTO=ICMP "); + nf_log_buf_add(m, "PROTO=ICMP "); if (ntohs(ih->frag_off) & IP_OFFSET) break; @@ -270,19 +271,19 @@ static void dump_ipv4_packet(struct sbuff *m, ich = skb_header_pointer(skb, iphoff + ih->ihl * 4, sizeof(_icmph), &_icmph); if (ich == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] ", - skb->len - iphoff - ih->ihl*4); + nf_log_buf_add(m, "INCOMPLETE [%u bytes] ", + skb->len - iphoff - ih->ihl*4); break; } /* Max length: 18 "TYPE=255 CODE=255 " */ - sb_add(m, "TYPE=%u CODE=%u ", ich->type, ich->code); + nf_log_buf_add(m, "TYPE=%u CODE=%u ", ich->type, ich->code); /* Max length: 25 "INCOMPLETE [65535 bytes] " */ if (ich->type <= NR_ICMP_TYPES && required_len[ich->type] && skb->len-iphoff-ih->ihl*4 < required_len[ich->type]) { - sb_add(m, "INCOMPLETE [%u bytes] ", + nf_log_buf_add(m, "INCOMPLETE [%u bytes] ", skb->len - iphoff - ih->ihl*4); break; } @@ -291,35 +292,37 @@ static void dump_ipv4_packet(struct sbuff *m, case ICMP_ECHOREPLY: case ICMP_ECHO: /* Max length: 19 "ID=65535 SEQ=65535 " */ - sb_add(m, "ID=%u SEQ=%u ", + nf_log_buf_add(m, "ID=%u SEQ=%u ", ntohs(ich->un.echo.id), ntohs(ich->un.echo.sequence)); break; case ICMP_PARAMETERPROB: /* Max length: 14 "PARAMETER=255 " */ - sb_add(m, "PARAMETER=%u ", + nf_log_buf_add(m, "PARAMETER=%u ", ntohl(ich->un.gateway) >> 24); break; case ICMP_REDIRECT: /* Max length: 24 "GATEWAY=255.255.255.255 " */ - sb_add(m, "GATEWAY=%pI4 ", &ich->un.gateway); + nf_log_buf_add(m, "GATEWAY=%pI4 ", &ich->un.gateway); /* Fall through */ case ICMP_DEST_UNREACH: case ICMP_SOURCE_QUENCH: case ICMP_TIME_EXCEEDED: /* Max length: 3+maxlen */ if (!iphoff) { /* Only recurse once. */ - sb_add(m, "["); + nf_log_buf_add(m, "["); dump_ipv4_packet(m, info, skb, iphoff + ih->ihl*4+sizeof(_icmph)); - sb_add(m, "] "); + nf_log_buf_add(m, "] "); } /* Max length: 10 "MTU=65535 " */ if (ich->type == ICMP_DEST_UNREACH && - ich->code == ICMP_FRAG_NEEDED) - sb_add(m, "MTU=%u ", ntohs(ich->un.frag.mtu)); + ich->code == ICMP_FRAG_NEEDED) { + nf_log_buf_add(m, "MTU=%u ", + ntohs(ich->un.frag.mtu)); + } } break; } @@ -332,19 +335,19 @@ static void dump_ipv4_packet(struct sbuff *m, break; /* Max length: 9 "PROTO=AH " */ - sb_add(m, "PROTO=AH "); + nf_log_buf_add(m, "PROTO=AH "); /* Max length: 25 "INCOMPLETE [65535 bytes] " */ ah = skb_header_pointer(skb, iphoff+ih->ihl*4, sizeof(_ahdr), &_ahdr); if (ah == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] ", - skb->len - iphoff - ih->ihl*4); + nf_log_buf_add(m, "INCOMPLETE [%u bytes] ", + skb->len - iphoff - ih->ihl*4); break; } /* Length: 15 "SPI=0xF1234567 " */ - sb_add(m, "SPI=0x%x ", ntohl(ah->spi)); + nf_log_buf_add(m, "SPI=0x%x ", ntohl(ah->spi)); break; } case IPPROTO_ESP: { @@ -352,7 +355,7 @@ static void dump_ipv4_packet(struct sbuff *m, const struct ip_esp_hdr *eh; /* Max length: 10 "PROTO=ESP " */ - sb_add(m, "PROTO=ESP "); + nf_log_buf_add(m, "PROTO=ESP "); if (ntohs(ih->frag_off) & IP_OFFSET) break; @@ -361,18 +364,18 @@ static void dump_ipv4_packet(struct sbuff *m, eh = skb_header_pointer(skb, iphoff+ih->ihl*4, sizeof(_esph), &_esph); if (eh == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] ", - skb->len - iphoff - ih->ihl*4); + nf_log_buf_add(m, "INCOMPLETE [%u bytes] ", + skb->len - iphoff - ih->ihl*4); break; } /* Length: 15 "SPI=0xF1234567 " */ - sb_add(m, "SPI=0x%x ", ntohl(eh->spi)); + nf_log_buf_add(m, "SPI=0x%x ", ntohl(eh->spi)); break; } /* Max length: 10 "PROTO 255 " */ default: - sb_add(m, "PROTO=%u ", ih->protocol); + nf_log_buf_add(m, "PROTO=%u ", ih->protocol); } /* Max length: 15 "UID=4294967295 " */ @@ -381,7 +384,7 @@ static void dump_ipv4_packet(struct sbuff *m, /* Max length: 16 "MARK=0xFFFFFFFF " */ if (!iphoff && skb->mark) - sb_add(m, "MARK=0x%x ", skb->mark); + nf_log_buf_add(m, "MARK=0x%x ", skb->mark); /* Proto Max log string length */ /* IP: 40+46+6+11+127 = 230 */ @@ -398,9 +401,9 @@ static void dump_ipv4_packet(struct sbuff *m, /* maxlen = 230+ 91 + 230 + 252 = 803 */ } -static void dump_ipv4_mac_header(struct sbuff *m, - const struct nf_loginfo *info, - const struct sk_buff *skb) +static void dump_ipv4_mac_header(struct nf_log_buf *m, + const struct nf_loginfo *info, + const struct sk_buff *skb) { struct net_device *dev = skb->dev; unsigned int logflags = 0; @@ -413,30 +416,30 @@ static void dump_ipv4_mac_header(struct sbuff *m, switch (dev->type) { case ARPHRD_ETHER: - sb_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ", - eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, - ntohs(eth_hdr(skb)->h_proto)); + nf_log_buf_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ", + eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, + ntohs(eth_hdr(skb)->h_proto)); return; default: break; } fallback: - sb_add(m, "MAC="); + nf_log_buf_add(m, "MAC="); if (dev->hard_header_len && skb->mac_header != skb->network_header) { const unsigned char *p = skb_mac_header(skb); unsigned int i; - sb_add(m, "%02x", *p++); + nf_log_buf_add(m, "%02x", *p++); for (i = 1; i < dev->hard_header_len; i++, p++) - sb_add(m, ":%02x", *p); + nf_log_buf_add(m, ":%02x", *p); } - sb_add(m, " "); + nf_log_buf_add(m, " "); } static void -log_packet_common(struct sbuff *m, +log_packet_common(struct nf_log_buf *m, u_int8_t pf, unsigned int hooknum, const struct sk_buff *skb, @@ -445,10 +448,10 @@ log_packet_common(struct sbuff *m, const struct nf_loginfo *loginfo, const char *prefix) { - sb_add(m, KERN_SOH "%c%sIN=%s OUT=%s ", - '0' + loginfo->u.log.level, prefix, - in ? in->name : "", - out ? out->name : ""); + nf_log_buf_add(m, KERN_SOH "%c%sIN=%s OUT=%s ", + '0' + loginfo->u.log.level, prefix, + in ? in->name : "", + out ? out->name : ""); #ifdef CONFIG_BRIDGE_NETFILTER if (skb->nf_bridge) { const struct net_device *physindev; @@ -456,10 +459,10 @@ log_packet_common(struct sbuff *m, physindev = skb->nf_bridge->physindev; if (physindev && in != physindev) - sb_add(m, "PHYSIN=%s ", physindev->name); + nf_log_buf_add(m, "PHYSIN=%s ", physindev->name); physoutdev = skb->nf_bridge->physoutdev; if (physoutdev && out != physoutdev) - sb_add(m, "PHYSOUT=%s ", physoutdev->name); + nf_log_buf_add(m, "PHYSOUT=%s ", physoutdev->name); } #endif } @@ -475,13 +478,13 @@ ipt_log_packet(struct net *net, const struct nf_loginfo *loginfo, const char *prefix) { - struct sbuff *m; + struct nf_log_buf *m; /* FIXME: Disabled from containers until syslog ns is supported */ if (!net_eq(net, &init_net)) return; - m = sb_open(); + m = nf_log_buf_open(); if (!loginfo) loginfo = &default_loginfo; @@ -493,15 +496,15 @@ ipt_log_packet(struct net *net, dump_ipv4_packet(m, loginfo, skb, 0); - sb_close(m); + nf_log_buf_close(m); } #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) /* One level of recursion won't kill us */ -static void dump_ipv6_packet(struct sbuff *m, - const struct nf_loginfo *info, - const struct sk_buff *skb, unsigned int ip6hoff, - int recurse) +static void dump_ipv6_packet(struct nf_log_buf *m, + const struct nf_loginfo *info, + const struct sk_buff *skb, unsigned int ip6hoff, + int recurse) { u_int8_t currenthdr; int fragment; @@ -518,19 +521,18 @@ static void dump_ipv6_packet(struct sbuff *m, ih = skb_header_pointer(skb, ip6hoff, sizeof(_ip6h), &_ip6h); if (ih == NULL) { - sb_add(m, "TRUNCATED"); + nf_log_buf_add(m, "TRUNCATED"); return; } /* Max length: 88 "SRC=0000.0000.0000.0000.0000.0000.0000.0000 DST=0000.0000.0000.0000.0000.0000.0000.0000 " */ - sb_add(m, "SRC=%pI6 DST=%pI6 ", &ih->saddr, &ih->daddr); + nf_log_buf_add(m, "SRC=%pI6 DST=%pI6 ", &ih->saddr, &ih->daddr); /* Max length: 44 "LEN=65535 TC=255 HOPLIMIT=255 FLOWLBL=FFFFF " */ - sb_add(m, "LEN=%Zu TC=%u HOPLIMIT=%u FLOWLBL=%u ", - ntohs(ih->payload_len) + sizeof(struct ipv6hdr), - (ntohl(*(__be32 *)ih) & 0x0ff00000) >> 20, - ih->hop_limit, - (ntohl(*(__be32 *)ih) & 0x000fffff)); + nf_log_buf_add(m, "LEN=%Zu TC=%u HOPLIMIT=%u FLOWLBL=%u ", + ntohs(ih->payload_len) + sizeof(struct ipv6hdr), + (ntohl(*(__be32 *)ih) & 0x0ff00000) >> 20, + ih->hop_limit, (ntohl(*(__be32 *)ih) & 0x000fffff)); fragment = 0; ptr = ip6hoff + sizeof(struct ipv6hdr); @@ -541,35 +543,35 @@ static void dump_ipv6_packet(struct sbuff *m, hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr); if (hp == NULL) { - sb_add(m, "TRUNCATED"); + nf_log_buf_add(m, "TRUNCATED"); return; } /* Max length: 48 "OPT (...) " */ if (logflags & XT_LOG_IPOPT) - sb_add(m, "OPT ( "); + nf_log_buf_add(m, "OPT ( "); switch (currenthdr) { case IPPROTO_FRAGMENT: { struct frag_hdr _fhdr; const struct frag_hdr *fh; - sb_add(m, "FRAG:"); + nf_log_buf_add(m, "FRAG:"); fh = skb_header_pointer(skb, ptr, sizeof(_fhdr), &_fhdr); if (fh == NULL) { - sb_add(m, "TRUNCATED "); + nf_log_buf_add(m, "TRUNCATED "); return; } /* Max length: 6 "65535 " */ - sb_add(m, "%u ", ntohs(fh->frag_off) & 0xFFF8); + nf_log_buf_add(m, "%u ", ntohs(fh->frag_off) & 0xFFF8); /* Max length: 11 "INCOMPLETE " */ if (fh->frag_off & htons(0x0001)) - sb_add(m, "INCOMPLETE "); + nf_log_buf_add(m, "INCOMPLETE "); - sb_add(m, "ID:%08x ", ntohl(fh->identification)); + nf_log_buf_add(m, "ID:%08x ", ntohl(fh->identification)); if (ntohs(fh->frag_off) & 0xFFF8) fragment = 1; @@ -583,7 +585,7 @@ static void dump_ipv6_packet(struct sbuff *m, case IPPROTO_HOPOPTS: if (fragment) { if (logflags & XT_LOG_IPOPT) - sb_add(m, ")"); + nf_log_buf_add(m, ")"); return; } hdrlen = ipv6_optlen(hp); @@ -595,10 +597,10 @@ static void dump_ipv6_packet(struct sbuff *m, const struct ip_auth_hdr *ah; /* Max length: 3 "AH " */ - sb_add(m, "AH "); + nf_log_buf_add(m, "AH "); if (fragment) { - sb_add(m, ")"); + nf_log_buf_add(m, ")"); return; } @@ -609,13 +611,13 @@ static void dump_ipv6_packet(struct sbuff *m, * Max length: 26 "INCOMPLETE [65535 * bytes] )" */ - sb_add(m, "INCOMPLETE [%u bytes] )", - skb->len - ptr); + nf_log_buf_add(m, "INCOMPLETE [%u bytes] )", + skb->len - ptr); return; } /* Length: 15 "SPI=0xF1234567 */ - sb_add(m, "SPI=0x%x ", ntohl(ah->spi)); + nf_log_buf_add(m, "SPI=0x%x ", ntohl(ah->spi)); } @@ -627,10 +629,10 @@ static void dump_ipv6_packet(struct sbuff *m, const struct ip_esp_hdr *eh; /* Max length: 4 "ESP " */ - sb_add(m, "ESP "); + nf_log_buf_add(m, "ESP "); if (fragment) { - sb_add(m, ")"); + nf_log_buf_add(m, ")"); return; } @@ -640,23 +642,23 @@ static void dump_ipv6_packet(struct sbuff *m, eh = skb_header_pointer(skb, ptr, sizeof(_esph), &_esph); if (eh == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] )", - skb->len - ptr); + nf_log_buf_add(m, "INCOMPLETE [%u bytes] )", + skb->len - ptr); return; } /* Length: 16 "SPI=0xF1234567 )" */ - sb_add(m, "SPI=0x%x )", ntohl(eh->spi)); + nf_log_buf_add(m, "SPI=0x%x )", ntohl(eh->spi)); } return; default: /* Max length: 20 "Unknown Ext Hdr 255" */ - sb_add(m, "Unknown Ext Hdr %u", currenthdr); + nf_log_buf_add(m, "Unknown Ext Hdr %u", currenthdr); return; } if (logflags & XT_LOG_IPOPT) - sb_add(m, ") "); + nf_log_buf_add(m, ") "); currenthdr = hp->nexthdr; ptr += hdrlen; @@ -678,7 +680,7 @@ static void dump_ipv6_packet(struct sbuff *m, const struct icmp6hdr *ic; /* Max length: 13 "PROTO=ICMPv6 " */ - sb_add(m, "PROTO=ICMPv6 "); + nf_log_buf_add(m, "PROTO=ICMPv6 "); if (fragment) break; @@ -686,20 +688,22 @@ static void dump_ipv6_packet(struct sbuff *m, /* Max length: 25 "INCOMPLETE [65535 bytes] " */ ic = skb_header_pointer(skb, ptr, sizeof(_icmp6h), &_icmp6h); if (ic == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - ptr); + nf_log_buf_add(m, "INCOMPLETE [%u bytes] ", + skb->len - ptr); return; } /* Max length: 18 "TYPE=255 CODE=255 " */ - sb_add(m, "TYPE=%u CODE=%u ", ic->icmp6_type, ic->icmp6_code); + nf_log_buf_add(m, "TYPE=%u CODE=%u ", + ic->icmp6_type, ic->icmp6_code); switch (ic->icmp6_type) { case ICMPV6_ECHO_REQUEST: case ICMPV6_ECHO_REPLY: /* Max length: 19 "ID=65535 SEQ=65535 " */ - sb_add(m, "ID=%u SEQ=%u ", - ntohs(ic->icmp6_identifier), - ntohs(ic->icmp6_sequence)); + nf_log_buf_add(m, "ID=%u SEQ=%u ", + ntohs(ic->icmp6_identifier), + ntohs(ic->icmp6_sequence)); break; case ICMPV6_MGM_QUERY: case ICMPV6_MGM_REPORT: @@ -708,28 +712,30 @@ static void dump_ipv6_packet(struct sbuff *m, case ICMPV6_PARAMPROB: /* Max length: 17 "POINTER=ffffffff " */ - sb_add(m, "POINTER=%08x ", ntohl(ic->icmp6_pointer)); + nf_log_buf_add(m, "POINTER=%08x ", + ntohl(ic->icmp6_pointer)); /* Fall through */ case ICMPV6_DEST_UNREACH: case ICMPV6_PKT_TOOBIG: case ICMPV6_TIME_EXCEED: /* Max length: 3+maxlen */ if (recurse) { - sb_add(m, "["); + nf_log_buf_add(m, "["); dump_ipv6_packet(m, info, skb, ptr + sizeof(_icmp6h), 0); - sb_add(m, "] "); + nf_log_buf_add(m, "] "); } /* Max length: 10 "MTU=65535 " */ if (ic->icmp6_type == ICMPV6_PKT_TOOBIG) - sb_add(m, "MTU=%u ", ntohl(ic->icmp6_mtu)); + nf_log_buf_add(m, "MTU=%u ", + ntohl(ic->icmp6_mtu)); } break; } /* Max length: 10 "PROTO=255 " */ default: - sb_add(m, "PROTO=%u ", currenthdr); + nf_log_buf_add(m, "PROTO=%u ", currenthdr); } /* Max length: 15 "UID=4294967295 " */ @@ -738,12 +744,12 @@ static void dump_ipv6_packet(struct sbuff *m, /* Max length: 16 "MARK=0xFFFFFFFF " */ if (recurse && skb->mark) - sb_add(m, "MARK=0x%x ", skb->mark); + nf_log_buf_add(m, "MARK=0x%x ", skb->mark); } -static void dump_ipv6_mac_header(struct sbuff *m, - const struct nf_loginfo *info, - const struct sk_buff *skb) +static void dump_ipv6_mac_header(struct nf_log_buf *m, + const struct nf_loginfo *info, + const struct sk_buff *skb) { struct net_device *dev = skb->dev; unsigned int logflags = 0; @@ -756,16 +762,16 @@ static void dump_ipv6_mac_header(struct sbuff *m, switch (dev->type) { case ARPHRD_ETHER: - sb_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ", - eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, - ntohs(eth_hdr(skb)->h_proto)); + nf_log_buf_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ", + eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, + ntohs(eth_hdr(skb)->h_proto)); return; default: break; } fallback: - sb_add(m, "MAC="); + nf_log_buf_add(m, "MAC="); if (dev->hard_header_len && skb->mac_header != skb->network_header) { const unsigned char *p = skb_mac_header(skb); @@ -780,20 +786,21 @@ fallback: } if (p != NULL) { - sb_add(m, "%02x", *p++); + nf_log_buf_add(m, "%02x", *p++); for (i = 1; i < len; i++) - sb_add(m, ":%02x", *p++); + nf_log_buf_add(m, ":%02x", *p++); } - sb_add(m, " "); + nf_log_buf_add(m, " "); if (dev->type == ARPHRD_SIT) { const struct iphdr *iph = (struct iphdr *)skb_mac_header(skb); - sb_add(m, "TUNNEL=%pI4->%pI4 ", &iph->saddr, - &iph->daddr); + nf_log_buf_add(m, "TUNNEL=%pI4->%pI4 ", + &iph->saddr, &iph->daddr); } - } else - sb_add(m, " "); + } else { + nf_log_buf_add(m, " "); + } } static void @@ -806,13 +813,13 @@ ip6t_log_packet(struct net *net, const struct nf_loginfo *loginfo, const char *prefix) { - struct sbuff *m; + struct nf_log_buf *m; /* FIXME: Disabled from containers until syslog ns is supported */ if (!net_eq(net, &init_net)) return; - m = sb_open(); + m = nf_log_buf_open(); if (!loginfo) loginfo = &default_loginfo; @@ -824,7 +831,7 @@ ip6t_log_packet(struct net *net, dump_ipv6_packet(m, loginfo, skb, skb_network_offset(skb), 1); - sb_close(m); + nf_log_buf_close(m); } #endif -- cgit v1.2.3 From 3760cd20402d4c131e1994c968ecb055fa0f74bc Mon Sep 17 00:00:00 2001 From: "Chen, Gong" Date: Wed, 11 Jun 2014 13:59:45 -0700 Subject: CPER: Adjust code flow of some functions Some codes can be reorganzied as a common function for other usages. Signed-off-by: Chen, Gong Signed-off-by: Tony Luck --- drivers/firmware/efi/cper.c | 155 ++++++++++++++++++++++++++++---------------- include/linux/cper.h | 9 +++ 2 files changed, 109 insertions(+), 55 deletions(-) (limited to 'include') diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c index 1491dd4f08f9..ac33a9fed341 100644 --- a/drivers/firmware/efi/cper.c +++ b/drivers/firmware/efi/cper.c @@ -34,6 +34,9 @@ #include #define INDENT_SP " " + +static char rcd_decode_str[CPER_REC_LEN]; + /* * CPER record ID need to be unique even after reboot, because record * ID is used as index for ERST storage, while CPER records from @@ -50,18 +53,19 @@ u64 cper_next_record_id(void) } EXPORT_SYMBOL_GPL(cper_next_record_id); -static const char *cper_severity_strs[] = { +static const char * const severity_strs[] = { "recoverable", "fatal", "corrected", "info", }; -static const char *cper_severity_str(unsigned int severity) +const char *cper_severity_str(unsigned int severity) { - return severity < ARRAY_SIZE(cper_severity_strs) ? - cper_severity_strs[severity] : "unknown"; + return severity < ARRAY_SIZE(severity_strs) ? + severity_strs[severity] : "unknown"; } +EXPORT_SYMBOL_GPL(cper_severity_str); /* * cper_print_bits - print strings for set bits @@ -100,32 +104,32 @@ void cper_print_bits(const char *pfx, unsigned int bits, printk("%s\n", buf); } -static const char * const cper_proc_type_strs[] = { +static const char * const proc_type_strs[] = { "IA32/X64", "IA64", }; -static const char * const cper_proc_isa_strs[] = { +static const char * const proc_isa_strs[] = { "IA32", "IA64", "X64", }; -static const char * const cper_proc_error_type_strs[] = { +static const char * const proc_error_type_strs[] = { "cache error", "TLB error", "bus error", "micro-architectural error", }; -static const char * const cper_proc_op_strs[] = { +static const char * const proc_op_strs[] = { "unknown or generic", "data read", "data write", "instruction execution", }; -static const char * const cper_proc_flag_strs[] = { +static const char * const proc_flag_strs[] = { "restartable", "precise IP", "overflow", @@ -137,26 +141,26 @@ static void cper_print_proc_generic(const char *pfx, { if (proc->validation_bits & CPER_PROC_VALID_TYPE) printk("%s""processor_type: %d, %s\n", pfx, proc->proc_type, - proc->proc_type < ARRAY_SIZE(cper_proc_type_strs) ? - cper_proc_type_strs[proc->proc_type] : "unknown"); + proc->proc_type < ARRAY_SIZE(proc_type_strs) ? + proc_type_strs[proc->proc_type] : "unknown"); if (proc->validation_bits & CPER_PROC_VALID_ISA) printk("%s""processor_isa: %d, %s\n", pfx, proc->proc_isa, - proc->proc_isa < ARRAY_SIZE(cper_proc_isa_strs) ? - cper_proc_isa_strs[proc->proc_isa] : "unknown"); + proc->proc_isa < ARRAY_SIZE(proc_isa_strs) ? + proc_isa_strs[proc->proc_isa] : "unknown"); if (proc->validation_bits & CPER_PROC_VALID_ERROR_TYPE) { printk("%s""error_type: 0x%02x\n", pfx, proc->proc_error_type); cper_print_bits(pfx, proc->proc_error_type, - cper_proc_error_type_strs, - ARRAY_SIZE(cper_proc_error_type_strs)); + proc_error_type_strs, + ARRAY_SIZE(proc_error_type_strs)); } if (proc->validation_bits & CPER_PROC_VALID_OPERATION) printk("%s""operation: %d, %s\n", pfx, proc->operation, - proc->operation < ARRAY_SIZE(cper_proc_op_strs) ? - cper_proc_op_strs[proc->operation] : "unknown"); + proc->operation < ARRAY_SIZE(proc_op_strs) ? + proc_op_strs[proc->operation] : "unknown"); if (proc->validation_bits & CPER_PROC_VALID_FLAGS) { printk("%s""flags: 0x%02x\n", pfx, proc->flags); - cper_print_bits(pfx, proc->flags, cper_proc_flag_strs, - ARRAY_SIZE(cper_proc_flag_strs)); + cper_print_bits(pfx, proc->flags, proc_flag_strs, + ARRAY_SIZE(proc_flag_strs)); } if (proc->validation_bits & CPER_PROC_VALID_LEVEL) printk("%s""level: %d\n", pfx, proc->level); @@ -177,7 +181,7 @@ static void cper_print_proc_generic(const char *pfx, printk("%s""IP: 0x%016llx\n", pfx, proc->ip); } -static const char *cper_mem_err_type_strs[] = { +static const char * const mem_err_type_strs[] = { "unknown", "no error", "single-bit ECC", @@ -196,58 +200,99 @@ static const char *cper_mem_err_type_strs[] = { "physical memory map-out event", }; -static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem) +const char *cper_mem_err_type_str(unsigned int etype) { - if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS) - printk("%s""error_status: 0x%016llx\n", pfx, mem->error_status); - if (mem->validation_bits & CPER_MEM_VALID_PA) - printk("%s""physical_address: 0x%016llx\n", - pfx, mem->physical_addr); - if (mem->validation_bits & CPER_MEM_VALID_PA_MASK) - printk("%s""physical_address_mask: 0x%016llx\n", - pfx, mem->physical_addr_mask); + return etype < ARRAY_SIZE(mem_err_type_strs) ? + mem_err_type_strs[etype] : "unknown"; +} +EXPORT_SYMBOL_GPL(cper_mem_err_type_str); + +static int cper_mem_err_location(const struct cper_sec_mem_err *mem, char *msg) +{ + u32 len, n; + + if (!msg) + return 0; + + n = 0; + len = CPER_REC_LEN - 1; if (mem->validation_bits & CPER_MEM_VALID_NODE) - pr_debug("node: %d\n", mem->node); + n += scnprintf(msg + n, len - n, "node: %d ", mem->node); if (mem->validation_bits & CPER_MEM_VALID_CARD) - pr_debug("card: %d\n", mem->card); + n += scnprintf(msg + n, len - n, "card: %d ", mem->card); if (mem->validation_bits & CPER_MEM_VALID_MODULE) - pr_debug("module: %d\n", mem->module); + n += scnprintf(msg + n, len - n, "module: %d ", mem->module); if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER) - pr_debug("rank: %d\n", mem->rank); + n += scnprintf(msg + n, len - n, "rank: %d ", mem->rank); if (mem->validation_bits & CPER_MEM_VALID_BANK) - pr_debug("bank: %d\n", mem->bank); + n += scnprintf(msg + n, len - n, "bank: %d ", mem->bank); if (mem->validation_bits & CPER_MEM_VALID_DEVICE) - pr_debug("device: %d\n", mem->device); + n += scnprintf(msg + n, len - n, "device: %d ", mem->device); if (mem->validation_bits & CPER_MEM_VALID_ROW) - pr_debug("row: %d\n", mem->row); + n += scnprintf(msg + n, len - n, "row: %d ", mem->row); if (mem->validation_bits & CPER_MEM_VALID_COLUMN) - pr_debug("column: %d\n", mem->column); + n += scnprintf(msg + n, len - n, "column: %d ", mem->column); if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION) - pr_debug("bit_position: %d\n", mem->bit_pos); + n += scnprintf(msg + n, len - n, "bit_position: %d ", + mem->bit_pos); if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID) - pr_debug("requestor_id: 0x%016llx\n", mem->requestor_id); + n += scnprintf(msg + n, len - n, "requestor_id: 0x%016llx ", + mem->requestor_id); if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID) - pr_debug("responder_id: 0x%016llx\n", mem->responder_id); + n += scnprintf(msg + n, len - n, "responder_id: 0x%016llx ", + mem->responder_id); if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID) - pr_debug("target_id: 0x%016llx\n", mem->target_id); + scnprintf(msg + n, len - n, "target_id: 0x%016llx ", + mem->target_id); + + msg[n] = '\0'; + return n; +} + +static int cper_dimm_err_location(const struct cper_sec_mem_err *mem, char *msg) +{ + u32 len, n; + const char *bank = NULL, *device = NULL; + + if (!msg || !(mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE)) + return 0; + + n = 0; + len = CPER_REC_LEN - 1; + dmi_memdev_name(mem->mem_dev_handle, &bank, &device); + if (bank && device) + n = snprintf(msg, len, "DIMM location: %s %s ", bank, device); + else + n = snprintf(msg, len, + "DIMM location: not present. DMI handle: 0x%.4x ", + mem->mem_dev_handle); + + msg[n] = '\0'; + return n; +} + +static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem) +{ + if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS) + printk("%s""error_status: 0x%016llx\n", pfx, mem->error_status); + if (mem->validation_bits & CPER_MEM_VALID_PA) + printk("%s""physical_address: 0x%016llx\n", + pfx, mem->physical_addr); + if (mem->validation_bits & CPER_MEM_VALID_PA_MASK) + printk("%s""physical_address_mask: 0x%016llx\n", + pfx, mem->physical_addr_mask); + if (cper_mem_err_location(mem, rcd_decode_str)) + printk("%s%s\n", pfx, rcd_decode_str); if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) { u8 etype = mem->error_type; printk("%s""error_type: %d, %s\n", pfx, etype, - etype < ARRAY_SIZE(cper_mem_err_type_strs) ? - cper_mem_err_type_strs[etype] : "unknown"); - } - if (mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE) { - const char *bank = NULL, *device = NULL; - dmi_memdev_name(mem->mem_dev_handle, &bank, &device); - if (bank != NULL && device != NULL) - printk("%s""DIMM location: %s %s", pfx, bank, device); - else - printk("%s""DIMM DMI handle: 0x%.4x", - pfx, mem->mem_dev_handle); + cper_mem_err_type_str(etype)); } + if (cper_dimm_err_location(mem, rcd_decode_str)) + printk("%s%s\n", pfx, rcd_decode_str); } -static const char *cper_pcie_port_type_strs[] = { +static const char * const pcie_port_type_strs[] = { "PCIe end point", "legacy PCI end point", "unknown", @@ -266,8 +311,8 @@ static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie, { if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE) printk("%s""port_type: %d, %s\n", pfx, pcie->port_type, - pcie->port_type < ARRAY_SIZE(cper_pcie_port_type_strs) ? - cper_pcie_port_type_strs[pcie->port_type] : "unknown"); + pcie->port_type < ARRAY_SIZE(pcie_port_type_strs) ? + pcie_port_type_strs[pcie->port_type] : "unknown"); if (pcie->validation_bits & CPER_PCIE_VALID_VERSION) printk("%s""version: %d.%d\n", pfx, pcie->version.major, pcie->version.minor); diff --git a/include/linux/cper.h b/include/linux/cper.h index 2fc0ec3d89cc..ed088b9c1298 100644 --- a/include/linux/cper.h +++ b/include/linux/cper.h @@ -35,6 +35,13 @@ */ #define CPER_RECORD_REV 0x0100 +/* + * CPER record length contains the CPER fields which are relevant for further + * handling of a memory error in userspace (we don't carry all the fields + * defined in the UEFI spec because some of them don't make any sense.) + * Currently, a length of 256 should be more than enough. + */ +#define CPER_REC_LEN 256 /* * Severity difinition for error_severity in struct cper_record_header * and section_severity in struct cper_section_descriptor @@ -395,6 +402,8 @@ struct cper_sec_pcie { #pragma pack() u64 cper_next_record_id(void); +const char *cper_severity_str(unsigned int); +const char *cper_mem_err_type_str(unsigned int); void cper_print_bits(const char *prefix, unsigned int bits, const char * const strs[], unsigned int strs_size); -- cgit v1.2.3 From d963cd95bea93b7db9390a71d1e2cabbb3b2c3ea Mon Sep 17 00:00:00 2001 From: "Chen, Gong" Date: Wed, 11 Jun 2014 14:02:20 -0700 Subject: RAS, debugfs: Add debugfs interface for RAS subsystem Implement a new debugfs interface for RAS susbsystem. A file named daemon_active is added there accordingly. This file is used to track if user space daemon accesses perf/trace interface or not. One can track which daemon opens it via "lsof /path/to/debugfs/ras/daemon_active". Signed-off-by: Chen, Gong Link: http://lkml.kernel.org/r/1402475691-30045-5-git-send-email-gong.chen@linux.intel.com Signed-off-by: Borislav Petkov Signed-off-by: Tony Luck --- drivers/ras/Makefile | 2 +- drivers/ras/debugfs.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/ras/ras.c | 14 +++++++++++++ include/linux/ras.h | 14 +++++++++++++ 4 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 drivers/ras/debugfs.c create mode 100644 include/linux/ras.h (limited to 'include') diff --git a/drivers/ras/Makefile b/drivers/ras/Makefile index 223e806fa5bf..d7f73341ced3 100644 --- a/drivers/ras/Makefile +++ b/drivers/ras/Makefile @@ -1 +1 @@ -obj-$(CONFIG_RAS) += ras.o +obj-$(CONFIG_RAS) += ras.o debugfs.o diff --git a/drivers/ras/debugfs.c b/drivers/ras/debugfs.c new file mode 100644 index 000000000000..0322acf67ea5 --- /dev/null +++ b/drivers/ras/debugfs.c @@ -0,0 +1,56 @@ +#include + +static struct dentry *ras_debugfs_dir; + +static atomic_t trace_count = ATOMIC_INIT(0); + +int ras_userspace_consumers(void) +{ + return atomic_read(&trace_count); +} +EXPORT_SYMBOL_GPL(ras_userspace_consumers); + +static int trace_show(struct seq_file *m, void *v) +{ + return atomic_read(&trace_count); +} + +static int trace_open(struct inode *inode, struct file *file) +{ + atomic_inc(&trace_count); + return single_open(file, trace_show, NULL); +} + +static int trace_release(struct inode *inode, struct file *file) +{ + atomic_dec(&trace_count); + return single_release(inode, file); +} + +static const struct file_operations trace_fops = { + .open = trace_open, + .read = seq_read, + .llseek = seq_lseek, + .release = trace_release, +}; + +int __init ras_add_daemon_trace(void) +{ + struct dentry *fentry; + + if (!ras_debugfs_dir) + return -ENOENT; + + fentry = debugfs_create_file("daemon_active", S_IRUSR, ras_debugfs_dir, + NULL, &trace_fops); + if (!fentry) + return -ENODEV; + + return 0; + +} + +void __init ras_debugfs_init(void) +{ + ras_debugfs_dir = debugfs_create_dir("ras", NULL); +} diff --git a/drivers/ras/ras.c b/drivers/ras/ras.c index b0c6ed1d8e77..4cac43a1e25c 100644 --- a/drivers/ras/ras.c +++ b/drivers/ras/ras.c @@ -5,8 +5,22 @@ * Chen, Gong */ +#include +#include + #define CREATE_TRACE_POINTS #define TRACE_INCLUDE_PATH ../../include/ras #include +static int __init ras_init(void) +{ + int rc = 0; + + ras_debugfs_init(); + rc = ras_add_daemon_trace(); + + return rc; +} +subsys_initcall(ras_init); + EXPORT_TRACEPOINT_SYMBOL_GPL(mc_event); diff --git a/include/linux/ras.h b/include/linux/ras.h new file mode 100644 index 000000000000..2aceeafd6fe5 --- /dev/null +++ b/include/linux/ras.h @@ -0,0 +1,14 @@ +#ifndef __RAS_H__ +#define __RAS_H__ + +#ifdef CONFIG_DEBUG_FS +int ras_userspace_consumers(void); +void ras_debugfs_init(void); +int ras_add_daemon_trace(void); +#else +static inline int ras_userspace_consumers(void) { return 0; } +static inline void ras_debugfs_init(void) { return; } +static inline int ras_add_daemon_trace(void) { return 0; } +#endif + +#endif -- cgit v1.2.3 From 2dfb7d51a61d7ca91b131c8db612f27d9390f2d5 Mon Sep 17 00:00:00 2001 From: "Chen, Gong" Date: Tue, 17 Jun 2014 22:33:07 -0400 Subject: trace, RAS: Add eMCA trace event interface Add trace interface to elaborate all H/W error related information. Signed-off-by: Chen, Gong Acked-by: Borislav Petkov Signed-off-by: Tony Luck --- drivers/acpi/Kconfig | 4 ++- drivers/acpi/acpi_extlog.c | 27 ++++++++++++++++--- drivers/firmware/efi/cper.c | 45 ++++++++++++++++++++++++++++--- drivers/ras/ras.c | 3 +++ include/linux/cper.h | 23 ++++++++++++++++ include/ras/ras_event.h | 64 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 158 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index a34a22841002..206942b8d105 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -370,6 +370,7 @@ config ACPI_EXTLOG tristate "Extended Error Log support" depends on X86_MCE && X86_LOCAL_APIC select UEFI_CPER + select RAS default n help Certain usages such as Predictive Failure Analysis (PFA) require @@ -384,6 +385,7 @@ config ACPI_EXTLOG Enhanced MCA Logging allows firmware to provide additional error information to system software, synchronous with MCE or CMCI. This - driver adds support for that functionality. + driver adds support for that functionality with corresponding + tracepoint which carries that information to userspace. endif # ACPI diff --git a/drivers/acpi/acpi_extlog.c b/drivers/acpi/acpi_extlog.c index 185334114d71..e61da957f30f 100644 --- a/drivers/acpi/acpi_extlog.c +++ b/drivers/acpi/acpi_extlog.c @@ -16,6 +16,7 @@ #include #include "apei/apei-internal.h" +#include #define EXT_ELOG_ENTRY_MASK GENMASK_ULL(51, 0) /* elog entry address mask */ @@ -137,8 +138,12 @@ static int extlog_print(struct notifier_block *nb, unsigned long val, struct mce *mce = (struct mce *)data; int bank = mce->bank; int cpu = mce->extcpu; - struct acpi_generic_status *estatus; - int rc; + struct acpi_generic_status *estatus, *tmp; + struct acpi_generic_data *gdata; + const uuid_le *fru_id = &NULL_UUID_LE; + char *fru_text = ""; + uuid_le *sec_type; + static u32 err_seq; estatus = extlog_elog_entry_check(cpu, bank); if (estatus == NULL) @@ -148,7 +153,23 @@ static int extlog_print(struct notifier_block *nb, unsigned long val, /* clear record status to enable BIOS to update it again */ estatus->block_status = 0; - rc = print_extlog_rcd(NULL, (struct acpi_generic_status *)elog_buf, cpu); + tmp = (struct acpi_generic_status *)elog_buf; + print_extlog_rcd(NULL, tmp, cpu); + + /* log event via trace */ + err_seq++; + gdata = (struct acpi_generic_data *)(tmp + 1); + if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID) + fru_id = (uuid_le *)gdata->fru_id; + if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) + fru_text = gdata->fru_text; + sec_type = (uuid_le *)gdata->section_type; + if (!uuid_le_cmp(*sec_type, CPER_SEC_PLATFORM_MEM)) { + struct cper_sec_mem_err *mem = (void *)(gdata + 1); + if (gdata->error_data_length >= sizeof(*mem)) + trace_extlog_mem_event(mem, err_seq, fru_id, fru_text, + (u8)gdata->error_severity); + } return NOTIFY_STOP; } diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c index ac33a9fed341..437e6fd47311 100644 --- a/drivers/firmware/efi/cper.c +++ b/drivers/firmware/efi/cper.c @@ -207,7 +207,7 @@ const char *cper_mem_err_type_str(unsigned int etype) } EXPORT_SYMBOL_GPL(cper_mem_err_type_str); -static int cper_mem_err_location(const struct cper_sec_mem_err *mem, char *msg) +static int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg) { u32 len, n; @@ -249,7 +249,7 @@ static int cper_mem_err_location(const struct cper_sec_mem_err *mem, char *msg) return n; } -static int cper_dimm_err_location(const struct cper_sec_mem_err *mem, char *msg) +static int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg) { u32 len, n; const char *bank = NULL, *device = NULL; @@ -271,8 +271,44 @@ static int cper_dimm_err_location(const struct cper_sec_mem_err *mem, char *msg) return n; } +void cper_mem_err_pack(const struct cper_sec_mem_err *mem, + struct cper_mem_err_compact *cmem) +{ + cmem->validation_bits = mem->validation_bits; + cmem->node = mem->node; + cmem->card = mem->card; + cmem->module = mem->module; + cmem->bank = mem->bank; + cmem->device = mem->device; + cmem->row = mem->row; + cmem->column = mem->column; + cmem->bit_pos = mem->bit_pos; + cmem->requestor_id = mem->requestor_id; + cmem->responder_id = mem->responder_id; + cmem->target_id = mem->target_id; + cmem->rank = mem->rank; + cmem->mem_array_handle = mem->mem_array_handle; + cmem->mem_dev_handle = mem->mem_dev_handle; +} + +const char *cper_mem_err_unpack(struct trace_seq *p, + struct cper_mem_err_compact *cmem) +{ + const char *ret = p->buffer + p->len; + + if (cper_mem_err_location(cmem, rcd_decode_str)) + trace_seq_printf(p, "%s", rcd_decode_str); + if (cper_dimm_err_location(cmem, rcd_decode_str)) + trace_seq_printf(p, "%s", rcd_decode_str); + trace_seq_putc(p, '\0'); + + return ret; +} + static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem) { + struct cper_mem_err_compact cmem; + if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS) printk("%s""error_status: 0x%016llx\n", pfx, mem->error_status); if (mem->validation_bits & CPER_MEM_VALID_PA) @@ -281,14 +317,15 @@ static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem) if (mem->validation_bits & CPER_MEM_VALID_PA_MASK) printk("%s""physical_address_mask: 0x%016llx\n", pfx, mem->physical_addr_mask); - if (cper_mem_err_location(mem, rcd_decode_str)) + cper_mem_err_pack(mem, &cmem); + if (cper_mem_err_location(&cmem, rcd_decode_str)) printk("%s%s\n", pfx, rcd_decode_str); if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) { u8 etype = mem->error_type; printk("%s""error_type: %d, %s\n", pfx, etype, cper_mem_err_type_str(etype)); } - if (cper_dimm_err_location(mem, rcd_decode_str)) + if (cper_dimm_err_location(&cmem, rcd_decode_str)) printk("%s%s\n", pfx, rcd_decode_str); } diff --git a/drivers/ras/ras.c b/drivers/ras/ras.c index 4cac43a1e25c..b67dd362b7b6 100644 --- a/drivers/ras/ras.c +++ b/drivers/ras/ras.c @@ -23,4 +23,7 @@ static int __init ras_init(void) } subsys_initcall(ras_init); +#if defined(CONFIG_ACPI_EXTLOG) || defined(CONFIG_ACPI_EXTLOG_MODULE) +EXPORT_TRACEPOINT_SYMBOL_GPL(extlog_mem_event); +#endif EXPORT_TRACEPOINT_SYMBOL_GPL(mc_event); diff --git a/include/linux/cper.h b/include/linux/cper.h index ed088b9c1298..76abba4b238e 100644 --- a/include/linux/cper.h +++ b/include/linux/cper.h @@ -22,6 +22,7 @@ #define LINUX_CPER_H #include +#include /* CPER record signature and the size */ #define CPER_SIG_RECORD "CPER" @@ -363,6 +364,24 @@ struct cper_sec_mem_err { __u16 mem_dev_handle; /* module handle in UEFI 2.4 */ }; +struct cper_mem_err_compact { + __u64 validation_bits; + __u16 node; + __u16 card; + __u16 module; + __u16 bank; + __u16 device; + __u16 row; + __u16 column; + __u16 bit_pos; + __u64 requestor_id; + __u64 responder_id; + __u64 target_id; + __u16 rank; + __u16 mem_array_handle; + __u16 mem_dev_handle; +}; + struct cper_sec_pcie { __u64 validation_bits; __u32 port_type; @@ -406,5 +425,9 @@ const char *cper_severity_str(unsigned int); const char *cper_mem_err_type_str(unsigned int); void cper_print_bits(const char *prefix, unsigned int bits, const char * const strs[], unsigned int strs_size); +void cper_mem_err_pack(const struct cper_sec_mem_err *, + struct cper_mem_err_compact *); +const char *cper_mem_err_unpack(struct trace_seq *, + struct cper_mem_err_compact *); #endif diff --git a/include/ras/ras_event.h b/include/ras/ras_event.h index acbcbb88eaaa..47da53c27ffa 100644 --- a/include/ras/ras_event.h +++ b/include/ras/ras_event.h @@ -9,6 +9,70 @@ #include #include #include +#include + +/* + * MCE Extended Error Log trace event + * + * These events are generated when hardware detects a corrected or + * uncorrected event. + */ + +/* memory trace event */ + +#if defined(CONFIG_ACPI_EXTLOG) || defined(CONFIG_ACPI_EXTLOG_MODULE) +TRACE_EVENT(extlog_mem_event, + TP_PROTO(struct cper_sec_mem_err *mem, + u32 err_seq, + const uuid_le *fru_id, + const char *fru_text, + u8 sev), + + TP_ARGS(mem, err_seq, fru_id, fru_text, sev), + + TP_STRUCT__entry( + __field(u32, err_seq) + __field(u8, etype) + __field(u8, sev) + __field(u64, pa) + __field(u8, pa_mask_lsb) + __field_struct(uuid_le, fru_id) + __string(fru_text, fru_text) + __field_struct(struct cper_mem_err_compact, data) + ), + + TP_fast_assign( + __entry->err_seq = err_seq; + if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) + __entry->etype = mem->error_type; + else + __entry->etype = ~0; + __entry->sev = sev; + if (mem->validation_bits & CPER_MEM_VALID_PA) + __entry->pa = mem->physical_addr; + else + __entry->pa = ~0ull; + + if (mem->validation_bits & CPER_MEM_VALID_PA_MASK) + __entry->pa_mask_lsb = (u8)__ffs64(mem->physical_addr_mask); + else + __entry->pa_mask_lsb = ~0; + __entry->fru_id = *fru_id; + __assign_str(fru_text, fru_text); + cper_mem_err_pack(mem, &__entry->data); + ), + + TP_printk("{%d} %s error: %s physical addr: %016llx (mask lsb: %x) %sFRU: %pUl %.20s", + __entry->err_seq, + cper_severity_str(__entry->sev), + cper_mem_err_type_str(__entry->etype), + __entry->pa, + __entry->pa_mask_lsb, + cper_mem_err_unpack(p, &__entry->data), + &__entry->fru_id, + __get_str(fru_text)) +); +#endif /* * Hardware Events Report -- cgit v1.2.3 From d0d480cce8f522b37c2c1de38230fc9ad15fa506 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Jun 2014 10:08:44 -0700 Subject: leds: add led-class attribute-group support Allow led-class devices to be created with optional attribute groups. This is needed in order to allow led drivers to create custom device attributes in a race-free manner. Signed-off-by: Johan Hovold Signed-off-by: Bryan Wu --- drivers/leds/led-class.c | 5 +++-- include/linux/leds.h | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index f37d63cf726b..aa29198fca3e 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -210,8 +210,9 @@ static const struct dev_pm_ops leds_class_dev_pm_ops = { */ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) { - led_cdev->dev = device_create(leds_class, parent, 0, led_cdev, - "%s", led_cdev->name); + led_cdev->dev = device_create_with_groups(leds_class, parent, 0, + led_cdev, led_cdev->groups, + "%s", led_cdev->name); if (IS_ERR(led_cdev->dev)) return PTR_ERR(led_cdev->dev); diff --git a/include/linux/leds.h b/include/linux/leds.h index 0287ab296689..e43686472197 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -63,6 +63,8 @@ struct led_classdev { unsigned long *delay_off); struct device *dev; + const struct attribute_group **groups; + struct list_head node; /* LED Device list */ const char *default_trigger; /* Trigger to use */ -- cgit v1.2.3 From 6d514b4e7737ad75a7e7e0a3f7dde45d46341691 Mon Sep 17 00:00:00 2001 From: George Spelvin Date: Mon, 23 Jun 2014 15:11:54 +0200 Subject: lib: crc32: Greatly shrink CRC combining code There's no need for a full 32x32 matrix, when rows before the last are just shifted copies of the rows after them. There's still room for improvement (especially on X86 processors with CRC32 and PCLMUL instructions), but this is a large step in the right direction [which is in particular useful for its current user, namely SCTP checksumming over multiple skb frags[] entries, i.e. in IPVS balancing when other CRC32 offloads are not available]. The internal primitive is now called crc32_generic_shift and takes one less argument; the XOR with crc2 is done in inline wrappers. Signed-off-by: George Spelvin Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/linux/crc32.h | 14 ++++- lib/crc32.c | 147 ++++++++++++++++++++++++-------------------------- 2 files changed, 82 insertions(+), 79 deletions(-) (limited to 'include') diff --git a/include/linux/crc32.h b/include/linux/crc32.h index 7d275c4fc011..edf34e876e40 100644 --- a/include/linux/crc32.h +++ b/include/linux/crc32.h @@ -29,7 +29,12 @@ extern u32 crc32_be(u32 crc, unsigned char const *p, size_t len); * with the same initializer as crc1, and crc2 seed was 0. See * also crc32_combine_test(). */ -extern u32 crc32_le_combine(u32 crc1, u32 crc2, size_t len2); +u32 __attribute_const__ crc32_le_shift(u32 crc, size_t len); + +static inline u32 crc32_le_combine(u32 crc1, u32 crc2, size_t len2) +{ + return crc32_le_shift(crc1, len2) ^ crc2; +} extern u32 __crc32c_le(u32 crc, unsigned char const *p, size_t len); @@ -51,7 +56,12 @@ extern u32 __crc32c_le(u32 crc, unsigned char const *p, size_t len); * seeded with the same initializer as crc1, and crc2 seed * was 0. See also crc32c_combine_test(). */ -extern u32 __crc32c_le_combine(u32 crc1, u32 crc2, size_t len2); +u32 __attribute_const__ __crc32c_le_shift(u32 crc, size_t len); + +static inline u32 __crc32c_le_combine(u32 crc1, u32 crc2, size_t len2) +{ + return __crc32c_le_shift(crc1, len2) ^ crc2; +} #define crc32(seed, data, length) crc32_le(seed, (unsigned char const *)(data), length) diff --git a/lib/crc32.c b/lib/crc32.c index 21a7b2135af6..9af30ff334c5 100644 --- a/lib/crc32.c +++ b/lib/crc32.c @@ -50,30 +50,6 @@ MODULE_AUTHOR("Matt Domsch "); MODULE_DESCRIPTION("Various CRC32 calculations"); MODULE_LICENSE("GPL"); -#define GF2_DIM 32 - -static u32 gf2_matrix_times(u32 *mat, u32 vec) -{ - u32 sum = 0; - - while (vec) { - if (vec & 1) - sum ^= *mat; - vec >>= 1; - mat++; - } - - return sum; -} - -static void gf2_matrix_square(u32 *square, u32 *mat) -{ - int i; - - for (i = 0; i < GF2_DIM; i++) - square[i] = gf2_matrix_times(mat, mat[i]); -} - #if CRC_LE_BITS > 8 || CRC_BE_BITS > 8 /* implements slicing-by-4 or slicing-by-8 algorithm */ @@ -155,51 +131,6 @@ crc32_body(u32 crc, unsigned char const *buf, size_t len, const u32 (*tab)[256]) } #endif -/* For conditions of distribution and use, see copyright notice in zlib.h */ -static u32 crc32_generic_combine(u32 crc1, u32 crc2, size_t len2, - u32 polynomial) -{ - u32 even[GF2_DIM]; /* Even-power-of-two zeros operator */ - u32 odd[GF2_DIM]; /* Odd-power-of-two zeros operator */ - u32 row; - int i; - - if (len2 <= 0) - return crc1; - - /* Put operator for one zero bit in odd */ - odd[0] = polynomial; - row = 1; - for (i = 1; i < GF2_DIM; i++) { - odd[i] = row; - row <<= 1; - } - - gf2_matrix_square(even, odd); /* Put operator for two zero bits in even */ - gf2_matrix_square(odd, even); /* Put operator for four zero bits in odd */ - - /* Apply len2 zeros to crc1 (first square will put the operator for one - * zero byte, eight zero bits, in even). - */ - do { - /* Apply zeros operator for this bit of len2 */ - gf2_matrix_square(even, odd); - if (len2 & 1) - crc1 = gf2_matrix_times(even, crc1); - len2 >>= 1; - /* If no more bits set, then done */ - if (len2 == 0) - break; - /* Another iteration of the loop with odd and even swapped */ - gf2_matrix_square(odd, even); - if (len2 & 1) - crc1 = gf2_matrix_times(odd, crc1); - len2 >>= 1; - } while (len2 != 0); - - crc1 ^= crc2; - return crc1; -} /** * crc32_le_generic() - Calculate bitwise little-endian Ethernet AUTODIN II @@ -271,19 +202,81 @@ u32 __pure __crc32c_le(u32 crc, unsigned char const *p, size_t len) (const u32 (*)[256])crc32ctable_le, CRC32C_POLY_LE); } #endif -u32 __pure crc32_le_combine(u32 crc1, u32 crc2, size_t len2) +EXPORT_SYMBOL(crc32_le); +EXPORT_SYMBOL(__crc32c_le); + +/* + * This multiplies the polynomials x and y modulo the given modulus. + * This follows the "little-endian" CRC convention that the lsbit + * represents the highest power of x, and the msbit represents x^0. + */ +static u32 __attribute_const__ gf2_multiply(u32 x, u32 y, u32 modulus) { - return crc32_generic_combine(crc1, crc2, len2, CRCPOLY_LE); + u32 product = x & 1 ? y : 0; + int i; + + for (i = 0; i < 31; i++) { + product = (product >> 1) ^ (product & 1 ? modulus : 0); + x >>= 1; + product ^= x & 1 ? y : 0; + } + + return product; } -u32 __pure __crc32c_le_combine(u32 crc1, u32 crc2, size_t len2) +/** + * crc32_generic_shift - Append len 0 bytes to crc, in logarithmic time + * @crc: The original little-endian CRC (i.e. lsbit is x^31 coefficient) + * @len: The number of bytes. @crc is multiplied by x^(8*@len) + * @polynomial: The modulus used to reduce the result to 32 bits. + * + * It's possible to parallelize CRC computations by computing a CRC + * over separate ranges of a buffer, then summing them. + * This shifts the given CRC by 8*len bits (i.e. produces the same effect + * as appending len bytes of zero to the data), in time proportional + * to log(len). + */ +static u32 __attribute_const__ crc32_generic_shift(u32 crc, size_t len, + u32 polynomial) { - return crc32_generic_combine(crc1, crc2, len2, CRC32C_POLY_LE); + u32 power = polynomial; /* CRC of x^32 */ + int i; + + /* Shift up to 32 bits in the simple linear way */ + for (i = 0; i < 8 * (int)(len & 3); i++) + crc = (crc >> 1) ^ (crc & 1 ? polynomial : 0); + + len >>= 2; + if (!len) + return crc; + + for (;;) { + /* "power" is x^(2^i), modulo the polynomial */ + if (len & 1) + crc = gf2_multiply(crc, power, polynomial); + + len >>= 1; + if (!len) + break; + + /* Square power, advancing to x^(2^(i+1)) */ + power = gf2_multiply(power, power, polynomial); + } + + return crc; } -EXPORT_SYMBOL(crc32_le); -EXPORT_SYMBOL(crc32_le_combine); -EXPORT_SYMBOL(__crc32c_le); -EXPORT_SYMBOL(__crc32c_le_combine); + +u32 __attribute_const__ crc32_le_shift(u32 crc, size_t len) +{ + return crc32_generic_shift(crc, len, CRCPOLY_LE); +} + +u32 __attribute_const__ __crc32c_le_shift(u32 crc, size_t len) +{ + return crc32_generic_shift(crc, len, CRC32C_POLY_LE); +} +EXPORT_SYMBOL(crc32_le_shift); +EXPORT_SYMBOL(__crc32c_le_shift); /** * crc32_be_generic() - Calculate bitwise big-endian Ethernet AUTODIN II CRC32 -- cgit v1.2.3 From d8f1c4778e957273c3b5b6e045d8d3af38484ca8 Mon Sep 17 00:00:00 2001 From: George Spelvin Date: Mon, 23 Jun 2014 15:11:56 +0200 Subject: lib: crc32: Add some additional __pure annotations In case they help the compiler. Signed-off-by: George Spelvin Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/linux/crc32.h | 6 +++--- lib/crc32.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/crc32.h b/include/linux/crc32.h index edf34e876e40..9e8a032c1788 100644 --- a/include/linux/crc32.h +++ b/include/linux/crc32.h @@ -8,8 +8,8 @@ #include #include -extern u32 crc32_le(u32 crc, unsigned char const *p, size_t len); -extern u32 crc32_be(u32 crc, unsigned char const *p, size_t len); +u32 __pure crc32_le(u32 crc, unsigned char const *p, size_t len); +u32 __pure crc32_be(u32 crc, unsigned char const *p, size_t len); /** * crc32_le_combine - Combine two crc32 check values into one. For two @@ -36,7 +36,7 @@ static inline u32 crc32_le_combine(u32 crc1, u32 crc2, size_t len2) return crc32_le_shift(crc1, len2) ^ crc2; } -extern u32 __crc32c_le(u32 crc, unsigned char const *p, size_t len); +u32 __pure __crc32c_le(u32 crc, unsigned char const *p, size_t len); /** * __crc32c_le_combine - Combine two crc32c check values into one. For two diff --git a/lib/crc32.c b/lib/crc32.c index af938ab12468..9a907d489d95 100644 --- a/lib/crc32.c +++ b/lib/crc32.c @@ -53,7 +53,7 @@ MODULE_LICENSE("GPL"); #if CRC_LE_BITS > 8 || CRC_BE_BITS > 8 /* implements slicing-by-4 or slicing-by-8 algorithm */ -static inline u32 +static inline u32 __pure crc32_body(u32 crc, unsigned char const *buf, size_t len, const u32 (*tab)[256]) { # ifdef __LITTLE_ENDIAN -- cgit v1.2.3 From f88649721268999bdff09777847080a52004f691 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 24 Jun 2014 10:05:11 -0700 Subject: ipv4: fix dst race in sk_dst_get() When IP route cache had been removed in linux-3.6, we broke assumption that dst entries were all freed after rcu grace period. DST_NOCACHE dst were supposed to be freed from dst_release(). But it appears we want to keep such dst around, either in UDP sockets or tunnels. In sk_dst_get() we need to make sure dst refcount is not 0 before incrementing it, or else we might end up freeing a dst twice. DST_NOCACHE set on a dst does not mean this dst can not be attached to a socket or a tunnel. Then, before actual freeing, we need to observe a rcu grace period to make sure all other cpus can catch the fact the dst is no longer usable. Signed-off-by: Eric Dumazet Reported-by: Dormando Signed-off-by: David S. Miller --- include/net/sock.h | 4 ++-- net/core/dst.c | 16 +++++++++++----- net/ipv4/ip_tunnel.c | 14 +++++--------- 3 files changed, 18 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index 07b7fcd60d80..173cae485de1 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1730,8 +1730,8 @@ sk_dst_get(struct sock *sk) rcu_read_lock(); dst = rcu_dereference(sk->sk_dst_cache); - if (dst) - dst_hold(dst); + if (dst && !atomic_inc_not_zero(&dst->__refcnt)) + dst = NULL; rcu_read_unlock(); return dst; } diff --git a/net/core/dst.c b/net/core/dst.c index 80d6286c8b62..a028409ee438 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -269,6 +269,15 @@ again: } EXPORT_SYMBOL(dst_destroy); +static void dst_destroy_rcu(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) { @@ -276,11 +285,8 @@ void dst_release(struct dst_entry *dst) newrefcnt = atomic_dec_return(&dst->__refcnt); WARN_ON(newrefcnt < 0); - if (unlikely(dst->flags & DST_NOCACHE) && !newrefcnt) { - dst = dst_destroy(dst); - if (dst) - __dst_free(dst); - } + if (unlikely(dst->flags & DST_NOCACHE) && !newrefcnt) + call_rcu(&dst->rcu_head, dst_destroy_rcu); } } EXPORT_SYMBOL(dst_release); diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 097b3e7c1e8f..54b6731dab55 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -73,12 +73,7 @@ static void __tunnel_dst_set(struct ip_tunnel_dst *idst, { struct dst_entry *old_dst; - if (dst) { - if (dst->flags & DST_NOCACHE) - dst = NULL; - else - dst_clone(dst); - } + dst_clone(dst); old_dst = xchg((__force struct dst_entry **)&idst->dst, dst); dst_release(old_dst); } @@ -108,13 +103,14 @@ static struct rtable *tunnel_rtable_get(struct ip_tunnel *t, u32 cookie) rcu_read_lock(); dst = rcu_dereference(this_cpu_ptr(t->dst_cache)->dst); + if (dst && !atomic_inc_not_zero(&dst->__refcnt)) + dst = NULL; if (dst) { if (dst->obsolete && dst->ops->check(dst, cookie) == NULL) { - rcu_read_unlock(); tunnel_dst_reset(t); - return NULL; + dst_release(dst); + dst = NULL; } - dst_hold(dst); } rcu_read_unlock(); return (struct rtable *)dst; -- cgit v1.2.3 From a69f5edb8ba20c87c5f7c96ec40581f9f51f2910 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 24 Jun 2014 11:20:48 -0700 Subject: mac_pton: Use bool not int return Use bool instead of int as the return type. All uses are tested with !. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- include/linux/kernel.h | 2 +- lib/net_utils.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 4c52907a6d8b..a9e2268ecccb 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -501,7 +501,7 @@ static inline char * __deprecated pack_hex_byte(char *buf, u8 byte) extern int hex_to_bin(char ch); extern int __must_check hex2bin(u8 *dst, const char *src, size_t count); -int mac_pton(const char *s, u8 *mac); +bool mac_pton(const char *s, u8 *mac); /* * General tracing related utility functions - trace_printk(), diff --git a/lib/net_utils.c b/lib/net_utils.c index 2e3c52c8d050..148fc6e99ef6 100644 --- a/lib/net_utils.c +++ b/lib/net_utils.c @@ -3,24 +3,24 @@ #include #include -int mac_pton(const char *s, u8 *mac) +bool mac_pton(const char *s, u8 *mac) { int i; /* XX:XX:XX:XX:XX:XX */ if (strlen(s) < 3 * ETH_ALEN - 1) - return 0; + return false; /* Don't dirty result unless string is valid MAC. */ for (i = 0; i < ETH_ALEN; i++) { if (!isxdigit(s[i * 3]) || !isxdigit(s[i * 3 + 1])) - return 0; + return false; if (i != ETH_ALEN - 1 && s[i * 3 + 2] != ':') - return 0; + return false; } for (i = 0; i < ETH_ALEN; i++) { mac[i] = (hex_to_bin(s[i * 3]) << 4) | hex_to_bin(s[i * 3 + 1]); } - return 1; + return true; } EXPORT_SYMBOL(mac_pton); -- cgit v1.2.3 From a6eacef7fba7834da4d22762ea0d8524df3993a8 Mon Sep 17 00:00:00 2001 From: Erik Hugne Date: Wed, 25 Jun 2014 10:07:05 +0200 Subject: tipc: bump max configurable window size The maximum window size is limited by the sequence gap field, which was expanded with bd7845337b105e090dd18912d511139945fa7586 ("tipc: Expand link sequence gap field to 13 bits") We remove the artificial limit that prevents the link window to be set larger than 150. Signed-off-by: Erik Hugne Reviewed-by: Jon Maloy Signed-off-by: David S. Miller --- include/uapi/linux/tipc_config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/tipc_config.h b/include/uapi/linux/tipc_config.h index 41a76acbb305..876d0a14863c 100644 --- a/include/uapi/linux/tipc_config.h +++ b/include/uapi/linux/tipc_config.h @@ -182,7 +182,7 @@ #define TIPC_MIN_LINK_WIN 16 #define TIPC_DEF_LINK_WIN 50 -#define TIPC_MAX_LINK_WIN 150 +#define TIPC_MAX_LINK_WIN 8191 struct tipc_node_info { -- cgit v1.2.3 From 3482f2c52b77bf6596e24aae82e204a0603eba66 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 27 Mar 2014 17:18:55 -0700 Subject: of: Create of_console_check() for selecting a console specified in /chosen The devicetree has a binding for specifying the console device in the /chosen node, but the kernel doesn't use it consistently. This change adds an API for testing if a device node is a console, and adds a preferred console entry if it is. At the same time this patch removes the of_device_is_stdout_path() API since it is unused. Signed-off-by: Grant Likely Tested-by: Sascha Hauer --- drivers/of/base.c | 23 +++++++++++++---------- include/linux/of.h | 6 +++--- 2 files changed, 16 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/of/base.c b/drivers/of/base.c index b9864806e9b8..df9b2bb7bb27 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -17,6 +17,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ +#include #include #include #include @@ -2180,20 +2181,22 @@ const char *of_prop_next_string(struct property *prop, const char *cur) EXPORT_SYMBOL_GPL(of_prop_next_string); /** - * of_device_is_stdout_path - check if a device node matches the - * linux,stdout-path property - * - * Check if this device node matches the linux,stdout-path property - * in the chosen node. return true if yes, false otherwise. + * of_console_check() - Test and setup console for DT setup + * @dn - Pointer to device node + * @name - Name to use for preferred console without index. ex. "ttyS" + * @index - Index to use for preferred console. + * + * Check if the given device node matches the stdout-path property in the + * /chosen node. If it does then register it as the preferred console and return + * TRUE. Otherwise return FALSE. */ -int of_device_is_stdout_path(struct device_node *dn) +bool of_console_check(struct device_node *dn, char *name, int index) { - if (!of_stdout) + if (!dn || dn != of_stdout || console_set_on_cmdline) return false; - - return of_stdout == dn; + return add_preferred_console(name, index, NULL); } -EXPORT_SYMBOL_GPL(of_device_is_stdout_path); +EXPORT_SYMBOL_GPL(of_console_check); /** * of_find_next_cache_node - Find a node's subsidiary cache diff --git a/include/linux/of.h b/include/linux/of.h index 196b34c1ef4e..9d9734056e39 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -352,7 +352,7 @@ const __be32 *of_prop_next_u32(struct property *prop, const __be32 *cur, */ const char *of_prop_next_string(struct property *prop, const char *cur); -int of_device_is_stdout_path(struct device_node *dn); +bool of_console_check(struct device_node *dn, char *name, int index); #else /* CONFIG_OF */ @@ -564,9 +564,9 @@ static inline int of_machine_is_compatible(const char *compat) return 0; } -static inline int of_device_is_stdout_path(struct device_node *dn) +static inline bool of_console_check(const struct device_node *dn, const char *name, int index) { - return 0; + return false; } static inline const __be32 *of_prop_next_u32(struct property *prop, -- cgit v1.2.3 From a752ee56ad84bf9a35b8323af1ad22b03c1df2c4 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Fri, 28 Mar 2014 08:12:18 -0700 Subject: tty: Update hypervisor tty drivers to use core stdout parsing code. The evh_bytechan, hvc_opal and hvc_vio drivers all open code the parsing of the stdout node in the device tree. This patch simplifies the driver by removing the duplicated functionality. Signed-off-by: Grant Likely --- drivers/of/base.c | 5 ++++- drivers/tty/ehv_bytechan.c | 43 ++++--------------------------------------- drivers/tty/hvc/hvc_opal.c | 15 +++------------ drivers/tty/hvc/hvc_vio.c | 29 ++++++++++------------------- include/linux/of.h | 1 + 5 files changed, 22 insertions(+), 71 deletions(-) (limited to 'include') diff --git a/drivers/of/base.c b/drivers/of/base.c index df9b2bb7bb27..e4f95ba0a3eb 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -36,7 +36,7 @@ struct device_node *of_allnodes; EXPORT_SYMBOL(of_allnodes); struct device_node *of_chosen; struct device_node *of_aliases; -static struct device_node *of_stdout; +struct device_node *of_stdout; static struct kset *of_kset; @@ -2063,9 +2063,12 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)) of_chosen = of_find_node_by_path("/chosen@0"); if (of_chosen) { + /* linux,stdout-path and /aliases/stdout are for legacy compatibility */ const char *name = of_get_property(of_chosen, "stdout-path", NULL); if (!name) name = of_get_property(of_chosen, "linux,stdout-path", NULL); + if (IS_ENABLED(CONFIG_PPC) && !name) + name = of_get_property(of_aliases, "stdout", NULL); if (name) of_stdout = of_find_node_by_path(name); } diff --git a/drivers/tty/ehv_bytechan.c b/drivers/tty/ehv_bytechan.c index 0419b69e270f..4f485e88f60c 100644 --- a/drivers/tty/ehv_bytechan.c +++ b/drivers/tty/ehv_bytechan.c @@ -108,55 +108,23 @@ static void disable_tx_interrupt(struct ehv_bc_data *bc) * * The byte channel to be used for the console is specified via a "stdout" * property in the /chosen node. - * - * For compatible with legacy device trees, we also look for a "stdout" alias. */ static int find_console_handle(void) { - struct device_node *np, *np2; + struct device_node *np = of_stdout; const char *sprop = NULL; const uint32_t *iprop; - np = of_find_node_by_path("/chosen"); - if (np) - sprop = of_get_property(np, "stdout-path", NULL); - - if (!np || !sprop) { - of_node_put(np); - np = of_find_node_by_name(NULL, "aliases"); - if (np) - sprop = of_get_property(np, "stdout", NULL); - } - - if (!sprop) { - of_node_put(np); - return 0; - } - /* We don't care what the aliased node is actually called. We only * care if it's compatible with "epapr,hv-byte-channel", because that - * indicates that it's a byte channel node. We use a temporary - * variable, 'np2', because we can't release 'np' until we're done with - * 'sprop'. + * indicates that it's a byte channel node. */ - np2 = of_find_node_by_path(sprop); - of_node_put(np); - np = np2; - if (!np) { - pr_warning("ehv-bc: stdout node '%s' does not exist\n", sprop); - return 0; - } - - /* Is it a byte channel? */ - if (!of_device_is_compatible(np, "epapr,hv-byte-channel")) { - of_node_put(np); + if (!np || !of_device_is_compatible(np, "epapr,hv-byte-channel")) return 0; - } stdout_irq = irq_of_parse_and_map(np, 0); if (stdout_irq == NO_IRQ) { - pr_err("ehv-bc: no 'interrupts' property in %s node\n", sprop); - of_node_put(np); + pr_err("ehv-bc: no 'interrupts' property in %s node\n", np->full_name); return 0; } @@ -167,12 +135,9 @@ static int find_console_handle(void) if (!iprop) { pr_err("ehv-bc: no 'hv-handle' property in %s node\n", np->name); - of_node_put(np); return 0; } stdout_bc = be32_to_cpu(*iprop); - - of_node_put(np); return 1; } diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c index a585079b4b38..a2cc5f834c63 100644 --- a/drivers/tty/hvc/hvc_opal.c +++ b/drivers/tty/hvc/hvc_opal.c @@ -342,22 +342,13 @@ static void udbg_init_opal_common(void) void __init hvc_opal_init_early(void) { - struct device_node *stdout_node = NULL; + struct device_node *stdout_node = of_node_get(of_stdout); const __be32 *termno; - const char *name = NULL; const struct hv_ops *ops; u32 index; - /* find the boot console from /chosen/stdout */ - if (of_chosen) - name = of_get_property(of_chosen, "linux,stdout-path", NULL); - if (name) { - stdout_node = of_find_node_by_path(name); - if (!stdout_node) { - pr_err("hvc_opal: Failed to locate default console!\n"); - return; - } - } else { + /* If the console wasn't in /chosen, try /ibm,opal */ + if (!stdout_node) { struct device_node *opal, *np; /* Current OPAL takeover doesn't provide the stdout diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c index b594abfbf21e..5618b5fc7500 100644 --- a/drivers/tty/hvc/hvc_vio.c +++ b/drivers/tty/hvc/hvc_vio.c @@ -404,42 +404,35 @@ module_exit(hvc_vio_exit); void __init hvc_vio_init_early(void) { - struct device_node *stdout_node; const __be32 *termno; const char *name; const struct hv_ops *ops; /* find the boot console from /chosen/stdout */ - if (!of_chosen) + if (!of_stdout) return; - name = of_get_property(of_chosen, "linux,stdout-path", NULL); - if (name == NULL) - return; - stdout_node = of_find_node_by_path(name); - if (!stdout_node) - return; - name = of_get_property(stdout_node, "name", NULL); + name = of_get_property(of_stdout, "name", NULL); if (!name) { printk(KERN_WARNING "stdout node missing 'name' property!\n"); - goto out; + return; } /* Check if it's a virtual terminal */ if (strncmp(name, "vty", 3) != 0) - goto out; - termno = of_get_property(stdout_node, "reg", NULL); + return; + termno = of_get_property(of_stdout, "reg", NULL); if (termno == NULL) - goto out; + return; hvterm_priv0.termno = of_read_number(termno, 1); spin_lock_init(&hvterm_priv0.buf_lock); hvterm_privs[0] = &hvterm_priv0; /* Check the protocol */ - if (of_device_is_compatible(stdout_node, "hvterm1")) { + if (of_device_is_compatible(of_stdout, "hvterm1")) { hvterm_priv0.proto = HV_PROTOCOL_RAW; ops = &hvterm_raw_ops; } - else if (of_device_is_compatible(stdout_node, "hvterm-protocol")) { + else if (of_device_is_compatible(of_stdout, "hvterm-protocol")) { hvterm_priv0.proto = HV_PROTOCOL_HVSI; ops = &hvterm_hvsi_ops; hvsilib_init(&hvterm_priv0.hvsi, hvc_get_chars, hvc_put_chars, @@ -447,7 +440,7 @@ void __init hvc_vio_init_early(void) /* HVSI, perform the handshake now */ hvsilib_establish(&hvterm_priv0.hvsi); } else - goto out; + return; udbg_putc = udbg_hvc_putc; udbg_getc = udbg_hvc_getc; udbg_getc_poll = udbg_hvc_getc_poll; @@ -456,14 +449,12 @@ void __init hvc_vio_init_early(void) * backend for HVSI, only do udbg */ if (hvterm_priv0.proto == HV_PROTOCOL_HVSI) - goto out; + return; #endif /* Check whether the user has requested a different console. */ if (!strstr(cmd_line, "console=")) add_preferred_console("hvc", 0, NULL); hvc_instantiate(0, 0, ops); -out: - of_node_put(stdout_node); } /* call this from early_init() for a working debug console on diff --git a/include/linux/of.h b/include/linux/of.h index 9d9734056e39..f0d256273c83 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -113,6 +113,7 @@ static inline void of_node_put(struct device_node *node) { } extern struct device_node *of_allnodes; extern struct device_node *of_chosen; extern struct device_node *of_aliases; +extern struct device_node *of_stdout; extern raw_spinlock_t devtree_lock; static inline bool of_have_populated_dt(void) -- cgit v1.2.3 From 7b8278358cc2b453ca6e75eedb3741cdb7e97236 Mon Sep 17 00:00:00 2001 From: Aristeu Rozanski Date: Wed, 18 Jun 2014 11:05:01 -0300 Subject: edac: add DDR4 and RDDR4 Haswell memory controller can make use of DDR4 and Registered DDR4 Cc: tony.luck@intel.com Signed-off-by: Aristeu Rozanski Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/edac_mc_sysfs.c | 4 +++- include/linux/edac.h | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index 01fae8289cf0..a6cd36100663 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -108,7 +108,9 @@ static const char * const mem_types[] = { [MEM_RDDR2] = "Registered-DDR2", [MEM_XDR] = "XDR", [MEM_DDR3] = "Unbuffered-DDR3", - [MEM_RDDR3] = "Registered-DDR3" + [MEM_RDDR3] = "Registered-DDR3", + [MEM_DDR4] = "Unbuffered-DDR4", + [MEM_RDDR4] = "Registered-DDR4" }; static const char * const dev_types[] = { diff --git a/include/linux/edac.h b/include/linux/edac.h index 8e6c20af11a2..e1e68da6f35c 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -194,6 +194,9 @@ static inline char *mc_event_error_type(const unsigned int err_type) * @MEM_DDR3: DDR3 RAM * @MEM_RDDR3: Registered DDR3 RAM * This is a variant of the DDR3 memories. + * @MEM_DDR4: DDR4 RAM + * @MEM_RDDR4: Registered DDR4 RAM + * This is a variant of the DDR4 memories. */ enum mem_type { MEM_EMPTY = 0, @@ -213,6 +216,8 @@ enum mem_type { MEM_XDR, MEM_DDR3, MEM_RDDR3, + MEM_DDR4, + MEM_RDDR4, }; #define MEM_FLAG_EMPTY BIT(MEM_EMPTY) -- cgit v1.2.3 From 0b86dbf675e0170a191a9ca18e5e99fd39a678c0 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 23 Jun 2014 08:44:40 +0100 Subject: Fix 32-bit regression in block device read(2) blkdev_read_iter() wants to cap the iov_iter by the amount of data remaining to the end of device. That's what iov_iter_truncate() is for (trim iter->count if it's above the given limit). So far, so good, but the argument of iov_iter_truncate() is size_t, so on 32bit boxen (in case of a large device) we end up with that upper limit truncated down to 32 bits *before* comparing it with iter->count. Easily fixed by making iov_iter_truncate() take 64bit argument - it does the right thing after such change (we only reach the assignment in there when the current value of iter->count is greater than the limit, i.e. for anything that would get truncated we don't reach the assignment at all) and that argument is not the new value of iter->count - it's an upper limit for such. The overhead of passing u64 is not an issue - the thing is inlined, so callers passing size_t won't pay any penalty. Reported-and-tested-by: Theodore Tso Signed-off-by: Al Viro Tested-by: Alan Cox Tested-by: Bruno Wolff III Signed-off-by: Linus Torvalds --- include/linux/uio.h | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/uio.h b/include/linux/uio.h index e2231e47cec1..d54985e0705e 100644 --- a/include/linux/uio.h +++ b/include/linux/uio.h @@ -94,8 +94,20 @@ static inline size_t iov_iter_count(struct iov_iter *i) return i->count; } -static inline void iov_iter_truncate(struct iov_iter *i, size_t count) +/* + * Cap the iov_iter by given limit; note that the second argument is + * *not* the new size - it's upper limit for such. Passing it a value + * greater than the amount of data in iov_iter is fine - it'll just do + * nothing in that case. + */ +static inline void iov_iter_truncate(struct iov_iter *i, u64 count) { + /* + * count doesn't have to fit in size_t - comparison extends both + * operands to u64 here and any value that would be truncated by + * conversion in assignement is by definition greater than all + * values of size_t, including old i->count. + */ if (i->count > count) i->count = count; } -- cgit v1.2.3 From 11c32d7b6274cb0f554943d65bd4a126c4a86dcd Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 22 May 2014 23:25:14 +0200 Subject: video: move Versatile CLCD helpers This moves the Versatile-specific helper code and panel database down into the drivers/video folder next to the CLCD driver itself, preserving the config symbol but also moving the header to platform data. This is necessary to rid the Integrator of this final inclusion dependency and get us one less user of the plat-versatile folder. Cc: Arnd Bergmann Cc: Jean-Christophe Plagniol-Villard Cc: linux-fbdev@vger.kernel.org Cc: Russell King Acked-by: Tomi Valkeinen Signed-off-by: Linus Walleij --- arch/arm/mach-integrator/Kconfig | 1 - arch/arm/mach-integrator/integrator_cp.c | 3 +- arch/arm/mach-realview/core.c | 2 +- arch/arm/mach-versatile/core.c | 2 +- arch/arm/mach-vexpress/ct-ca9x4.c | 3 +- arch/arm/plat-versatile/Kconfig | 3 - arch/arm/plat-versatile/Makefile | 1 - arch/arm/plat-versatile/clcd.c | 182 --------------------- arch/arm/plat-versatile/include/plat/clcd.h | 9 - drivers/video/fbdev/Kconfig | 7 + drivers/video/fbdev/Makefile | 1 + drivers/video/fbdev/amba-clcd-versatile.c | 182 +++++++++++++++++++++ include/linux/platform_data/video-clcd-versatile.h | 9 + 13 files changed, 203 insertions(+), 202 deletions(-) delete mode 100644 arch/arm/plat-versatile/clcd.c delete mode 100644 arch/arm/plat-versatile/include/plat/clcd.h create mode 100644 drivers/video/fbdev/amba-clcd-versatile.c create mode 100644 include/linux/platform_data/video-clcd-versatile.h (limited to 'include') diff --git a/arch/arm/mach-integrator/Kconfig b/arch/arm/mach-integrator/Kconfig index 64f8e2564a37..c455e974bbfe 100644 --- a/arch/arm/mach-integrator/Kconfig +++ b/arch/arm/mach-integrator/Kconfig @@ -17,7 +17,6 @@ config ARCH_INTEGRATOR_CP bool "Support Integrator/CP platform" select ARCH_CINTEGRATOR select ARM_TIMER_SP804 - select PLAT_VERSATILE_CLCD select SERIAL_AMBA_PL011 if TTY select SERIAL_AMBA_PL011_CONSOLE if TTY select SOC_BUS diff --git a/arch/arm/mach-integrator/integrator_cp.c b/arch/arm/mach-integrator/integrator_cp.c index a938242b0c95..0228165d2d64 100644 --- a/arch/arm/mach-integrator/integrator_cp.c +++ b/arch/arm/mach-integrator/integrator_cp.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -36,8 +37,6 @@ #include #include -#include - #include "hardware.h" #include "cm.h" #include "common.h" diff --git a/arch/arm/mach-realview/core.c b/arch/arm/mach-realview/core.c index 8c1b39a0caa0..850e506926df 100644 --- a/arch/arm/mach-realview/core.c +++ b/arch/arm/mach-realview/core.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -48,7 +49,6 @@ #include #include -#include #include #include "core.h" diff --git a/arch/arm/mach-versatile/core.c b/arch/arm/mach-versatile/core.c index be83ba25f81b..08fb8c89f414 100644 --- a/arch/arm/mach-versatile/core.c +++ b/arch/arm/mach-versatile/core.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -53,7 +54,6 @@ #include #include -#include #include #include "core.h" diff --git a/arch/arm/mach-vexpress/ct-ca9x4.c b/arch/arm/mach-vexpress/ct-ca9x4.c index 86150d7a2e7d..27bea049380a 100644 --- a/arch/arm/mach-vexpress/ct-ca9x4.c +++ b/arch/arm/mach-vexpress/ct-ca9x4.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -29,8 +30,6 @@ #include #include -#include - static struct map_desc ct_ca9x4_io_desc[] __initdata = { { .virtual = V2T_PERIPH, diff --git a/arch/arm/plat-versatile/Kconfig b/arch/arm/plat-versatile/Kconfig index fce41e93b6a4..a301ca2c7d00 100644 --- a/arch/arm/plat-versatile/Kconfig +++ b/arch/arm/plat-versatile/Kconfig @@ -3,9 +3,6 @@ if PLAT_VERSATILE config PLAT_VERSATILE_CLOCK bool -config PLAT_VERSATILE_CLCD - bool - config PLAT_VERSATILE_SCHED_CLOCK def_bool y diff --git a/arch/arm/plat-versatile/Makefile b/arch/arm/plat-versatile/Makefile index 2e0c472958ae..03c4900ac3f4 100644 --- a/arch/arm/plat-versatile/Makefile +++ b/arch/arm/plat-versatile/Makefile @@ -1,6 +1,5 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include obj-$(CONFIG_PLAT_VERSATILE_CLOCK) += clock.o -obj-$(CONFIG_PLAT_VERSATILE_CLCD) += clcd.o obj-$(CONFIG_PLAT_VERSATILE_SCHED_CLOCK) += sched-clock.o obj-$(CONFIG_SMP) += headsmp.o platsmp.o diff --git a/arch/arm/plat-versatile/clcd.c b/arch/arm/plat-versatile/clcd.c deleted file mode 100644 index 6628cc27efc5..000000000000 --- a/arch/arm/plat-versatile/clcd.c +++ /dev/null @@ -1,182 +0,0 @@ -#include -#include -#include -#include -#include - -static struct clcd_panel vga = { - .mode = { - .name = "VGA", - .refresh = 60, - .xres = 640, - .yres = 480, - .pixclock = 39721, - .left_margin = 40, - .right_margin = 24, - .upper_margin = 32, - .lower_margin = 11, - .hsync_len = 96, - .vsync_len = 2, - .sync = 0, - .vmode = FB_VMODE_NONINTERLACED, - }, - .width = -1, - .height = -1, - .tim2 = TIM2_BCD | TIM2_IPC, - .cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP(1), - .caps = CLCD_CAP_5551 | CLCD_CAP_565 | CLCD_CAP_888, - .bpp = 16, -}; - -static struct clcd_panel xvga = { - .mode = { - .name = "XVGA", - .refresh = 60, - .xres = 1024, - .yres = 768, - .pixclock = 15748, - .left_margin = 152, - .right_margin = 48, - .upper_margin = 23, - .lower_margin = 3, - .hsync_len = 104, - .vsync_len = 4, - .sync = 0, - .vmode = FB_VMODE_NONINTERLACED, - }, - .width = -1, - .height = -1, - .tim2 = TIM2_BCD | TIM2_IPC, - .cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP(1), - .caps = CLCD_CAP_5551 | CLCD_CAP_565 | CLCD_CAP_888, - .bpp = 16, -}; - -/* Sanyo TM38QV67A02A - 3.8 inch QVGA (320x240) Color TFT */ -static struct clcd_panel sanyo_tm38qv67a02a = { - .mode = { - .name = "Sanyo TM38QV67A02A", - .refresh = 116, - .xres = 320, - .yres = 240, - .pixclock = 100000, - .left_margin = 6, - .right_margin = 6, - .upper_margin = 5, - .lower_margin = 5, - .hsync_len = 6, - .vsync_len = 6, - .sync = 0, - .vmode = FB_VMODE_NONINTERLACED, - }, - .width = -1, - .height = -1, - .tim2 = TIM2_BCD, - .cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP(1), - .caps = CLCD_CAP_5551, - .bpp = 16, -}; - -static struct clcd_panel sanyo_2_5_in = { - .mode = { - .name = "Sanyo QVGA Portrait", - .refresh = 116, - .xres = 240, - .yres = 320, - .pixclock = 100000, - .left_margin = 20, - .right_margin = 10, - .upper_margin = 2, - .lower_margin = 2, - .hsync_len = 10, - .vsync_len = 2, - .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - .vmode = FB_VMODE_NONINTERLACED, - }, - .width = -1, - .height = -1, - .tim2 = TIM2_IVS | TIM2_IHS | TIM2_IPC, - .cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP(1), - .caps = CLCD_CAP_5551, - .bpp = 16, -}; - -/* Epson L2F50113T00 - 2.2 inch 176x220 Color TFT */ -static struct clcd_panel epson_l2f50113t00 = { - .mode = { - .name = "Epson L2F50113T00", - .refresh = 390, - .xres = 176, - .yres = 220, - .pixclock = 62500, - .left_margin = 3, - .right_margin = 2, - .upper_margin = 1, - .lower_margin = 0, - .hsync_len = 3, - .vsync_len = 2, - .sync = 0, - .vmode = FB_VMODE_NONINTERLACED, - }, - .width = -1, - .height = -1, - .tim2 = TIM2_BCD | TIM2_IPC, - .cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP(1), - .caps = CLCD_CAP_5551, - .bpp = 16, -}; - -static struct clcd_panel *panels[] = { - &vga, - &xvga, - &sanyo_tm38qv67a02a, - &sanyo_2_5_in, - &epson_l2f50113t00, -}; - -struct clcd_panel *versatile_clcd_get_panel(const char *name) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(panels); i++) - if (strcmp(panels[i]->mode.name, name) == 0) - break; - - if (i < ARRAY_SIZE(panels)) - return panels[i]; - - pr_err("CLCD: couldn't get parameters for panel %s\n", name); - - return NULL; -} - -int versatile_clcd_setup_dma(struct clcd_fb *fb, unsigned long framesize) -{ - dma_addr_t dma; - - fb->fb.screen_base = dma_alloc_writecombine(&fb->dev->dev, framesize, - &dma, GFP_KERNEL); - if (!fb->fb.screen_base) { - pr_err("CLCD: unable to map framebuffer\n"); - return -ENOMEM; - } - - fb->fb.fix.smem_start = dma; - fb->fb.fix.smem_len = framesize; - - return 0; -} - -int versatile_clcd_mmap_dma(struct clcd_fb *fb, struct vm_area_struct *vma) -{ - return dma_mmap_writecombine(&fb->dev->dev, vma, - fb->fb.screen_base, - fb->fb.fix.smem_start, - fb->fb.fix.smem_len); -} - -void versatile_clcd_remove_dma(struct clcd_fb *fb) -{ - dma_free_writecombine(&fb->dev->dev, fb->fb.fix.smem_len, - fb->fb.screen_base, fb->fb.fix.smem_start); -} diff --git a/arch/arm/plat-versatile/include/plat/clcd.h b/arch/arm/plat-versatile/include/plat/clcd.h deleted file mode 100644 index 6bb6a1d2019b..000000000000 --- a/arch/arm/plat-versatile/include/plat/clcd.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef PLAT_CLCD_H -#define PLAT_CLCD_H - -struct clcd_panel *versatile_clcd_get_panel(const char *); -int versatile_clcd_setup_dma(struct clcd_fb *, unsigned long); -int versatile_clcd_mmap_dma(struct clcd_fb *, struct vm_area_struct *); -void versatile_clcd_remove_dma(struct clcd_fb *); - -#endif diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index 59c98bfd5a8a..5edc7a054e03 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -290,6 +290,13 @@ config FB_ARMCLCD here and read . The module will be called amba-clcd. +# Helper logic selected only by the ARM Versatile platform family. +config PLAT_VERSATILE_CLCD + depends on FB_ARMCLCD + depends on (PLAT_VERSATILE || ARCH_INTEGRATOR) + default y + bool + config FB_ACORN bool "Acorn VIDC support" depends on (FB = y) && ARM && ARCH_ACORN diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile index 0284f2a12538..0b2090d2e52e 100644 --- a/drivers/video/fbdev/Makefile +++ b/drivers/video/fbdev/Makefile @@ -78,6 +78,7 @@ obj-$(CONFIG_FB_ATMEL) += atmel_lcdfb.o obj-$(CONFIG_FB_PVR2) += pvr2fb.o obj-$(CONFIG_FB_VOODOO1) += sstfb.o obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.o +obj-$(CONFIG_PLAT_VERSATILE_CLCD) += amba-clcd-versatile.o obj-$(CONFIG_FB_GOLDFISH) += goldfishfb.o obj-$(CONFIG_FB_68328) += 68328fb.o obj-$(CONFIG_FB_GBE) += gbefb.o diff --git a/drivers/video/fbdev/amba-clcd-versatile.c b/drivers/video/fbdev/amba-clcd-versatile.c new file mode 100644 index 000000000000..7a8afcd4573e --- /dev/null +++ b/drivers/video/fbdev/amba-clcd-versatile.c @@ -0,0 +1,182 @@ +#include +#include +#include +#include +#include + +static struct clcd_panel vga = { + .mode = { + .name = "VGA", + .refresh = 60, + .xres = 640, + .yres = 480, + .pixclock = 39721, + .left_margin = 40, + .right_margin = 24, + .upper_margin = 32, + .lower_margin = 11, + .hsync_len = 96, + .vsync_len = 2, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED, + }, + .width = -1, + .height = -1, + .tim2 = TIM2_BCD | TIM2_IPC, + .cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP(1), + .caps = CLCD_CAP_5551 | CLCD_CAP_565 | CLCD_CAP_888, + .bpp = 16, +}; + +static struct clcd_panel xvga = { + .mode = { + .name = "XVGA", + .refresh = 60, + .xres = 1024, + .yres = 768, + .pixclock = 15748, + .left_margin = 152, + .right_margin = 48, + .upper_margin = 23, + .lower_margin = 3, + .hsync_len = 104, + .vsync_len = 4, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED, + }, + .width = -1, + .height = -1, + .tim2 = TIM2_BCD | TIM2_IPC, + .cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP(1), + .caps = CLCD_CAP_5551 | CLCD_CAP_565 | CLCD_CAP_888, + .bpp = 16, +}; + +/* Sanyo TM38QV67A02A - 3.8 inch QVGA (320x240) Color TFT */ +static struct clcd_panel sanyo_tm38qv67a02a = { + .mode = { + .name = "Sanyo TM38QV67A02A", + .refresh = 116, + .xres = 320, + .yres = 240, + .pixclock = 100000, + .left_margin = 6, + .right_margin = 6, + .upper_margin = 5, + .lower_margin = 5, + .hsync_len = 6, + .vsync_len = 6, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED, + }, + .width = -1, + .height = -1, + .tim2 = TIM2_BCD, + .cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP(1), + .caps = CLCD_CAP_5551, + .bpp = 16, +}; + +static struct clcd_panel sanyo_2_5_in = { + .mode = { + .name = "Sanyo QVGA Portrait", + .refresh = 116, + .xres = 240, + .yres = 320, + .pixclock = 100000, + .left_margin = 20, + .right_margin = 10, + .upper_margin = 2, + .lower_margin = 2, + .hsync_len = 10, + .vsync_len = 2, + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED, + }, + .width = -1, + .height = -1, + .tim2 = TIM2_IVS | TIM2_IHS | TIM2_IPC, + .cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP(1), + .caps = CLCD_CAP_5551, + .bpp = 16, +}; + +/* Epson L2F50113T00 - 2.2 inch 176x220 Color TFT */ +static struct clcd_panel epson_l2f50113t00 = { + .mode = { + .name = "Epson L2F50113T00", + .refresh = 390, + .xres = 176, + .yres = 220, + .pixclock = 62500, + .left_margin = 3, + .right_margin = 2, + .upper_margin = 1, + .lower_margin = 0, + .hsync_len = 3, + .vsync_len = 2, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED, + }, + .width = -1, + .height = -1, + .tim2 = TIM2_BCD | TIM2_IPC, + .cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP(1), + .caps = CLCD_CAP_5551, + .bpp = 16, +}; + +static struct clcd_panel *panels[] = { + &vga, + &xvga, + &sanyo_tm38qv67a02a, + &sanyo_2_5_in, + &epson_l2f50113t00, +}; + +struct clcd_panel *versatile_clcd_get_panel(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(panels); i++) + if (strcmp(panels[i]->mode.name, name) == 0) + break; + + if (i < ARRAY_SIZE(panels)) + return panels[i]; + + pr_err("CLCD: couldn't get parameters for panel %s\n", name); + + return NULL; +} + +int versatile_clcd_setup_dma(struct clcd_fb *fb, unsigned long framesize) +{ + dma_addr_t dma; + + fb->fb.screen_base = dma_alloc_writecombine(&fb->dev->dev, framesize, + &dma, GFP_KERNEL); + if (!fb->fb.screen_base) { + pr_err("CLCD: unable to map framebuffer\n"); + return -ENOMEM; + } + + fb->fb.fix.smem_start = dma; + fb->fb.fix.smem_len = framesize; + + return 0; +} + +int versatile_clcd_mmap_dma(struct clcd_fb *fb, struct vm_area_struct *vma) +{ + return dma_mmap_writecombine(&fb->dev->dev, vma, + fb->fb.screen_base, + fb->fb.fix.smem_start, + fb->fb.fix.smem_len); +} + +void versatile_clcd_remove_dma(struct clcd_fb *fb) +{ + dma_free_writecombine(&fb->dev->dev, fb->fb.fix.smem_len, + fb->fb.screen_base, fb->fb.fix.smem_start); +} diff --git a/include/linux/platform_data/video-clcd-versatile.h b/include/linux/platform_data/video-clcd-versatile.h new file mode 100644 index 000000000000..6bb6a1d2019b --- /dev/null +++ b/include/linux/platform_data/video-clcd-versatile.h @@ -0,0 +1,9 @@ +#ifndef PLAT_CLCD_H +#define PLAT_CLCD_H + +struct clcd_panel *versatile_clcd_get_panel(const char *); +int versatile_clcd_setup_dma(struct clcd_fb *, unsigned long); +int versatile_clcd_mmap_dma(struct clcd_fb *, struct vm_area_struct *); +void versatile_clcd_remove_dma(struct clcd_fb *); + +#endif -- cgit v1.2.3 From 83e96d443b372611adf19e4171d41deb1d8760cf Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 19 Jun 2014 20:47:14 +0200 Subject: netfilter: log: split family specific code to nf_log_{ip,ip6,common}.c files The plain text logging is currently embedded into the xt_LOG target. In order to be able to use the plain text logging from nft_log, as a first step, this patch moves the family specific code to the following files and Kconfig symbols: 1) net/ipv4/netfilter/nf_log_ip.c: CONFIG_NF_LOG_IPV4 2) net/ipv6/netfilter/nf_log_ip6.c: CONFIG_NF_LOG_IPV6 3) net/netfilter/nf_log_common.c: CONFIG_NF_LOG_COMMON These new modules will be required by xt_LOG and nft_log. This patch is based on original patch from Arturo Borrero Gonzalez. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_log.h | 28 ++ net/ipv4/netfilter/Kconfig | 5 + net/ipv4/netfilter/Makefile | 3 + net/ipv4/netfilter/nf_log_ipv4.c | 385 +++++++++++++++++ net/ipv6/netfilter/Kconfig | 5 + net/ipv6/netfilter/Makefile | 3 + net/ipv6/netfilter/nf_log_ipv6.c | 417 +++++++++++++++++++ net/netfilter/Kconfig | 6 + net/netfilter/Makefile | 3 + net/netfilter/nf_log_common.c | 187 +++++++++ net/netfilter/xt_LOG.c | 879 +-------------------------------------- 11 files changed, 1047 insertions(+), 874 deletions(-) create mode 100644 net/ipv4/netfilter/nf_log_ipv4.c create mode 100644 net/ipv6/netfilter/nf_log_ipv6.c create mode 100644 net/netfilter/nf_log_common.c (limited to 'include') diff --git a/include/net/netfilter/nf_log.h b/include/net/netfilter/nf_log.h index aaec845de106..bba354e78f49 100644 --- a/include/net/netfilter/nf_log.h +++ b/include/net/netfilter/nf_log.h @@ -78,4 +78,32 @@ struct nf_log_buf *nf_log_buf_open(void); __printf(2, 3) int nf_log_buf_add(struct nf_log_buf *m, const char *f, ...); void nf_log_buf_close(struct nf_log_buf *m); +void nf_log_ip_packet(struct net *net, u_int8_t pf, + unsigned int hooknum, const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct nf_loginfo *loginfo, + const char *prefix); + +void nf_log_ip6_packet(struct net *net, u_int8_t pf, + unsigned int hooknum, const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct nf_loginfo *loginfo, + const char *prefix); + +/* common logging functions */ +int nf_log_dump_udp_header(struct nf_log_buf *m, const struct sk_buff *skb, + u8 proto, int fragment, unsigned int offset); +int nf_log_dump_tcp_header(struct nf_log_buf *m, const struct sk_buff *skb, + u8 proto, int fragment, unsigned int offset, + unsigned int logflags); +void nf_log_dump_sk_uid_gid(struct nf_log_buf *m, struct sock *sk); +void nf_log_dump_packet_common(struct nf_log_buf *m, u_int8_t pf, + unsigned int hooknum, const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct nf_loginfo *loginfo, + const char *prefix); + #endif /* _NF_LOG_H */ diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index 730faacbc5f8..0c980293d225 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig @@ -159,6 +159,11 @@ config IP_NF_TARGET_SYNPROXY To compile it as a module, choose M here. If unsure, say N. +config NF_LOG_IPV4 + tristate "IPv4 packet logging" + default m if NETFILTER_ADVANCED=n + select NF_LOG_COMMON + # NAT + specific targets: nf_conntrack config NF_NAT_IPV4 tristate "IPv4 NAT" diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index 90b82405331e..730e0c1ead90 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile @@ -19,6 +19,9 @@ obj-$(CONFIG_NF_NAT_IPV4) += nf_nat_ipv4.o # defrag obj-$(CONFIG_NF_DEFRAG_IPV4) += nf_defrag_ipv4.o +# logging +obj-$(CONFIG_NF_LOG_IPV4) += nf_log_ipv4.o + # NAT helpers (nf_conntrack) obj-$(CONFIG_NF_NAT_H323) += nf_nat_h323.o obj-$(CONFIG_NF_NAT_PPTP) += nf_nat_pptp.o diff --git a/net/ipv4/netfilter/nf_log_ipv4.c b/net/ipv4/netfilter/nf_log_ipv4.c new file mode 100644 index 000000000000..7e69a401a29e --- /dev/null +++ b/net/ipv4/netfilter/nf_log_ipv4.c @@ -0,0 +1,385 @@ +/* (C) 1999-2001 Paul `Rusty' Russell + * (C) 2002-2004 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 + +static struct nf_loginfo default_loginfo = { + .type = NF_LOG_TYPE_LOG, + .u = { + .log = { + .level = 5, + .logflags = NF_LOG_MASK, + }, + }, +}; + +/* One level of recursion won't kill us */ +static void dump_ipv4_packet(struct nf_log_buf *m, + const struct nf_loginfo *info, + const struct sk_buff *skb, unsigned int iphoff) +{ + struct iphdr _iph; + const struct iphdr *ih; + unsigned int logflags; + + if (info->type == NF_LOG_TYPE_LOG) + logflags = info->u.log.logflags; + else + logflags = NF_LOG_MASK; + + ih = skb_header_pointer(skb, iphoff, sizeof(_iph), &_iph); + if (ih == NULL) { + nf_log_buf_add(m, "TRUNCATED"); + return; + } + + /* Important fields: + * TOS, len, DF/MF, fragment offset, TTL, src, dst, options. */ + /* Max length: 40 "SRC=255.255.255.255 DST=255.255.255.255 " */ + nf_log_buf_add(m, "SRC=%pI4 DST=%pI4 ", &ih->saddr, &ih->daddr); + + /* Max length: 46 "LEN=65535 TOS=0xFF PREC=0xFF TTL=255 ID=65535 " */ + nf_log_buf_add(m, "LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ", + ntohs(ih->tot_len), ih->tos & IPTOS_TOS_MASK, + ih->tos & IPTOS_PREC_MASK, ih->ttl, ntohs(ih->id)); + + /* Max length: 6 "CE DF MF " */ + if (ntohs(ih->frag_off) & IP_CE) + nf_log_buf_add(m, "CE "); + if (ntohs(ih->frag_off) & IP_DF) + nf_log_buf_add(m, "DF "); + if (ntohs(ih->frag_off) & IP_MF) + nf_log_buf_add(m, "MF "); + + /* Max length: 11 "FRAG:65535 " */ + if (ntohs(ih->frag_off) & IP_OFFSET) + nf_log_buf_add(m, "FRAG:%u ", ntohs(ih->frag_off) & IP_OFFSET); + + if ((logflags & XT_LOG_IPOPT) && + ih->ihl * 4 > sizeof(struct iphdr)) { + const unsigned char *op; + unsigned char _opt[4 * 15 - sizeof(struct iphdr)]; + unsigned int i, optsize; + + optsize = ih->ihl * 4 - sizeof(struct iphdr); + op = skb_header_pointer(skb, iphoff+sizeof(_iph), + optsize, _opt); + if (op == NULL) { + nf_log_buf_add(m, "TRUNCATED"); + return; + } + + /* Max length: 127 "OPT (" 15*4*2chars ") " */ + nf_log_buf_add(m, "OPT ("); + for (i = 0; i < optsize; i++) + nf_log_buf_add(m, "%02X", op[i]); + nf_log_buf_add(m, ") "); + } + + switch (ih->protocol) { + case IPPROTO_TCP: + if (nf_log_dump_tcp_header(m, skb, ih->protocol, + ntohs(ih->frag_off) & IP_OFFSET, + iphoff+ih->ihl*4, logflags)) + return; + break; + case IPPROTO_UDP: + case IPPROTO_UDPLITE: + if (nf_log_dump_udp_header(m, skb, ih->protocol, + ntohs(ih->frag_off) & IP_OFFSET, + iphoff+ih->ihl*4)) + return; + break; + case IPPROTO_ICMP: { + struct icmphdr _icmph; + const struct icmphdr *ich; + static const size_t required_len[NR_ICMP_TYPES+1] + = { [ICMP_ECHOREPLY] = 4, + [ICMP_DEST_UNREACH] + = 8 + sizeof(struct iphdr), + [ICMP_SOURCE_QUENCH] + = 8 + sizeof(struct iphdr), + [ICMP_REDIRECT] + = 8 + sizeof(struct iphdr), + [ICMP_ECHO] = 4, + [ICMP_TIME_EXCEEDED] + = 8 + sizeof(struct iphdr), + [ICMP_PARAMETERPROB] + = 8 + sizeof(struct iphdr), + [ICMP_TIMESTAMP] = 20, + [ICMP_TIMESTAMPREPLY] = 20, + [ICMP_ADDRESS] = 12, + [ICMP_ADDRESSREPLY] = 12 }; + + /* Max length: 11 "PROTO=ICMP " */ + nf_log_buf_add(m, "PROTO=ICMP "); + + if (ntohs(ih->frag_off) & IP_OFFSET) + break; + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + ich = skb_header_pointer(skb, iphoff + ih->ihl * 4, + sizeof(_icmph), &_icmph); + if (ich == NULL) { + nf_log_buf_add(m, "INCOMPLETE [%u bytes] ", + skb->len - iphoff - ih->ihl*4); + break; + } + + /* Max length: 18 "TYPE=255 CODE=255 " */ + nf_log_buf_add(m, "TYPE=%u CODE=%u ", ich->type, ich->code); + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + if (ich->type <= NR_ICMP_TYPES && + required_len[ich->type] && + skb->len-iphoff-ih->ihl*4 < required_len[ich->type]) { + nf_log_buf_add(m, "INCOMPLETE [%u bytes] ", + skb->len - iphoff - ih->ihl*4); + break; + } + + switch (ich->type) { + case ICMP_ECHOREPLY: + case ICMP_ECHO: + /* Max length: 19 "ID=65535 SEQ=65535 " */ + nf_log_buf_add(m, "ID=%u SEQ=%u ", + ntohs(ich->un.echo.id), + ntohs(ich->un.echo.sequence)); + break; + + case ICMP_PARAMETERPROB: + /* Max length: 14 "PARAMETER=255 " */ + nf_log_buf_add(m, "PARAMETER=%u ", + ntohl(ich->un.gateway) >> 24); + break; + case ICMP_REDIRECT: + /* Max length: 24 "GATEWAY=255.255.255.255 " */ + nf_log_buf_add(m, "GATEWAY=%pI4 ", &ich->un.gateway); + /* Fall through */ + case ICMP_DEST_UNREACH: + case ICMP_SOURCE_QUENCH: + case ICMP_TIME_EXCEEDED: + /* Max length: 3+maxlen */ + if (!iphoff) { /* Only recurse once. */ + nf_log_buf_add(m, "["); + dump_ipv4_packet(m, info, skb, + iphoff + ih->ihl*4+sizeof(_icmph)); + nf_log_buf_add(m, "] "); + } + + /* Max length: 10 "MTU=65535 " */ + if (ich->type == ICMP_DEST_UNREACH && + ich->code == ICMP_FRAG_NEEDED) { + nf_log_buf_add(m, "MTU=%u ", + ntohs(ich->un.frag.mtu)); + } + } + break; + } + /* Max Length */ + case IPPROTO_AH: { + struct ip_auth_hdr _ahdr; + const struct ip_auth_hdr *ah; + + if (ntohs(ih->frag_off) & IP_OFFSET) + break; + + /* Max length: 9 "PROTO=AH " */ + nf_log_buf_add(m, "PROTO=AH "); + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + ah = skb_header_pointer(skb, iphoff+ih->ihl*4, + sizeof(_ahdr), &_ahdr); + if (ah == NULL) { + nf_log_buf_add(m, "INCOMPLETE [%u bytes] ", + skb->len - iphoff - ih->ihl*4); + break; + } + + /* Length: 15 "SPI=0xF1234567 " */ + nf_log_buf_add(m, "SPI=0x%x ", ntohl(ah->spi)); + break; + } + case IPPROTO_ESP: { + struct ip_esp_hdr _esph; + const struct ip_esp_hdr *eh; + + /* Max length: 10 "PROTO=ESP " */ + nf_log_buf_add(m, "PROTO=ESP "); + + if (ntohs(ih->frag_off) & IP_OFFSET) + break; + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + eh = skb_header_pointer(skb, iphoff+ih->ihl*4, + sizeof(_esph), &_esph); + if (eh == NULL) { + nf_log_buf_add(m, "INCOMPLETE [%u bytes] ", + skb->len - iphoff - ih->ihl*4); + break; + } + + /* Length: 15 "SPI=0xF1234567 " */ + nf_log_buf_add(m, "SPI=0x%x ", ntohl(eh->spi)); + break; + } + /* Max length: 10 "PROTO 255 " */ + default: + nf_log_buf_add(m, "PROTO=%u ", ih->protocol); + } + + /* Max length: 15 "UID=4294967295 " */ + if ((logflags & XT_LOG_UID) && !iphoff) + nf_log_dump_sk_uid_gid(m, skb->sk); + + /* Max length: 16 "MARK=0xFFFFFFFF " */ + if (!iphoff && skb->mark) + nf_log_buf_add(m, "MARK=0x%x ", skb->mark); + + /* Proto Max log string length */ + /* IP: 40+46+6+11+127 = 230 */ + /* TCP: 10+max(25,20+30+13+9+32+11+127) = 252 */ + /* UDP: 10+max(25,20) = 35 */ + /* UDPLITE: 14+max(25,20) = 39 */ + /* ICMP: 11+max(25, 18+25+max(19,14,24+3+n+10,3+n+10)) = 91+n */ + /* ESP: 10+max(25)+15 = 50 */ + /* AH: 9+max(25)+15 = 49 */ + /* unknown: 10 */ + + /* (ICMP allows recursion one level deep) */ + /* maxlen = IP + ICMP + IP + max(TCP,UDP,ICMP,unknown) */ + /* maxlen = 230+ 91 + 230 + 252 = 803 */ +} + +static void dump_ipv4_mac_header(struct nf_log_buf *m, + const struct nf_loginfo *info, + const struct sk_buff *skb) +{ + struct net_device *dev = skb->dev; + unsigned int logflags = 0; + + if (info->type == NF_LOG_TYPE_LOG) + logflags = info->u.log.logflags; + + if (!(logflags & XT_LOG_MACDECODE)) + goto fallback; + + switch (dev->type) { + case ARPHRD_ETHER: + nf_log_buf_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ", + eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, + ntohs(eth_hdr(skb)->h_proto)); + return; + default: + break; + } + +fallback: + nf_log_buf_add(m, "MAC="); + if (dev->hard_header_len && + skb->mac_header != skb->network_header) { + const unsigned char *p = skb_mac_header(skb); + unsigned int i; + + nf_log_buf_add(m, "%02x", *p++); + for (i = 1; i < dev->hard_header_len; i++, p++) + nf_log_buf_add(m, ":%02x", *p); + } + nf_log_buf_add(m, " "); +} + +void nf_log_ip_packet(struct net *net, u_int8_t pf, + unsigned int hooknum, const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct nf_loginfo *loginfo, + const char *prefix) +{ + struct nf_log_buf *m; + + /* FIXME: Disabled from containers until syslog ns is supported */ + if (!net_eq(net, &init_net)) + return; + + m = nf_log_buf_open(); + + if (!loginfo) + loginfo = &default_loginfo; + + nf_log_dump_packet_common(m, pf, hooknum, skb, in, + out, loginfo, prefix); + + if (in != NULL) + dump_ipv4_mac_header(m, loginfo, skb); + + dump_ipv4_packet(m, loginfo, skb, 0); + + nf_log_buf_close(m); +} +EXPORT_SYMBOL_GPL(nf_log_ip_packet); + +static struct nf_logger nf_ip_logger __read_mostly = { + .name = "nf_log_ipv4", + .type = NF_LOG_TYPE_LOG, + .logfn = nf_log_ip_packet, + .me = THIS_MODULE, +}; + +static int __net_init nf_log_ipv4_net_init(struct net *net) +{ + nf_log_set(net, NFPROTO_IPV4, &nf_ip_logger); + return 0; +} + +static void __net_exit nf_log_ipv4_net_exit(struct net *net) +{ + nf_log_unset(net, &nf_ip_logger); +} + +static struct pernet_operations nf_log_ipv4_net_ops = { + .init = nf_log_ipv4_net_init, + .exit = nf_log_ipv4_net_exit, +}; + +static int __init nf_log_ipv4_init(void) +{ + int ret; + + ret = register_pernet_subsys(&nf_log_ipv4_net_ops); + if (ret < 0) + return ret; + + nf_log_register(NFPROTO_IPV4, &nf_ip_logger); + return 0; +} + +static void __exit nf_log_ipv4_exit(void) +{ + unregister_pernet_subsys(&nf_log_ipv4_net_ops); + nf_log_unregister(&nf_ip_logger); +} + +module_init(nf_log_ipv4_init); +module_exit(nf_log_ipv4_exit); + +MODULE_AUTHOR("Netfilter Core Team "); +MODULE_DESCRIPTION("Netfilter IPv4 packet logging"); +MODULE_LICENSE("GPL"); diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig index 4bff1f297e39..1537130e9f5b 100644 --- a/net/ipv6/netfilter/Kconfig +++ b/net/ipv6/netfilter/Kconfig @@ -227,6 +227,11 @@ config IP6_NF_SECURITY If unsure, say N. +config NF_LOG_IPV6 + tristate "IPv6 packet logging" + depends on NETFILTER_ADVANCED + select NF_LOG_COMMON + config NF_NAT_IPV6 tristate "IPv6 NAT" depends on NF_CONNTRACK_IPV6 diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile index 70d3dd66f2cd..c0b263104ed2 100644 --- a/net/ipv6/netfilter/Makefile +++ b/net/ipv6/netfilter/Makefile @@ -23,6 +23,9 @@ obj-$(CONFIG_NF_NAT_IPV6) += nf_nat_ipv6.o nf_defrag_ipv6-y := nf_defrag_ipv6_hooks.o nf_conntrack_reasm.o obj-$(CONFIG_NF_DEFRAG_IPV6) += nf_defrag_ipv6.o +# logging +obj-$(CONFIG_NF_LOG_IPV6) += nf_log_ipv6.o + # nf_tables obj-$(CONFIG_NF_TABLES_IPV6) += nf_tables_ipv6.o obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV6) += nft_chain_route_ipv6.o diff --git a/net/ipv6/netfilter/nf_log_ipv6.c b/net/ipv6/netfilter/nf_log_ipv6.c new file mode 100644 index 000000000000..804060946d2b --- /dev/null +++ b/net/ipv6/netfilter/nf_log_ipv6.c @@ -0,0 +1,417 @@ +/* (C) 1999-2001 Paul `Rusty' Russell + * (C) 2002-2004 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 + +static struct nf_loginfo default_loginfo = { + .type = NF_LOG_TYPE_LOG, + .u = { + .log = { + .level = 5, + .logflags = NF_LOG_MASK, + }, + }, +}; + +/* One level of recursion won't kill us */ +static void dump_ipv6_packet(struct nf_log_buf *m, + const struct nf_loginfo *info, + const struct sk_buff *skb, unsigned int ip6hoff, + int recurse) +{ + u_int8_t currenthdr; + int fragment; + struct ipv6hdr _ip6h; + const struct ipv6hdr *ih; + unsigned int ptr; + unsigned int hdrlen = 0; + unsigned int logflags; + + if (info->type == NF_LOG_TYPE_LOG) + logflags = info->u.log.logflags; + else + logflags = NF_LOG_MASK; + + ih = skb_header_pointer(skb, ip6hoff, sizeof(_ip6h), &_ip6h); + if (ih == NULL) { + nf_log_buf_add(m, "TRUNCATED"); + return; + } + + /* Max length: 88 "SRC=0000.0000.0000.0000.0000.0000.0000.0000 DST=0000.0000.0000.0000.0000.0000.0000.0000 " */ + nf_log_buf_add(m, "SRC=%pI6 DST=%pI6 ", &ih->saddr, &ih->daddr); + + /* Max length: 44 "LEN=65535 TC=255 HOPLIMIT=255 FLOWLBL=FFFFF " */ + nf_log_buf_add(m, "LEN=%Zu TC=%u HOPLIMIT=%u FLOWLBL=%u ", + ntohs(ih->payload_len) + sizeof(struct ipv6hdr), + (ntohl(*(__be32 *)ih) & 0x0ff00000) >> 20, + ih->hop_limit, + (ntohl(*(__be32 *)ih) & 0x000fffff)); + + fragment = 0; + ptr = ip6hoff + sizeof(struct ipv6hdr); + currenthdr = ih->nexthdr; + while (currenthdr != NEXTHDR_NONE && ip6t_ext_hdr(currenthdr)) { + struct ipv6_opt_hdr _hdr; + const struct ipv6_opt_hdr *hp; + + hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr); + if (hp == NULL) { + nf_log_buf_add(m, "TRUNCATED"); + return; + } + + /* Max length: 48 "OPT (...) " */ + if (logflags & XT_LOG_IPOPT) + nf_log_buf_add(m, "OPT ( "); + + switch (currenthdr) { + case IPPROTO_FRAGMENT: { + struct frag_hdr _fhdr; + const struct frag_hdr *fh; + + nf_log_buf_add(m, "FRAG:"); + fh = skb_header_pointer(skb, ptr, sizeof(_fhdr), + &_fhdr); + if (fh == NULL) { + nf_log_buf_add(m, "TRUNCATED "); + return; + } + + /* Max length: 6 "65535 " */ + nf_log_buf_add(m, "%u ", ntohs(fh->frag_off) & 0xFFF8); + + /* Max length: 11 "INCOMPLETE " */ + if (fh->frag_off & htons(0x0001)) + nf_log_buf_add(m, "INCOMPLETE "); + + nf_log_buf_add(m, "ID:%08x ", + ntohl(fh->identification)); + + if (ntohs(fh->frag_off) & 0xFFF8) + fragment = 1; + + hdrlen = 8; + + break; + } + case IPPROTO_DSTOPTS: + case IPPROTO_ROUTING: + case IPPROTO_HOPOPTS: + if (fragment) { + if (logflags & XT_LOG_IPOPT) + nf_log_buf_add(m, ")"); + return; + } + hdrlen = ipv6_optlen(hp); + break; + /* Max Length */ + case IPPROTO_AH: + if (logflags & XT_LOG_IPOPT) { + struct ip_auth_hdr _ahdr; + const struct ip_auth_hdr *ah; + + /* Max length: 3 "AH " */ + nf_log_buf_add(m, "AH "); + + if (fragment) { + nf_log_buf_add(m, ")"); + return; + } + + ah = skb_header_pointer(skb, ptr, sizeof(_ahdr), + &_ahdr); + if (ah == NULL) { + /* + * Max length: 26 "INCOMPLETE [65535 + * bytes] )" + */ + nf_log_buf_add(m, "INCOMPLETE [%u bytes] )", + skb->len - ptr); + return; + } + + /* Length: 15 "SPI=0xF1234567 */ + nf_log_buf_add(m, "SPI=0x%x ", ntohl(ah->spi)); + + } + + hdrlen = (hp->hdrlen+2)<<2; + break; + case IPPROTO_ESP: + if (logflags & XT_LOG_IPOPT) { + struct ip_esp_hdr _esph; + const struct ip_esp_hdr *eh; + + /* Max length: 4 "ESP " */ + nf_log_buf_add(m, "ESP "); + + if (fragment) { + nf_log_buf_add(m, ")"); + return; + } + + /* + * Max length: 26 "INCOMPLETE [65535 bytes] )" + */ + eh = skb_header_pointer(skb, ptr, sizeof(_esph), + &_esph); + if (eh == NULL) { + nf_log_buf_add(m, "INCOMPLETE [%u bytes] )", + skb->len - ptr); + return; + } + + /* Length: 16 "SPI=0xF1234567 )" */ + nf_log_buf_add(m, "SPI=0x%x )", + ntohl(eh->spi)); + } + return; + default: + /* Max length: 20 "Unknown Ext Hdr 255" */ + nf_log_buf_add(m, "Unknown Ext Hdr %u", currenthdr); + return; + } + if (logflags & XT_LOG_IPOPT) + nf_log_buf_add(m, ") "); + + currenthdr = hp->nexthdr; + ptr += hdrlen; + } + + switch (currenthdr) { + case IPPROTO_TCP: + if (nf_log_dump_tcp_header(m, skb, currenthdr, fragment, + ptr, logflags)) + return; + break; + case IPPROTO_UDP: + case IPPROTO_UDPLITE: + if (nf_log_dump_udp_header(m, skb, currenthdr, fragment, ptr)) + return; + break; + case IPPROTO_ICMPV6: { + struct icmp6hdr _icmp6h; + const struct icmp6hdr *ic; + + /* Max length: 13 "PROTO=ICMPv6 " */ + nf_log_buf_add(m, "PROTO=ICMPv6 "); + + if (fragment) + break; + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + ic = skb_header_pointer(skb, ptr, sizeof(_icmp6h), &_icmp6h); + if (ic == NULL) { + nf_log_buf_add(m, "INCOMPLETE [%u bytes] ", + skb->len - ptr); + return; + } + + /* Max length: 18 "TYPE=255 CODE=255 " */ + nf_log_buf_add(m, "TYPE=%u CODE=%u ", + ic->icmp6_type, ic->icmp6_code); + + switch (ic->icmp6_type) { + case ICMPV6_ECHO_REQUEST: + case ICMPV6_ECHO_REPLY: + /* Max length: 19 "ID=65535 SEQ=65535 " */ + nf_log_buf_add(m, "ID=%u SEQ=%u ", + ntohs(ic->icmp6_identifier), + ntohs(ic->icmp6_sequence)); + break; + case ICMPV6_MGM_QUERY: + case ICMPV6_MGM_REPORT: + case ICMPV6_MGM_REDUCTION: + break; + + case ICMPV6_PARAMPROB: + /* Max length: 17 "POINTER=ffffffff " */ + nf_log_buf_add(m, "POINTER=%08x ", + ntohl(ic->icmp6_pointer)); + /* Fall through */ + case ICMPV6_DEST_UNREACH: + case ICMPV6_PKT_TOOBIG: + case ICMPV6_TIME_EXCEED: + /* Max length: 3+maxlen */ + if (recurse) { + nf_log_buf_add(m, "["); + dump_ipv6_packet(m, info, skb, + ptr + sizeof(_icmp6h), 0); + nf_log_buf_add(m, "] "); + } + + /* Max length: 10 "MTU=65535 " */ + if (ic->icmp6_type == ICMPV6_PKT_TOOBIG) { + nf_log_buf_add(m, "MTU=%u ", + ntohl(ic->icmp6_mtu)); + } + } + break; + } + /* Max length: 10 "PROTO=255 " */ + default: + nf_log_buf_add(m, "PROTO=%u ", currenthdr); + } + + /* Max length: 15 "UID=4294967295 " */ + if ((logflags & XT_LOG_UID) && recurse) + nf_log_dump_sk_uid_gid(m, skb->sk); + + /* Max length: 16 "MARK=0xFFFFFFFF " */ + if (recurse && skb->mark) + nf_log_buf_add(m, "MARK=0x%x ", skb->mark); +} + +static void dump_ipv6_mac_header(struct nf_log_buf *m, + const struct nf_loginfo *info, + const struct sk_buff *skb) +{ + struct net_device *dev = skb->dev; + unsigned int logflags = 0; + + if (info->type == NF_LOG_TYPE_LOG) + logflags = info->u.log.logflags; + + if (!(logflags & XT_LOG_MACDECODE)) + goto fallback; + + switch (dev->type) { + case ARPHRD_ETHER: + nf_log_buf_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ", + eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, + ntohs(eth_hdr(skb)->h_proto)); + return; + default: + break; + } + +fallback: + nf_log_buf_add(m, "MAC="); + if (dev->hard_header_len && + skb->mac_header != skb->network_header) { + const unsigned char *p = skb_mac_header(skb); + unsigned int len = dev->hard_header_len; + unsigned int i; + + if (dev->type == ARPHRD_SIT) { + p -= ETH_HLEN; + + if (p < skb->head) + p = NULL; + } + + if (p != NULL) { + nf_log_buf_add(m, "%02x", *p++); + for (i = 1; i < len; i++) + nf_log_buf_add(m, ":%02x", *p++); + } + nf_log_buf_add(m, " "); + + if (dev->type == ARPHRD_SIT) { + const struct iphdr *iph = + (struct iphdr *)skb_mac_header(skb); + nf_log_buf_add(m, "TUNNEL=%pI4->%pI4 ", &iph->saddr, + &iph->daddr); + } + } else { + nf_log_buf_add(m, " "); + } +} + +void nf_log_ip6_packet(struct net *net, u_int8_t pf, + unsigned int hooknum, const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct nf_loginfo *loginfo, + const char *prefix) +{ + struct nf_log_buf *m; + + /* FIXME: Disabled from containers until syslog ns is supported */ + if (!net_eq(net, &init_net)) + return; + + m = nf_log_buf_open(); + + if (!loginfo) + loginfo = &default_loginfo; + + nf_log_dump_packet_common(m, pf, hooknum, skb, in, out, + loginfo, prefix); + + if (in != NULL) + dump_ipv6_mac_header(m, loginfo, skb); + + dump_ipv6_packet(m, loginfo, skb, skb_network_offset(skb), 1); + + nf_log_buf_close(m); +} +EXPORT_SYMBOL_GPL(nf_log_ip6_packet); + +static struct nf_logger nf_ip6_logger __read_mostly = { + .name = "nf_log_ipv6", + .type = NF_LOG_TYPE_LOG, + .logfn = nf_log_ip6_packet, + .me = THIS_MODULE, +}; + +static int __net_init nf_log_ipv6_net_init(struct net *net) +{ + nf_log_set(net, NFPROTO_IPV6, &nf_ip6_logger); + return 0; +} + +static void __net_exit nf_log_ipv6_net_exit(struct net *net) +{ + nf_log_unset(net, &nf_ip6_logger); +} + +static struct pernet_operations nf_log_ipv6_net_ops = { + .init = nf_log_ipv6_net_init, + .exit = nf_log_ipv6_net_exit, +}; + +static int __init nf_log_ipv6_init(void) +{ + int ret; + + ret = register_pernet_subsys(&nf_log_ipv6_net_ops); + if (ret < 0) + return ret; + + nf_log_register(NFPROTO_IPV6, &nf_ip6_logger); + return 0; +} + +static void __exit nf_log_ipv6_exit(void) +{ + unregister_pernet_subsys(&nf_log_ipv6_net_ops); + nf_log_unregister(&nf_ip6_logger); +} + +module_init(nf_log_ipv6_init); +module_exit(nf_log_ipv6_exit); + +MODULE_AUTHOR("Netfilter Core Team "); +MODULE_DESCRIPTION("Netfilter IPv4 packet logging"); +MODULE_LICENSE("GPL"); diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index e9410d17619d..f17b273c9082 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -359,6 +359,9 @@ config NETFILTER_NETLINK_QUEUE_CT If this option is enabled, NFQUEUE can include Connection Tracking information together with the packet is the enqueued via NFNETLINK. +config NF_LOG_COMMON + tristate + config NF_NAT tristate @@ -744,6 +747,9 @@ config NETFILTER_XT_TARGET_LED config NETFILTER_XT_TARGET_LOG tristate "LOG target support" + select NF_LOG + select NF_LOG_IPV4 + select NF_LOG_IPV6 default m if NETFILTER_ADVANCED=n help This option adds a `LOG' target, which allows you to create rules in diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index bffdad774da7..8308624a406a 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -47,6 +47,9 @@ obj-$(CONFIG_NF_CONNTRACK_TFTP) += nf_conntrack_tftp.o nf_nat-y := nf_nat_core.o nf_nat_proto_unknown.o nf_nat_proto_common.o \ nf_nat_proto_udp.o nf_nat_proto_tcp.o nf_nat_helper.o +# generic transport layer logging +obj-$(CONFIG_NF_LOG_COMMON) += nf_log_common.o + obj-$(CONFIG_NF_NAT) += nf_nat.o # NAT protocols (nf_nat) diff --git a/net/netfilter/nf_log_common.c b/net/netfilter/nf_log_common.c new file mode 100644 index 000000000000..eeb8ef4ff1a3 --- /dev/null +++ b/net/netfilter/nf_log_common.c @@ -0,0 +1,187 @@ +/* (C) 1999-2001 Paul `Rusty' Russell + * (C) 2002-2004 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 + +int nf_log_dump_udp_header(struct nf_log_buf *m, const struct sk_buff *skb, + u8 proto, int fragment, unsigned int offset) +{ + struct udphdr _udph; + const struct udphdr *uh; + + if (proto == IPPROTO_UDP) + /* Max length: 10 "PROTO=UDP " */ + nf_log_buf_add(m, "PROTO=UDP "); + else /* Max length: 14 "PROTO=UDPLITE " */ + nf_log_buf_add(m, "PROTO=UDPLITE "); + + if (fragment) + goto out; + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph); + if (uh == NULL) { + nf_log_buf_add(m, "INCOMPLETE [%u bytes] ", skb->len - offset); + + return 1; + } + + /* Max length: 20 "SPT=65535 DPT=65535 " */ + nf_log_buf_add(m, "SPT=%u DPT=%u LEN=%u ", + ntohs(uh->source), ntohs(uh->dest), ntohs(uh->len)); + +out: + return 0; +} +EXPORT_SYMBOL_GPL(nf_log_dump_udp_header); + +int nf_log_dump_tcp_header(struct nf_log_buf *m, const struct sk_buff *skb, + u8 proto, int fragment, unsigned int offset, + unsigned int logflags) +{ + struct tcphdr _tcph; + const struct tcphdr *th; + + /* Max length: 10 "PROTO=TCP " */ + nf_log_buf_add(m, "PROTO=TCP "); + + if (fragment) + return 0; + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph); + if (th == NULL) { + nf_log_buf_add(m, "INCOMPLETE [%u bytes] ", skb->len - offset); + return 1; + } + + /* Max length: 20 "SPT=65535 DPT=65535 " */ + nf_log_buf_add(m, "SPT=%u DPT=%u ", + ntohs(th->source), ntohs(th->dest)); + /* Max length: 30 "SEQ=4294967295 ACK=4294967295 " */ + if (logflags & XT_LOG_TCPSEQ) { + nf_log_buf_add(m, "SEQ=%u ACK=%u ", + ntohl(th->seq), ntohl(th->ack_seq)); + } + + /* Max length: 13 "WINDOW=65535 " */ + nf_log_buf_add(m, "WINDOW=%u ", ntohs(th->window)); + /* Max length: 9 "RES=0x3C " */ + nf_log_buf_add(m, "RES=0x%02x ", (u_int8_t)(ntohl(tcp_flag_word(th) & + TCP_RESERVED_BITS) >> 22)); + /* Max length: 32 "CWR ECE URG ACK PSH RST SYN FIN " */ + if (th->cwr) + nf_log_buf_add(m, "CWR "); + if (th->ece) + nf_log_buf_add(m, "ECE "); + if (th->urg) + nf_log_buf_add(m, "URG "); + if (th->ack) + nf_log_buf_add(m, "ACK "); + if (th->psh) + nf_log_buf_add(m, "PSH "); + if (th->rst) + nf_log_buf_add(m, "RST "); + if (th->syn) + nf_log_buf_add(m, "SYN "); + if (th->fin) + nf_log_buf_add(m, "FIN "); + /* Max length: 11 "URGP=65535 " */ + nf_log_buf_add(m, "URGP=%u ", ntohs(th->urg_ptr)); + + if ((logflags & XT_LOG_TCPOPT) && th->doff*4 > sizeof(struct tcphdr)) { + u_int8_t _opt[60 - sizeof(struct tcphdr)]; + const u_int8_t *op; + unsigned int i; + unsigned int optsize = th->doff*4 - sizeof(struct tcphdr); + + op = skb_header_pointer(skb, offset + sizeof(struct tcphdr), + optsize, _opt); + if (op == NULL) { + nf_log_buf_add(m, "OPT (TRUNCATED)"); + return 1; + } + + /* Max length: 127 "OPT (" 15*4*2chars ") " */ + nf_log_buf_add(m, "OPT ("); + for (i = 0; i < optsize; i++) + nf_log_buf_add(m, "%02X", op[i]); + + nf_log_buf_add(m, ") "); + } + + return 0; +} +EXPORT_SYMBOL_GPL(nf_log_dump_tcp_header); + +void nf_log_dump_sk_uid_gid(struct nf_log_buf *m, struct sock *sk) +{ + if (!sk || sk->sk_state == TCP_TIME_WAIT) + return; + + read_lock_bh(&sk->sk_callback_lock); + if (sk->sk_socket && sk->sk_socket->file) { + const struct cred *cred = sk->sk_socket->file->f_cred; + nf_log_buf_add(m, "UID=%u GID=%u ", + from_kuid_munged(&init_user_ns, cred->fsuid), + from_kgid_munged(&init_user_ns, cred->fsgid)); + } + read_unlock_bh(&sk->sk_callback_lock); +} +EXPORT_SYMBOL_GPL(nf_log_dump_sk_uid_gid); + +void +nf_log_dump_packet_common(struct nf_log_buf *m, u_int8_t pf, + unsigned int hooknum, const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct nf_loginfo *loginfo, const char *prefix) +{ + nf_log_buf_add(m, KERN_SOH "%c%sIN=%s OUT=%s ", + '0' + loginfo->u.log.level, prefix, + in ? in->name : "", + out ? out->name : ""); +#ifdef CONFIG_BRIDGE_NETFILTER + if (skb->nf_bridge) { + const struct net_device *physindev; + const struct net_device *physoutdev; + + physindev = skb->nf_bridge->physindev; + if (physindev && in != physindev) + nf_log_buf_add(m, "PHYSIN=%s ", physindev->name); + physoutdev = skb->nf_bridge->physoutdev; + if (physoutdev && out != physoutdev) + nf_log_buf_add(m, "PHYSOUT=%s ", physoutdev->name); + } +#endif +} +EXPORT_SYMBOL_GPL(nf_log_dump_packet_common); + +static int __init nf_log_common_init(void) +{ + return 0; +} + +static void __exit nf_log_common_exit(void) {} + +module_init(nf_log_common_init); +module_exit(nf_log_common_exit); + +MODULE_LICENSE("GPL"); diff --git a/net/netfilter/xt_LOG.c b/net/netfilter/xt_LOG.c index 649b85fc5463..5a6bd60e20d6 100644 --- a/net/netfilter/xt_LOG.c +++ b/net/netfilter/xt_LOG.c @@ -28,813 +28,6 @@ #include #include -static struct nf_loginfo default_loginfo = { - .type = NF_LOG_TYPE_LOG, - .u = { - .log = { - .level = 5, - .logflags = NF_LOG_MASK, - }, - }, -}; - -static int dump_udp_header(struct nf_log_buf *m, const struct sk_buff *skb, - u8 proto, int fragment, unsigned int offset) -{ - struct udphdr _udph; - const struct udphdr *uh; - - if (proto == IPPROTO_UDP) - /* Max length: 10 "PROTO=UDP " */ - nf_log_buf_add(m, "PROTO=UDP "); - else /* Max length: 14 "PROTO=UDPLITE " */ - nf_log_buf_add(m, "PROTO=UDPLITE "); - - if (fragment) - goto out; - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph); - if (uh == NULL) { - nf_log_buf_add(m, "INCOMPLETE [%u bytes] ", skb->len - offset); - - return 1; - } - - /* Max length: 20 "SPT=65535 DPT=65535 " */ - nf_log_buf_add(m, "SPT=%u DPT=%u LEN=%u ", - ntohs(uh->source), ntohs(uh->dest), ntohs(uh->len)); - -out: - return 0; -} - -static int dump_tcp_header(struct nf_log_buf *m, const struct sk_buff *skb, - u8 proto, int fragment, unsigned int offset, - unsigned int logflags) -{ - struct tcphdr _tcph; - const struct tcphdr *th; - - /* Max length: 10 "PROTO=TCP " */ - nf_log_buf_add(m, "PROTO=TCP "); - - if (fragment) - return 0; - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph); - if (th == NULL) { - nf_log_buf_add(m, "INCOMPLETE [%u bytes] ", skb->len - offset); - return 1; - } - - /* Max length: 20 "SPT=65535 DPT=65535 " */ - nf_log_buf_add(m, "SPT=%u DPT=%u ", - ntohs(th->source), ntohs(th->dest)); - /* Max length: 30 "SEQ=4294967295 ACK=4294967295 " */ - if (logflags & XT_LOG_TCPSEQ) { - nf_log_buf_add(m, "SEQ=%u ACK=%u ", - ntohl(th->seq), ntohl(th->ack_seq)); - } - - /* Max length: 13 "WINDOW=65535 " */ - nf_log_buf_add(m, "WINDOW=%u ", ntohs(th->window)); - /* Max length: 9 "RES=0x3C " */ - nf_log_buf_add(m, "RES=0x%02x ", (u_int8_t)(ntohl(tcp_flag_word(th) & - TCP_RESERVED_BITS) >> 22)); - /* Max length: 32 "CWR ECE URG ACK PSH RST SYN FIN " */ - if (th->cwr) - nf_log_buf_add(m, "CWR "); - if (th->ece) - nf_log_buf_add(m, "ECE "); - if (th->urg) - nf_log_buf_add(m, "URG "); - if (th->ack) - nf_log_buf_add(m, "ACK "); - if (th->psh) - nf_log_buf_add(m, "PSH "); - if (th->rst) - nf_log_buf_add(m, "RST "); - if (th->syn) - nf_log_buf_add(m, "SYN "); - if (th->fin) - nf_log_buf_add(m, "FIN "); - /* Max length: 11 "URGP=65535 " */ - nf_log_buf_add(m, "URGP=%u ", ntohs(th->urg_ptr)); - - if ((logflags & XT_LOG_TCPOPT) && th->doff*4 > sizeof(struct tcphdr)) { - u_int8_t _opt[60 - sizeof(struct tcphdr)]; - const u_int8_t *op; - unsigned int i; - unsigned int optsize = th->doff*4 - sizeof(struct tcphdr); - - op = skb_header_pointer(skb, offset + sizeof(struct tcphdr), - optsize, _opt); - if (op == NULL) { - nf_log_buf_add(m, "OPT (TRUNCATED)"); - return 1; - } - - /* Max length: 127 "OPT (" 15*4*2chars ") " */ - nf_log_buf_add(m, "OPT ("); - for (i = 0; i < optsize; i++) - nf_log_buf_add(m, "%02X", op[i]); - - nf_log_buf_add(m, ") "); - } - - return 0; -} - -static void dump_sk_uid_gid(struct nf_log_buf *m, struct sock *sk) -{ - if (!sk || sk->sk_state == TCP_TIME_WAIT) - return; - - read_lock_bh(&sk->sk_callback_lock); - if (sk->sk_socket && sk->sk_socket->file) { - const struct cred *cred = sk->sk_socket->file->f_cred; - nf_log_buf_add(m, "UID=%u GID=%u ", - from_kuid_munged(&init_user_ns, cred->fsuid), - from_kgid_munged(&init_user_ns, cred->fsgid)); - } - read_unlock_bh(&sk->sk_callback_lock); -} - -/* One level of recursion won't kill us */ -static void dump_ipv4_packet(struct nf_log_buf *m, - const struct nf_loginfo *info, - const struct sk_buff *skb, unsigned int iphoff) -{ - struct iphdr _iph; - const struct iphdr *ih; - unsigned int logflags; - - if (info->type == NF_LOG_TYPE_LOG) - logflags = info->u.log.logflags; - else - logflags = NF_LOG_MASK; - - ih = skb_header_pointer(skb, iphoff, sizeof(_iph), &_iph); - if (ih == NULL) { - nf_log_buf_add(m, "TRUNCATED"); - return; - } - - /* Important fields: - * TOS, len, DF/MF, fragment offset, TTL, src, dst, options. */ - /* Max length: 40 "SRC=255.255.255.255 DST=255.255.255.255 " */ - nf_log_buf_add(m, "SRC=%pI4 DST=%pI4 ", - &ih->saddr, &ih->daddr); - - /* Max length: 46 "LEN=65535 TOS=0xFF PREC=0xFF TTL=255 ID=65535 " */ - nf_log_buf_add(m, "LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ", - ntohs(ih->tot_len), ih->tos & IPTOS_TOS_MASK, - ih->tos & IPTOS_PREC_MASK, ih->ttl, ntohs(ih->id)); - - /* Max length: 6 "CE DF MF " */ - if (ntohs(ih->frag_off) & IP_CE) - nf_log_buf_add(m, "CE "); - if (ntohs(ih->frag_off) & IP_DF) - nf_log_buf_add(m, "DF "); - if (ntohs(ih->frag_off) & IP_MF) - nf_log_buf_add(m, "MF "); - - /* Max length: 11 "FRAG:65535 " */ - if (ntohs(ih->frag_off) & IP_OFFSET) - nf_log_buf_add(m, "FRAG:%u ", ntohs(ih->frag_off) & IP_OFFSET); - - if ((logflags & XT_LOG_IPOPT) && - ih->ihl * 4 > sizeof(struct iphdr)) { - const unsigned char *op; - unsigned char _opt[4 * 15 - sizeof(struct iphdr)]; - unsigned int i, optsize; - - optsize = ih->ihl * 4 - sizeof(struct iphdr); - op = skb_header_pointer(skb, iphoff+sizeof(_iph), - optsize, _opt); - if (op == NULL) { - nf_log_buf_add(m, "TRUNCATED"); - return; - } - - /* Max length: 127 "OPT (" 15*4*2chars ") " */ - nf_log_buf_add(m, "OPT ("); - for (i = 0; i < optsize; i++) - nf_log_buf_add(m, "%02X", op[i]); - nf_log_buf_add(m, ") "); - } - - switch (ih->protocol) { - case IPPROTO_TCP: - if (dump_tcp_header(m, skb, ih->protocol, - ntohs(ih->frag_off) & IP_OFFSET, - iphoff+ih->ihl*4, logflags)) - return; - break; - case IPPROTO_UDP: - case IPPROTO_UDPLITE: - if (dump_udp_header(m, skb, ih->protocol, - ntohs(ih->frag_off) & IP_OFFSET, - iphoff+ih->ihl*4)) - return; - break; - case IPPROTO_ICMP: { - struct icmphdr _icmph; - const struct icmphdr *ich; - static const size_t required_len[NR_ICMP_TYPES+1] - = { [ICMP_ECHOREPLY] = 4, - [ICMP_DEST_UNREACH] - = 8 + sizeof(struct iphdr), - [ICMP_SOURCE_QUENCH] - = 8 + sizeof(struct iphdr), - [ICMP_REDIRECT] - = 8 + sizeof(struct iphdr), - [ICMP_ECHO] = 4, - [ICMP_TIME_EXCEEDED] - = 8 + sizeof(struct iphdr), - [ICMP_PARAMETERPROB] - = 8 + sizeof(struct iphdr), - [ICMP_TIMESTAMP] = 20, - [ICMP_TIMESTAMPREPLY] = 20, - [ICMP_ADDRESS] = 12, - [ICMP_ADDRESSREPLY] = 12 }; - - /* Max length: 11 "PROTO=ICMP " */ - nf_log_buf_add(m, "PROTO=ICMP "); - - if (ntohs(ih->frag_off) & IP_OFFSET) - break; - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - ich = skb_header_pointer(skb, iphoff + ih->ihl * 4, - sizeof(_icmph), &_icmph); - if (ich == NULL) { - nf_log_buf_add(m, "INCOMPLETE [%u bytes] ", - skb->len - iphoff - ih->ihl*4); - break; - } - - /* Max length: 18 "TYPE=255 CODE=255 " */ - nf_log_buf_add(m, "TYPE=%u CODE=%u ", ich->type, ich->code); - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - if (ich->type <= NR_ICMP_TYPES && - required_len[ich->type] && - skb->len-iphoff-ih->ihl*4 < required_len[ich->type]) { - nf_log_buf_add(m, "INCOMPLETE [%u bytes] ", - skb->len - iphoff - ih->ihl*4); - break; - } - - switch (ich->type) { - case ICMP_ECHOREPLY: - case ICMP_ECHO: - /* Max length: 19 "ID=65535 SEQ=65535 " */ - nf_log_buf_add(m, "ID=%u SEQ=%u ", - ntohs(ich->un.echo.id), - ntohs(ich->un.echo.sequence)); - break; - - case ICMP_PARAMETERPROB: - /* Max length: 14 "PARAMETER=255 " */ - nf_log_buf_add(m, "PARAMETER=%u ", - ntohl(ich->un.gateway) >> 24); - break; - case ICMP_REDIRECT: - /* Max length: 24 "GATEWAY=255.255.255.255 " */ - nf_log_buf_add(m, "GATEWAY=%pI4 ", &ich->un.gateway); - /* Fall through */ - case ICMP_DEST_UNREACH: - case ICMP_SOURCE_QUENCH: - case ICMP_TIME_EXCEEDED: - /* Max length: 3+maxlen */ - if (!iphoff) { /* Only recurse once. */ - nf_log_buf_add(m, "["); - dump_ipv4_packet(m, info, skb, - iphoff + ih->ihl*4+sizeof(_icmph)); - nf_log_buf_add(m, "] "); - } - - /* Max length: 10 "MTU=65535 " */ - if (ich->type == ICMP_DEST_UNREACH && - ich->code == ICMP_FRAG_NEEDED) { - nf_log_buf_add(m, "MTU=%u ", - ntohs(ich->un.frag.mtu)); - } - } - break; - } - /* Max Length */ - case IPPROTO_AH: { - struct ip_auth_hdr _ahdr; - const struct ip_auth_hdr *ah; - - if (ntohs(ih->frag_off) & IP_OFFSET) - break; - - /* Max length: 9 "PROTO=AH " */ - nf_log_buf_add(m, "PROTO=AH "); - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - ah = skb_header_pointer(skb, iphoff+ih->ihl*4, - sizeof(_ahdr), &_ahdr); - if (ah == NULL) { - nf_log_buf_add(m, "INCOMPLETE [%u bytes] ", - skb->len - iphoff - ih->ihl*4); - break; - } - - /* Length: 15 "SPI=0xF1234567 " */ - nf_log_buf_add(m, "SPI=0x%x ", ntohl(ah->spi)); - break; - } - case IPPROTO_ESP: { - struct ip_esp_hdr _esph; - const struct ip_esp_hdr *eh; - - /* Max length: 10 "PROTO=ESP " */ - nf_log_buf_add(m, "PROTO=ESP "); - - if (ntohs(ih->frag_off) & IP_OFFSET) - break; - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - eh = skb_header_pointer(skb, iphoff+ih->ihl*4, - sizeof(_esph), &_esph); - if (eh == NULL) { - nf_log_buf_add(m, "INCOMPLETE [%u bytes] ", - skb->len - iphoff - ih->ihl*4); - break; - } - - /* Length: 15 "SPI=0xF1234567 " */ - nf_log_buf_add(m, "SPI=0x%x ", ntohl(eh->spi)); - break; - } - /* Max length: 10 "PROTO 255 " */ - default: - nf_log_buf_add(m, "PROTO=%u ", ih->protocol); - } - - /* Max length: 15 "UID=4294967295 " */ - if ((logflags & XT_LOG_UID) && !iphoff) - dump_sk_uid_gid(m, skb->sk); - - /* Max length: 16 "MARK=0xFFFFFFFF " */ - if (!iphoff && skb->mark) - nf_log_buf_add(m, "MARK=0x%x ", skb->mark); - - /* Proto Max log string length */ - /* IP: 40+46+6+11+127 = 230 */ - /* TCP: 10+max(25,20+30+13+9+32+11+127) = 252 */ - /* UDP: 10+max(25,20) = 35 */ - /* UDPLITE: 14+max(25,20) = 39 */ - /* ICMP: 11+max(25, 18+25+max(19,14,24+3+n+10,3+n+10)) = 91+n */ - /* ESP: 10+max(25)+15 = 50 */ - /* AH: 9+max(25)+15 = 49 */ - /* unknown: 10 */ - - /* (ICMP allows recursion one level deep) */ - /* maxlen = IP + ICMP + IP + max(TCP,UDP,ICMP,unknown) */ - /* maxlen = 230+ 91 + 230 + 252 = 803 */ -} - -static void dump_ipv4_mac_header(struct nf_log_buf *m, - const struct nf_loginfo *info, - const struct sk_buff *skb) -{ - struct net_device *dev = skb->dev; - unsigned int logflags = 0; - - if (info->type == NF_LOG_TYPE_LOG) - logflags = info->u.log.logflags; - - if (!(logflags & XT_LOG_MACDECODE)) - goto fallback; - - switch (dev->type) { - case ARPHRD_ETHER: - nf_log_buf_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ", - eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, - ntohs(eth_hdr(skb)->h_proto)); - return; - default: - break; - } - -fallback: - nf_log_buf_add(m, "MAC="); - if (dev->hard_header_len && - skb->mac_header != skb->network_header) { - const unsigned char *p = skb_mac_header(skb); - unsigned int i; - - nf_log_buf_add(m, "%02x", *p++); - for (i = 1; i < dev->hard_header_len; i++, p++) - nf_log_buf_add(m, ":%02x", *p); - } - nf_log_buf_add(m, " "); -} - -static void -log_packet_common(struct nf_log_buf *m, - u_int8_t pf, - unsigned int hooknum, - const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct nf_loginfo *loginfo, - const char *prefix) -{ - nf_log_buf_add(m, KERN_SOH "%c%sIN=%s OUT=%s ", - '0' + loginfo->u.log.level, prefix, - in ? in->name : "", - out ? out->name : ""); -#ifdef CONFIG_BRIDGE_NETFILTER - if (skb->nf_bridge) { - const struct net_device *physindev; - const struct net_device *physoutdev; - - physindev = skb->nf_bridge->physindev; - if (physindev && in != physindev) - nf_log_buf_add(m, "PHYSIN=%s ", physindev->name); - physoutdev = skb->nf_bridge->physoutdev; - if (physoutdev && out != physoutdev) - nf_log_buf_add(m, "PHYSOUT=%s ", physoutdev->name); - } -#endif -} - - -static void -ipt_log_packet(struct net *net, - u_int8_t pf, - unsigned int hooknum, - const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct nf_loginfo *loginfo, - const char *prefix) -{ - struct nf_log_buf *m; - - /* FIXME: Disabled from containers until syslog ns is supported */ - if (!net_eq(net, &init_net)) - return; - - m = nf_log_buf_open(); - - if (!loginfo) - loginfo = &default_loginfo; - - log_packet_common(m, pf, hooknum, skb, in, out, loginfo, prefix); - - if (in != NULL) - dump_ipv4_mac_header(m, loginfo, skb); - - dump_ipv4_packet(m, loginfo, skb, 0); - - nf_log_buf_close(m); -} - -#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) -/* One level of recursion won't kill us */ -static void dump_ipv6_packet(struct nf_log_buf *m, - const struct nf_loginfo *info, - const struct sk_buff *skb, unsigned int ip6hoff, - int recurse) -{ - u_int8_t currenthdr; - int fragment; - struct ipv6hdr _ip6h; - const struct ipv6hdr *ih; - unsigned int ptr; - unsigned int hdrlen = 0; - unsigned int logflags; - - if (info->type == NF_LOG_TYPE_LOG) - logflags = info->u.log.logflags; - else - logflags = NF_LOG_MASK; - - ih = skb_header_pointer(skb, ip6hoff, sizeof(_ip6h), &_ip6h); - if (ih == NULL) { - nf_log_buf_add(m, "TRUNCATED"); - return; - } - - /* Max length: 88 "SRC=0000.0000.0000.0000.0000.0000.0000.0000 DST=0000.0000.0000.0000.0000.0000.0000.0000 " */ - nf_log_buf_add(m, "SRC=%pI6 DST=%pI6 ", &ih->saddr, &ih->daddr); - - /* Max length: 44 "LEN=65535 TC=255 HOPLIMIT=255 FLOWLBL=FFFFF " */ - nf_log_buf_add(m, "LEN=%Zu TC=%u HOPLIMIT=%u FLOWLBL=%u ", - ntohs(ih->payload_len) + sizeof(struct ipv6hdr), - (ntohl(*(__be32 *)ih) & 0x0ff00000) >> 20, - ih->hop_limit, (ntohl(*(__be32 *)ih) & 0x000fffff)); - - fragment = 0; - ptr = ip6hoff + sizeof(struct ipv6hdr); - currenthdr = ih->nexthdr; - while (currenthdr != NEXTHDR_NONE && ip6t_ext_hdr(currenthdr)) { - struct ipv6_opt_hdr _hdr; - const struct ipv6_opt_hdr *hp; - - hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr); - if (hp == NULL) { - nf_log_buf_add(m, "TRUNCATED"); - return; - } - - /* Max length: 48 "OPT (...) " */ - if (logflags & XT_LOG_IPOPT) - nf_log_buf_add(m, "OPT ( "); - - switch (currenthdr) { - case IPPROTO_FRAGMENT: { - struct frag_hdr _fhdr; - const struct frag_hdr *fh; - - nf_log_buf_add(m, "FRAG:"); - fh = skb_header_pointer(skb, ptr, sizeof(_fhdr), - &_fhdr); - if (fh == NULL) { - nf_log_buf_add(m, "TRUNCATED "); - return; - } - - /* Max length: 6 "65535 " */ - nf_log_buf_add(m, "%u ", ntohs(fh->frag_off) & 0xFFF8); - - /* Max length: 11 "INCOMPLETE " */ - if (fh->frag_off & htons(0x0001)) - nf_log_buf_add(m, "INCOMPLETE "); - - nf_log_buf_add(m, "ID:%08x ", ntohl(fh->identification)); - - if (ntohs(fh->frag_off) & 0xFFF8) - fragment = 1; - - hdrlen = 8; - - break; - } - case IPPROTO_DSTOPTS: - case IPPROTO_ROUTING: - case IPPROTO_HOPOPTS: - if (fragment) { - if (logflags & XT_LOG_IPOPT) - nf_log_buf_add(m, ")"); - return; - } - hdrlen = ipv6_optlen(hp); - break; - /* Max Length */ - case IPPROTO_AH: - if (logflags & XT_LOG_IPOPT) { - struct ip_auth_hdr _ahdr; - const struct ip_auth_hdr *ah; - - /* Max length: 3 "AH " */ - nf_log_buf_add(m, "AH "); - - if (fragment) { - nf_log_buf_add(m, ")"); - return; - } - - ah = skb_header_pointer(skb, ptr, sizeof(_ahdr), - &_ahdr); - if (ah == NULL) { - /* - * Max length: 26 "INCOMPLETE [65535 - * bytes] )" - */ - nf_log_buf_add(m, "INCOMPLETE [%u bytes] )", - skb->len - ptr); - return; - } - - /* Length: 15 "SPI=0xF1234567 */ - nf_log_buf_add(m, "SPI=0x%x ", ntohl(ah->spi)); - - } - - hdrlen = (hp->hdrlen+2)<<2; - break; - case IPPROTO_ESP: - if (logflags & XT_LOG_IPOPT) { - struct ip_esp_hdr _esph; - const struct ip_esp_hdr *eh; - - /* Max length: 4 "ESP " */ - nf_log_buf_add(m, "ESP "); - - if (fragment) { - nf_log_buf_add(m, ")"); - return; - } - - /* - * Max length: 26 "INCOMPLETE [65535 bytes] )" - */ - eh = skb_header_pointer(skb, ptr, sizeof(_esph), - &_esph); - if (eh == NULL) { - nf_log_buf_add(m, "INCOMPLETE [%u bytes] )", - skb->len - ptr); - return; - } - - /* Length: 16 "SPI=0xF1234567 )" */ - nf_log_buf_add(m, "SPI=0x%x )", ntohl(eh->spi)); - - } - return; - default: - /* Max length: 20 "Unknown Ext Hdr 255" */ - nf_log_buf_add(m, "Unknown Ext Hdr %u", currenthdr); - return; - } - if (logflags & XT_LOG_IPOPT) - nf_log_buf_add(m, ") "); - - currenthdr = hp->nexthdr; - ptr += hdrlen; - } - - switch (currenthdr) { - case IPPROTO_TCP: - if (dump_tcp_header(m, skb, currenthdr, fragment, ptr, - logflags)) - return; - break; - case IPPROTO_UDP: - case IPPROTO_UDPLITE: - if (dump_udp_header(m, skb, currenthdr, fragment, ptr)) - return; - break; - case IPPROTO_ICMPV6: { - struct icmp6hdr _icmp6h; - const struct icmp6hdr *ic; - - /* Max length: 13 "PROTO=ICMPv6 " */ - nf_log_buf_add(m, "PROTO=ICMPv6 "); - - if (fragment) - break; - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - ic = skb_header_pointer(skb, ptr, sizeof(_icmp6h), &_icmp6h); - if (ic == NULL) { - nf_log_buf_add(m, "INCOMPLETE [%u bytes] ", - skb->len - ptr); - return; - } - - /* Max length: 18 "TYPE=255 CODE=255 " */ - nf_log_buf_add(m, "TYPE=%u CODE=%u ", - ic->icmp6_type, ic->icmp6_code); - - switch (ic->icmp6_type) { - case ICMPV6_ECHO_REQUEST: - case ICMPV6_ECHO_REPLY: - /* Max length: 19 "ID=65535 SEQ=65535 " */ - nf_log_buf_add(m, "ID=%u SEQ=%u ", - ntohs(ic->icmp6_identifier), - ntohs(ic->icmp6_sequence)); - break; - case ICMPV6_MGM_QUERY: - case ICMPV6_MGM_REPORT: - case ICMPV6_MGM_REDUCTION: - break; - - case ICMPV6_PARAMPROB: - /* Max length: 17 "POINTER=ffffffff " */ - nf_log_buf_add(m, "POINTER=%08x ", - ntohl(ic->icmp6_pointer)); - /* Fall through */ - case ICMPV6_DEST_UNREACH: - case ICMPV6_PKT_TOOBIG: - case ICMPV6_TIME_EXCEED: - /* Max length: 3+maxlen */ - if (recurse) { - nf_log_buf_add(m, "["); - dump_ipv6_packet(m, info, skb, - ptr + sizeof(_icmp6h), 0); - nf_log_buf_add(m, "] "); - } - - /* Max length: 10 "MTU=65535 " */ - if (ic->icmp6_type == ICMPV6_PKT_TOOBIG) - nf_log_buf_add(m, "MTU=%u ", - ntohl(ic->icmp6_mtu)); - } - break; - } - /* Max length: 10 "PROTO=255 " */ - default: - nf_log_buf_add(m, "PROTO=%u ", currenthdr); - } - - /* Max length: 15 "UID=4294967295 " */ - if ((logflags & XT_LOG_UID) && recurse) - dump_sk_uid_gid(m, skb->sk); - - /* Max length: 16 "MARK=0xFFFFFFFF " */ - if (recurse && skb->mark) - nf_log_buf_add(m, "MARK=0x%x ", skb->mark); -} - -static void dump_ipv6_mac_header(struct nf_log_buf *m, - const struct nf_loginfo *info, - const struct sk_buff *skb) -{ - struct net_device *dev = skb->dev; - unsigned int logflags = 0; - - if (info->type == NF_LOG_TYPE_LOG) - logflags = info->u.log.logflags; - - if (!(logflags & XT_LOG_MACDECODE)) - goto fallback; - - switch (dev->type) { - case ARPHRD_ETHER: - nf_log_buf_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ", - eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, - ntohs(eth_hdr(skb)->h_proto)); - return; - default: - break; - } - -fallback: - nf_log_buf_add(m, "MAC="); - if (dev->hard_header_len && - skb->mac_header != skb->network_header) { - const unsigned char *p = skb_mac_header(skb); - unsigned int len = dev->hard_header_len; - unsigned int i; - - if (dev->type == ARPHRD_SIT) { - p -= ETH_HLEN; - - if (p < skb->head) - p = NULL; - } - - if (p != NULL) { - nf_log_buf_add(m, "%02x", *p++); - for (i = 1; i < len; i++) - nf_log_buf_add(m, ":%02x", *p++); - } - nf_log_buf_add(m, " "); - - if (dev->type == ARPHRD_SIT) { - const struct iphdr *iph = - (struct iphdr *)skb_mac_header(skb); - nf_log_buf_add(m, "TUNNEL=%pI4->%pI4 ", - &iph->saddr, &iph->daddr); - } - } else { - nf_log_buf_add(m, " "); - } -} - -static void -ip6t_log_packet(struct net *net, - u_int8_t pf, - unsigned int hooknum, - const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct nf_loginfo *loginfo, - const char *prefix) -{ - struct nf_log_buf *m; - - /* FIXME: Disabled from containers until syslog ns is supported */ - if (!net_eq(net, &init_net)) - return; - - m = nf_log_buf_open(); - - if (!loginfo) - loginfo = &default_loginfo; - - log_packet_common(m, pf, hooknum, skb, in, out, loginfo, prefix); - - if (in != NULL) - dump_ipv6_mac_header(m, loginfo, skb); - - dump_ipv6_packet(m, loginfo, skb, skb_network_offset(skb), 1); - - nf_log_buf_close(m); -} -#endif - static unsigned int log_tg(struct sk_buff *skb, const struct xt_action_param *par) { @@ -847,12 +40,12 @@ log_tg(struct sk_buff *skb, const struct xt_action_param *par) li.u.log.logflags = loginfo->logflags; if (par->family == NFPROTO_IPV4) - ipt_log_packet(net, NFPROTO_IPV4, par->hooknum, skb, par->in, - par->out, &li, loginfo->prefix); + nf_log_ip_packet(net, NFPROTO_IPV4, par->hooknum, skb, par->in, + par->out, &li, loginfo->prefix); #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) else if (par->family == NFPROTO_IPV6) - ip6t_log_packet(net, NFPROTO_IPV6, par->hooknum, skb, par->in, - par->out, &li, loginfo->prefix); + nf_log_ip6_packet(net, NFPROTO_IPV6, par->hooknum, skb, par->in, + par->out, &li, loginfo->prefix); #endif else WARN_ON_ONCE(1); @@ -901,75 +94,13 @@ static struct xt_target log_tg_regs[] __read_mostly = { #endif }; -static struct nf_logger ipt_log_logger __read_mostly = { - .name = "ipt_LOG", - .type = NF_LOG_TYPE_LOG, - .logfn = &ipt_log_packet, - .me = THIS_MODULE, -}; - -#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) -static struct nf_logger ip6t_log_logger __read_mostly = { - .name = "ip6t_LOG", - .type = NF_LOG_TYPE_LOG, - .logfn = &ip6t_log_packet, - .me = THIS_MODULE, -}; -#endif - -static int __net_init log_net_init(struct net *net) -{ - nf_log_set(net, NFPROTO_IPV4, &ipt_log_logger); -#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) - nf_log_set(net, NFPROTO_IPV6, &ip6t_log_logger); -#endif - return 0; -} - -static void __net_exit log_net_exit(struct net *net) -{ - nf_log_unset(net, &ipt_log_logger); -#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) - nf_log_unset(net, &ip6t_log_logger); -#endif -} - -static struct pernet_operations log_net_ops = { - .init = log_net_init, - .exit = log_net_exit, -}; - static int __init log_tg_init(void) { - int ret; - - ret = register_pernet_subsys(&log_net_ops); - if (ret < 0) - goto err_pernet; - - ret = xt_register_targets(log_tg_regs, ARRAY_SIZE(log_tg_regs)); - if (ret < 0) - goto err_target; - - nf_log_register(NFPROTO_IPV4, &ipt_log_logger); -#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) - nf_log_register(NFPROTO_IPV6, &ip6t_log_logger); -#endif - return 0; - -err_target: - unregister_pernet_subsys(&log_net_ops); -err_pernet: - return ret; + return xt_register_targets(log_tg_regs, ARRAY_SIZE(log_tg_regs)); } static void __exit log_tg_exit(void) { - unregister_pernet_subsys(&log_net_ops); - nf_log_unregister(&ipt_log_logger); -#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) - nf_log_unregister(&ip6t_log_logger); -#endif xt_unregister_targets(log_tg_regs, ARRAY_SIZE(log_tg_regs)); } -- cgit v1.2.3 From fab4085f4e248b8a80bb1dadbbacb2bacd8017c3 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 18 Jun 2014 19:38:25 +0200 Subject: netfilter: log: nf_log_packet() as real unified interface Before this patch, the nf_loginfo parameter specified the logging configuration in case the specified default logger was loaded. This patch updates the semantics of the nf_loginfo parameter in nf_log_packet() which now indicates the logger that you explicitly want to use. Thus, nf_log_packet() is exposed as an unified interface which internally routes the log message to the corresponding logger type by family. The module dependencies are expressed by the new nf_logger_find_get() and nf_logger_put() functions which bump the logger module refcount. Thus, you can not remove logger modules that are used by rules anymore. Another important effect of this change is that the family specific module is only loaded when required. Therefore, xt_LOG and nft_log will just trigger the autoload of the nf_log_{ip,ip6} modules according to the family. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_log.h | 20 ++++++-------------- net/ipv4/netfilter/nf_log_ipv4.c | 14 +++++++------- net/ipv6/netfilter/nf_log_ipv6.c | 14 +++++++------- net/netfilter/nf_log.c | 41 +++++++++++++++++++++++++++++++++++++++- net/netfilter/nfnetlink_log.c | 3 +++ net/netfilter/xt_LOG.c | 22 ++++++++++----------- 6 files changed, 73 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_log.h b/include/net/netfilter/nf_log.h index bba354e78f49..b82dd19b8f26 100644 --- a/include/net/netfilter/nf_log.h +++ b/include/net/netfilter/nf_log.h @@ -61,6 +61,12 @@ int nf_log_bind_pf(struct net *net, u_int8_t pf, const struct nf_logger *logger); void nf_log_unbind_pf(struct net *net, u_int8_t pf); +int nf_logger_find_get(int pf, enum nf_log_type type); +void nf_logger_put(int pf, enum nf_log_type type); + +#define MODULE_ALIAS_NF_LOGGER(family, type) \ + MODULE_ALIAS("nf-logger-" __stringify(family) "-" __stringify(type)) + /* Calls the registered backend logging function */ __printf(8, 9) void nf_log_packet(struct net *net, @@ -78,20 +84,6 @@ struct nf_log_buf *nf_log_buf_open(void); __printf(2, 3) int nf_log_buf_add(struct nf_log_buf *m, const char *f, ...); void nf_log_buf_close(struct nf_log_buf *m); -void nf_log_ip_packet(struct net *net, u_int8_t pf, - unsigned int hooknum, const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct nf_loginfo *loginfo, - const char *prefix); - -void nf_log_ip6_packet(struct net *net, u_int8_t pf, - unsigned int hooknum, const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct nf_loginfo *loginfo, - const char *prefix); - /* common logging functions */ int nf_log_dump_udp_header(struct nf_log_buf *m, const struct sk_buff *skb, u8 proto, int fragment, unsigned int offset); diff --git a/net/ipv4/netfilter/nf_log_ipv4.c b/net/ipv4/netfilter/nf_log_ipv4.c index 7e69a401a29e..078bdca1b607 100644 --- a/net/ipv4/netfilter/nf_log_ipv4.c +++ b/net/ipv4/netfilter/nf_log_ipv4.c @@ -306,12 +306,12 @@ fallback: nf_log_buf_add(m, " "); } -void nf_log_ip_packet(struct net *net, u_int8_t pf, - unsigned int hooknum, const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct nf_loginfo *loginfo, - const char *prefix) +static void nf_log_ip_packet(struct net *net, u_int8_t pf, + unsigned int hooknum, const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct nf_loginfo *loginfo, + const char *prefix) { struct nf_log_buf *m; @@ -334,7 +334,6 @@ void nf_log_ip_packet(struct net *net, u_int8_t pf, nf_log_buf_close(m); } -EXPORT_SYMBOL_GPL(nf_log_ip_packet); static struct nf_logger nf_ip_logger __read_mostly = { .name = "nf_log_ipv4", @@ -383,3 +382,4 @@ module_exit(nf_log_ipv4_exit); MODULE_AUTHOR("Netfilter Core Team "); MODULE_DESCRIPTION("Netfilter IPv4 packet logging"); MODULE_LICENSE("GPL"); +MODULE_ALIAS_NF_LOGGER(AF_INET, 0); diff --git a/net/ipv6/netfilter/nf_log_ipv6.c b/net/ipv6/netfilter/nf_log_ipv6.c index 804060946d2b..7b17a0be93e7 100644 --- a/net/ipv6/netfilter/nf_log_ipv6.c +++ b/net/ipv6/netfilter/nf_log_ipv6.c @@ -338,12 +338,12 @@ fallback: } } -void nf_log_ip6_packet(struct net *net, u_int8_t pf, - unsigned int hooknum, const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct nf_loginfo *loginfo, - const char *prefix) +static void nf_log_ip6_packet(struct net *net, u_int8_t pf, + unsigned int hooknum, const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct nf_loginfo *loginfo, + const char *prefix) { struct nf_log_buf *m; @@ -366,7 +366,6 @@ void nf_log_ip6_packet(struct net *net, u_int8_t pf, nf_log_buf_close(m); } -EXPORT_SYMBOL_GPL(nf_log_ip6_packet); static struct nf_logger nf_ip6_logger __read_mostly = { .name = "nf_log_ipv6", @@ -415,3 +414,4 @@ module_exit(nf_log_ipv6_exit); MODULE_AUTHOR("Netfilter Core Team "); MODULE_DESCRIPTION("Netfilter IPv4 packet logging"); MODULE_LICENSE("GPL"); +MODULE_ALIAS_NF_LOGGER(AF_INET6, 0); diff --git a/net/netfilter/nf_log.c b/net/netfilter/nf_log.c index 0b6b2c874199..0b2161c689e0 100644 --- a/net/netfilter/nf_log.c +++ b/net/netfilter/nf_log.c @@ -132,6 +132,41 @@ void nf_log_unbind_pf(struct net *net, u_int8_t pf) } EXPORT_SYMBOL(nf_log_unbind_pf); +int nf_logger_find_get(int pf, enum nf_log_type type) +{ + struct nf_logger *logger; + int ret = -ENOENT; + + logger = loggers[pf][type]; + if (logger == NULL) + request_module("nf-logger-%u-%u", pf, type); + + rcu_read_lock(); + logger = rcu_dereference(loggers[pf][type]); + if (logger == NULL) + goto out; + + if (logger && try_module_get(logger->me)) + ret = 0; +out: + rcu_read_unlock(); + return ret; +} +EXPORT_SYMBOL_GPL(nf_logger_find_get); + +void nf_logger_put(int pf, enum nf_log_type type) +{ + struct nf_logger *logger; + + BUG_ON(loggers[pf][type] == NULL); + + rcu_read_lock(); + logger = rcu_dereference(loggers[pf][type]); + module_put(logger->me); + rcu_read_unlock(); +} +EXPORT_SYMBOL_GPL(nf_logger_put); + void nf_log_packet(struct net *net, u_int8_t pf, unsigned int hooknum, @@ -146,7 +181,11 @@ void nf_log_packet(struct net *net, const struct nf_logger *logger; rcu_read_lock(); - logger = rcu_dereference(net->nf.nf_loggers[pf]); + if (loginfo != NULL) + logger = rcu_dereference(loggers[pf][loginfo->type]); + else + logger = rcu_dereference(net->nf.nf_loggers[pf]); + if (logger) { va_start(args, fmt); vsnprintf(prefix, sizeof(prefix), fmt, args); diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index 160bb8ea9923..a11c5ff2f720 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -1106,6 +1106,9 @@ MODULE_DESCRIPTION("netfilter userspace logging"); MODULE_AUTHOR("Harald Welte "); MODULE_LICENSE("GPL"); MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_ULOG); +MODULE_ALIAS_NF_LOGGER(AF_INET, 1); +MODULE_ALIAS_NF_LOGGER(AF_INET6, 1); +MODULE_ALIAS_NF_LOGGER(AF_BRIDGE, 1); module_init(nfnetlink_log_init); module_exit(nfnetlink_log_fini); diff --git a/net/netfilter/xt_LOG.c b/net/netfilter/xt_LOG.c index 5a6bd60e20d6..00eb49196e75 100644 --- a/net/netfilter/xt_LOG.c +++ b/net/netfilter/xt_LOG.c @@ -39,17 +39,8 @@ log_tg(struct sk_buff *skb, const struct xt_action_param *par) li.u.log.level = loginfo->level; li.u.log.logflags = loginfo->logflags; - if (par->family == NFPROTO_IPV4) - nf_log_ip_packet(net, NFPROTO_IPV4, par->hooknum, skb, par->in, - par->out, &li, loginfo->prefix); -#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) - else if (par->family == NFPROTO_IPV6) - nf_log_ip6_packet(net, NFPROTO_IPV6, par->hooknum, skb, par->in, - par->out, &li, loginfo->prefix); -#endif - else - WARN_ON_ONCE(1); - + nf_log_packet(net, par->family, par->hooknum, skb, par->in, par->out, + &li, loginfo->prefix); return XT_CONTINUE; } @@ -70,7 +61,12 @@ static int log_tg_check(const struct xt_tgchk_param *par) return -EINVAL; } - return 0; + return nf_logger_find_get(par->family, NF_LOG_TYPE_LOG); +} + +static void log_tg_destroy(const struct xt_tgdtor_param *par) +{ + nf_logger_put(par->family, NF_LOG_TYPE_LOG); } static struct xt_target log_tg_regs[] __read_mostly = { @@ -80,6 +76,7 @@ static struct xt_target log_tg_regs[] __read_mostly = { .target = log_tg, .targetsize = sizeof(struct xt_log_info), .checkentry = log_tg_check, + .destroy = log_tg_destroy, .me = THIS_MODULE, }, #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) @@ -89,6 +86,7 @@ static struct xt_target log_tg_regs[] __read_mostly = { .target = log_tg, .targetsize = sizeof(struct xt_log_info), .checkentry = log_tg_check, + .destroy = log_tg_destroy, .me = THIS_MODULE, }, #endif -- cgit v1.2.3 From 960649d1923c31a7f771162fa0eef00210044262 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 23 Jun 2014 00:28:18 +0200 Subject: netfilter: bridge: add generic packet logger This adds the generic plain text packet loggger for bridged packets. It routes the logging message to the real protocol packet logger. I decided not to refactor the ebt_log code for two reasons: 1) The ebt_log output is not consistent with the IPv4 and IPv6 Netfilter packet loggers. The output is different for no good reason and it adds redundant code to handle packet logging. 2) To avoid breaking backward compatibility for applications outthere that are parsing the specific ebt_log output, the ebt_log output has been left as is. So only nftables will use the new consistent logging format for logged bridged packets. More decisions coming in this patch: 1) This also removes ebt_log as default logger for bridged packets. Thus, nf_log_packet() routes packet to this new packet logger instead. This doesn't break backward compatibility since nf_log_packet() is not used to log packets in plain text format from anywhere in the ebtables/netfilter bridge code. 2) The new bridge packet logger also performs a lazy request to register the real IPv4, ARP and IPv6 netfilter packet loggers. If the real protocol logger is no available (not compiled or the module is not available in the system, not packet logging happens. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_log.h | 1 + net/bridge/netfilter/Kconfig | 3 ++ net/bridge/netfilter/Makefile | 3 ++ net/bridge/netfilter/ebt_log.c | 48 ++---------------- net/bridge/netfilter/nf_log_bridge.c | 96 ++++++++++++++++++++++++++++++++++++ net/netfilter/nf_log.c | 7 +++ 6 files changed, 115 insertions(+), 43 deletions(-) create mode 100644 net/bridge/netfilter/nf_log_bridge.c (limited to 'include') diff --git a/include/net/netfilter/nf_log.h b/include/net/netfilter/nf_log.h index b82dd19b8f26..534e1f2ac4fc 100644 --- a/include/net/netfilter/nf_log.h +++ b/include/net/netfilter/nf_log.h @@ -63,6 +63,7 @@ void nf_log_unbind_pf(struct net *net, u_int8_t pf); int nf_logger_find_get(int pf, enum nf_log_type type); void nf_logger_put(int pf, enum nf_log_type type); +void nf_logger_request_module(int pf, enum nf_log_type type); #define MODULE_ALIAS_NF_LOGGER(family, type) \ MODULE_ALIAS("nf-logger-" __stringify(family) "-" __stringify(type)) diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig index 3a76ac7b7141..4ce0b313f72c 100644 --- a/net/bridge/netfilter/Kconfig +++ b/net/bridge/netfilter/Kconfig @@ -14,6 +14,9 @@ config NFT_BRIDGE_META help Add support for bridge dedicated meta key. +config NF_LOG_BRIDGE + tristate "Bridge packet logging" + endif # NF_TABLES_BRIDGE menuconfig BRIDGE_NF_EBTABLES diff --git a/net/bridge/netfilter/Makefile b/net/bridge/netfilter/Makefile index 6f2f3943d66f..1f78ea0d90e4 100644 --- a/net/bridge/netfilter/Makefile +++ b/net/bridge/netfilter/Makefile @@ -5,6 +5,9 @@ obj-$(CONFIG_NF_TABLES_BRIDGE) += nf_tables_bridge.o obj-$(CONFIG_NFT_BRIDGE_META) += nft_meta_bridge.o +# packet logging +obj-$(CONFIG_NF_LOG_BRIDGE) += nf_log_bridge.o + obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o # tables diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c index 0577477aacd8..17f2e4bc2a29 100644 --- a/net/bridge/netfilter/ebt_log.c +++ b/net/bridge/netfilter/ebt_log.c @@ -186,6 +186,10 @@ ebt_log_tg(struct sk_buff *skb, const struct xt_action_param *par) li.u.log.level = info->loglevel; li.u.log.logflags = info->bitmask; + /* Remember that we have to use ebt_log_packet() not to break backward + * compatibility. We cannot use the default bridge packet logger via + * nf_log_packet() with NFT_LOG_TYPE_LOG here. --Pablo + */ if (info->bitmask & EBT_LOG_NFLOG) nf_log_packet(net, NFPROTO_BRIDGE, par->hooknum, skb, par->in, par->out, &li, "%s", info->prefix); @@ -205,55 +209,13 @@ static struct xt_target ebt_log_tg_reg __read_mostly = { .me = THIS_MODULE, }; -static struct nf_logger ebt_log_logger __read_mostly = { - .name = "ebt_log", - .type = NF_LOG_TYPE_LOG, - .logfn = &ebt_log_packet, - .me = THIS_MODULE, -}; - -static int __net_init ebt_log_net_init(struct net *net) -{ - nf_log_set(net, NFPROTO_BRIDGE, &ebt_log_logger); - return 0; -} - -static void __net_exit ebt_log_net_fini(struct net *net) -{ - nf_log_unset(net, &ebt_log_logger); -} - -static struct pernet_operations ebt_log_net_ops = { - .init = ebt_log_net_init, - .exit = ebt_log_net_fini, -}; - static int __init ebt_log_init(void) { - int ret; - - ret = register_pernet_subsys(&ebt_log_net_ops); - if (ret < 0) - goto err_pernet; - - ret = xt_register_target(&ebt_log_tg_reg); - if (ret < 0) - goto err_target; - - nf_log_register(NFPROTO_BRIDGE, &ebt_log_logger); - - return ret; - -err_target: - unregister_pernet_subsys(&ebt_log_net_ops); -err_pernet: - return ret; + return xt_register_target(&ebt_log_tg_reg); } static void __exit ebt_log_fini(void) { - unregister_pernet_subsys(&ebt_log_net_ops); - nf_log_unregister(&ebt_log_logger); xt_unregister_target(&ebt_log_tg_reg); } diff --git a/net/bridge/netfilter/nf_log_bridge.c b/net/bridge/netfilter/nf_log_bridge.c new file mode 100644 index 000000000000..5d9953a90929 --- /dev/null +++ b/net/bridge/netfilter/nf_log_bridge.c @@ -0,0 +1,96 @@ +/* + * (C) 2014 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 +#include + +#include +#include + +static void nf_log_bridge_packet(struct net *net, u_int8_t pf, + unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct nf_loginfo *loginfo, + const char *prefix) +{ + switch (eth_hdr(skb)->h_proto) { + case htons(ETH_P_IP): + nf_log_packet(net, NFPROTO_IPV4, hooknum, skb, in, out, + loginfo, "%s", prefix); + break; + case htons(ETH_P_IPV6): + nf_log_packet(net, NFPROTO_IPV6, hooknum, skb, in, out, + loginfo, "%s", prefix); + break; + case htons(ETH_P_ARP): + case htons(ETH_P_RARP): + nf_log_packet(net, NFPROTO_ARP, hooknum, skb, in, out, + loginfo, "%s", prefix); + break; + } +} + +static struct nf_logger nf_bridge_logger __read_mostly = { + .name = "nf_log_bridge", + .type = NF_LOG_TYPE_LOG, + .logfn = nf_log_bridge_packet, + .me = THIS_MODULE, +}; + +static int __net_init nf_log_bridge_net_init(struct net *net) +{ + nf_log_set(net, NFPROTO_BRIDGE, &nf_bridge_logger); + return 0; +} + +static void __net_exit nf_log_bridge_net_exit(struct net *net) +{ + nf_log_unset(net, &nf_bridge_logger); +} + +static struct pernet_operations nf_log_bridge_net_ops = { + .init = nf_log_bridge_net_init, + .exit = nf_log_bridge_net_exit, +}; + +static int __init nf_log_bridge_init(void) +{ + int ret; + + /* Request to load the real packet loggers. */ + nf_logger_request_module(NFPROTO_IPV4, NF_LOG_TYPE_LOG); + nf_logger_request_module(NFPROTO_IPV6, NF_LOG_TYPE_LOG); + nf_logger_request_module(NFPROTO_ARP, NF_LOG_TYPE_LOG); + + ret = register_pernet_subsys(&nf_log_bridge_net_ops); + if (ret < 0) + return ret; + + nf_log_register(NFPROTO_BRIDGE, &nf_bridge_logger); + return 0; +} + +static void __exit nf_log_bridge_exit(void) +{ + unregister_pernet_subsys(&nf_log_bridge_net_ops); + nf_log_unregister(&nf_bridge_logger); +} + +module_init(nf_log_bridge_init); +module_exit(nf_log_bridge_exit); + +MODULE_AUTHOR("Pablo Neira Ayuso "); +MODULE_DESCRIPTION("Netfilter bridge packet logging"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_NF_LOGGER(AF_BRIDGE, 0); diff --git a/net/netfilter/nf_log.c b/net/netfilter/nf_log.c index 0b2161c689e0..daad6022c689 100644 --- a/net/netfilter/nf_log.c +++ b/net/netfilter/nf_log.c @@ -132,6 +132,13 @@ void nf_log_unbind_pf(struct net *net, u_int8_t pf) } EXPORT_SYMBOL(nf_log_unbind_pf); +void nf_logger_request_module(int pf, enum nf_log_type type) +{ + if (loggers[pf][type] == NULL) + request_module("nf-logger-%u-%u", pf, type); +} +EXPORT_SYMBOL_GPL(nf_logger_request_module); + int nf_logger_find_get(int pf, enum nf_log_type type) { struct nf_logger *logger; -- cgit v1.2.3 From 09d27b88f15f08fcfbaf57d9b0b4489816264815 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 25 Jun 2014 13:37:13 +0200 Subject: netfilter: nft_log: complete logging support Use the unified nf_log_packet() interface that allows us explicit logger selection through the nf_loginfo structure. If you specify the group attribute, this means you want to receive logging messages through nfnetlink_log. In that case, the snaplen and qthreshold attributes allows you to tune internal aspects of the netlink logging infrastructure. On the other hand, if the level is specified, then the plain text format through the kernel logging ring is used instead, which is also used by default if neither group nor level are indicated. Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter/nf_tables.h | 4 ++ net/netfilter/nft_log.c | 76 +++++++++++++++++++++++++------- 2 files changed, 63 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 2a88f645a5d8..801bdd1e56e3 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -697,6 +697,8 @@ enum nft_counter_attributes { * @NFTA_LOG_PREFIX: prefix to prepend to log messages (NLA_STRING) * @NFTA_LOG_SNAPLEN: length of payload to include in netlink message (NLA_U32) * @NFTA_LOG_QTHRESHOLD: queue threshold (NLA_U32) + * @NFTA_LOG_LEVEL: log level (NLA_U32) + * @NFTA_LOG_FLAGS: logging flags (NLA_U32) */ enum nft_log_attributes { NFTA_LOG_UNSPEC, @@ -704,6 +706,8 @@ enum nft_log_attributes { NFTA_LOG_PREFIX, NFTA_LOG_SNAPLEN, NFTA_LOG_QTHRESHOLD, + NFTA_LOG_LEVEL, + NFTA_LOG_FLAGS, __NFTA_LOG_MAX }; #define NFTA_LOG_MAX (__NFTA_LOG_MAX - 1) diff --git a/net/netfilter/nft_log.c b/net/netfilter/nft_log.c index 55d4297d8417..5b1a4f5c3dcc 100644 --- a/net/netfilter/nft_log.c +++ b/net/netfilter/nft_log.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2008-2009 Patrick McHardy + * Copyright (c) 2012-2014 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 @@ -41,6 +42,8 @@ static const struct nla_policy nft_log_policy[NFTA_LOG_MAX + 1] = { [NFTA_LOG_PREFIX] = { .type = NLA_STRING }, [NFTA_LOG_SNAPLEN] = { .type = NLA_U32 }, [NFTA_LOG_QTHRESHOLD] = { .type = NLA_U16 }, + [NFTA_LOG_LEVEL] = { .type = NLA_U32 }, + [NFTA_LOG_FLAGS] = { .type = NLA_U32 }, }; static int nft_log_init(const struct nft_ctx *ctx, @@ -58,18 +61,41 @@ static int nft_log_init(const struct nft_ctx *ctx, if (priv->prefix == NULL) return -ENOMEM; nla_strlcpy(priv->prefix, nla, nla_len(nla) + 1); - } else + } else { priv->prefix = (char *)nft_log_null_prefix; + } - li->type = NF_LOG_TYPE_ULOG; + li->type = NF_LOG_TYPE_LOG; + if (tb[NFTA_LOG_LEVEL] != NULL && + tb[NFTA_LOG_GROUP] != NULL) + return -EINVAL; if (tb[NFTA_LOG_GROUP] != NULL) + li->type = NF_LOG_TYPE_ULOG; + + switch (li->type) { + case NF_LOG_TYPE_LOG: + if (tb[NFTA_LOG_LEVEL] != NULL) { + li->u.log.level = + ntohl(nla_get_be32(tb[NFTA_LOG_LEVEL]));; + } else { + li->u.log.level = 4; + } + if (tb[NFTA_LOG_FLAGS] != NULL) { + li->u.log.logflags = + ntohl(nla_get_be32(tb[NFTA_LOG_FLAGS])); + } + break; + case NF_LOG_TYPE_ULOG: li->u.ulog.group = ntohs(nla_get_be16(tb[NFTA_LOG_GROUP])); - - if (tb[NFTA_LOG_SNAPLEN] != NULL) - li->u.ulog.copy_len = ntohl(nla_get_be32(tb[NFTA_LOG_SNAPLEN])); - if (tb[NFTA_LOG_QTHRESHOLD] != NULL) { - li->u.ulog.qthreshold = - ntohs(nla_get_be16(tb[NFTA_LOG_QTHRESHOLD])); + if (tb[NFTA_LOG_SNAPLEN] != NULL) { + li->u.ulog.copy_len = + ntohl(nla_get_be32(tb[NFTA_LOG_SNAPLEN])); + } + if (tb[NFTA_LOG_QTHRESHOLD] != NULL) { + li->u.ulog.qthreshold = + ntohs(nla_get_be16(tb[NFTA_LOG_QTHRESHOLD])); + } + break; } if (ctx->afi->family == NFPROTO_INET) { @@ -113,17 +139,33 @@ static int nft_log_dump(struct sk_buff *skb, const struct nft_expr *expr) if (priv->prefix != nft_log_null_prefix) if (nla_put_string(skb, NFTA_LOG_PREFIX, priv->prefix)) goto nla_put_failure; - if (li->u.ulog.group) - if (nla_put_be16(skb, NFTA_LOG_GROUP, htons(li->u.ulog.group))) - goto nla_put_failure; - if (li->u.ulog.copy_len) - if (nla_put_be32(skb, NFTA_LOG_SNAPLEN, - htonl(li->u.ulog.copy_len))) + switch (li->type) { + case NF_LOG_TYPE_LOG: + if (nla_put_be32(skb, NFTA_LOG_LEVEL, htonl(li->u.log.level))) goto nla_put_failure; - if (li->u.ulog.qthreshold) - if (nla_put_be16(skb, NFTA_LOG_QTHRESHOLD, - htons(li->u.ulog.qthreshold))) + + if (li->u.log.logflags) { + if (nla_put_be32(skb, NFTA_LOG_FLAGS, + htonl(li->u.log.logflags))) + goto nla_put_failure; + } + break; + case NF_LOG_TYPE_ULOG: + if (nla_put_be16(skb, NFTA_LOG_GROUP, htons(li->u.ulog.group))) goto nla_put_failure; + + if (li->u.ulog.copy_len) { + if (nla_put_be32(skb, NFTA_LOG_SNAPLEN, + htonl(li->u.ulog.copy_len))) + goto nla_put_failure; + } + if (li->u.ulog.qthreshold) { + if (nla_put_be16(skb, NFTA_LOG_QTHRESHOLD, + htons(li->u.ulog.qthreshold))) + goto nla_put_failure; + } + break; + } return 0; nla_put_failure: -- cgit v1.2.3 From 5d98e61d337c181f199a6cb864569cc4e116ef4c Mon Sep 17 00:00:00 2001 From: Lan Tianyu Date: Tue, 20 May 2014 20:59:23 +0800 Subject: I2C/ACPI: Add i2c ACPI operation region support ACPI 5.0 spec(5.5.2.4.5) defines GenericSerialBus(i2c, spi, uart) operation region. It allows ACPI aml code able to access such kind of devices to implement some ACPI standard method. ACPI Spec defines some access attribute to associate with i2c protocol. AttribQuick Read/Write Quick Protocol AttribSendReceive Send/Receive Byte Protocol AttribByte Read/Write Byte Protocol AttribWord Read/Write Word Protocol AttribBlock Read/Write Block Protocol AttribBytes Read/Write N-Bytes Protocol AttribProcessCall Process Call Protocol AttribBlockProcessCall Write Block-Read Block Process Call Protocol AttribRawBytes Raw Read/Write N-BytesProtocol AttribRawProcessBytes Raw Process Call Protocol On the Asus T100TA, Bios use GenericSerialBus operation region to access i2c device to get battery info. Sample code From Asus T100TA Scope (_SB.I2C1) { Name (UMPC, ResourceTemplate () { I2cSerialBus (0x0066, ControllerInitiated, 0x00061A80, AddressingMode7Bit, "\\_SB.I2C1", 0x00, ResourceConsumer, , ) }) ... OperationRegion (DVUM, GenericSerialBus, Zero, 0x0100) Field (DVUM, BufferAcc, NoLock, Preserve) { Connection (UMPC), Offset (0x81), AccessAs (BufferAcc, AttribBytes (0x3E)), FGC0, 8 } ... } Device (BATC) { Name (_HID, EisaId ("PNP0C0A")) // _HID: Hardware ID Name (_UID, One) // _UID: Unique ID ... Method (_BST, 0, NotSerialized) // _BST: Battery Status { If (LEqual (AVBL, One)) { Store (FGC0, BFFG) If (LNotEqual (STAT, One)) { ShiftRight (CHST, 0x04, Local0) And (Local0, 0x03, Local0) If (LOr (LEqual (Local0, One), LEqual (Local0, 0x02))) { Store (0x02, Local1) } ... } The i2c operation region is defined under I2C1 scope. _BST method under battery device BATC read battery status from the field "FCG0". The request would be sent to i2c operation region handler. This patch is to add i2c ACPI operation region support. Due to there are only "Byte" and "Bytes" protocol access on the Asus T100TA, other protocols have not been tested. About RawBytes and RawProcessBytes protocol, they needs specific drivers to interpret reference data from AML code according ACPI 5.0 SPEC(5.5.2.4.5.3.9 and 5.5.2.4.5.3.10). So far, not found such case and will add when find real case. Signed-off-by: Lan Tianyu Reviewed-by: Mika Westerberg Signed-off-by: Wolfram Sang --- drivers/i2c/Makefile | 5 +- drivers/i2c/i2c-acpi.c | 273 +++++++++++++++++++++++++++++++++++++++++++++++++ drivers/i2c/i2c-core.c | 2 + include/linux/acpi.h | 11 ++ include/linux/i2c.h | 10 ++ 5 files changed, 300 insertions(+), 1 deletion(-) create mode 100644 drivers/i2c/i2c-acpi.c (limited to 'include') diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 1722f50f2473..80db3073aa84 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -2,8 +2,11 @@ # Makefile for the i2c core. # +i2ccore-y := i2c-core.o +i2ccore-$(CONFIG_ACPI) += i2c-acpi.o + obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o -obj-$(CONFIG_I2C) += i2c-core.o +obj-$(CONFIG_I2C) += i2ccore.o obj-$(CONFIG_I2C_SMBUS) += i2c-smbus.o obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o obj-$(CONFIG_I2C_MUX) += i2c-mux.o diff --git a/drivers/i2c/i2c-acpi.c b/drivers/i2c/i2c-acpi.c new file mode 100644 index 000000000000..f7f4c89c09b3 --- /dev/null +++ b/drivers/i2c/i2c-acpi.c @@ -0,0 +1,273 @@ +/* + * I2C ACPI code + * + * Copyright (C) 2014 Intel Corp + * + * Author: Lan Tianyu + * + * 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. + */ +#define pr_fmt(fmt) "I2C/ACPI : " fmt + +#include +#include +#include +#include +#include + +struct acpi_i2c_handler_data { + struct acpi_connection_info info; + struct i2c_adapter *adapter; +}; + +struct gsb_buffer { + u8 status; + u8 len; + union { + u16 wdata; + u8 bdata; + u8 data[0]; + }; +} __packed; + +static int acpi_gsb_i2c_read_bytes(struct i2c_client *client, + u8 cmd, u8 *data, u8 data_len) +{ + + struct i2c_msg msgs[2]; + int ret; + u8 *buffer; + + buffer = kzalloc(data_len, GFP_KERNEL); + if (!buffer) + return AE_NO_MEMORY; + + msgs[0].addr = client->addr; + msgs[0].flags = client->flags; + msgs[0].len = 1; + msgs[0].buf = &cmd; + + msgs[1].addr = client->addr; + msgs[1].flags = client->flags | I2C_M_RD; + msgs[1].len = data_len; + msgs[1].buf = buffer; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + dev_err(&client->adapter->dev, "i2c read failed\n"); + else + memcpy(data, buffer, data_len); + + kfree(buffer); + return ret; +} + +static int acpi_gsb_i2c_write_bytes(struct i2c_client *client, + u8 cmd, u8 *data, u8 data_len) +{ + + struct i2c_msg msgs[1]; + u8 *buffer; + int ret = AE_OK; + + buffer = kzalloc(data_len + 1, GFP_KERNEL); + if (!buffer) + return AE_NO_MEMORY; + + buffer[0] = cmd; + memcpy(buffer + 1, data, data_len); + + msgs[0].addr = client->addr; + msgs[0].flags = client->flags; + msgs[0].len = data_len + 1; + msgs[0].buf = buffer; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + dev_err(&client->adapter->dev, "i2c write failed\n"); + + kfree(buffer); + return ret; +} + +static acpi_status +acpi_i2c_space_handler(u32 function, acpi_physical_address command, + u32 bits, u64 *value64, + void *handler_context, void *region_context) +{ + struct gsb_buffer *gsb = (struct gsb_buffer *)value64; + struct acpi_i2c_handler_data *data = handler_context; + struct acpi_connection_info *info = &data->info; + struct acpi_resource_i2c_serialbus *sb; + struct i2c_adapter *adapter = data->adapter; + struct i2c_client client; + struct acpi_resource *ares; + u32 accessor_type = function >> 16; + u8 action = function & ACPI_IO_MASK; + acpi_status ret = AE_OK; + int status; + + ret = acpi_buffer_to_resource(info->connection, info->length, &ares); + if (ACPI_FAILURE(ret)) + return ret; + + if (!value64 || ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) { + ret = AE_BAD_PARAMETER; + goto err; + } + + sb = &ares->data.i2c_serial_bus; + if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_I2C) { + ret = AE_BAD_PARAMETER; + goto err; + } + + memset(&client, 0, sizeof(client)); + client.adapter = adapter; + client.addr = sb->slave_address; + client.flags = 0; + + if (sb->access_mode == ACPI_I2C_10BIT_MODE) + client.flags |= I2C_CLIENT_TEN; + + switch (accessor_type) { + case ACPI_GSB_ACCESS_ATTRIB_SEND_RCV: + if (action == ACPI_READ) { + status = i2c_smbus_read_byte(&client); + if (status >= 0) { + gsb->bdata = status; + status = 0; + } + } else { + status = i2c_smbus_write_byte(&client, gsb->bdata); + } + break; + + case ACPI_GSB_ACCESS_ATTRIB_BYTE: + if (action == ACPI_READ) { + status = i2c_smbus_read_byte_data(&client, command); + if (status >= 0) { + gsb->bdata = status; + status = 0; + } + } else { + status = i2c_smbus_write_byte_data(&client, command, + gsb->bdata); + } + break; + + case ACPI_GSB_ACCESS_ATTRIB_WORD: + if (action == ACPI_READ) { + status = i2c_smbus_read_word_data(&client, command); + if (status >= 0) { + gsb->wdata = status; + status = 0; + } + } else { + status = i2c_smbus_write_word_data(&client, command, + gsb->wdata); + } + break; + + case ACPI_GSB_ACCESS_ATTRIB_BLOCK: + if (action == ACPI_READ) { + status = i2c_smbus_read_block_data(&client, command, + gsb->data); + if (status >= 0) { + gsb->len = status; + status = 0; + } + } else { + status = i2c_smbus_write_block_data(&client, command, + gsb->len, gsb->data); + } + break; + + case ACPI_GSB_ACCESS_ATTRIB_MULTIBYTE: + if (action == ACPI_READ) { + status = acpi_gsb_i2c_read_bytes(&client, command, + gsb->data, info->access_length); + if (status > 0) + status = 0; + } else { + status = acpi_gsb_i2c_write_bytes(&client, command, + gsb->data, info->access_length); + } + break; + + default: + pr_info("protocol(0x%02x) is not supported.\n", accessor_type); + ret = AE_BAD_PARAMETER; + goto err; + } + + gsb->status = status; + + err: + ACPI_FREE(ares); + return ret; +} + + +int acpi_i2c_install_space_handler(struct i2c_adapter *adapter) +{ + acpi_handle handle = ACPI_HANDLE(adapter->dev.parent); + struct acpi_i2c_handler_data *data; + acpi_status status; + + if (!handle) + return -ENODEV; + + data = kzalloc(sizeof(struct acpi_i2c_handler_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->adapter = adapter; + status = acpi_bus_attach_private_data(handle, (void *)data); + if (ACPI_FAILURE(status)) { + kfree(data); + return -ENOMEM; + } + + status = acpi_install_address_space_handler(handle, + ACPI_ADR_SPACE_GSBUS, + &acpi_i2c_space_handler, + NULL, + data); + if (ACPI_FAILURE(status)) { + dev_err(&adapter->dev, "Error installing i2c space handler\n"); + acpi_bus_detach_private_data(handle); + kfree(data); + return -ENOMEM; + } + + return 0; +} + +void acpi_i2c_remove_space_handler(struct i2c_adapter *adapter) +{ + acpi_handle handle = ACPI_HANDLE(adapter->dev.parent); + struct acpi_i2c_handler_data *data; + acpi_status status; + + if (!handle) + return; + + acpi_remove_address_space_handler(handle, + ACPI_ADR_SPACE_GSBUS, + &acpi_i2c_space_handler); + + status = acpi_bus_get_private_data(handle, (void **)&data); + if (ACPI_SUCCESS(status)) + kfree(data); + + acpi_bus_detach_private_data(handle); +} diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 7c7f4b856bad..e25cb84cb297 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -1293,6 +1293,7 @@ exit_recovery: /* create pre-declared device nodes */ of_i2c_register_devices(adap); acpi_i2c_register_devices(adap); + acpi_i2c_install_space_handler(adap); if (adap->nr < __i2c_first_dynamic_bus_num) i2c_scan_static_board_info(adap); @@ -1466,6 +1467,7 @@ void i2c_del_adapter(struct i2c_adapter *adap) return; } + acpi_i2c_remove_space_handler(adap); /* Tell drivers about this removal */ mutex_lock(&core_lock); bus_for_each_drv(&i2c_bus_type, NULL, adap, diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 358c01b971db..40718e91e171 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -364,6 +364,17 @@ extern bool osc_sb_apei_support_acked; #define OSC_PCI_EXPRESS_CAPABILITY_CONTROL 0x00000010 #define OSC_PCI_CONTROL_MASKS 0x0000001f +#define ACPI_GSB_ACCESS_ATTRIB_QUICK 0x00000002 +#define ACPI_GSB_ACCESS_ATTRIB_SEND_RCV 0x00000004 +#define ACPI_GSB_ACCESS_ATTRIB_BYTE 0x00000006 +#define ACPI_GSB_ACCESS_ATTRIB_WORD 0x00000008 +#define ACPI_GSB_ACCESS_ATTRIB_BLOCK 0x0000000A +#define ACPI_GSB_ACCESS_ATTRIB_MULTIBYTE 0x0000000B +#define ACPI_GSB_ACCESS_ATTRIB_WORD_CALL 0x0000000C +#define ACPI_GSB_ACCESS_ATTRIB_BLOCK_CALL 0x0000000D +#define ACPI_GSB_ACCESS_ATTRIB_RAW_BYTES 0x0000000E +#define ACPI_GSB_ACCESS_ATTRIB_RAW_PROCESS 0x0000000F + extern acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 *mask, u32 req); diff --git a/include/linux/i2c.h b/include/linux/i2c.h index b556e0ab946f..f7a939a2cb56 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -577,4 +577,14 @@ static inline struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node } #endif /* CONFIG_OF */ +#ifdef CONFIG_ACPI +int acpi_i2c_install_space_handler(struct i2c_adapter *adapter); +void acpi_i2c_remove_space_handler(struct i2c_adapter *adapter); +#else +static inline void acpi_i2c_remove_space_handler(struct i2c_adapter *adapter) +{ } +static inline int acpi_i2c_install_space_handler(struct i2c_adapter *adapter) +{ return 0; } +#endif + #endif /* _LINUX_I2C_H */ -- cgit v1.2.3 From da3c6647ee08711c7edc28d7fea4ad69fc5ffcca Mon Sep 17 00:00:00 2001 From: Lan Tianyu Date: Tue, 20 May 2014 20:59:24 +0800 Subject: I2C/ACPI: Clean up I2C ACPI code and Add CONFIG_I2C_ACPI config Clean up ACPI related code in the i2c core and add CONFIG_I2C_ACPI to enable I2C ACPI code. Current there is a race between removing I2C ACPI operation region and ACPI AML code accessing. So make i2c core built-in if CONFIG_I2C_ACPI is set. Reviewed-by: Mika Westerberg Signed-off-by: Lan Tianyu Signed-off-by: Wolfram Sang --- drivers/i2c/Kconfig | 18 +++++++++- drivers/i2c/Makefile | 2 +- drivers/i2c/i2c-acpi.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/i2c/i2c-core.c | 95 -------------------------------------------------- include/linux/i2c.h | 4 ++- 5 files changed, 110 insertions(+), 98 deletions(-) (limited to 'include') diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 7b7ea320a258..3e3b680dc007 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -2,7 +2,9 @@ # I2C subsystem configuration # -menuconfig I2C +menu "I2C support" + +config I2C tristate "I2C support" select RT_MUTEXES ---help--- @@ -21,6 +23,18 @@ menuconfig I2C This I2C support can also be built as a module. If so, the module will be called i2c-core. +config I2C_ACPI + bool "I2C ACPI support" + select I2C + depends on ACPI + default y + help + Say Y here if you want to enable ACPI I2C support. This includes support + for automatic enumeration of I2C slave devices and support for ACPI I2C + Operation Regions. Operation Regions allow firmware (BIOS) code to + access I2C slave devices, such as smart batteries through an I2C host + controller driver. + if I2C config I2C_BOARDINFO @@ -124,3 +138,5 @@ config I2C_DEBUG_BUS on. endif # I2C + +endmenu diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 80db3073aa84..a1f590cbb435 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -3,7 +3,7 @@ # i2ccore-y := i2c-core.o -i2ccore-$(CONFIG_ACPI) += i2c-acpi.o +i2ccore-$(CONFIG_I2C_ACPI) += i2c-acpi.o obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o obj-$(CONFIG_I2C) += i2ccore.o diff --git a/drivers/i2c/i2c-acpi.c b/drivers/i2c/i2c-acpi.c index f7f4c89c09b3..e8b61967334b 100644 --- a/drivers/i2c/i2c-acpi.c +++ b/drivers/i2c/i2c-acpi.c @@ -37,6 +37,95 @@ struct gsb_buffer { }; } __packed; +static int acpi_i2c_add_resource(struct acpi_resource *ares, void *data) +{ + struct i2c_board_info *info = data; + + if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) { + struct acpi_resource_i2c_serialbus *sb; + + sb = &ares->data.i2c_serial_bus; + if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_I2C) { + info->addr = sb->slave_address; + if (sb->access_mode == ACPI_I2C_10BIT_MODE) + info->flags |= I2C_CLIENT_TEN; + } + } else if (info->irq < 0) { + struct resource r; + + if (acpi_dev_resource_interrupt(ares, 0, &r)) + info->irq = r.start; + } + + /* Tell the ACPI core to skip this resource */ + return 1; +} + +static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level, + void *data, void **return_value) +{ + struct i2c_adapter *adapter = data; + struct list_head resource_list; + struct i2c_board_info info; + struct acpi_device *adev; + int ret; + + if (acpi_bus_get_device(handle, &adev)) + return AE_OK; + if (acpi_bus_get_status(adev) || !adev->status.present) + return AE_OK; + + memset(&info, 0, sizeof(info)); + info.acpi_node.companion = adev; + info.irq = -1; + + INIT_LIST_HEAD(&resource_list); + ret = acpi_dev_get_resources(adev, &resource_list, + acpi_i2c_add_resource, &info); + acpi_dev_free_resource_list(&resource_list); + + if (ret < 0 || !info.addr) + return AE_OK; + + adev->power.flags.ignore_parent = true; + strlcpy(info.type, dev_name(&adev->dev), sizeof(info.type)); + if (!i2c_new_device(adapter, &info)) { + adev->power.flags.ignore_parent = false; + dev_err(&adapter->dev, + "failed to add I2C device %s from ACPI\n", + dev_name(&adev->dev)); + } + + return AE_OK; +} + +/** + * acpi_i2c_register_devices - enumerate I2C slave devices behind adapter + * @adap: pointer to adapter + * + * Enumerate all I2C slave devices behind this adapter by walking the ACPI + * namespace. When a device is found it will be added to the Linux device + * model and bound to the corresponding ACPI handle. + */ +void acpi_i2c_register_devices(struct i2c_adapter *adap) +{ + acpi_handle handle; + acpi_status status; + + if (!adap->dev.parent) + return; + + handle = ACPI_HANDLE(adap->dev.parent); + if (!handle) + return; + + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, + acpi_i2c_add_device, NULL, + adap, NULL); + if (ACPI_FAILURE(status)) + dev_warn(&adap->dev, "failed to enumerate I2C slaves\n"); +} + static int acpi_gsb_i2c_read_bytes(struct i2c_client *client, u8 cmd, u8 *data, u8 data_len) { diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index e25cb84cb297..4ccff114b147 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -1092,101 +1092,6 @@ EXPORT_SYMBOL(of_find_i2c_adapter_by_node); static void of_i2c_register_devices(struct i2c_adapter *adap) { } #endif /* CONFIG_OF */ -/* ACPI support code */ - -#if IS_ENABLED(CONFIG_ACPI) -static int acpi_i2c_add_resource(struct acpi_resource *ares, void *data) -{ - struct i2c_board_info *info = data; - - if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) { - struct acpi_resource_i2c_serialbus *sb; - - sb = &ares->data.i2c_serial_bus; - if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_I2C) { - info->addr = sb->slave_address; - if (sb->access_mode == ACPI_I2C_10BIT_MODE) - info->flags |= I2C_CLIENT_TEN; - } - } else if (info->irq < 0) { - struct resource r; - - if (acpi_dev_resource_interrupt(ares, 0, &r)) - info->irq = r.start; - } - - /* Tell the ACPI core to skip this resource */ - return 1; -} - -static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level, - void *data, void **return_value) -{ - struct i2c_adapter *adapter = data; - struct list_head resource_list; - struct i2c_board_info info; - struct acpi_device *adev; - int ret; - - if (acpi_bus_get_device(handle, &adev)) - return AE_OK; - if (acpi_bus_get_status(adev) || !adev->status.present) - return AE_OK; - - memset(&info, 0, sizeof(info)); - info.acpi_node.companion = adev; - info.irq = -1; - - INIT_LIST_HEAD(&resource_list); - ret = acpi_dev_get_resources(adev, &resource_list, - acpi_i2c_add_resource, &info); - acpi_dev_free_resource_list(&resource_list); - - if (ret < 0 || !info.addr) - return AE_OK; - - adev->power.flags.ignore_parent = true; - strlcpy(info.type, dev_name(&adev->dev), sizeof(info.type)); - if (!i2c_new_device(adapter, &info)) { - adev->power.flags.ignore_parent = false; - dev_err(&adapter->dev, - "failed to add I2C device %s from ACPI\n", - dev_name(&adev->dev)); - } - - return AE_OK; -} - -/** - * acpi_i2c_register_devices - enumerate I2C slave devices behind adapter - * @adap: pointer to adapter - * - * Enumerate all I2C slave devices behind this adapter by walking the ACPI - * namespace. When a device is found it will be added to the Linux device - * model and bound to the corresponding ACPI handle. - */ -static void acpi_i2c_register_devices(struct i2c_adapter *adap) -{ - acpi_handle handle; - acpi_status status; - - if (!adap->dev.parent) - return; - - handle = ACPI_HANDLE(adap->dev.parent); - if (!handle) - return; - - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, - acpi_i2c_add_device, NULL, - adap, NULL); - if (ACPI_FAILURE(status)) - dev_warn(&adap->dev, "failed to enumerate I2C slaves\n"); -} -#else -static inline void acpi_i2c_register_devices(struct i2c_adapter *adap) {} -#endif /* CONFIG_ACPI */ - static int i2c_do_add_adapter(struct i2c_driver *driver, struct i2c_adapter *adap) { diff --git a/include/linux/i2c.h b/include/linux/i2c.h index f7a939a2cb56..ea507665896c 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -577,10 +577,12 @@ static inline struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node } #endif /* CONFIG_OF */ -#ifdef CONFIG_ACPI +#ifdef CONFIG_I2C_ACPI int acpi_i2c_install_space_handler(struct i2c_adapter *adapter); void acpi_i2c_remove_space_handler(struct i2c_adapter *adapter); +void acpi_i2c_register_devices(struct i2c_adapter *adap); #else +static inline void acpi_i2c_register_devices(struct i2c_adapter *adap) { } static inline void acpi_i2c_remove_space_handler(struct i2c_adapter *adapter) { } static inline int acpi_i2c_install_space_handler(struct i2c_adapter *adapter) -- cgit v1.2.3 From 9ad7860450ea65c6bbcbd52a9a25b54b07e35941 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 27 Jun 2014 10:41:00 -0500 Subject: Revert "tools: ffs-test: convert to new descriptor format fixing compilation error" This reverts commit f2af74123f8c5a735248547f4286a3adc28633c1. There is a better fix for this build error coming in a following patch. Signed-of-by: Felipe Balbi --- include/uapi/linux/usb/functionfs.h | 2 +- tools/usb/Makefile | 6 +----- tools/usb/ffs-test.c | 20 ++------------------ 3 files changed, 4 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/usb/functionfs.h b/include/uapi/linux/usb/functionfs.h index ecb3a31f7ca6..2a4b4a72a4f9 100644 --- a/include/uapi/linux/usb/functionfs.h +++ b/include/uapi/linux/usb/functionfs.h @@ -53,7 +53,7 @@ struct usb_endpoint_descriptor_no_audio { * structure. Any flags that are not recognised cause the whole block to be * rejected with -ENOSYS. * - * Legacy descriptors format (deprecated as of 3.14): + * Legacy descriptors format: * * | off | name | type | description | * |-----+-----------+--------------+--------------------------------------| diff --git a/tools/usb/Makefile b/tools/usb/Makefile index d576b3bac3cf..acf2165c04e6 100644 --- a/tools/usb/Makefile +++ b/tools/usb/Makefile @@ -6,11 +6,7 @@ WARNINGS = -Wall -Wextra CFLAGS = $(WARNINGS) -g -I../include LDFLAGS = $(PTHREAD_LIBS) -all: testusb ffs-test ffs-test-legacy - -ffs-test-legacy: ffs-test.c - $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) -DUSE_LEGACY_DESC_HEAD - +all: testusb ffs-test %: %.c $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) diff --git a/tools/usb/ffs-test.c b/tools/usb/ffs-test.c index 74b353d9eb50..fe1e66b6ef40 100644 --- a/tools/usb/ffs-test.c +++ b/tools/usb/ffs-test.c @@ -1,5 +1,5 @@ /* - * ffs-test.c -- user mode filesystem api for usb composite function + * ffs-test.c.c -- user mode filesystem api for usb composite function * * Copyright (C) 2010 Samsung Electronics * Author: Michal Nazarewicz @@ -21,8 +21,6 @@ /* $(CROSS_COMPILE)cc -Wall -Wextra -g -o ffs-test ffs-test.c -lpthread */ -/* Uncomment to make the tool use legacy FFS descriptor headers. */ -/* #define USE_LEGACY_DESC_HEAD */ #define _BSD_SOURCE /* for endian.h */ @@ -108,15 +106,7 @@ static void _msg(unsigned level, const char *fmt, ...) /******************** Descriptors and Strings *******************************/ static const struct { - struct { - __le32 magic; - __le32 length; -#ifndef USE_LEGACY_DESC_HEAD - __le32 flags; -#endif - __le32 fs_count; - __le32 hs_count; - } __attribute__((packed)) header; + struct usb_functionfs_descs_head header; struct { struct usb_interface_descriptor intf; struct usb_endpoint_descriptor_no_audio sink; @@ -124,13 +114,7 @@ static const struct { } __attribute__((packed)) fs_descs, hs_descs; } __attribute__((packed)) descriptors = { .header = { -#ifdef USE_LEGACY_DESC_HEAD .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC), -#else - .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2), - .flags = cpu_to_le32(FUNCTIONFS_HAS_FS_DESC | - FUNCTIONFS_HAS_HS_DESC), -#endif .length = cpu_to_le32(sizeof descriptors), .fs_count = 3, .hs_count = 3, -- cgit v1.2.3 From 09122141785348bf9539762a5f5dbbae3761c783 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Fri, 13 Jun 2014 15:38:04 +0200 Subject: usb: gadget: f_fs: resurect usb_functionfs_descs_head structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Even though usb_functionfs_descs_head structure is now deprecated, it has been used by some user space tools. Its removel in commit [ac8dde1: “Add flags to descriptors block”] was an oversight leading to build breakage for such tools. Bring it back so that old user space tools can still be build without problems on newer kernel versions. Cc: # 3.14 Reported-by: Lad, Prabhakar Reported-by: Krzysztof Opasiak Signed-off-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- include/uapi/linux/usb/functionfs.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/usb/functionfs.h b/include/uapi/linux/usb/functionfs.h index 2a4b4a72a4f9..24b68c59dcf8 100644 --- a/include/uapi/linux/usb/functionfs.h +++ b/include/uapi/linux/usb/functionfs.h @@ -33,6 +33,13 @@ struct usb_endpoint_descriptor_no_audio { __u8 bInterval; } __attribute__((packed)); +/* Legacy format, deprecated as of 3.14. */ +struct usb_functionfs_descs_head { + __le32 magic; + __le32 length; + __le32 fs_count; + __le32 hs_count; +} __attribute__((packed, deprecated)); /* * Descriptors format: -- cgit v1.2.3 From ac5ccdba3a1659b3517e7e99ef7d35a6a2d77cf4 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 19 Jun 2014 21:22:56 +0300 Subject: iovec: move memcpy_from/toiovecend to lib/iovec.c ERROR: "memcpy_fromiovecend" [drivers/vhost/vhost_scsi.ko] undefined! commit 9f977ef7b671f6169eca78bf40f230fe84b7c7e5 vhost-scsi: Include prot_bytes into expected data transfer length in target-pending makes drivers/vhost/scsi.c call memcpy_fromiovecend(). This function is not available when CONFIG_NET is not enabled. socket.h already includes uio.h, so no callers need updating. Reported-by: Randy Dunlap Cc: Stephen Rothwell Cc: "David S. Miller" Signed-off-by: David S. Miller Signed-off-by: Michael S. Tsirkin Signed-off-by: Nicholas Bellinger --- include/linux/socket.h | 4 ---- include/linux/uio.h | 5 ++++- lib/iovec.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++ net/core/iovec.c | 55 -------------------------------------------------- 4 files changed, 59 insertions(+), 60 deletions(-) (limited to 'include') diff --git a/include/linux/socket.h b/include/linux/socket.h index 8e98297f1388..ec538fc287a6 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -305,8 +305,6 @@ struct ucred { /* IPX options */ #define IPX_TYPE 1 -extern int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov, - int offset, int len); extern int csum_partial_copy_fromiovecend(unsigned char *kdata, struct iovec *iov, int offset, @@ -315,8 +313,6 @@ extern unsigned long iov_pages(const struct iovec *iov, int offset, unsigned long nr_segs); extern int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr_storage *address, int mode); -extern int memcpy_toiovecend(const struct iovec *v, unsigned char *kdata, - int offset, int len); extern int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr_storage *kaddr); extern int put_cmsg(struct msghdr*, int level, int type, int len, void *data); diff --git a/include/linux/uio.h b/include/linux/uio.h index e2231e47cec1..04c8c4bb4927 100644 --- a/include/linux/uio.h +++ b/include/linux/uio.h @@ -111,6 +111,9 @@ static inline void iov_iter_reexpand(struct iov_iter *i, size_t count) int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len); int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len); - +int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov, + int offset, int len); +int memcpy_toiovecend(const struct iovec *v, unsigned char *kdata, + int offset, int len); #endif diff --git a/lib/iovec.c b/lib/iovec.c index 454baa88bf27..7a7c2da4cddf 100644 --- a/lib/iovec.c +++ b/lib/iovec.c @@ -51,3 +51,58 @@ int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len) return 0; } EXPORT_SYMBOL(memcpy_toiovec); + +/* + * Copy kernel to iovec. Returns -EFAULT on error. + */ + +int memcpy_toiovecend(const struct iovec *iov, unsigned char *kdata, + int offset, int len) +{ + int copy; + for (; len > 0; ++iov) { + /* Skip over the finished iovecs */ + if (unlikely(offset >= iov->iov_len)) { + offset -= iov->iov_len; + continue; + } + copy = min_t(unsigned int, iov->iov_len - offset, len); + if (copy_to_user(iov->iov_base + offset, kdata, copy)) + return -EFAULT; + offset = 0; + kdata += copy; + len -= copy; + } + + return 0; +} +EXPORT_SYMBOL(memcpy_toiovecend); + +/* + * Copy iovec to kernel. Returns -EFAULT on error. + */ + +int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov, + int offset, int len) +{ + /* Skip over the finished iovecs */ + while (offset >= iov->iov_len) { + offset -= iov->iov_len; + iov++; + } + + while (len > 0) { + u8 __user *base = iov->iov_base + offset; + int copy = min_t(unsigned int, len, iov->iov_len - offset); + + offset = 0; + if (copy_from_user(kdata, base, copy)) + return -EFAULT; + len -= copy; + kdata += copy; + iov++; + } + + return 0; +} +EXPORT_SYMBOL(memcpy_fromiovecend); diff --git a/net/core/iovec.c b/net/core/iovec.c index b61869429f4c..827dd6beb49c 100644 --- a/net/core/iovec.c +++ b/net/core/iovec.c @@ -74,61 +74,6 @@ int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr_storage *a return err; } -/* - * Copy kernel to iovec. Returns -EFAULT on error. - */ - -int memcpy_toiovecend(const struct iovec *iov, unsigned char *kdata, - int offset, int len) -{ - int copy; - for (; len > 0; ++iov) { - /* Skip over the finished iovecs */ - if (unlikely(offset >= iov->iov_len)) { - offset -= iov->iov_len; - continue; - } - copy = min_t(unsigned int, iov->iov_len - offset, len); - if (copy_to_user(iov->iov_base + offset, kdata, copy)) - return -EFAULT; - offset = 0; - kdata += copy; - len -= copy; - } - - return 0; -} -EXPORT_SYMBOL(memcpy_toiovecend); - -/* - * Copy iovec to kernel. Returns -EFAULT on error. - */ - -int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov, - int offset, int len) -{ - /* Skip over the finished iovecs */ - while (offset >= iov->iov_len) { - offset -= iov->iov_len; - iov++; - } - - while (len > 0) { - u8 __user *base = iov->iov_base + offset; - int copy = min_t(unsigned int, len, iov->iov_len - offset); - - offset = 0; - if (copy_from_user(kdata, base, copy)) - return -EFAULT; - len -= copy; - kdata += copy; - iov++; - } - - return 0; -} -EXPORT_SYMBOL(memcpy_fromiovecend); - /* * And now for the all-in-one: copy and checksum from a user iovec * directly to a datagram -- cgit v1.2.3 From 57b47553f65e12e2e4f1608168374b0e651de843 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Wed, 25 Jun 2014 17:09:50 +0300 Subject: tcp: cookie_v4_init_sequence: skb should be const Signed-off-by: Octavian Purdila Signed-off-by: David S. Miller --- include/net/tcp.h | 5 +++-- net/ipv4/syncookies.c | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 06a3023080c0..39e47c4e4f19 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -493,10 +493,11 @@ static inline u32 tcp_cookie_time(void) u32 __cookie_v4_init_sequence(const struct iphdr *iph, const struct tcphdr *th, u16 *mssp); -__u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mss); +__u32 cookie_v4_init_sequence(struct sock *sk, const struct sk_buff *skb, + __u16 *mss); #else static inline __u32 cookie_v4_init_sequence(struct sock *sk, - struct sk_buff *skb, + const struct sk_buff *skb, __u16 *mss) { return 0; diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index c86624b36a62..c0c75688896e 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -170,7 +170,8 @@ u32 __cookie_v4_init_sequence(const struct iphdr *iph, const struct tcphdr *th, } EXPORT_SYMBOL_GPL(__cookie_v4_init_sequence); -__u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp) +__u32 cookie_v4_init_sequence(struct sock *sk, const struct sk_buff *skb, + __u16 *mssp) { const struct iphdr *iph = ip_hdr(skb); const struct tcphdr *th = tcp_hdr(skb); -- cgit v1.2.3 From 476eab8251641ea2ae4666ca8a1436ebc2b8e9c3 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Wed, 25 Jun 2014 17:09:52 +0300 Subject: net: remove inet6_reqsk_alloc Since pktops is only used for IPv6 only and opts is used for IPv4 only, we can move these fields into a union and this allows us to drop the inet6_reqsk_alloc function as after this change it becomes equivalent with inet_reqsk_alloc. This patch also fixes a kmemcheck issue in the IPv6 stack: the flags field was not annotated after a request_sock was allocated. Signed-off-by: Octavian Purdila Signed-off-by: David S. Miller --- include/linux/ipv6.h | 10 ---------- include/net/inet_sock.h | 6 ++++-- net/dccp/ipv6.c | 2 +- net/ipv6/syncookies.c | 2 +- net/ipv6/tcp_ipv6.c | 2 +- 5 files changed, 7 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 2faef339d8f2..c811300b0b0c 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -256,16 +256,6 @@ static inline struct ipv6_pinfo * inet6_sk(const struct sock *__sk) return inet_sk(__sk)->pinet6; } -static inline struct request_sock *inet6_reqsk_alloc(struct request_sock_ops *ops) -{ - struct request_sock *req = reqsk_alloc(ops); - - if (req) - inet_rsk(req)->pktopts = NULL; - - return req; -} - static inline struct raw6_sock *raw6_sk(const struct sock *sk) { return (struct raw6_sock *)sk; diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index b1edf17bec01..a829b77523cf 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -88,8 +88,10 @@ struct inet_request_sock { acked : 1, no_srccheck: 1; kmemcheck_bitfield_end(flags); - struct ip_options_rcu *opt; - struct sk_buff *pktopts; + union { + struct ip_options_rcu *opt; + struct sk_buff *pktopts; + }; u32 ir_mark; }; diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 4db3c2a1679c..04cb17d4b0ce 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -386,7 +386,7 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb) if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) goto drop; - req = inet6_reqsk_alloc(&dccp6_request_sock_ops); + req = inet_reqsk_alloc(&dccp6_request_sock_ops); if (req == NULL) goto drop; diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index a822b880689b..83cea1d39466 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -187,7 +187,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) goto out; ret = NULL; - req = inet6_reqsk_alloc(&tcp6_request_sock_ops); + req = inet_reqsk_alloc(&tcp6_request_sock_ops); if (!req) goto out; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index a962455471ba..5e2d7e655c0f 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1010,7 +1010,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) goto drop; } - req = inet6_reqsk_alloc(&tcp6_request_sock_ops); + req = inet_reqsk_alloc(&tcp6_request_sock_ops); if (req == NULL) goto drop; -- cgit v1.2.3 From 16bea70aa7302b6f3bf3502d5a0efb4ea2ce4712 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Wed, 25 Jun 2014 17:09:53 +0300 Subject: tcp: add init_req method to tcp_request_sock_ops Move the specific IPv4/IPv6 intializations to a new method in tcp_request_sock_ops in preparation for unifying tcp_v4_conn_request and tcp_v6_conn_request. Signed-off-by: Octavian Purdila Signed-off-by: David S. Miller --- include/linux/tcp.h | 3 --- include/net/tcp.h | 2 ++ net/ipv4/tcp_ipv4.c | 29 ++++++++++++++++------------ net/ipv6/tcp_ipv6.c | 55 +++++++++++++++++++++++++++++++---------------------- 4 files changed, 51 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index a0513210798f..fa5258f322e7 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -111,10 +111,7 @@ struct tcp_request_sock_ops; struct tcp_request_sock { struct inet_request_sock req; -#ifdef CONFIG_TCP_MD5SIG - /* Only used by TCP MD5 Signature so far. */ const struct tcp_request_sock_ops *af_specific; -#endif struct sock *listener; /* needed for TFO */ u32 rcv_isn; u32 snt_isn; diff --git a/include/net/tcp.h b/include/net/tcp.h index 39e47c4e4f19..7ad8ce296c3b 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1613,6 +1613,8 @@ struct tcp_request_sock_ops { const struct request_sock *req, const struct sk_buff *skb); #endif + void (*init_req)(struct request_sock *req, struct sock *sk, + struct sk_buff *skb); }; int tcpv4_offload_init(void); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 145f6402c560..f86a86b30d20 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1237,6 +1237,17 @@ static bool tcp_v4_inbound_md5_hash(struct sock *sk, const struct sk_buff *skb) #endif +static void tcp_v4_init_req(struct request_sock *req, struct sock *sk, + struct sk_buff *skb) +{ + struct inet_request_sock *ireq = inet_rsk(req); + + ireq->ir_loc_addr = ip_hdr(skb)->daddr; + ireq->ir_rmt_addr = ip_hdr(skb)->saddr; + ireq->no_srccheck = inet_sk(sk)->transparent; + ireq->opt = tcp_v4_save_options(skb); +} + struct request_sock_ops tcp_request_sock_ops __read_mostly = { .family = PF_INET, .obj_size = sizeof(struct tcp_request_sock), @@ -1247,26 +1258,26 @@ struct request_sock_ops tcp_request_sock_ops __read_mostly = { .syn_ack_timeout = tcp_syn_ack_timeout, }; -#ifdef CONFIG_TCP_MD5SIG static const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = { +#ifdef CONFIG_TCP_MD5SIG .md5_lookup = tcp_v4_reqsk_md5_lookup, .calc_md5_hash = tcp_v4_md5_hash_skb, -}; #endif + .init_req = tcp_v4_init_req, +}; int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) { struct tcp_options_received tmp_opt; struct request_sock *req; - struct inet_request_sock *ireq; struct tcp_sock *tp = tcp_sk(sk); struct dst_entry *dst = NULL; __be32 saddr = ip_hdr(skb)->saddr; - __be32 daddr = ip_hdr(skb)->daddr; __u32 isn = TCP_SKB_CB(skb)->when; bool want_cookie = false, fastopen; struct flowi4 fl4; struct tcp_fastopen_cookie foc = { .len = -1 }; + const struct tcp_request_sock_ops *af_ops; int err; /* Never answer to SYNs send to broadcast or multicast */ @@ -1298,9 +1309,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) if (!req) goto drop; -#ifdef CONFIG_TCP_MD5SIG - tcp_rsk(req)->af_specific = &tcp_request_sock_ipv4_ops; -#endif + af_ops = tcp_rsk(req)->af_specific = &tcp_request_sock_ipv4_ops; tcp_clear_options(&tmp_opt); tmp_opt.mss_clamp = TCP_MSS_DEFAULT; @@ -1313,11 +1322,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) tmp_opt.tstamp_ok = tmp_opt.saw_tstamp; tcp_openreq_init(req, &tmp_opt, skb, sk); - ireq = inet_rsk(req); - ireq->ir_loc_addr = daddr; - ireq->ir_rmt_addr = saddr; - ireq->no_srccheck = inet_sk(sk)->transparent; - ireq->opt = tcp_v4_save_options(skb); + af_ops->init_req(req, sk, skb); if (security_inet_conn_request(sk, skb, req)) goto drop_and_free; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 5e2d7e655c0f..87a360c3eba9 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -720,6 +720,31 @@ static int tcp_v6_inbound_md5_hash(struct sock *sk, const struct sk_buff *skb) } #endif +static void tcp_v6_init_req(struct request_sock *req, struct sock *sk, + struct sk_buff *skb) +{ + struct inet_request_sock *ireq = inet_rsk(req); + struct ipv6_pinfo *np = inet6_sk(sk); + + ireq->ir_v6_rmt_addr = ipv6_hdr(skb)->saddr; + ireq->ir_v6_loc_addr = ipv6_hdr(skb)->daddr; + + ireq->ir_iif = sk->sk_bound_dev_if; + + /* So that link locals have meaning */ + if (!sk->sk_bound_dev_if && + ipv6_addr_type(&ireq->ir_v6_rmt_addr) & IPV6_ADDR_LINKLOCAL) + ireq->ir_iif = inet6_iif(skb); + + if (!TCP_SKB_CB(skb)->when && + (ipv6_opt_accepted(sk, skb) || np->rxopt.bits.rxinfo || + np->rxopt.bits.rxoinfo || np->rxopt.bits.rxhlim || + np->rxopt.bits.rxohlim || np->repflow)) { + atomic_inc(&skb->users); + ireq->pktopts = skb; + } +} + struct request_sock_ops tcp6_request_sock_ops __read_mostly = { .family = AF_INET6, .obj_size = sizeof(struct tcp6_request_sock), @@ -730,12 +755,13 @@ struct request_sock_ops tcp6_request_sock_ops __read_mostly = { .syn_ack_timeout = tcp_syn_ack_timeout, }; -#ifdef CONFIG_TCP_MD5SIG static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = { +#ifdef CONFIG_TCP_MD5SIG .md5_lookup = tcp_v6_reqsk_md5_lookup, .calc_md5_hash = tcp_v6_md5_hash_skb, -}; #endif + .init_req = tcp_v6_init_req, +}; static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 tsval, u32 tsecr, int oif, @@ -983,13 +1009,13 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) struct tcp_options_received tmp_opt; struct request_sock *req; struct inet_request_sock *ireq; - struct ipv6_pinfo *np = inet6_sk(sk); struct tcp_sock *tp = tcp_sk(sk); __u32 isn = TCP_SKB_CB(skb)->when; struct dst_entry *dst = NULL; struct tcp_fastopen_cookie foc = { .len = -1 }; bool want_cookie = false, fastopen; struct flowi6 fl6; + const struct tcp_request_sock_ops *af_ops; int err; if (skb->protocol == htons(ETH_P_IP)) @@ -1014,9 +1040,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) if (req == NULL) goto drop; -#ifdef CONFIG_TCP_MD5SIG - tcp_rsk(req)->af_specific = &tcp_request_sock_ipv6_ops; -#endif + af_ops = tcp_rsk(req)->af_specific = &tcp_request_sock_ipv6_ops; tcp_clear_options(&tmp_opt); tmp_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr); @@ -1030,27 +1054,12 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) tcp_openreq_init(req, &tmp_opt, skb, sk); ireq = inet_rsk(req); - ireq->ir_v6_rmt_addr = ipv6_hdr(skb)->saddr; - ireq->ir_v6_loc_addr = ipv6_hdr(skb)->daddr; + af_ops->init_req(req, sk, skb); + if (!want_cookie || tmp_opt.tstamp_ok) TCP_ECN_create_request(req, skb, sock_net(sk)); - ireq->ir_iif = sk->sk_bound_dev_if; - - /* So that link locals have meaning */ - if (!sk->sk_bound_dev_if && - ipv6_addr_type(&ireq->ir_v6_rmt_addr) & IPV6_ADDR_LINKLOCAL) - ireq->ir_iif = inet6_iif(skb); - if (!isn) { - if (ipv6_opt_accepted(sk, skb) || - np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo || - np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim || - np->repflow) { - atomic_inc(&skb->users); - ireq->pktopts = skb; - } - if (want_cookie) { isn = cookie_v6_init_sequence(sk, skb, &req->mss); req->cookie_ts = tmp_opt.tstamp_ok; -- cgit v1.2.3 From fb7b37a7f3d6f7b7ba05ee526fee96810d5b92a8 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Wed, 25 Jun 2014 17:09:54 +0300 Subject: tcp: add init_cookie_seq method to tcp_request_sock_ops Move the specific IPv4/IPv6 cookie sequence initialization to a new method in tcp_request_sock_ops in preparation for unifying tcp_v4_conn_request and tcp_v6_conn_request. Signed-off-by: Octavian Purdila Signed-off-by: David S. Miller --- include/net/tcp.h | 34 ++++++++++++++++++++-------------- net/ipv4/tcp_ipv4.c | 5 ++++- net/ipv6/tcp_ipv6.c | 5 ++++- 3 files changed, 28 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 7ad8ce296c3b..086d00ec6d8b 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -495,13 +495,6 @@ u32 __cookie_v4_init_sequence(const struct iphdr *iph, const struct tcphdr *th, u16 *mssp); __u32 cookie_v4_init_sequence(struct sock *sk, const struct sk_buff *skb, __u16 *mss); -#else -static inline __u32 cookie_v4_init_sequence(struct sock *sk, - const struct sk_buff *skb, - __u16 *mss) -{ - return 0; -} #endif __u32 cookie_init_timestamp(struct request_sock *req); @@ -517,13 +510,6 @@ u32 __cookie_v6_init_sequence(const struct ipv6hdr *iph, const struct tcphdr *th, u16 *mssp); __u32 cookie_v6_init_sequence(struct sock *sk, const struct sk_buff *skb, __u16 *mss); -#else -static inline __u32 cookie_v6_init_sequence(struct sock *sk, - struct sk_buff *skb, - __u16 *mss) -{ - return 0; -} #endif /* tcp_output.c */ @@ -1615,8 +1601,28 @@ struct tcp_request_sock_ops { #endif void (*init_req)(struct request_sock *req, struct sock *sk, struct sk_buff *skb); +#ifdef CONFIG_SYN_COOKIES + __u32 (*cookie_init_seq)(struct sock *sk, const struct sk_buff *skb, + __u16 *mss); +#endif }; +#ifdef CONFIG_SYN_COOKIES +static inline __u32 cookie_init_sequence(const struct tcp_request_sock_ops *ops, + struct sock *sk, struct sk_buff *skb, + __u16 *mss) +{ + return ops->cookie_init_seq(sk, skb, mss); +} +#else +static inline __u32 cookie_init_sequence(const struct tcp_request_sock_ops *ops, + struct sock *sk, struct sk_buff *skb, + __u16 *mss) +{ + return 0; +} +#endif + int tcpv4_offload_init(void); void tcp_v4_init(void); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index f86a86b30d20..8c69e44c287b 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1264,6 +1264,9 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = { .calc_md5_hash = tcp_v4_md5_hash_skb, #endif .init_req = tcp_v4_init_req, +#ifdef CONFIG_SYN_COOKIES + .cookie_init_seq = cookie_v4_init_sequence, +#endif }; int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) @@ -1331,7 +1334,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) TCP_ECN_create_request(req, skb, sock_net(sk)); if (want_cookie) { - isn = cookie_v4_init_sequence(sk, skb, &req->mss); + isn = cookie_init_sequence(af_ops, sk, skb, &req->mss); req->cookie_ts = tmp_opt.tstamp_ok; } else if (!isn) { /* VJ's idea. We save last timestamp seen diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 87a360c3eba9..17710cffddaa 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -761,6 +761,9 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = { .calc_md5_hash = tcp_v6_md5_hash_skb, #endif .init_req = tcp_v6_init_req, +#ifdef CONFIG_SYN_COOKIES + .cookie_init_seq = cookie_v6_init_sequence, +#endif }; static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win, @@ -1061,7 +1064,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) if (!isn) { if (want_cookie) { - isn = cookie_v6_init_sequence(sk, skb, &req->mss); + isn = cookie_init_sequence(af_ops, sk, skb, &req->mss); req->cookie_ts = tmp_opt.tstamp_ok; goto have_isn; } -- cgit v1.2.3 From d94e0417ad8d96d7d96b69335338ad942eaeecf1 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Wed, 25 Jun 2014 17:09:55 +0300 Subject: tcp: add route_req method to tcp_request_sock_ops Create wrappers with same signature for the IPv4/IPv6 request routing calls and use these wrappers (via route_req method from tcp_request_sock_ops) in tcp_v4_conn_request and tcp_v6_conn_request with the purpose of unifying the two functions in a later patch. We can later drop the wrapper functions and modify inet_csk_route_req and inet6_cks_route_req to use the same signature. Signed-off-by: Octavian Purdila Signed-off-by: David S. Miller --- include/net/tcp.h | 3 +++ net/ipv4/tcp_ipv4.c | 36 +++++++++++++++++++++++++++++------- net/ipv6/tcp_ipv6.c | 26 ++++++++++++++++++++------ 3 files changed, 52 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 086d00ec6d8b..59fcc5934c79 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1605,6 +1605,9 @@ struct tcp_request_sock_ops { __u32 (*cookie_init_seq)(struct sock *sk, const struct sk_buff *skb, __u16 *mss); #endif + struct dst_entry *(*route_req)(struct sock *sk, struct flowi *fl, + const struct request_sock *req, + bool *strict); }; #ifdef CONFIG_SYN_COOKIES diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 8c69e44c287b..54fbbd8b4fcd 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1248,6 +1248,22 @@ static void tcp_v4_init_req(struct request_sock *req, struct sock *sk, ireq->opt = tcp_v4_save_options(skb); } +static struct dst_entry *tcp_v4_route_req(struct sock *sk, struct flowi *fl, + const struct request_sock *req, + bool *strict) +{ + struct dst_entry *dst = inet_csk_route_req(sk, &fl->u.ip4, req); + + if (strict) { + if (fl->u.ip4.daddr == inet_rsk(req)->ir_rmt_addr) + *strict = true; + else + *strict = false; + } + + return dst; +} + struct request_sock_ops tcp_request_sock_ops __read_mostly = { .family = PF_INET, .obj_size = sizeof(struct tcp_request_sock), @@ -1267,6 +1283,7 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = { #ifdef CONFIG_SYN_COOKIES .cookie_init_seq = cookie_v4_init_sequence, #endif + .route_req = tcp_v4_route_req, }; int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) @@ -1346,11 +1363,13 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) * timewait bucket, so that all the necessary checks * are made in the function processing timewait state. */ - if (tmp_opt.saw_tstamp && - tcp_death_row.sysctl_tw_recycle && - (dst = inet_csk_route_req(sk, &fl4, req)) != NULL && - fl4.daddr == saddr) { - if (!tcp_peer_is_proven(req, dst, true)) { + if (tmp_opt.saw_tstamp && tcp_death_row.sysctl_tw_recycle) { + bool strict; + + dst = af_ops->route_req(sk, (struct flowi *)&fl4, req, + &strict); + if (dst && strict && + !tcp_peer_is_proven(req, dst, true)) { NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED); goto drop_and_release; } @@ -1374,8 +1393,11 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) isn = tcp_v4_init_sequence(skb); } - if (!dst && (dst = inet_csk_route_req(sk, &fl4, req)) == NULL) - goto drop_and_free; + if (!dst) { + dst = af_ops->route_req(sk, (struct flowi *)&fl4, req, NULL); + if (!dst) + goto drop_and_free; + } tcp_rsk(req)->snt_isn = isn; tcp_openreq_init_rwin(req, sk, dst); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 17710cffddaa..d780d8808566 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -745,6 +745,16 @@ static void tcp_v6_init_req(struct request_sock *req, struct sock *sk, } } +static struct dst_entry *tcp_v6_route_req(struct sock *sk, struct flowi *fl, + const struct request_sock *req, + bool *strict) +{ + if (strict) + *strict = true; + return inet6_csk_route_req(sk, &fl->u.ip6, req); +} + + struct request_sock_ops tcp6_request_sock_ops __read_mostly = { .family = AF_INET6, .obj_size = sizeof(struct tcp6_request_sock), @@ -764,6 +774,7 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = { #ifdef CONFIG_SYN_COOKIES .cookie_init_seq = cookie_v6_init_sequence, #endif + .route_req = tcp_v6_route_req, }; static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win, @@ -1078,10 +1089,10 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) * timewait bucket, so that all the necessary checks * are made in the function processing timewait state. */ - if (tmp_opt.saw_tstamp && - tcp_death_row.sysctl_tw_recycle && - (dst = inet6_csk_route_req(sk, &fl6, req)) != NULL) { - if (!tcp_peer_is_proven(req, dst, true)) { + if (tmp_opt.saw_tstamp && tcp_death_row.sysctl_tw_recycle) { + dst = af_ops->route_req(sk, (struct flowi *)&fl6, req, + NULL); + if (dst && !tcp_peer_is_proven(req, dst, true)) { NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED); goto drop_and_release; } @@ -1110,8 +1121,11 @@ have_isn: if (security_inet_conn_request(sk, skb, req)) goto drop_and_release; - if (!dst && (dst = inet6_csk_route_req(sk, &fl6, req)) == NULL) - goto drop_and_free; + if (!dst) { + dst = af_ops->route_req(sk, (struct flowi *)&fl6, req, NULL); + if (!dst) + goto drop_and_free; + } tcp_rsk(req)->snt_isn = isn; tcp_openreq_init_rwin(req, sk, dst); -- cgit v1.2.3 From 936b8bdb53f90840e658904530f9db8d02ac804b Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Wed, 25 Jun 2014 17:09:57 +0300 Subject: tcp: add init_seq method to tcp_request_sock_ops More work in preparation of unifying tcp_v4_conn_request and tcp_v6_conn_request: indirect the init sequence calls via the tcp_request_sock_ops. Signed-off-by: Octavian Purdila Signed-off-by: David S. Miller --- include/net/tcp.h | 1 + net/ipv4/tcp_ipv4.c | 5 +++-- net/ipv6/tcp_ipv6.c | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 59fcc5934c79..8cacf0d6ed4d 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1608,6 +1608,7 @@ struct tcp_request_sock_ops { struct dst_entry *(*route_req)(struct sock *sk, struct flowi *fl, const struct request_sock *req, bool *strict); + __u32 (*init_seq)(const struct sk_buff *skb); }; #ifdef CONFIG_SYN_COOKIES diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 54fbbd8b4fcd..43478265006a 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -99,7 +99,7 @@ static int tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key, struct inet_hashinfo tcp_hashinfo; EXPORT_SYMBOL(tcp_hashinfo); -static inline __u32 tcp_v4_init_sequence(const struct sk_buff *skb) +static __u32 tcp_v4_init_sequence(const struct sk_buff *skb) { return secure_tcp_sequence_number(ip_hdr(skb)->daddr, ip_hdr(skb)->saddr, @@ -1284,6 +1284,7 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = { .cookie_init_seq = cookie_v4_init_sequence, #endif .route_req = tcp_v4_route_req, + .init_seq = tcp_v4_init_sequence, }; int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) @@ -1391,7 +1392,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) goto drop_and_release; } - isn = tcp_v4_init_sequence(skb); + isn = af_ops->init_seq(skb); } if (!dst) { dst = af_ops->route_req(sk, (struct flowi *)&fl4, req, NULL); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 91b8a2e699f3..2fd886fe8340 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -775,6 +775,7 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = { .cookie_init_seq = cookie_v6_init_sequence, #endif .route_req = tcp_v6_route_req, + .init_seq = tcp_v6_init_sequence, }; static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win, @@ -1114,7 +1115,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) goto drop_and_release; } - isn = tcp_v6_init_sequence(skb); + isn = af_ops->init_seq(skb); } if (!dst) { -- cgit v1.2.3 From d6274bd8d6ea84b7b54cc1c3fde6bcb6143b104f Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Wed, 25 Jun 2014 17:09:58 +0300 Subject: tcp: add send_synack method to tcp_request_sock_ops Create a new tcp_request_sock_ops method to unify the IPv4/IPv6 signature for tcp_v[46]_send_synack. This allows us to later unify tcp_v4_rtx_synack with tcp_v6_rtx_synack and tcp_v4_conn_request with tcp_v4_conn_request. Signed-off-by: Octavian Purdila Signed-off-by: David S. Miller --- include/net/tcp.h | 3 +++ net/ipv4/tcp_ipv4.c | 9 ++++++--- net/ipv6/tcp_ipv6.c | 14 ++++++++------ 3 files changed, 17 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 8cacf0d6ed4d..8c05c25018d5 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1609,6 +1609,9 @@ struct tcp_request_sock_ops { const struct request_sock *req, bool *strict); __u32 (*init_seq)(const struct sk_buff *skb); + int (*send_synack)(struct sock *sk, struct dst_entry *dst, + struct flowi *fl, struct request_sock *req, + u16 queue_mapping, struct tcp_fastopen_cookie *foc); }; #ifdef CONFIG_SYN_COOKIES diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 43478265006a..b5945ac50876 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -814,6 +814,7 @@ static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, * socket. */ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, + struct flowi *fl, struct request_sock *req, u16 queue_mapping, struct tcp_fastopen_cookie *foc) @@ -846,7 +847,8 @@ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, static int tcp_v4_rtx_synack(struct sock *sk, struct request_sock *req) { - int res = tcp_v4_send_synack(sk, NULL, req, 0, NULL); + const struct tcp_request_sock_ops *af_ops = tcp_rsk(req)->af_specific; + int res = af_ops->send_synack(sk, NULL, NULL, req, 0, NULL); if (!res) { TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS); @@ -1285,6 +1287,7 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = { #endif .route_req = tcp_v4_route_req, .init_seq = tcp_v4_init_sequence, + .send_synack = tcp_v4_send_synack, }; int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) @@ -1404,8 +1407,8 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) tcp_openreq_init_rwin(req, sk, dst); fastopen = !want_cookie && tcp_try_fastopen(sk, skb, req, &foc, dst); - err = tcp_v4_send_synack(sk, dst, req, - skb_get_queue_mapping(skb), &foc); + err = af_ops->send_synack(sk, dst, NULL, req, + skb_get_queue_mapping(skb), &foc); if (!fastopen) { if (err || want_cookie) goto drop_and_free; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 2fd886fe8340..210b6105afed 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -470,13 +470,14 @@ out: static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst, - struct flowi6 *fl6, + struct flowi *fl, struct request_sock *req, u16 queue_mapping, struct tcp_fastopen_cookie *foc) { struct inet_request_sock *ireq = inet_rsk(req); struct ipv6_pinfo *np = inet6_sk(sk); + struct flowi6 *fl6 = &fl->u.ip6; struct sk_buff *skb; int err = -ENOMEM; @@ -507,10 +508,11 @@ done: static int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req) { - struct flowi6 fl6; + const struct tcp_request_sock_ops *af_ops = tcp_rsk(req)->af_specific; + struct flowi fl; int res; - res = tcp_v6_send_synack(sk, NULL, &fl6, req, 0, NULL); + res = af_ops->send_synack(sk, NULL, &fl, req, 0, NULL); if (!res) { TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS); NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPSYNRETRANS); @@ -754,7 +756,6 @@ static struct dst_entry *tcp_v6_route_req(struct sock *sk, struct flowi *fl, return inet6_csk_route_req(sk, &fl->u.ip6, req); } - struct request_sock_ops tcp6_request_sock_ops __read_mostly = { .family = AF_INET6, .obj_size = sizeof(struct tcp6_request_sock), @@ -776,6 +777,7 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = { #endif .route_req = tcp_v6_route_req, .init_seq = tcp_v6_init_sequence, + .send_synack = tcp_v6_send_synack, }; static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win, @@ -1128,8 +1130,8 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) tcp_openreq_init_rwin(req, sk, dst); fastopen = !want_cookie && tcp_try_fastopen(sk, skb, req, &foc, dst); - err = tcp_v6_send_synack(sk, dst, &fl6, req, - skb_get_queue_mapping(skb), &foc); + err = af_ops->send_synack(sk, dst, (struct flowi *)&fl6, req, + skb_get_queue_mapping(skb), &foc); if (!fastopen) { if (err || want_cookie) goto drop_and_free; -- cgit v1.2.3 From 5db92c994982ed826cf38f38d58bd09bc326aef6 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Wed, 25 Jun 2014 17:09:59 +0300 Subject: tcp: unify tcp_v4_rtx_synack and tcp_v6_rtx_synack Signed-off-by: Octavian Purdila Signed-off-by: David S. Miller --- include/net/tcp.h | 2 ++ net/ipv4/tcp_ipv4.c | 14 +------------- net/ipv4/tcp_output.c | 15 +++++++++++++++ net/ipv6/tcp_ipv6.c | 15 +-------------- 4 files changed, 19 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 8c05c25018d5..8e9c28dccb80 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1573,6 +1573,8 @@ int tcp4_proc_init(void); void tcp4_proc_exit(void); #endif +int tcp_rtx_synack(struct sock *sk, struct request_sock *req); + /* TCP af-specific functions */ struct tcp_sock_af_ops { #ifdef CONFIG_TCP_MD5SIG diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index b5945ac50876..597dd9d75210 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -845,18 +845,6 @@ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, return err; } -static int tcp_v4_rtx_synack(struct sock *sk, struct request_sock *req) -{ - const struct tcp_request_sock_ops *af_ops = tcp_rsk(req)->af_specific; - int res = af_ops->send_synack(sk, NULL, NULL, req, 0, NULL); - - if (!res) { - TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS); - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPSYNRETRANS); - } - return res; -} - /* * IPv4 request_sock destructor. */ @@ -1269,7 +1257,7 @@ static struct dst_entry *tcp_v4_route_req(struct sock *sk, struct flowi *fl, struct request_sock_ops tcp_request_sock_ops __read_mostly = { .family = PF_INET, .obj_size = sizeof(struct tcp_request_sock), - .rtx_syn_ack = tcp_v4_rtx_synack, + .rtx_syn_ack = tcp_rtx_synack, .send_ack = tcp_v4_reqsk_send_ack, .destructor = tcp_v4_reqsk_destructor, .send_reset = tcp_v4_send_reset, diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index d92bce0ea24e..f8f2a944a1ce 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -3299,3 +3299,18 @@ void tcp_send_probe0(struct sock *sk) TCP_RTO_MAX); } } + +int tcp_rtx_synack(struct sock *sk, struct request_sock *req) +{ + const struct tcp_request_sock_ops *af_ops = tcp_rsk(req)->af_specific; + struct flowi fl; + int res; + + res = af_ops->send_synack(sk, NULL, &fl, req, 0, NULL); + if (!res) { + TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS); + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPSYNRETRANS); + } + return res; +} +EXPORT_SYMBOL(tcp_rtx_synack); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 210b6105afed..41389bbb08c0 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -506,19 +506,6 @@ done: return err; } -static int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req) -{ - const struct tcp_request_sock_ops *af_ops = tcp_rsk(req)->af_specific; - struct flowi fl; - int res; - - res = af_ops->send_synack(sk, NULL, &fl, req, 0, NULL); - if (!res) { - TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS); - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPSYNRETRANS); - } - return res; -} static void tcp_v6_reqsk_destructor(struct request_sock *req) { @@ -759,7 +746,7 @@ static struct dst_entry *tcp_v6_route_req(struct sock *sk, struct flowi *fl, struct request_sock_ops tcp6_request_sock_ops __read_mostly = { .family = AF_INET6, .obj_size = sizeof(struct tcp6_request_sock), - .rtx_syn_ack = tcp_v6_rtx_synack, + .rtx_syn_ack = tcp_rtx_synack, .send_ack = tcp_v6_reqsk_send_ack, .destructor = tcp_v6_reqsk_destructor, .send_reset = tcp_v6_send_reset, -- cgit v1.2.3 From 2aec4a297b21f3690486bbf8f7d5d29281ba6a48 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Wed, 25 Jun 2014 17:10:00 +0300 Subject: tcp: add mss_clamp to tcp_request_sock_ops Add mss_clamp member to tcp_request_sock_ops so that we can later unify tcp_v4_conn_request and tcp_v6_conn_request. Signed-off-by: Octavian Purdila Signed-off-by: David S. Miller --- include/net/tcp.h | 1 + net/ipv4/tcp_ipv4.c | 3 ++- net/ipv6/tcp_ipv6.c | 4 +++- 3 files changed, 6 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 8e9c28dccb80..30fe98bc8957 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1592,6 +1592,7 @@ struct tcp_sock_af_ops { }; struct tcp_request_sock_ops { + u16 mss_clamp; #ifdef CONFIG_TCP_MD5SIG struct tcp_md5sig_key *(*md5_lookup) (struct sock *sk, struct request_sock *req); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 597dd9d75210..499d440539ad 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1265,6 +1265,7 @@ struct request_sock_ops tcp_request_sock_ops __read_mostly = { }; static const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = { + .mss_clamp = TCP_MSS_DEFAULT, #ifdef CONFIG_TCP_MD5SIG .md5_lookup = tcp_v4_reqsk_md5_lookup, .calc_md5_hash = tcp_v4_md5_hash_skb, @@ -1324,7 +1325,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) af_ops = tcp_rsk(req)->af_specific = &tcp_request_sock_ipv4_ops; tcp_clear_options(&tmp_opt); - tmp_opt.mss_clamp = TCP_MSS_DEFAULT; + tmp_opt.mss_clamp = af_ops->mss_clamp; tmp_opt.user_mss = tp->rx_opt.user_mss; tcp_parse_options(skb, &tmp_opt, 0, want_cookie ? NULL : &foc); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 41389bbb08c0..ad658332cf7d 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -754,6 +754,8 @@ struct request_sock_ops tcp6_request_sock_ops __read_mostly = { }; static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = { + .mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - + sizeof(struct ipv6hdr), #ifdef CONFIG_TCP_MD5SIG .md5_lookup = tcp_v6_reqsk_md5_lookup, .calc_md5_hash = tcp_v6_md5_hash_skb, @@ -1047,7 +1049,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) af_ops = tcp_rsk(req)->af_specific = &tcp_request_sock_ipv6_ops; tcp_clear_options(&tmp_opt); - tmp_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr); + tmp_opt.mss_clamp = af_ops->mss_clamp; tmp_opt.user_mss = tp->rx_opt.user_mss; tcp_parse_options(skb, &tmp_opt, 0, want_cookie ? NULL : &foc); -- cgit v1.2.3 From 695da14eb0af21129187ed3810e329b21262e45f Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Wed, 25 Jun 2014 17:10:01 +0300 Subject: tcp: add queue_add_hash to tcp_request_sock_ops Add queue_add_hash member to tcp_request_sock_ops so that we can later unify tcp_v4_conn_request and tcp_v6_conn_request. Signed-off-by: Octavian Purdila Signed-off-by: David S. Miller --- include/net/tcp.h | 2 ++ net/ipv4/tcp_ipv4.c | 3 ++- net/ipv6/tcp_ipv6.c | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 30fe98bc8957..cec6e2cf0610 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1615,6 +1615,8 @@ struct tcp_request_sock_ops { int (*send_synack)(struct sock *sk, struct dst_entry *dst, struct flowi *fl, struct request_sock *req, u16 queue_mapping, struct tcp_fastopen_cookie *foc); + void (*queue_hash_add)(struct sock *sk, struct request_sock *req, + const unsigned long timeout); }; #ifdef CONFIG_SYN_COOKIES diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 499d440539ad..845c39de97ab 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1277,6 +1277,7 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = { .route_req = tcp_v4_route_req, .init_seq = tcp_v4_init_sequence, .send_synack = tcp_v4_send_synack, + .queue_hash_add = inet_csk_reqsk_queue_hash_add, }; int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) @@ -1403,7 +1404,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) goto drop_and_free; tcp_rsk(req)->listener = NULL; - inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT); + af_ops->queue_hash_add(sk, req, TCP_TIMEOUT_INIT); } return 0; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index ad658332cf7d..8232bc7423c6 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -767,6 +767,7 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = { .route_req = tcp_v6_route_req, .init_seq = tcp_v6_init_sequence, .send_synack = tcp_v6_send_synack, + .queue_hash_add = inet6_csk_reqsk_queue_hash_add, }; static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win, @@ -1126,7 +1127,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) goto drop_and_free; tcp_rsk(req)->listener = NULL; - inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT); + af_ops->queue_hash_add(sk, req, TCP_TIMEOUT_INIT); } return 0; -- cgit v1.2.3 From 1fb6f159fd21c640a28eb65fbd62ce8c9f6a777e Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Wed, 25 Jun 2014 17:10:02 +0300 Subject: tcp: add tcp_conn_request Create tcp_conn_request and remove most of the code from tcp_v4_conn_request and tcp_v6_conn_request. Signed-off-by: Octavian Purdila Signed-off-by: David S. Miller --- include/net/tcp.h | 3 ++ net/ipv4/tcp_input.c | 148 +++++++++++++++++++++++++++++++++++++++++++++++++++ net/ipv4/tcp_ipv4.c | 128 +------------------------------------------- net/ipv6/tcp_ipv6.c | 120 +---------------------------------------- 4 files changed, 155 insertions(+), 244 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index cec6e2cf0610..0d5389aecf18 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1574,6 +1574,9 @@ void tcp4_proc_exit(void); #endif int tcp_rtx_synack(struct sock *sk, struct request_sock *req); +int tcp_conn_request(struct request_sock_ops *rsk_ops, + const struct tcp_request_sock_ops *af_ops, + struct sock *sk, struct sk_buff *skb); /* TCP af-specific functions */ struct tcp_sock_af_ops { diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index b5c23756965a..97e48d60c4e8 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5877,3 +5877,151 @@ discard: return 0; } EXPORT_SYMBOL(tcp_rcv_state_process); + +static inline void pr_drop_req(struct request_sock *req, __u16 port, int family) +{ + struct inet_request_sock *ireq = inet_rsk(req); + + if (family == AF_INET) + LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("drop open request from %pI4/%u\n"), + &ireq->ir_rmt_addr, port); + else + LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("drop open request from %pI6/%u\n"), + &ireq->ir_v6_rmt_addr, port); +} + +int tcp_conn_request(struct request_sock_ops *rsk_ops, + const struct tcp_request_sock_ops *af_ops, + struct sock *sk, struct sk_buff *skb) +{ + struct tcp_options_received tmp_opt; + struct request_sock *req; + struct tcp_sock *tp = tcp_sk(sk); + struct dst_entry *dst = NULL; + __u32 isn = TCP_SKB_CB(skb)->when; + bool want_cookie = false, fastopen; + struct flowi fl; + struct tcp_fastopen_cookie foc = { .len = -1 }; + int err; + + + /* TW buckets are converted to open requests without + * limitations, they conserve resources and peer is + * evidently real one. + */ + if ((sysctl_tcp_syncookies == 2 || + inet_csk_reqsk_queue_is_full(sk)) && !isn) { + want_cookie = tcp_syn_flood_action(sk, skb, rsk_ops->slab_name); + if (!want_cookie) + goto drop; + } + + + /* Accept backlog is full. If we have already queued enough + * of warm entries in syn queue, drop request. It is better than + * clogging syn queue with openreqs with exponentially increasing + * timeout. + */ + if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) { + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); + goto drop; + } + + req = inet_reqsk_alloc(rsk_ops); + if (!req) + goto drop; + + tcp_rsk(req)->af_specific = af_ops; + + tcp_clear_options(&tmp_opt); + tmp_opt.mss_clamp = af_ops->mss_clamp; + tmp_opt.user_mss = tp->rx_opt.user_mss; + tcp_parse_options(skb, &tmp_opt, 0, want_cookie ? NULL : &foc); + + if (want_cookie && !tmp_opt.saw_tstamp) + tcp_clear_options(&tmp_opt); + + tmp_opt.tstamp_ok = tmp_opt.saw_tstamp; + tcp_openreq_init(req, &tmp_opt, skb, sk); + + af_ops->init_req(req, sk, skb); + + if (security_inet_conn_request(sk, skb, req)) + goto drop_and_free; + + if (!want_cookie || tmp_opt.tstamp_ok) + TCP_ECN_create_request(req, skb, sock_net(sk)); + + if (want_cookie) { + isn = cookie_init_sequence(af_ops, sk, skb, &req->mss); + req->cookie_ts = tmp_opt.tstamp_ok; + } else if (!isn) { + /* VJ's idea. We save last timestamp seen + * from the destination in peer table, when entering + * state TIME-WAIT, and check against it before + * accepting new connection request. + * + * If "isn" is not zero, this request hit alive + * timewait bucket, so that all the necessary checks + * are made in the function processing timewait state. + */ + if (tmp_opt.saw_tstamp && tcp_death_row.sysctl_tw_recycle) { + bool strict; + + dst = af_ops->route_req(sk, &fl, req, &strict); + if (dst && strict && + !tcp_peer_is_proven(req, dst, true)) { + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED); + goto drop_and_release; + } + } + /* Kill the following clause, if you dislike this way. */ + else if (!sysctl_tcp_syncookies && + (sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(sk) < + (sysctl_max_syn_backlog >> 2)) && + !tcp_peer_is_proven(req, dst, false)) { + /* Without syncookies last quarter of + * backlog is filled with destinations, + * proven to be alive. + * It means that we continue to communicate + * to destinations, already remembered + * to the moment of synflood. + */ + pr_drop_req(req, ntohs(tcp_hdr(skb)->source), + rsk_ops->family); + goto drop_and_release; + } + + isn = af_ops->init_seq(skb); + } + if (!dst) { + dst = af_ops->route_req(sk, &fl, req, NULL); + if (!dst) + goto drop_and_free; + } + + tcp_rsk(req)->snt_isn = isn; + tcp_openreq_init_rwin(req, sk, dst); + fastopen = !want_cookie && + tcp_try_fastopen(sk, skb, req, &foc, dst); + err = af_ops->send_synack(sk, dst, &fl, req, + skb_get_queue_mapping(skb), &foc); + if (!fastopen) { + if (err || want_cookie) + goto drop_and_free; + + tcp_rsk(req)->listener = NULL; + af_ops->queue_hash_add(sk, req, TCP_TIMEOUT_INIT); + } + + return 0; + +drop_and_release: + dst_release(dst); +drop_and_free: + reqsk_free(req); +drop: + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); + return 0; +} +EXPORT_SYMBOL(tcp_conn_request); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 845c39de97ab..5dfebd2f2e38 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1282,137 +1282,13 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = { int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) { - struct tcp_options_received tmp_opt; - struct request_sock *req; - struct tcp_sock *tp = tcp_sk(sk); - struct dst_entry *dst = NULL; - __be32 saddr = ip_hdr(skb)->saddr; - __u32 isn = TCP_SKB_CB(skb)->when; - bool want_cookie = false, fastopen; - struct flowi4 fl4; - struct tcp_fastopen_cookie foc = { .len = -1 }; - const struct tcp_request_sock_ops *af_ops; - int err; - /* Never answer to SYNs send to broadcast or multicast */ if (skb_rtable(skb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) goto drop; - /* TW buckets are converted to open requests without - * limitations, they conserve resources and peer is - * evidently real one. - */ - if ((sysctl_tcp_syncookies == 2 || - inet_csk_reqsk_queue_is_full(sk)) && !isn) { - want_cookie = tcp_syn_flood_action(sk, skb, "TCP"); - if (!want_cookie) - goto drop; - } - - /* Accept backlog is full. If we have already queued enough - * of warm entries in syn queue, drop request. It is better than - * clogging syn queue with openreqs with exponentially increasing - * timeout. - */ - if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) { - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); - goto drop; - } - - req = inet_reqsk_alloc(&tcp_request_sock_ops); - if (!req) - goto drop; - - af_ops = tcp_rsk(req)->af_specific = &tcp_request_sock_ipv4_ops; - - tcp_clear_options(&tmp_opt); - tmp_opt.mss_clamp = af_ops->mss_clamp; - tmp_opt.user_mss = tp->rx_opt.user_mss; - tcp_parse_options(skb, &tmp_opt, 0, want_cookie ? NULL : &foc); - - if (want_cookie && !tmp_opt.saw_tstamp) - tcp_clear_options(&tmp_opt); - - tmp_opt.tstamp_ok = tmp_opt.saw_tstamp; - tcp_openreq_init(req, &tmp_opt, skb, sk); - - af_ops->init_req(req, sk, skb); - - if (security_inet_conn_request(sk, skb, req)) - goto drop_and_free; + return tcp_conn_request(&tcp_request_sock_ops, + &tcp_request_sock_ipv4_ops, sk, skb); - if (!want_cookie || tmp_opt.tstamp_ok) - TCP_ECN_create_request(req, skb, sock_net(sk)); - - if (want_cookie) { - isn = cookie_init_sequence(af_ops, sk, skb, &req->mss); - req->cookie_ts = tmp_opt.tstamp_ok; - } else if (!isn) { - /* VJ's idea. We save last timestamp seen - * from the destination in peer table, when entering - * state TIME-WAIT, and check against it before - * accepting new connection request. - * - * If "isn" is not zero, this request hit alive - * timewait bucket, so that all the necessary checks - * are made in the function processing timewait state. - */ - if (tmp_opt.saw_tstamp && tcp_death_row.sysctl_tw_recycle) { - bool strict; - - dst = af_ops->route_req(sk, (struct flowi *)&fl4, req, - &strict); - if (dst && strict && - !tcp_peer_is_proven(req, dst, true)) { - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED); - goto drop_and_release; - } - } - /* Kill the following clause, if you dislike this way. */ - else if (!sysctl_tcp_syncookies && - (sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(sk) < - (sysctl_max_syn_backlog >> 2)) && - !tcp_peer_is_proven(req, dst, false)) { - /* Without syncookies last quarter of - * backlog is filled with destinations, - * proven to be alive. - * It means that we continue to communicate - * to destinations, already remembered - * to the moment of synflood. - */ - LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("drop open request from %pI4/%u\n"), - &saddr, ntohs(tcp_hdr(skb)->source)); - goto drop_and_release; - } - - isn = af_ops->init_seq(skb); - } - if (!dst) { - dst = af_ops->route_req(sk, (struct flowi *)&fl4, req, NULL); - if (!dst) - goto drop_and_free; - } - - tcp_rsk(req)->snt_isn = isn; - tcp_openreq_init_rwin(req, sk, dst); - fastopen = !want_cookie && - tcp_try_fastopen(sk, skb, req, &foc, dst); - err = af_ops->send_synack(sk, dst, NULL, req, - skb_get_queue_mapping(skb), &foc); - if (!fastopen) { - if (err || want_cookie) - goto drop_and_free; - - tcp_rsk(req)->listener = NULL; - af_ops->queue_hash_add(sk, req, TCP_TIMEOUT_INIT); - } - - return 0; - -drop_and_release: - dst_release(dst); -drop_and_free: - reqsk_free(req); drop: NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); return 0; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 8232bc7423c6..bc24ee21339a 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1008,133 +1008,17 @@ static struct sock *tcp_v6_hnd_req(struct sock *sk, struct sk_buff *skb) return sk; } -/* FIXME: this is substantially similar to the ipv4 code. - * Can some kind of merge be done? -- erics - */ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) { - struct tcp_options_received tmp_opt; - struct request_sock *req; - struct inet_request_sock *ireq; - struct tcp_sock *tp = tcp_sk(sk); - __u32 isn = TCP_SKB_CB(skb)->when; - struct dst_entry *dst = NULL; - struct tcp_fastopen_cookie foc = { .len = -1 }; - bool want_cookie = false, fastopen; - struct flowi6 fl6; - const struct tcp_request_sock_ops *af_ops; - int err; - if (skb->protocol == htons(ETH_P_IP)) return tcp_v4_conn_request(sk, skb); if (!ipv6_unicast_destination(skb)) goto drop; - if ((sysctl_tcp_syncookies == 2 || - inet_csk_reqsk_queue_is_full(sk)) && !isn) { - want_cookie = tcp_syn_flood_action(sk, skb, "TCPv6"); - if (!want_cookie) - goto drop; - } + return tcp_conn_request(&tcp6_request_sock_ops, + &tcp_request_sock_ipv6_ops, sk, skb); - if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) { - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); - goto drop; - } - - req = inet_reqsk_alloc(&tcp6_request_sock_ops); - if (req == NULL) - goto drop; - - af_ops = tcp_rsk(req)->af_specific = &tcp_request_sock_ipv6_ops; - - tcp_clear_options(&tmp_opt); - tmp_opt.mss_clamp = af_ops->mss_clamp; - tmp_opt.user_mss = tp->rx_opt.user_mss; - tcp_parse_options(skb, &tmp_opt, 0, want_cookie ? NULL : &foc); - - if (want_cookie && !tmp_opt.saw_tstamp) - tcp_clear_options(&tmp_opt); - - tmp_opt.tstamp_ok = tmp_opt.saw_tstamp; - tcp_openreq_init(req, &tmp_opt, skb, sk); - - ireq = inet_rsk(req); - af_ops->init_req(req, sk, skb); - - if (security_inet_conn_request(sk, skb, req)) - goto drop_and_release; - - if (!want_cookie || tmp_opt.tstamp_ok) - TCP_ECN_create_request(req, skb, sock_net(sk)); - - if (want_cookie) { - isn = cookie_init_sequence(af_ops, sk, skb, &req->mss); - req->cookie_ts = tmp_opt.tstamp_ok; - } else if (!isn) { - /* VJ's idea. We save last timestamp seen - * from the destination in peer table, when entering - * state TIME-WAIT, and check against it before - * accepting new connection request. - * - * If "isn" is not zero, this request hit alive - * timewait bucket, so that all the necessary checks - * are made in the function processing timewait state. - */ - if (tmp_opt.saw_tstamp && tcp_death_row.sysctl_tw_recycle) { - dst = af_ops->route_req(sk, (struct flowi *)&fl6, req, - NULL); - if (dst && !tcp_peer_is_proven(req, dst, true)) { - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED); - goto drop_and_release; - } - } - /* Kill the following clause, if you dislike this way. */ - else if (!sysctl_tcp_syncookies && - (sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(sk) < - (sysctl_max_syn_backlog >> 2)) && - !tcp_peer_is_proven(req, dst, false)) { - /* Without syncookies last quarter of - * backlog is filled with destinations, - * proven to be alive. - * It means that we continue to communicate - * to destinations, already remembered - * to the moment of synflood. - */ - LIMIT_NETDEBUG(KERN_DEBUG "TCP: drop open request from %pI6/%u\n", - &ireq->ir_v6_rmt_addr, ntohs(tcp_hdr(skb)->source)); - goto drop_and_release; - } - - isn = af_ops->init_seq(skb); - } - - if (!dst) { - dst = af_ops->route_req(sk, (struct flowi *)&fl6, req, NULL); - if (!dst) - goto drop_and_free; - } - - tcp_rsk(req)->snt_isn = isn; - tcp_openreq_init_rwin(req, sk, dst); - fastopen = !want_cookie && - tcp_try_fastopen(sk, skb, req, &foc, dst); - err = af_ops->send_synack(sk, dst, (struct flowi *)&fl6, req, - skb_get_queue_mapping(skb), &foc); - if (!fastopen) { - if (err || want_cookie) - goto drop_and_free; - - tcp_rsk(req)->listener = NULL; - af_ops->queue_hash_add(sk, req, TCP_TIMEOUT_INIT); - } - return 0; - -drop_and_release: - dst_release(dst); -drop_and_free: - reqsk_free(req); drop: NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); return 0; /* don't send reset */ -- cgit v1.2.3 From d630dc4c9adb41e5bd1e06df2dbeaf622469ddd5 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 28 Jun 2014 08:10:12 -0400 Subject: percpu-refcount: one bit is enough for REF_STATUS percpu-refcount currently reserves two lowest bits of its percpu pointer to indicate its state; however, only one bit is used for PCPU_REF_DEAD. Simplify it by removing PCPU_STATUS_BITS/MASK and testing PCPU_REF_DEAD directly. This also allows the compiler to choose a more efficient instruction depending on the architecture. Signed-off-by: Tejun Heo Cc: Kent Overstreet Cc: Christoph Lameter --- include/linux/percpu-refcount.h | 4 +--- lib/percpu-refcount.c | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/percpu-refcount.h b/include/linux/percpu-refcount.h index 5d8920e23073..bfdeb0d48e21 100644 --- a/include/linux/percpu-refcount.h +++ b/include/linux/percpu-refcount.h @@ -88,12 +88,10 @@ static inline void percpu_ref_kill(struct percpu_ref *ref) return percpu_ref_kill_and_confirm(ref, NULL); } -#define PCPU_STATUS_BITS 2 -#define PCPU_STATUS_MASK ((1 << PCPU_STATUS_BITS) - 1) #define PCPU_REF_PTR 0 #define PCPU_REF_DEAD 1 -#define REF_STATUS(count) (((unsigned long) count) & PCPU_STATUS_MASK) +#define REF_STATUS(count) (((unsigned long) count) & PCPU_REF_DEAD) /** * percpu_ref_get - increment a percpu refcount diff --git a/lib/percpu-refcount.c b/lib/percpu-refcount.c index 963b7034a51b..17bce2bccc14 100644 --- a/lib/percpu-refcount.c +++ b/lib/percpu-refcount.c @@ -96,7 +96,7 @@ static void percpu_ref_kill_rcu(struct rcu_head *rcu) /* Mask out PCPU_REF_DEAD */ pcpu_count = (unsigned __percpu *) - (((unsigned long) pcpu_count) & ~PCPU_STATUS_MASK); + (((unsigned long) pcpu_count) & ~PCPU_REF_DEAD); for_each_possible_cpu(cpu) count += *per_cpu_ptr(pcpu_count, cpu); -- cgit v1.2.3 From eae7975ddf031b3084f4a5f7d88f698aefad96fb Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 28 Jun 2014 08:10:13 -0400 Subject: percpu-refcount: add helpers for ->percpu_count accesses * All four percpu_ref_*() operations implemented in the header file perform the same operation to determine whether the percpu_ref is alive and extract the percpu pointer. Factor out the common logic into __pcpu_ref_alive(). This doesn't change the generated code. * There are a couple places in percpu-refcount.c which masks out PCPU_REF_DEAD to obtain the percpu pointer. Factor it out into pcpu_count_ptr(). * The above changes make the WARN_ON_ONCE() conditional at the top of percpu_ref_kill_and_confirm() the only user of REF_STATUS(). Test PCPU_REF_DEAD directly and remove REF_STATUS(). This patch doesn't introduce any functional change. Signed-off-by: Tejun Heo Cc: Kent Overstreet Cc: Christoph Lameter --- include/linux/percpu-refcount.h | 35 +++++++++++++++++++++-------------- lib/percpu-refcount.c | 17 +++++++++-------- 2 files changed, 30 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/include/linux/percpu-refcount.h b/include/linux/percpu-refcount.h index bfdeb0d48e21..b62a4ee6d6ad 100644 --- a/include/linux/percpu-refcount.h +++ b/include/linux/percpu-refcount.h @@ -88,10 +88,25 @@ static inline void percpu_ref_kill(struct percpu_ref *ref) return percpu_ref_kill_and_confirm(ref, NULL); } -#define PCPU_REF_PTR 0 #define PCPU_REF_DEAD 1 -#define REF_STATUS(count) (((unsigned long) count) & PCPU_REF_DEAD) +/* + * Internal helper. Don't use outside percpu-refcount proper. The + * function doesn't return the pointer and let the caller test it for NULL + * because doing so forces the compiler to generate two conditional + * branches as it can't assume that @ref->pcpu_count is not NULL. + */ +static inline bool __pcpu_ref_alive(struct percpu_ref *ref, + unsigned __percpu **pcpu_countp) +{ + unsigned long pcpu_ptr = (unsigned long)ACCESS_ONCE(ref->pcpu_count); + + if (unlikely(pcpu_ptr & PCPU_REF_DEAD)) + return false; + + *pcpu_countp = (unsigned __percpu *)pcpu_ptr; + return true; +} /** * percpu_ref_get - increment a percpu refcount @@ -105,9 +120,7 @@ static inline void percpu_ref_get(struct percpu_ref *ref) rcu_read_lock_sched(); - pcpu_count = ACCESS_ONCE(ref->pcpu_count); - - if (likely(REF_STATUS(pcpu_count) == PCPU_REF_PTR)) + if (__pcpu_ref_alive(ref, &pcpu_count)) this_cpu_inc(*pcpu_count); else atomic_inc(&ref->count); @@ -131,9 +144,7 @@ static inline bool percpu_ref_tryget(struct percpu_ref *ref) rcu_read_lock_sched(); - pcpu_count = ACCESS_ONCE(ref->pcpu_count); - - if (likely(REF_STATUS(pcpu_count) == PCPU_REF_PTR)) { + if (__pcpu_ref_alive(ref, &pcpu_count)) { this_cpu_inc(*pcpu_count); ret = true; } else { @@ -166,9 +177,7 @@ static inline bool percpu_ref_tryget_live(struct percpu_ref *ref) rcu_read_lock_sched(); - pcpu_count = ACCESS_ONCE(ref->pcpu_count); - - if (likely(REF_STATUS(pcpu_count) == PCPU_REF_PTR)) { + if (__pcpu_ref_alive(ref, &pcpu_count)) { this_cpu_inc(*pcpu_count); ret = true; } @@ -191,9 +200,7 @@ static inline void percpu_ref_put(struct percpu_ref *ref) rcu_read_lock_sched(); - pcpu_count = ACCESS_ONCE(ref->pcpu_count); - - if (likely(REF_STATUS(pcpu_count) == PCPU_REF_PTR)) + if (__pcpu_ref_alive(ref, &pcpu_count)) this_cpu_dec(*pcpu_count); else if (unlikely(atomic_dec_and_test(&ref->count))) ref->release(ref); diff --git a/lib/percpu-refcount.c b/lib/percpu-refcount.c index 17bce2bccc14..087f1a04f9bc 100644 --- a/lib/percpu-refcount.c +++ b/lib/percpu-refcount.c @@ -31,6 +31,11 @@ #define PCPU_COUNT_BIAS (1U << 31) +static unsigned __percpu *pcpu_count_ptr(struct percpu_ref *ref) +{ + return (unsigned __percpu *)((unsigned long)ref->pcpu_count & ~PCPU_REF_DEAD); +} + /** * percpu_ref_init - initialize a percpu refcount * @ref: percpu_ref to initialize @@ -74,7 +79,7 @@ EXPORT_SYMBOL_GPL(percpu_ref_init); */ void percpu_ref_cancel_init(struct percpu_ref *ref) { - unsigned __percpu *pcpu_count = ref->pcpu_count; + unsigned __percpu *pcpu_count = pcpu_count_ptr(ref); int cpu; WARN_ON_ONCE(atomic_read(&ref->count) != 1 + PCPU_COUNT_BIAS); @@ -82,7 +87,7 @@ void percpu_ref_cancel_init(struct percpu_ref *ref) if (pcpu_count) { for_each_possible_cpu(cpu) WARN_ON_ONCE(*per_cpu_ptr(pcpu_count, cpu)); - free_percpu(ref->pcpu_count); + free_percpu(pcpu_count); } } EXPORT_SYMBOL_GPL(percpu_ref_cancel_init); @@ -90,14 +95,10 @@ EXPORT_SYMBOL_GPL(percpu_ref_cancel_init); static void percpu_ref_kill_rcu(struct rcu_head *rcu) { struct percpu_ref *ref = container_of(rcu, struct percpu_ref, rcu); - unsigned __percpu *pcpu_count = ref->pcpu_count; + unsigned __percpu *pcpu_count = pcpu_count_ptr(ref); unsigned count = 0; int cpu; - /* Mask out PCPU_REF_DEAD */ - pcpu_count = (unsigned __percpu *) - (((unsigned long) pcpu_count) & ~PCPU_REF_DEAD); - for_each_possible_cpu(cpu) count += *per_cpu_ptr(pcpu_count, cpu); @@ -152,7 +153,7 @@ static void percpu_ref_kill_rcu(struct rcu_head *rcu) void percpu_ref_kill_and_confirm(struct percpu_ref *ref, percpu_ref_func_t *confirm_kill) { - WARN_ONCE(REF_STATUS(ref->pcpu_count) == PCPU_REF_DEAD, + WARN_ONCE((unsigned long)ref->pcpu_count & PCPU_REF_DEAD, "percpu_ref_kill() called more than once!\n"); ref->pcpu_count = (unsigned __percpu *) -- cgit v1.2.3 From 7d742075120deb831c7b94c268ca20d409e91d60 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 28 Jun 2014 08:10:13 -0400 Subject: percpu-refcount: use unsigned long for pcpu_count pointer percpu_ref->pcpu_count is a percpu pointer with a status flag in its lowest bit. As such, it always goes through arithmetic operations which is very cumbersome to do on a pointer. It has to be first casted to unsigned long and then back. Let's just make the field unsigned long so that we can skip the first casts. While at it, rename it to pcpu_counter_ptr to clarify that it's a pointer value. Signed-off-by: Tejun Heo Cc: Kent Overstreet Cc: Christoph Lameter --- include/linux/percpu-refcount.h | 4 ++-- lib/percpu-refcount.c | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/percpu-refcount.h b/include/linux/percpu-refcount.h index b62a4ee6d6ad..6f8cd4c0546c 100644 --- a/include/linux/percpu-refcount.h +++ b/include/linux/percpu-refcount.h @@ -61,7 +61,7 @@ struct percpu_ref { * hack because we need to keep the pointer around for * percpu_ref_kill_rcu()) */ - unsigned __percpu *pcpu_count; + unsigned long pcpu_count_ptr; percpu_ref_func_t *release; percpu_ref_func_t *confirm_kill; struct rcu_head rcu; @@ -99,7 +99,7 @@ static inline void percpu_ref_kill(struct percpu_ref *ref) static inline bool __pcpu_ref_alive(struct percpu_ref *ref, unsigned __percpu **pcpu_countp) { - unsigned long pcpu_ptr = (unsigned long)ACCESS_ONCE(ref->pcpu_count); + unsigned long pcpu_ptr = ACCESS_ONCE(ref->pcpu_count_ptr); if (unlikely(pcpu_ptr & PCPU_REF_DEAD)) return false; diff --git a/lib/percpu-refcount.c b/lib/percpu-refcount.c index 087f1a04f9bc..94e5b624de64 100644 --- a/lib/percpu-refcount.c +++ b/lib/percpu-refcount.c @@ -33,7 +33,7 @@ static unsigned __percpu *pcpu_count_ptr(struct percpu_ref *ref) { - return (unsigned __percpu *)((unsigned long)ref->pcpu_count & ~PCPU_REF_DEAD); + return (unsigned __percpu *)(ref->pcpu_count_ptr & ~PCPU_REF_DEAD); } /** @@ -51,8 +51,8 @@ int percpu_ref_init(struct percpu_ref *ref, percpu_ref_func_t *release) { atomic_set(&ref->count, 1 + PCPU_COUNT_BIAS); - ref->pcpu_count = alloc_percpu(unsigned); - if (!ref->pcpu_count) + ref->pcpu_count_ptr = (unsigned long)alloc_percpu(unsigned); + if (!ref->pcpu_count_ptr) return -ENOMEM; ref->release = release; @@ -153,11 +153,10 @@ static void percpu_ref_kill_rcu(struct rcu_head *rcu) void percpu_ref_kill_and_confirm(struct percpu_ref *ref, percpu_ref_func_t *confirm_kill) { - WARN_ONCE((unsigned long)ref->pcpu_count & PCPU_REF_DEAD, + WARN_ONCE(ref->pcpu_count_ptr & PCPU_REF_DEAD, "percpu_ref_kill() called more than once!\n"); - ref->pcpu_count = (unsigned __percpu *) - (((unsigned long) ref->pcpu_count)|PCPU_REF_DEAD); + ref->pcpu_count_ptr |= PCPU_REF_DEAD; ref->confirm_kill = confirm_kill; call_rcu_sched(&ref->rcu, percpu_ref_kill_rcu); -- cgit v1.2.3 From 9a1049da9bd2cd83fe11d46433e603c193aa9c71 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 28 Jun 2014 08:10:14 -0400 Subject: percpu-refcount: require percpu_ref to be exited explicitly Currently, a percpu_ref undoes percpu_ref_init() automatically by freeing the allocated percpu area when the percpu_ref is killed. While seemingly convenient, this has the following niggles. * It's impossible to re-init a released reference counter without going through re-allocation. * In the similar vein, it's impossible to initialize a percpu_ref count with static percpu variables. * We need and have an explicit destructor anyway for failure paths - percpu_ref_cancel_init(). This patch removes the automatic percpu counter freeing in percpu_ref_kill_rcu() and repurposes percpu_ref_cancel_init() into a generic destructor now named percpu_ref_exit(). percpu_ref_destroy() is considered but it gets confusing with percpu_ref_kill() while "exit" clearly indicates that it's the counterpart of percpu_ref_init(). All percpu_ref_cancel_init() users are updated to invoke percpu_ref_exit() instead and explicit percpu_ref_exit() calls are added to the destruction path of all percpu_ref users. Signed-off-by: Tejun Heo Acked-by: Benjamin LaHaise Cc: Kent Overstreet Cc: Christoph Lameter Cc: Benjamin LaHaise Cc: Nicholas A. Bellinger Cc: Li Zefan --- drivers/target/target_core_tpg.c | 4 +++- fs/aio.c | 6 ++++-- include/linux/percpu-refcount.h | 6 ++---- kernel/cgroup.c | 8 +++++--- lib/percpu-refcount.c | 33 ++++++++++----------------------- 5 files changed, 24 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c index c036595b17cf..fddfae61222f 100644 --- a/drivers/target/target_core_tpg.c +++ b/drivers/target/target_core_tpg.c @@ -825,7 +825,7 @@ int core_tpg_add_lun( ret = core_dev_export(dev, tpg, lun); if (ret < 0) { - percpu_ref_cancel_init(&lun->lun_ref); + percpu_ref_exit(&lun->lun_ref); return ret; } @@ -880,5 +880,7 @@ int core_tpg_post_dellun( lun->lun_status = TRANSPORT_LUN_STATUS_FREE; spin_unlock(&tpg->tpg_lun_lock); + percpu_ref_exit(&lun->lun_ref); + return 0; } diff --git a/fs/aio.c b/fs/aio.c index 5e0d7f9cb693..ea1bc2e8f4f3 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -506,6 +506,8 @@ static void free_ioctx(struct work_struct *work) aio_free_ring(ctx); free_percpu(ctx->cpu); + percpu_ref_exit(&ctx->reqs); + percpu_ref_exit(&ctx->users); kmem_cache_free(kioctx_cachep, ctx); } @@ -715,8 +717,8 @@ err_ctx: err: mutex_unlock(&ctx->ring_lock); free_percpu(ctx->cpu); - percpu_ref_cancel_init(&ctx->reqs); - percpu_ref_cancel_init(&ctx->users); + percpu_ref_exit(&ctx->reqs); + percpu_ref_exit(&ctx->users); kmem_cache_free(kioctx_cachep, ctx); pr_debug("error allocating ioctx %d\n", err); return ERR_PTR(err); diff --git a/include/linux/percpu-refcount.h b/include/linux/percpu-refcount.h index 6f8cd4c0546c..0ddd2839ca84 100644 --- a/include/linux/percpu-refcount.h +++ b/include/linux/percpu-refcount.h @@ -57,9 +57,7 @@ struct percpu_ref { atomic_t count; /* * The low bit of the pointer indicates whether the ref is in percpu - * mode; if set, then get/put will manipulate the atomic_t (this is a - * hack because we need to keep the pointer around for - * percpu_ref_kill_rcu()) + * mode; if set, then get/put will manipulate the atomic_t. */ unsigned long pcpu_count_ptr; percpu_ref_func_t *release; @@ -69,7 +67,7 @@ struct percpu_ref { int __must_check percpu_ref_init(struct percpu_ref *ref, percpu_ref_func_t *release); -void percpu_ref_cancel_init(struct percpu_ref *ref); +void percpu_ref_exit(struct percpu_ref *ref); void percpu_ref_kill_and_confirm(struct percpu_ref *ref, percpu_ref_func_t *confirm_kill); diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 7868fc3c0bc5..c06aa5e257a8 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -1638,7 +1638,7 @@ destroy_root: exit_root_id: cgroup_exit_root_id(root); cancel_ref: - percpu_ref_cancel_init(&root_cgrp->self.refcnt); + percpu_ref_exit(&root_cgrp->self.refcnt); out: free_cgrp_cset_links(&tmp_links); return ret; @@ -4133,6 +4133,8 @@ static void css_free_work_fn(struct work_struct *work) container_of(work, struct cgroup_subsys_state, destroy_work); struct cgroup *cgrp = css->cgroup; + percpu_ref_exit(&css->refcnt); + if (css->ss) { /* css free path */ if (css->parent) @@ -4330,7 +4332,7 @@ err_list_del: err_free_id: cgroup_idr_remove(&ss->css_idr, css->id); err_free_percpu_ref: - percpu_ref_cancel_init(&css->refcnt); + percpu_ref_exit(&css->refcnt); err_free_css: call_rcu(&css->rcu_head, css_free_rcu_fn); return err; @@ -4441,7 +4443,7 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, out_free_id: cgroup_idr_remove(&root->cgroup_idr, cgrp->id); out_cancel_ref: - percpu_ref_cancel_init(&cgrp->self.refcnt); + percpu_ref_exit(&cgrp->self.refcnt); out_free_cgrp: kfree(cgrp); out_unlock: diff --git a/lib/percpu-refcount.c b/lib/percpu-refcount.c index 94e5b624de64..ac4299120087 100644 --- a/lib/percpu-refcount.c +++ b/lib/percpu-refcount.c @@ -61,36 +61,25 @@ int percpu_ref_init(struct percpu_ref *ref, percpu_ref_func_t *release) EXPORT_SYMBOL_GPL(percpu_ref_init); /** - * percpu_ref_cancel_init - cancel percpu_ref_init() - * @ref: percpu_ref to cancel init for + * percpu_ref_exit - undo percpu_ref_init() + * @ref: percpu_ref to exit * - * Once a percpu_ref is initialized, its destruction is initiated by - * percpu_ref_kill() and completes asynchronously, which can be painful to - * do when destroying a half-constructed object in init failure path. - * - * This function destroys @ref without invoking @ref->release and the - * memory area containing it can be freed immediately on return. To - * prevent accidental misuse, it's required that @ref has finished - * percpu_ref_init(), whether successful or not, but never used. - * - * The weird name and usage restriction are to prevent people from using - * this function by mistake for normal shutdown instead of - * percpu_ref_kill(). + * This function exits @ref. The caller is responsible for ensuring that + * @ref is no longer in active use. The usual places to invoke this + * function from are the @ref->release() callback or in init failure path + * where percpu_ref_init() succeeded but other parts of the initialization + * of the embedding object failed. */ -void percpu_ref_cancel_init(struct percpu_ref *ref) +void percpu_ref_exit(struct percpu_ref *ref) { unsigned __percpu *pcpu_count = pcpu_count_ptr(ref); - int cpu; - - WARN_ON_ONCE(atomic_read(&ref->count) != 1 + PCPU_COUNT_BIAS); if (pcpu_count) { - for_each_possible_cpu(cpu) - WARN_ON_ONCE(*per_cpu_ptr(pcpu_count, cpu)); free_percpu(pcpu_count); + ref->pcpu_count_ptr = PCPU_REF_DEAD; } } -EXPORT_SYMBOL_GPL(percpu_ref_cancel_init); +EXPORT_SYMBOL_GPL(percpu_ref_exit); static void percpu_ref_kill_rcu(struct rcu_head *rcu) { @@ -102,8 +91,6 @@ static void percpu_ref_kill_rcu(struct rcu_head *rcu) for_each_possible_cpu(cpu) count += *per_cpu_ptr(pcpu_count, cpu); - free_percpu(pcpu_count); - pr_debug("global %i pcpu %i", atomic_read(&ref->count), (int) count); /* -- cgit v1.2.3 From 2d7227828e1475c7b272e55bd70c4cec8eea219a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 28 Jun 2014 08:10:14 -0400 Subject: percpu-refcount: implement percpu_ref_reinit() and percpu_ref_is_zero() Now that explicit invocation of percpu_ref_exit() is necessary to free the percpu counter, we can implement percpu_ref_reinit() which reinitializes a released percpu_ref. This can be used implement scalable gating switch which can be drained and then re-opened without worrying about memory allocation failures. percpu_ref_is_zero() is added to be used in a sanity check in percpu_ref_exit(). As this function will be useful for other purposes too, make it a public interface. v2: Use smp_read_barrier_depends() instead of smp_load_acquire(). We only need data dep barrier and smp_load_acquire() is stronger and heavier on some archs. Spotted by Lai Jiangshan. Signed-off-by: Tejun Heo Cc: Kent Overstreet Cc: Christoph Lameter Cc: Lai Jiangshan --- include/linux/percpu-refcount.h | 19 +++++++++++++++++++ lib/percpu-refcount.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) (limited to 'include') diff --git a/include/linux/percpu-refcount.h b/include/linux/percpu-refcount.h index 0ddd2839ca84..3dfbf237cd8f 100644 --- a/include/linux/percpu-refcount.h +++ b/include/linux/percpu-refcount.h @@ -67,6 +67,7 @@ struct percpu_ref { int __must_check percpu_ref_init(struct percpu_ref *ref, percpu_ref_func_t *release); +void percpu_ref_reinit(struct percpu_ref *ref); void percpu_ref_exit(struct percpu_ref *ref); void percpu_ref_kill_and_confirm(struct percpu_ref *ref, percpu_ref_func_t *confirm_kill); @@ -99,6 +100,9 @@ static inline bool __pcpu_ref_alive(struct percpu_ref *ref, { unsigned long pcpu_ptr = ACCESS_ONCE(ref->pcpu_count_ptr); + /* paired with smp_store_release() in percpu_ref_reinit() */ + smp_read_barrier_depends(); + if (unlikely(pcpu_ptr & PCPU_REF_DEAD)) return false; @@ -206,4 +210,19 @@ static inline void percpu_ref_put(struct percpu_ref *ref) rcu_read_unlock_sched(); } +/** + * percpu_ref_is_zero - test whether a percpu refcount reached zero + * @ref: percpu_ref to test + * + * Returns %true if @ref reached zero. + */ +static inline bool percpu_ref_is_zero(struct percpu_ref *ref) +{ + unsigned __percpu *pcpu_count; + + if (__pcpu_ref_alive(ref, &pcpu_count)) + return false; + return !atomic_read(&ref->count); +} + #endif diff --git a/lib/percpu-refcount.c b/lib/percpu-refcount.c index ac4299120087..fe5a3342e960 100644 --- a/lib/percpu-refcount.c +++ b/lib/percpu-refcount.c @@ -60,6 +60,41 @@ int percpu_ref_init(struct percpu_ref *ref, percpu_ref_func_t *release) } EXPORT_SYMBOL_GPL(percpu_ref_init); +/** + * percpu_ref_reinit - re-initialize a percpu refcount + * @ref: perpcu_ref to re-initialize + * + * Re-initialize @ref so that it's in the same state as when it finished + * percpu_ref_init(). @ref must have been initialized successfully, killed + * and reached 0 but not exited. + * + * Note that percpu_ref_tryget[_live]() are safe to perform on @ref while + * this function is in progress. + */ +void percpu_ref_reinit(struct percpu_ref *ref) +{ + unsigned __percpu *pcpu_count = pcpu_count_ptr(ref); + int cpu; + + BUG_ON(!pcpu_count); + WARN_ON(!percpu_ref_is_zero(ref)); + + atomic_set(&ref->count, 1 + PCPU_COUNT_BIAS); + + /* + * Restore per-cpu operation. smp_store_release() is paired with + * smp_read_barrier_depends() in __pcpu_ref_alive() and guarantees + * that the zeroing is visible to all percpu accesses which can see + * the following PCPU_REF_DEAD clearing. + */ + for_each_possible_cpu(cpu) + *per_cpu_ptr(pcpu_count, cpu) = 0; + + smp_store_release(&ref->pcpu_count_ptr, + ref->pcpu_count_ptr & ~PCPU_REF_DEAD); +} +EXPORT_SYMBOL_GPL(percpu_ref_reinit); + /** * percpu_ref_exit - undo percpu_ref_init() * @ref: percpu_ref to exit -- cgit v1.2.3 From d9288d0ba12de1b5efb830b9128e4cc6877318fc Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 22 Jun 2014 17:56:23 -0700 Subject: ASoC: rsnd: SSI + DMA can select BUSIF Sound data needs to be sent to R-Car sound SSI when playback. But, there are 2 interfaces for it. 1st is SSITDR/SSIRDR which are mapped on SSI. 2nd is SSIn_BUSIF which are mapped on SSIU. 2nd SSIn_BUSIF is used when DMA transfer, and it is always used if sound data came from via SRC. But, we can use it when SSI+DMA case too. (Current driver is assuming 1st SSITDR/SSIRDR for it) 2nd SSIn_BUSIF can be used as FIFO. This is very helpful/useful for SSI+DMA. But DMA address / DMA ID are not same between 1st/2nd cases. This patch care about these settings. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/renesas,rsnd.txt | 1 + include/sound/rcar_snd.h | 1 + sound/soc/sh/rcar/core.c | 22 +++++--- sound/soc/sh/rcar/gen.c | 64 ++++++++++++++-------- sound/soc/sh/rcar/rsnd.h | 10 +++- sound/soc/sh/rcar/src.c | 37 +++++++++---- sound/soc/sh/rcar/ssi.c | 33 ++++++++++- 7 files changed, 123 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt index 8346cab046cd..41a120c2389d 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt @@ -21,6 +21,7 @@ SSI subnode properties: - interrupts : Should contain SSI interrupt for PIO transfer - shared-pin : if shared clock pin - pio-transfer : use PIO transfer mode +- no-busif : BUSIF is not ussed when [mem -> SSI] via DMA case SRC subnode properties: no properties at this point diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index f4a706f82cb7..d76412b84b48 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -34,6 +34,7 @@ * B : SSI direction */ #define RSND_SSI_CLK_PIN_SHARE (1 << 31) +#define RSND_SSI_NO_BUSIF (1 << 30) /* SSI+DMA without BUSIF */ #define RSND_SSI(_dma_id, _pio_irq, _flags) \ { .dma_id = _dma_id, .pio_irq = _pio_irq, .flags = _flags } diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 7f68b33dcbbb..8c3707a68603 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -138,6 +138,17 @@ char *rsnd_mod_name(struct rsnd_mod *mod) return mod->ops->name; } +char *rsnd_mod_dma_name(struct rsnd_mod *mod) +{ + if (!mod || !mod->ops) + return "unknown"; + + if (!mod->ops->dma_name) + return mod->ops->name; + + return mod->ops->dma_name(mod); +} + void rsnd_mod_init(struct rsnd_priv *priv, struct rsnd_mod *mod, struct rsnd_mod_ops *ops, @@ -261,7 +272,7 @@ static int _rsnd_dma_of_name(char *dma_name, struct rsnd_mod *mod) { if (mod) return snprintf(dma_name, DMA_NAME_SIZE / 2, "%s%d", - rsnd_mod_name(mod), rsnd_mod_id(mod)); + rsnd_mod_dma_name(mod), rsnd_mod_id(mod)); else return snprintf(dma_name, DMA_NAME_SIZE / 2, "mem"); @@ -343,11 +354,8 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - if (dev->of_node) - rsnd_dma_of_name(dma, is_play, dma_name); - else - snprintf(dma_name, DMA_NAME_SIZE, - is_play ? "tx" : "rx"); + rsnd_dma_of_name(dma, is_play, dma_name); + rsnd_gen_dma_addr(priv, dma, &cfg, is_play, id); dev_dbg(dev, "dma name : %s\n", dma_name); @@ -359,8 +367,6 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, return -EIO; } - rsnd_gen_dma_addr(priv, dma, &cfg, is_play, id); - ret = dmaengine_slave_config(dma->chan, &cfg); if (ret < 0) goto rsnd_dma_init_err; diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 0280a11c0899..46677af6c748 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -165,15 +165,19 @@ static int rsnd_gen_regmap_init(struct rsnd_priv *priv, * * ex) R-Car H2 case * mod / DMAC in / DMAC out / DMAC PP in / DMAC pp out - * SSI : 0xec541000 / 0xec241008 / 0xec24100c / 0xec400000 / 0xec400000 + * SSI : 0xec541000 / 0xec241008 / 0xec24100c + * SSIU: 0xec541000 / 0xec100000 / 0xec100000 / 0xec400000 / 0xec400000 * SCU : 0xec500000 / 0xec000000 / 0xec004000 / 0xec300000 / 0xec304000 * CMD : 0xec500000 / 0xec008000 0xec308000 */ #define RDMA_SSI_I_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0x8) #define RDMA_SSI_O_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0xc) -#define RDMA_SSI_I_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i)) -#define RDMA_SSI_O_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i)) +#define RDMA_SSIU_I_N(addr, i) (addr ##_reg - 0x00441000 + (0x1000 * i)) +#define RDMA_SSIU_O_N(addr, i) (addr ##_reg - 0x00441000 + (0x1000 * i)) + +#define RDMA_SSIU_I_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i)) +#define RDMA_SSIU_O_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i)) #define RDMA_SRC_I_N(addr, i) (addr ##_reg - 0x00500000 + (0x400 * i)) #define RDMA_SRC_O_N(addr, i) (addr ##_reg - 0x004fc000 + (0x400 * i)) @@ -204,26 +208,36 @@ static void rsnd_gen2_dma_addr(struct rsnd_priv *priv, struct dma_addr { dma_addr_t src_addr; dma_addr_t dst_addr; - } dma_addrs[2][2][3] = { - { /* SRC */ - /* Capture */ - {{ 0, 0 }, - { RDMA_SRC_O_N(src, id), 0 }, - { RDMA_CMD_O_N(src, id), 0 }}, - /* Playback */ - {{ 0, 0, }, - { 0, RDMA_SRC_I_N(src, id) }, - { 0, RDMA_SRC_I_N(src, id) }} - }, { /* SSI */ - /* Capture */ - {{ RDMA_SSI_O_N(ssi, id), 0 }, - { RDMA_SSI_O_P(ssi, id), RDMA_SRC_I_P(src, id) }, - { RDMA_SSI_O_P(ssi, id), RDMA_SRC_I_P(src, id) }}, - /* Playback */ - {{ 0, RDMA_SSI_I_N(ssi, id) }, - { RDMA_SRC_O_P(src, id), RDMA_SSI_I_P(ssi, id) }, - { RDMA_CMD_O_P(src, id), RDMA_SSI_I_P(ssi, id) }} - } + } dma_addrs[3][2][3] = { + /* SRC */ + {{{ 0, 0 }, + /* Capture */ + { RDMA_SRC_O_N(src, id), 0 }, + { RDMA_CMD_O_N(src, id), 0 } }, + /* Playback */ + {{ 0, 0, }, + { 0, RDMA_SRC_I_N(src, id) }, + { 0, RDMA_SRC_I_N(src, id) } } + }, + /* SSI */ + /* Capture */ + {{{ RDMA_SSI_O_N(ssi, id), 0 }, + { 0, 0 }, + { 0, 0 } }, + /* Playback */ + {{ 0, RDMA_SSI_I_N(ssi, id) }, + { 0, 0 }, + { 0, 0 } } + }, + /* SSIU */ + /* Capture */ + {{{ RDMA_SSIU_O_N(ssi, id), 0 }, + { RDMA_SSIU_O_P(ssi, id), RDMA_SRC_I_P(src, id) }, + { RDMA_SSIU_O_P(ssi, id), RDMA_SRC_I_P(src, id) } }, + /* Playback */ + {{ 0, RDMA_SSIU_I_N(ssi, id) }, + { RDMA_SRC_O_P(src, id), RDMA_SSIU_I_P(ssi, id) }, + { RDMA_CMD_O_P(src, id), RDMA_SSIU_I_P(ssi, id) } } }, }; /* it shouldn't happen */ @@ -232,6 +246,10 @@ static void rsnd_gen2_dma_addr(struct rsnd_priv *priv, return; } + /* use SSIU or SSI ? */ + if (is_ssi && (0 == strcmp(rsnd_mod_dma_name(mod), "ssiu"))) + is_ssi++; + cfg->src_addr = dma_addrs[is_ssi][is_play][use_src + use_dvc].src_addr; cfg->dst_addr = dma_addrs[is_ssi][is_play][use_src + use_dvc].dst_addr; diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 067a89e9f25c..a1466c1570bc 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -185,6 +185,7 @@ enum rsnd_mod_type { struct rsnd_mod_ops { char *name; + char* (*dma_name)(struct rsnd_mod *mod); int (*probe)(struct rsnd_mod *mod, struct rsnd_dai *rdai); int (*remove)(struct rsnd_mod *mod, @@ -224,6 +225,7 @@ void rsnd_mod_init(struct rsnd_priv *priv, enum rsnd_mod_type type, int id); char *rsnd_mod_name(struct rsnd_mod *mod); +char *rsnd_mod_dma_name(struct rsnd_mod *mod); /* * R-Car sound DAI @@ -391,8 +393,12 @@ struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id); unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, struct rsnd_dai_stream *io, struct snd_pcm_runtime *runtime); -int rsnd_src_ssi_mode_init(struct rsnd_mod *ssi_mod, - struct rsnd_dai *rdai); +int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, + struct rsnd_dai *rdai, + int use_busif); +int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod, + struct rsnd_dai *rdai, + int use_busif); int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod, struct rsnd_dai *rdai); diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 200eda019bc7..4d39505c21cf 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -106,18 +106,17 @@ struct rsnd_src { /* * Gen1/Gen2 common functions */ -int rsnd_src_ssi_mode_init(struct rsnd_mod *ssi_mod, - struct rsnd_dai *rdai) +int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, + struct rsnd_dai *rdai, + int use_busif) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(ssi_mod); - struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); int ssi_id = rsnd_mod_id(ssi_mod); /* * SSI_MODE0 */ rsnd_mod_bset(ssi_mod, SSI_MODE0, (1 << ssi_id), - src_mod ? 0 : (1 << ssi_id)); + !use_busif << ssi_id); /* * SSI_MODE1 @@ -143,6 +142,29 @@ int rsnd_src_ssi_mode_init(struct rsnd_mod *ssi_mod, 0x2 << shift : 0x1 << shift); } + /* + * DMA settings for SSIU + */ + if (use_busif) { + rsnd_mod_write(ssi_mod, SSI_BUSIF_ADINR, + rsnd_get_adinr(ssi_mod)); + rsnd_mod_write(ssi_mod, SSI_BUSIF_MODE, 1); + rsnd_mod_write(ssi_mod, SSI_CTRL, 0x1); + } + + return 0; +} + +int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod, + struct rsnd_dai *rdai, + int use_busif) +{ + /* + * DMA settings for SSIU + */ + if (use_busif) + rsnd_mod_write(ssi_mod, SSI_CTRL, 0); + return 0; } @@ -467,9 +489,6 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod, if (ret < 0) return ret; - rsnd_mod_write(mod, SSI_BUSIF_ADINR, rsnd_get_adinr(mod)); - rsnd_mod_write(mod, SSI_BUSIF_MODE, 1); - rsnd_mod_write(mod, SRC_SRCCR, 0x00011110); rsnd_mod_write(mod, SRC_BSDSR, 0x01800000); @@ -554,7 +573,6 @@ static int rsnd_src_start_gen2(struct rsnd_mod *mod, rsnd_dma_start(rsnd_mod_to_dma(&src->mod)); - rsnd_mod_write(mod, SSI_CTRL, 0x1); rsnd_mod_write(mod, SRC_CTRL, val); return rsnd_src_start(mod, rdai); @@ -565,7 +583,6 @@ static int rsnd_src_stop_gen2(struct rsnd_mod *mod, { struct rsnd_src *src = rsnd_mod_to_src(mod); - rsnd_mod_write(mod, SSI_CTRL, 0); rsnd_mod_write(mod, SRC_CTRL, 0); rsnd_dma_stop(rsnd_mod_to_dma(&src->mod)); diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 2df723df5d19..34e84009162b 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -90,6 +90,20 @@ struct rsnd_ssi { #define rsnd_ssi_mode_flags(p) ((p)->info->flags) #define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id) +static int rsnd_ssi_use_busif(struct rsnd_mod *mod) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + int use_busif = 0; + + if (!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_NO_BUSIF)) + use_busif = 1; + if (rsnd_io_to_mod_src(io)) + use_busif = 1; + + return use_busif; +} + static void rsnd_ssi_status_check(struct rsnd_mod *mod, u32 bit) { @@ -289,8 +303,6 @@ static int rsnd_ssi_init(struct rsnd_mod *mod, ssi->cr_own = cr; ssi->err = -1; /* ignore 1st error */ - rsnd_src_ssi_mode_init(mod, rdai); - return 0; } @@ -389,6 +401,8 @@ static int rsnd_ssi_pio_start(struct rsnd_mod *mod, /* enable PIO IRQ */ ssi->cr_etc = UIEN | OIEN | DIEN; + rsnd_src_ssiu_start(mod, rdai, 0); + rsnd_src_enable_ssi_irq(mod, rdai); rsnd_ssi_hw_start(ssi, rdai, io); @@ -405,6 +419,8 @@ static int rsnd_ssi_pio_stop(struct rsnd_mod *mod, rsnd_ssi_hw_stop(ssi, rdai); + rsnd_src_ssiu_stop(mod, rdai, 0); + return 0; } @@ -457,6 +473,8 @@ static int rsnd_ssi_dma_start(struct rsnd_mod *mod, /* enable DMA transfer */ ssi->cr_etc = DMEN; + rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod)); + rsnd_dma_start(dma); rsnd_ssi_hw_start(ssi, ssi->rdai, io); @@ -482,11 +500,19 @@ static int rsnd_ssi_dma_stop(struct rsnd_mod *mod, rsnd_dma_stop(dma); + rsnd_src_ssiu_stop(mod, rdai, 1); + return 0; } +static char *rsnd_ssi_dma_name(struct rsnd_mod *mod) +{ + return rsnd_ssi_use_busif(mod) ? "ssiu" : SSI_NAME; +} + static struct rsnd_mod_ops rsnd_ssi_dma_ops = { .name = SSI_NAME, + .dma_name = rsnd_ssi_dma_name, .probe = rsnd_ssi_dma_probe, .remove = rsnd_ssi_dma_remove, .init = rsnd_ssi_init, @@ -595,6 +621,9 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev, */ ssi_info->dma_id = of_get_property(np, "pio-transfer", NULL) ? 0 : 1; + + if (of_get_property(np, "no-busif", NULL)) + ssi_info->flags |= RSND_SSI_NO_BUSIF; } rsnd_of_parse_ssi_end: -- cgit v1.2.3 From b2373f255cacdc1ea4da25e75a5a78949ffd9d66 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Tue, 3 Jun 2014 11:36:03 +0800 Subject: btrfs: create sprout should rename fsid on the sysfs as well Creating sprout will change the fsid of the mounted root. do the same on the sysfs as well. reproducer: mount /dev/sdb /btrfs (seed disk) btrfs dev add /dev/sdc /btrfs mount -o rw,remount /btrfs btrfs dev del /dev/sdb /btrfs mount /dev/sdb /btrfs Error: kobject_add_internal failed for fe350492-dc28-4051-a601-e017b17e6145 with -EEXIST, don't try to register things with the same name in the same directory. Signed-off-by: Anand Jain Reviewed-by: David Sterba Signed-off-by: Chris Mason --- fs/btrfs/volumes.c | 9 +++++++++ include/uapi/linux/btrfs.h | 1 + 2 files changed, 10 insertions(+) (limited to 'include') diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 19b67e969b52..6ca0d9cc46f9 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2154,6 +2154,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) mutex_unlock(&root->fs_info->fs_devices->device_list_mutex); if (seeding_dev) { + char fsid_buf[BTRFS_UUID_UNPARSED_SIZE]; ret = init_first_rw_device(trans, root, device); if (ret) { btrfs_abort_transaction(trans, root, ret); @@ -2164,6 +2165,14 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) btrfs_abort_transaction(trans, root, ret); goto error_trans; } + + /* Sprouting would change fsid of the mounted root, + * so rename the fsid on the sysfs + */ + snprintf(fsid_buf, BTRFS_UUID_UNPARSED_SIZE, "%pU", + root->fs_info->fsid); + if (kobject_rename(&root->fs_info->super_kobj, fsid_buf)) + goto error_trans; } else { ret = btrfs_add_device(trans, root, device); if (ret) { diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h index 6f9c38ce45c7..2f47824e7a36 100644 --- a/include/uapi/linux/btrfs.h +++ b/include/uapi/linux/btrfs.h @@ -38,6 +38,7 @@ struct btrfs_ioctl_vol_args { #define BTRFS_SUBVOL_QGROUP_INHERIT (1ULL << 2) #define BTRFS_FSID_SIZE 16 #define BTRFS_UUID_SIZE 16 +#define BTRFS_UUID_UNPARSED_SIZE 37 #define BTRFS_QGROUP_INHERIT_SET_LIMITS (1ULL << 0) -- cgit v1.2.3 From 44ff0254b89079a8a95e652635e760d93196ac1f Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Thu, 5 Jun 2014 13:35:14 -0700 Subject: clk: exynos5420: Remove aclk66_peric from the clock tree description The "aclk66_peric" clock is a gate clock with a whole bunch of gates underneath it. This big gate isn't very useful to include in our clock tree. If any of the children need to be turned on then the big gate will need to be on anyway. ...and there are plenty of other "big gates" that aren't described in our clock tree, some of which shut off collections of clocks that have no relationship in the hierarchy so are hard to model. "aclk66_peric" is causing earlyprintk problems since it gets disabled as part of the boot process, so let's just remove it. Strangely (and for no good reason) this clock is exported as part of the common clock bindings. Remove it since there are no in-kernel device trees using it and no reason anyone out of tree should refer to it either. Signed-off-by: Doug Anderson Signed-off-by: Tomasz Figa --- drivers/clk/samsung/clk-exynos5420.c | 85 ++++++++++++++++++++++------------ include/dt-bindings/clock/exynos5420.h | 1 - 2 files changed, 55 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index 9d7d7eed03fd..61eccf0dd72f 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -890,8 +890,6 @@ static struct samsung_gate_clock exynos5x_gate_clks[] __initdata = { GATE_BUS_TOP, 9, CLK_IGNORE_UNUSED, 0), GATE(0, "aclk66_psgen", "mout_user_aclk66_psgen", GATE_BUS_TOP, 10, CLK_IGNORE_UNUSED, 0), - GATE(CLK_ACLK66_PERIC, "aclk66_peric", "mout_user_aclk66_peric", - GATE_BUS_TOP, 11, CLK_IGNORE_UNUSED, 0), GATE(0, "aclk266_isp", "mout_user_aclk266_isp", GATE_BUS_TOP, 13, 0, 0), GATE(0, "aclk166", "mout_user_aclk166", @@ -994,34 +992,61 @@ static struct samsung_gate_clock exynos5x_gate_clks[] __initdata = { SRC_MASK_FSYS, 24, CLK_SET_RATE_PARENT, 0), /* PERIC Block */ - GATE(CLK_UART0, "uart0", "aclk66_peric", GATE_IP_PERIC, 0, 0, 0), - GATE(CLK_UART1, "uart1", "aclk66_peric", GATE_IP_PERIC, 1, 0, 0), - GATE(CLK_UART2, "uart2", "aclk66_peric", GATE_IP_PERIC, 2, 0, 0), - GATE(CLK_UART3, "uart3", "aclk66_peric", GATE_IP_PERIC, 3, 0, 0), - GATE(CLK_I2C0, "i2c0", "aclk66_peric", GATE_IP_PERIC, 6, 0, 0), - GATE(CLK_I2C1, "i2c1", "aclk66_peric", GATE_IP_PERIC, 7, 0, 0), - GATE(CLK_I2C2, "i2c2", "aclk66_peric", GATE_IP_PERIC, 8, 0, 0), - GATE(CLK_I2C3, "i2c3", "aclk66_peric", GATE_IP_PERIC, 9, 0, 0), - GATE(CLK_USI0, "usi0", "aclk66_peric", GATE_IP_PERIC, 10, 0, 0), - GATE(CLK_USI1, "usi1", "aclk66_peric", GATE_IP_PERIC, 11, 0, 0), - GATE(CLK_USI2, "usi2", "aclk66_peric", GATE_IP_PERIC, 12, 0, 0), - GATE(CLK_USI3, "usi3", "aclk66_peric", GATE_IP_PERIC, 13, 0, 0), - GATE(CLK_I2C_HDMI, "i2c_hdmi", "aclk66_peric", GATE_IP_PERIC, 14, 0, 0), - GATE(CLK_TSADC, "tsadc", "aclk66_peric", GATE_IP_PERIC, 15, 0, 0), - GATE(CLK_SPI0, "spi0", "aclk66_peric", GATE_IP_PERIC, 16, 0, 0), - GATE(CLK_SPI1, "spi1", "aclk66_peric", GATE_IP_PERIC, 17, 0, 0), - GATE(CLK_SPI2, "spi2", "aclk66_peric", GATE_IP_PERIC, 18, 0, 0), - GATE(CLK_I2S1, "i2s1", "aclk66_peric", GATE_IP_PERIC, 20, 0, 0), - GATE(CLK_I2S2, "i2s2", "aclk66_peric", GATE_IP_PERIC, 21, 0, 0), - GATE(CLK_PCM1, "pcm1", "aclk66_peric", GATE_IP_PERIC, 22, 0, 0), - GATE(CLK_PCM2, "pcm2", "aclk66_peric", GATE_IP_PERIC, 23, 0, 0), - GATE(CLK_PWM, "pwm", "aclk66_peric", GATE_IP_PERIC, 24, 0, 0), - GATE(CLK_SPDIF, "spdif", "aclk66_peric", GATE_IP_PERIC, 26, 0, 0), - GATE(CLK_USI4, "usi4", "aclk66_peric", GATE_IP_PERIC, 28, 0, 0), - GATE(CLK_USI5, "usi5", "aclk66_peric", GATE_IP_PERIC, 30, 0, 0), - GATE(CLK_USI6, "usi6", "aclk66_peric", GATE_IP_PERIC, 31, 0, 0), - - GATE(CLK_KEYIF, "keyif", "aclk66_peric", GATE_BUS_PERIC, 22, 0, 0), + GATE(CLK_UART0, "uart0", "mout_user_aclk66_peric", + GATE_IP_PERIC, 0, 0, 0), + GATE(CLK_UART1, "uart1", "mout_user_aclk66_peric", + GATE_IP_PERIC, 1, 0, 0), + GATE(CLK_UART2, "uart2", "mout_user_aclk66_peric", + GATE_IP_PERIC, 2, 0, 0), + GATE(CLK_UART3, "uart3", "mout_user_aclk66_peric", + GATE_IP_PERIC, 3, 0, 0), + GATE(CLK_I2C0, "i2c0", "mout_user_aclk66_peric", + GATE_IP_PERIC, 6, 0, 0), + GATE(CLK_I2C1, "i2c1", "mout_user_aclk66_peric", + GATE_IP_PERIC, 7, 0, 0), + GATE(CLK_I2C2, "i2c2", "mout_user_aclk66_peric", + GATE_IP_PERIC, 8, 0, 0), + GATE(CLK_I2C3, "i2c3", "mout_user_aclk66_peric", + GATE_IP_PERIC, 9, 0, 0), + GATE(CLK_USI0, "usi0", "mout_user_aclk66_peric", + GATE_IP_PERIC, 10, 0, 0), + GATE(CLK_USI1, "usi1", "mout_user_aclk66_peric", + GATE_IP_PERIC, 11, 0, 0), + GATE(CLK_USI2, "usi2", "mout_user_aclk66_peric", + GATE_IP_PERIC, 12, 0, 0), + GATE(CLK_USI3, "usi3", "mout_user_aclk66_peric", + GATE_IP_PERIC, 13, 0, 0), + GATE(CLK_I2C_HDMI, "i2c_hdmi", "mout_user_aclk66_peric", + GATE_IP_PERIC, 14, 0, 0), + GATE(CLK_TSADC, "tsadc", "mout_user_aclk66_peric", + GATE_IP_PERIC, 15, 0, 0), + GATE(CLK_SPI0, "spi0", "mout_user_aclk66_peric", + GATE_IP_PERIC, 16, 0, 0), + GATE(CLK_SPI1, "spi1", "mout_user_aclk66_peric", + GATE_IP_PERIC, 17, 0, 0), + GATE(CLK_SPI2, "spi2", "mout_user_aclk66_peric", + GATE_IP_PERIC, 18, 0, 0), + GATE(CLK_I2S1, "i2s1", "mout_user_aclk66_peric", + GATE_IP_PERIC, 20, 0, 0), + GATE(CLK_I2S2, "i2s2", "mout_user_aclk66_peric", + GATE_IP_PERIC, 21, 0, 0), + GATE(CLK_PCM1, "pcm1", "mout_user_aclk66_peric", + GATE_IP_PERIC, 22, 0, 0), + GATE(CLK_PCM2, "pcm2", "mout_user_aclk66_peric", + GATE_IP_PERIC, 23, 0, 0), + GATE(CLK_PWM, "pwm", "mout_user_aclk66_peric", + GATE_IP_PERIC, 24, 0, 0), + GATE(CLK_SPDIF, "spdif", "mout_user_aclk66_peric", + GATE_IP_PERIC, 26, 0, 0), + GATE(CLK_USI4, "usi4", "mout_user_aclk66_peric", + GATE_IP_PERIC, 28, 0, 0), + GATE(CLK_USI5, "usi5", "mout_user_aclk66_peric", + GATE_IP_PERIC, 30, 0, 0), + GATE(CLK_USI6, "usi6", "mout_user_aclk66_peric", + GATE_IP_PERIC, 31, 0, 0), + + GATE(CLK_KEYIF, "keyif", "mout_user_aclk66_peric", + GATE_BUS_PERIC, 22, 0, 0), /* PERIS Block */ GATE(CLK_CHIPID, "chipid", "aclk66_psgen", diff --git a/include/dt-bindings/clock/exynos5420.h b/include/dt-bindings/clock/exynos5420.h index 97dcb89d37d3..14e1c8f9640c 100644 --- a/include/dt-bindings/clock/exynos5420.h +++ b/include/dt-bindings/clock/exynos5420.h @@ -63,7 +63,6 @@ #define CLK_SCLK_MPHY_IXTAL24 161 /* gate clocks */ -#define CLK_ACLK66_PERIC 256 #define CLK_UART0 257 #define CLK_UART1 258 #define CLK_UART2 259 -- cgit v1.2.3 From 17d3f1d27ce2fd377ddb03531b87dd9e96e01b34 Mon Sep 17 00:00:00 2001 From: Jonghwa Lee Date: Tue, 27 May 2014 20:27:08 +0900 Subject: clk: exynos4: Add PPMU IP block source clocks. Exynos4 has saveral PPMUs and each of them has operation clock which can be gated through CMU's SFR control. New clocks are listed below. All clocks are added as a gate-typed clock. CLK_PPMULEFT, CLK_PPMURIGHT, CLK_PPMUCAMIF, CLK_PPMUTV, CLK_PPMUMFC_L, CLK_PPMUMFC_R, CLK_G3D, CLK_PPMUIMAGE, CLK_PPMULCD0, CLK_PPMULCD1, CLK_PPMUFILE, CLK_PPMUGPS, CLK_PPMUDMC0, CLK_PPMUDMC1, CLK_PPMUCPU, CLK_PPMUACP, Signed-off-by: Jonghwa Lee Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham Signed-off-by: Tomasz Figa --- drivers/clk/samsung/clk-exynos4.c | 19 +++++++++++++++++++ include/dt-bindings/clock/exynos4.h | 18 ++++++++++++++++++ 2 files changed, 37 insertions(+) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index b08d310bd1bd..75573a4c9674 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -680,6 +680,8 @@ static struct samsung_gate_clock exynos4_gate_clks[] __initdata = { * the device name and clock alias names specified below for some * of the clocks can be removed. */ + GATE(CLK_PPMULEFT, "ppmuleft", "aclk200", GATE_IP_LEFTBUS, 1, 0, 0), + GATE(CLK_PPMURIGHT, "ppmuright", "aclk200", GATE_IP_RIGHTBUS, 1, 0, 0), GATE(CLK_SCLK_HDMI, "sclk_hdmi", "mout_hdmi", SRC_MASK_TV, 0, 0, 0), GATE(CLK_SCLK_SPDIF, "sclk_spdif", "mout_spdif", SRC_MASK_PERIL1, 8, 0, 0), @@ -695,11 +697,13 @@ static struct samsung_gate_clock exynos4_gate_clks[] __initdata = { GATE(CLK_SROMC, "sromc", "aclk133", GATE_IP_FSYS, 11, 0, 0), GATE(CLK_SCLK_G3D, "sclk_g3d", "div_g3d", GATE_IP_G3D, 0, CLK_SET_RATE_PARENT, 0), + GATE(CLK_PPMUG3D, "ppmug3d", "aclk200", GATE_IP_G3D, 1, 0, 0), GATE(CLK_USB_DEVICE, "usb_device", "aclk133", GATE_IP_FSYS, 13, 0, 0), GATE(CLK_ONENAND, "onenand", "aclk133", GATE_IP_FSYS, 15, 0, 0), GATE(CLK_NFCON, "nfcon", "aclk133", GATE_IP_FSYS, 16, 0, 0), GATE(CLK_GPS, "gps", "aclk133", GATE_IP_GPS, 0, 0, 0), GATE(CLK_SMMU_GPS, "smmu_gps", "aclk133", GATE_IP_GPS, 1, 0, 0), + GATE(CLK_PPMUGPS, "ppmugps", "aclk200", GATE_IP_GPS, 2, 0, 0), GATE(CLK_SLIMBUS, "slimbus", "aclk100", GATE_IP_PERIL, 25, 0, 0), GATE(CLK_SCLK_CAM0, "sclk_cam0", "div_cam0", GATE_SCLK_CAM, 4, CLK_SET_RATE_PARENT, 0), @@ -781,19 +785,24 @@ static struct samsung_gate_clock exynos4_gate_clks[] __initdata = { 0, 0), GATE(CLK_SMMU_JPEG, "smmu_jpeg", "aclk160", GATE_IP_CAM, 11, 0, 0), + GATE(CLK_PPMUCAMIF, "ppmucamif", "aclk160", GATE_IP_CAM, 16, 0, 0), GATE(CLK_PIXELASYNCM0, "pxl_async0", "aclk160", GATE_IP_CAM, 17, 0, 0), GATE(CLK_PIXELASYNCM1, "pxl_async1", "aclk160", GATE_IP_CAM, 18, 0, 0), GATE(CLK_SMMU_TV, "smmu_tv", "aclk160", GATE_IP_TV, 4, 0, 0), + GATE(CLK_PPMUTV, "ppmutv", "aclk160", GATE_IP_TV, 5, 0, 0), GATE(CLK_MFC, "mfc", "aclk100", GATE_IP_MFC, 0, 0, 0), GATE(CLK_SMMU_MFCL, "smmu_mfcl", "aclk100", GATE_IP_MFC, 1, 0, 0), GATE(CLK_SMMU_MFCR, "smmu_mfcr", "aclk100", GATE_IP_MFC, 2, 0, 0), + GATE(CLK_PPMUMFC_L, "ppmumfc_l", "aclk100", GATE_IP_MFC, 3, 0, 0), + GATE(CLK_PPMUMFC_R, "ppmumfc_r", "aclk100", GATE_IP_MFC, 4, 0, 0), GATE(CLK_FIMD0, "fimd0", "aclk160", GATE_IP_LCD0, 0, 0, 0), GATE(CLK_SMMU_FIMD0, "smmu_fimd0", "aclk160", GATE_IP_LCD0, 4, 0, 0), + GATE(CLK_PPMULCD0, "ppmulcd0", "aclk160", GATE_IP_LCD0, 5, 0, 0), GATE(CLK_PDMA0, "pdma0", "aclk133", GATE_IP_FSYS, 0, 0, 0), GATE(CLK_PDMA1, "pdma1", "aclk133", GATE_IP_FSYS, 1, @@ -806,6 +815,7 @@ static struct samsung_gate_clock exynos4_gate_clks[] __initdata = { 0, 0), GATE(CLK_SDMMC3, "sdmmc3", "aclk133", GATE_IP_FSYS, 8, 0, 0), + GATE(CLK_PPMUFILE, "ppmufile", "aclk133", GATE_IP_FSYS, 17, 0, 0), GATE(CLK_UART0, "uart0", "aclk100", GATE_IP_PERIL, 0, 0, 0), GATE(CLK_UART1, "uart1", "aclk100", GATE_IP_PERIL, 1, @@ -852,6 +862,10 @@ static struct samsung_gate_clock exynos4_gate_clks[] __initdata = { 0, 0), GATE(CLK_AC97, "ac97", "aclk100", GATE_IP_PERIL, 27, 0, 0), + GATE(CLK_PPMUDMC0, "ppmudmc0", "aclk133", GATE_IP_DMC, 8, 0, 0), + GATE(CLK_PPMUDMC1, "ppmudmc1", "aclk133", GATE_IP_DMC, 9, 0, 0), + GATE(CLK_PPMUCPU, "ppmucpu", "aclk133", GATE_IP_DMC, 10, 0, 0), + GATE(CLK_PPMUACP, "ppmuacp", "aclk133", GATE_IP_DMC, 16, 0, 0), }; /* list of gate clocks supported in exynos4210 soc */ @@ -863,6 +877,9 @@ static struct samsung_gate_clock exynos4210_gate_clks[] __initdata = { GATE(CLK_SMMU_G2D, "smmu_g2d", "aclk200", E4210_GATE_IP_IMAGE, 3, 0, 0), GATE(CLK_SMMU_MDMA, "smmu_mdma", "aclk200", E4210_GATE_IP_IMAGE, 5, 0, 0), + GATE(CLK_PPMUIMAGE, "ppmuimage", "aclk200", E4210_GATE_IP_IMAGE, 9, 0, + 0), + GATE(CLK_PPMULCD1, "ppmulcd1", "aclk160", E4210_GATE_IP_LCD1, 5, 0, 0), GATE(CLK_PCIE_PHY, "pcie_phy", "aclk133", GATE_IP_FSYS, 2, 0, 0), GATE(CLK_SATA_PHY, "sata_phy", "aclk133", GATE_IP_FSYS, 3, 0, 0), GATE(CLK_SATA, "sata", "aclk133", GATE_IP_FSYS, 10, 0, 0), @@ -906,6 +923,8 @@ static struct samsung_gate_clock exynos4x12_gate_clks[] __initdata = { GATE(CLK_MDMA, "mdma", "aclk200", E4X12_GATE_IP_IMAGE, 2, 0, 0), GATE(CLK_SMMU_MDMA, "smmu_mdma", "aclk200", E4X12_GATE_IP_IMAGE, 5, 0, 0), + GATE(CLK_PPMUIMAGE, "ppmuimage", "aclk200", E4X12_GATE_IP_IMAGE, 9, 0, + 0), GATE(CLK_MIPI_HSI, "mipi_hsi", "aclk133", GATE_IP_FSYS, 10, 0, 0), GATE(CLK_CHIPID, "chipid", "aclk100", E4X12_GATE_IP_PERIR, 0, 0, 0), GATE(CLK_SYSREG, "sysreg", "aclk100", E4X12_GATE_IP_PERIR, 1, diff --git a/include/dt-bindings/clock/exynos4.h b/include/dt-bindings/clock/exynos4.h index 1106ca540a96..60fadfc8d6ba 100644 --- a/include/dt-bindings/clock/exynos4.h +++ b/include/dt-bindings/clock/exynos4.h @@ -230,6 +230,24 @@ #define CLK_MOUT_G3D 394 #define CLK_ACLK400_MCUISP 395 /* Exynos4x12 only */ +/* gate clocks - ppmu */ +#define CLK_PPMULEFT 400 +#define CLK_PPMURIGHT 401 +#define CLK_PPMUCAMIF 402 +#define CLK_PPMUTV 403 +#define CLK_PPMUMFC_L 404 +#define CLK_PPMUMFC_R 405 +#define CLK_PPMUG3D 406 +#define CLK_PPMUIMAGE 407 +#define CLK_PPMULCD0 408 +#define CLK_PPMULCD1 409 /* Exynos4210 only */ +#define CLK_PPMUFILE 410 +#define CLK_PPMUGPS 411 +#define CLK_PPMUDMC0 412 +#define CLK_PPMUDMC1 413 +#define CLK_PPMUCPU 414 +#define CLK_PPMUACP 415 + /* div clocks */ #define CLK_DIV_ISP0 450 /* Exynos4x12 only */ #define CLK_DIV_ISP1 451 /* Exynos4x12 only */ -- cgit v1.2.3 From cf2cb0b27116883c23761e974acba5f3bd719d21 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Wed, 7 May 2014 12:42:28 -0400 Subject: ftrace: Use macros for numbers in ftrace rec shift bits As new flags will be added to the ftrace dynamic record, and since the flags field is also a counter, converting the numbers used to do the shifting and masking into a set of macros where we only need to deal with the max bit count of the counter and the number of bits for the flags will prevent mistakes in the future. Dealing with only two numbers is much easier than updating all the macros that deal with shifting and masking. Signed-off-by: Steven Rostedt --- include/linux/ftrace.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 404a686a3644..e4e7df422021 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -322,8 +322,11 @@ enum { FTRACE_FL_REGS_EN = (1UL << 31) }; -#define FTRACE_FL_MASK (0x7UL << 29) -#define FTRACE_REF_MAX ((1UL << 29) - 1) +#define FTRACE_REF_MAX_SHIFT 29 +#define FTRACE_FL_BITS 3 +#define FTRACE_FL_MASKED_BITS ((1UL << FTRACE_FL_BITS) - 1) +#define FTRACE_FL_MASK (FTRACE_FL_MASKED_BITS << FTRACE_REF_MAX_SHIFT) +#define FTRACE_REF_MAX ((1UL << FTRACE_REF_MAX_SHIFT) - 1) struct dyn_ftrace { unsigned long ip; /* address of mcount call-site */ -- cgit v1.2.3 From 0376bde11be5b87c9fd7d6813ac5fd7e1798b1bf Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Wed, 7 May 2014 13:46:45 -0400 Subject: ftrace: Add ftrace_rec_counter() macro to simplify the code The ftrace dynamic record has a flags element that also has a counter. Instead of hard coding "rec->flags & ~FTRACE_FL_MASK" all over the place. Use a macro instead. Signed-off-by: Steven Rostedt --- include/linux/ftrace.h | 2 ++ kernel/trace/ftrace.c | 12 ++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index e4e7df422021..e5baa6b2c93f 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -328,6 +328,8 @@ enum { #define FTRACE_FL_MASK (FTRACE_FL_MASKED_BITS << FTRACE_REF_MAX_SHIFT) #define FTRACE_REF_MAX ((1UL << FTRACE_REF_MAX_SHIFT) - 1) +#define ftrace_rec_count(rec) ((rec)->flags & ~FTRACE_FL_MASK) + struct dyn_ftrace { unsigned long ip; /* address of mcount call-site */ unsigned long flags; diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index b867c647e5bc..a58d840305c3 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -1592,7 +1592,7 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops, if (inc) { rec->flags++; - if (FTRACE_WARN_ON((rec->flags & ~FTRACE_FL_MASK) == FTRACE_REF_MAX)) + if (FTRACE_WARN_ON(ftrace_rec_count(rec) == FTRACE_REF_MAX)) return; /* * If any ops wants regs saved for this function @@ -1601,7 +1601,7 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops, if (ops->flags & FTRACE_OPS_FL_SAVE_REGS) rec->flags |= FTRACE_FL_REGS; } else { - if (FTRACE_WARN_ON((rec->flags & ~FTRACE_FL_MASK) == 0)) + if (FTRACE_WARN_ON(ftrace_rec_count(rec) == 0)) return; rec->flags--; /* @@ -1610,7 +1610,7 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops, * still any ops for this record that wants regs. * If not, we can stop recording them. */ - if ((rec->flags & ~FTRACE_FL_MASK) > 0 && + if (ftrace_rec_count(rec) > 0 && rec->flags & FTRACE_FL_REGS && ops->flags & FTRACE_OPS_FL_SAVE_REGS) { if (!test_rec_ops_needs_regs(rec)) @@ -1700,7 +1700,7 @@ static int ftrace_check_record(struct dyn_ftrace *rec, int enable, int update) * If we are disabling calls, then disable all records that * are enabled. */ - if (enable && (rec->flags & ~FTRACE_FL_MASK)) + if (enable && ftrace_rec_count(rec)) flag = FTRACE_FL_ENABLED; /* @@ -1746,7 +1746,7 @@ static int ftrace_check_record(struct dyn_ftrace *rec, int enable, int update) if (update) { /* If there's no more users, clear all flags */ - if (!(rec->flags & ~FTRACE_FL_MASK)) + if (!ftrace_rec_count(rec)) rec->flags = 0; else /* Just disable the record (keep REGS state) */ @@ -2685,7 +2685,7 @@ static int t_show(struct seq_file *m, void *v) seq_printf(m, "%ps", (void *)rec->ip); if (iter->flags & FTRACE_ITER_ENABLED) seq_printf(m, " (%ld)%s", - rec->flags & ~FTRACE_FL_MASK, + ftrace_rec_count(rec), rec->flags & FTRACE_FL_REGS ? " R" : ""); seq_printf(m, "\n"); -- cgit v1.2.3 From 4e26445faad366d67d7723622bf6a60a6f0f5993 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Mon, 30 Jun 2014 11:50:28 +0800 Subject: kernfs: introduce kernfs_pin_sb() kernfs_pin_sb() tries to get a refcnt of the superblock. This will be used by cgroupfs. v2: - make kernfs_pin_sb() return the superblock. - drop kernfs_drop_sb(). tj: Updated the comment a bit. [ This is a prerequisite for a bugfix. ] Cc: # 3.15 Acked-by: Greg Kroah-Hartman Signed-off-by: Li Zefan Signed-off-by: Tejun Heo --- fs/kernfs/mount.c | 30 ++++++++++++++++++++++++++++++ include/linux/kernfs.h | 1 + 2 files changed, 31 insertions(+) (limited to 'include') diff --git a/fs/kernfs/mount.c b/fs/kernfs/mount.c index d171b98a6cdd..f973ae9b05f1 100644 --- a/fs/kernfs/mount.c +++ b/fs/kernfs/mount.c @@ -211,6 +211,36 @@ void kernfs_kill_sb(struct super_block *sb) kernfs_put(root_kn); } +/** + * kernfs_pin_sb: try to pin the superblock associated with a kernfs_root + * @kernfs_root: the kernfs_root in question + * @ns: the namespace tag + * + * Pin the superblock so the superblock won't be destroyed in subsequent + * operations. This can be used to block ->kill_sb() which may be useful + * for kernfs users which dynamically manage superblocks. + * + * Returns NULL if there's no superblock associated to this kernfs_root, or + * -EINVAL if the superblock is being freed. + */ +struct super_block *kernfs_pin_sb(struct kernfs_root *root, const void *ns) +{ + struct kernfs_super_info *info; + struct super_block *sb = NULL; + + mutex_lock(&kernfs_mutex); + list_for_each_entry(info, &root->supers, node) { + if (info->ns == ns) { + sb = info->sb; + if (!atomic_inc_not_zero(&info->sb->s_active)) + sb = ERR_PTR(-EINVAL); + break; + } + } + mutex_unlock(&kernfs_mutex); + return sb; +} + void __init kernfs_init(void) { kernfs_node_cache = kmem_cache_create("kernfs_node_cache", diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 17aa1cce6f8e..20f493564917 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -304,6 +304,7 @@ struct dentry *kernfs_mount_ns(struct file_system_type *fs_type, int flags, struct kernfs_root *root, unsigned long magic, bool *new_sb_created, const void *ns); void kernfs_kill_sb(struct super_block *sb); +struct super_block *kernfs_pin_sb(struct kernfs_root *root, const void *ns); void kernfs_init(void); -- cgit v1.2.3 From a1a6cc1d2ea9e3adf81faab87b834bc903856207 Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Tue, 1 Jul 2014 06:32:23 +0900 Subject: ata: pata_samsung_cf: removes s5pc100 related ata codes This patch removes support for s5pc100 ata because of no more support S5PC100 SoC in mainline. Signed-off-by: Kukjin Kim Signed-off-by: Tejun Heo --- drivers/ata/pata_samsung_cf.c | 13 ------------- include/linux/platform_data/ata-samsung_cf.h | 1 - 2 files changed, 14 deletions(-) (limited to 'include') diff --git a/drivers/ata/pata_samsung_cf.c b/drivers/ata/pata_samsung_cf.c index fb528831fb92..2578fc16960a 100644 --- a/drivers/ata/pata_samsung_cf.c +++ b/drivers/ata/pata_samsung_cf.c @@ -54,7 +54,6 @@ enum s3c_cpu_type { TYPE_S3C64XX, - TYPE_S5PC100, TYPE_S5PV210, }; @@ -476,10 +475,6 @@ static void pata_s3c_hwinit(struct s3c_ide_info *info, writel(0x1b, info->ide_addr + S3C_ATA_IRQ_MSK); break; - case TYPE_S5PC100: - pata_s3c_cfg_mode(info->sfr_addr); - /* FALLTHROUGH */ - case TYPE_S5PV210: /* Configure as little endian */ pata_s3c_set_endian(info->ide_addr, 0); @@ -549,11 +544,6 @@ static int __init pata_s3c_probe(struct platform_device *pdev) info->sfr_addr = info->ide_addr + 0x1800; info->ide_addr += 0x1900; info->fifo_status_reg = 0x94; - } else if (cpu_type == TYPE_S5PC100) { - ap->ops = &pata_s5p_port_ops; - info->sfr_addr = info->ide_addr + 0x1800; - info->ide_addr += 0x1900; - info->fifo_status_reg = 0x84; } else { ap->ops = &pata_s5p_port_ops; info->fifo_status_reg = 0x84; @@ -652,9 +642,6 @@ static struct platform_device_id pata_s3c_driver_ids[] = { { .name = "s3c64xx-pata", .driver_data = TYPE_S3C64XX, - }, { - .name = "s5pc100-pata", - .driver_data = TYPE_S5PC100, }, { .name = "s5pv210-pata", .driver_data = TYPE_S5PV210, diff --git a/include/linux/platform_data/ata-samsung_cf.h b/include/linux/platform_data/ata-samsung_cf.h index c2049e3d7444..748e71642c4a 100644 --- a/include/linux/platform_data/ata-samsung_cf.h +++ b/include/linux/platform_data/ata-samsung_cf.h @@ -29,7 +29,6 @@ extern void s3c_ide_set_platdata(struct s3c_ide_platdata *pdata); /* architecture-specific IDE configuration */ extern void s3c64xx_ide_setup_gpio(void); -extern void s5pc100_ide_setup_gpio(void); extern void s5pv210_ide_setup_gpio(void); #endif /*__ATA_SAMSUNG_CF_H */ -- cgit v1.2.3 From adc82f77bee3487651f8ad253fb1c8a7bf4ec658 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Tue, 20 May 2014 18:30:03 +0200 Subject: usb: gadget: net2280: Add support for PLX USB338X This patch adds support for the PLX USB3380 and USB3382. This driver is based on the driver from the manufacturer. Since USB338X is register compatible with NET2280, I thought that it would be better to include this hardware into net2280 driver. Manufacturer's driver only supported the USB33X, did not follow the Kernel Style and contain some trivial errors. This patch has tried to address this issues. This patch has only been tested on USB338x hardware, but the merge has been done trying to not affect the behaviour of NET2280. Tested-by: Alan Stern Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 10 +- drivers/usb/gadget/net2280.c | 1119 ++++++++++++++++++++++++++++++++++++++---- drivers/usb/gadget/net2280.h | 121 ++++- include/linux/usb/usb338x.h | 199 ++++++++ 4 files changed, 1351 insertions(+), 98 deletions(-) create mode 100644 include/linux/usb/usb338x.h (limited to 'include') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index ba18e9c110cc..49e434ec527d 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -409,7 +409,7 @@ config USB_NET2272_DMA If unsure, say "N" here. The driver works fine in PIO mode. config USB_NET2280 - tristate "NetChip 228x" + tristate "NetChip 228x / PLX USB338x" depends on PCI help NetChip 2280 / 2282 is a PCI based USB peripheral controller which @@ -419,6 +419,14 @@ config USB_NET2280 (for control transfers) and several endpoints with dedicated functions. + PLX 3380 / 3382 is a PCIe based USB peripheral controller which + supports full, high speed USB 2.0 and super speed USB 3.0 + data transfers. + + It has eight configurable endpoints, as well as endpoint zero + (for control transfers) and several endpoints with dedicated + functions. + Say "y" to link the driver statically, or "m" to build a dynamically linked module called "net2280" and force all gadget drivers to also be dynamically linked. diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index 300b3a71383b..8112d9140a90 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -18,6 +18,9 @@ * hint to completely eliminate some IRQs, if a later IRQ is guaranteed * and DMA chaining is enabled. * + * MSI is enabled by default. The legacy IRQ is used if MSI couldn't + * be enabled. + * * Note that almost all the errata workarounds here are only needed for * rev1 chips. Rev1a silicon (0110) fixes almost all of them. */ @@ -25,10 +28,14 @@ /* * Copyright (C) 2003 David Brownell * Copyright (C) 2003-2005 PLX Technology, Inc. + * Copyright (C) 2014 Ricardo Ribalda - Qtechnology/AS * * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility * with 2282 chip * + * Modified Ricardo Ribalda Qtechnology AS to provide compatibility + * with usb 338x chip. Based on PLX driver + * * 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 @@ -61,9 +68,8 @@ #include #include - -#define DRIVER_DESC "PLX NET228x USB Peripheral Controller" -#define DRIVER_VERSION "2005 Sept 27" +#define DRIVER_DESC "PLX NET228x/USB338x USB Peripheral Controller" +#define DRIVER_VERSION "2005 Sept 27/v3.0" #define EP_DONTUSE 13 /* nonzero */ @@ -73,11 +79,12 @@ static const char driver_name [] = "net2280"; static const char driver_desc [] = DRIVER_DESC; +static const u32 ep_bit[9] = { 0, 17, 2, 19, 4, 1, 18, 3, 20 }; static const char ep0name [] = "ep0"; static const char *const ep_name [] = { ep0name, "ep-a", "ep-b", "ep-c", "ep-d", - "ep-e", "ep-f", + "ep-e", "ep-f", "ep-g", "ep-h", }; /* use_dma -- general goodness, fewer interrupts, less cpu load (vs PIO) @@ -90,11 +97,12 @@ static const char *const ep_name [] = { */ static bool use_dma = 1; static bool use_dma_chaining = 0; +static bool use_msi = 1; /* "modprobe net2280 use_dma=n" etc */ module_param (use_dma, bool, S_IRUGO); module_param (use_dma_chaining, bool, S_IRUGO); - +module_param(use_msi, bool, S_IRUGO); /* mode 0 == ep-{a,b,c,d} 1K fifo each * mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable @@ -140,6 +148,18 @@ static char *type_string (u8 bmAttributes) #define dma_done_ie cpu_to_le32 (1 << DMA_DONE_INTERRUPT_ENABLE) /*-------------------------------------------------------------------------*/ +static inline void enable_pciirqenb(struct net2280_ep *ep) +{ + u32 tmp = readl(&ep->dev->regs->pciirqenb0); + + if (ep->dev->pdev->vendor == 0x17cc) + tmp |= 1 << ep->num; + else + tmp |= 1 << ep_bit[ep->num]; + writel(tmp, &ep->dev->regs->pciirqenb0); + + return; +} static int net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) @@ -148,6 +168,7 @@ net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) struct net2280_ep *ep; u32 max, tmp; unsigned long flags; + static const u32 ep_key[9] = { 1, 0, 1, 0, 1, 1, 0, 1, 0 }; ep = container_of (_ep, struct net2280_ep, ep); if (!_ep || !desc || ep->desc || _ep->name == ep0name @@ -161,9 +182,17 @@ net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE) return -EDOM; + if (dev->pdev->vendor == 0x10b5) { + if ((desc->bEndpointAddress & 0x0f) >= 0x0c) + return -EDOM; + ep->is_in = !!usb_endpoint_dir_in(desc); + if (dev->enhanced_mode && ep->is_in && ep_key[ep->num]) + return -EINVAL; + } + /* sanity check ep-e/ep-f since their fifos are small */ max = usb_endpoint_maxp (desc) & 0x1fff; - if (ep->num > 4 && max > 64) + if (ep->num > 4 && max > 64 && (dev->pdev->vendor == 0x17cc)) return -ERANGE; spin_lock_irqsave (&dev->lock, flags); @@ -176,7 +205,7 @@ net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) ep->out_overflow = 0; /* set speed-dependent max packet; may kick in high bandwidth */ - set_idx_reg (dev->regs, REG_EP_MAXPKT (dev, ep->num), max); + set_max_speed(ep, max); /* FIFO lines can't go to different packets. PIO is ok, so * use it instead of troublesome (non-bulk) multi-packet DMA. @@ -199,23 +228,43 @@ net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) &ep->regs->ep_rsp); } else if (tmp == USB_ENDPOINT_XFER_BULK) { /* catch some particularly blatant driver bugs */ - if ((dev->gadget.speed == USB_SPEED_HIGH - && max != 512) - || (dev->gadget.speed == USB_SPEED_FULL - && max > 64)) { - spin_unlock_irqrestore (&dev->lock, flags); + if ((dev->gadget.speed == USB_SPEED_SUPER && max != 1024) || + (dev->gadget.speed == USB_SPEED_HIGH && max != 512) || + (dev->gadget.speed == USB_SPEED_FULL && max > 64)) { + spin_unlock_irqrestore(&dev->lock, flags); return -ERANGE; } } ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC) ? 1 : 0; - tmp <<= ENDPOINT_TYPE; - tmp |= desc->bEndpointAddress; - tmp |= (4 << ENDPOINT_BYTE_COUNT); /* default full fifo lines */ - tmp |= 1 << ENDPOINT_ENABLE; - wmb (); + /* Enable this endpoint */ + if (dev->pdev->vendor == 0x17cc) { + tmp <<= ENDPOINT_TYPE; + tmp |= desc->bEndpointAddress; + /* default full fifo lines */ + tmp |= (4 << ENDPOINT_BYTE_COUNT); + tmp |= 1 << ENDPOINT_ENABLE; + ep->is_in = (tmp & USB_DIR_IN) != 0; + } else { + /* In Legacy mode, only OUT endpoints are used */ + if (dev->enhanced_mode && ep->is_in) { + tmp <<= IN_ENDPOINT_TYPE; + tmp |= (1 << IN_ENDPOINT_ENABLE); + /* Not applicable to Legacy */ + tmp |= (1 << ENDPOINT_DIRECTION); + } else { + tmp <<= OUT_ENDPOINT_TYPE; + tmp |= (1 << OUT_ENDPOINT_ENABLE); + tmp |= (ep->is_in << ENDPOINT_DIRECTION); + } + + tmp |= usb_endpoint_num(desc); + tmp |= (ep->ep.maxburst << MAX_BURST_SIZE); + } + + /* Make sure all the registers are written before ep_rsp*/ + wmb(); /* for OUT transfers, block the rx fifo until a read is posted */ - ep->is_in = (tmp & USB_DIR_IN) != 0; if (!ep->is_in) writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp); else if (dev->pdev->device != 0x2280) { @@ -226,12 +275,11 @@ net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) | (1 << CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp); } - writel (tmp, &ep->regs->ep_cfg); + writel(tmp, &ep->cfg->ep_cfg); /* enable irqs */ if (!ep->dma) { /* pio, per-packet */ - tmp = (1 << ep->num) | readl (&dev->regs->pciirqenb0); - writel (tmp, &dev->regs->pciirqenb0); + enable_pciirqenb(ep); tmp = (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE) | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE); @@ -251,8 +299,7 @@ net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) tmp = (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE); writel (tmp, &ep->regs->ep_irqenb); - tmp = (1 << ep->num) | readl (&dev->regs->pciirqenb0); - writel (tmp, &dev->regs->pciirqenb0); + enable_pciirqenb(ep); } } @@ -286,7 +333,8 @@ static int handshake (u32 __iomem *ptr, u32 mask, u32 done, int usec) static const struct usb_ep_ops net2280_ep_ops; -static void ep_reset (struct net2280_regs __iomem *regs, struct net2280_ep *ep) +static void ep_reset_228x(struct net2280_regs __iomem *regs, + struct net2280_ep *ep) { u32 tmp; @@ -361,6 +409,55 @@ static void ep_reset (struct net2280_regs __iomem *regs, struct net2280_ep *ep) /* fifo size is handled separately */ } +static void ep_reset_338x(struct net2280_regs __iomem *regs, + struct net2280_ep *ep) +{ + u32 tmp, dmastat; + + ep->desc = NULL; + INIT_LIST_HEAD(&ep->queue); + + usb_ep_set_maxpacket_limit(&ep->ep, ~0); + ep->ep.ops = &net2280_ep_ops; + + /* disable the dma, irqs, endpoint... */ + if (ep->dma) { + writel(0, &ep->dma->dmactl); + writel((1 << DMA_ABORT_DONE_INTERRUPT) | + (1 << DMA_PAUSE_DONE_INTERRUPT) | + (1 << DMA_SCATTER_GATHER_DONE_INTERRUPT) | + (1 << DMA_TRANSACTION_DONE_INTERRUPT) + /* | (1 << DMA_ABORT) */ + , &ep->dma->dmastat); + + dmastat = readl(&ep->dma->dmastat); + if (dmastat == 0x5002) { + WARNING(ep->dev, "The dmastat return = %x!!\n", + dmastat); + writel(0x5a, &ep->dma->dmastat); + } + + tmp = readl(®s->pciirqenb0); + tmp &= ~(1 << ep_bit[ep->num]); + writel(tmp, ®s->pciirqenb0); + } else { + if (ep->num < 5) { + tmp = readl(®s->pciirqenb1); + tmp &= ~(1 << (8 + ep->num)); /* completion */ + writel(tmp, ®s->pciirqenb1); + } + } + writel(0, &ep->regs->ep_irqenb); + + writel((1 << SHORT_PACKET_OUT_DONE_INTERRUPT) | + (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT) | + (1 << FIFO_OVERFLOW) | + (1 << DATA_PACKET_RECEIVED_INTERRUPT) | + (1 << DATA_PACKET_TRANSMITTED_INTERRUPT) | + (1 << DATA_OUT_PING_TOKEN_INTERRUPT) | + (1 << DATA_IN_TOKEN_INTERRUPT), &ep->regs->ep_stat); +} + static void nuke (struct net2280_ep *); static int net2280_disable (struct usb_ep *_ep) @@ -374,13 +471,17 @@ static int net2280_disable (struct usb_ep *_ep) spin_lock_irqsave (&ep->dev->lock, flags); nuke (ep); - ep_reset (ep->dev->regs, ep); + + if (ep->dev->pdev->vendor == 0x10b5) + ep_reset_338x(ep->dev->regs, ep); + else + ep_reset_228x(ep->dev->regs, ep); VDEBUG (ep->dev, "disabled %s %s\n", ep->dma ? "dma" : "pio", _ep->name); /* synch memory views with the device */ - (void) readl (&ep->regs->ep_cfg); + (void)readl(&ep->cfg->ep_cfg); if (use_dma && !ep->dma && ep->num >= 1 && ep->num <= 4) ep->dma = &ep->dev->dma [ep->num - 1]; @@ -698,6 +799,8 @@ static void start_queue (struct net2280_ep *ep, u32 dmactl, u32 td_dma) writel (readl (&dma->dmastat), &dma->dmastat); writel (td_dma, &dma->dmadesc); + if (ep->dev->pdev->vendor == 0x10b5) + dmactl |= (0x01 << DMA_REQUEST_OUTSTANDING); writel (dmactl, &dma->dmactl); /* erratum 0116 workaround part 3: pci arbiter away from net2280 */ @@ -772,6 +875,21 @@ static void start_dma (struct net2280_ep *ep, struct net2280_request *req) start_queue (ep, tmp, req->td_dma); } +static inline void resume_dma(struct net2280_ep *ep) +{ + writel(readl(&ep->dma->dmactl) | (1 << DMA_ENABLE), &ep->dma->dmactl); + + ep->dma_started = true; +} + +static inline void ep_stop_dma(struct net2280_ep *ep) +{ + writel(readl(&ep->dma->dmactl) & ~(1 << DMA_ENABLE), &ep->dma->dmactl); + spin_stop_dma(ep->dma); + + ep->dma_started = false; +} + static inline void queue_dma (struct net2280_ep *ep, struct net2280_request *req, int valid) { @@ -874,8 +992,23 @@ net2280_queue (struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) /* kickstart this i/o queue? */ if (list_empty (&ep->queue) && !ep->stopped) { + /* DMA request while EP halted */ + if (ep->dma && + (readl(&ep->regs->ep_rsp) & (1 << CLEAR_ENDPOINT_HALT)) && + (dev->pdev->vendor == 0x10b5)) { + int valid = 1; + if (ep->is_in) { + int expect; + expect = likely(req->req.zero || + ((req->req.length % + ep->ep.maxpacket) != 0)); + if (expect != ep->in_fifo_validate) + valid = 0; + } + queue_dma(ep, req, valid); + } /* use DMA if the endpoint supports it, else pio */ - if (ep->dma) + else if (ep->dma) start_dma (ep, req); else { /* maybe there's no control data, just status ack */ @@ -993,6 +1126,8 @@ static void scan_dma_completions (struct net2280_ep *ep) } else if (!ep->is_in && (req->req.length % ep->ep.maxpacket) != 0) { tmp = readl (&ep->regs->ep_stat); + if (ep->dev->pdev->vendor == 0x10b5) + return dma_done(ep, req, tmp, 0); /* AVOID TROUBLE HERE by not issuing short reads from * your gadget driver. That helps avoids errata 0121, @@ -1079,7 +1214,7 @@ static void restart_dma (struct net2280_ep *ep) start_queue (ep, dmactl, req->td_dma); } -static void abort_dma (struct net2280_ep *ep) +static void abort_dma_228x(struct net2280_ep *ep) { /* abort the current transfer */ if (likely (!list_empty (&ep->queue))) { @@ -1091,6 +1226,19 @@ static void abort_dma (struct net2280_ep *ep) scan_dma_completions (ep); } +static void abort_dma_338x(struct net2280_ep *ep) +{ + writel((1 << DMA_ABORT), &ep->dma->dmastat); + spin_stop_dma(ep->dma); +} + +static void abort_dma(struct net2280_ep *ep) +{ + if (ep->dev->pdev->vendor == 0x17cc) + return abort_dma_228x(ep); + return abort_dma_338x(ep); +} + /* dequeue ALL requests */ static void nuke (struct net2280_ep *ep) { @@ -1244,6 +1392,9 @@ net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged) ep->wedged = 1; } else { clear_halt (ep); + if (ep->dev->pdev->vendor == 0x10b5 && + !list_empty(&ep->queue) && ep->td_dma) + restart_dma(ep); ep->wedged = 0; } (void) readl (&ep->regs->ep_rsp); @@ -1367,10 +1518,13 @@ static int net2280_set_selfpowered (struct usb_gadget *_gadget, int value) spin_lock_irqsave (&dev->lock, flags); tmp = readl (&dev->usb->usbctl); - if (value) + if (value) { tmp |= (1 << SELF_POWERED_STATUS); - else + dev->selfpowered = 1; + } else { tmp &= ~(1 << SELF_POWERED_STATUS); + dev->selfpowered = 0; + } writel (tmp, &dev->usb->usbctl); spin_unlock_irqrestore (&dev->lock, flags); @@ -1504,14 +1658,14 @@ static ssize_t registers_show(struct device *_dev, /* DMA Control Registers */ /* Configurable EP Control Registers */ - for (i = 0; i < 7; i++) { + for (i = 0; i < dev->n_ep; i++) { struct net2280_ep *ep; ep = &dev->ep [i]; if (i && !ep->desc) continue; - t1 = readl (&ep->regs->ep_cfg); + t1 = readl(&ep->cfg->ep_cfg); t2 = readl (&ep->regs->ep_rsp) & 0xff; t = scnprintf (next, size, "\n%s\tcfg %05x rsp (%02x) %s%s%s%s%s%s%s%s" @@ -1571,7 +1725,7 @@ static ssize_t registers_show(struct device *_dev, t = scnprintf (next, size, "\nirqs: "); size -= t; next += t; - for (i = 0; i < 7; i++) { + for (i = 0; i < dev->n_ep; i++) { struct net2280_ep *ep; ep = &dev->ep [i]; @@ -1606,7 +1760,7 @@ static ssize_t queues_show(struct device *_dev, struct device_attribute *attr, size = PAGE_SIZE; spin_lock_irqsave (&dev->lock, flags); - for (i = 0; i < 7; i++) { + for (i = 0; i < dev->n_ep; i++) { struct net2280_ep *ep = &dev->ep [i]; struct net2280_request *req; int t; @@ -1735,6 +1889,121 @@ static void set_fifo_mode (struct net2280 *dev, int mode) list_add_tail (&dev->ep [6].ep.ep_list, &dev->gadget.ep_list); } +static void defect7374_disable_data_eps(struct net2280 *dev) +{ + /* + * For Defect 7374, disable data EPs (and more): + * - This phase undoes the earlier phase of the Defect 7374 workaround, + * returing ep regs back to normal. + */ + struct net2280_ep *ep; + int i; + unsigned char ep_sel; + u32 tmp_reg; + + for (i = 1; i < 5; i++) { + ep = &dev->ep[i]; + writel(0, &ep->cfg->ep_cfg); + } + + /* CSROUT, CSRIN, PCIOUT, PCIIN, STATIN, RCIN */ + for (i = 0; i < 6; i++) + writel(0, &dev->dep[i].dep_cfg); + + for (ep_sel = 0; ep_sel <= 21; ep_sel++) { + /* Select an endpoint for subsequent operations: */ + tmp_reg = readl(&dev->plregs->pl_ep_ctrl); + writel(((tmp_reg & ~0x1f) | ep_sel), &dev->plregs->pl_ep_ctrl); + + if (ep_sel < 2 || (ep_sel > 9 && ep_sel < 14) || + ep_sel == 18 || ep_sel == 20) + continue; + + /* Change settings on some selected endpoints */ + tmp_reg = readl(&dev->plregs->pl_ep_cfg_4); + tmp_reg &= ~(1 << NON_CTRL_IN_TOLERATE_BAD_DIR); + writel(tmp_reg, &dev->plregs->pl_ep_cfg_4); + tmp_reg = readl(&dev->plregs->pl_ep_ctrl); + tmp_reg |= (1 << EP_INITIALIZED); + writel(tmp_reg, &dev->plregs->pl_ep_ctrl); + } +} + +static void defect7374_enable_data_eps_zero(struct net2280 *dev) +{ + u32 tmp = 0, tmp_reg; + u32 fsmvalue, scratch; + int i; + unsigned char ep_sel; + + scratch = get_idx_reg(dev->regs, SCRATCH); + fsmvalue = scratch & (0xf << DEFECT7374_FSM_FIELD); + scratch &= ~(0xf << DEFECT7374_FSM_FIELD); + + /*See if firmware needs to set up for workaround*/ + if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) { + WARNING(dev, "Operate Defect 7374 workaround soft this time"); + WARNING(dev, "It will operate on cold-reboot and SS connect"); + + /*GPEPs:*/ + tmp = ((0 << ENDPOINT_NUMBER) | (1 << ENDPOINT_DIRECTION) | + (2 << OUT_ENDPOINT_TYPE) | (2 << IN_ENDPOINT_TYPE) | + ((dev->enhanced_mode) ? + 1 << OUT_ENDPOINT_ENABLE : 1 << ENDPOINT_ENABLE) | + (1 << IN_ENDPOINT_ENABLE)); + + for (i = 1; i < 5; i++) + writel(tmp, &dev->ep[i].cfg->ep_cfg); + + /* CSRIN, PCIIN, STATIN, RCIN*/ + tmp = ((0 << ENDPOINT_NUMBER) | (1 << ENDPOINT_ENABLE)); + writel(tmp, &dev->dep[1].dep_cfg); + writel(tmp, &dev->dep[3].dep_cfg); + writel(tmp, &dev->dep[4].dep_cfg); + writel(tmp, &dev->dep[5].dep_cfg); + + /*Implemented for development and debug. + * Can be refined/tuned later.*/ + for (ep_sel = 0; ep_sel <= 21; ep_sel++) { + /* Select an endpoint for subsequent operations: */ + tmp_reg = readl(&dev->plregs->pl_ep_ctrl); + writel(((tmp_reg & ~0x1f) | ep_sel), + &dev->plregs->pl_ep_ctrl); + + if (ep_sel == 1) { + tmp = + (readl(&dev->plregs->pl_ep_ctrl) | + (1 << CLEAR_ACK_ERROR_CODE) | 0); + writel(tmp, &dev->plregs->pl_ep_ctrl); + continue; + } + + if (ep_sel == 0 || (ep_sel > 9 && ep_sel < 14) || + ep_sel == 18 || ep_sel == 20) + continue; + + tmp = (readl(&dev->plregs->pl_ep_cfg_4) | + (1 << NON_CTRL_IN_TOLERATE_BAD_DIR) | 0); + writel(tmp, &dev->plregs->pl_ep_cfg_4); + + tmp = readl(&dev->plregs->pl_ep_ctrl) & + ~(1 << EP_INITIALIZED); + writel(tmp, &dev->plregs->pl_ep_ctrl); + + } + + /* Set FSM to focus on the first Control Read: + * - Tip: Connection speed is known upon the first + * setup request.*/ + scratch |= DEFECT7374_FSM_WAITING_FOR_CONTROL_READ; + set_idx_reg(dev->regs, SCRATCH, scratch); + + } else{ + WARNING(dev, "Defect 7374 workaround soft will NOT operate"); + WARNING(dev, "It will operate on cold-reboot and SS connect"); + } +} + /* keeping it simple: * - one bus driver, initted first; * - one function driver, initted second @@ -1744,7 +2013,7 @@ static void set_fifo_mode (struct net2280 *dev, int mode) * perhaps to bind specific drivers to specific devices. */ -static void usb_reset (struct net2280 *dev) +static void usb_reset_228x(struct net2280 *dev) { u32 tmp; @@ -1760,11 +2029,11 @@ static void usb_reset (struct net2280 *dev) /* clear old dma and irq state */ for (tmp = 0; tmp < 4; tmp++) { - struct net2280_ep *ep = &dev->ep [tmp + 1]; - + struct net2280_ep *ep = &dev->ep[tmp + 1]; if (ep->dma) - abort_dma (ep); + abort_dma(ep); } + writel (~0, &dev->regs->irqstat0), writel (~(1 << SUSPEND_REQUEST_INTERRUPT), &dev->regs->irqstat1), @@ -1780,7 +2049,67 @@ static void usb_reset (struct net2280 *dev) set_fifo_mode (dev, (fifo_mode <= 2) ? fifo_mode : 0); } -static void usb_reinit (struct net2280 *dev) +static void usb_reset_338x(struct net2280 *dev) +{ + u32 tmp; + u32 fsmvalue; + + dev->gadget.speed = USB_SPEED_UNKNOWN; + (void)readl(&dev->usb->usbctl); + + net2280_led_init(dev); + + fsmvalue = get_idx_reg(dev->regs, SCRATCH) & + (0xf << DEFECT7374_FSM_FIELD); + + /* See if firmware needs to set up for workaround: */ + if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) { + INFO(dev, "%s: Defect 7374 FsmValue 0x%08x\n", __func__, + fsmvalue); + } else { + /* disable automatic responses, and irqs */ + writel(0, &dev->usb->stdrsp); + writel(0, &dev->regs->pciirqenb0); + writel(0, &dev->regs->pciirqenb1); + } + + /* clear old dma and irq state */ + for (tmp = 0; tmp < 4; tmp++) { + struct net2280_ep *ep = &dev->ep[tmp + 1]; + + if (ep->dma) + abort_dma(ep); + } + + writel(~0, &dev->regs->irqstat0), writel(~0, &dev->regs->irqstat1); + + if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ) { + /* reset, and enable pci */ + tmp = readl(&dev->regs->devinit) | + (1 << PCI_ENABLE) | + (1 << FIFO_SOFT_RESET) | + (1 << USB_SOFT_RESET) | + (1 << M8051_RESET); + + writel(tmp, &dev->regs->devinit); + } + + /* always ep-{1,2,3,4} ... maybe not ep-3 or ep-4 */ + INIT_LIST_HEAD(&dev->gadget.ep_list); + + for (tmp = 1; tmp < dev->n_ep; tmp++) + list_add_tail(&dev->ep[tmp].ep.ep_list, &dev->gadget.ep_list); + +} + +static void usb_reset(struct net2280 *dev) +{ + if (dev->pdev->vendor == 0x17cc) + return usb_reset_228x(dev); + return usb_reset_338x(dev); +} + +static void usb_reinit_228x(struct net2280 *dev) { u32 tmp; int init_dma; @@ -1803,7 +2132,8 @@ static void usb_reinit (struct net2280 *dev) } else ep->fifo_size = 64; ep->regs = &dev->epregs [tmp]; - ep_reset (dev->regs, ep); + ep->cfg = &dev->epregs[tmp]; + ep_reset_228x(dev->regs, ep); } usb_ep_set_maxpacket_limit(&dev->ep [0].ep, 64); usb_ep_set_maxpacket_limit(&dev->ep [5].ep, 64); @@ -1820,7 +2150,122 @@ static void usb_reinit (struct net2280 *dev) writel (EP_DONTUSE, &dev->dep [tmp].dep_cfg); } -static void ep0_start (struct net2280 *dev) +static void usb_reinit_338x(struct net2280 *dev) +{ + int init_dma; + int i; + u32 tmp, val; + u32 fsmvalue; + static const u32 ne[9] = { 0, 1, 2, 3, 4, 1, 2, 3, 4 }; + static const u32 ep_reg_addr[9] = { 0x00, 0xC0, 0x00, 0xC0, 0x00, + 0x00, 0xC0, 0x00, 0xC0 }; + + /* use_dma changes are ignored till next device re-init */ + init_dma = use_dma; + + /* basic endpoint init */ + for (i = 0; i < dev->n_ep; i++) { + struct net2280_ep *ep = &dev->ep[i]; + + ep->ep.name = ep_name[i]; + ep->dev = dev; + ep->num = i; + + if (i > 0 && i <= 4 && init_dma) + ep->dma = &dev->dma[i - 1]; + + if (dev->enhanced_mode) { + ep->cfg = &dev->epregs[ne[i]]; + ep->regs = (struct net2280_ep_regs __iomem *) + (((void *)&dev->epregs[ne[i]]) + + ep_reg_addr[i]); + ep->fiforegs = &dev->fiforegs[i]; + } else { + ep->cfg = &dev->epregs[i]; + ep->regs = &dev->epregs[i]; + ep->fiforegs = &dev->fiforegs[i]; + } + + ep->fifo_size = (i != 0) ? 2048 : 512; + + ep_reset_338x(dev->regs, ep); + } + usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 512); + + dev->gadget.ep0 = &dev->ep[0].ep; + dev->ep[0].stopped = 0; + + /* Link layer set up */ + fsmvalue = get_idx_reg(dev->regs, SCRATCH) & + (0xf << DEFECT7374_FSM_FIELD); + + /* See if driver needs to set up for workaround: */ + if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) + INFO(dev, "%s: Defect 7374 FsmValue %08x\n", + __func__, fsmvalue); + else { + tmp = readl(&dev->usb_ext->usbctl2) & + ~((1 << U1_ENABLE) | (1 << U2_ENABLE) | (1 << LTM_ENABLE)); + writel(tmp, &dev->usb_ext->usbctl2); + } + + /* Hardware Defect and Workaround */ + val = readl(&dev->ll_lfps_regs->ll_lfps_5); + val &= ~(0xf << TIMER_LFPS_6US); + val |= 0x5 << TIMER_LFPS_6US; + writel(val, &dev->ll_lfps_regs->ll_lfps_5); + + val = readl(&dev->ll_lfps_regs->ll_lfps_6); + val &= ~(0xffff << TIMER_LFPS_80US); + val |= 0x0100 << TIMER_LFPS_80US; + writel(val, &dev->ll_lfps_regs->ll_lfps_6); + + /* + * AA_AB Errata. Issue 4. Workaround for SuperSpeed USB + * Hot Reset Exit Handshake may Fail in Specific Case using + * Default Register Settings. Workaround for Enumeration test. + */ + val = readl(&dev->ll_tsn_regs->ll_tsn_counters_2); + val &= ~(0x1f << HOT_TX_NORESET_TS2); + val |= 0x10 << HOT_TX_NORESET_TS2; + writel(val, &dev->ll_tsn_regs->ll_tsn_counters_2); + + val = readl(&dev->ll_tsn_regs->ll_tsn_counters_3); + val &= ~(0x1f << HOT_RX_RESET_TS2); + val |= 0x3 << HOT_RX_RESET_TS2; + writel(val, &dev->ll_tsn_regs->ll_tsn_counters_3); + + /* + * Set Recovery Idle to Recover bit: + * - On SS connections, setting Recovery Idle to Recover Fmw improves + * link robustness with various hosts and hubs. + * - It is safe to set for all connection speeds; all chip revisions. + * - R-M-W to leave other bits undisturbed. + * - Reference PLX TT-7372 + */ + val = readl(&dev->ll_chicken_reg->ll_tsn_chicken_bit); + val |= (1 << RECOVERY_IDLE_TO_RECOVER_FMW); + writel(val, &dev->ll_chicken_reg->ll_tsn_chicken_bit); + + INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); + + /* disable dedicated endpoints */ + writel(0x0D, &dev->dep[0].dep_cfg); + writel(0x0D, &dev->dep[1].dep_cfg); + writel(0x0E, &dev->dep[2].dep_cfg); + writel(0x0E, &dev->dep[3].dep_cfg); + writel(0x0F, &dev->dep[4].dep_cfg); + writel(0x0C, &dev->dep[5].dep_cfg); +} + +static void usb_reinit(struct net2280 *dev) +{ + if (dev->pdev->vendor == 0x17cc) + return usb_reinit_228x(dev); + return usb_reinit_338x(dev); +} + +static void ep0_start_228x(struct net2280 *dev) { writel ( (1 << CLEAR_EP_HIDE_STATUS_PHASE) | (1 << CLEAR_NAK_OUT_PACKETS) @@ -1863,6 +2308,61 @@ static void ep0_start (struct net2280 *dev) (void) readl (&dev->usb->usbctl); } +static void ep0_start_338x(struct net2280 *dev) +{ + u32 fsmvalue; + + fsmvalue = get_idx_reg(dev->regs, SCRATCH) & + (0xf << DEFECT7374_FSM_FIELD); + + if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) + INFO(dev, "%s: Defect 7374 FsmValue %08x\n", __func__, + fsmvalue); + else + writel((1 << CLEAR_NAK_OUT_PACKETS_MODE) | + (1 << SET_EP_HIDE_STATUS_PHASE), + &dev->epregs[0].ep_rsp); + + /* + * hardware optionally handles a bunch of standard requests + * that the API hides from drivers anyway. have it do so. + * endpoint status/features are handled in software, to + * help pass tests for some dubious behavior. + */ + writel((1 << SET_ISOCHRONOUS_DELAY) | + (1 << SET_SEL) | + (1 << SET_TEST_MODE) | + (1 << SET_ADDRESS) | + (1 << GET_INTERFACE_STATUS) | + (1 << GET_DEVICE_STATUS), + &dev->usb->stdrsp); + dev->wakeup_enable = 1; + writel((1 << USB_ROOT_PORT_WAKEUP_ENABLE) | + (dev->softconnect << USB_DETECT_ENABLE) | + (1 << DEVICE_REMOTE_WAKEUP_ENABLE), + &dev->usb->usbctl); + + /* enable irqs so we can see ep0 and general operation */ + writel((1 << SETUP_PACKET_INTERRUPT_ENABLE) | + (1 << ENDPOINT_0_INTERRUPT_ENABLE) + , &dev->regs->pciirqenb0); + writel((1 << PCI_INTERRUPT_ENABLE) | + (1 << ROOT_PORT_RESET_INTERRUPT_ENABLE) | + (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE) | + (1 << VBUS_INTERRUPT_ENABLE), + &dev->regs->pciirqenb1); + + /* don't leave any writes posted */ + (void)readl(&dev->usb->usbctl); +} + +static void ep0_start(struct net2280 *dev) +{ + if (dev->pdev->vendor == 0x17cc) + return ep0_start_228x(dev); + return ep0_start_338x(dev); +} + /* when a driver is successfully registered, it will receive * control requests including set_configuration(), which enables * non-control requests. then usb traffic follows until a @@ -1886,7 +2386,7 @@ static int net2280_start(struct usb_gadget *_gadget, dev = container_of (_gadget, struct net2280, gadget); - for (i = 0; i < 7; i++) + for (i = 0; i < dev->n_ep; i++) dev->ep [i].irqs = 0; /* hook up the driver ... */ @@ -1900,13 +2400,17 @@ static int net2280_start(struct usb_gadget *_gadget, if (retval) goto err_func; /* Enable force-full-speed testing mode, if desired */ - if (full_speed) + if (full_speed && dev->pdev->vendor == 0x17cc) writel(1 << FORCE_FULL_SPEED_MODE, &dev->usb->xcvrdiag); /* ... then enable host detection and ep0; and we're ready * for set_configuration as well as eventual disconnect. */ net2280_led_active (dev, 1); + + if (dev->pdev->vendor == 0x10b5) + defect7374_enable_data_eps_zero(dev); + ep0_start (dev); DEBUG (dev, "%s ready, usbctl %08x stdrsp %08x\n", @@ -1937,7 +2441,7 @@ stop_activity (struct net2280 *dev, struct usb_gadget_driver *driver) * and kill any outstanding requests. */ usb_reset (dev); - for (i = 0; i < 7; i++) + for (i = 0; i < dev->n_ep; i++) nuke (&dev->ep [i]); /* report disconnect; the driver is already quiesced */ @@ -1967,7 +2471,8 @@ static int net2280_stop(struct usb_gadget *_gadget, net2280_led_active (dev, 0); /* Disable full-speed test mode */ - writel(0, &dev->usb->xcvrdiag); + if (dev->pdev->vendor == 0x17cc) + writel(0, &dev->usb->xcvrdiag); device_remove_file (&dev->pdev->dev, &dev_attr_function); device_remove_file (&dev->pdev->dev, &dev_attr_queues); @@ -2219,6 +2724,350 @@ get_ep_by_addr (struct net2280 *dev, u16 wIndex) return NULL; } +static void defect7374_workaround(struct net2280 *dev, struct usb_ctrlrequest r) +{ + u32 scratch, fsmvalue; + u32 ack_wait_timeout, state; + + /* Workaround for Defect 7374 (U1/U2 erroneously rejected): */ + scratch = get_idx_reg(dev->regs, SCRATCH); + fsmvalue = scratch & (0xf << DEFECT7374_FSM_FIELD); + scratch &= ~(0xf << DEFECT7374_FSM_FIELD); + + if (!((fsmvalue == DEFECT7374_FSM_WAITING_FOR_CONTROL_READ) && + (r.bRequestType & USB_DIR_IN))) + return; + + /* This is the first Control Read for this connection: */ + if (!(readl(&dev->usb->usbstat) & (1 << SUPER_SPEED_MODE))) { + /* + * Connection is NOT SS: + * - Connection must be FS or HS. + * - This FSM state should allow workaround software to + * run after the next USB connection. + */ + scratch |= DEFECT7374_FSM_NON_SS_CONTROL_READ; + goto restore_data_eps; + } + + /* Connection is SS: */ + for (ack_wait_timeout = 0; + ack_wait_timeout < DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS; + ack_wait_timeout++) { + + state = readl(&dev->plregs->pl_ep_status_1) + & (0xff << STATE); + if ((state >= (ACK_GOOD_NORMAL << STATE)) && + (state <= (ACK_GOOD_MORE_ACKS_TO_COME << STATE))) { + scratch |= DEFECT7374_FSM_SS_CONTROL_READ; + break; + } + + /* + * We have not yet received host's Data Phase ACK + * - Wait and try again. + */ + udelay(DEFECT_7374_PROCESSOR_WAIT_TIME); + + continue; + } + + + if (ack_wait_timeout >= DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS) { + ERROR(dev, "FAIL: Defect 7374 workaround waited but failed " + "to detect SS host's data phase ACK."); + ERROR(dev, "PL_EP_STATUS_1(23:16):.Expected from 0x11 to 0x16" + "got 0x%2.2x.\n", state >> STATE); + } else { + WARNING(dev, "INFO: Defect 7374 workaround waited about\n" + "%duSec for Control Read Data Phase ACK\n", + DEFECT_7374_PROCESSOR_WAIT_TIME * ack_wait_timeout); + } + +restore_data_eps: + /* + * Restore data EPs to their pre-workaround settings (disabled, + * initialized, and other details). + */ + defect7374_disable_data_eps(dev); + + set_idx_reg(dev->regs, SCRATCH, scratch); + + return; +} + +static void ep_stall(struct net2280_ep *ep, int stall) +{ + struct net2280 *dev = ep->dev; + u32 val; + static const u32 ep_pl[9] = { 0, 3, 4, 7, 8, 2, 5, 6, 9 }; + + if (stall) { + writel((1 << SET_ENDPOINT_HALT) | + /* (1 << SET_NAK_PACKETS) | */ + (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE), + &ep->regs->ep_rsp); + ep->is_halt = 1; + } else { + if (dev->gadget.speed == USB_SPEED_SUPER) { + /* + * Workaround for SS SeqNum not cleared via + * Endpoint Halt (Clear) bit. select endpoint + */ + val = readl(&dev->plregs->pl_ep_ctrl); + val = (val & ~0x1f) | ep_pl[ep->num]; + writel(val, &dev->plregs->pl_ep_ctrl); + + val |= (1 << SEQUENCE_NUMBER_RESET); + writel(val, &dev->plregs->pl_ep_ctrl); + } + val = readl(&ep->regs->ep_rsp); + val |= (1 << CLEAR_ENDPOINT_HALT) | + (1 << CLEAR_ENDPOINT_TOGGLE); + writel(val + /* | (1 << CLEAR_NAK_PACKETS)*/ + , &ep->regs->ep_rsp); + ep->is_halt = 0; + val = readl(&ep->regs->ep_rsp); + } +} + +static void ep_stdrsp(struct net2280_ep *ep, int value, int wedged) +{ + /* set/clear, then synch memory views with the device */ + if (value) { + ep->stopped = 1; + if (ep->num == 0) + ep->dev->protocol_stall = 1; + else { + if (ep->dma) + ep_stop_dma(ep); + ep_stall(ep, true); + } + + if (wedged) + ep->wedged = 1; + } else { + ep->stopped = 0; + ep->wedged = 0; + + ep_stall(ep, false); + + /* Flush the queue */ + if (!list_empty(&ep->queue)) { + struct net2280_request *req = + list_entry(ep->queue.next, struct net2280_request, + queue); + if (ep->dma) + resume_dma(ep); + else { + if (ep->is_in) + write_fifo(ep, &req->req); + else { + if (read_fifo(ep, req)) + done(ep, req, 0); + } + } + } + } +} + +static void handle_stat0_irqs_superspeed(struct net2280 *dev, + struct net2280_ep *ep, struct usb_ctrlrequest r) +{ + int tmp = 0; + +#define w_value le16_to_cpu(r.wValue) +#define w_index le16_to_cpu(r.wIndex) +#define w_length le16_to_cpu(r.wLength) + + switch (r.bRequest) { + struct net2280_ep *e; + u16 status; + + case USB_REQ_SET_CONFIGURATION: + dev->addressed_state = !w_value; + goto usb3_delegate; + + case USB_REQ_GET_STATUS: + switch (r.bRequestType) { + case (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE): + status = dev->wakeup_enable ? 0x02 : 0x00; + if (dev->selfpowered) + status |= 1 << 0; + status |= (dev->u1_enable << 2 | dev->u2_enable << 3 | + dev->ltm_enable << 4); + writel(0, &dev->epregs[0].ep_irqenb); + set_fifo_bytecount(ep, sizeof(status)); + writel((__force u32) status, &dev->epregs[0].ep_data); + allow_status_338x(ep); + break; + + case (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT): + e = get_ep_by_addr(dev, w_index); + if (!e) + goto do_stall3; + status = readl(&e->regs->ep_rsp) & + (1 << CLEAR_ENDPOINT_HALT); + writel(0, &dev->epregs[0].ep_irqenb); + set_fifo_bytecount(ep, sizeof(status)); + writel((__force u32) status, &dev->epregs[0].ep_data); + allow_status_338x(ep); + break; + + default: + goto usb3_delegate; + } + break; + + case USB_REQ_CLEAR_FEATURE: + switch (r.bRequestType) { + case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE): + if (!dev->addressed_state) { + switch (w_value) { + case USB_DEVICE_U1_ENABLE: + dev->u1_enable = 0; + writel(readl(&dev->usb_ext->usbctl2) & + ~(1 << U1_ENABLE), + &dev->usb_ext->usbctl2); + allow_status_338x(ep); + goto next_endpoints3; + + case USB_DEVICE_U2_ENABLE: + dev->u2_enable = 0; + writel(readl(&dev->usb_ext->usbctl2) & + ~(1 << U2_ENABLE), + &dev->usb_ext->usbctl2); + allow_status_338x(ep); + goto next_endpoints3; + + case USB_DEVICE_LTM_ENABLE: + dev->ltm_enable = 0; + writel(readl(&dev->usb_ext->usbctl2) & + ~(1 << LTM_ENABLE), + &dev->usb_ext->usbctl2); + allow_status_338x(ep); + goto next_endpoints3; + + default: + break; + } + } + if (w_value == USB_DEVICE_REMOTE_WAKEUP) { + dev->wakeup_enable = 0; + writel(readl(&dev->usb->usbctl) & + ~(1 << DEVICE_REMOTE_WAKEUP_ENABLE), + &dev->usb->usbctl); + allow_status_338x(ep); + break; + } + goto usb3_delegate; + + case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT): + e = get_ep_by_addr(dev, w_index); + if (!e) + goto do_stall3; + if (w_value != USB_ENDPOINT_HALT) + goto do_stall3; + VDEBUG(dev, "%s clear halt\n", e->ep.name); + ep_stall(e, false); + if (!list_empty(&e->queue) && e->td_dma) + restart_dma(e); + allow_status(ep); + ep->stopped = 1; + break; + + default: + goto usb3_delegate; + } + break; + case USB_REQ_SET_FEATURE: + switch (r.bRequestType) { + case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE): + if (!dev->addressed_state) { + switch (w_value) { + case USB_DEVICE_U1_ENABLE: + dev->u1_enable = 1; + writel(readl(&dev->usb_ext->usbctl2) | + (1 << U1_ENABLE), + &dev->usb_ext->usbctl2); + allow_status_338x(ep); + goto next_endpoints3; + + case USB_DEVICE_U2_ENABLE: + dev->u2_enable = 1; + writel(readl(&dev->usb_ext->usbctl2) | + (1 << U2_ENABLE), + &dev->usb_ext->usbctl2); + allow_status_338x(ep); + goto next_endpoints3; + + case USB_DEVICE_LTM_ENABLE: + dev->ltm_enable = 1; + writel(readl(&dev->usb_ext->usbctl2) | + (1 << LTM_ENABLE), + &dev->usb_ext->usbctl2); + allow_status_338x(ep); + goto next_endpoints3; + default: + break; + } + } + + if (w_value == USB_DEVICE_REMOTE_WAKEUP) { + dev->wakeup_enable = 1; + writel(readl(&dev->usb->usbctl) | + (1 << DEVICE_REMOTE_WAKEUP_ENABLE), + &dev->usb->usbctl); + allow_status_338x(ep); + break; + } + goto usb3_delegate; + + case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT): + e = get_ep_by_addr(dev, w_index); + if (!e || (w_value != USB_ENDPOINT_HALT)) + goto do_stall3; + ep_stdrsp(e, true, false); + allow_status_338x(ep); + break; + + default: + goto usb3_delegate; + } + + break; + default: + +usb3_delegate: + VDEBUG(dev, "setup %02x.%02x v%04x i%04x l%04x ep_cfg %08x\n", + r.bRequestType, r.bRequest, + w_value, w_index, w_length, + readl(&ep->cfg->ep_cfg)); + + ep->responded = 0; + spin_unlock(&dev->lock); + tmp = dev->driver->setup(&dev->gadget, &r); + spin_lock(&dev->lock); + } +do_stall3: + if (tmp < 0) { + VDEBUG(dev, "req %02x.%02x protocol STALL; stat %d\n", + r.bRequestType, r.bRequest, tmp); + dev->protocol_stall = 1; + /* TD 9.9 Halt Endpoint test. TD 9.22 Set feature test */ + ep_stall(ep, true); + } + +next_endpoints3: + +#undef w_value +#undef w_index +#undef w_length + + return; +} + static void handle_stat0_irqs (struct net2280 *dev, u32 stat) { struct net2280_ep *ep; @@ -2240,10 +3089,20 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat) struct net2280_request *req; if (dev->gadget.speed == USB_SPEED_UNKNOWN) { - if (readl (&dev->usb->usbstat) & (1 << HIGH_SPEED)) + u32 val = readl(&dev->usb->usbstat); + if (val & (1 << SUPER_SPEED)) { + dev->gadget.speed = USB_SPEED_SUPER; + usb_ep_set_maxpacket_limit(&dev->ep[0].ep, + EP0_SS_MAX_PACKET_SIZE); + } else if (val & (1 << HIGH_SPEED)) { dev->gadget.speed = USB_SPEED_HIGH; - else + usb_ep_set_maxpacket_limit(&dev->ep[0].ep, + EP0_HS_MAX_PACKET_SIZE); + } else { dev->gadget.speed = USB_SPEED_FULL; + usb_ep_set_maxpacket_limit(&dev->ep[0].ep, + EP0_HS_MAX_PACKET_SIZE); + } net2280_led_speed (dev, dev->gadget.speed); DEBUG(dev, "%s\n", usb_speed_string(dev->gadget.speed)); } @@ -2261,32 +3120,38 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat) } ep->stopped = 0; dev->protocol_stall = 0; - - if (ep->dev->pdev->device == 0x2280) - tmp = (1 << FIFO_OVERFLOW) - | (1 << FIFO_UNDERFLOW); - else - tmp = 0; - - writel (tmp | (1 << TIMEOUT) - | (1 << USB_STALL_SENT) - | (1 << USB_IN_NAK_SENT) - | (1 << USB_IN_ACK_RCVD) - | (1 << USB_OUT_PING_NAK_SENT) - | (1 << USB_OUT_ACK_SENT) - | (1 << SHORT_PACKET_OUT_DONE_INTERRUPT) - | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT) - | (1 << DATA_PACKET_RECEIVED_INTERRUPT) - | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT) - | (1 << DATA_OUT_PING_TOKEN_INTERRUPT) - | (1 << DATA_IN_TOKEN_INTERRUPT) - , &ep->regs->ep_stat); - u.raw [0] = readl (&dev->usb->setup0123); - u.raw [1] = readl (&dev->usb->setup4567); + if (dev->pdev->vendor == 0x10b5) + ep->is_halt = 0; + else{ + if (ep->dev->pdev->device == 0x2280) + tmp = (1 << FIFO_OVERFLOW) | + (1 << FIFO_UNDERFLOW); + else + tmp = 0; + + writel(tmp | (1 << TIMEOUT) | + (1 << USB_STALL_SENT) | + (1 << USB_IN_NAK_SENT) | + (1 << USB_IN_ACK_RCVD) | + (1 << USB_OUT_PING_NAK_SENT) | + (1 << USB_OUT_ACK_SENT) | + (1 << SHORT_PACKET_OUT_DONE_INTERRUPT) | + (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT) | + (1 << DATA_PACKET_RECEIVED_INTERRUPT) | + (1 << DATA_PACKET_TRANSMITTED_INTERRUPT) | + (1 << DATA_OUT_PING_TOKEN_INTERRUPT) | + (1 << DATA_IN_TOKEN_INTERRUPT) + , &ep->regs->ep_stat); + } + u.raw[0] = readl(&dev->usb->setup0123); + u.raw[1] = readl(&dev->usb->setup4567); cpu_to_le32s (&u.raw [0]); cpu_to_le32s (&u.raw [1]); + if (dev->pdev->vendor == 0x10b5) + defect7374_workaround(dev, u.r); + tmp = 0; #define w_value le16_to_cpu(u.r.wValue) @@ -2318,6 +3183,12 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat) * everything else goes uplevel to the gadget code. */ ep->responded = 1; + + if (dev->gadget.speed == USB_SPEED_SUPER) { + handle_stat0_irqs_superspeed(dev, ep, u.r); + goto next_endpoints; + } + switch (u.r.bRequest) { case USB_REQ_GET_STATUS: { struct net2280_ep *e; @@ -2360,8 +3231,11 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat) VDEBUG(dev, "%s wedged, halt not cleared\n", ep->ep.name); } else { - VDEBUG(dev, "%s clear halt\n", ep->ep.name); + VDEBUG(dev, "%s clear halt\n", e->ep.name); clear_halt(e); + if (ep->dev->pdev->vendor == 0x10b5 && + !list_empty(&e->queue) && e->td_dma) + restart_dma(e); } allow_status (ep); goto next_endpoints; @@ -2381,6 +3255,8 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat) if (e->ep.name == ep0name) goto do_stall; set_halt (e); + if (dev->pdev->vendor == 0x10b5 && e->dma) + abort_dma(e); allow_status (ep); VDEBUG (dev, "%s set halt\n", ep->ep.name); goto next_endpoints; @@ -2392,7 +3268,7 @@ delegate: "ep_cfg %08x\n", u.r.bRequestType, u.r.bRequest, w_value, w_index, w_length, - readl (&ep->regs->ep_cfg)); + readl(&ep->cfg->ep_cfg)); ep->responded = 0; spin_unlock (&dev->lock); tmp = dev->driver->setup (&dev->gadget, &u.r); @@ -2455,7 +3331,7 @@ static void handle_stat1_irqs (struct net2280 *dev, u32 stat) /* after disconnect there's nothing else to do! */ tmp = (1 << VBUS_INTERRUPT) | (1 << ROOT_PORT_RESET_INTERRUPT); - mask = (1 << HIGH_SPEED) | (1 << FULL_SPEED); + mask = (1 << SUPER_SPEED) | (1 << HIGH_SPEED) | (1 << FULL_SPEED); /* VBUS disconnect is indicated by VBUS_PIN and VBUS_INTERRUPT set. * Root Port Reset is indicated by ROOT_PORT_RESET_INTERRUPT set and @@ -2546,12 +3422,19 @@ static void handle_stat1_irqs (struct net2280 *dev, u32 stat) tmp = readl (&dma->dmastat); writel (tmp, &dma->dmastat); + /* dma sync*/ + if (dev->pdev->vendor == 0x10b5) { + u32 r_dmacount = readl(&dma->dmacount); + if (!ep->is_in && (r_dmacount & 0x00FFFFFF) && + (tmp & (1 << DMA_TRANSACTION_DONE_INTERRUPT))) + continue; + } + /* chaining should stop on abort, short OUT from fifo, * or (stat0 codepath) short OUT transfer. */ if (!use_dma_chaining) { - if ((tmp & (1 << DMA_TRANSACTION_DONE_INTERRUPT)) - == 0) { + if (!(tmp & (1 << DMA_TRANSACTION_DONE_INTERRUPT))) { DEBUG (ep->dev, "%s no xact done? %08x\n", ep->ep.name, tmp); continue; @@ -2625,7 +3508,8 @@ static irqreturn_t net2280_irq (int irq, void *_dev) struct net2280 *dev = _dev; /* shared interrupt, not ours */ - if (!(readl(&dev->regs->irqstat0) & (1 << INTA_ASSERTED))) + if (dev->pdev->vendor == 0x17cc && + (!(readl(&dev->regs->irqstat0) & (1 << INTA_ASSERTED)))) return IRQ_NONE; spin_lock (&dev->lock); @@ -2636,6 +3520,13 @@ static irqreturn_t net2280_irq (int irq, void *_dev) /* control requests and PIO */ handle_stat0_irqs (dev, readl (&dev->regs->irqstat0)); + if (dev->pdev->vendor == 0x10b5) { + /* re-enable interrupt to trigger any possible new interrupt */ + u32 pciirqenb1 = readl(&dev->regs->pciirqenb1); + writel(pciirqenb1 & 0x7FFFFFFF, &dev->regs->pciirqenb1); + writel(pciirqenb1, &dev->regs->pciirqenb1); + } + spin_unlock (&dev->lock); return IRQ_HANDLED; @@ -2674,6 +3565,8 @@ static void net2280_remove (struct pci_dev *pdev) } if (dev->got_irq) free_irq (pdev->irq, dev); + if (use_msi && dev->pdev->vendor == 0x10b5) + pci_disable_msi(pdev); if (dev->regs) iounmap (dev->regs); if (dev->region) @@ -2708,7 +3601,8 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) spin_lock_init (&dev->lock); dev->pdev = pdev; dev->gadget.ops = &net2280_ops; - dev->gadget.max_speed = USB_SPEED_HIGH; + dev->gadget.max_speed = (dev->pdev->vendor == 0x10b5) ? + USB_SPEED_SUPER : USB_SPEED_HIGH; /* the "gadget" abstracts/virtualizes the controller */ dev->gadget.name = driver_name; @@ -2750,8 +3644,39 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) dev->dep = (struct net2280_dep_regs __iomem *) (base + 0x0200); dev->epregs = (struct net2280_ep_regs __iomem *) (base + 0x0300); - /* put into initial config, link up all endpoints */ - writel (0, &dev->usb->usbctl); + if (dev->pdev->vendor == 0x10b5) { + u32 fsmvalue; + u32 usbstat; + dev->usb_ext = (struct usb338x_usb_ext_regs __iomem *) + (base + 0x00b4); + dev->fiforegs = (struct usb338x_fifo_regs __iomem *) + (base + 0x0500); + dev->llregs = (struct usb338x_ll_regs __iomem *) + (base + 0x0700); + dev->ll_lfps_regs = (struct usb338x_ll_lfps_regs __iomem *) + (base + 0x0748); + dev->ll_tsn_regs = (struct usb338x_ll_tsn_regs __iomem *) + (base + 0x077c); + dev->ll_chicken_reg = (struct usb338x_ll_chi_regs __iomem *) + (base + 0x079c); + dev->plregs = (struct usb338x_pl_regs __iomem *) + (base + 0x0800); + usbstat = readl(&dev->usb->usbstat); + dev->enhanced_mode = (usbstat & (1 << 11)) ? 1 : 0; + dev->n_ep = (dev->enhanced_mode) ? 9 : 5; + /* put into initial config, link up all endpoints */ + fsmvalue = get_idx_reg(dev->regs, SCRATCH) & + (0xf << DEFECT7374_FSM_FIELD); + /* See if firmware needs to set up for workaround: */ + if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ) + writel(0, &dev->usb->usbctl); + } else{ + dev->enhanced_mode = 0; + dev->n_ep = 7; + /* put into initial config, link up all endpoints */ + writel(0, &dev->usb->usbctl); + } + usb_reset (dev); usb_reinit (dev); @@ -2762,6 +3687,10 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) goto done; } + if (use_msi && dev->pdev->vendor == 0x10b5) + if (pci_enable_msi(pdev)) + ERROR(dev, "Failed to enable MSI mode\n"); + if (request_irq (pdev->irq, net2280_irq, IRQF_SHARED, driver_name, dev) != 0) { ERROR (dev, "request interrupt %d failed\n", pdev->irq); @@ -2797,7 +3726,8 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) } /* enable lower-overhead pci memory bursts during DMA */ - writel ( (1 << DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE) + if (dev->pdev->vendor == 0x17cc) + writel((1 << DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE) // 256 write retries may not be enough... // | (1 << PCI_RETRY_ABORT_ENABLE) | (1 << DMA_READ_MULTIPLE_ENABLE) @@ -2814,10 +3744,10 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) INFO (dev, "%s\n", driver_desc); INFO (dev, "irq %d, pci mem %p, chip rev %04x\n", pdev->irq, base, dev->chiprev); - INFO (dev, "version: " DRIVER_VERSION "; dma %s\n", - use_dma - ? (use_dma_chaining ? "chaining" : "enabled") - : "disabled"); + INFO(dev, "version: " DRIVER_VERSION "; dma %s %s\n", + use_dma ? (use_dma_chaining ? "chaining" : "enabled") + : "disabled", + dev->enhanced_mode ? "enhanced mode" : "legacy mode"); retval = device_create_file (&pdev->dev, &dev_attr_registers); if (retval) goto done; @@ -2849,7 +3779,8 @@ static void net2280_shutdown (struct pci_dev *pdev) writel (0, &dev->usb->usbctl); /* Disable full-speed test mode */ - writel(0, &dev->usb->xcvrdiag); + if (dev->pdev->vendor == 0x17cc) + writel(0, &dev->usb->xcvrdiag); } @@ -2869,8 +3800,24 @@ static const struct pci_device_id pci_ids [] = { { .device = 0x2282, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - -}, { /* end: all zeroes */ } +}, + { + .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), + .class_mask = ~0, + .vendor = 0x10b5, + .device = 0x3380, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { + .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), + .class_mask = ~0, + .vendor = 0x10b5, + .device = 0x3382, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, +{ /* end: all zeroes */ } }; MODULE_DEVICE_TABLE (pci, pci_ids); diff --git a/drivers/usb/gadget/net2280.h b/drivers/usb/gadget/net2280.h index a844be0d683a..a257516abbd6 100644 --- a/drivers/usb/gadget/net2280.h +++ b/drivers/usb/gadget/net2280.h @@ -6,6 +6,7 @@ /* * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com) * Copyright (C) 2003 David Brownell + * Copyright (C) 2014 Ricardo Ribalda - Qtechnology/AS * * 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 @@ -14,6 +15,7 @@ */ #include +#include /*-------------------------------------------------------------------------*/ @@ -59,13 +61,14 @@ set_idx_reg (struct net2280_regs __iomem *regs, u32 index, u32 value) #define CHIPREV_1 0x0100 #define CHIPREV_1A 0x0110 -#ifdef __KERNEL__ +/* DEFECT 7374 */ +#define DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS 200 +#define DEFECT_7374_PROCESSOR_WAIT_TIME 10 -/* ep a-f highspeed and fullspeed maxpacket, addresses - * computed from ep->num - */ -#define REG_EP_MAXPKT(dev,num) (((num) + 1) * 0x10 + \ - (((dev)->gadget.speed == USB_SPEED_HIGH) ? 0 : 1)) +/* ep0 max packet size */ +#define EP0_SS_MAX_PACKET_SIZE 0x200 +#define EP0_HS_MAX_PACKET_SIZE 0x40 +#ifdef __KERNEL__ /*-------------------------------------------------------------------------*/ @@ -85,12 +88,15 @@ struct net2280_dma { struct net2280_ep { struct usb_ep ep; + struct net2280_ep_regs __iomem *cfg; struct net2280_ep_regs __iomem *regs; struct net2280_dma_regs __iomem *dma; struct net2280_dma *dummy; + struct usb338x_fifo_regs __iomem *fiforegs; dma_addr_t td_dma; /* of dummy */ struct net2280 *dev; unsigned long irqs; + unsigned is_halt:1, dma_started:1; /* analogous to a host-side qh */ struct list_head queue; @@ -116,10 +122,19 @@ static inline void allow_status (struct net2280_ep *ep) ep->stopped = 1; } -/* count (<= 4) bytes in the next fifo write will be valid */ -static inline void set_fifo_bytecount (struct net2280_ep *ep, unsigned count) +static void allow_status_338x(struct net2280_ep *ep) { - writeb (count, 2 + (u8 __iomem *) &ep->regs->ep_cfg); + /* + * Control Status Phase Handshake was set by the chip when the setup + * packet arrived. While set, the chip automatically NAKs the host's + * Status Phase tokens. + */ + writel(1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE, &ep->regs->ep_rsp); + + ep->stopped = 1; + + /* TD 9.9 Halt Endpoint test. TD 9.22 set feature test. */ + ep->responded = 0; } struct net2280_request { @@ -135,23 +150,38 @@ struct net2280 { /* each pci device provides one gadget, several endpoints */ struct usb_gadget gadget; spinlock_t lock; - struct net2280_ep ep [7]; + struct net2280_ep ep[9]; struct usb_gadget_driver *driver; unsigned enabled : 1, protocol_stall : 1, softconnect : 1, got_irq : 1, - region : 1; + region:1, + u1_enable:1, + u2_enable:1, + ltm_enable:1, + wakeup_enable:1, + selfpowered:1, + addressed_state:1; u16 chiprev; + int enhanced_mode; + int n_ep; /* pci state used to access those endpoints */ struct pci_dev *pdev; struct net2280_regs __iomem *regs; struct net2280_usb_regs __iomem *usb; + struct usb338x_usb_ext_regs __iomem *usb_ext; struct net2280_pci_regs __iomem *pci; struct net2280_dma_regs __iomem *dma; struct net2280_dep_regs __iomem *dep; struct net2280_ep_regs __iomem *epregs; + struct usb338x_fifo_regs __iomem *fiforegs; + struct usb338x_ll_regs __iomem *llregs; + struct usb338x_ll_lfps_regs __iomem *ll_lfps_regs; + struct usb338x_ll_tsn_regs __iomem *ll_tsn_regs; + struct usb338x_ll_chi_regs __iomem *ll_chicken_reg; + struct usb338x_pl_regs __iomem *plregs; struct pci_pool *requests; // statistics... @@ -179,6 +209,43 @@ static inline void clear_halt (struct net2280_ep *ep) , &ep->regs->ep_rsp); } +/* + * FSM value for Defect 7374 (U1U2 Test) is managed in + * chip's SCRATCH register: + */ +#define DEFECT7374_FSM_FIELD 28 + +/* Waiting for Control Read: + * - A transition to this state indicates a fresh USB connection, + * before the first Setup Packet. The connection speed is not + * known. Firmware is waiting for the first Control Read. + * - Starting state: This state can be thought of as the FSM's typical + * starting state. + * - Tip: Upon the first SS Control Read the FSM never + * returns to this state. + */ +#define DEFECT7374_FSM_WAITING_FOR_CONTROL_READ (1 << DEFECT7374_FSM_FIELD) + +/* Non-SS Control Read: + * - A transition to this state indicates detection of the first HS + * or FS Control Read. + * - Tip: Upon the first SS Control Read the FSM never + * returns to this state. + */ +#define DEFECT7374_FSM_NON_SS_CONTROL_READ (2 << DEFECT7374_FSM_FIELD) + +/* SS Control Read: + * - A transition to this state indicates detection of the + * first SS Control Read. + * - This state indicates workaround completion. Workarounds no longer + * need to be applied (as long as the chip remains powered up). + * - Tip: Once in this state the FSM state does not change (until + * the chip's power is lost and restored). + * - This can be thought of as the final state of the FSM; + * the FSM 'locks-up' in this state until the chip loses power. + */ +#define DEFECT7374_FSM_SS_CONTROL_READ (3 << DEFECT7374_FSM_FIELD) + #ifdef USE_RDK_LEDS static inline void net2280_led_init (struct net2280 *dev) @@ -198,6 +265,9 @@ void net2280_led_speed (struct net2280 *dev, enum usb_device_speed speed) { u32 val = readl (&dev->regs->gpioctl); switch (speed) { + case USB_SPEED_SUPER: /* green + red */ + val |= (1 << GPIO0_DATA) | (1 << GPIO1_DATA); + break; case USB_SPEED_HIGH: /* green */ val &= ~(1 << GPIO0_DATA); val |= (1 << GPIO1_DATA); @@ -271,6 +341,17 @@ static inline void net2280_led_shutdown (struct net2280 *dev) /*-------------------------------------------------------------------------*/ +static inline void set_fifo_bytecount(struct net2280_ep *ep, unsigned count) +{ + if (ep->dev->pdev->vendor == 0x17cc) + writeb(count, 2 + (u8 __iomem *) &ep->regs->ep_cfg); + else{ + u32 tmp = readl(&ep->cfg->ep_cfg) & + (~(0x07 << EP_FIFO_BYTE_COUNT)); + writel(tmp | (count << EP_FIFO_BYTE_COUNT), &ep->cfg->ep_cfg); + } +} + static inline void start_out_naking (struct net2280_ep *ep) { /* NOTE: hardware races lurk here, and PING protocol issues */ @@ -305,4 +386,22 @@ static inline void stop_out_naking (struct net2280_ep *ep) writel ((1 << CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp); } + +static inline void set_max_speed(struct net2280_ep *ep, u32 max) +{ + u32 reg; + static const u32 ep_enhanced[9] = { 0x10, 0x60, 0x30, 0x80, + 0x50, 0x20, 0x70, 0x40, 0x90 }; + + if (ep->dev->enhanced_mode) + reg = ep_enhanced[ep->num]; + else{ + reg = (ep->num + 1) * 0x10; + if (ep->dev->gadget.speed != USB_SPEED_HIGH) + reg += 1; + } + + set_idx_reg(ep->dev->regs, reg, max); +} + #endif /* __KERNEL__ */ diff --git a/include/linux/usb/usb338x.h b/include/linux/usb/usb338x.h new file mode 100644 index 000000000000..f92eb635b9d3 --- /dev/null +++ b/include/linux/usb/usb338x.h @@ -0,0 +1,199 @@ +/* + * USB 338x super/high/full speed USB device controller. + * Unlike many such controllers, this one talks PCI. + * + * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com) + * Copyright (C) 2003 David Brownell + * Copyright (C) 2014 Ricardo Ribalda - Qtechnology/AS + * + * 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. + * + */ + +#ifndef __LINUX_USB_USB338X_H +#define __LINUX_USB_USB338X_H + +#include + +/* + * Extra defined bits for net2280 registers + */ +#define SCRATCH 0x0b + +#define DEFECT7374_FSM_FIELD 28 +#define SUPER_SPEED 8 +#define DMA_REQUEST_OUTSTANDING 5 +#define DMA_PAUSE_DONE_INTERRUPT 26 +#define SET_ISOCHRONOUS_DELAY 24 +#define SET_SEL 22 +#define SUPER_SPEED_MODE 8 + +/*ep_cfg*/ +#define MAX_BURST_SIZE 24 +#define EP_FIFO_BYTE_COUNT 16 +#define IN_ENDPOINT_ENABLE 14 +#define IN_ENDPOINT_TYPE 12 +#define OUT_ENDPOINT_ENABLE 10 +#define OUT_ENDPOINT_TYPE 8 + +struct usb338x_usb_ext_regs { + u32 usbclass; +#define DEVICE_PROTOCOL 16 +#define DEVICE_SUB_CLASS 8 +#define DEVICE_CLASS 0 + u32 ss_sel; +#define U2_SYSTEM_EXIT_LATENCY 8 +#define U1_SYSTEM_EXIT_LATENCY 0 + u32 ss_del; +#define U2_DEVICE_EXIT_LATENCY 8 +#define U1_DEVICE_EXIT_LATENCY 0 + u32 usb2lpm; +#define USB_L1_LPM_HIRD 2 +#define USB_L1_LPM_REMOTE_WAKE 1 +#define USB_L1_LPM_SUPPORT 0 + u32 usb3belt; +#define BELT_MULTIPLIER 10 +#define BEST_EFFORT_LATENCY_TOLERANCE 0 + u32 usbctl2; +#define LTM_ENABLE 7 +#define U2_ENABLE 6 +#define U1_ENABLE 5 +#define FUNCTION_SUSPEND 4 +#define USB3_CORE_ENABLE 3 +#define USB2_CORE_ENABLE 2 +#define SERIAL_NUMBER_STRING_ENABLE 0 + u32 in_timeout; +#define GPEP3_TIMEOUT 19 +#define GPEP2_TIMEOUT 18 +#define GPEP1_TIMEOUT 17 +#define GPEP0_TIMEOUT 16 +#define GPEP3_TIMEOUT_VALUE 13 +#define GPEP3_TIMEOUT_ENABLE 12 +#define GPEP2_TIMEOUT_VALUE 9 +#define GPEP2_TIMEOUT_ENABLE 8 +#define GPEP1_TIMEOUT_VALUE 5 +#define GPEP1_TIMEOUT_ENABLE 4 +#define GPEP0_TIMEOUT_VALUE 1 +#define GPEP0_TIMEOUT_ENABLE 0 + u32 isodelay; +#define ISOCHRONOUS_DELAY 0 +} __packed; + +struct usb338x_fifo_regs { + /* offset 0x0500, 0x0520, 0x0540, 0x0560, 0x0580 */ + u32 ep_fifo_size_base; +#define IN_FIFO_BASE_ADDRESS 22 +#define IN_FIFO_SIZE 16 +#define OUT_FIFO_BASE_ADDRESS 6 +#define OUT_FIFO_SIZE 0 + u32 ep_fifo_out_wrptr; + u32 ep_fifo_out_rdptr; + u32 ep_fifo_in_wrptr; + u32 ep_fifo_in_rdptr; + u32 unused[3]; +} __packed; + + +/* Link layer */ +struct usb338x_ll_regs { + /* offset 0x700 */ + u32 ll_ltssm_ctrl1; + u32 ll_ltssm_ctrl2; + u32 ll_ltssm_ctrl3; + u32 unused[2]; + u32 ll_general_ctrl0; + u32 ll_general_ctrl1; +#define PM_U3_AUTO_EXIT 29 +#define PM_U2_AUTO_EXIT 28 +#define PM_U1_AUTO_EXIT 27 +#define PM_FORCE_U2_ENTRY 26 +#define PM_FORCE_U1_ENTRY 25 +#define PM_LGO_COLLISION_SEND_LAU 24 +#define PM_DIR_LINK_REJECT 23 +#define PM_FORCE_LINK_ACCEPT 22 +#define PM_DIR_ENTRY_U3 20 +#define PM_DIR_ENTRY_U2 19 +#define PM_DIR_ENTRY_U1 18 +#define PM_U2_ENABLE 17 +#define PM_U1_ENABLE 16 +#define SKP_THRESHOLD_ADJUST_FMW 8 +#define RESEND_DPP_ON_LRTY_FMW 7 +#define DL_BIT_VALUE_FMW 6 +#define FORCE_DL_BIT 5 + u32 ll_general_ctrl2; +#define SELECT_INVERT_LANE_POLARITY 7 +#define FORCE_INVERT_LANE_POLARITY 6 + u32 ll_general_ctrl3; + u32 ll_general_ctrl4; + u32 ll_error_gen; +} __packed; + +struct usb338x_ll_lfps_regs { + /* offset 0x748 */ + u32 ll_lfps_5; +#define TIMER_LFPS_6US 16 + u32 ll_lfps_6; +#define TIMER_LFPS_80US 0 +} __packed; + +struct usb338x_ll_tsn_regs { + /* offset 0x77C */ + u32 ll_tsn_counters_2; +#define HOT_TX_NORESET_TS2 24 + u32 ll_tsn_counters_3; +#define HOT_RX_RESET_TS2 0 +} __packed; + +struct usb338x_ll_chi_regs { + /* offset 0x79C */ + u32 ll_tsn_chicken_bit; +#define RECOVERY_IDLE_TO_RECOVER_FMW 3 +} __packed; + +/* protocol layer */ +struct usb338x_pl_regs { + /* offset 0x800 */ + u32 pl_reg_1; + u32 pl_reg_2; + u32 pl_reg_3; + u32 pl_reg_4; + u32 pl_ep_ctrl; + /* Protocol Layer Endpoint Control*/ +#define PL_EP_CTRL 0x810 +#define ENDPOINT_SELECT 0 + /* [4:0] */ +#define EP_INITIALIZED 16 +#define SEQUENCE_NUMBER_RESET 17 +#define CLEAR_ACK_ERROR_CODE 20 + u32 pl_reg_6; + u32 pl_reg_7; + u32 pl_reg_8; + u32 pl_ep_status_1; + /* Protocol Layer Endpoint Status 1*/ +#define PL_EP_STATUS_1 0x820 +#define STATE 16 +#define ACK_GOOD_NORMAL 0x11 +#define ACK_GOOD_MORE_ACKS_TO_COME 0x16 + u32 pl_ep_status_2; + u32 pl_ep_status_3; + /* Protocol Layer Endpoint Status 3*/ +#define PL_EP_STATUS_3 0x828 +#define SEQUENCE_NUMBER 0 + u32 pl_ep_status_4; + /* Protocol Layer Endpoint Status 4*/ +#define PL_EP_STATUS_4 0x82c + u32 pl_ep_cfg_4; + /* Protocol Layer Endpoint Configuration 4*/ +#define PL_EP_CFG_4 0x830 +#define NON_CTRL_IN_TOLERATE_BAD_DIR 6 +} __packed; + +#endif /* __LINUX_USB_USB338X_H */ -- cgit v1.2.3 From b0d0ce8b6b91a0f6f99045b6019fc4c824634fb4 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 30 Jun 2014 12:10:24 +0200 Subject: spi: sh-msiof: Add DMA support Add DMA support to the MSIOF driver using platform data. As MSIOF DMA is limited to 32-bit words (requiring byte/wordswapping for smaller wordsizes), and the group length is limited to 256 words, DMA is performed on two fixed pages, allocated and mapped at driver initialization time. Performance figures (in Mbps) on r8a7791/koelsch at different SPI clock frequencies for 1024-byte and 4096-byte transfers: 1024 bytes 4096 bytes - 3.25 MHz: PIO 2.1, DMA 2.6 | PIO 2.8, DMA 3.1 - 6.5 MHz: PIO 3.2, DMA 4.4 | PIO 5.0, DMA 5.9 - 13 MHz: PIO 4.2, DMA 6.6 | PIO 8.2, DMA 10.7 - 26 MHz: PIO 5.9, DMA 10.4 | PIO 12.4, DMA 18.4 Note that DMA is only faster than PIO for transfers that exceed the FIFO size (typically 64 words / 256 bytes). Also note that large transfers (larger than the group length for DMA, or larger than the FIFO size for PIO), should use cs-gpio (with the appropriate pinmux setup), as the hardware chipselect will be deasserted in between chunks. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 348 ++++++++++++++++++++++++++++++++++++++++++- include/linux/spi/sh_msiof.h | 2 + 2 files changed, 343 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 824f44e6bd88..9922ed3a4441 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -2,6 +2,7 @@ * SuperH MSIOF SPI Master Interface * * Copyright (c) 2009 Magnus Damm + * Copyright (C) 2014 Glider bvba * * 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 @@ -13,6 +14,8 @@ #include #include #include +#include +#include #include #include #include @@ -23,6 +26,7 @@ #include #include #include +#include #include #include @@ -37,6 +41,7 @@ struct sh_msiof_chipdata { }; struct sh_msiof_spi_priv { + struct spi_master *master; void __iomem *mapbase; struct clk *clk; struct platform_device *pdev; @@ -45,6 +50,10 @@ struct sh_msiof_spi_priv { struct completion done; int tx_fifo_size; int rx_fifo_size; + void *tx_dma_page; + void *rx_dma_page; + dma_addr_t tx_dma_addr; + dma_addr_t rx_dma_addr; }; #define TMDR1 0x00 /* Transmit Mode Register 1 */ @@ -84,6 +93,8 @@ struct sh_msiof_spi_priv { #define MDR2_WDLEN1(i) (((i) - 1) << 16) /* Word Count (1-64/256 (SH, A1))) */ #define MDR2_GRPMASK1 0x00000001 /* Group Output Mask 1 (SH, A1) */ +#define MAX_WDLEN 256U + /* TSCR and RSCR */ #define SCR_BRPS_MASK 0x1f00 /* Prescaler Setting (1-32) */ #define SCR_BRPS(i) (((i) - 1) << 8) @@ -282,8 +293,6 @@ static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p, * 1 0 11 11 0 0 * 1 1 11 11 1 1 */ - sh_msiof_write(p, FCTR, 0); - tmp = MDR1_SYNCMD_SPI | 1 << MDR1_FLD_SHIFT | MDR1_XXSTP; tmp |= !cs_high << MDR1_SYNCAC_SHIFT; tmp |= lsb_first << MDR1_BITLSB_SHIFT; @@ -319,8 +328,6 @@ static void sh_msiof_spi_set_mode_regs(struct sh_msiof_spi_priv *p, if (rx_buf) sh_msiof_write(p, RMDR2, dr2); - - sh_msiof_write(p, IER, STR_TEOF | STR_REOF); } static void sh_msiof_reset_str(struct sh_msiof_spi_priv *p) @@ -563,8 +570,12 @@ static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p, /* the fifo contents need shifting */ fifo_shift = 32 - bits; + /* default FIFO watermarks for PIO */ + sh_msiof_write(p, FCTR, 0); + /* setup msiof transfer mode registers */ sh_msiof_spi_set_mode_regs(p, tx_buf, rx_buf, bits, words); + sh_msiof_write(p, IER, IER_TEOFE | IER_REOFE); /* write tx fifo */ if (tx_buf) @@ -609,11 +620,170 @@ stop_ier: return ret; } +static void sh_msiof_dma_complete(void *arg) +{ + struct sh_msiof_spi_priv *p = arg; + + sh_msiof_write(p, IER, 0); + complete(&p->done); +} + +static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx, + void *rx, unsigned int len) +{ + u32 ier_bits = 0; + struct dma_async_tx_descriptor *desc_tx = NULL, *desc_rx = NULL; + dma_cookie_t cookie; + int ret; + + /* 1 stage FIFO watermarks for DMA */ + sh_msiof_write(p, FCTR, FCTR_TFWM_1 | FCTR_RFWM_1); + + /* setup msiof transfer mode registers (32-bit words) */ + sh_msiof_spi_set_mode_regs(p, tx, rx, 32, len / 4); + + if (tx) { + ier_bits |= IER_TDREQE | IER_TDMAE; + dma_sync_single_for_device(&p->pdev->dev, p->tx_dma_addr, len, + DMA_TO_DEVICE); + desc_tx = dmaengine_prep_slave_single(p->master->dma_tx, + p->tx_dma_addr, len, DMA_TO_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc_tx) + return -EIO; + } + + if (rx) { + ier_bits |= IER_RDREQE | IER_RDMAE; + desc_rx = dmaengine_prep_slave_single(p->master->dma_rx, + p->rx_dma_addr, len, DMA_FROM_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc_rx) + return -EIO; + } + sh_msiof_write(p, IER, ier_bits); + + reinit_completion(&p->done); + + if (rx) { + desc_rx->callback = sh_msiof_dma_complete; + desc_rx->callback_param = p; + cookie = dmaengine_submit(desc_rx); + if (dma_submit_error(cookie)) { + ret = cookie; + goto stop_ier; + } + dma_async_issue_pending(p->master->dma_rx); + } + + if (tx) { + if (rx) { + /* No callback */ + desc_tx->callback = NULL; + } else { + desc_tx->callback = sh_msiof_dma_complete; + desc_tx->callback_param = p; + } + cookie = dmaengine_submit(desc_tx); + if (dma_submit_error(cookie)) { + ret = cookie; + goto stop_rx; + } + dma_async_issue_pending(p->master->dma_tx); + } + + ret = sh_msiof_spi_start(p, rx); + if (ret) { + dev_err(&p->pdev->dev, "failed to start hardware\n"); + goto stop_tx; + } + + /* wait for tx fifo to be emptied / rx fifo to be filled */ + ret = wait_for_completion_timeout(&p->done, HZ); + if (!ret) { + dev_err(&p->pdev->dev, "DMA timeout\n"); + ret = -ETIMEDOUT; + goto stop_reset; + } + + /* clear status bits */ + sh_msiof_reset_str(p); + + ret = sh_msiof_spi_stop(p, rx); + if (ret) { + dev_err(&p->pdev->dev, "failed to shut down hardware\n"); + return ret; + } + + if (rx) + dma_sync_single_for_cpu(&p->pdev->dev, p->rx_dma_addr, len, + DMA_FROM_DEVICE); + + return 0; + +stop_reset: + sh_msiof_reset_str(p); + sh_msiof_spi_stop(p, rx); +stop_tx: + if (tx) + dmaengine_terminate_all(p->master->dma_tx); +stop_rx: + if (rx) + dmaengine_terminate_all(p->master->dma_rx); +stop_ier: + sh_msiof_write(p, IER, 0); + return ret; +} + +static void copy_bswap32(u32 *dst, const u32 *src, unsigned int words) +{ + /* src or dst can be unaligned, but not both */ + if ((unsigned long)src & 3) { + while (words--) { + *dst++ = swab32(get_unaligned(src)); + src++; + } + } else if ((unsigned long)dst & 3) { + while (words--) { + put_unaligned(swab32(*src++), dst); + dst++; + } + } else { + while (words--) + *dst++ = swab32(*src++); + } +} + +static void copy_wswap32(u32 *dst, const u32 *src, unsigned int words) +{ + /* src or dst can be unaligned, but not both */ + if ((unsigned long)src & 3) { + while (words--) { + *dst++ = swahw32(get_unaligned(src)); + src++; + } + } else if ((unsigned long)dst & 3) { + while (words--) { + put_unaligned(swahw32(*src++), dst); + dst++; + } + } else { + while (words--) + *dst++ = swahw32(*src++); + } +} + +static void copy_plain32(u32 *dst, const u32 *src, unsigned int words) +{ + memcpy(dst, src, words * 4); +} + static int sh_msiof_transfer_one(struct spi_master *master, struct spi_device *spi, struct spi_transfer *t) { struct sh_msiof_spi_priv *p = spi_master_get_devdata(master); + void (*copy32)(u32 *, const u32 *, unsigned int); void (*tx_fifo)(struct sh_msiof_spi_priv *, const void *, int, int); void (*rx_fifo)(struct sh_msiof_spi_priv *, void *, int, int); const void *tx_buf = t->tx_buf; @@ -624,7 +794,48 @@ static int sh_msiof_transfer_one(struct spi_master *master, unsigned int words; int n; bool swab; + int ret; + + /* setup clocks (clock already enabled in chipselect()) */ + sh_msiof_spi_set_clk_regs(p, clk_get_rate(p->clk), t->speed_hz); + + while (master->dma_tx && len > 15) { + /* + * DMA supports 32-bit words only, hence pack 8-bit and 16-bit + * words, with byte resp. word swapping. + */ + unsigned int l = min(len, MAX_WDLEN * 4); + + if (bits <= 8) { + if (l & 3) + break; + copy32 = copy_bswap32; + } else if (bits <= 16) { + if (l & 1) + break; + copy32 = copy_wswap32; + } else { + copy32 = copy_plain32; + } + + if (tx_buf) + copy32(p->tx_dma_page, tx_buf, l / 4); + ret = sh_msiof_dma_once(p, tx_buf, rx_buf, l); + if (ret) + return ret; + + if (rx_buf) { + copy32(rx_buf, p->rx_dma_page, l / 4); + rx_buf += l; + } + if (tx_buf) + tx_buf += l; + + len -= l; + if (!len) + return 0; + } if (bits <= 8 && len > 15 && !(len & 3)) { bits = 32; @@ -673,9 +884,6 @@ static int sh_msiof_transfer_one(struct spi_master *master, rx_fifo = sh_msiof_spi_read_fifo_32; } - /* setup clocks (clock already enabled in chipselect()) */ - sh_msiof_spi_set_clk_regs(p, clk_get_rate(p->clk), t->speed_hz); - /* transfer in fifo sized chunks */ words = len / bytes_per_word; @@ -745,6 +953,123 @@ static struct sh_msiof_spi_info *sh_msiof_spi_parse_dt(struct device *dev) } #endif +static struct dma_chan *sh_msiof_request_dma_chan(struct device *dev, + enum dma_transfer_direction dir, unsigned int id, dma_addr_t port_addr) +{ + dma_cap_mask_t mask; + struct dma_chan *chan; + struct dma_slave_config cfg; + int ret; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + chan = dma_request_channel(mask, shdma_chan_filter, + (void *)(unsigned long)id); + if (!chan) { + dev_warn(dev, "dma_request_channel failed\n"); + return NULL; + } + + memset(&cfg, 0, sizeof(cfg)); + cfg.slave_id = id; + cfg.direction = dir; + if (dir == DMA_MEM_TO_DEV) + cfg.dst_addr = port_addr; + else + cfg.src_addr = port_addr; + + ret = dmaengine_slave_config(chan, &cfg); + if (ret) { + dev_warn(dev, "dmaengine_slave_config failed %d\n", ret); + dma_release_channel(chan); + return NULL; + } + + return chan; +} + +static int sh_msiof_request_dma(struct sh_msiof_spi_priv *p) +{ + struct platform_device *pdev = p->pdev; + struct device *dev = &pdev->dev; + const struct sh_msiof_spi_info *info = dev_get_platdata(dev); + const struct resource *res; + struct spi_master *master; + + if (!info || !info->dma_tx_id || !info->dma_rx_id) + return 0; /* The driver assumes no error */ + + /* The DMA engine uses the second register set, if present */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + master = p->master; + master->dma_tx = sh_msiof_request_dma_chan(dev, DMA_MEM_TO_DEV, + info->dma_tx_id, + res->start + TFDR); + if (!master->dma_tx) + return -ENODEV; + + master->dma_rx = sh_msiof_request_dma_chan(dev, DMA_DEV_TO_MEM, + info->dma_rx_id, + res->start + RFDR); + if (!master->dma_rx) + goto free_tx_chan; + + p->tx_dma_page = (void *)__get_free_page(GFP_KERNEL | GFP_DMA); + if (!p->tx_dma_page) + goto free_rx_chan; + + p->rx_dma_page = (void *)__get_free_page(GFP_KERNEL | GFP_DMA); + if (!p->rx_dma_page) + goto free_tx_page; + + p->tx_dma_addr = dma_map_single(dev, p->tx_dma_page, PAGE_SIZE, + DMA_TO_DEVICE); + if (dma_mapping_error(dev, p->tx_dma_addr)) + goto free_rx_page; + + p->rx_dma_addr = dma_map_single(dev, p->rx_dma_page, PAGE_SIZE, + DMA_FROM_DEVICE); + if (dma_mapping_error(dev, p->rx_dma_addr)) + goto unmap_tx_page; + + dev_info(dev, "DMA available"); + return 0; + +unmap_tx_page: + dma_unmap_single(dev, p->tx_dma_addr, PAGE_SIZE, DMA_TO_DEVICE); +free_rx_page: + free_page((unsigned long)p->rx_dma_page); +free_tx_page: + free_page((unsigned long)p->tx_dma_page); +free_rx_chan: + dma_release_channel(master->dma_rx); +free_tx_chan: + dma_release_channel(master->dma_tx); + master->dma_tx = NULL; + return -ENODEV; +} + +static void sh_msiof_release_dma(struct sh_msiof_spi_priv *p) +{ + struct spi_master *master = p->master; + struct device *dev; + + if (!master->dma_tx) + return; + + dev = &p->pdev->dev; + dma_unmap_single(dev, p->rx_dma_addr, PAGE_SIZE, DMA_FROM_DEVICE); + dma_unmap_single(dev, p->tx_dma_addr, PAGE_SIZE, DMA_TO_DEVICE); + free_page((unsigned long)p->rx_dma_page); + free_page((unsigned long)p->tx_dma_page); + dma_release_channel(master->dma_rx); + dma_release_channel(master->dma_tx); +} + static int sh_msiof_spi_probe(struct platform_device *pdev) { struct resource *r; @@ -763,6 +1088,7 @@ static int sh_msiof_spi_probe(struct platform_device *pdev) p = spi_master_get_devdata(master); platform_set_drvdata(pdev, p); + p->master = master; of_id = of_match_device(sh_msiof_match, &pdev->dev); if (of_id) { @@ -833,6 +1159,10 @@ static int sh_msiof_spi_probe(struct platform_device *pdev) master->auto_runtime_pm = true; master->transfer_one = sh_msiof_transfer_one; + ret = sh_msiof_request_dma(p); + if (ret < 0) + dev_warn(&pdev->dev, "DMA not available, using PIO\n"); + ret = devm_spi_register_master(&pdev->dev, master); if (ret < 0) { dev_err(&pdev->dev, "spi_register_master error.\n"); @@ -842,6 +1172,7 @@ static int sh_msiof_spi_probe(struct platform_device *pdev) return 0; err2: + sh_msiof_release_dma(p); pm_runtime_disable(&pdev->dev); err1: spi_master_put(master); @@ -850,6 +1181,9 @@ static int sh_msiof_spi_probe(struct platform_device *pdev) static int sh_msiof_spi_remove(struct platform_device *pdev) { + struct sh_msiof_spi_priv *p = platform_get_drvdata(pdev); + + sh_msiof_release_dma(p); pm_runtime_disable(&pdev->dev); return 0; } diff --git a/include/linux/spi/sh_msiof.h b/include/linux/spi/sh_msiof.h index 2e8db3d2d2e5..88a14d81c49e 100644 --- a/include/linux/spi/sh_msiof.h +++ b/include/linux/spi/sh_msiof.h @@ -5,6 +5,8 @@ struct sh_msiof_spi_info { int tx_fifo_override; int rx_fifo_override; u16 num_chipselect; + unsigned int dma_tx_id; + unsigned int dma_rx_id; }; #endif /* __SPI_SH_MSIOF_H__ */ -- cgit v1.2.3 From b14bf2d0c0358140041d1c1805a674376964d0e0 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 30 Jun 2014 11:04:21 -0400 Subject: usb-storage/SCSI: Add broken_fua blacklist flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some buggy JMicron USB-ATA bridges don't know how to translate the FUA bit in READs or WRITEs. This patch adds an entry in unusual_devs.h and a blacklist flag to tell the sd driver not to use FUA. Signed-off-by: Alan Stern Reported-by: Michael Büsch Tested-by: Michael Büsch Acked-by: James Bottomley CC: Matthew Dharm CC: Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/sd.c | 5 ++++- drivers/usb/storage/scsiglue.c | 4 ++++ drivers/usb/storage/unusual_devs.h | 7 +++++++ include/linux/usb_usual.h | 4 +++- include/scsi/scsi_device.h | 1 + 5 files changed, 19 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index e9689d57ccb6..6825eda1114a 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -2441,7 +2441,10 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer) } sdkp->DPOFUA = (data.device_specific & 0x10) != 0; - if (sdkp->DPOFUA && !sdkp->device->use_10_for_rw) { + if (sdp->broken_fua) { + sd_first_printk(KERN_NOTICE, sdkp, "Disabling FUA\n"); + sdkp->DPOFUA = 0; + } else if (sdkp->DPOFUA && !sdkp->device->use_10_for_rw) { sd_first_printk(KERN_NOTICE, sdkp, "Uses READ/WRITE(6), disabling FUA\n"); sdkp->DPOFUA = 0; diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 9d38ddc8da49..866b5df36ed1 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -256,6 +256,10 @@ static int slave_configure(struct scsi_device *sdev) if (us->fflags & US_FL_WRITE_CACHE) sdev->wce_default_on = 1; + /* A few buggy USB-ATA bridges don't understand FUA */ + if (us->fflags & US_FL_BROKEN_FUA) + sdev->broken_fua = 1; + } else { /* Non-disk-type devices don't need to blacklist any pages diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 174a447868cd..80a5b366255f 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1936,6 +1936,13 @@ UNUSUAL_DEV( 0x14cd, 0x6600, 0x0201, 0x0201, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_IGNORE_RESIDUE ), +/* Reported by Michael Büsch */ +UNUSUAL_DEV( 0x152d, 0x0567, 0x0114, 0x0114, + "JMicron", + "USB to ATA/ATAPI Bridge", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_BROKEN_FUA ), + /* Reported by Alexandre Oliva * JMicron responds to USN and several other SCSI ioctls with a * residue that causes subsequent I/O requests to fail. */ diff --git a/include/linux/usb_usual.h b/include/linux/usb_usual.h index 1a64b26046ed..9b7de1b46437 100644 --- a/include/linux/usb_usual.h +++ b/include/linux/usb_usual.h @@ -70,7 +70,9 @@ US_FLAG(NEEDS_CAP16, 0x00400000) \ /* cannot handle READ_CAPACITY_10 */ \ US_FLAG(IGNORE_UAS, 0x00800000) \ - /* Device advertises UAS but it is broken */ + /* Device advertises UAS but it is broken */ \ + US_FLAG(BROKEN_FUA, 0x01000000) \ + /* Cannot handle FUA in WRITE or READ CDBs */ \ #define US_FLAG(name, value) US_FL_##name = value , enum { US_DO_ALL_FLAGS }; diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 5853c913d2b0..27ab31017f09 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -173,6 +173,7 @@ struct scsi_device { unsigned is_visible:1; /* is the device visible in sysfs */ unsigned wce_default_on:1; /* Cache is ON by default */ unsigned no_dif:1; /* T10 PI (DIF) should be disabled */ + unsigned broken_fua:1; /* Don't set FUA bit */ atomic_t disk_events_disable_depth; /* disable depth for disk events */ -- cgit v1.2.3 From 7f502361531e9eecb396cf99bdc9e9a59f7ebd7f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 30 Jun 2014 01:26:23 -0700 Subject: ipv4: irq safe sk_dst_[re]set() and ipv4_sk_update_pmtu() fix We have two different ways to handle changes to sk->sk_dst First way (used by TCP) assumes socket lock is owned by caller, and use no extra lock : __sk_dst_set() & __sk_dst_reset() Another way (used by UDP) uses sk_dst_lock because socket lock is not always taken. Note that sk_dst_lock is not softirq safe. These ways are not inter changeable for a given socket type. ipv4_sk_update_pmtu(), added in linux-3.8, added a race, as it used the socket lock as synchronization, but users might be UDP sockets. Instead of converting sk_dst_lock to a softirq safe version, use xchg() as we did for sk_rx_dst in commit e47eb5dfb296b ("udp: ipv4: do not use sk_dst_lock from softirq context") In a follow up patch, we probably can remove sk_dst_lock, as it is only used in IPv6. Signed-off-by: Eric Dumazet Cc: Steffen Klassert Fixes: 9cb3a50c5f63e ("ipv4: Invalidate the socket cached route on pmtu events if possible") Signed-off-by: David S. Miller --- include/net/sock.h | 12 ++++++------ net/ipv4/route.c | 15 ++++++++------- 2 files changed, 14 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index 173cae485de1..c556fd9b05ac 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1768,9 +1768,11 @@ __sk_dst_set(struct sock *sk, struct dst_entry *dst) static inline void sk_dst_set(struct sock *sk, struct dst_entry *dst) { - spin_lock(&sk->sk_dst_lock); - __sk_dst_set(sk, dst); - spin_unlock(&sk->sk_dst_lock); + struct dst_entry *old_dst; + + sk_tx_queue_clear(sk); + old_dst = xchg(&sk->sk_dst_cache, dst); + dst_release(old_dst); } static inline void @@ -1782,9 +1784,7 @@ __sk_dst_reset(struct sock *sk) static inline void sk_dst_reset(struct sock *sk) { - spin_lock(&sk->sk_dst_lock); - __sk_dst_reset(sk); - spin_unlock(&sk->sk_dst_lock); + sk_dst_set(sk, NULL); } struct dst_entry *__sk_dst_check(struct sock *sk, u32 cookie); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 082239ffe34a..3162ea923ded 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1010,7 +1010,7 @@ void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu) const struct iphdr *iph = (const struct iphdr *) skb->data; struct flowi4 fl4; struct rtable *rt; - struct dst_entry *dst; + struct dst_entry *odst = NULL; bool new = false; bh_lock_sock(sk); @@ -1018,16 +1018,17 @@ void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu) if (!ip_sk_accept_pmtu(sk)) goto out; - rt = (struct rtable *) __sk_dst_get(sk); + odst = sk_dst_get(sk); - if (sock_owned_by_user(sk) || !rt) { + if (sock_owned_by_user(sk) || !odst) { __ipv4_sk_update_pmtu(skb, sk, mtu); goto out; } __build_flow_key(&fl4, sk, iph, 0, 0, 0, 0, 0); - if (!__sk_dst_check(sk, 0)) { + rt = (struct rtable *)odst; + if (odst->obsolete && odst->ops->check(odst, 0) == NULL) { rt = ip_route_output_flow(sock_net(sk), &fl4, sk); if (IS_ERR(rt)) goto out; @@ -1037,8 +1038,7 @@ void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu) __ip_rt_update_pmtu((struct rtable *) rt->dst.path, &fl4, mtu); - dst = dst_check(&rt->dst, 0); - if (!dst) { + if (!dst_check(&rt->dst, 0)) { if (new) dst_release(&rt->dst); @@ -1050,10 +1050,11 @@ void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu) } if (new) - __sk_dst_set(sk, &rt->dst); + sk_dst_set(sk, &rt->dst); out: bh_unlock_sock(sk); + dst_release(odst); } EXPORT_SYMBOL_GPL(ipv4_sk_update_pmtu); -- cgit v1.2.3 From 981409b25e2a99409b26daa67293ca1cfd5ea0a0 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Fri, 16 Nov 2012 14:46:04 +0530 Subject: fbdev: arm has __raw I/O accessors, use them in fb.h This removes the sparse warnings on arm platforms: warning: cast removes address space of expression Signed-off-by: Archit Taneja Signed-off-by: Tomi Valkeinen Cc: H Hartley Sweeten Cc: Alexander Shiyan Cc: Russell King --- include/linux/fb.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/fb.h b/include/linux/fb.h index b6bfda99add3..09bb7a18d287 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -553,7 +553,7 @@ static inline struct apertures_struct *alloc_apertures(unsigned int max_num) { #define fb_memcpy_fromfb sbus_memcpy_fromio #define fb_memcpy_tofb sbus_memcpy_toio -#elif defined(__i386__) || defined(__alpha__) || defined(__x86_64__) || defined(__hppa__) || defined(__sh__) || defined(__powerpc__) || defined(__avr32__) || defined(__bfin__) +#elif defined(__i386__) || defined(__alpha__) || defined(__x86_64__) || defined(__hppa__) || defined(__sh__) || defined(__powerpc__) || defined(__avr32__) || defined(__bfin__) || defined(__arm__) #define fb_readb __raw_readb #define fb_readw __raw_readw -- cgit v1.2.3 From 79922b8009c074e30d3a97f5a24519f11814ad03 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Tue, 6 May 2014 21:56:17 -0400 Subject: ftrace: Optimize function graph to be called directly Function graph tracing is a bit different than the function tracers, as it is processed after either the ftrace_caller or ftrace_regs_caller and we only have one place to modify the jump to ftrace_graph_caller, the jump needs to happen after the restore of registeres. The function graph tracer is dependent on the function tracer, where even if the function graph tracing is going on by itself, the save and restore of registers is still done for function tracing regardless of if function tracing is happening, before it calls the function graph code. If there's no function tracing happening, it is possible to just call the function graph tracer directly, and avoid the wasted effort to save and restore regs for function tracing. This requires adding new flags to the dyn_ftrace records: FTRACE_FL_TRAMP FTRACE_FL_TRAMP_EN The first is set if the count for the record is one, and the ftrace_ops associated to that record has its own trampoline. That way the mcount code can call that trampoline directly. In the future, trampolines can be added to arbitrary ftrace_ops, where you can have two or more ftrace_ops registered to ftrace (like kprobes and perf) and if they are not tracing the same functions, then instead of doing a loop to check all registered ftrace_ops against their hashes, just call the ftrace_ops trampoline directly, which would call the registered ftrace_ops function directly. Without this patch perf showed: 0.05% hackbench [kernel.kallsyms] [k] ftrace_caller 0.05% hackbench [kernel.kallsyms] [k] arch_local_irq_save 0.05% hackbench [kernel.kallsyms] [k] native_sched_clock 0.04% hackbench [kernel.kallsyms] [k] __buffer_unlock_commit 0.04% hackbench [kernel.kallsyms] [k] preempt_trace 0.04% hackbench [kernel.kallsyms] [k] prepare_ftrace_return 0.04% hackbench [kernel.kallsyms] [k] __this_cpu_preempt_check 0.04% hackbench [kernel.kallsyms] [k] ftrace_graph_caller See that the ftrace_caller took up more time than the ftrace_graph_caller did. With this patch: 0.05% hackbench [kernel.kallsyms] [k] __buffer_unlock_commit 0.04% hackbench [kernel.kallsyms] [k] call_filter_check_discard 0.04% hackbench [kernel.kallsyms] [k] ftrace_graph_caller 0.04% hackbench [kernel.kallsyms] [k] sched_clock The ftrace_caller is no where to be found and ftrace_graph_caller still takes up the same percentage. Signed-off-by: Steven Rostedt --- arch/x86/kernel/mcount_64.S | 5 + include/linux/ftrace.h | 19 +++- kernel/trace/ftrace.c | 242 ++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 254 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/arch/x86/kernel/mcount_64.S b/arch/x86/kernel/mcount_64.S index c050a0153168..6b4e3c3b3d74 100644 --- a/arch/x86/kernel/mcount_64.S +++ b/arch/x86/kernel/mcount_64.S @@ -182,6 +182,10 @@ END(function_hook) ENTRY(ftrace_graph_caller) MCOUNT_SAVE_FRAME + /* Check if tracing was disabled (quick check) */ + cmpl $0, function_trace_stop + jne fgraph_skip + #ifdef CC_USING_FENTRY leaq SS+16(%rsp), %rdi movq $0, %rdx /* No framepointers needed */ @@ -194,6 +198,7 @@ ENTRY(ftrace_graph_caller) call prepare_ftrace_return +fgraph_skip: MCOUNT_RESTORE_FRAME retq diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index e5baa6b2c93f..11e18fd58b1a 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -118,12 +118,15 @@ struct ftrace_ops { ftrace_func_t func; struct ftrace_ops *next; unsigned long flags; - int __percpu *disabled; void *private; + int __percpu *disabled; #ifdef CONFIG_DYNAMIC_FTRACE + int trampolines; struct ftrace_hash *notrace_hash; struct ftrace_hash *filter_hash; + struct ftrace_hash *tramp_hash; struct mutex regex_lock; + unsigned long trampoline; #endif }; @@ -317,13 +320,15 @@ extern int ftrace_nr_registered_ops(void); * from tracing that function. */ enum { - FTRACE_FL_ENABLED = (1UL << 29), + FTRACE_FL_ENABLED = (1UL << 31), FTRACE_FL_REGS = (1UL << 30), - FTRACE_FL_REGS_EN = (1UL << 31) + FTRACE_FL_REGS_EN = (1UL << 29), + FTRACE_FL_TRAMP = (1UL << 28), + FTRACE_FL_TRAMP_EN = (1UL << 27), }; -#define FTRACE_REF_MAX_SHIFT 29 -#define FTRACE_FL_BITS 3 +#define FTRACE_REF_MAX_SHIFT 27 +#define FTRACE_FL_BITS 5 #define FTRACE_FL_MASKED_BITS ((1UL << FTRACE_FL_BITS) - 1) #define FTRACE_FL_MASK (FTRACE_FL_MASKED_BITS << FTRACE_REF_MAX_SHIFT) #define FTRACE_REF_MAX ((1UL << FTRACE_REF_MAX_SHIFT) - 1) @@ -436,6 +441,10 @@ void ftrace_modify_all_code(int command); #define FTRACE_ADDR ((unsigned long)ftrace_caller) #endif +#ifndef FTRACE_GRAPH_ADDR +#define FTRACE_GRAPH_ADDR ((unsigned long)ftrace_graph_caller) +#endif + #ifndef FTRACE_REGS_ADDR #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS # define FTRACE_REGS_ADDR ((unsigned long)ftrace_regs_caller) diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index a58d840305c3..5d15eb8146a7 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -1042,6 +1042,8 @@ static struct pid * const ftrace_swapper_pid = &init_struct_pid; #ifdef CONFIG_DYNAMIC_FTRACE +static struct ftrace_ops *removed_ops; + #ifndef CONFIG_FTRACE_MCOUNT_RECORD # error Dynamic ftrace depends on MCOUNT_RECORD #endif @@ -1512,6 +1514,33 @@ static bool test_rec_ops_needs_regs(struct dyn_ftrace *rec) return keep_regs; } +static void ftrace_remove_tramp(struct ftrace_ops *ops, + struct dyn_ftrace *rec) +{ + struct ftrace_func_entry *entry; + + entry = ftrace_lookup_ip(ops->tramp_hash, rec->ip); + if (!entry) + return; + + /* + * The tramp_hash entry will be removed at time + * of update. + */ + ops->trampolines--; + rec->flags &= ~FTRACE_FL_TRAMP; +} + +static void ftrace_clear_tramps(struct dyn_ftrace *rec) +{ + struct ftrace_ops *op; + + do_for_each_ftrace_op(op, ftrace_ops_list) { + if (op->trampolines) + ftrace_remove_tramp(op, rec); + } while_for_each_ftrace_op(op); +} + static void __ftrace_hash_rec_update(struct ftrace_ops *ops, int filter_hash, bool inc) @@ -1594,6 +1623,28 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops, rec->flags++; if (FTRACE_WARN_ON(ftrace_rec_count(rec) == FTRACE_REF_MAX)) return; + + /* + * If there's only a single callback registered to a + * function, and the ops has a trampoline registered + * for it, then we can call it directly. + */ + if (ftrace_rec_count(rec) == 1 && ops->trampoline) { + rec->flags |= FTRACE_FL_TRAMP; + ops->trampolines++; + } else { + /* + * If we are adding another function callback + * to this function, and the previous had a + * trampoline used, then we need to go back to + * the default trampoline. + */ + rec->flags &= ~FTRACE_FL_TRAMP; + + /* remove trampolines from any ops for this rec */ + ftrace_clear_tramps(rec); + } + /* * If any ops wants regs saved for this function * then all ops will get saved regs. @@ -1604,6 +1655,10 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops, if (FTRACE_WARN_ON(ftrace_rec_count(rec) == 0)) return; rec->flags--; + + if (ops->trampoline && !ftrace_rec_count(rec)) + ftrace_remove_tramp(ops, rec); + /* * If the rec had REGS enabled and the ops that is * being removed had REGS set, then see if there is @@ -1616,6 +1671,11 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops, if (!test_rec_ops_needs_regs(rec)) rec->flags &= ~FTRACE_FL_REGS; } + + /* + * flags will be cleared in ftrace_check_record() + * if rec count is zero. + */ } count++; /* Shortcut, if we handled all records, we are done. */ @@ -1704,13 +1764,19 @@ static int ftrace_check_record(struct dyn_ftrace *rec, int enable, int update) flag = FTRACE_FL_ENABLED; /* - * If enabling and the REGS flag does not match the REGS_EN, then - * do not ignore this record. Set flags to fail the compare against - * ENABLED. + * If enabling and the REGS flag does not match the REGS_EN, or + * the TRAMP flag doesn't match the TRAMP_EN, then do not ignore + * this record. Set flags to fail the compare against ENABLED. */ - if (flag && - (!(rec->flags & FTRACE_FL_REGS) != !(rec->flags & FTRACE_FL_REGS_EN))) - flag |= FTRACE_FL_REGS; + if (flag) { + if (!(rec->flags & FTRACE_FL_REGS) != + !(rec->flags & FTRACE_FL_REGS_EN)) + flag |= FTRACE_FL_REGS; + + if (!(rec->flags & FTRACE_FL_TRAMP) != + !(rec->flags & FTRACE_FL_TRAMP_EN)) + flag |= FTRACE_FL_TRAMP; + } /* If the state of this record hasn't changed, then do nothing */ if ((rec->flags & FTRACE_FL_ENABLED) == flag) @@ -1728,6 +1794,12 @@ static int ftrace_check_record(struct dyn_ftrace *rec, int enable, int update) else rec->flags &= ~FTRACE_FL_REGS_EN; } + if (flag & FTRACE_FL_TRAMP) { + if (rec->flags & FTRACE_FL_TRAMP) + rec->flags |= FTRACE_FL_TRAMP_EN; + else + rec->flags &= ~FTRACE_FL_TRAMP_EN; + } } /* @@ -1736,7 +1808,7 @@ static int ftrace_check_record(struct dyn_ftrace *rec, int enable, int update) * Otherwise, * return UPDATE_MODIFY_CALL to tell the caller to convert * from the save regs, to a non-save regs function or - * vice versa. + * vice versa, or from a trampoline call. */ if (flag & FTRACE_FL_ENABLED) return FTRACE_UPDATE_MAKE_CALL; @@ -1783,6 +1855,43 @@ int ftrace_test_record(struct dyn_ftrace *rec, int enable) return ftrace_check_record(rec, enable, 0); } +static struct ftrace_ops * +ftrace_find_tramp_ops_curr(struct dyn_ftrace *rec) +{ + struct ftrace_ops *op; + + /* Removed ops need to be tested first */ + if (removed_ops && removed_ops->tramp_hash) { + if (ftrace_lookup_ip(removed_ops->tramp_hash, rec->ip)) + return removed_ops; + } + + do_for_each_ftrace_op(op, ftrace_ops_list) { + if (!op->tramp_hash) + continue; + + if (ftrace_lookup_ip(op->tramp_hash, rec->ip)) + return op; + + } while_for_each_ftrace_op(op); + + return NULL; +} + +static struct ftrace_ops * +ftrace_find_tramp_ops_new(struct dyn_ftrace *rec) +{ + struct ftrace_ops *op; + + do_for_each_ftrace_op(op, ftrace_ops_list) { + /* pass rec in as regs to have non-NULL val */ + if (ftrace_ops_test(op, rec->ip, rec)) + return op; + } while_for_each_ftrace_op(op); + + return NULL; +} + /** * ftrace_get_addr_new - Get the call address to set to * @rec: The ftrace record descriptor @@ -1795,6 +1904,20 @@ int ftrace_test_record(struct dyn_ftrace *rec, int enable) */ unsigned long ftrace_get_addr_new(struct dyn_ftrace *rec) { + struct ftrace_ops *ops; + + /* Trampolines take precedence over regs */ + if (rec->flags & FTRACE_FL_TRAMP) { + ops = ftrace_find_tramp_ops_new(rec); + if (FTRACE_WARN_ON(!ops || !ops->trampoline)) { + pr_warning("Bad trampoline accounting at: %p (%pS)\n", + (void *)rec->ip, (void *)rec->ip); + /* Ftrace is shutting down, return anything */ + return (unsigned long)FTRACE_ADDR; + } + return ops->trampoline; + } + if (rec->flags & FTRACE_FL_REGS) return (unsigned long)FTRACE_REGS_ADDR; else @@ -1813,6 +1936,20 @@ unsigned long ftrace_get_addr_new(struct dyn_ftrace *rec) */ unsigned long ftrace_get_addr_curr(struct dyn_ftrace *rec) { + struct ftrace_ops *ops; + + /* Trampolines take precedence over regs */ + if (rec->flags & FTRACE_FL_TRAMP_EN) { + ops = ftrace_find_tramp_ops_curr(rec); + if (FTRACE_WARN_ON(!ops)) { + pr_warning("Bad trampoline accounting at: %p (%pS)\n", + (void *)rec->ip, (void *)rec->ip); + /* Ftrace is shutting down, return anything */ + return (unsigned long)FTRACE_ADDR; + } + return ops->trampoline; + } + if (rec->flags & FTRACE_FL_REGS_EN) return (unsigned long)FTRACE_REGS_ADDR; else @@ -2055,6 +2192,78 @@ void __weak arch_ftrace_update_code(int command) ftrace_run_stop_machine(command); } +static int ftrace_save_ops_tramp_hash(struct ftrace_ops *ops) +{ + struct ftrace_page *pg; + struct dyn_ftrace *rec; + int size, bits; + int ret; + + size = ops->trampolines; + bits = 0; + /* + * Make the hash size about 1/2 the # found + */ + for (size /= 2; size; size >>= 1) + bits++; + + ops->tramp_hash = alloc_ftrace_hash(bits); + /* + * TODO: a failed allocation is going to screw up + * the accounting of what needs to be modified + * and not. For now, we kill ftrace if we fail + * to allocate here. But there are ways around this, + * but that will take a little more work. + */ + if (!ops->tramp_hash) + return -ENOMEM; + + do_for_each_ftrace_rec(pg, rec) { + if (ftrace_rec_count(rec) == 1 && + ftrace_ops_test(ops, rec->ip, rec)) { + + /* This record had better have a trampoline */ + if (FTRACE_WARN_ON(!(rec->flags & FTRACE_FL_TRAMP_EN))) + return -1; + + ret = add_hash_entry(ops->tramp_hash, rec->ip); + if (ret < 0) + return ret; + } + } while_for_each_ftrace_rec(); + + return 0; +} + +static int ftrace_save_tramp_hashes(void) +{ + struct ftrace_ops *op; + int ret; + + /* + * Now that any trampoline is being used, we need to save the + * hashes for the ops that have them. This allows the mapping + * back from the record to the ops that has the trampoline to + * know what code is being replaced. Modifying code must always + * verify what it is changing. + */ + do_for_each_ftrace_op(op, ftrace_ops_list) { + + /* The tramp_hash is recreated each time. */ + free_ftrace_hash(op->tramp_hash); + op->tramp_hash = NULL; + + if (op->trampolines) { + ret = ftrace_save_ops_tramp_hash(op); + if (ret) + return ret; + } + + } while_for_each_ftrace_op(op); + + return 0; +} + static void ftrace_run_update_code(int command) { int ret; @@ -2081,6 +2290,9 @@ static void ftrace_run_update_code(int command) ret = ftrace_arch_code_modify_post_process(); FTRACE_WARN_ON(ret); + + ret = ftrace_save_tramp_hashes(); + FTRACE_WARN_ON(ret); } static ftrace_func_t saved_ftrace_func; @@ -2171,8 +2383,16 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command) return 0; } + /* + * If the ops uses a trampoline, then it needs to be + * tested first on update. + */ + removed_ops = ops; + ftrace_run_update_code(command); + removed_ops = NULL; + /* * Dynamic ops may be freed, we must make sure that all * callers are done before leaving this function. @@ -5116,6 +5336,11 @@ int register_ftrace_graph(trace_func_graph_ret_t retfunc, /* Function graph doesn't use the .func field of global_ops */ global_ops.flags |= FTRACE_OPS_FL_STUB; +#ifdef CONFIG_DYNAMIC_FTRACE + /* Optimize function graph calling (if implemented by arch) */ + global_ops.trampoline = FTRACE_GRAPH_ADDR; +#endif + ret = ftrace_startup(&global_ops, FTRACE_START_FUNC_RET); out: @@ -5136,6 +5361,9 @@ void unregister_ftrace_graph(void) __ftrace_graph_entry = ftrace_graph_entry_stub; ftrace_shutdown(&global_ops, FTRACE_STOP_FUNC_RET); global_ops.flags &= ~FTRACE_OPS_FL_STUB; +#ifdef CONFIG_DYNAMIC_FTRACE + global_ops.trampoline = 0; +#endif unregister_pm_notifier(&ftrace_suspend_notifier); unregister_trace_sched_switch(ftrace_graph_probe_sched_switch, NULL); -- cgit v1.2.3 From 12306276fabcb746a14979e96f43a13c724dec49 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Fri, 20 Jun 2014 13:38:54 -0400 Subject: tracing: Move the trace_seq_* functions into its own trace_seq.c file The trace_seq_*() functions are a nice utility that allows users to manipulate buffers with printf() like formats. It has its own trace_seq.h header in include/linux and should be in its own file. Being tied with trace_output.c is rather awkward. Signed-off-by: Steven Rostedt --- include/linux/trace_seq.h | 2 + kernel/trace/Makefile | 1 + kernel/trace/trace.c | 24 ---- kernel/trace/trace_output.c | 268 --------------------------------------- kernel/trace/trace_output.h | 3 - kernel/trace/trace_seq.c | 303 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 306 insertions(+), 295 deletions(-) create mode 100644 kernel/trace/trace_seq.c (limited to 'include') diff --git a/include/linux/trace_seq.h b/include/linux/trace_seq.h index 136116924d8d..66ea365acf01 100644 --- a/include/linux/trace_seq.h +++ b/include/linux/trace_seq.h @@ -25,6 +25,8 @@ trace_seq_init(struct trace_seq *s) s->full = 0; } +#define MAX_MEMHEX_BYTES 8 + /* * Currently only defined when tracing is enabled. */ diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index 2611613f14f1..67d6369ddf83 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_RING_BUFFER_BENCHMARK) += ring_buffer_benchmark.o obj-$(CONFIG_TRACING) += trace.o obj-$(CONFIG_TRACING) += trace_output.o +obj-$(CONFIG_TRACING) += trace_seq.o obj-$(CONFIG_TRACING) += trace_stat.o obj-$(CONFIG_TRACING) += trace_printk.o obj-$(CONFIG_CONTEXT_SWITCH_TRACER) += trace_sched_switch.o diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 384ede311717..eeb233cbac4f 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -923,30 +923,6 @@ out: return ret; } -ssize_t trace_seq_to_user(struct trace_seq *s, char __user *ubuf, size_t cnt) -{ - int len; - int ret; - - if (!cnt) - return 0; - - if (s->len <= s->readpos) - return -EBUSY; - - len = s->len - s->readpos; - if (cnt > len) - cnt = len; - ret = copy_to_user(ubuf, s->buffer + s->readpos, cnt); - if (ret == cnt) - return -EFAULT; - - cnt -= ret; - - s->readpos += cnt; - return cnt; -} - static ssize_t trace_seq_to_buffer(struct trace_seq *s, void *buf, size_t cnt) { int len; diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c index f3dad80c20b2..b8930f79a04b 100644 --- a/kernel/trace/trace_output.c +++ b/kernel/trace/trace_output.c @@ -20,23 +20,6 @@ static struct hlist_head event_hash[EVENT_HASHSIZE] __read_mostly; static int next_event_type = __TRACE_LAST_TYPE + 1; -int trace_print_seq(struct seq_file *m, struct trace_seq *s) -{ - int len = s->len >= PAGE_SIZE ? PAGE_SIZE - 1 : s->len; - int ret; - - ret = seq_write(m, s->buffer, len); - - /* - * Only reset this buffer if we successfully wrote to the - * seq_file buffer. - */ - if (!ret) - trace_seq_init(s); - - return ret; -} - enum print_line_t trace_print_bputs_msg_only(struct trace_iterator *iter) { struct trace_seq *s = &iter->seq; @@ -85,257 +68,6 @@ enum print_line_t trace_print_printk_msg_only(struct trace_iterator *iter) return TRACE_TYPE_HANDLED; } -/** - * trace_seq_printf - sequence printing of trace information - * @s: trace sequence descriptor - * @fmt: printf format string - * - * It returns 0 if the trace oversizes the buffer's free - * space, 1 otherwise. - * - * The tracer may use either sequence operations or its own - * copy to user routines. To simplify formating of a trace - * trace_seq_printf is used to store strings into a special - * buffer (@s). Then the output may be either used by - * the sequencer or pulled into another buffer. - */ -int -trace_seq_printf(struct trace_seq *s, const char *fmt, ...) -{ - int len = (PAGE_SIZE - 1) - s->len; - va_list ap; - int ret; - - if (s->full || !len) - return 0; - - va_start(ap, fmt); - ret = vsnprintf(s->buffer + s->len, len, fmt, ap); - va_end(ap); - - /* If we can't write it all, don't bother writing anything */ - if (ret >= len) { - s->full = 1; - return 0; - } - - s->len += ret; - - return 1; -} -EXPORT_SYMBOL_GPL(trace_seq_printf); - -/** - * trace_seq_bitmask - put a list of longs as a bitmask print output - * @s: trace sequence descriptor - * @maskp: points to an array of unsigned longs that represent a bitmask - * @nmaskbits: The number of bits that are valid in @maskp - * - * It returns 0 if the trace oversizes the buffer's free - * space, 1 otherwise. - * - * Writes a ASCII representation of a bitmask string into @s. - */ -int -trace_seq_bitmask(struct trace_seq *s, const unsigned long *maskp, - int nmaskbits) -{ - int len = (PAGE_SIZE - 1) - s->len; - int ret; - - if (s->full || !len) - return 0; - - ret = bitmap_scnprintf(s->buffer, len, maskp, nmaskbits); - s->len += ret; - - return 1; -} -EXPORT_SYMBOL_GPL(trace_seq_bitmask); - -/** - * trace_seq_vprintf - sequence printing of trace information - * @s: trace sequence descriptor - * @fmt: printf format string - * - * The tracer may use either sequence operations or its own - * copy to user routines. To simplify formating of a trace - * trace_seq_printf is used to store strings into a special - * buffer (@s). Then the output may be either used by - * the sequencer or pulled into another buffer. - */ -int -trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args) -{ - int len = (PAGE_SIZE - 1) - s->len; - int ret; - - if (s->full || !len) - return 0; - - ret = vsnprintf(s->buffer + s->len, len, fmt, args); - - /* If we can't write it all, don't bother writing anything */ - if (ret >= len) { - s->full = 1; - return 0; - } - - s->len += ret; - - return len; -} -EXPORT_SYMBOL_GPL(trace_seq_vprintf); - -int trace_seq_bprintf(struct trace_seq *s, const char *fmt, const u32 *binary) -{ - int len = (PAGE_SIZE - 1) - s->len; - int ret; - - if (s->full || !len) - return 0; - - ret = bstr_printf(s->buffer + s->len, len, fmt, binary); - - /* If we can't write it all, don't bother writing anything */ - if (ret >= len) { - s->full = 1; - return 0; - } - - s->len += ret; - - return len; -} - -/** - * trace_seq_puts - trace sequence printing of simple string - * @s: trace sequence descriptor - * @str: simple string to record - * - * The tracer may use either the sequence operations or its own - * copy to user routines. This function records a simple string - * into a special buffer (@s) for later retrieval by a sequencer - * or other mechanism. - */ -int trace_seq_puts(struct trace_seq *s, const char *str) -{ - int len = strlen(str); - - if (s->full) - return 0; - - if (len > ((PAGE_SIZE - 1) - s->len)) { - s->full = 1; - return 0; - } - - memcpy(s->buffer + s->len, str, len); - s->len += len; - - return len; -} - -int trace_seq_putc(struct trace_seq *s, unsigned char c) -{ - if (s->full) - return 0; - - if (s->len >= (PAGE_SIZE - 1)) { - s->full = 1; - return 0; - } - - s->buffer[s->len++] = c; - - return 1; -} -EXPORT_SYMBOL(trace_seq_putc); - -int trace_seq_putmem(struct trace_seq *s, const void *mem, size_t len) -{ - if (s->full) - return 0; - - if (len > ((PAGE_SIZE - 1) - s->len)) { - s->full = 1; - return 0; - } - - memcpy(s->buffer + s->len, mem, len); - s->len += len; - - return len; -} - -int trace_seq_putmem_hex(struct trace_seq *s, const void *mem, size_t len) -{ - unsigned char hex[HEX_CHARS]; - const unsigned char *data = mem; - int i, j; - - if (s->full) - return 0; - -#ifdef __BIG_ENDIAN - for (i = 0, j = 0; i < len; i++) { -#else - for (i = len-1, j = 0; i >= 0; i--) { -#endif - hex[j++] = hex_asc_hi(data[i]); - hex[j++] = hex_asc_lo(data[i]); - } - hex[j++] = ' '; - - return trace_seq_putmem(s, hex, j); -} - -void *trace_seq_reserve(struct trace_seq *s, size_t len) -{ - void *ret; - - if (s->full) - return NULL; - - if (len > ((PAGE_SIZE - 1) - s->len)) { - s->full = 1; - return NULL; - } - - ret = s->buffer + s->len; - s->len += len; - - return ret; -} - -int trace_seq_path(struct trace_seq *s, const struct path *path) -{ - unsigned char *p; - - if (s->full) - return 0; - - if (s->len >= (PAGE_SIZE - 1)) { - s->full = 1; - return 0; - } - - p = d_path(path, s->buffer + s->len, PAGE_SIZE - s->len); - if (!IS_ERR(p)) { - p = mangle_path(s->buffer + s->len, p, "\n"); - if (p) { - s->len = p - s->buffer; - return 1; - } - } else { - s->buffer[s->len++] = '?'; - return 1; - } - - s->full = 1; - return 0; -} - const char * ftrace_print_flags_seq(struct trace_seq *p, const char *delim, unsigned long flags, diff --git a/kernel/trace/trace_output.h b/kernel/trace/trace_output.h index 127a9d8c8357..bf7daf2237ed 100644 --- a/kernel/trace/trace_output.h +++ b/kernel/trace/trace_output.h @@ -35,9 +35,6 @@ trace_print_lat_fmt(struct trace_seq *s, struct trace_entry *entry); extern int __unregister_ftrace_event(struct trace_event *event); extern struct rw_semaphore trace_event_sem; -#define MAX_MEMHEX_BYTES 8 -#define HEX_CHARS (MAX_MEMHEX_BYTES*2 + 1) - #define SEQ_PUT_FIELD_RET(s, x) \ do { \ if (!trace_seq_putmem(s, &(x), sizeof(x))) \ diff --git a/kernel/trace/trace_seq.c b/kernel/trace/trace_seq.c new file mode 100644 index 000000000000..5ba99c6cf834 --- /dev/null +++ b/kernel/trace/trace_seq.c @@ -0,0 +1,303 @@ +/* + * trace_seq.c + * + * Copyright (C) 2008-2014 Red Hat Inc, Steven Rostedt + * + */ +#include +#include +#include + +int trace_print_seq(struct seq_file *m, struct trace_seq *s) +{ + int len = s->len >= PAGE_SIZE ? PAGE_SIZE - 1 : s->len; + int ret; + + ret = seq_write(m, s->buffer, len); + + /* + * Only reset this buffer if we successfully wrote to the + * seq_file buffer. + */ + if (!ret) + trace_seq_init(s); + + return ret; +} + +/** + * trace_seq_printf - sequence printing of trace information + * @s: trace sequence descriptor + * @fmt: printf format string + * + * It returns 0 if the trace oversizes the buffer's free + * space, 1 otherwise. + * + * The tracer may use either sequence operations or its own + * copy to user routines. To simplify formating of a trace + * trace_seq_printf is used to store strings into a special + * buffer (@s). Then the output may be either used by + * the sequencer or pulled into another buffer. + */ +int +trace_seq_printf(struct trace_seq *s, const char *fmt, ...) +{ + int len = (PAGE_SIZE - 1) - s->len; + va_list ap; + int ret; + + if (s->full || !len) + return 0; + + va_start(ap, fmt); + ret = vsnprintf(s->buffer + s->len, len, fmt, ap); + va_end(ap); + + /* If we can't write it all, don't bother writing anything */ + if (ret >= len) { + s->full = 1; + return 0; + } + + s->len += ret; + + return 1; +} +EXPORT_SYMBOL_GPL(trace_seq_printf); + +/** + * trace_seq_bitmask - put a list of longs as a bitmask print output + * @s: trace sequence descriptor + * @maskp: points to an array of unsigned longs that represent a bitmask + * @nmaskbits: The number of bits that are valid in @maskp + * + * It returns 0 if the trace oversizes the buffer's free + * space, 1 otherwise. + * + * Writes a ASCII representation of a bitmask string into @s. + */ +int +trace_seq_bitmask(struct trace_seq *s, const unsigned long *maskp, + int nmaskbits) +{ + int len = (PAGE_SIZE - 1) - s->len; + int ret; + + if (s->full || !len) + return 0; + + ret = bitmap_scnprintf(s->buffer, len, maskp, nmaskbits); + s->len += ret; + + return 1; +} +EXPORT_SYMBOL_GPL(trace_seq_bitmask); + +/** + * trace_seq_vprintf - sequence printing of trace information + * @s: trace sequence descriptor + * @fmt: printf format string + * + * The tracer may use either sequence operations or its own + * copy to user routines. To simplify formating of a trace + * trace_seq_printf is used to store strings into a special + * buffer (@s). Then the output may be either used by + * the sequencer or pulled into another buffer. + */ +int +trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args) +{ + int len = (PAGE_SIZE - 1) - s->len; + int ret; + + if (s->full || !len) + return 0; + + ret = vsnprintf(s->buffer + s->len, len, fmt, args); + + /* If we can't write it all, don't bother writing anything */ + if (ret >= len) { + s->full = 1; + return 0; + } + + s->len += ret; + + return len; +} +EXPORT_SYMBOL_GPL(trace_seq_vprintf); + +int trace_seq_bprintf(struct trace_seq *s, const char *fmt, const u32 *binary) +{ + int len = (PAGE_SIZE - 1) - s->len; + int ret; + + if (s->full || !len) + return 0; + + ret = bstr_printf(s->buffer + s->len, len, fmt, binary); + + /* If we can't write it all, don't bother writing anything */ + if (ret >= len) { + s->full = 1; + return 0; + } + + s->len += ret; + + return len; +} + +/** + * trace_seq_puts - trace sequence printing of simple string + * @s: trace sequence descriptor + * @str: simple string to record + * + * The tracer may use either the sequence operations or its own + * copy to user routines. This function records a simple string + * into a special buffer (@s) for later retrieval by a sequencer + * or other mechanism. + */ +int trace_seq_puts(struct trace_seq *s, const char *str) +{ + int len = strlen(str); + + if (s->full) + return 0; + + if (len > ((PAGE_SIZE - 1) - s->len)) { + s->full = 1; + return 0; + } + + memcpy(s->buffer + s->len, str, len); + s->len += len; + + return len; +} + +int trace_seq_putc(struct trace_seq *s, unsigned char c) +{ + if (s->full) + return 0; + + if (s->len >= (PAGE_SIZE - 1)) { + s->full = 1; + return 0; + } + + s->buffer[s->len++] = c; + + return 1; +} +EXPORT_SYMBOL(trace_seq_putc); + +int trace_seq_putmem(struct trace_seq *s, const void *mem, size_t len) +{ + if (s->full) + return 0; + + if (len > ((PAGE_SIZE - 1) - s->len)) { + s->full = 1; + return 0; + } + + memcpy(s->buffer + s->len, mem, len); + s->len += len; + + return len; +} + +#define HEX_CHARS (MAX_MEMHEX_BYTES*2 + 1) + +int trace_seq_putmem_hex(struct trace_seq *s, const void *mem, size_t len) +{ + unsigned char hex[HEX_CHARS]; + const unsigned char *data = mem; + int i, j; + + if (s->full) + return 0; + +#ifdef __BIG_ENDIAN + for (i = 0, j = 0; i < len; i++) { +#else + for (i = len-1, j = 0; i >= 0; i--) { +#endif + hex[j++] = hex_asc_hi(data[i]); + hex[j++] = hex_asc_lo(data[i]); + } + hex[j++] = ' '; + + return trace_seq_putmem(s, hex, j); +} + +void *trace_seq_reserve(struct trace_seq *s, size_t len) +{ + void *ret; + + if (s->full) + return NULL; + + if (len > ((PAGE_SIZE - 1) - s->len)) { + s->full = 1; + return NULL; + } + + ret = s->buffer + s->len; + s->len += len; + + return ret; +} + +int trace_seq_path(struct trace_seq *s, const struct path *path) +{ + unsigned char *p; + + if (s->full) + return 0; + + if (s->len >= (PAGE_SIZE - 1)) { + s->full = 1; + return 0; + } + + p = d_path(path, s->buffer + s->len, PAGE_SIZE - s->len); + if (!IS_ERR(p)) { + p = mangle_path(s->buffer + s->len, p, "\n"); + if (p) { + s->len = p - s->buffer; + return 1; + } + } else { + s->buffer[s->len++] = '?'; + return 1; + } + + s->full = 1; + return 0; +} + +ssize_t trace_seq_to_user(struct trace_seq *s, char __user *ubuf, size_t cnt) +{ + int len; + int ret; + + if (!cnt) + return 0; + + if (s->len <= s->readpos) + return -EBUSY; + + len = s->len - s->readpos; + if (cnt > len) + cnt = len; + ret = copy_to_user(ubuf, s->buffer + s->readpos, cnt); + if (ret == cnt) + return -EFAULT; + + cnt -= ret; + + s->readpos += cnt; + return cnt; +} -- cgit v1.2.3 From 36aabfff50b6a03bcfd2c3cfbd7b83eb0a9ce0c1 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Fri, 20 Jun 2014 17:38:01 -0400 Subject: tracing: Clean up trace_seq.c For using trace_seq_*() functions in NMI context, I posted a patch to move it to the lib/ directory. This caused Andrew Morton to take a look at the code. He went through and gave a lot of comments about missing kernel doc, inconsistent types for the save variable, mix match of EXPORT_SYMBOL_GPL() and EXPORT_SYMBOL() as well as missing EXPORT_SYMBOL*()s. There were a few comments about the way variables were being compared (int vs uint). All these were good review comments and should be implemented regardless of if trace_seq.c should be moved to lib/ or not. Signed-off-by: Steven Rostedt --- include/linux/trace_seq.h | 20 ++--- kernel/trace/trace_seq.c | 207 +++++++++++++++++++++++++++++++++++++++------- 2 files changed, 185 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/include/linux/trace_seq.h b/include/linux/trace_seq.h index 66ea365acf01..1f05317f51c4 100644 --- a/include/linux/trace_seq.h +++ b/include/linux/trace_seq.h @@ -38,14 +38,14 @@ int trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args); extern int trace_seq_bprintf(struct trace_seq *s, const char *fmt, const u32 *binary); extern int trace_print_seq(struct seq_file *m, struct trace_seq *s); -extern ssize_t trace_seq_to_user(struct trace_seq *s, char __user *ubuf, - size_t cnt); +extern int trace_seq_to_user(struct trace_seq *s, char __user *ubuf, + int cnt); extern int trace_seq_puts(struct trace_seq *s, const char *str); extern int trace_seq_putc(struct trace_seq *s, unsigned char c); -extern int trace_seq_putmem(struct trace_seq *s, const void *mem, size_t len); +extern int trace_seq_putmem(struct trace_seq *s, const void *mem, unsigned int len); extern int trace_seq_putmem_hex(struct trace_seq *s, const void *mem, - size_t len); -extern void *trace_seq_reserve(struct trace_seq *s, size_t len); + unsigned int len); +extern void *trace_seq_reserve(struct trace_seq *s, unsigned int len); extern int trace_seq_path(struct trace_seq *s, const struct path *path); extern int trace_seq_bitmask(struct trace_seq *s, const unsigned long *maskp, @@ -73,8 +73,8 @@ static inline int trace_print_seq(struct seq_file *m, struct trace_seq *s) { return 0; } -static inline ssize_t trace_seq_to_user(struct trace_seq *s, char __user *ubuf, - size_t cnt) +static inline int trace_seq_to_user(struct trace_seq *s, char __user *ubuf, + int cnt) { return 0; } @@ -87,16 +87,16 @@ static inline int trace_seq_putc(struct trace_seq *s, unsigned char c) return 0; } static inline int -trace_seq_putmem(struct trace_seq *s, const void *mem, size_t len) +trace_seq_putmem(struct trace_seq *s, const void *mem, unsigned int len) { return 0; } static inline int trace_seq_putmem_hex(struct trace_seq *s, const void *mem, - size_t len) + unsigned int len) { return 0; } -static inline void *trace_seq_reserve(struct trace_seq *s, size_t len) +static inline void *trace_seq_reserve(struct trace_seq *s, unsigned int len) { return NULL; } diff --git a/kernel/trace/trace_seq.c b/kernel/trace/trace_seq.c index 5ba99c6cf834..0fabca773e51 100644 --- a/kernel/trace/trace_seq.c +++ b/kernel/trace/trace_seq.c @@ -3,21 +3,55 @@ * * Copyright (C) 2008-2014 Red Hat Inc, Steven Rostedt * + * The trace_seq is a handy tool that allows you to pass a descriptor around + * to a buffer that other functions can write to. It is similar to the + * seq_file functionality but has some differences. + * + * To use it, the trace_seq must be initialized with trace_seq_init(). + * This will set up the counters within the descriptor. You can call + * trace_seq_init() more than once to reset the trace_seq to start + * from scratch. + * + * The buffer size is currently PAGE_SIZE, although it may become dynamic + * in the future. + * + * A write to the buffer will either succed or fail. That is, unlike + * sprintf() there will not be a partial write (well it may write into + * the buffer but it wont update the pointers). This allows users to + * try to write something into the trace_seq buffer and if it fails + * they can flush it and try again. + * */ #include #include #include +/* How much buffer is left on the trace_seq? */ +#define TRACE_SEQ_BUF_LEFT(s) ((PAGE_SIZE - 1) - (s)->len) + +/* How much buffer is written? */ +#define TRACE_SEQ_BUF_USED(s) min((s)->len, (unsigned int)(PAGE_SIZE - 1)) + +/** + * trace_print_seq - move the contents of trace_seq into a seq_file + * @m: the seq_file descriptor that is the destination + * @s: the trace_seq descriptor that is the source. + * + * Returns 0 on success and non zero on error. If it succeeds to + * write to the seq_file it will reset the trace_seq, otherwise + * it does not modify the trace_seq to let the caller try again. + */ int trace_print_seq(struct seq_file *m, struct trace_seq *s) { - int len = s->len >= PAGE_SIZE ? PAGE_SIZE - 1 : s->len; + unsigned int len = TRACE_SEQ_BUF_USED(s); int ret; ret = seq_write(m, s->buffer, len); /* * Only reset this buffer if we successfully wrote to the - * seq_file buffer. + * seq_file buffer. This lets the caller try again or + * do something else with the contents. */ if (!ret) trace_seq_init(s); @@ -30,19 +64,20 @@ int trace_print_seq(struct seq_file *m, struct trace_seq *s) * @s: trace sequence descriptor * @fmt: printf format string * - * It returns 0 if the trace oversizes the buffer's free - * space, 1 otherwise. - * * The tracer may use either sequence operations or its own * copy to user routines. To simplify formating of a trace - * trace_seq_printf is used to store strings into a special + * trace_seq_printf() is used to store strings into a special * buffer (@s). Then the output may be either used by * the sequencer or pulled into another buffer. + * + * Returns 1 if we successfully written all the contents to + * the buffer. + * Returns 0 if we the length to write is bigger than the + * reserved buffer space. In this case, nothing gets written. */ -int -trace_seq_printf(struct trace_seq *s, const char *fmt, ...) +int trace_seq_printf(struct trace_seq *s, const char *fmt, ...) { - int len = (PAGE_SIZE - 1) - s->len; + unsigned int len = TRACE_SEQ_BUF_LEFT(s); va_list ap; int ret; @@ -66,21 +101,22 @@ trace_seq_printf(struct trace_seq *s, const char *fmt, ...) EXPORT_SYMBOL_GPL(trace_seq_printf); /** - * trace_seq_bitmask - put a list of longs as a bitmask print output + * trace_seq_bitmask - write a bitmask array in its ASCII representation * @s: trace sequence descriptor * @maskp: points to an array of unsigned longs that represent a bitmask * @nmaskbits: The number of bits that are valid in @maskp * - * It returns 0 if the trace oversizes the buffer's free - * space, 1 otherwise. - * * Writes a ASCII representation of a bitmask string into @s. + * + * Returns 1 if we successfully written all the contents to + * the buffer. + * Returns 0 if we the length to write is bigger than the + * reserved buffer space. In this case, nothing gets written. */ -int -trace_seq_bitmask(struct trace_seq *s, const unsigned long *maskp, - int nmaskbits) +int trace_seq_bitmask(struct trace_seq *s, const unsigned long *maskp, + int nmaskbits) { - int len = (PAGE_SIZE - 1) - s->len; + unsigned int len = TRACE_SEQ_BUF_LEFT(s); int ret; if (s->full || !len) @@ -103,11 +139,12 @@ EXPORT_SYMBOL_GPL(trace_seq_bitmask); * trace_seq_printf is used to store strings into a special * buffer (@s). Then the output may be either used by * the sequencer or pulled into another buffer. + * + * Returns how much it wrote to the buffer. */ -int -trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args) +int trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args) { - int len = (PAGE_SIZE - 1) - s->len; + unsigned int len = TRACE_SEQ_BUF_LEFT(s); int ret; if (s->full || !len) @@ -127,9 +164,26 @@ trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args) } EXPORT_SYMBOL_GPL(trace_seq_vprintf); +/** + * trace_seq_bprintf - Write the printf string from binary arguments + * @s: trace sequence descriptor + * @fmt: The format string for the @binary arguments + * @binary: The binary arguments for @fmt. + * + * When recording in a fast path, a printf may be recorded with just + * saving the format and the arguments as they were passed to the + * function, instead of wasting cycles converting the arguments into + * ASCII characters. Instead, the arguments are saved in a 32 bit + * word array that is defined by the format string constraints. + * + * This function will take the format and the binary array and finish + * the conversion into the ASCII string within the buffer. + * + * Returns how much it wrote to the buffer. + */ int trace_seq_bprintf(struct trace_seq *s, const char *fmt, const u32 *binary) { - int len = (PAGE_SIZE - 1) - s->len; + unsigned int len = TRACE_SEQ_BUF_LEFT(s); int ret; if (s->full || !len) @@ -147,6 +201,7 @@ int trace_seq_bprintf(struct trace_seq *s, const char *fmt, const u32 *binary) return len; } +EXPORT_SYMBOL_GPL(trace_seq_bprintf); /** * trace_seq_puts - trace sequence printing of simple string @@ -157,15 +212,17 @@ int trace_seq_bprintf(struct trace_seq *s, const char *fmt, const u32 *binary) * copy to user routines. This function records a simple string * into a special buffer (@s) for later retrieval by a sequencer * or other mechanism. + * + * Returns how much it wrote to the buffer. */ int trace_seq_puts(struct trace_seq *s, const char *str) { - int len = strlen(str); + unsigned int len = strlen(str); if (s->full) return 0; - if (len > ((PAGE_SIZE - 1) - s->len)) { + if (len > TRACE_SEQ_BUF_LEFT(s)) { s->full = 1; return 0; } @@ -175,13 +232,26 @@ int trace_seq_puts(struct trace_seq *s, const char *str) return len; } +EXPORT_SYMBOL_GPL(trace_seq_puts); +/** + * trace_seq_putc - trace sequence printing of simple character + * @s: trace sequence descriptor + * @c: simple character to record + * + * The tracer may use either the sequence operations or its own + * copy to user routines. This function records a simple charater + * into a special buffer (@s) for later retrieval by a sequencer + * or other mechanism. + * + * Returns how much it wrote to the buffer. + */ int trace_seq_putc(struct trace_seq *s, unsigned char c) { if (s->full) return 0; - if (s->len >= (PAGE_SIZE - 1)) { + if (TRACE_SEQ_BUF_LEFT(s) < 1) { s->full = 1; return 0; } @@ -190,14 +260,26 @@ int trace_seq_putc(struct trace_seq *s, unsigned char c) return 1; } -EXPORT_SYMBOL(trace_seq_putc); +EXPORT_SYMBOL_GPL(trace_seq_putc); -int trace_seq_putmem(struct trace_seq *s, const void *mem, size_t len) +/** + * trace_seq_putmem - write raw data into the trace_seq buffer + * @s: trace sequence descriptor + * @mem: The raw memory to copy into the buffer + * @len: The length of the raw memory to copy (in bytes) + * + * There may be cases where raw memory needs to be written into the + * buffer and a strcpy() would not work. Using this function allows + * for such cases. + * + * Returns how much it wrote to the buffer. + */ +int trace_seq_putmem(struct trace_seq *s, const void *mem, unsigned int len) { if (s->full) return 0; - if (len > ((PAGE_SIZE - 1) - s->len)) { + if (len > TRACE_SEQ_BUF_LEFT(s)) { s->full = 1; return 0; } @@ -207,10 +289,24 @@ int trace_seq_putmem(struct trace_seq *s, const void *mem, size_t len) return len; } +EXPORT_SYMBOL_GPL(trace_seq_putmem); #define HEX_CHARS (MAX_MEMHEX_BYTES*2 + 1) -int trace_seq_putmem_hex(struct trace_seq *s, const void *mem, size_t len) +/** + * trace_seq_putmem_hex - write raw memory into the buffer in ASCII hex + * @s: trace sequence descriptor + * @mem: The raw memory to write its hex ASCII representation of + * @len: The length of the raw memory to copy (in bytes) + * + * This is similar to trace_seq_putmem() except instead of just copying the + * raw memory into the buffer it writes its ASCII representation of it + * in hex characters. + * + * Returns how much it wrote to the buffer. + */ +int trace_seq_putmem_hex(struct trace_seq *s, const void *mem, + unsigned int len) { unsigned char hex[HEX_CHARS]; const unsigned char *data = mem; @@ -231,15 +327,27 @@ int trace_seq_putmem_hex(struct trace_seq *s, const void *mem, size_t len) return trace_seq_putmem(s, hex, j); } +EXPORT_SYMBOL_GPL(trace_seq_putmem_hex); -void *trace_seq_reserve(struct trace_seq *s, size_t len) +/** + * trace_seq_reserve - reserve space on the sequence buffer + * @s: trace sequence descriptor + * @len: The amount to reserver. + * + * If for some reason there is a need to save some space on the + * buffer to fill in later, this function is used for that purpose. + * The given length will be reserved and the pointer to that + * location on the buffer is returned, unless there is not enough + * buffer left to hold the given length then NULL is returned. + */ +void *trace_seq_reserve(struct trace_seq *s, unsigned int len) { void *ret; if (s->full) return NULL; - if (len > ((PAGE_SIZE - 1) - s->len)) { + if (len > TRACE_SEQ_BUF_LEFT(s)) { s->full = 1; return NULL; } @@ -249,7 +357,20 @@ void *trace_seq_reserve(struct trace_seq *s, size_t len) return ret; } +EXPORT_SYMBOL_GPL(trace_seq_reserve); +/** + * trace_seq_path - copy a path into the sequence buffer + * @s: trace sequence descriptor + * @path: path to write into the sequence buffer. + * + * Write a path name into the sequence buffer. + * + * Returns 1 if we successfully written all the contents to + * the buffer. + * Returns 0 if we the length to write is bigger than the + * reserved buffer space. In this case, nothing gets written. + */ int trace_seq_path(struct trace_seq *s, const struct path *path) { unsigned char *p; @@ -257,7 +378,7 @@ int trace_seq_path(struct trace_seq *s, const struct path *path) if (s->full) return 0; - if (s->len >= (PAGE_SIZE - 1)) { + if (TRACE_SEQ_BUF_LEFT(s) < 1) { s->full = 1; return 0; } @@ -277,8 +398,29 @@ int trace_seq_path(struct trace_seq *s, const struct path *path) s->full = 1; return 0; } +EXPORT_SYMBOL_GPL(trace_seq_path); -ssize_t trace_seq_to_user(struct trace_seq *s, char __user *ubuf, size_t cnt) +/** + * trace_seq_to_user - copy the squence buffer to user space + * @s: trace sequence descriptor + * @ubuf: The userspace memory location to copy to + * @cnt: The amount to copy + * + * Copies the sequence buffer into the userspace memory pointed to + * by @ubuf. It starts from the last read position (@s->readpos) + * and writes up to @cnt characters or till it reaches the end of + * the content in the buffer (@s->len), which ever comes first. + * + * On success, it returns a positive number of the number of bytes + * it copied. + * + * On failure it returns -EBUSY if all of the content in the + * sequence has been already read, which includes nothing in the + * sequenc (@s->len == @s->readpos). + * + * Returns -EFAULT if the copy to userspace fails. + */ +int trace_seq_to_user(struct trace_seq *s, char __user *ubuf, int cnt) { int len; int ret; @@ -301,3 +443,4 @@ ssize_t trace_seq_to_user(struct trace_seq *s, char __user *ubuf, size_t cnt) s->readpos += cnt; return cnt; } +EXPORT_SYMBOL_GPL(trace_seq_to_user); -- cgit v1.2.3 From 6d2289f3faa71dcc5bba15c7aeba4f31c185b6df Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Fri, 20 Jun 2014 23:31:26 -0400 Subject: tracing: Make trace_seq_putmem_hex() more robust Currently trace_seq_putmem_hex() can only take as a parameter a pointer to something that is 8 bytes or less, otherwise it will overflow the buffer. This is protected by a macro that encompasses the call to trace_seq_putmem_hex() that has a BUILD_BUG_ON() for the variable before it is passed in. This is not very robust and if trace_seq_putmem_hex() ever gets used outside that macro it will cause issues. Instead of only being able to produce a hex output of memory that is for a single word, change it to be more robust and allow any size input. Signed-off-by: Steven Rostedt --- include/linux/trace_seq.h | 2 -- kernel/trace/trace_output.h | 1 - kernel/trace/trace_seq.c | 26 +++++++++++++++++++------- 3 files changed, 19 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/trace_seq.h b/include/linux/trace_seq.h index 1f05317f51c4..8283762ab7ef 100644 --- a/include/linux/trace_seq.h +++ b/include/linux/trace_seq.h @@ -25,8 +25,6 @@ trace_seq_init(struct trace_seq *s) s->full = 0; } -#define MAX_MEMHEX_BYTES 8 - /* * Currently only defined when tracing is enabled. */ diff --git a/kernel/trace/trace_output.h b/kernel/trace/trace_output.h index bf7daf2237ed..80b25b585a70 100644 --- a/kernel/trace/trace_output.h +++ b/kernel/trace/trace_output.h @@ -43,7 +43,6 @@ do { \ #define SEQ_PUT_HEX_FIELD_RET(s, x) \ do { \ - BUILD_BUG_ON(sizeof(x) > MAX_MEMHEX_BYTES); \ if (!trace_seq_putmem_hex(s, &(x), sizeof(x))) \ return TRACE_TYPE_PARTIAL_LINE; \ } while (0) diff --git a/kernel/trace/trace_seq.c b/kernel/trace/trace_seq.c index 0fabca773e51..88c0f80f0a1f 100644 --- a/kernel/trace/trace_seq.c +++ b/kernel/trace/trace_seq.c @@ -291,6 +291,7 @@ int trace_seq_putmem(struct trace_seq *s, const void *mem, unsigned int len) } EXPORT_SYMBOL_GPL(trace_seq_putmem); +#define MAX_MEMHEX_BYTES 8U #define HEX_CHARS (MAX_MEMHEX_BYTES*2 + 1) /** @@ -310,22 +311,33 @@ int trace_seq_putmem_hex(struct trace_seq *s, const void *mem, { unsigned char hex[HEX_CHARS]; const unsigned char *data = mem; + unsigned int start_len; int i, j; + int cnt = 0; if (s->full) return 0; + while (len) { + start_len = min(len, HEX_CHARS - 1); #ifdef __BIG_ENDIAN - for (i = 0, j = 0; i < len; i++) { + for (i = 0, j = 0; i < start_len; i++) { #else - for (i = len-1, j = 0; i >= 0; i--) { + for (i = start_len-1, j = 0; i >= 0; i--) { #endif - hex[j++] = hex_asc_hi(data[i]); - hex[j++] = hex_asc_lo(data[i]); - } - hex[j++] = ' '; + hex[j++] = hex_asc_hi(data[i]); + hex[j++] = hex_asc_lo(data[i]); + } + if (WARN_ON_ONCE(j == 0 || j/2 > len)) + break; + + /* j increments twice per loop */ + len -= j / 2; + hex[j++] = ' '; - return trace_seq_putmem(s, hex, j); + cnt += trace_seq_putmem(s, hex, j); + } + return cnt; } EXPORT_SYMBOL_GPL(trace_seq_putmem_hex); -- cgit v1.2.3 From 9096032fbcdcdb80b76f1046346499e20417988e Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Mon, 23 Jun 2014 16:42:07 -0400 Subject: tracing: Remove trace_seq_reserve() trace_seq_reserve() has no users in the kernel, it just wastes space. Remove it. Cc: Eduard - Gabriel Munteanu Signed-off-by: Steven Rostedt --- include/linux/trace_seq.h | 5 ----- kernel/trace/trace_seq.c | 30 ------------------------------ 2 files changed, 35 deletions(-) (limited to 'include') diff --git a/include/linux/trace_seq.h b/include/linux/trace_seq.h index 8283762ab7ef..dd85753e1bb0 100644 --- a/include/linux/trace_seq.h +++ b/include/linux/trace_seq.h @@ -43,7 +43,6 @@ extern int trace_seq_putc(struct trace_seq *s, unsigned char c); extern int trace_seq_putmem(struct trace_seq *s, const void *mem, unsigned int len); extern int trace_seq_putmem_hex(struct trace_seq *s, const void *mem, unsigned int len); -extern void *trace_seq_reserve(struct trace_seq *s, unsigned int len); extern int trace_seq_path(struct trace_seq *s, const struct path *path); extern int trace_seq_bitmask(struct trace_seq *s, const unsigned long *maskp, @@ -94,10 +93,6 @@ static inline int trace_seq_putmem_hex(struct trace_seq *s, const void *mem, { return 0; } -static inline void *trace_seq_reserve(struct trace_seq *s, unsigned int len) -{ - return NULL; -} static inline int trace_seq_path(struct trace_seq *s, const struct path *path) { return 0; diff --git a/kernel/trace/trace_seq.c b/kernel/trace/trace_seq.c index 88c0f80f0a1f..1f24ed99dca2 100644 --- a/kernel/trace/trace_seq.c +++ b/kernel/trace/trace_seq.c @@ -341,36 +341,6 @@ int trace_seq_putmem_hex(struct trace_seq *s, const void *mem, } EXPORT_SYMBOL_GPL(trace_seq_putmem_hex); -/** - * trace_seq_reserve - reserve space on the sequence buffer - * @s: trace sequence descriptor - * @len: The amount to reserver. - * - * If for some reason there is a need to save some space on the - * buffer to fill in later, this function is used for that purpose. - * The given length will be reserved and the pointer to that - * location on the buffer is returned, unless there is not enough - * buffer left to hold the given length then NULL is returned. - */ -void *trace_seq_reserve(struct trace_seq *s, unsigned int len) -{ - void *ret; - - if (s->full) - return NULL; - - if (len > TRACE_SEQ_BUF_LEFT(s)) { - s->full = 1; - return NULL; - } - - ret = s->buffer + s->len; - s->len += len; - - return ret; -} -EXPORT_SYMBOL_GPL(trace_seq_reserve); - /** * trace_seq_path - copy a path into the sequence buffer * @s: trace sequence descriptor -- cgit v1.2.3 From 7b039cb4c5a90d8ea576b17e096f7334457aeb57 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Thu, 26 Jun 2014 09:42:41 -0400 Subject: tracing: Add trace_seq_buffer_ptr() helper function There's several locations in the kernel that open code the calculation of the next location in the trace_seq buffer. This is usually done with p->buffer + p->len Instead of having this open coded, supply a helper function in the header to do it for them. This function is called trace_seq_buffer_ptr(). Link: http://lkml.kernel.org/p/20140626220129.452783019@goodmis.org Acked-by: Paolo Bonzini Signed-off-by: Steven Rostedt --- arch/x86/kvm/mmutrace.h | 2 +- drivers/scsi/scsi_trace.c | 16 ++++++++-------- include/linux/trace_seq.h | 15 +++++++++++++++ kernel/trace/trace_output.c | 14 +++++++------- 4 files changed, 31 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/arch/x86/kvm/mmutrace.h b/arch/x86/kvm/mmutrace.h index 9d2e0ffcb190..2e5652b62fd6 100644 --- a/arch/x86/kvm/mmutrace.h +++ b/arch/x86/kvm/mmutrace.h @@ -22,7 +22,7 @@ __entry->unsync = sp->unsync; #define KVM_MMU_PAGE_PRINTK() ({ \ - const char *ret = p->buffer + p->len; \ + const char *ret = trace_seq_buffer_ptr(p); \ static const char *access_str[] = { \ "---", "--x", "w--", "w-x", "-u-", "-ux", "wu-", "wux" \ }; \ diff --git a/drivers/scsi/scsi_trace.c b/drivers/scsi/scsi_trace.c index 2bea4f0b684a..503594e5f76d 100644 --- a/drivers/scsi/scsi_trace.c +++ b/drivers/scsi/scsi_trace.c @@ -28,7 +28,7 @@ scsi_trace_misc(struct trace_seq *, unsigned char *, int); static const char * scsi_trace_rw6(struct trace_seq *p, unsigned char *cdb, int len) { - const char *ret = p->buffer + p->len; + const char *ret = trace_seq_buffer_ptr(p); sector_t lba = 0, txlen = 0; lba |= ((cdb[1] & 0x1F) << 16); @@ -46,7 +46,7 @@ scsi_trace_rw6(struct trace_seq *p, unsigned char *cdb, int len) static const char * scsi_trace_rw10(struct trace_seq *p, unsigned char *cdb, int len) { - const char *ret = p->buffer + p->len; + const char *ret = trace_seq_buffer_ptr(p); sector_t lba = 0, txlen = 0; lba |= (cdb[2] << 24); @@ -71,7 +71,7 @@ scsi_trace_rw10(struct trace_seq *p, unsigned char *cdb, int len) static const char * scsi_trace_rw12(struct trace_seq *p, unsigned char *cdb, int len) { - const char *ret = p->buffer + p->len; + const char *ret = trace_seq_buffer_ptr(p); sector_t lba = 0, txlen = 0; lba |= (cdb[2] << 24); @@ -94,7 +94,7 @@ scsi_trace_rw12(struct trace_seq *p, unsigned char *cdb, int len) static const char * scsi_trace_rw16(struct trace_seq *p, unsigned char *cdb, int len) { - const char *ret = p->buffer + p->len; + const char *ret = trace_seq_buffer_ptr(p); sector_t lba = 0, txlen = 0; lba |= ((u64)cdb[2] << 56); @@ -125,7 +125,7 @@ scsi_trace_rw16(struct trace_seq *p, unsigned char *cdb, int len) static const char * scsi_trace_rw32(struct trace_seq *p, unsigned char *cdb, int len) { - const char *ret = p->buffer + p->len, *cmd; + const char *ret = trace_seq_buffer_ptr(p), *cmd; sector_t lba = 0, txlen = 0; u32 ei_lbrt = 0; @@ -180,7 +180,7 @@ out: static const char * scsi_trace_unmap(struct trace_seq *p, unsigned char *cdb, int len) { - const char *ret = p->buffer + p->len; + const char *ret = trace_seq_buffer_ptr(p); unsigned int regions = cdb[7] << 8 | cdb[8]; trace_seq_printf(p, "regions=%u", (regions - 8) / 16); @@ -192,7 +192,7 @@ scsi_trace_unmap(struct trace_seq *p, unsigned char *cdb, int len) static const char * scsi_trace_service_action_in(struct trace_seq *p, unsigned char *cdb, int len) { - const char *ret = p->buffer + p->len, *cmd; + const char *ret = trace_seq_buffer_ptr(p), *cmd; sector_t lba = 0; u32 alloc_len = 0; @@ -247,7 +247,7 @@ scsi_trace_varlen(struct trace_seq *p, unsigned char *cdb, int len) static const char * scsi_trace_misc(struct trace_seq *p, unsigned char *cdb, int len) { - const char *ret = p->buffer + p->len; + const char *ret = trace_seq_buffer_ptr(p); trace_seq_printf(p, "-"); trace_seq_putc(p, 0); diff --git a/include/linux/trace_seq.h b/include/linux/trace_seq.h index dd85753e1bb0..ea6c9dea79e3 100644 --- a/include/linux/trace_seq.h +++ b/include/linux/trace_seq.h @@ -25,6 +25,21 @@ trace_seq_init(struct trace_seq *s) s->full = 0; } +/** + * trace_seq_buffer_ptr - return pointer to next location in buffer + * @s: trace sequence descriptor + * + * Returns the pointer to the buffer where the next write to + * the buffer will happen. This is useful to save the location + * that is about to be written to and then return the result + * of that write. + */ +static inline unsigned char * +trace_seq_buffer_ptr(struct trace_seq *s) +{ + return s->buffer + s->len; +} + /* * Currently only defined when tracing is enabled. */ diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c index b8930f79a04b..c6977d5a9b12 100644 --- a/kernel/trace/trace_output.c +++ b/kernel/trace/trace_output.c @@ -75,7 +75,7 @@ ftrace_print_flags_seq(struct trace_seq *p, const char *delim, { unsigned long mask; const char *str; - const char *ret = p->buffer + p->len; + const char *ret = trace_seq_buffer_ptr(p); int i, first = 1; for (i = 0; flag_array[i].name && flags; i++) { @@ -111,7 +111,7 @@ ftrace_print_symbols_seq(struct trace_seq *p, unsigned long val, const struct trace_print_flags *symbol_array) { int i; - const char *ret = p->buffer + p->len; + const char *ret = trace_seq_buffer_ptr(p); for (i = 0; symbol_array[i].name; i++) { @@ -122,7 +122,7 @@ ftrace_print_symbols_seq(struct trace_seq *p, unsigned long val, break; } - if (ret == (const char *)(p->buffer + p->len)) + if (ret == (const char *)(trace_seq_buffer_ptr(p))) trace_seq_printf(p, "0x%lx", val); trace_seq_putc(p, 0); @@ -137,7 +137,7 @@ ftrace_print_symbols_seq_u64(struct trace_seq *p, unsigned long long val, const struct trace_print_flags_u64 *symbol_array) { int i; - const char *ret = p->buffer + p->len; + const char *ret = trace_seq_buffer_ptr(p); for (i = 0; symbol_array[i].name; i++) { @@ -148,7 +148,7 @@ ftrace_print_symbols_seq_u64(struct trace_seq *p, unsigned long long val, break; } - if (ret == (const char *)(p->buffer + p->len)) + if (ret == (const char *)(trace_seq_buffer_ptr(p))) trace_seq_printf(p, "0x%llx", val); trace_seq_putc(p, 0); @@ -162,7 +162,7 @@ const char * ftrace_print_bitmask_seq(struct trace_seq *p, void *bitmask_ptr, unsigned int bitmask_size) { - const char *ret = p->buffer + p->len; + const char *ret = trace_seq_buffer_ptr(p); trace_seq_bitmask(p, bitmask_ptr, bitmask_size * 8); trace_seq_putc(p, 0); @@ -175,7 +175,7 @@ const char * ftrace_print_hex_seq(struct trace_seq *p, const unsigned char *buf, int buf_len) { int i; - const char *ret = p->buffer + p->len; + const char *ret = trace_seq_buffer_ptr(p); for (i = 0; i < buf_len; i++) trace_seq_printf(p, "%s%2.2x", i == 0 ? "" : " ", buf[i]); -- cgit v1.2.3 From 780db2071ac4d167ee4154ad9c96088f1bba044b Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 1 Jul 2014 10:31:13 -0600 Subject: blk-mq: decouble blk-mq freezing from generic bypassing blk_mq freezing is entangled with generic bypassing which bypasses blkcg and io scheduler and lets IO requests fall through the block layer to the drivers in FIFO order. This allows forward progress on IOs with the advanced features disabled so that those features can be configured or altered without worrying about stalling IO which may lead to deadlock through memory allocation. However, generic bypassing doesn't quite fit blk-mq. blk-mq currently doesn't make use of blkcg or ioscheds and it maps bypssing to freezing, which blocks request processing and drains all the in-flight ones. This causes problems as bypassing assumes that request processing is online. blk-mq works around this by conditionally allowing request processing for the problem case - during queue initialization. Another weirdity is that except for during queue cleanup, bypassing started on the generic side prevents blk-mq from processing new requests but doesn't drain the in-flight ones. This shouldn't break anything but again highlights that something isn't quite right here. The root cause is conflating blk-mq freezing and generic bypassing which are two different mechanisms. The only intersecting purpose that they serve is during queue cleanup. Let's properly separate blk-mq freezing from generic bypassing and simply use it where necessary. * request_queue->mq_freeze_depth is added and blk_mq_[un]freeze_queue() now operate on this counter instead of ->bypass_depth. The replacement for QUEUE_FLAG_BYPASS isn't added but the counter is tested directly. This will be further updated by later changes. * blk_mq_drain_queue() is dropped and "__" prefix is dropped from blk_mq_freeze_queue(). Queue cleanup path now calls blk_mq_freeze_queue() directly. * blk_queue_enter()'s fast path condition is simplified to simply check @q->mq_freeze_depth. Previously, the condition was !blk_queue_dying(q) && (!blk_queue_bypass(q) || !blk_queue_init_done(q)) mq_freeze_depth is incremented right after dying is set and blk_queue_init_done() exception isn't necessary as blk-mq doesn't start frozen, which only leaves the blk_queue_bypass() test which can be replaced by @q->mq_freeze_depth test. This change simplifies the code and reduces confusion in the area. Signed-off-by: Tejun Heo Cc: Jens Axboe Cc: Nicholas A. Bellinger Signed-off-by: Jens Axboe --- block/blk-core.c | 2 +- block/blk-mq.c | 17 ++++++----------- block/blk-mq.h | 2 +- include/linux/blkdev.h | 1 + 4 files changed, 9 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index 0d0bdd65b2d7..c359d72e9d76 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -514,7 +514,7 @@ void blk_cleanup_queue(struct request_queue *q) * prevent that q->request_fn() gets invoked after draining finished. */ if (q->mq_ops) { - blk_mq_drain_queue(q); + blk_mq_freeze_queue(q); spin_lock_irq(lock); } else { spin_lock_irq(lock); diff --git a/block/blk-mq.c b/block/blk-mq.c index f4bdddd7ed99..1e324a123d40 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -84,15 +84,14 @@ static int blk_mq_queue_enter(struct request_queue *q) smp_mb(); /* we have problems freezing the queue if it's initializing */ - if (!blk_queue_dying(q) && - (!blk_queue_bypass(q) || !blk_queue_init_done(q))) + if (!q->mq_freeze_depth) return 0; __percpu_counter_add(&q->mq_usage_counter, -1, 1000000); spin_lock_irq(q->queue_lock); ret = wait_event_interruptible_lock_irq(q->mq_freeze_wq, - !blk_queue_bypass(q) || blk_queue_dying(q), + !q->mq_freeze_depth || blk_queue_dying(q), *q->queue_lock); /* inc usage with lock hold to avoid freeze_queue runs here */ if (!ret && !blk_queue_dying(q)) @@ -129,11 +128,10 @@ void blk_mq_drain_queue(struct request_queue *q) * Guarantee no request is in use, so we can change any data structure of * the queue afterward. */ -static void blk_mq_freeze_queue(struct request_queue *q) +void blk_mq_freeze_queue(struct request_queue *q) { spin_lock_irq(q->queue_lock); - q->bypass_depth++; - queue_flag_set(QUEUE_FLAG_BYPASS, q); + q->mq_freeze_depth++; spin_unlock_irq(q->queue_lock); blk_mq_drain_queue(q); @@ -144,11 +142,8 @@ static void blk_mq_unfreeze_queue(struct request_queue *q) bool wake = false; spin_lock_irq(q->queue_lock); - if (!--q->bypass_depth) { - queue_flag_clear(QUEUE_FLAG_BYPASS, q); - wake = true; - } - WARN_ON_ONCE(q->bypass_depth < 0); + wake = !--q->mq_freeze_depth; + WARN_ON_ONCE(q->mq_freeze_depth < 0); spin_unlock_irq(q->queue_lock); if (wake) wake_up_all(&q->mq_freeze_wq); diff --git a/block/blk-mq.h b/block/blk-mq.h index 26460884c6cd..ca4964a6295d 100644 --- a/block/blk-mq.h +++ b/block/blk-mq.h @@ -28,7 +28,7 @@ struct blk_mq_ctx { void __blk_mq_complete_request(struct request *rq); void blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx, bool async); void blk_mq_init_flush(struct request_queue *q); -void blk_mq_drain_queue(struct request_queue *q); +void blk_mq_freeze_queue(struct request_queue *q); void blk_mq_free_queue(struct request_queue *q); void blk_mq_clone_flush_request(struct request *flush_rq, struct request *orig_rq); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 8699bcf5f099..c8f344ff74fe 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -470,6 +470,7 @@ struct request_queue { struct mutex sysfs_lock; int bypass_depth; + int mq_freeze_depth; #if defined(CONFIG_BLK_DEV_BSG) bsg_job_fn *bsg_job_fn; -- cgit v1.2.3 From add703fda981b9719d37f371498b9f129acbd997 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 1 Jul 2014 10:34:38 -0600 Subject: blk-mq: use percpu_ref for mq usage count Currently, blk-mq uses a percpu_counter to keep track of how many usages are in flight. The percpu_counter is drained while freezing to ensure that no usage is left in-flight after freezing is complete. blk_mq_queue_enter/exit() and blk_mq_[un]freeze_queue() implement this per-cpu gating mechanism. This type of code has relatively high chance of subtle bugs which are extremely difficult to trigger and it's way too hairy to be open coded in blk-mq. percpu_ref can serve the same purpose after the recent changes. This patch replaces the open-coded per-cpu usage counting and draining mechanism with percpu_ref. blk_mq_queue_enter() performs tryget_live on the ref and exit() performs put. blk_mq_freeze_queue() kills the ref and waits until the reference count reaches zero. blk_mq_unfreeze_queue() revives the ref and wakes up the waiters. Signed-off-by: Tejun Heo Cc: Jens Axboe Cc: Nicholas A. Bellinger Cc: Kent Overstreet Signed-off-by: Jens Axboe --- block/blk-mq.c | 68 +++++++++++++++++++++----------------------------- include/linux/blkdev.h | 3 ++- 2 files changed, 31 insertions(+), 40 deletions(-) (limited to 'include') diff --git a/block/blk-mq.c b/block/blk-mq.c index 22682fb4be65..5189cb1e478a 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -78,34 +78,32 @@ static void blk_mq_hctx_clear_pending(struct blk_mq_hw_ctx *hctx, static int blk_mq_queue_enter(struct request_queue *q) { - int ret; - - __percpu_counter_add(&q->mq_usage_counter, 1, 1000000); - smp_mb(); - - /* we have problems freezing the queue if it's initializing */ - if (!q->mq_freeze_depth) - return 0; - - __percpu_counter_add(&q->mq_usage_counter, -1, 1000000); + while (true) { + int ret; - spin_lock_irq(q->queue_lock); - ret = wait_event_interruptible_lock_irq(q->mq_freeze_wq, - !q->mq_freeze_depth || blk_queue_dying(q), - *q->queue_lock); - /* inc usage with lock hold to avoid freeze_queue runs here */ - if (!ret && !blk_queue_dying(q)) - __percpu_counter_add(&q->mq_usage_counter, 1, 1000000); - else if (blk_queue_dying(q)) - ret = -ENODEV; - spin_unlock_irq(q->queue_lock); + if (percpu_ref_tryget_live(&q->mq_usage_counter)) + return 0; - return ret; + ret = wait_event_interruptible(q->mq_freeze_wq, + !q->mq_freeze_depth || blk_queue_dying(q)); + if (blk_queue_dying(q)) + return -ENODEV; + if (ret) + return ret; + } } static void blk_mq_queue_exit(struct request_queue *q) { - __percpu_counter_add(&q->mq_usage_counter, -1, 1000000); + percpu_ref_put(&q->mq_usage_counter); +} + +static void blk_mq_usage_counter_release(struct percpu_ref *ref) +{ + struct request_queue *q = + container_of(ref, struct request_queue, mq_usage_counter); + + wake_up_all(&q->mq_freeze_wq); } /* @@ -118,18 +116,9 @@ void blk_mq_freeze_queue(struct request_queue *q) q->mq_freeze_depth++; spin_unlock_irq(q->queue_lock); - while (true) { - s64 count; - - spin_lock_irq(q->queue_lock); - count = percpu_counter_sum(&q->mq_usage_counter); - spin_unlock_irq(q->queue_lock); - - if (count == 0) - break; - blk_mq_start_hw_queues(q); - msleep(10); - } + percpu_ref_kill(&q->mq_usage_counter); + blk_mq_run_queues(q, false); + wait_event(q->mq_freeze_wq, percpu_ref_is_zero(&q->mq_usage_counter)); } static void blk_mq_unfreeze_queue(struct request_queue *q) @@ -140,8 +129,10 @@ static void blk_mq_unfreeze_queue(struct request_queue *q) wake = !--q->mq_freeze_depth; WARN_ON_ONCE(q->mq_freeze_depth < 0); spin_unlock_irq(q->queue_lock); - if (wake) + if (wake) { + percpu_ref_reinit(&q->mq_usage_counter); wake_up_all(&q->mq_freeze_wq); + } } bool blk_mq_can_queue(struct blk_mq_hw_ctx *hctx) @@ -1785,7 +1776,7 @@ struct request_queue *blk_mq_init_queue(struct blk_mq_tag_set *set) if (!q) goto err_hctxs; - if (percpu_counter_init(&q->mq_usage_counter, 0)) + if (percpu_ref_init(&q->mq_usage_counter, blk_mq_usage_counter_release)) goto err_map; setup_timer(&q->timeout, blk_mq_rq_timer, (unsigned long) q); @@ -1878,7 +1869,7 @@ void blk_mq_free_queue(struct request_queue *q) blk_mq_exit_hw_queues(q, set, set->nr_hw_queues); blk_mq_free_hw_queues(q, set); - percpu_counter_destroy(&q->mq_usage_counter); + percpu_ref_exit(&q->mq_usage_counter); free_percpu(q->queue_ctx); kfree(q->queue_hw_ctx); @@ -2037,8 +2028,7 @@ static int __init blk_mq_init(void) { blk_mq_cpu_init(); - /* Must be called after percpu_counter_hotcpu_callback() */ - hotcpu_notifier(blk_mq_queue_reinit_notify, -10); + hotcpu_notifier(blk_mq_queue_reinit_notify, 0); return 0; } diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index c8f344ff74fe..518b46555b80 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -484,7 +485,7 @@ struct request_queue { #endif struct rcu_head rcu_head; wait_queue_head_t mq_freeze_wq; - struct percpu_counter mq_usage_counter; + struct percpu_ref mq_usage_counter; struct list_head all_q_node; struct blk_mq_tag_set *tag_set; -- cgit v1.2.3 From cbcd1054a1fd2aa980fc11ff28e436fc4aaa2d54 Mon Sep 17 00:00:00 2001 From: Gu Zheng Date: Tue, 1 Jul 2014 10:36:47 -0600 Subject: bio-integrity: add "bip_max_vcnt" into struct bio_integrity_payload Commit 08778795 ("block: Fix nr_vecs for inline integrity vectors") from Martin introduces the function bip_integrity_vecs(get the useful vectors) to fix the issue about nr_vecs for inline integrity vectors that reported by David Milburn. But it seems that bip_integrity_vecs() will return the wrong number if the bio is not based on any bio_set for some reason(bio->bi_pool == NULL), because in that case, the bip_inline_vecs[0] is malloced directly. So here we add the bip_max_vcnt to record the count of vector slots, and cleanup the function bip_integrity_vecs(). Signed-off-by: Gu Zheng Cc: Martin K. Petersen Cc: Kent Overstreet Signed-off-by: Jens Axboe --- block/bio-integrity.c | 12 +++--------- include/linux/bio.h | 1 + 2 files changed, 4 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/block/bio-integrity.c b/block/bio-integrity.c index 9e241063a616..bc423f7b02da 100644 --- a/block/bio-integrity.c +++ b/block/bio-integrity.c @@ -70,8 +70,10 @@ struct bio_integrity_payload *bio_integrity_alloc(struct bio *bio, bs->bvec_integrity_pool); if (!bip->bip_vec) goto err; + bip->bip_max_vcnt = bvec_nr_vecs(idx); } else { bip->bip_vec = bip->bip_inline_vecs; + bip->bip_max_vcnt = inline_vecs; } bip->bip_slab = idx; @@ -114,14 +116,6 @@ void bio_integrity_free(struct bio *bio) } EXPORT_SYMBOL(bio_integrity_free); -static inline unsigned int bip_integrity_vecs(struct bio_integrity_payload *bip) -{ - if (bip->bip_slab == BIO_POOL_NONE) - return BIP_INLINE_VECS; - - return bvec_nr_vecs(bip->bip_slab); -} - /** * bio_integrity_add_page - Attach integrity metadata * @bio: bio to update @@ -137,7 +131,7 @@ int bio_integrity_add_page(struct bio *bio, struct page *page, struct bio_integrity_payload *bip = bio->bi_integrity; struct bio_vec *iv; - if (bip->bip_vcnt >= bip_integrity_vecs(bip)) { + if (bip->bip_vcnt >= bip->bip_max_vcnt) { printk(KERN_ERR "%s: bip_vec full\n", __func__); return 0; } diff --git a/include/linux/bio.h b/include/linux/bio.h index d2633ee099d9..b39e5000ff58 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -308,6 +308,7 @@ struct bio_integrity_payload { unsigned short bip_slab; /* slab the bip came from */ unsigned short bip_vcnt; /* # of integrity bio_vecs */ + unsigned short bip_max_vcnt; /* integrity bio_vec slots */ unsigned bip_owns_buf:1; /* should free bip_buf */ struct work_struct bip_work; /* I/O completion */ -- cgit v1.2.3 From d15156138dad40205c9fdd9abe85c9e1479ae272 Mon Sep 17 00:00:00 2001 From: Douglas Gilbert Date: Tue, 1 Jul 2014 10:48:05 -0600 Subject: block SG_IO: add SG_FLAG_Q_AT_HEAD flag After the SG_IO ioctl was copied into the block layer and later into the bsg driver, subtle differences emerged. One difference is the way injected commands are queued through the block layer (i.e. this is not SCSI device queueing nor SATA NCQ). Summarizing: - SG_IO on block layer device: blk_exec*(at_head=false) - sg device SG_IO: at_head=true - bsg device SG_IO: at_head=true Some time ago Boaz Harrosh introduced a sg v4 flag called BSG_FLAG_Q_AT_TAIL to override the bsg driver default. A recent patch titled: "sg: add SG_FLAG_Q_AT_TAIL flag" allowed the sg driver default to be overridden. This patch allows a SG_IO ioctl sent to a block layer device to have its default overridden. ChangeLog: - introduce SG_FLAG_Q_AT_HEAD flag in sg.h to cause commands that are injected via a block layer device SG_IO ioctl to set at_head=true - make comments clearer about queueing in sg.h since the header is used both by the sg device and block layer device implementations of the SG_IO ioctl. - introduce BSG_FLAG_Q_AT_HEAD in bsg.h for compatibility (it does nothing) and update comments. Signed-off-by: Douglas Gilbert Reviewed-by: Christoph Hellwig Reviewed-by: Mike Christie Signed-off-by: Jens Axboe --- block/scsi_ioctl.c | 5 ++++- include/scsi/sg.h | 3 +++ include/uapi/linux/bsg.h | 11 ++++++----- 3 files changed, 13 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c index bda1497add4c..51bf5155ee75 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c @@ -290,6 +290,7 @@ static int sg_io(struct request_queue *q, struct gendisk *bd_disk, unsigned long start_time; ssize_t ret = 0; int writing = 0; + int at_head = 0; struct request *rq; char sense[SCSI_SENSE_BUFFERSIZE]; struct bio *bio; @@ -313,6 +314,8 @@ static int sg_io(struct request_queue *q, struct gendisk *bd_disk, case SG_DXFER_FROM_DEV: break; } + if (hdr->flags & SG_FLAG_Q_AT_HEAD) + at_head = 1; rq = blk_get_request(q, writing ? WRITE : READ, GFP_KERNEL); if (!rq) @@ -369,7 +372,7 @@ static int sg_io(struct request_queue *q, struct gendisk *bd_disk, * (if he doesn't check that is his problem). * N.B. a non-zero SCSI status is _not_ necessarily an error. */ - blk_execute_rq(q, bd_disk, rq, 0); + blk_execute_rq(q, bd_disk, rq, at_head); hdr->duration = jiffies_to_msecs(jiffies - start_time); diff --git a/include/scsi/sg.h b/include/scsi/sg.h index a9f3c6fc3f57..4734c15ab5d6 100644 --- a/include/scsi/sg.h +++ b/include/scsi/sg.h @@ -129,6 +129,9 @@ typedef struct sg_io_hdr #define SG_FLAG_MMAP_IO 4 /* request memory mapped IO */ #define SG_FLAG_NO_DXFER 0x10000 /* no transfer of kernel buffers to/from */ /* user space (debug indirect IO) */ +/* defaults:: for sg driver: Q_AT_HEAD; for block layer: Q_AT_TAIL */ +#define SG_FLAG_Q_AT_TAIL 0x10 +#define SG_FLAG_Q_AT_HEAD 0x20 /* following 'info' values are "or"-ed together */ #define SG_INFO_OK_MASK 0x1 diff --git a/include/uapi/linux/bsg.h b/include/uapi/linux/bsg.h index 7a12e1c0f371..02986cf8b6f1 100644 --- a/include/uapi/linux/bsg.h +++ b/include/uapi/linux/bsg.h @@ -10,12 +10,13 @@ #define BSG_SUB_PROTOCOL_SCSI_TRANSPORT 2 /* - * For flags member below - * sg.h sg_io_hdr also has bits defined for it's flags member. However - * none of these bits are implemented/used by bsg. The bits below are - * allocated to not conflict with sg.h ones anyway. + * For flag constants below: + * sg.h sg_io_hdr also has bits defined for it's flags member. These + * two flag values (0x10 and 0x20) have the same meaning in sg.h . For + * bsg the BSG_FLAG_Q_AT_HEAD flag is ignored since it is the deafult. */ -#define BSG_FLAG_Q_AT_TAIL 0x10 /* default, == 0 at this bit, is Q_AT_HEAD */ +#define BSG_FLAG_Q_AT_TAIL 0x10 /* default is Q_AT_HEAD */ +#define BSG_FLAG_Q_AT_HEAD 0x20 struct sg_io_v4 { __s32 guard; /* [i] 'Q' to differentiate from v3 */ -- cgit v1.2.3 From cb553215d5d277d4838d7d6b7722e964bcf5ca1f Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Thu, 26 Jun 2014 17:41:47 +0800 Subject: include/uapi/linux/virtio_blk.h: introduce feature of VIRTIO_BLK_F_MQ Current virtio-blk spec only supports one virtual queue for transfering data between VM and host, and inside VM all kinds of operations on the virtual queue needs to hold one lock, so cause below problems: - bad scalability - bad throughput This patch requests to introduce feature of VIRTIO_BLK_F_MQ so that more than one virtual queues can be used to virtio-blk device, then above problems can be solved or eased. Signed-off-by: Ming Lei Acked-by: Michael S. Tsirkin Signed-off-by: Jens Axboe --- include/uapi/linux/virtio_blk.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/virtio_blk.h b/include/uapi/linux/virtio_blk.h index 6d8e61c48563..9ad67b267584 100644 --- a/include/uapi/linux/virtio_blk.h +++ b/include/uapi/linux/virtio_blk.h @@ -40,6 +40,7 @@ #define VIRTIO_BLK_F_WCE 9 /* Writeback mode enabled after reset */ #define VIRTIO_BLK_F_TOPOLOGY 10 /* Topology information is available */ #define VIRTIO_BLK_F_CONFIG_WCE 11 /* Writeback mode available in config */ +#define VIRTIO_BLK_F_MQ 12 /* support more than one vq */ #ifndef __KERNEL__ /* Old (deprecated) name for VIRTIO_BLK_F_WCE. */ @@ -77,6 +78,10 @@ struct virtio_blk_config { /* writeback mode (if VIRTIO_BLK_F_CONFIG_WCE) */ __u8 wce; + __u8 unused; + + /* number of vqs, only available when VIRTIO_BLK_F_MQ is set */ + __u16 num_queues; } __attribute__((packed)); /* -- cgit v1.2.3 From d93331965729850303f6111381c1a4a9e9b8ae5a Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Wed, 25 Jun 2014 14:44:53 -0700 Subject: ipv6: Allow accepting RA from local IP addresses. This can be used in virtual networking applications, and may have other uses as well. The option is disabled by default. A specific use case is setting up virtual routers, bridges, and hosts on a single OS without the use of network namespaces or virtual machines. With proper use of ip rules, routing tables, veth interface pairs and/or other virtual interfaces, and applications that can bind to interfaces and/or IP addresses, it is possibly to create one or more virtual routers with multiple hosts attached. The host interfaces can act as IPv6 systems, with radvd running on the ports in the virtual routers. With the option provided in this patch enabled, those hosts can now properly obtain IPv6 addresses from the radvd. Signed-off-by: Ben Greear Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 12 ++++++++++++ include/linux/ipv6.h | 1 + include/uapi/linux/ipv6.h | 1 + include/uapi/linux/sysctl.h | 1 + kernel/sysctl_binary.c | 1 + net/ipv6/addrconf.c | 10 ++++++++++ net/ipv6/ndisc.c | 21 +++++++++++++-------- 7 files changed, 39 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index ab42c95f9985..10e216c6e05e 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -1210,6 +1210,18 @@ accept_ra_defrtr - BOOLEAN Functional default: enabled if accept_ra is enabled. disabled if accept_ra is disabled. +accept_ra_from_local - BOOLEAN + Accept RA with source-address that is found on local machine + if the RA is otherwise proper and able to be accepted. + Default is to NOT accept these as it may be an un-intended + network loop. + + Functional default: + enabled if accept_ra_from_local is enabled + on a specific interface. + disabled if accept_ra_from_local is disabled + on a specific interface. + accept_ra_pinfo - BOOLEAN Learn Prefix Information in Router Advertisement. diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index c811300b0b0c..b0f2452f1d58 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -39,6 +39,7 @@ struct ipv6_devconf { #endif __s32 proxy_ndp; __s32 accept_source_route; + __s32 accept_ra_from_local; #ifdef CONFIG_IPV6_OPTIMISTIC_DAD __s32 optimistic_dad; #endif diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h index 593b0e32d956..efa2666f4b8a 100644 --- a/include/uapi/linux/ipv6.h +++ b/include/uapi/linux/ipv6.h @@ -163,6 +163,7 @@ enum { DEVCONF_MLDV1_UNSOLICITED_REPORT_INTERVAL, DEVCONF_MLDV2_UNSOLICITED_REPORT_INTERVAL, DEVCONF_SUPPRESS_FRAG_NDISC, + DEVCONF_ACCEPT_RA_FROM_LOCAL, DEVCONF_MAX }; diff --git a/include/uapi/linux/sysctl.h b/include/uapi/linux/sysctl.h index 6d6721341f49..43aaba1cc037 100644 --- a/include/uapi/linux/sysctl.h +++ b/include/uapi/linux/sysctl.h @@ -568,6 +568,7 @@ enum { NET_IPV6_ACCEPT_RA_RT_INFO_MAX_PLEN=22, NET_IPV6_PROXY_NDP=23, NET_IPV6_ACCEPT_SOURCE_ROUTE=25, + NET_IPV6_ACCEPT_RA_FROM_LOCAL=26, __NET_IPV6_MAX }; diff --git a/kernel/sysctl_binary.c b/kernel/sysctl_binary.c index 653cbbd9e7ad..e4ba9a5a5ccb 100644 --- a/kernel/sysctl_binary.c +++ b/kernel/sysctl_binary.c @@ -522,6 +522,7 @@ static const struct bin_table bin_net_ipv6_conf_var_table[] = { { CTL_INT, NET_IPV6_ACCEPT_RA_RT_INFO_MAX_PLEN, "accept_ra_rt_info_max_plen" }, { CTL_INT, NET_IPV6_PROXY_NDP, "proxy_ndp" }, { CTL_INT, NET_IPV6_ACCEPT_SOURCE_ROUTE, "accept_source_route" }, + { CTL_INT, NET_IPV6_ACCEPT_RA_FROM_LOCAL, "accept_ra_from_local" }, {} }; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 5667b3003af9..358edd2272ac 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -186,6 +186,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { .max_desync_factor = MAX_DESYNC_FACTOR, .max_addresses = IPV6_MAX_ADDRESSES, .accept_ra_defrtr = 1, + .accept_ra_from_local = 0, .accept_ra_pinfo = 1, #ifdef CONFIG_IPV6_ROUTER_PREF .accept_ra_rtr_pref = 1, @@ -222,6 +223,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { .max_desync_factor = MAX_DESYNC_FACTOR, .max_addresses = IPV6_MAX_ADDRESSES, .accept_ra_defrtr = 1, + .accept_ra_from_local = 0, .accept_ra_pinfo = 1, #ifdef CONFIG_IPV6_ROUTER_PREF .accept_ra_rtr_pref = 1, @@ -4321,6 +4323,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, array[DEVCONF_FORCE_TLLAO] = cnf->force_tllao; array[DEVCONF_NDISC_NOTIFY] = cnf->ndisc_notify; array[DEVCONF_SUPPRESS_FRAG_NDISC] = cnf->suppress_frag_ndisc; + array[DEVCONF_ACCEPT_RA_FROM_LOCAL] = cnf->accept_ra_from_local; } static inline size_t inet6_ifla6_size(void) @@ -5167,6 +5170,13 @@ static struct addrconf_sysctl_table .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "accept_ra_from_local", + .data = &ipv6_devconf.accept_ra_from_local, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { /* sentinel */ } diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 736c11c6d266..a845e3d2057e 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1148,11 +1148,15 @@ static void ndisc_router_discovery(struct sk_buff *skb) goto skip_defrtr; } - if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, - NULL, 0)) { + /* Do not accept RA with source-addr found on local machine unless + * accept_ra_from_local is set to true. + */ + if (!(in6_dev->cnf.accept_ra_from_local || + ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, + NULL, 0))) { ND_PRINTK(2, info, - "RA: %s, chk_addr failed for dev: %s\n", - __func__, skb->dev->name); + "RA from local address detected on dev: %s: default router ignored\n", + skb->dev->name); goto skip_defrtr; } @@ -1290,11 +1294,12 @@ skip_linkparms: } #ifdef CONFIG_IPV6_ROUTE_INFO - if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, - NULL, 0)) { + if (!(in6_dev->cnf.accept_ra_from_local || + ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, + NULL, 0))) { ND_PRINTK(2, info, - "RA: %s, chk-addr (route info) is false for dev: %s\n", - __func__, skb->dev->name); + "RA from local address detected on dev: %s: router info ignored.\n", + skb->dev->name); goto skip_routeinfo; } -- cgit v1.2.3 From 330d282216d6e4d845a21b72572dc4df4122e8fa Mon Sep 17 00:00:00 2001 From: Zhengyu He Date: Tue, 1 Jul 2014 12:11:47 -0700 Subject: core: fix typo in percpu read_mostly section This fixes a typo that named the read_mostly section of percpu as readmostly. It works fine with SMP because the linker script specifies .data..percpu..readmostly. However, UP kernel builds don't have percpu sections defined and the non-percpu version of the section is called data..read_mostly, so .data..readmostly will float around and may break things unexpectedly. Looking at the original change that introduced data..percpu..readmostly (commit c957ef2c59e952803766ddc22e89981ab534606f), it looks like this was the original intention. Tested: Built UP kernel and confirmed the sections got merged. - Before the patch: $ objdump -h vmlinux.o | grep '\.data\.\.read.*mostly' 38 .data..read_mostly 00004418 0000000000000000 0000000000000000 00431ac0 2**6 50 .data..readmostly 00000014 0000000000000000 0000000000000000 00444000 2**3 - After the patch: $ objdump -h vmlinux.o | grep '\.data\.\.read.*mostly' 38 .data..read_mostly 00004438 0000000000000000 0000000000000000 00431ac0 2**6 Signed-off-by: Zhengyu He Signed-off-by: Filipe Brandenburger Signed-off-by: Tejun Heo --- include/asm-generic/vmlinux.lds.h | 2 +- include/linux/percpu-defs.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 471ba48c7ae4..c1c0b0cf39b4 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -693,7 +693,7 @@ . = ALIGN(PAGE_SIZE); \ *(.data..percpu..page_aligned) \ . = ALIGN(cacheline); \ - *(.data..percpu..readmostly) \ + *(.data..percpu..read_mostly) \ . = ALIGN(cacheline); \ *(.data..percpu) \ *(.data..percpu..shared_aligned) \ diff --git a/include/linux/percpu-defs.h b/include/linux/percpu-defs.h index a5fc7d01aad6..dec01d6c3f80 100644 --- a/include/linux/percpu-defs.h +++ b/include/linux/percpu-defs.h @@ -146,10 +146,10 @@ * Declaration/definition used for per-CPU variables that must be read mostly. */ #define DECLARE_PER_CPU_READ_MOSTLY(type, name) \ - DECLARE_PER_CPU_SECTION(type, name, "..readmostly") + DECLARE_PER_CPU_SECTION(type, name, "..read_mostly") #define DEFINE_PER_CPU_READ_MOSTLY(type, name) \ - DEFINE_PER_CPU_SECTION(type, name, "..readmostly") + DEFINE_PER_CPU_SECTION(type, name, "..read_mostly") /* * Intermodule exports for per-CPU variables. sparse forgets about -- cgit v1.2.3 From 5974b794cbcd5fac1bb605749c1ac445f5a2d07c Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 27 Jun 2014 09:01:10 +0300 Subject: dt/bindings: Binding documentation for Palmas clk32kg and clk32kgaudio clocks Palmas class of devices can provide 32K clock(s) to be used by other devices on the board. Depending on the actual device the provided clocks can be: CLK32K_KG and CLK32K_KGAUDIO or only one: CLK32K_KG (TPS659039 for example) Use separate compatible flags for the two 32K clock. A system which needs or have only one of the 32k clock from Palmas will need to add node(s) for each clock as separate section in the dts file. The two compatible property is: "ti,palmas-clk32kg" for clk32kg clock "ti,palmas-clk32kgaudio" for clk32kgaudio clock Apart from the register control of the clocks - which is done via the clock API there is a posibility to enable the external sleep control. In this way the clock can be enabled/disabled on demand by the user of the clock. Signed-off-by: Peter Ujfalusi Reviewed-by: Nishanth Menon Signed-off-by: Mike Turquette --- .../bindings/clock/clk-palmas-clk32kg-clocks.txt | 35 ++++++++++++++++++++++ include/dt-bindings/mfd/palmas.h | 18 +++++++++++ 2 files changed, 53 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/clk-palmas-clk32kg-clocks.txt create mode 100644 include/dt-bindings/mfd/palmas.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/clk-palmas-clk32kg-clocks.txt b/Documentation/devicetree/bindings/clock/clk-palmas-clk32kg-clocks.txt new file mode 100644 index 000000000000..4208886d834a --- /dev/null +++ b/Documentation/devicetree/bindings/clock/clk-palmas-clk32kg-clocks.txt @@ -0,0 +1,35 @@ +* Palmas 32KHz clocks * + +Palmas device has two clock output pins for 32KHz, KG and KG_AUDIO. + +This binding uses the common clock binding ./clock-bindings.txt. + +Required properties: +- compatible : "ti,palmas-clk32kg" for clk32kg clock + "ti,palmas-clk32kgaudio" for clk32kgaudio clock +- #clock-cells : shall be set to 0. + +Optional property: +- ti,external-sleep-control: The external enable input pins controlled the + enable/disable of clocks. The external enable input pins ENABLE1, + ENABLE2 and NSLEEP. The valid values for the external pins are: + PALMAS_EXT_CONTROL_PIN_ENABLE1 for ENABLE1 pin + PALMAS_EXT_CONTROL_PIN_ENABLE2 for ENABLE2 pin + PALMAS_EXT_CONTROL_PIN_NSLEEP for NSLEEP pin + Option 0 or missing this property means the clock is enabled/disabled + via register access and these pins do not have any control. + The macros of external control pins for DTS is defined at + dt-bindings/mfd/palmas.h + +Example: + #include + ... + palmas: tps65913@58 { + ... + clk32kg: palmas_clk32k@0 { + compatible = "ti,palmas-clk32kg"; + #clock-cells = <0>; + ti,external-sleep-control = ; + }; + ... + }; diff --git a/include/dt-bindings/mfd/palmas.h b/include/dt-bindings/mfd/palmas.h new file mode 100644 index 000000000000..2c8ac4841385 --- /dev/null +++ b/include/dt-bindings/mfd/palmas.h @@ -0,0 +1,18 @@ +/* + * This header provides macros for Palmas device bindings. + * + * Copyright (c) 2013, NVIDIA Corporation. + * + * Author: Laxman Dewangan + * + */ + +#ifndef __DT_BINDINGS_PALMAS_H__ +#define __DT_BINDINGS_PALMAS_H + +/* External control pins */ +#define PALMAS_EXT_CONTROL_PIN_ENABLE1 1 +#define PALMAS_EXT_CONTROL_PIN_ENABLE2 2 +#define PALMAS_EXT_CONTROL_PIN_NSLEEP 3 + +#endif /* __DT_BINDINGS_PALMAS_H */ -- cgit v1.2.3 From b6220ad66bcd4a50737eb3c08e9466aa44f3bc98 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 24 Jun 2014 18:05:29 -0700 Subject: sched: Fix compiler warnings Commit 143e1e28cb (sched: Rework sched_domain topology definition) introduced a number of functions with a return value of 'const int'. gcc doesn't know what to do with that and, if the kernel is compiled with W=1, complains with the following warnings whenever sched.h is included. include/linux/sched.h:875:25: warning: type qualifiers ignored on function return type include/linux/sched.h:882:25: warning: type qualifiers ignored on function return type include/linux/sched.h:889:25: warning: type qualifiers ignored on function return type include/linux/sched.h:1002:21: warning: type qualifiers ignored on function return type Commits fb2aa855 (sched, ARM: Create a dedicated scheduler topology table) and 607b45e9a (sched, powerpc: Create a dedicated topology table) introduce the same warning in the arm and powerpc code. Drop 'const' from the function declarations to fix the problem. The fix for all three patches has to be applied together to avoid compilation failures for the affected architectures. Acked-by: Vincent Guittot Acked-by: Benjamin Herrenschmidt Signed-off-by: Guenter Roeck Cc: Russell King Cc: Paul Mackerras Cc: Dietmar Eggemann Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1403658329-13196-1-git-send-email-linux@roeck-us.net Signed-off-by: Ingo Molnar --- arch/arm/kernel/topology.c | 2 +- arch/powerpc/kernel/smp.c | 2 +- include/linux/sched.h | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c index 9d853189028b..e35d880f9773 100644 --- a/arch/arm/kernel/topology.c +++ b/arch/arm/kernel/topology.c @@ -275,7 +275,7 @@ void store_cpu_topology(unsigned int cpuid) cpu_topology[cpuid].socket_id, mpidr); } -static inline const int cpu_corepower_flags(void) +static inline int cpu_corepower_flags(void) { return SD_SHARE_PKG_RESOURCES | SD_SHARE_POWERDOMAIN; } diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c index 51a3ff78838a..1007fb802e6b 100644 --- a/arch/powerpc/kernel/smp.c +++ b/arch/powerpc/kernel/smp.c @@ -747,7 +747,7 @@ int setup_profiling_timer(unsigned int multiplier) #ifdef CONFIG_SCHED_SMT /* cpumask of CPUs with asymetric SMT dependancy */ -static const int powerpc_smt_flags(void) +static int powerpc_smt_flags(void) { int flags = SD_SHARE_CPUCAPACITY | SD_SHARE_PKG_RESOURCES; diff --git a/include/linux/sched.h b/include/linux/sched.h index 306f4f0c987a..0376b054a0d0 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -872,21 +872,21 @@ enum cpu_idle_type { #define SD_NUMA 0x4000 /* cross-node balancing */ #ifdef CONFIG_SCHED_SMT -static inline const int cpu_smt_flags(void) +static inline int cpu_smt_flags(void) { return SD_SHARE_CPUCAPACITY | SD_SHARE_PKG_RESOURCES; } #endif #ifdef CONFIG_SCHED_MC -static inline const int cpu_core_flags(void) +static inline int cpu_core_flags(void) { return SD_SHARE_PKG_RESOURCES; } #endif #ifdef CONFIG_NUMA -static inline const int cpu_numa_flags(void) +static inline int cpu_numa_flags(void) { return SD_NUMA; } @@ -999,7 +999,7 @@ void free_sched_domains(cpumask_var_t doms[], unsigned int ndoms); bool cpus_share_cache(int this_cpu, int that_cpu); typedef const struct cpumask *(*sched_domain_mask_f)(int cpu); -typedef const int (*sched_domain_flags_f)(void); +typedef int (*sched_domain_flags_f)(void); #define SDTL_OVERLAP 0x01 -- cgit v1.2.3 From 9fe516ba3fb29b6f6a752ffd93342fdee500ec01 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 27 Jun 2014 08:36:16 -0700 Subject: inet: move ipv6only in sock_common When an UDP application switches from AF_INET to AF_INET6 sockets, we have a small performance degradation for IPv4 communications because of extra cache line misses to access ipv6only information. This can also be noticed for TCP listeners, as ipv6_only_sock() is also used from __inet_lookup_listener()->compute_score() This is magnified when SO_REUSEPORT is used. Move ipv6only into struct sock_common so that it is available at no extra cost in lookups. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/ipv6.h | 10 +++++----- include/net/inet_timewait_sock.h | 3 ++- include/net/sock.h | 4 +++- net/dccp/minisocks.c | 4 +--- net/ipv4/tcp_minisocks.c | 2 +- net/ipv6/af_inet6.c | 6 +++--- net/ipv6/ipv6_sockglue.c | 4 ++-- net/ipv6/udp.c | 3 +-- 8 files changed, 18 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index b0f2452f1d58..5dc68c3ebcbd 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -194,7 +194,7 @@ struct ipv6_pinfo { sndflow:1, repflow:1, pmtudisc:3, - ipv6only:1, + padding:1, /* 1 bit hole */ srcprefs:3, /* 001: prefer temporary address * 010: prefer public address * 100: prefer care-of address @@ -273,8 +273,8 @@ static inline void inet_sk_copy_descendant(struct sock *sk_to, __inet_sk_copy_descendant(sk_to, sk_from, ancestor_size); } -#define __ipv6_only_sock(sk) (inet6_sk(sk)->ipv6only) -#define ipv6_only_sock(sk) ((sk)->sk_family == PF_INET6 && __ipv6_only_sock(sk)) +#define __ipv6_only_sock(sk) (sk->sk_ipv6only) +#define ipv6_only_sock(sk) (__ipv6_only_sock(sk)) #define ipv6_sk_rxinfo(sk) ((sk)->sk_family == PF_INET6 && \ inet6_sk(sk)->rxopt.bits.rxinfo) @@ -287,8 +287,8 @@ static inline const struct in6_addr *inet6_rcv_saddr(const struct sock *sk) static inline int inet_v6_ipv6only(const struct sock *sk) { - return likely(sk->sk_state != TCP_TIME_WAIT) ? - ipv6_only_sock(sk) : inet_twsk(sk)->tw_ipv6only; + /* ipv6only field is at same position for timewait and other sockets */ + return ipv6_only_sock(sk); } #else #define __ipv6_only_sock(sk) 0 diff --git a/include/net/inet_timewait_sock.h b/include/net/inet_timewait_sock.h index 61474ea02152..6c566034e26d 100644 --- a/include/net/inet_timewait_sock.h +++ b/include/net/inet_timewait_sock.h @@ -108,6 +108,7 @@ struct inet_timewait_sock { #define tw_family __tw_common.skc_family #define tw_state __tw_common.skc_state #define tw_reuse __tw_common.skc_reuse +#define tw_ipv6only __tw_common.skc_ipv6only #define tw_bound_dev_if __tw_common.skc_bound_dev_if #define tw_node __tw_common.skc_nulls_node #define tw_bind_node __tw_common.skc_bind_node @@ -131,7 +132,7 @@ struct inet_timewait_sock { __be16 tw_sport; kmemcheck_bitfield_begin(flags); /* And these are ours. */ - unsigned int tw_ipv6only : 1, + unsigned int tw_pad0 : 1, /* 1 bit hole */ tw_transparent : 1, tw_flowlabel : 20, tw_pad : 2, /* 2 bits hole */ diff --git a/include/net/sock.h b/include/net/sock.h index 173cae485de1..8d4c9473e7d7 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -181,7 +181,8 @@ struct sock_common { unsigned short skc_family; volatile unsigned char skc_state; unsigned char skc_reuse:4; - unsigned char skc_reuseport:4; + unsigned char skc_reuseport:1; + unsigned char skc_ipv6only:1; int skc_bound_dev_if; union { struct hlist_node skc_bind_node; @@ -317,6 +318,7 @@ struct sock { #define sk_state __sk_common.skc_state #define sk_reuse __sk_common.skc_reuse #define sk_reuseport __sk_common.skc_reuseport +#define sk_ipv6only __sk_common.skc_ipv6only #define sk_bound_dev_if __sk_common.skc_bound_dev_if #define sk_bind_node __sk_common.skc_bind_node #define sk_prot __sk_common.skc_prot diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c index c69eb9c4fbb8..b50dc436db1f 100644 --- a/net/dccp/minisocks.c +++ b/net/dccp/minisocks.c @@ -55,11 +55,9 @@ void dccp_time_wait(struct sock *sk, int state, int timeo) const int rto = (icsk->icsk_rto << 2) - (icsk->icsk_rto >> 1); #if IS_ENABLED(CONFIG_IPV6) if (tw->tw_family == PF_INET6) { - const struct ipv6_pinfo *np = inet6_sk(sk); - tw->tw_v6_daddr = sk->sk_v6_daddr; tw->tw_v6_rcv_saddr = sk->sk_v6_rcv_saddr; - tw->tw_ipv6only = np->ipv6only; + tw->tw_ipv6only = sk->sk_ipv6only; } #endif /* Linkage updates. */ diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index e68e0d4af6c9..1649988bd1b6 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -298,7 +298,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) tw->tw_v6_rcv_saddr = sk->sk_v6_rcv_saddr; tw->tw_tclass = np->tclass; tw->tw_flowlabel = np->flow_label >> 12; - tw->tw_ipv6only = np->ipv6only; + tw->tw_ipv6only = sk->sk_ipv6only; } #endif diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 7cb4392690dd..a426cd7099bb 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -197,7 +197,7 @@ lookup_protocol: np->mcast_hops = IPV6_DEFAULT_MCASTHOPS; np->mc_loop = 1; np->pmtudisc = IPV6_PMTUDISC_WANT; - np->ipv6only = net->ipv6.sysctl.bindv6only; + sk->sk_ipv6only = net->ipv6.sysctl.bindv6only; /* Init the ipv4 part of the socket since we can have sockets * using v6 API for ipv4. @@ -294,7 +294,7 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) /* Binding to v4-mapped address on a v6-only socket * makes no sense */ - if (np->ipv6only) { + if (sk->sk_ipv6only) { err = -EINVAL; goto out; } @@ -371,7 +371,7 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) if (addr_type != IPV6_ADDR_ANY) { sk->sk_userlocks |= SOCK_BINDADDR_LOCK; if (addr_type != IPV6_ADDR_MAPPED) - np->ipv6only = 1; + sk->sk_ipv6only = 1; } if (snum) sk->sk_userlocks |= SOCK_BINDPORT_LOCK; diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index edb58aff4ae7..cc34f65179e4 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -235,7 +235,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, if (optlen < sizeof(int) || inet_sk(sk)->inet_num) goto e_inval; - np->ipv6only = valbool; + sk->sk_ipv6only = valbool; retv = 0; break; @@ -1058,7 +1058,7 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, } case IPV6_V6ONLY: - val = np->ipv6only; + val = sk->sk_ipv6only; break; case IPV6_RECVPKTINFO: diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 95c834799288..c2bd28fd43e4 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -79,7 +79,6 @@ static unsigned int udp6_ehashfn(struct net *net, int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2) { const struct in6_addr *sk2_rcv_saddr6 = inet6_rcv_saddr(sk2); - int sk_ipv6only = ipv6_only_sock(sk); int sk2_ipv6only = inet_v6_ipv6only(sk2); int addr_type = ipv6_addr_type(&sk->sk_v6_rcv_saddr); int addr_type2 = sk2_rcv_saddr6 ? ipv6_addr_type(sk2_rcv_saddr6) : IPV6_ADDR_MAPPED; @@ -95,7 +94,7 @@ int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2) return 1; if (addr_type == IPV6_ADDR_ANY && - !(sk_ipv6only && addr_type2 == IPV6_ADDR_MAPPED)) + !(ipv6_only_sock(sk) && addr_type2 == IPV6_ADDR_MAPPED)) return 1; if (sk2_rcv_saddr6 && -- cgit v1.2.3 From d9daa24720891a88bedb93928f57767da96e5c80 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sat, 28 Jun 2014 01:23:35 +0200 Subject: net: fix circular dependency in of_mdio code Commit 86f6cf4127 (net: of_mdio: add of_mdiobus_link_phydev()) introduced a circular dependency between libphy and of_mdio. depmod: ERROR: /kernel/drivers/net/phy/libphy.ko in dependency cycle! depmod: ERROR: /kernel/drivers/of/of_mdio.ko in dependency cycle! The problem is that of_mdio.c references &mdio_bus_type and libphy now references of_mdiobus_link_phydev. Fix this by not exporting of_mdiobus_link_phydev() from of_mdio.ko. Make it a static function in mdio_bus.c instead. Signed-off-by: Daniel Mack Reported-by: Jeff Mahoney Fixes: 86f6cf4127 (net: of_mdio: add of_mdiobus_link_phydev()) Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/mdio_bus.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ drivers/of/of_mdio.c | 34 ---------------------------------- include/linux/of_mdio.h | 8 -------- 3 files changed, 44 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 2e58aa54484c..4eaadcfcb0fe 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -187,6 +187,50 @@ struct mii_bus *of_mdio_find_bus(struct device_node *mdio_bus_np) return d ? to_mii_bus(d) : NULL; } EXPORT_SYMBOL(of_mdio_find_bus); + +/* Walk the list of subnodes of a mdio bus and look for a node that matches the + * phy's address with its 'reg' property. If found, set the of_node pointer for + * the phy. This allows auto-probed pyh devices to be supplied with information + * passed in via DT. + */ +static void of_mdiobus_link_phydev(struct mii_bus *mdio, + struct phy_device *phydev) +{ + struct device *dev = &phydev->dev; + struct device_node *child; + + if (dev->of_node || !mdio->dev.of_node) + return; + + for_each_available_child_of_node(mdio->dev.of_node, child) { + int addr; + int ret; + + ret = of_property_read_u32(child, "reg", &addr); + if (ret < 0) { + dev_err(dev, "%s has invalid PHY address\n", + child->full_name); + continue; + } + + /* A PHY must have a reg property in the range [0-31] */ + if (addr >= PHY_MAX_ADDR) { + dev_err(dev, "%s PHY address %i is too large\n", + child->full_name, addr); + continue; + } + + if (addr == phydev->addr) { + dev->of_node = child; + return; + } + } +} +#else /* !IS_ENABLED(CONFIG_OF_MDIO) */ +static inline void of_mdiobus_link_phydev(struct mii_bus *mdio, + struct phy_device *phydev) +{ +} #endif /** diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index a3bf2122a8d5..401b2453da45 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -182,40 +182,6 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) } EXPORT_SYMBOL(of_mdiobus_register); -/** - * of_mdiobus_link_phydev - Find a device node for a phy - * @mdio: pointer to mii_bus structure - * @phydev: phydev for which the of_node pointer should be set - * - * Walk the list of subnodes of a mdio bus and look for a node that matches the - * phy's address with its 'reg' property. If found, set the of_node pointer for - * the phy. This allows auto-probed pyh devices to be supplied with information - * passed in via DT. - */ -void of_mdiobus_link_phydev(struct mii_bus *mdio, - struct phy_device *phydev) -{ - struct device *dev = &phydev->dev; - struct device_node *child; - - if (dev->of_node || !mdio->dev.of_node) - return; - - for_each_available_child_of_node(mdio->dev.of_node, child) { - int addr; - - addr = of_mdio_parse_addr(&mdio->dev, child); - if (addr < 0) - continue; - - if (addr == phydev->addr) { - dev->of_node = child; - return; - } - } -} -EXPORT_SYMBOL(of_mdiobus_link_phydev); - /* Helper function for of_phy_find_device */ static int of_phy_match(struct device *dev, void *phy_np) { diff --git a/include/linux/of_mdio.h b/include/linux/of_mdio.h index a70c9493d55a..d449018d0726 100644 --- a/include/linux/of_mdio.h +++ b/include/linux/of_mdio.h @@ -25,9 +25,6 @@ struct phy_device *of_phy_attach(struct net_device *dev, extern struct mii_bus *of_mdio_find_bus(struct device_node *mdio_np); -extern void of_mdiobus_link_phydev(struct mii_bus *mdio, - struct phy_device *phydev); - #else /* CONFIG_OF */ static inline int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) { @@ -63,11 +60,6 @@ static inline struct mii_bus *of_mdio_find_bus(struct device_node *mdio_np) { return NULL; } - -static inline void of_mdiobus_link_phydev(struct mii_bus *mdio, - struct phy_device *phydev) -{ -} #endif /* CONFIG_OF */ #if defined(CONFIG_OF) && defined(CONFIG_FIXED_PHY) -- cgit v1.2.3 From 628627bfd943c077c65489acd8b23c7bb14eb0e2 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 4 Mar 2014 18:50:53 +0100 Subject: clocksource: shmobile: Remove unused sh_timer_config members The name, channel_offset, timer_bit, clockevent_rating and clocksource_rating members are unused. Remove them. Signed-off-by: Laurent Pinchart Tested-by: Simon Horman --- include/linux/sh_timer.h | 5 ----- 1 file changed, 5 deletions(-) (limited to 'include') diff --git a/include/linux/sh_timer.h b/include/linux/sh_timer.h index 8e1e036d6d45..64638b058076 100644 --- a/include/linux/sh_timer.h +++ b/include/linux/sh_timer.h @@ -2,11 +2,6 @@ #define __SH_TIMER_H__ struct sh_timer_config { - char *name; - long channel_offset; - int timer_bit; - unsigned long clockevent_rating; - unsigned long clocksource_rating; unsigned int channels_mask; }; -- cgit v1.2.3 From ecca47ce8294843045e7465d76fee84dbf07a004 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 1 Jul 2014 16:41:03 -0400 Subject: kernfs: kernfs_notify() must be useable from non-sleepable contexts d911d9874801 ("kernfs: make kernfs_notify() trigger inotify events too") added fsnotify triggering to kernfs_notify() which requires a sleepable context. There are already existing users of kernfs_notify() which invoke it from an atomic context and in general it's silly to require a sleepable context for triggering a notification. The following is an invalid context bug triggerd by md invoking sysfs_notify() from IO completion path. BUG: sleeping function called from invalid context at kernel/locking/mutex.c:586 in_atomic(): 1, irqs_disabled(): 1, pid: 0, name: swapper/1 2 locks held by swapper/1/0: #0: (&(&vblk->vq_lock)->rlock){-.-...}, at: [] virtblk_done+0x42/0xe0 [virtio_blk] #1: (&(&bitmap->counts.lock)->rlock){-.....}, at: [] bitmap_endwrite+0x68/0x240 irq event stamp: 33518 hardirqs last enabled at (33515): [] default_idle+0x1f/0x230 hardirqs last disabled at (33516): [] common_interrupt+0x6d/0x72 softirqs last enabled at (33518): [] _local_bh_enable+0x22/0x50 softirqs last disabled at (33517): [] irq_enter+0x60/0x80 CPU: 1 PID: 0 Comm: swapper/1 Not tainted 3.16.0-0.rc2.git2.1.fc21.x86_64 #1 Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 0000000000000000 f90db13964f4ee05 ffff88007d403b80 ffffffff81807b4c 0000000000000000 ffff88007d403ba8 ffffffff810d4f14 0000000000000000 0000000000441800 ffff880078fa1780 ffff88007d403c38 ffffffff8180caf2 Call Trace: [] dump_stack+0x4d/0x66 [] __might_sleep+0x184/0x240 [] mutex_lock_nested+0x42/0x440 [] kernfs_notify+0x90/0x150 [] bitmap_endwrite+0xcc/0x240 [] close_write+0x93/0xb0 [raid1] [] r1_bio_write_done+0x29/0x50 [raid1] [] raid1_end_write_request+0xe4/0x260 [raid1] [] bio_endio+0x6b/0xa0 [] blk_update_request+0x94/0x420 [] blk_mq_end_io+0x1a/0x70 [] virtblk_request_done+0x32/0x80 [virtio_blk] [] __blk_mq_complete_request+0x88/0x120 [] blk_mq_complete_request+0x2a/0x30 [] virtblk_done+0x66/0xe0 [virtio_blk] [] vring_interrupt+0x3a/0xa0 [virtio_ring] [] handle_irq_event_percpu+0x77/0x340 [] handle_irq_event+0x3d/0x60 [] handle_edge_irq+0x66/0x130 [] handle_irq+0x84/0x150 [] do_IRQ+0x4d/0xe0 [] common_interrupt+0x72/0x72 [] ? native_safe_halt+0x6/0x10 [] default_idle+0x24/0x230 [] arch_cpu_idle+0xf/0x20 [] cpu_startup_entry+0x37c/0x7b0 [] start_secondary+0x25b/0x300 This patch fixes it by punting the notification delivery through a work item. This ends up adding an extra pointer to kernfs_elem_attr enlarging kernfs_node by a pointer, which is not ideal but not a very big deal either. If this turns out to be an actual issue, we can move kernfs_elem_attr->size to kernfs_node->iattr later. Signed-off-by: Tejun Heo Reported-by: Josh Boyer Cc: Jens Axboe Reviewed-by: Michael S. Tsirkin Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/file.c | 69 ++++++++++++++++++++++++++++++++++++++++---------- include/linux/kernfs.h | 1 + 2 files changed, 56 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index e3d37f607f97..d895b4b7b661 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -39,6 +39,19 @@ struct kernfs_open_node { struct list_head files; /* goes through kernfs_open_file.list */ }; +/* + * kernfs_notify() may be called from any context and bounces notifications + * through a work item. To minimize space overhead in kernfs_node, the + * pending queue is implemented as a singly linked list of kernfs_nodes. + * The list is terminated with the self pointer so that whether a + * kernfs_node is on the list or not can be determined by testing the next + * pointer for NULL. + */ +#define KERNFS_NOTIFY_EOL ((void *)&kernfs_notify_list) + +static DEFINE_SPINLOCK(kernfs_notify_lock); +static struct kernfs_node *kernfs_notify_list = KERNFS_NOTIFY_EOL; + static struct kernfs_open_file *kernfs_of(struct file *file) { return ((struct seq_file *)file->private_data)->private; @@ -783,24 +796,25 @@ static unsigned int kernfs_fop_poll(struct file *filp, poll_table *wait) return DEFAULT_POLLMASK|POLLERR|POLLPRI; } -/** - * kernfs_notify - notify a kernfs file - * @kn: file to notify - * - * Notify @kn such that poll(2) on @kn wakes up. - */ -void kernfs_notify(struct kernfs_node *kn) +static void kernfs_notify_workfn(struct work_struct *work) { - struct kernfs_root *root = kernfs_root(kn); + struct kernfs_node *kn; struct kernfs_open_node *on; struct kernfs_super_info *info; - unsigned long flags; - - if (WARN_ON(kernfs_type(kn) != KERNFS_FILE)) +repeat: + /* pop one off the notify_list */ + spin_lock_irq(&kernfs_notify_lock); + kn = kernfs_notify_list; + if (kn == KERNFS_NOTIFY_EOL) { + spin_unlock_irq(&kernfs_notify_lock); return; + } + kernfs_notify_list = kn->attr.notify_next; + kn->attr.notify_next = NULL; + spin_unlock_irq(&kernfs_notify_lock); /* kick poll */ - spin_lock_irqsave(&kernfs_open_node_lock, flags); + spin_lock_irq(&kernfs_open_node_lock); on = kn->attr.open; if (on) { @@ -808,12 +822,12 @@ void kernfs_notify(struct kernfs_node *kn) wake_up_interruptible(&on->poll); } - spin_unlock_irqrestore(&kernfs_open_node_lock, flags); + spin_unlock_irq(&kernfs_open_node_lock); /* kick fsnotify */ mutex_lock(&kernfs_mutex); - list_for_each_entry(info, &root->supers, node) { + list_for_each_entry(info, &kernfs_root(kn)->supers, node) { struct inode *inode; struct dentry *dentry; @@ -833,6 +847,33 @@ void kernfs_notify(struct kernfs_node *kn) } mutex_unlock(&kernfs_mutex); + kernfs_put(kn); + goto repeat; +} + +/** + * kernfs_notify - notify a kernfs file + * @kn: file to notify + * + * Notify @kn such that poll(2) on @kn wakes up. Maybe be called from any + * context. + */ +void kernfs_notify(struct kernfs_node *kn) +{ + static DECLARE_WORK(kernfs_notify_work, kernfs_notify_workfn); + unsigned long flags; + + if (WARN_ON(kernfs_type(kn) != KERNFS_FILE)) + return; + + spin_lock_irqsave(&kernfs_notify_lock, flags); + if (!kn->attr.notify_next) { + kernfs_get(kn); + kn->attr.notify_next = kernfs_notify_list; + kernfs_notify_list = kn; + schedule_work(&kernfs_notify_work); + } + spin_unlock_irqrestore(&kernfs_notify_lock, flags); } EXPORT_SYMBOL_GPL(kernfs_notify); diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 17aa1cce6f8e..145375ea0bd9 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -91,6 +91,7 @@ struct kernfs_elem_attr { const struct kernfs_ops *ops; struct kernfs_open_node *open; loff_t size; + struct kernfs_node *notify_next; /* for kernfs_notify() */ }; /* -- cgit v1.2.3 From 5e8351de740d9eff26cc146a6591a4e7517496b0 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 30 Jun 2014 20:31:13 +0800 Subject: ASoC: add RT5670 CODEC driver This patch adds a minimum support of Realtek ALC5670 codec. Signed-off-by: Bard Liao Signed-off-by: Mark Brown --- include/sound/rt5670.h | 27 + sound/soc/codecs/Kconfig | 6 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/rt5670-dsp.h | 54 + sound/soc/codecs/rt5670.c | 2692 +++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/rt5670.h | 2000 ++++++++++++++++++++++++++++++ 6 files changed, 4781 insertions(+) create mode 100644 include/sound/rt5670.h create mode 100644 sound/soc/codecs/rt5670-dsp.h create mode 100644 sound/soc/codecs/rt5670.c create mode 100644 sound/soc/codecs/rt5670.h (limited to 'include') diff --git a/include/sound/rt5670.h b/include/sound/rt5670.h new file mode 100644 index 000000000000..bd311197a3b5 --- /dev/null +++ b/include/sound/rt5670.h @@ -0,0 +1,27 @@ +/* + * linux/sound/rt5670.h -- Platform data for RT5670 + * + * Copyright 2014 Realtek Microelectronics + * + * 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_SND_RT5670_H +#define __LINUX_SND_RT5670_H + +struct rt5670_platform_data { + int jd_mode; + bool in2_diff; + + bool dmic_en; + unsigned int dmic1_data_pin; + /* 0 = GPIO6; 1 = IN2P; 3 = GPIO7*/ + unsigned int dmic2_data_pin; + /* 0 = GPIO8; 1 = IN3N; */ + unsigned int dmic3_data_pin; + /* 0 = GPIO9; 1 = GPIO10; 2 = GPIO5*/ +}; + +#endif diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 9d88845dc7c4..f31b132e75c5 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -78,6 +78,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_RT5640 if I2C select SND_SOC_RT5645 if I2C select SND_SOC_RT5651 if I2C + select SND_SOC_RT5670 if I2C select SND_SOC_RT5677 if I2C select SND_SOC_SGTL5000 if I2C select SND_SOC_SI476X if MFD_SI476X_CORE @@ -445,10 +446,12 @@ config SND_SOC_RL6231 default y if SND_SOC_RT5640=y default y if SND_SOC_RT5645=y default y if SND_SOC_RT5651=y + default y if SND_SOC_RT5670=y default y if SND_SOC_RT5677=y default m if SND_SOC_RT5640=m default m if SND_SOC_RT5645=m default m if SND_SOC_RT5651=m + default m if SND_SOC_RT5670=m default m if SND_SOC_RT5677=m config SND_SOC_RT5631 @@ -463,6 +466,9 @@ config SND_SOC_RT5645 config SND_SOC_RT5651 tristate +config SND_SOC_RT5670 + tristate + config SND_SOC_RT5677 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index be3377b8d73f..c6648789073b 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -72,6 +72,7 @@ snd-soc-rt5631-objs := rt5631.o snd-soc-rt5640-objs := rt5640.o snd-soc-rt5645-objs := rt5645.o snd-soc-rt5651-objs := rt5651.o +snd-soc-rt5670-objs := rt5670.o snd-soc-rt5677-objs := rt5677.o snd-soc-sgtl5000-objs := sgtl5000.o snd-soc-alc5623-objs := alc5623.o @@ -237,6 +238,7 @@ obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o obj-$(CONFIG_SND_SOC_RT5645) += snd-soc-rt5645.o obj-$(CONFIG_SND_SOC_RT5651) += snd-soc-rt5651.o +obj-$(CONFIG_SND_SOC_RT5670) += snd-soc-rt5670.o obj-$(CONFIG_SND_SOC_RT5677) += snd-soc-rt5677.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o diff --git a/sound/soc/codecs/rt5670-dsp.h b/sound/soc/codecs/rt5670-dsp.h new file mode 100644 index 000000000000..a34d0cdb8198 --- /dev/null +++ b/sound/soc/codecs/rt5670-dsp.h @@ -0,0 +1,54 @@ +/* + * rt5670-dsp.h -- RT5670 ALSA SoC DSP driver + * + * Copyright 2014 Realtek Microelectronics + * Author: Bard Liao + * + * 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 __RT5670_DSP_H__ +#define __RT5670_DSP_H__ + +#define RT5670_DSP_CTRL1 0xe0 +#define RT5670_DSP_CTRL2 0xe1 +#define RT5670_DSP_CTRL3 0xe2 +#define RT5670_DSP_CTRL4 0xe3 +#define RT5670_DSP_CTRL5 0xe4 + +/* DSP Control 1 (0xe0) */ +#define RT5670_DSP_CMD_MASK (0xff << 8) +#define RT5670_DSP_CMD_PE (0x0d << 8) /* Patch Entry */ +#define RT5670_DSP_CMD_MW (0x3b << 8) /* Memory Write */ +#define RT5670_DSP_CMD_MR (0x37 << 8) /* Memory Read */ +#define RT5670_DSP_CMD_RR (0x60 << 8) /* Register Read */ +#define RT5670_DSP_CMD_RW (0x68 << 8) /* Register Write */ +#define RT5670_DSP_REG_DATHI (0x26 << 8) /* High Data Addr */ +#define RT5670_DSP_REG_DATLO (0x25 << 8) /* Low Data Addr */ +#define RT5670_DSP_CLK_MASK (0x3 << 6) +#define RT5670_DSP_CLK_SFT 6 +#define RT5670_DSP_CLK_768K (0x0 << 6) +#define RT5670_DSP_CLK_384K (0x1 << 6) +#define RT5670_DSP_CLK_192K (0x2 << 6) +#define RT5670_DSP_CLK_96K (0x3 << 6) +#define RT5670_DSP_BUSY_MASK (0x1 << 5) +#define RT5670_DSP_RW_MASK (0x1 << 4) +#define RT5670_DSP_DL_MASK (0x3 << 2) +#define RT5670_DSP_DL_0 (0x0 << 2) +#define RT5670_DSP_DL_1 (0x1 << 2) +#define RT5670_DSP_DL_2 (0x2 << 2) +#define RT5670_DSP_DL_3 (0x3 << 2) +#define RT5670_DSP_I2C_AL_16 (0x1 << 1) +#define RT5670_DSP_CMD_EN (0x1) + +struct rt5670_dsp_param { + u16 cmd_fmt; + u16 addr; + u16 data; + u8 cmd; +}; + +#endif /* __RT5670_DSP_H__ */ + diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c new file mode 100644 index 000000000000..879d42e1a1bd --- /dev/null +++ b/sound/soc/codecs/rt5670.c @@ -0,0 +1,2692 @@ +/* + * rt5670.c -- RT5670 ALSA SoC audio codec driver + * + * Copyright 2014 Realtek Semiconductor Corp. + * Author: Bard Liao + * + * 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 "rl6231.h" +#include "rt5670.h" +#include "rt5670-dsp.h" + +#define RT5670_DEVICE_ID 0x6271 + +#define RT5670_PR_RANGE_BASE (0xff + 1) +#define RT5670_PR_SPACING 0x100 + +#define RT5670_PR_BASE (RT5670_PR_RANGE_BASE + (0 * RT5670_PR_SPACING)) + +static const struct regmap_range_cfg rt5670_ranges[] = { + { .name = "PR", .range_min = RT5670_PR_BASE, + .range_max = RT5670_PR_BASE + 0xf8, + .selector_reg = RT5670_PRIV_INDEX, + .selector_mask = 0xff, + .selector_shift = 0x0, + .window_start = RT5670_PRIV_DATA, + .window_len = 0x1, }, +}; + +static struct reg_default init_list[] = { + { RT5670_PR_BASE + 0x14, 0x9a8a }, + { RT5670_PR_BASE + 0x38, 0x3ba1 }, + { RT5670_PR_BASE + 0x3d, 0x3640 }, +}; +#define RT5670_INIT_REG_LEN ARRAY_SIZE(init_list) + +static const struct reg_default rt5670_reg[] = { + { 0x00, 0x0000 }, + { 0x02, 0x8888 }, + { 0x03, 0x8888 }, + { 0x0a, 0x0001 }, + { 0x0b, 0x0827 }, + { 0x0c, 0x0000 }, + { 0x0d, 0x0008 }, + { 0x0e, 0x0000 }, + { 0x0f, 0x0808 }, + { 0x19, 0xafaf }, + { 0x1a, 0xafaf }, + { 0x1b, 0x0011 }, + { 0x1c, 0x2f2f }, + { 0x1d, 0x2f2f }, + { 0x1e, 0x0000 }, + { 0x1f, 0x2f2f }, + { 0x20, 0x0000 }, + { 0x26, 0x7860 }, + { 0x27, 0x7860 }, + { 0x28, 0x7871 }, + { 0x29, 0x8080 }, + { 0x2a, 0x5656 }, + { 0x2b, 0x5454 }, + { 0x2c, 0xaaa0 }, + { 0x2d, 0x0000 }, + { 0x2e, 0x2f2f }, + { 0x2f, 0x1002 }, + { 0x30, 0x0000 }, + { 0x31, 0x5f00 }, + { 0x32, 0x0000 }, + { 0x33, 0x0000 }, + { 0x34, 0x0000 }, + { 0x35, 0x0000 }, + { 0x36, 0x0000 }, + { 0x37, 0x0000 }, + { 0x38, 0x0000 }, + { 0x3b, 0x0000 }, + { 0x3c, 0x007f }, + { 0x3d, 0x0000 }, + { 0x3e, 0x007f }, + { 0x45, 0xe00f }, + { 0x4c, 0x5380 }, + { 0x4f, 0x0073 }, + { 0x52, 0x00d3 }, + { 0x53, 0xf0f0 }, + { 0x61, 0x0000 }, + { 0x62, 0x0001 }, + { 0x63, 0x00c3 }, + { 0x64, 0x0000 }, + { 0x65, 0x0000 }, + { 0x66, 0x0000 }, + { 0x6f, 0x8000 }, + { 0x70, 0x8000 }, + { 0x71, 0x8000 }, + { 0x72, 0x8000 }, + { 0x73, 0x1110 }, + { 0x74, 0x0e00 }, + { 0x75, 0x1505 }, + { 0x76, 0x0015 }, + { 0x77, 0x0c00 }, + { 0x78, 0x4000 }, + { 0x79, 0x0123 }, + { 0x7f, 0x1100 }, + { 0x80, 0x0000 }, + { 0x81, 0x0000 }, + { 0x82, 0x0000 }, + { 0x83, 0x0000 }, + { 0x84, 0x0000 }, + { 0x85, 0x0000 }, + { 0x86, 0x0008 }, + { 0x87, 0x0000 }, + { 0x88, 0x0000 }, + { 0x89, 0x0000 }, + { 0x8a, 0x0000 }, + { 0x8b, 0x0000 }, + { 0x8c, 0x0007 }, + { 0x8d, 0x0000 }, + { 0x8e, 0x0004 }, + { 0x8f, 0x1100 }, + { 0x90, 0x0646 }, + { 0x91, 0x0c06 }, + { 0x93, 0x0000 }, + { 0x94, 0x0000 }, + { 0x95, 0x0000 }, + { 0x97, 0x0000 }, + { 0x98, 0x0000 }, + { 0x99, 0x0000 }, + { 0x9a, 0x2184 }, + { 0x9b, 0x010a }, + { 0x9c, 0x0aea }, + { 0x9d, 0x000c }, + { 0x9e, 0x0400 }, + { 0xae, 0x7000 }, + { 0xaf, 0x0000 }, + { 0xb0, 0x6000 }, + { 0xb1, 0x0000 }, + { 0xb2, 0x0000 }, + { 0xb3, 0x001f }, + { 0xb4, 0x2206 }, + { 0xb5, 0x1f00 }, + { 0xb6, 0x0000 }, + { 0xb7, 0x0000 }, + { 0xbb, 0x0000 }, + { 0xbc, 0x0000 }, + { 0xbd, 0x0000 }, + { 0xbe, 0x0000 }, + { 0xbf, 0x0000 }, + { 0xc0, 0x0000 }, + { 0xc1, 0x0000 }, + { 0xc2, 0x0000 }, + { 0xcd, 0x0000 }, + { 0xce, 0x0000 }, + { 0xcf, 0x1813 }, + { 0xd0, 0x0690 }, + { 0xd1, 0x1c17 }, + { 0xd3, 0xb320 }, + { 0xd4, 0x0000 }, + { 0xd6, 0x0400 }, + { 0xd9, 0x0809 }, + { 0xda, 0x0000 }, + { 0xdb, 0x0001 }, + { 0xdc, 0x0049 }, + { 0xdd, 0x0009 }, + { 0xe6, 0x8000 }, + { 0xe7, 0x0000 }, + { 0xec, 0xb300 }, + { 0xed, 0x0000 }, + { 0xee, 0xb300 }, + { 0xef, 0x0000 }, + { 0xf8, 0x0000 }, + { 0xf9, 0x0000 }, + { 0xfa, 0x8010 }, + { 0xfb, 0x0033 }, + { 0xfc, 0x0080 }, +}; + +static bool rt5670_volatile_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rt5670_ranges); i++) { + if ((reg >= rt5670_ranges[i].window_start && + reg <= rt5670_ranges[i].window_start + + rt5670_ranges[i].window_len) || + (reg >= rt5670_ranges[i].range_min && + reg <= rt5670_ranges[i].range_max)) { + return true; + } + } + + switch (reg) { + case RT5670_RESET: + case RT5670_PDM_DATA_CTRL1: + case RT5670_PDM1_DATA_CTRL4: + case RT5670_PDM2_DATA_CTRL4: + case RT5670_PRIV_DATA: + case RT5670_ASRC_5: + case RT5670_CJ_CTRL1: + case RT5670_CJ_CTRL2: + case RT5670_CJ_CTRL3: + case RT5670_A_JD_CTRL1: + case RT5670_A_JD_CTRL2: + case RT5670_VAD_CTRL5: + case RT5670_ADC_EQ_CTRL1: + case RT5670_EQ_CTRL1: + case RT5670_ALC_CTRL_1: + case RT5670_IRQ_CTRL1: + case RT5670_IRQ_CTRL2: + case RT5670_INT_IRQ_ST: + case RT5670_IL_CMD: + case RT5670_DSP_CTRL1: + case RT5670_DSP_CTRL2: + case RT5670_DSP_CTRL3: + case RT5670_DSP_CTRL4: + case RT5670_DSP_CTRL5: + case RT5670_VENDOR_ID: + case RT5670_VENDOR_ID1: + case RT5670_VENDOR_ID2: + return true; + default: + return false; + } +} + +static bool rt5670_readable_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rt5670_ranges); i++) { + if ((reg >= rt5670_ranges[i].window_start && + reg <= rt5670_ranges[i].window_start + + rt5670_ranges[i].window_len) || + (reg >= rt5670_ranges[i].range_min && + reg <= rt5670_ranges[i].range_max)) { + return true; + } + } + + switch (reg) { + case RT5670_RESET: + case RT5670_HP_VOL: + case RT5670_LOUT1: + case RT5670_CJ_CTRL1: + case RT5670_CJ_CTRL2: + case RT5670_CJ_CTRL3: + case RT5670_IN2: + case RT5670_INL1_INR1_VOL: + case RT5670_DAC1_DIG_VOL: + case RT5670_DAC2_DIG_VOL: + case RT5670_DAC_CTRL: + case RT5670_STO1_ADC_DIG_VOL: + case RT5670_MONO_ADC_DIG_VOL: + case RT5670_STO2_ADC_DIG_VOL: + case RT5670_ADC_BST_VOL1: + case RT5670_ADC_BST_VOL2: + case RT5670_STO2_ADC_MIXER: + case RT5670_STO1_ADC_MIXER: + case RT5670_MONO_ADC_MIXER: + case RT5670_AD_DA_MIXER: + case RT5670_STO_DAC_MIXER: + case RT5670_DD_MIXER: + case RT5670_DIG_MIXER: + case RT5670_DSP_PATH1: + case RT5670_DSP_PATH2: + case RT5670_DIG_INF1_DATA: + case RT5670_DIG_INF2_DATA: + case RT5670_PDM_OUT_CTRL: + case RT5670_PDM_DATA_CTRL1: + case RT5670_PDM1_DATA_CTRL2: + case RT5670_PDM1_DATA_CTRL3: + case RT5670_PDM1_DATA_CTRL4: + case RT5670_PDM2_DATA_CTRL2: + case RT5670_PDM2_DATA_CTRL3: + case RT5670_PDM2_DATA_CTRL4: + case RT5670_REC_L1_MIXER: + case RT5670_REC_L2_MIXER: + case RT5670_REC_R1_MIXER: + case RT5670_REC_R2_MIXER: + case RT5670_HPO_MIXER: + case RT5670_MONO_MIXER: + case RT5670_OUT_L1_MIXER: + case RT5670_OUT_R1_MIXER: + case RT5670_LOUT_MIXER: + case RT5670_PWR_DIG1: + case RT5670_PWR_DIG2: + case RT5670_PWR_ANLG1: + case RT5670_PWR_ANLG2: + case RT5670_PWR_MIXER: + case RT5670_PWR_VOL: + case RT5670_PRIV_INDEX: + case RT5670_PRIV_DATA: + case RT5670_I2S4_SDP: + case RT5670_I2S1_SDP: + case RT5670_I2S2_SDP: + case RT5670_I2S3_SDP: + case RT5670_ADDA_CLK1: + case RT5670_ADDA_CLK2: + case RT5670_DMIC_CTRL1: + case RT5670_DMIC_CTRL2: + case RT5670_TDM_CTRL_1: + case RT5670_TDM_CTRL_2: + case RT5670_TDM_CTRL_3: + case RT5670_DSP_CLK: + case RT5670_GLB_CLK: + case RT5670_PLL_CTRL1: + case RT5670_PLL_CTRL2: + case RT5670_ASRC_1: + case RT5670_ASRC_2: + case RT5670_ASRC_3: + case RT5670_ASRC_4: + case RT5670_ASRC_5: + case RT5670_ASRC_7: + case RT5670_ASRC_8: + case RT5670_ASRC_9: + case RT5670_ASRC_10: + case RT5670_ASRC_11: + case RT5670_ASRC_12: + case RT5670_ASRC_13: + case RT5670_ASRC_14: + case RT5670_DEPOP_M1: + case RT5670_DEPOP_M2: + case RT5670_DEPOP_M3: + case RT5670_CHARGE_PUMP: + case RT5670_MICBIAS: + case RT5670_A_JD_CTRL1: + case RT5670_A_JD_CTRL2: + case RT5670_VAD_CTRL1: + case RT5670_VAD_CTRL2: + case RT5670_VAD_CTRL3: + case RT5670_VAD_CTRL4: + case RT5670_VAD_CTRL5: + case RT5670_ADC_EQ_CTRL1: + case RT5670_ADC_EQ_CTRL2: + case RT5670_EQ_CTRL1: + case RT5670_EQ_CTRL2: + case RT5670_ALC_DRC_CTRL1: + case RT5670_ALC_DRC_CTRL2: + case RT5670_ALC_CTRL_1: + case RT5670_ALC_CTRL_2: + case RT5670_ALC_CTRL_3: + case RT5670_JD_CTRL: + case RT5670_IRQ_CTRL1: + case RT5670_IRQ_CTRL2: + case RT5670_INT_IRQ_ST: + case RT5670_GPIO_CTRL1: + case RT5670_GPIO_CTRL2: + case RT5670_GPIO_CTRL3: + case RT5670_SCRABBLE_FUN: + case RT5670_SCRABBLE_CTRL: + case RT5670_BASE_BACK: + case RT5670_MP3_PLUS1: + case RT5670_MP3_PLUS2: + case RT5670_ADJ_HPF1: + case RT5670_ADJ_HPF2: + case RT5670_HP_CALIB_AMP_DET: + case RT5670_SV_ZCD1: + case RT5670_SV_ZCD2: + case RT5670_IL_CMD: + case RT5670_IL_CMD2: + case RT5670_IL_CMD3: + case RT5670_DRC_HL_CTRL1: + case RT5670_DRC_HL_CTRL2: + case RT5670_ADC_MONO_HP_CTRL1: + case RT5670_ADC_MONO_HP_CTRL2: + case RT5670_ADC_STO2_HP_CTRL1: + case RT5670_ADC_STO2_HP_CTRL2: + case RT5670_JD_CTRL3: + case RT5670_JD_CTRL4: + case RT5670_DIG_MISC: + case RT5670_DSP_CTRL1: + case RT5670_DSP_CTRL2: + case RT5670_DSP_CTRL3: + case RT5670_DSP_CTRL4: + case RT5670_DSP_CTRL5: + case RT5670_GEN_CTRL2: + case RT5670_GEN_CTRL3: + case RT5670_VENDOR_ID: + case RT5670_VENDOR_ID1: + case RT5670_VENDOR_ID2: + return true; + default: + return false; + } +} + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0); +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0); +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0); +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0); +static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); + +/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ +static unsigned int bst_tlv[] = { + TLV_DB_RANGE_HEAD(7), + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), + 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0), + 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0), + 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0), +}; + +/* Interface data select */ +static const char * const rt5670_data_select[] = { + "Normal", "Swap", "left copy to right", "right copy to left" +}; + +static const SOC_ENUM_SINGLE_DECL(rt5670_if2_dac_enum, RT5670_DIG_INF1_DATA, + RT5670_IF2_DAC_SEL_SFT, rt5670_data_select); + +static const SOC_ENUM_SINGLE_DECL(rt5670_if2_adc_enum, RT5670_DIG_INF1_DATA, + RT5670_IF2_ADC_SEL_SFT, rt5670_data_select); + +static const struct snd_kcontrol_new rt5670_snd_controls[] = { + /* Headphone Output Volume */ + SOC_DOUBLE("HP Playback Switch", RT5670_HP_VOL, + RT5670_L_MUTE_SFT, RT5670_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("HP Playback Volume", RT5670_HP_VOL, + RT5670_L_VOL_SFT, RT5670_R_VOL_SFT, + 39, 0, out_vol_tlv), + /* OUTPUT Control */ + SOC_DOUBLE("OUT Channel Switch", RT5670_LOUT1, + RT5670_VOL_L_SFT, RT5670_VOL_R_SFT, 1, 1), + SOC_DOUBLE_TLV("OUT Playback Volume", RT5670_LOUT1, + RT5670_L_VOL_SFT, RT5670_R_VOL_SFT, 39, 1, out_vol_tlv), + /* DAC Digital Volume */ + SOC_DOUBLE("DAC2 Playback Switch", RT5670_DAC_CTRL, + RT5670_M_DAC_L2_VOL_SFT, RT5670_M_DAC_R2_VOL_SFT, 1, 1), + SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5670_DAC1_DIG_VOL, + RT5670_L_VOL_SFT, RT5670_R_VOL_SFT, + 175, 0, dac_vol_tlv), + SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5670_DAC2_DIG_VOL, + RT5670_L_VOL_SFT, RT5670_R_VOL_SFT, + 175, 0, dac_vol_tlv), + /* IN1/IN2 Control */ + SOC_SINGLE_TLV("IN1 Boost Volume", RT5670_CJ_CTRL1, + RT5670_BST_SFT1, 8, 0, bst_tlv), + SOC_SINGLE_TLV("IN2 Boost Volume", RT5670_IN2, + RT5670_BST_SFT1, 8, 0, bst_tlv), + /* INL/INR Volume Control */ + SOC_DOUBLE_TLV("IN Capture Volume", RT5670_INL1_INR1_VOL, + RT5670_INL_VOL_SFT, RT5670_INR_VOL_SFT, + 31, 1, in_vol_tlv), + /* ADC Digital Volume Control */ + SOC_DOUBLE("ADC Capture Switch", RT5670_STO1_ADC_DIG_VOL, + RT5670_L_MUTE_SFT, RT5670_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("ADC Capture Volume", RT5670_STO1_ADC_DIG_VOL, + RT5670_L_VOL_SFT, RT5670_R_VOL_SFT, + 127, 0, adc_vol_tlv), + + SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5670_MONO_ADC_DIG_VOL, + RT5670_L_VOL_SFT, RT5670_R_VOL_SFT, + 127, 0, adc_vol_tlv), + + /* ADC Boost Volume Control */ + SOC_DOUBLE_TLV("STO1 ADC Boost Gain Volume", RT5670_ADC_BST_VOL1, + RT5670_STO1_ADC_L_BST_SFT, RT5670_STO1_ADC_R_BST_SFT, + 3, 0, adc_bst_tlv), + + SOC_DOUBLE_TLV("STO2 ADC Boost Gain Volume", RT5670_ADC_BST_VOL1, + RT5670_STO2_ADC_L_BST_SFT, RT5670_STO2_ADC_R_BST_SFT, + 3, 0, adc_bst_tlv), + + SOC_ENUM("ADC IF2 Data Switch", rt5670_if2_adc_enum), + SOC_ENUM("DAC IF2 Data Switch", rt5670_if2_dac_enum), +}; + +/** + * set_dmic_clk - Set parameter of dmic. + * + * @w: DAPM widget. + * @kcontrol: The kcontrol of this widget. + * @event: Event id. + * + * Choose dmic clock between 1MHz and 3MHz. + * It is better for clock to approximate 3MHz. + */ +static int set_dmic_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); + int idx = -EINVAL; + + idx = rl6231_calc_dmic_clk(rt5670->sysclk); + + if (idx < 0) + dev_err(codec->dev, "Failed to set DMIC clock\n"); + else + snd_soc_update_bits(codec, RT5670_DMIC_CTRL1, + RT5670_DMIC_CLK_MASK, idx << RT5670_DMIC_CLK_SFT); + return idx; +} + +static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + unsigned int val; + + val = snd_soc_read(source->codec, RT5670_GLB_CLK); + val &= RT5670_SCLK_SRC_MASK; + if (val == RT5670_SCLK_SRC_PLL1) + return 1; + else + return 0; +} + +static int is_using_asrc(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + unsigned int reg, shift, val; + + switch (source->shift) { + case 0: + reg = RT5670_ASRC_3; + shift = 0; + break; + case 1: + reg = RT5670_ASRC_3; + shift = 4; + break; + case 2: + reg = RT5670_ASRC_5; + shift = 12; + break; + case 3: + reg = RT5670_ASRC_2; + shift = 0; + break; + case 8: + reg = RT5670_ASRC_2; + shift = 4; + break; + case 9: + reg = RT5670_ASRC_2; + shift = 8; + break; + case 10: + reg = RT5670_ASRC_2; + shift = 12; + break; + default: + return 0; + } + + val = (snd_soc_read(source->codec, reg) >> shift) & 0xf; + switch (val) { + case 1: + case 2: + case 3: + case 4: + return 1; + default: + return 0; + } + +} + +/* Digital Mixer */ +static const struct snd_kcontrol_new rt5670_sto1_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5670_STO1_ADC_MIXER, + RT5670_M_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5670_STO1_ADC_MIXER, + RT5670_M_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_sto1_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5670_STO1_ADC_MIXER, + RT5670_M_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5670_STO1_ADC_MIXER, + RT5670_M_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_sto2_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5670_STO2_ADC_MIXER, + RT5670_M_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5670_STO2_ADC_MIXER, + RT5670_M_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_sto2_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5670_STO2_ADC_MIXER, + RT5670_M_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5670_STO2_ADC_MIXER, + RT5670_M_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_mono_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5670_MONO_ADC_MIXER, + RT5670_M_MONO_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5670_MONO_ADC_MIXER, + RT5670_M_MONO_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_mono_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5670_MONO_ADC_MIXER, + RT5670_M_MONO_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5670_MONO_ADC_MIXER, + RT5670_M_MONO_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_dac_l_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5670_AD_DA_MIXER, + RT5670_M_ADCMIX_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5670_AD_DA_MIXER, + RT5670_M_DAC1_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_dac_r_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5670_AD_DA_MIXER, + RT5670_M_ADCMIX_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5670_AD_DA_MIXER, + RT5670_M_DAC1_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_sto_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5670_STO_DAC_MIXER, + RT5670_M_DAC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5670_STO_DAC_MIXER, + RT5670_M_DAC_L2_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5670_STO_DAC_MIXER, + RT5670_M_DAC_R1_STO_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_sto_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5670_STO_DAC_MIXER, + RT5670_M_DAC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5670_STO_DAC_MIXER, + RT5670_M_DAC_R2_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L1 Switch", RT5670_STO_DAC_MIXER, + RT5670_M_DAC_L1_STO_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_mono_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5670_DD_MIXER, + RT5670_M_DAC_L1_MONO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5670_DD_MIXER, + RT5670_M_DAC_L2_MONO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5670_DD_MIXER, + RT5670_M_DAC_R2_MONO_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_mono_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5670_DD_MIXER, + RT5670_M_DAC_R1_MONO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5670_DD_MIXER, + RT5670_M_DAC_R2_MONO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5670_DD_MIXER, + RT5670_M_DAC_L2_MONO_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_dig_l_mix[] = { + SOC_DAPM_SINGLE("Sto DAC Mix L Switch", RT5670_DIG_MIXER, + RT5670_M_STO_L_DAC_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5670_DIG_MIXER, + RT5670_M_DAC_L2_DAC_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5670_DIG_MIXER, + RT5670_M_DAC_R2_DAC_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_dig_r_mix[] = { + SOC_DAPM_SINGLE("Sto DAC Mix R Switch", RT5670_DIG_MIXER, + RT5670_M_STO_R_DAC_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5670_DIG_MIXER, + RT5670_M_DAC_R2_DAC_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5670_DIG_MIXER, + RT5670_M_DAC_L2_DAC_R_SFT, 1, 1), +}; + +/* Analog Input Mixer */ +static const struct snd_kcontrol_new rt5670_rec_l_mix[] = { + SOC_DAPM_SINGLE("INL Switch", RT5670_REC_L2_MIXER, + RT5670_M_IN_L_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5670_REC_L2_MIXER, + RT5670_M_BST2_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5670_REC_L2_MIXER, + RT5670_M_BST1_RM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_rec_r_mix[] = { + SOC_DAPM_SINGLE("INR Switch", RT5670_REC_R2_MIXER, + RT5670_M_IN_R_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5670_REC_R2_MIXER, + RT5670_M_BST2_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5670_REC_R2_MIXER, + RT5670_M_BST1_RM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_out_l_mix[] = { + SOC_DAPM_SINGLE("BST1 Switch", RT5670_OUT_L1_MIXER, + RT5670_M_BST1_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5670_OUT_L1_MIXER, + RT5670_M_IN_L_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5670_OUT_L1_MIXER, + RT5670_M_DAC_L2_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L1 Switch", RT5670_OUT_L1_MIXER, + RT5670_M_DAC_L1_OM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_out_r_mix[] = { + SOC_DAPM_SINGLE("BST2 Switch", RT5670_OUT_R1_MIXER, + RT5670_M_BST2_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5670_OUT_R1_MIXER, + RT5670_M_IN_R_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5670_OUT_R1_MIXER, + RT5670_M_DAC_R2_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5670_OUT_R1_MIXER, + RT5670_M_DAC_R1_OM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_hpo_mix[] = { + SOC_DAPM_SINGLE("DAC1 Switch", RT5670_HPO_MIXER, + RT5670_M_DAC1_HM_SFT, 1, 1), + SOC_DAPM_SINGLE("HPVOL Switch", RT5670_HPO_MIXER, + RT5670_M_HPVOL_HM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_hpvoll_mix[] = { + SOC_DAPM_SINGLE("DAC1 Switch", RT5670_HPO_MIXER, + RT5670_M_DACL1_HML_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5670_HPO_MIXER, + RT5670_M_INL1_HML_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_hpvolr_mix[] = { + SOC_DAPM_SINGLE("DAC1 Switch", RT5670_HPO_MIXER, + RT5670_M_DACR1_HMR_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5670_HPO_MIXER, + RT5670_M_INR1_HMR_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_lout_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5670_LOUT_MIXER, + RT5670_M_DAC_L1_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5670_LOUT_MIXER, + RT5670_M_DAC_R1_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTMIX L Switch", RT5670_LOUT_MIXER, + RT5670_M_OV_L_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTMIX R Switch", RT5670_LOUT_MIXER, + RT5670_M_OV_R_LM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_hpl_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5670_HPO_MIXER, + RT5670_M_DACL1_HML_SFT, 1, 1), + SOC_DAPM_SINGLE("INL1 Switch", RT5670_HPO_MIXER, + RT5670_M_INL1_HML_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_hpr_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5670_HPO_MIXER, + RT5670_M_DACR1_HMR_SFT, 1, 1), + SOC_DAPM_SINGLE("INR1 Switch", RT5670_HPO_MIXER, + RT5670_M_INR1_HMR_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new lout_l_enable_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5670_LOUT1, + RT5670_L_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new lout_r_enable_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5670_LOUT1, + RT5670_R_MUTE_SFT, 1, 1); + +/* DAC1 L/R source */ /* MX-29 [9:8] [11:10] */ +static const char * const rt5670_dac1_src[] = { + "IF1 DAC", "IF2 DAC" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5670_dac1l_enum, RT5670_AD_DA_MIXER, + RT5670_DAC1_L_SEL_SFT, rt5670_dac1_src); + +static const struct snd_kcontrol_new rt5670_dac1l_mux = + SOC_DAPM_ENUM("DAC1 L source", rt5670_dac1l_enum); + +static const SOC_ENUM_SINGLE_DECL( + rt5670_dac1r_enum, RT5670_AD_DA_MIXER, + RT5670_DAC1_R_SEL_SFT, rt5670_dac1_src); + +static const struct snd_kcontrol_new rt5670_dac1r_mux = + SOC_DAPM_ENUM("DAC1 R source", rt5670_dac1r_enum); + +/*DAC2 L/R source*/ /* MX-1B [6:4] [2:0] */ +/* TODO Use SOC_VALUE_ENUM_SINGLE_DECL */ +static const char * const rt5670_dac12_src[] = { + "IF1 DAC", "IF2 DAC", "IF3 DAC", "TxDC DAC", + "Bass", "VAD_ADC", "IF4 DAC" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5670_dac2l_enum, RT5670_DAC_CTRL, + RT5670_DAC2_L_SEL_SFT, rt5670_dac12_src); + +static const struct snd_kcontrol_new rt5670_dac_l2_mux = + SOC_DAPM_ENUM("DAC2 L source", rt5670_dac2l_enum); + +static const char * const rt5670_dacr2_src[] = { + "IF1 DAC", "IF2 DAC", "IF3 DAC", "TxDC DAC", "TxDP ADC", "IF4 DAC" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5670_dac2r_enum, RT5670_DAC_CTRL, + RT5670_DAC2_R_SEL_SFT, rt5670_dacr2_src); + +static const struct snd_kcontrol_new rt5670_dac_r2_mux = + SOC_DAPM_ENUM("DAC2 R source", rt5670_dac2r_enum); + +/*RxDP source*/ /* MX-2D [15:13] */ +static const char * const rt5670_rxdp_src[] = { + "IF2 DAC", "IF1 DAC", "STO1 ADC Mixer", "STO2 ADC Mixer", + "Mono ADC Mixer L", "Mono ADC Mixer R", "DAC1" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5670_rxdp_enum, RT5670_DSP_PATH1, + RT5670_RXDP_SEL_SFT, rt5670_rxdp_src); + +static const struct snd_kcontrol_new rt5670_rxdp_mux = + SOC_DAPM_ENUM("DAC2 L source", rt5670_rxdp_enum); + +/* MX-2D [1] [0] */ +static const char * const rt5670_dsp_bypass_src[] = { + "DSP", "Bypass" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5670_dsp_ul_enum, RT5670_DSP_PATH1, + RT5670_DSP_UL_SFT, rt5670_dsp_bypass_src); + +static const struct snd_kcontrol_new rt5670_dsp_ul_mux = + SOC_DAPM_ENUM("DSP UL source", rt5670_dsp_ul_enum); + +static const SOC_ENUM_SINGLE_DECL( + rt5670_dsp_dl_enum, RT5670_DSP_PATH1, + RT5670_DSP_DL_SFT, rt5670_dsp_bypass_src); + +static const struct snd_kcontrol_new rt5670_dsp_dl_mux = + SOC_DAPM_ENUM("DSP DL source", rt5670_dsp_dl_enum); + +/* Stereo2 ADC source */ +/* MX-26 [15] */ +static const char * const rt5670_stereo2_adc_lr_src[] = { + "L", "LR" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5670_stereo2_adc_lr_enum, RT5670_STO2_ADC_MIXER, + RT5670_STO2_ADC_SRC_SFT, rt5670_stereo2_adc_lr_src); + +static const struct snd_kcontrol_new rt5670_sto2_adc_lr_mux = + SOC_DAPM_ENUM("Stereo2 ADC LR source", rt5670_stereo2_adc_lr_enum); + +/* Stereo1 ADC source */ +/* MX-27 MX-26 [12] */ +static const char * const rt5670_stereo_adc1_src[] = { + "DAC MIX", "ADC" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5670_stereo1_adc1_enum, RT5670_STO1_ADC_MIXER, + RT5670_ADC_1_SRC_SFT, rt5670_stereo_adc1_src); + +static const struct snd_kcontrol_new rt5670_sto_adc_l1_mux = + SOC_DAPM_ENUM("Stereo1 ADC L1 source", rt5670_stereo1_adc1_enum); + +static const struct snd_kcontrol_new rt5670_sto_adc_r1_mux = + SOC_DAPM_ENUM("Stereo1 ADC R1 source", rt5670_stereo1_adc1_enum); + +static const SOC_ENUM_SINGLE_DECL( + rt5670_stereo2_adc1_enum, RT5670_STO2_ADC_MIXER, + RT5670_ADC_1_SRC_SFT, rt5670_stereo_adc1_src); + +static const struct snd_kcontrol_new rt5670_sto2_adc_l1_mux = + SOC_DAPM_ENUM("Stereo2 ADC L1 source", rt5670_stereo2_adc1_enum); + +static const struct snd_kcontrol_new rt5670_sto2_adc_r1_mux = + SOC_DAPM_ENUM("Stereo2 ADC R1 source", rt5670_stereo2_adc1_enum); + +/* MX-27 MX-26 [11] */ +static const char * const rt5670_stereo_adc2_src[] = { + "DAC MIX", "DMIC" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5670_stereo1_adc2_enum, RT5670_STO1_ADC_MIXER, + RT5670_ADC_2_SRC_SFT, rt5670_stereo_adc2_src); + +static const struct snd_kcontrol_new rt5670_sto_adc_l2_mux = + SOC_DAPM_ENUM("Stereo1 ADC L2 source", rt5670_stereo1_adc2_enum); + +static const struct snd_kcontrol_new rt5670_sto_adc_r2_mux = + SOC_DAPM_ENUM("Stereo1 ADC R2 source", rt5670_stereo1_adc2_enum); + +static const SOC_ENUM_SINGLE_DECL( + rt5670_stereo2_adc2_enum, RT5670_STO2_ADC_MIXER, + RT5670_ADC_2_SRC_SFT, rt5670_stereo_adc2_src); + +static const struct snd_kcontrol_new rt5670_sto2_adc_l2_mux = + SOC_DAPM_ENUM("Stereo2 ADC L2 source", rt5670_stereo2_adc2_enum); + +static const struct snd_kcontrol_new rt5670_sto2_adc_r2_mux = + SOC_DAPM_ENUM("Stereo2 ADC R2 source", rt5670_stereo2_adc2_enum); + +/* MX-27 MX26 [10] */ +static const char * const rt5670_stereo_adc_src[] = { + "ADC1L ADC2R", "ADC3" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5670_stereo1_adc_enum, RT5670_STO1_ADC_MIXER, + RT5670_ADC_SRC_SFT, rt5670_stereo_adc_src); + +static const struct snd_kcontrol_new rt5670_sto_adc_mux = + SOC_DAPM_ENUM("Stereo1 ADC source", rt5670_stereo1_adc_enum); + +static const SOC_ENUM_SINGLE_DECL( + rt5670_stereo2_adc_enum, RT5670_STO2_ADC_MIXER, + RT5670_ADC_SRC_SFT, rt5670_stereo_adc_src); + +static const struct snd_kcontrol_new rt5670_sto2_adc_mux = + SOC_DAPM_ENUM("Stereo2 ADC source", rt5670_stereo2_adc_enum); + +/* MX-27 MX-26 [9:8] */ +static const char * const rt5670_stereo_dmic_src[] = { + "DMIC1", "DMIC2", "DMIC3" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5670_stereo1_dmic_enum, RT5670_STO1_ADC_MIXER, + RT5670_DMIC_SRC_SFT, rt5670_stereo_dmic_src); + +static const struct snd_kcontrol_new rt5670_sto1_dmic_mux = + SOC_DAPM_ENUM("Stereo1 DMIC source", rt5670_stereo1_dmic_enum); + +static const SOC_ENUM_SINGLE_DECL( + rt5670_stereo2_dmic_enum, RT5670_STO2_ADC_MIXER, + RT5670_DMIC_SRC_SFT, rt5670_stereo_dmic_src); + +static const struct snd_kcontrol_new rt5670_sto2_dmic_mux = + SOC_DAPM_ENUM("Stereo2 DMIC source", rt5670_stereo2_dmic_enum); + +/* MX-27 [0] */ +static const char * const rt5670_stereo_dmic3_src[] = { + "DMIC3", "PDM ADC" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5670_stereo_dmic3_enum, RT5670_STO1_ADC_MIXER, + RT5670_DMIC3_SRC_SFT, rt5670_stereo_dmic3_src); + +static const struct snd_kcontrol_new rt5670_sto_dmic3_mux = + SOC_DAPM_ENUM("Stereo DMIC3 source", rt5670_stereo_dmic3_enum); + +/* Mono ADC source */ +/* MX-28 [12] */ +static const char * const rt5670_mono_adc_l1_src[] = { + "Mono DAC MIXL", "ADC1" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5670_mono_adc_l1_enum, RT5670_MONO_ADC_MIXER, + RT5670_MONO_ADC_L1_SRC_SFT, rt5670_mono_adc_l1_src); + +static const struct snd_kcontrol_new rt5670_mono_adc_l1_mux = + SOC_DAPM_ENUM("Mono ADC1 left source", rt5670_mono_adc_l1_enum); +/* MX-28 [11] */ +static const char * const rt5670_mono_adc_l2_src[] = { + "Mono DAC MIXL", "DMIC" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5670_mono_adc_l2_enum, RT5670_MONO_ADC_MIXER, + RT5670_MONO_ADC_L2_SRC_SFT, rt5670_mono_adc_l2_src); + +static const struct snd_kcontrol_new rt5670_mono_adc_l2_mux = + SOC_DAPM_ENUM("Mono ADC2 left source", rt5670_mono_adc_l2_enum); + +/* MX-28 [9:8] */ +static const char * const rt5670_mono_dmic_src[] = { + "DMIC1", "DMIC2", "DMIC3" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5670_mono_dmic_l_enum, RT5670_MONO_ADC_MIXER, + RT5670_MONO_DMIC_L_SRC_SFT, rt5670_mono_dmic_src); + +static const struct snd_kcontrol_new rt5670_mono_dmic_l_mux = + SOC_DAPM_ENUM("Mono DMIC left source", rt5670_mono_dmic_l_enum); +/* MX-28 [1:0] */ +static const SOC_ENUM_SINGLE_DECL( + rt5670_mono_dmic_r_enum, RT5670_MONO_ADC_MIXER, + RT5670_MONO_DMIC_R_SRC_SFT, rt5670_mono_dmic_src); + +static const struct snd_kcontrol_new rt5670_mono_dmic_r_mux = + SOC_DAPM_ENUM("Mono DMIC Right source", rt5670_mono_dmic_r_enum); +/* MX-28 [4] */ +static const char * const rt5670_mono_adc_r1_src[] = { + "Mono DAC MIXR", "ADC2" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5670_mono_adc_r1_enum, RT5670_MONO_ADC_MIXER, + RT5670_MONO_ADC_R1_SRC_SFT, rt5670_mono_adc_r1_src); + +static const struct snd_kcontrol_new rt5670_mono_adc_r1_mux = + SOC_DAPM_ENUM("Mono ADC1 right source", rt5670_mono_adc_r1_enum); +/* MX-28 [3] */ +static const char * const rt5670_mono_adc_r2_src[] = { + "Mono DAC MIXR", "DMIC" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5670_mono_adc_r2_enum, RT5670_MONO_ADC_MIXER, + RT5670_MONO_ADC_R2_SRC_SFT, rt5670_mono_adc_r2_src); + +static const struct snd_kcontrol_new rt5670_mono_adc_r2_mux = + SOC_DAPM_ENUM("Mono ADC2 right source", rt5670_mono_adc_r2_enum); + +/* MX-2D [3:2] */ +static const char * const rt5670_txdp_slot_src[] = { + "Slot 0-1", "Slot 2-3", "Slot 4-5", "Slot 6-7" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5670_txdp_slot_enum, RT5670_DSP_PATH1, + RT5670_TXDP_SLOT_SEL_SFT, rt5670_txdp_slot_src); + +static const struct snd_kcontrol_new rt5670_txdp_slot_mux = + SOC_DAPM_ENUM("TxDP Slot source", rt5670_txdp_slot_enum); + +/* MX-2F [15] */ +static const char * const rt5670_if1_adc2_in_src[] = { + "IF_ADC2", "VAD_ADC" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5670_if1_adc2_in_enum, RT5670_DIG_INF1_DATA, + RT5670_IF1_ADC2_IN_SFT, rt5670_if1_adc2_in_src); + +static const struct snd_kcontrol_new rt5670_if1_adc2_in_mux = + SOC_DAPM_ENUM("IF1 ADC2 IN source", rt5670_if1_adc2_in_enum); + +/* MX-2F [14:12] */ +static const char * const rt5670_if2_adc_in_src[] = { + "IF_ADC1", "IF_ADC2", "IF_ADC3", "TxDC_DAC", "TxDP_ADC", "VAD_ADC" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5670_if2_adc_in_enum, RT5670_DIG_INF1_DATA, + RT5670_IF2_ADC_IN_SFT, rt5670_if2_adc_in_src); + +static const struct snd_kcontrol_new rt5670_if2_adc_in_mux = + SOC_DAPM_ENUM("IF2 ADC IN source", rt5670_if2_adc_in_enum); + +/* MX-30 [5:4] */ +static const char * const rt5670_if4_adc_in_src[] = { + "IF_ADC1", "IF_ADC2", "IF_ADC3" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5670_if4_adc_in_enum, RT5670_DIG_INF2_DATA, + RT5670_IF4_ADC_IN_SFT, rt5670_if4_adc_in_src); + +static const struct snd_kcontrol_new rt5670_if4_adc_in_mux = + SOC_DAPM_ENUM("IF4 ADC IN source", rt5670_if4_adc_in_enum); + +/* MX-31 [15] [13] [11] [9] */ +static const char * const rt5670_pdm_src[] = { + "Mono DAC", "Stereo DAC" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5670_pdm1_l_enum, RT5670_PDM_OUT_CTRL, + RT5670_PDM1_L_SFT, rt5670_pdm_src); + +static const struct snd_kcontrol_new rt5670_pdm1_l_mux = + SOC_DAPM_ENUM("PDM1 L source", rt5670_pdm1_l_enum); + +static const SOC_ENUM_SINGLE_DECL( + rt5670_pdm1_r_enum, RT5670_PDM_OUT_CTRL, + RT5670_PDM1_R_SFT, rt5670_pdm_src); + +static const struct snd_kcontrol_new rt5670_pdm1_r_mux = + SOC_DAPM_ENUM("PDM1 R source", rt5670_pdm1_r_enum); + +static const SOC_ENUM_SINGLE_DECL( + rt5670_pdm2_l_enum, RT5670_PDM_OUT_CTRL, + RT5670_PDM2_L_SFT, rt5670_pdm_src); + +static const struct snd_kcontrol_new rt5670_pdm2_l_mux = + SOC_DAPM_ENUM("PDM2 L source", rt5670_pdm2_l_enum); + +static const SOC_ENUM_SINGLE_DECL( + rt5670_pdm2_r_enum, RT5670_PDM_OUT_CTRL, + RT5670_PDM2_R_SFT, rt5670_pdm_src); + +static const struct snd_kcontrol_new rt5670_pdm2_r_mux = + SOC_DAPM_ENUM("PDM2 R source", rt5670_pdm2_r_enum); + +/* MX-FA [12] */ +static const char * const rt5670_if1_adc1_in1_src[] = { + "IF_ADC1", "IF1_ADC3" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5670_if1_adc1_in1_enum, RT5670_DIG_MISC, + RT5670_IF1_ADC1_IN1_SFT, rt5670_if1_adc1_in1_src); + +static const struct snd_kcontrol_new rt5670_if1_adc1_in1_mux = + SOC_DAPM_ENUM("IF1 ADC1 IN1 source", rt5670_if1_adc1_in1_enum); + +/* MX-FA [11] */ +static const char * const rt5670_if1_adc1_in2_src[] = { + "IF1_ADC1_IN1", "IF1_ADC4" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5670_if1_adc1_in2_enum, RT5670_DIG_MISC, + RT5670_IF1_ADC1_IN2_SFT, rt5670_if1_adc1_in2_src); + +static const struct snd_kcontrol_new rt5670_if1_adc1_in2_mux = + SOC_DAPM_ENUM("IF1 ADC1 IN2 source", rt5670_if1_adc1_in2_enum); + +/* MX-FA [10] */ +static const char * const rt5670_if1_adc2_in1_src[] = { + "IF1_ADC2_IN", "IF1_ADC4" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5670_if1_adc2_in1_enum, RT5670_DIG_MISC, + RT5670_IF1_ADC2_IN1_SFT, rt5670_if1_adc2_in1_src); + +static const struct snd_kcontrol_new rt5670_if1_adc2_in1_mux = + SOC_DAPM_ENUM("IF1 ADC2 IN1 source", rt5670_if1_adc2_in1_enum); + +/* MX-9D [9:8] */ +static const char * const rt5670_vad_adc_src[] = { + "Sto1 ADC L", "Mono ADC L", "Mono ADC R", "Sto2 ADC L" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5670_vad_adc_enum, RT5670_VAD_CTRL4, + RT5670_VAD_SEL_SFT, rt5670_vad_adc_src); + +static const struct snd_kcontrol_new rt5670_vad_adc_mux = + SOC_DAPM_ENUM("VAD ADC source", rt5670_vad_adc_enum); + +static int rt5670_hp_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(rt5670->regmap, RT5670_CHARGE_PUMP, + RT5670_PM_HP_MASK, RT5670_PM_HP_HV); + regmap_update_bits(rt5670->regmap, RT5670_GEN_CTRL2, + 0x0400, 0x0400); + /* headphone amp power on */ + regmap_update_bits(rt5670->regmap, RT5670_PWR_ANLG1, + RT5670_PWR_HA | RT5670_PWR_FV1 | + RT5670_PWR_FV2, RT5670_PWR_HA | + RT5670_PWR_FV1 | RT5670_PWR_FV2); + /* depop parameters */ + regmap_write(rt5670->regmap, RT5670_DEPOP_M2, 0x3100); + regmap_write(rt5670->regmap, RT5670_DEPOP_M1, 0x8009); + regmap_write(rt5670->regmap, RT5670_PR_BASE + + RT5670_HP_DCC_INT1, 0x9f00); + mdelay(20); + regmap_write(rt5670->regmap, RT5670_DEPOP_M1, 0x8019); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt5670->regmap, RT5670_DEPOP_M1, 0x0004); + msleep(30); + break; + default: + return 0; + } + + return 0; +} + +static int rt5670_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* headphone unmute sequence */ + regmap_write(rt5670->regmap, RT5670_PR_BASE + + RT5670_MAMP_INT_REG2, 0xb400); + regmap_write(rt5670->regmap, RT5670_DEPOP_M3, 0x0772); + regmap_write(rt5670->regmap, RT5670_DEPOP_M1, 0x805d); + regmap_write(rt5670->regmap, RT5670_DEPOP_M1, 0x831d); + regmap_update_bits(rt5670->regmap, RT5670_GEN_CTRL2, + 0x0300, 0x0300); + regmap_update_bits(rt5670->regmap, RT5670_HP_VOL, + RT5670_L_MUTE | RT5670_R_MUTE, 0); + msleep(80); + regmap_write(rt5670->regmap, RT5670_DEPOP_M1, 0x8019); + break; + + case SND_SOC_DAPM_PRE_PMD: + /* headphone mute sequence */ + regmap_write(rt5670->regmap, RT5670_PR_BASE + + RT5670_MAMP_INT_REG2, 0xb400); + regmap_write(rt5670->regmap, RT5670_DEPOP_M3, 0x0772); + regmap_write(rt5670->regmap, RT5670_DEPOP_M1, 0x803d); + mdelay(10); + regmap_write(rt5670->regmap, RT5670_DEPOP_M1, 0x831d); + mdelay(10); + regmap_update_bits(rt5670->regmap, RT5670_HP_VOL, + RT5670_L_MUTE | RT5670_R_MUTE, + RT5670_L_MUTE | RT5670_R_MUTE); + msleep(20); + regmap_update_bits(rt5670->regmap, + RT5670_GEN_CTRL2, 0x0300, 0x0); + regmap_write(rt5670->regmap, RT5670_DEPOP_M1, 0x8019); + regmap_write(rt5670->regmap, RT5670_DEPOP_M3, 0x0707); + regmap_write(rt5670->regmap, RT5670_PR_BASE + + RT5670_MAMP_INT_REG2, 0xfc00); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5670_bst1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, RT5670_PWR_ANLG2, + RT5670_PWR_BST1_P, RT5670_PWR_BST1_P); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, RT5670_PWR_ANLG2, + RT5670_PWR_BST1_P, 0); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5670_bst2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, RT5670_PWR_ANLG2, + RT5670_PWR_BST2_P, RT5670_PWR_BST2_P); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, RT5670_PWR_ANLG2, + RT5670_PWR_BST2_P, 0); + break; + + default: + return 0; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rt5670_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("PLL1", RT5670_PWR_ANLG2, + RT5670_PWR_PLL_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S DSP", RT5670_PWR_DIG2, + RT5670_PWR_I2S_DSP_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Mic Det Power", RT5670_PWR_VOL, + RT5670_PWR_MIC_DET_BIT, 0, NULL, 0), + + /* ASRC */ + SND_SOC_DAPM_SUPPLY_S("I2S1 ASRC", 1, RT5670_ASRC_1, + 11, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5670_ASRC_1, + 12, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC STO ASRC", 1, RT5670_ASRC_1, + 10, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC MONO L ASRC", 1, RT5670_ASRC_1, + 9, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC MONO R ASRC", 1, RT5670_ASRC_1, + 8, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5670_ASRC_1, + 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC STO2 ASRC", 1, RT5670_ASRC_1, + 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC MONO L ASRC", 1, RT5670_ASRC_1, + 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC MONO R ASRC", 1, RT5670_ASRC_1, + 0, 0, NULL, 0), + + /* Input Side */ + /* micbias */ + SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5670_PWR_ANLG2, + RT5670_PWR_MB1_BIT, 0, NULL, 0), + + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC L1"), + SND_SOC_DAPM_INPUT("DMIC R1"), + SND_SOC_DAPM_INPUT("DMIC L2"), + SND_SOC_DAPM_INPUT("DMIC R2"), + SND_SOC_DAPM_INPUT("DMIC L3"), + SND_SOC_DAPM_INPUT("DMIC R3"), + + SND_SOC_DAPM_INPUT("IN1P"), + SND_SOC_DAPM_INPUT("IN1N"), + SND_SOC_DAPM_INPUT("IN2P"), + SND_SOC_DAPM_INPUT("IN2N"), + + SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DMIC3", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0, + set_dmic_clk, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5670_DMIC_CTRL1, + RT5670_DMIC_1_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC2 Power", RT5670_DMIC_CTRL1, + RT5670_DMIC_2_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC3 Power", RT5670_DMIC_CTRL1, + RT5670_DMIC_3_EN_SFT, 0, NULL, 0), + /* Boost */ + SND_SOC_DAPM_PGA_E("BST1", RT5670_PWR_ANLG2, RT5670_PWR_BST1_BIT, + 0, NULL, 0, rt5670_bst1_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("BST2", RT5670_PWR_ANLG2, RT5670_PWR_BST2_BIT, + 0, NULL, 0, rt5670_bst2_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + /* Input Volume */ + SND_SOC_DAPM_PGA("INL VOL", RT5670_PWR_VOL, + RT5670_PWR_IN_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("INR VOL", RT5670_PWR_VOL, + RT5670_PWR_IN_R_BIT, 0, NULL, 0), + + /* REC Mixer */ + SND_SOC_DAPM_MIXER("RECMIXL", RT5670_PWR_MIXER, RT5670_PWR_RM_L_BIT, 0, + rt5670_rec_l_mix, ARRAY_SIZE(rt5670_rec_l_mix)), + SND_SOC_DAPM_MIXER("RECMIXR", RT5670_PWR_MIXER, RT5670_PWR_RM_R_BIT, 0, + rt5670_rec_r_mix, ARRAY_SIZE(rt5670_rec_r_mix)), + /* ADCs */ + SND_SOC_DAPM_ADC("ADC 1", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC 2", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_PGA("ADC 1_2", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("ADC 1 power", RT5670_PWR_DIG1, + RT5670_PWR_ADC_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC 2 power", RT5670_PWR_DIG1, + RT5670_PWR_ADC_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC clock", RT5670_PR_BASE + + RT5670_CHOP_DAC_ADC, 12, 0, NULL, 0), + /* ADC Mux */ + SND_SOC_DAPM_MUX("Stereo1 DMIC Mux", SND_SOC_NOPM, 0, 0, + &rt5670_sto1_dmic_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_sto_adc_l2_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_sto_adc_r2_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_sto_adc_l1_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_sto_adc_r1_mux), + SND_SOC_DAPM_MUX("Stereo2 DMIC Mux", SND_SOC_NOPM, 0, 0, + &rt5670_sto2_dmic_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_sto2_adc_l2_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_sto2_adc_r2_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_sto2_adc_l1_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_sto2_adc_r1_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC LR Mux", SND_SOC_NOPM, 0, 0, + &rt5670_sto2_adc_lr_mux), + SND_SOC_DAPM_MUX("Mono DMIC L Mux", SND_SOC_NOPM, 0, 0, + &rt5670_mono_dmic_l_mux), + SND_SOC_DAPM_MUX("Mono DMIC R Mux", SND_SOC_NOPM, 0, 0, + &rt5670_mono_dmic_r_mux), + SND_SOC_DAPM_MUX("Mono ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_mono_adc_l2_mux), + SND_SOC_DAPM_MUX("Mono ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_mono_adc_l1_mux), + SND_SOC_DAPM_MUX("Mono ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_mono_adc_r1_mux), + SND_SOC_DAPM_MUX("Mono ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_mono_adc_r2_mux), + /* ADC Mixer */ + SND_SOC_DAPM_SUPPLY("ADC Stereo1 Filter", RT5670_PWR_DIG2, + RT5670_PWR_ADC_S1F_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC Stereo2 Filter", RT5670_PWR_DIG2, + RT5670_PWR_ADC_S2F_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Sto1 ADC MIXL", RT5670_STO1_ADC_DIG_VOL, + RT5670_L_MUTE_SFT, 1, rt5670_sto1_adc_l_mix, + ARRAY_SIZE(rt5670_sto1_adc_l_mix)), + SND_SOC_DAPM_MIXER("Sto1 ADC MIXR", RT5670_STO1_ADC_DIG_VOL, + RT5670_R_MUTE_SFT, 1, rt5670_sto1_adc_r_mix, + ARRAY_SIZE(rt5670_sto1_adc_r_mix)), + SND_SOC_DAPM_MIXER("Sto2 ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5670_sto2_adc_l_mix, + ARRAY_SIZE(rt5670_sto2_adc_l_mix)), + SND_SOC_DAPM_MIXER("Sto2 ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5670_sto2_adc_r_mix, + ARRAY_SIZE(rt5670_sto2_adc_r_mix)), + SND_SOC_DAPM_SUPPLY("ADC Mono Left Filter", RT5670_PWR_DIG2, + RT5670_PWR_ADC_MF_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Mono ADC MIXL", RT5670_MONO_ADC_DIG_VOL, + RT5670_L_MUTE_SFT, 1, rt5670_mono_adc_l_mix, + ARRAY_SIZE(rt5670_mono_adc_l_mix)), + SND_SOC_DAPM_SUPPLY("ADC Mono Right Filter", RT5670_PWR_DIG2, + RT5670_PWR_ADC_MF_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Mono ADC MIXR", RT5670_MONO_ADC_DIG_VOL, + RT5670_R_MUTE_SFT, 1, rt5670_mono_adc_r_mix, + ARRAY_SIZE(rt5670_mono_adc_r_mix)), + + /* ADC PGA */ + SND_SOC_DAPM_PGA("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo1 ADC MIXR", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo2 ADC MIXL", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo2 ADC MIXR", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Sto2 ADC LR MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo2 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Mono ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("VAD_ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF_ADC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF_ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF_ADC3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1_ADC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1_ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1_ADC3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1_ADC4", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* DSP */ + SND_SOC_DAPM_PGA("TxDP_ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("TxDP_ADC_L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("TxDP_ADC_R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("TxDC_DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("TDM Data Mux", SND_SOC_NOPM, 0, 0, + &rt5670_txdp_slot_mux), + + SND_SOC_DAPM_MUX("DSP UL Mux", SND_SOC_NOPM, 0, 0, + &rt5670_dsp_ul_mux), + SND_SOC_DAPM_MUX("DSP DL Mux", SND_SOC_NOPM, 0, 0, + &rt5670_dsp_dl_mux), + + SND_SOC_DAPM_MUX("RxDP Mux", SND_SOC_NOPM, 0, 0, + &rt5670_rxdp_mux), + + /* IF2 Mux */ + SND_SOC_DAPM_MUX("IF2 ADC Mux", SND_SOC_NOPM, 0, 0, + &rt5670_if2_adc_in_mux), + + /* Digital Interface */ + SND_SOC_DAPM_SUPPLY("I2S1", RT5670_PWR_DIG1, + RT5670_PWR_I2S1_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC2 L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC2 R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S2", RT5670_PWR_DIG1, + RT5670_PWR_I2S2_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Interface Select */ + SND_SOC_DAPM_MUX("IF1 ADC1 IN1 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_if1_adc1_in1_mux), + SND_SOC_DAPM_MUX("IF1 ADC1 IN2 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_if1_adc1_in2_mux), + SND_SOC_DAPM_MUX("IF1 ADC2 IN Mux", SND_SOC_NOPM, 0, 0, + &rt5670_if1_adc2_in_mux), + SND_SOC_DAPM_MUX("IF1 ADC2 IN1 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_if1_adc2_in1_mux), + SND_SOC_DAPM_MUX("VAD ADC Mux", SND_SOC_NOPM, 0, 0, + &rt5670_vad_adc_mux), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, + RT5670_GPIO_CTRL1, RT5670_I2S2_PIN_SFT, 1), + + /* Audio DSP */ + SND_SOC_DAPM_PGA("Audio DSP", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Output Side */ + /* DAC mixer before sound effect */ + SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0, + rt5670_dac_l_mix, ARRAY_SIZE(rt5670_dac_l_mix)), + SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0, + rt5670_dac_r_mix, ARRAY_SIZE(rt5670_dac_r_mix)), + SND_SOC_DAPM_PGA("DAC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* DAC2 channel Mux */ + SND_SOC_DAPM_MUX("DAC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_dac_l2_mux), + SND_SOC_DAPM_MUX("DAC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_dac_r2_mux), + SND_SOC_DAPM_PGA("DAC L2 Volume", RT5670_PWR_DIG1, + RT5670_PWR_DAC_L2_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("DAC R2 Volume", RT5670_PWR_DIG1, + RT5670_PWR_DAC_R2_BIT, 0, NULL, 0), + + SND_SOC_DAPM_MUX("DAC1 L Mux", SND_SOC_NOPM, 0, 0, &rt5670_dac1l_mux), + SND_SOC_DAPM_MUX("DAC1 R Mux", SND_SOC_NOPM, 0, 0, &rt5670_dac1r_mux), + + /* DAC Mixer */ + SND_SOC_DAPM_SUPPLY("DAC Stereo1 Filter", RT5670_PWR_DIG2, + RT5670_PWR_DAC_S1F_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Mono Left Filter", RT5670_PWR_DIG2, + RT5670_PWR_DAC_MF_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Mono Right Filter", RT5670_PWR_DIG2, + RT5670_PWR_DAC_MF_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5670_sto_dac_l_mix, + ARRAY_SIZE(rt5670_sto_dac_l_mix)), + SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5670_sto_dac_r_mix, + ARRAY_SIZE(rt5670_sto_dac_r_mix)), + SND_SOC_DAPM_MIXER("Mono DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5670_mono_dac_l_mix, + ARRAY_SIZE(rt5670_mono_dac_l_mix)), + SND_SOC_DAPM_MIXER("Mono DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5670_mono_dac_r_mix, + ARRAY_SIZE(rt5670_mono_dac_r_mix)), + SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5670_dig_l_mix, + ARRAY_SIZE(rt5670_dig_l_mix)), + SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5670_dig_r_mix, + ARRAY_SIZE(rt5670_dig_r_mix)), + + /* DACs */ + SND_SOC_DAPM_SUPPLY("DAC L1 Power", RT5670_PWR_DIG1, + RT5670_PWR_DAC_L1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC R1 Power", RT5670_PWR_DIG1, + RT5670_PWR_DAC_R1_BIT, 0, NULL, 0), + SND_SOC_DAPM_DAC("DAC L1", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC R1", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC L2", NULL, RT5670_PWR_DIG1, + RT5670_PWR_DAC_L2_BIT, 0), + + SND_SOC_DAPM_DAC("DAC R2", NULL, RT5670_PWR_DIG1, + RT5670_PWR_DAC_R2_BIT, 0), + /* OUT Mixer */ + + SND_SOC_DAPM_MIXER("OUT MIXL", RT5670_PWR_MIXER, RT5670_PWR_OM_L_BIT, + 0, rt5670_out_l_mix, ARRAY_SIZE(rt5670_out_l_mix)), + SND_SOC_DAPM_MIXER("OUT MIXR", RT5670_PWR_MIXER, RT5670_PWR_OM_R_BIT, + 0, rt5670_out_r_mix, ARRAY_SIZE(rt5670_out_r_mix)), + /* Ouput Volume */ + SND_SOC_DAPM_MIXER("HPOVOL MIXL", RT5670_PWR_VOL, + RT5670_PWR_HV_L_BIT, 0, + rt5670_hpvoll_mix, ARRAY_SIZE(rt5670_hpvoll_mix)), + SND_SOC_DAPM_MIXER("HPOVOL MIXR", RT5670_PWR_VOL, + RT5670_PWR_HV_R_BIT, 0, + rt5670_hpvolr_mix, ARRAY_SIZE(rt5670_hpvolr_mix)), + SND_SOC_DAPM_PGA("DAC 1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DAC 2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("HPOVOL", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* HPO/LOUT/Mono Mixer */ + SND_SOC_DAPM_MIXER("HPO MIX", SND_SOC_NOPM, 0, 0, + rt5670_hpo_mix, ARRAY_SIZE(rt5670_hpo_mix)), + SND_SOC_DAPM_MIXER("LOUT MIX", RT5670_PWR_ANLG1, RT5670_PWR_LM_BIT, + 0, rt5670_lout_mix, ARRAY_SIZE(rt5670_lout_mix)), + SND_SOC_DAPM_SUPPLY_S("Improve HP Amp Drv", 1, SND_SOC_NOPM, 0, 0, + rt5670_hp_power_event, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("HP L Amp", RT5670_PWR_ANLG1, + RT5670_PWR_HP_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("HP R Amp", RT5670_PWR_ANLG1, + RT5670_PWR_HP_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, + rt5670_hp_event, SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SWITCH("LOUT L Playback", SND_SOC_NOPM, 0, 0, + &lout_l_enable_control), + SND_SOC_DAPM_SWITCH("LOUT R Playback", SND_SOC_NOPM, 0, 0, + &lout_r_enable_control), + SND_SOC_DAPM_PGA("LOUT Amp", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* PDM */ + SND_SOC_DAPM_SUPPLY("PDM1 Power", RT5670_PWR_DIG2, + RT5670_PWR_PDM1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PDM2 Power", RT5670_PWR_DIG2, + RT5670_PWR_PDM2_BIT, 0, NULL, 0), + + SND_SOC_DAPM_MUX("PDM1 L Mux", RT5670_PDM_OUT_CTRL, + RT5670_M_PDM1_L_SFT, 1, &rt5670_pdm1_l_mux), + SND_SOC_DAPM_MUX("PDM1 R Mux", RT5670_PDM_OUT_CTRL, + RT5670_M_PDM1_R_SFT, 1, &rt5670_pdm1_r_mux), + SND_SOC_DAPM_MUX("PDM2 L Mux", RT5670_PDM_OUT_CTRL, + RT5670_M_PDM2_L_SFT, 1, &rt5670_pdm2_l_mux), + SND_SOC_DAPM_MUX("PDM2 R Mux", RT5670_PDM_OUT_CTRL, + RT5670_M_PDM2_R_SFT, 1, &rt5670_pdm2_r_mux), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), + SND_SOC_DAPM_OUTPUT("LOUTL"), + SND_SOC_DAPM_OUTPUT("LOUTR"), + SND_SOC_DAPM_OUTPUT("PDM1L"), + SND_SOC_DAPM_OUTPUT("PDM1R"), + SND_SOC_DAPM_OUTPUT("PDM2L"), + SND_SOC_DAPM_OUTPUT("PDM2R"), +}; + +static const struct snd_soc_dapm_route rt5670_dapm_routes[] = { + { "ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc }, + { "ADC Stereo2 Filter", NULL, "ADC STO2 ASRC", is_using_asrc }, + { "ADC Mono Left Filter", NULL, "ADC MONO L ASRC", is_using_asrc }, + { "ADC Mono Right Filter", NULL, "ADC MONO R ASRC", is_using_asrc }, + { "DAC Mono Left Filter", NULL, "DAC MONO L ASRC", is_using_asrc }, + { "DAC Mono Right Filter", NULL, "DAC MONO R ASRC", is_using_asrc }, + { "DAC Stereo1 Filter", NULL, "DAC STO ASRC", is_using_asrc }, + + { "I2S1", NULL, "I2S1 ASRC" }, + { "I2S2", NULL, "I2S2 ASRC" }, + + { "DMIC1", NULL, "DMIC L1" }, + { "DMIC1", NULL, "DMIC R1" }, + { "DMIC2", NULL, "DMIC L2" }, + { "DMIC2", NULL, "DMIC R2" }, + { "DMIC3", NULL, "DMIC L3" }, + { "DMIC3", NULL, "DMIC R3" }, + + { "BST1", NULL, "IN1P" }, + { "BST1", NULL, "IN1N" }, + { "BST1", NULL, "Mic Det Power" }, + { "BST2", NULL, "IN2P" }, + { "BST2", NULL, "IN2N" }, + + { "INL VOL", NULL, "IN2P" }, + { "INR VOL", NULL, "IN2N" }, + + { "RECMIXL", "INL Switch", "INL VOL" }, + { "RECMIXL", "BST2 Switch", "BST2" }, + { "RECMIXL", "BST1 Switch", "BST1" }, + + { "RECMIXR", "INR Switch", "INR VOL" }, + { "RECMIXR", "BST2 Switch", "BST2" }, + { "RECMIXR", "BST1 Switch", "BST1" }, + + { "ADC 1", NULL, "RECMIXL" }, + { "ADC 1", NULL, "ADC 1 power" }, + { "ADC 1", NULL, "ADC clock" }, + { "ADC 2", NULL, "RECMIXR" }, + { "ADC 2", NULL, "ADC 2 power" }, + { "ADC 2", NULL, "ADC clock" }, + + { "DMIC L1", NULL, "DMIC CLK" }, + { "DMIC L1", NULL, "DMIC1 Power" }, + { "DMIC R1", NULL, "DMIC CLK" }, + { "DMIC R1", NULL, "DMIC1 Power" }, + { "DMIC L2", NULL, "DMIC CLK" }, + { "DMIC L2", NULL, "DMIC2 Power" }, + { "DMIC R2", NULL, "DMIC CLK" }, + { "DMIC R2", NULL, "DMIC2 Power" }, + { "DMIC L3", NULL, "DMIC CLK" }, + { "DMIC L3", NULL, "DMIC3 Power" }, + { "DMIC R3", NULL, "DMIC CLK" }, + { "DMIC R3", NULL, "DMIC3 Power" }, + + { "Stereo1 DMIC Mux", "DMIC1", "DMIC1" }, + { "Stereo1 DMIC Mux", "DMIC2", "DMIC2" }, + { "Stereo1 DMIC Mux", "DMIC3", "DMIC3" }, + + { "Stereo2 DMIC Mux", "DMIC1", "DMIC1" }, + { "Stereo2 DMIC Mux", "DMIC2", "DMIC2" }, + { "Stereo2 DMIC Mux", "DMIC3", "DMIC3" }, + + { "Mono DMIC L Mux", "DMIC1", "DMIC L1" }, + { "Mono DMIC L Mux", "DMIC2", "DMIC L2" }, + { "Mono DMIC L Mux", "DMIC3", "DMIC L3" }, + + { "Mono DMIC R Mux", "DMIC1", "DMIC R1" }, + { "Mono DMIC R Mux", "DMIC2", "DMIC R2" }, + { "Mono DMIC R Mux", "DMIC3", "DMIC R3" }, + + { "ADC 1_2", NULL, "ADC 1" }, + { "ADC 1_2", NULL, "ADC 2" }, + + { "Stereo1 ADC L2 Mux", "DMIC", "Stereo1 DMIC Mux" }, + { "Stereo1 ADC L2 Mux", "DAC MIX", "DAC MIXL" }, + { "Stereo1 ADC L1 Mux", "ADC", "ADC 1_2" }, + { "Stereo1 ADC L1 Mux", "DAC MIX", "DAC MIXL" }, + + { "Stereo1 ADC R1 Mux", "ADC", "ADC 1_2" }, + { "Stereo1 ADC R1 Mux", "DAC MIX", "DAC MIXR" }, + { "Stereo1 ADC R2 Mux", "DMIC", "Stereo1 DMIC Mux" }, + { "Stereo1 ADC R2 Mux", "DAC MIX", "DAC MIXR" }, + + { "Mono ADC L2 Mux", "DMIC", "Mono DMIC L Mux" }, + { "Mono ADC L2 Mux", "Mono DAC MIXL", "Mono DAC MIXL" }, + { "Mono ADC L1 Mux", "Mono DAC MIXL", "Mono DAC MIXL" }, + { "Mono ADC L1 Mux", "ADC1", "ADC 1" }, + + { "Mono ADC R1 Mux", "Mono DAC MIXR", "Mono DAC MIXR" }, + { "Mono ADC R1 Mux", "ADC2", "ADC 2" }, + { "Mono ADC R2 Mux", "DMIC", "Mono DMIC R Mux" }, + { "Mono ADC R2 Mux", "Mono DAC MIXR", "Mono DAC MIXR" }, + + { "Sto1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux" }, + { "Sto1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux" }, + { "Sto1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux" }, + { "Sto1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux" }, + + { "Stereo1 ADC MIXL", NULL, "Sto1 ADC MIXL" }, + { "Stereo1 ADC MIXL", NULL, "ADC Stereo1 Filter" }, + { "ADC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "Stereo1 ADC MIXR", NULL, "Sto1 ADC MIXR" }, + { "Stereo1 ADC MIXR", NULL, "ADC Stereo1 Filter" }, + { "ADC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "Mono ADC MIXL", "ADC1 Switch", "Mono ADC L1 Mux" }, + { "Mono ADC MIXL", "ADC2 Switch", "Mono ADC L2 Mux" }, + { "Mono ADC MIXL", NULL, "ADC Mono Left Filter" }, + { "ADC Mono Left Filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "Mono ADC MIXR", "ADC1 Switch", "Mono ADC R1 Mux" }, + { "Mono ADC MIXR", "ADC2 Switch", "Mono ADC R2 Mux" }, + { "Mono ADC MIXR", NULL, "ADC Mono Right Filter" }, + { "ADC Mono Right Filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "Stereo2 ADC L2 Mux", "DMIC", "Stereo2 DMIC Mux" }, + { "Stereo2 ADC L2 Mux", "DAC MIX", "DAC MIXL" }, + { "Stereo2 ADC L1 Mux", "ADC", "ADC 1_2" }, + { "Stereo2 ADC L1 Mux", "DAC MIX", "DAC MIXL" }, + + { "Stereo2 ADC R1 Mux", "ADC", "ADC 1_2" }, + { "Stereo2 ADC R1 Mux", "DAC MIX", "DAC MIXR" }, + { "Stereo2 ADC R2 Mux", "DMIC", "Stereo2 DMIC Mux" }, + { "Stereo2 ADC R2 Mux", "DAC MIX", "DAC MIXR" }, + + { "Sto2 ADC MIXL", "ADC1 Switch", "Stereo2 ADC L1 Mux" }, + { "Sto2 ADC MIXL", "ADC2 Switch", "Stereo2 ADC L2 Mux" }, + { "Sto2 ADC MIXR", "ADC1 Switch", "Stereo2 ADC R1 Mux" }, + { "Sto2 ADC MIXR", "ADC2 Switch", "Stereo2 ADC R2 Mux" }, + + { "Sto2 ADC LR MIX", NULL, "Sto2 ADC MIXL" }, + { "Sto2 ADC LR MIX", NULL, "Sto2 ADC MIXR" }, + + { "Stereo2 ADC LR Mux", "L", "Sto2 ADC MIXL" }, + { "Stereo2 ADC LR Mux", "LR", "Sto2 ADC LR MIX" }, + + { "Stereo2 ADC MIXL", NULL, "Stereo2 ADC LR Mux" }, + { "Stereo2 ADC MIXL", NULL, "ADC Stereo2 Filter" }, + { "ADC Stereo2 Filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "Stereo2 ADC MIXR", NULL, "Sto2 ADC MIXR" }, + { "Stereo2 ADC MIXR", NULL, "ADC Stereo2 Filter" }, + { "ADC Stereo2 Filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "VAD ADC Mux", "Sto1 ADC L", "Stereo1 ADC MIXL" }, + { "VAD ADC Mux", "Mono ADC L", "Mono ADC MIXL" }, + { "VAD ADC Mux", "Mono ADC R", "Mono ADC MIXR" }, + { "VAD ADC Mux", "Sto2 ADC L", "Sto2 ADC MIXL" }, + + { "VAD_ADC", NULL, "VAD ADC Mux" }, + + { "IF_ADC1", NULL, "Stereo1 ADC MIXL" }, + { "IF_ADC1", NULL, "Stereo1 ADC MIXR" }, + { "IF_ADC2", NULL, "Mono ADC MIXL" }, + { "IF_ADC2", NULL, "Mono ADC MIXR" }, + { "IF_ADC3", NULL, "Stereo2 ADC MIXL" }, + { "IF_ADC3", NULL, "Stereo2 ADC MIXR" }, + + { "IF1 ADC1 IN1 Mux", "IF_ADC1", "IF_ADC1" }, + { "IF1 ADC1 IN1 Mux", "IF1_ADC3", "IF1_ADC3" }, + + { "IF1 ADC1 IN2 Mux", "IF1_ADC1_IN1", "IF1 ADC1 IN1 Mux" }, + { "IF1 ADC1 IN2 Mux", "IF1_ADC4", "IF1_ADC4" }, + + { "IF1 ADC2 IN Mux", "IF_ADC2", "IF_ADC2" }, + { "IF1 ADC2 IN Mux", "VAD_ADC", "VAD_ADC" }, + + { "IF1 ADC2 IN1 Mux", "IF1_ADC2_IN", "IF1 ADC2 IN Mux" }, + { "IF1 ADC2 IN1 Mux", "IF1_ADC4", "IF1_ADC4" }, + + { "IF1_ADC1" , NULL, "IF1 ADC1 IN2 Mux" }, + { "IF1_ADC2" , NULL, "IF1 ADC2 IN1 Mux" }, + + { "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL" }, + { "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR" }, + { "Stereo2 ADC MIX", NULL, "Sto2 ADC MIXL" }, + { "Stereo2 ADC MIX", NULL, "Sto2 ADC MIXR" }, + { "Mono ADC MIX", NULL, "Mono ADC MIXL" }, + { "Mono ADC MIX", NULL, "Mono ADC MIXR" }, + + { "RxDP Mux", "IF2 DAC", "IF2 DAC" }, + { "RxDP Mux", "IF1 DAC", "IF1 DAC2" }, + { "RxDP Mux", "STO1 ADC Mixer", "Stereo1 ADC MIX" }, + { "RxDP Mux", "STO2 ADC Mixer", "Stereo2 ADC MIX" }, + { "RxDP Mux", "Mono ADC Mixer L", "Mono ADC MIXL" }, + { "RxDP Mux", "Mono ADC Mixer R", "Mono ADC MIXR" }, + { "RxDP Mux", "DAC1", "DAC MIX" }, + + { "TDM Data Mux", "Slot 0-1", "Stereo1 ADC MIX" }, + { "TDM Data Mux", "Slot 2-3", "Mono ADC MIX" }, + { "TDM Data Mux", "Slot 4-5", "Stereo2 ADC MIX" }, + { "TDM Data Mux", "Slot 6-7", "IF2 DAC" }, + + { "DSP UL Mux", "Bypass", "TDM Data Mux" }, + { "DSP UL Mux", NULL, "I2S DSP" }, + { "DSP DL Mux", "Bypass", "RxDP Mux" }, + { "DSP DL Mux", NULL, "I2S DSP" }, + + { "TxDP_ADC_L", NULL, "DSP UL Mux" }, + { "TxDP_ADC_R", NULL, "DSP UL Mux" }, + { "TxDC_DAC", NULL, "DSP DL Mux" }, + + { "TxDP_ADC", NULL, "TxDP_ADC_L" }, + { "TxDP_ADC", NULL, "TxDP_ADC_R" }, + + { "IF1 ADC", NULL, "I2S1" }, + { "IF1 ADC", NULL, "IF1_ADC1" }, + { "IF1 ADC", NULL, "IF1_ADC2" }, + { "IF1 ADC", NULL, "IF_ADC3" }, + { "IF1 ADC", NULL, "TxDP_ADC" }, + + { "IF2 ADC Mux", "IF_ADC1", "IF_ADC1" }, + { "IF2 ADC Mux", "IF_ADC2", "IF_ADC2" }, + { "IF2 ADC Mux", "IF_ADC3", "IF_ADC3" }, + { "IF2 ADC Mux", "TxDC_DAC", "TxDC_DAC" }, + { "IF2 ADC Mux", "TxDP_ADC", "TxDP_ADC" }, + { "IF2 ADC Mux", "VAD_ADC", "VAD_ADC" }, + + { "IF2 ADC L", NULL, "IF2 ADC Mux" }, + { "IF2 ADC R", NULL, "IF2 ADC Mux" }, + + { "IF2 ADC", NULL, "I2S2" }, + { "IF2 ADC", NULL, "IF2 ADC L" }, + { "IF2 ADC", NULL, "IF2 ADC R" }, + + { "AIF1TX", NULL, "IF1 ADC" }, + { "AIF2TX", NULL, "IF2 ADC" }, + + { "IF1 DAC1", NULL, "AIF1RX" }, + { "IF1 DAC2", NULL, "AIF1RX" }, + { "IF2 DAC", NULL, "AIF2RX" }, + + { "IF1 DAC1", NULL, "I2S1" }, + { "IF1 DAC2", NULL, "I2S1" }, + { "IF2 DAC", NULL, "I2S2" }, + + { "IF1 DAC2 L", NULL, "IF1 DAC2" }, + { "IF1 DAC2 R", NULL, "IF1 DAC2" }, + { "IF1 DAC1 L", NULL, "IF1 DAC1" }, + { "IF1 DAC1 R", NULL, "IF1 DAC1" }, + { "IF2 DAC L", NULL, "IF2 DAC" }, + { "IF2 DAC R", NULL, "IF2 DAC" }, + + { "DAC1 L Mux", "IF1 DAC", "IF1 DAC1 L" }, + { "DAC1 L Mux", "IF2 DAC", "IF2 DAC L" }, + + { "DAC1 R Mux", "IF1 DAC", "IF1 DAC1 R" }, + { "DAC1 R Mux", "IF2 DAC", "IF2 DAC R" }, + + { "DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL" }, + { "DAC1 MIXL", "DAC1 Switch", "DAC1 L Mux" }, + { "DAC1 MIXL", NULL, "DAC Stereo1 Filter" }, + { "DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR" }, + { "DAC1 MIXR", "DAC1 Switch", "DAC1 R Mux" }, + { "DAC1 MIXR", NULL, "DAC Stereo1 Filter" }, + + { "DAC MIX", NULL, "DAC1 MIXL" }, + { "DAC MIX", NULL, "DAC1 MIXR" }, + + { "Audio DSP", NULL, "DAC1 MIXL" }, + { "Audio DSP", NULL, "DAC1 MIXR" }, + + { "DAC L2 Mux", "IF1 DAC", "IF1 DAC2 L" }, + { "DAC L2 Mux", "IF2 DAC", "IF2 DAC L" }, + { "DAC L2 Mux", "TxDC DAC", "TxDC_DAC" }, + { "DAC L2 Mux", "VAD_ADC", "VAD_ADC" }, + { "DAC L2 Volume", NULL, "DAC L2 Mux" }, + { "DAC L2 Volume", NULL, "DAC Mono Left Filter" }, + + { "DAC R2 Mux", "IF1 DAC", "IF1 DAC2 R" }, + { "DAC R2 Mux", "IF2 DAC", "IF2 DAC R" }, + { "DAC R2 Mux", "TxDC DAC", "TxDC_DAC" }, + { "DAC R2 Mux", "TxDP ADC", "TxDP_ADC" }, + { "DAC R2 Volume", NULL, "DAC R2 Mux" }, + { "DAC R2 Volume", NULL, "DAC Mono Right Filter" }, + + { "Stereo DAC MIXL", "DAC L1 Switch", "DAC1 MIXL" }, + { "Stereo DAC MIXL", "DAC R1 Switch", "DAC1 MIXR" }, + { "Stereo DAC MIXL", "DAC L2 Switch", "DAC L2 Volume" }, + { "Stereo DAC MIXL", NULL, "DAC Stereo1 Filter" }, + { "Stereo DAC MIXL", NULL, "DAC L1 Power" }, + { "Stereo DAC MIXR", "DAC R1 Switch", "DAC1 MIXR" }, + { "Stereo DAC MIXR", "DAC L1 Switch", "DAC1 MIXL" }, + { "Stereo DAC MIXR", "DAC R2 Switch", "DAC R2 Volume" }, + { "Stereo DAC MIXR", NULL, "DAC Stereo1 Filter" }, + { "Stereo DAC MIXR", NULL, "DAC R1 Power" }, + + { "Mono DAC MIXL", "DAC L1 Switch", "DAC1 MIXL" }, + { "Mono DAC MIXL", "DAC L2 Switch", "DAC L2 Volume" }, + { "Mono DAC MIXL", "DAC R2 Switch", "DAC R2 Volume" }, + { "Mono DAC MIXL", NULL, "DAC Mono Left Filter" }, + { "Mono DAC MIXR", "DAC R1 Switch", "DAC1 MIXR" }, + { "Mono DAC MIXR", "DAC R2 Switch", "DAC R2 Volume" }, + { "Mono DAC MIXR", "DAC L2 Switch", "DAC L2 Volume" }, + { "Mono DAC MIXR", NULL, "DAC Mono Right Filter" }, + + { "DAC MIXL", "Sto DAC Mix L Switch", "Stereo DAC MIXL" }, + { "DAC MIXL", "DAC L2 Switch", "DAC L2 Volume" }, + { "DAC MIXL", "DAC R2 Switch", "DAC R2 Volume" }, + { "DAC MIXR", "Sto DAC Mix R Switch", "Stereo DAC MIXR" }, + { "DAC MIXR", "DAC R2 Switch", "DAC R2 Volume" }, + { "DAC MIXR", "DAC L2 Switch", "DAC L2 Volume" }, + + { "DAC L1", NULL, "DAC L1 Power" }, + { "DAC L1", NULL, "Stereo DAC MIXL" }, + { "DAC L1", NULL, "PLL1", is_sys_clk_from_pll }, + { "DAC R1", NULL, "DAC R1 Power" }, + { "DAC R1", NULL, "Stereo DAC MIXR" }, + { "DAC R1", NULL, "PLL1", is_sys_clk_from_pll }, + { "DAC L2", NULL, "Mono DAC MIXL" }, + { "DAC L2", NULL, "PLL1", is_sys_clk_from_pll }, + { "DAC R2", NULL, "Mono DAC MIXR" }, + { "DAC R2", NULL, "PLL1", is_sys_clk_from_pll }, + + { "OUT MIXL", "BST1 Switch", "BST1" }, + { "OUT MIXL", "INL Switch", "INL VOL" }, + { "OUT MIXL", "DAC L2 Switch", "DAC L2" }, + { "OUT MIXL", "DAC L1 Switch", "DAC L1" }, + + { "OUT MIXR", "BST2 Switch", "BST2" }, + { "OUT MIXR", "INR Switch", "INR VOL" }, + { "OUT MIXR", "DAC R2 Switch", "DAC R2" }, + { "OUT MIXR", "DAC R1 Switch", "DAC R1" }, + + { "HPOVOL MIXL", "DAC1 Switch", "DAC L1" }, + { "HPOVOL MIXL", "INL Switch", "INL VOL" }, + { "HPOVOL MIXR", "DAC1 Switch", "DAC R1" }, + { "HPOVOL MIXR", "INR Switch", "INR VOL" }, + + { "DAC 2", NULL, "DAC L2" }, + { "DAC 2", NULL, "DAC R2" }, + { "DAC 1", NULL, "DAC L1" }, + { "DAC 1", NULL, "DAC R1" }, + { "HPOVOL", NULL, "HPOVOL MIXL" }, + { "HPOVOL", NULL, "HPOVOL MIXR" }, + { "HPO MIX", "DAC1 Switch", "DAC 1" }, + { "HPO MIX", "HPVOL Switch", "HPOVOL" }, + + { "LOUT MIX", "DAC L1 Switch", "DAC L1" }, + { "LOUT MIX", "DAC R1 Switch", "DAC R1" }, + { "LOUT MIX", "OUTMIX L Switch", "OUT MIXL" }, + { "LOUT MIX", "OUTMIX R Switch", "OUT MIXR" }, + + { "PDM1 L Mux", "Stereo DAC", "Stereo DAC MIXL" }, + { "PDM1 L Mux", "Mono DAC", "Mono DAC MIXL" }, + { "PDM1 L Mux", NULL, "PDM1 Power" }, + { "PDM1 R Mux", "Stereo DAC", "Stereo DAC MIXR" }, + { "PDM1 R Mux", "Mono DAC", "Mono DAC MIXR" }, + { "PDM1 R Mux", NULL, "PDM1 Power" }, + { "PDM2 L Mux", "Stereo DAC", "Stereo DAC MIXL" }, + { "PDM2 L Mux", "Mono DAC", "Mono DAC MIXL" }, + { "PDM2 L Mux", NULL, "PDM2 Power" }, + { "PDM2 R Mux", "Stereo DAC", "Stereo DAC MIXR" }, + { "PDM2 R Mux", "Mono DAC", "Mono DAC MIXR" }, + { "PDM2 R Mux", NULL, "PDM2 Power" }, + + { "HP Amp", NULL, "HPO MIX" }, + { "HP Amp", NULL, "Mic Det Power" }, + { "HPOL", NULL, "HP Amp" }, + { "HPOL", NULL, "HP L Amp" }, + { "HPOL", NULL, "Improve HP Amp Drv" }, + { "HPOR", NULL, "HP Amp" }, + { "HPOR", NULL, "HP R Amp" }, + { "HPOR", NULL, "Improve HP Amp Drv" }, + + { "LOUT Amp", NULL, "LOUT MIX" }, + { "LOUT L Playback", "Switch", "LOUT Amp" }, + { "LOUT R Playback", "Switch", "LOUT Amp" }, + { "LOUTL", NULL, "LOUT L Playback" }, + { "LOUTR", NULL, "LOUT R Playback" }, + { "LOUTL", NULL, "Improve HP Amp Drv" }, + { "LOUTR", NULL, "Improve HP Amp Drv" }, + + { "PDM1L", NULL, "PDM1 L Mux" }, + { "PDM1R", NULL, "PDM1 R Mux" }, + { "PDM2L", NULL, "PDM2 L Mux" }, + { "PDM2R", NULL, "PDM2 R Mux" }, +}; + +static int rt5670_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); + unsigned int val_len = 0, val_clk, mask_clk; + int pre_div, bclk_ms, frame_size; + + rt5670->lrck[dai->id] = params_rate(params); + pre_div = rl6231_get_clk_info(rt5670->sysclk, rt5670->lrck[dai->id]); + if (pre_div < 0) { + dev_err(codec->dev, "Unsupported clock setting %d for DAI %d\n", + rt5670->lrck[dai->id], dai->id); + return -EINVAL; + } + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size); + return -EINVAL; + } + bclk_ms = frame_size > 32; + rt5670->bclk[dai->id] = rt5670->lrck[dai->id] * (32 << bclk_ms); + + dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n", + rt5670->bclk[dai->id], rt5670->lrck[dai->id]); + dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n", + bclk_ms, pre_div, dai->id); + + switch (params_width(params)) { + case 16: + break; + case 20: + val_len |= RT5670_I2S_DL_20; + break; + case 24: + val_len |= RT5670_I2S_DL_24; + break; + case 8: + val_len |= RT5670_I2S_DL_8; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5670_AIF1: + mask_clk = RT5670_I2S_BCLK_MS1_MASK | RT5670_I2S_PD1_MASK; + val_clk = bclk_ms << RT5670_I2S_BCLK_MS1_SFT | + pre_div << RT5670_I2S_PD1_SFT; + snd_soc_update_bits(codec, RT5670_I2S1_SDP, + RT5670_I2S_DL_MASK, val_len); + snd_soc_update_bits(codec, RT5670_ADDA_CLK1, mask_clk, val_clk); + break; + case RT5670_AIF2: + mask_clk = RT5670_I2S_BCLK_MS2_MASK | RT5670_I2S_PD2_MASK; + val_clk = bclk_ms << RT5670_I2S_BCLK_MS2_SFT | + pre_div << RT5670_I2S_PD2_SFT; + snd_soc_update_bits(codec, RT5670_I2S2_SDP, + RT5670_I2S_DL_MASK, val_len); + snd_soc_update_bits(codec, RT5670_ADDA_CLK1, mask_clk, val_clk); + break; + default: + dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + + return 0; +} + +static int rt5670_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); + unsigned int reg_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + rt5670->master[dai->id] = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + reg_val |= RT5670_I2S_MS_S; + rt5670->master[dai->id] = 0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_val |= RT5670_I2S_BP_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT5670_I2S_DF_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT5670_I2S_DF_PCM_A; + break; + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT5670_I2S_DF_PCM_B; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5670_AIF1: + snd_soc_update_bits(codec, RT5670_I2S1_SDP, + RT5670_I2S_MS_MASK | RT5670_I2S_BP_MASK | + RT5670_I2S_DF_MASK, reg_val); + break; + case RT5670_AIF2: + snd_soc_update_bits(codec, RT5670_I2S2_SDP, + RT5670_I2S_MS_MASK | RT5670_I2S_BP_MASK | + RT5670_I2S_DF_MASK, reg_val); + break; + default: + dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + return 0; +} + +static int rt5670_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); + unsigned int reg_val = 0; + + if (freq == rt5670->sysclk && clk_id == rt5670->sysclk_src) + return 0; + + switch (clk_id) { + case RT5670_SCLK_S_MCLK: + reg_val |= RT5670_SCLK_SRC_MCLK; + break; + case RT5670_SCLK_S_PLL1: + reg_val |= RT5670_SCLK_SRC_PLL1; + break; + case RT5670_SCLK_S_RCCLK: + reg_val |= RT5670_SCLK_SRC_RCCLK; + break; + default: + dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + snd_soc_update_bits(codec, RT5670_GLB_CLK, + RT5670_SCLK_SRC_MASK, reg_val); + rt5670->sysclk = freq; + rt5670->sysclk_src = clk_id; + + dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); + + return 0; +} + +static int rt5670_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); + struct rl6231_pll_code pll_code; + int ret; + + if (source == rt5670->pll_src && freq_in == rt5670->pll_in && + freq_out == rt5670->pll_out) + return 0; + + if (!freq_in || !freq_out) { + dev_dbg(codec->dev, "PLL disabled\n"); + + rt5670->pll_in = 0; + rt5670->pll_out = 0; + snd_soc_update_bits(codec, RT5670_GLB_CLK, + RT5670_SCLK_SRC_MASK, RT5670_SCLK_SRC_MCLK); + return 0; + } + + switch (source) { + case RT5670_PLL1_S_MCLK: + snd_soc_update_bits(codec, RT5670_GLB_CLK, + RT5670_PLL1_SRC_MASK, RT5670_PLL1_SRC_MCLK); + break; + case RT5670_PLL1_S_BCLK1: + case RT5670_PLL1_S_BCLK2: + case RT5670_PLL1_S_BCLK3: + case RT5670_PLL1_S_BCLK4: + switch (dai->id) { + case RT5670_AIF1: + snd_soc_update_bits(codec, RT5670_GLB_CLK, + RT5670_PLL1_SRC_MASK, RT5670_PLL1_SRC_BCLK1); + break; + case RT5670_AIF2: + snd_soc_update_bits(codec, RT5670_GLB_CLK, + RT5670_PLL1_SRC_MASK, RT5670_PLL1_SRC_BCLK2); + break; + default: + dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + break; + default: + dev_err(codec->dev, "Unknown PLL source %d\n", source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(codec->dev, "Unsupport input clock %d\n", freq_in); + return ret; + } + + dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + snd_soc_write(codec, RT5670_PLL_CTRL1, + pll_code.n_code << RT5670_PLL_N_SFT | pll_code.k_code); + snd_soc_write(codec, RT5670_PLL_CTRL2, + (pll_code.m_bp ? 0 : pll_code.m_code) << RT5670_PLL_M_SFT | + pll_code.m_bp << RT5670_PLL_M_BP_SFT); + + rt5670->pll_in = freq_in; + rt5670->pll_out = freq_out; + rt5670->pll_src = source; + + return 0; +} + +static int rt5670_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_codec *codec = dai->codec; + unsigned int val = 0; + + if (rx_mask || tx_mask) + val |= (1 << 14); + + switch (slots) { + case 4: + val |= (1 << 12); + break; + case 6: + val |= (2 << 12); + break; + case 8: + val |= (3 << 12); + break; + case 2: + break; + default: + return -EINVAL; + } + + switch (slot_width) { + case 20: + val |= (1 << 10); + break; + case 24: + val |= (2 << 10); + break; + case 32: + val |= (3 << 10); + break; + case 16: + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, RT5670_TDM_CTRL_1, 0x7c00, val); + + return 0; +} + +static int rt5670_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) { + snd_soc_update_bits(codec, RT5670_PWR_ANLG1, + RT5670_PWR_VREF1 | RT5670_PWR_MB | + RT5670_PWR_BG | RT5670_PWR_VREF2, + RT5670_PWR_VREF1 | RT5670_PWR_MB | + RT5670_PWR_BG | RT5670_PWR_VREF2); + mdelay(10); + snd_soc_update_bits(codec, RT5670_PWR_ANLG1, + RT5670_PWR_FV1 | RT5670_PWR_FV2, + RT5670_PWR_FV1 | RT5670_PWR_FV2); + snd_soc_update_bits(codec, RT5670_CHARGE_PUMP, + RT5670_OSW_L_MASK | RT5670_OSW_R_MASK, + RT5670_OSW_L_DIS | RT5670_OSW_R_DIS); + snd_soc_update_bits(codec, RT5670_DIG_MISC, 0x1, 0x1); + snd_soc_update_bits(codec, RT5670_PWR_ANLG1, + RT5670_LDO_SEL_MASK, 0x3); + } + break; + case SND_SOC_BIAS_STANDBY: + snd_soc_write(codec, RT5670_PWR_DIG1, 0x0000); + snd_soc_write(codec, RT5670_PWR_DIG2, 0x0001); + snd_soc_write(codec, RT5670_PWR_VOL, 0x0000); + snd_soc_write(codec, RT5670_PWR_MIXER, 0x0001); + snd_soc_write(codec, RT5670_PWR_ANLG1, 0x2800); + snd_soc_write(codec, RT5670_PWR_ANLG2, 0x0004); + snd_soc_update_bits(codec, RT5670_DIG_MISC, 0x1, 0x0); + snd_soc_update_bits(codec, RT5670_PWR_ANLG1, + RT5670_LDO_SEL_MASK, 0x1); + break; + + default: + break; + } + codec->dapm.bias_level = level; + + return 0; +} + +static int rt5670_probe(struct snd_soc_codec *codec) +{ + struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); + + rt5670->codec = codec; + + return 0; +} + +static int rt5670_remove(struct snd_soc_codec *codec) +{ + struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); + + regmap_write(rt5670->regmap, RT5670_RESET, 0); + return 0; +} + +#ifdef CONFIG_PM +static int rt5670_suspend(struct snd_soc_codec *codec) +{ + struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); + + regcache_cache_only(rt5670->regmap, true); + regcache_mark_dirty(rt5670->regmap); + return 0; +} + +static int rt5670_resume(struct snd_soc_codec *codec) +{ + struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); + + regcache_cache_only(rt5670->regmap, false); + regcache_sync(rt5670->regmap); + + return 0; +} +#else +#define rt5670_suspend NULL +#define rt5670_resume NULL +#endif + +#define RT5670_STEREO_RATES SNDRV_PCM_RATE_8000_96000 +#define RT5670_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +struct snd_soc_dai_ops rt5670_aif_dai_ops = { + .hw_params = rt5670_hw_params, + .set_fmt = rt5670_set_dai_fmt, + .set_sysclk = rt5670_set_dai_sysclk, + .set_tdm_slot = rt5670_set_tdm_slot, + .set_pll = rt5670_set_dai_pll, +}; + +struct snd_soc_dai_driver rt5670_dai[] = { + { + .name = "rt5670-aif1", + .id = RT5670_AIF1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5670_STEREO_RATES, + .formats = RT5670_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5670_STEREO_RATES, + .formats = RT5670_FORMATS, + }, + .ops = &rt5670_aif_dai_ops, + }, + { + .name = "rt5670-aif2", + .id = RT5670_AIF2, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5670_STEREO_RATES, + .formats = RT5670_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5670_STEREO_RATES, + .formats = RT5670_FORMATS, + }, + .ops = &rt5670_aif_dai_ops, + }, +}; + +static struct snd_soc_codec_driver soc_codec_dev_rt5670 = { + .probe = rt5670_probe, + .remove = rt5670_remove, + .suspend = rt5670_suspend, + .resume = rt5670_resume, + .set_bias_level = rt5670_set_bias_level, + .idle_bias_off = true, + .controls = rt5670_snd_controls, + .num_controls = ARRAY_SIZE(rt5670_snd_controls), + .dapm_widgets = rt5670_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5670_dapm_widgets), + .dapm_routes = rt5670_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5670_dapm_routes), +}; + +static const struct regmap_config rt5670_regmap = { + .reg_bits = 8, + .val_bits = 16, + .max_register = RT5670_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5670_ranges) * + RT5670_PR_SPACING), + .volatile_reg = rt5670_volatile_register, + .readable_reg = rt5670_readable_register, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt5670_reg, + .num_reg_defaults = ARRAY_SIZE(rt5670_reg), + .ranges = rt5670_ranges, + .num_ranges = ARRAY_SIZE(rt5670_ranges), +}; + +static const struct i2c_device_id rt5670_i2c_id[] = { + { "rt5670", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt5670_i2c_id); + +static int rt5670_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct rt5670_platform_data *pdata = dev_get_platdata(&i2c->dev); + struct rt5670_priv *rt5670; + int ret; + unsigned int val; + + rt5670 = devm_kzalloc(&i2c->dev, + sizeof(struct rt5670_priv), + GFP_KERNEL); + if (NULL == rt5670) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt5670); + + if (pdata) + rt5670->pdata = *pdata; + + rt5670->regmap = devm_regmap_init_i2c(i2c, &rt5670_regmap); + if (IS_ERR(rt5670->regmap)) { + ret = PTR_ERR(rt5670->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + regmap_read(rt5670->regmap, RT5670_VENDOR_ID2, &val); + if (val != RT5670_DEVICE_ID) { + dev_err(&i2c->dev, + "Device with ID register %x is not rt5670/72\n", val); + return -ENODEV; + } + + regmap_write(rt5670->regmap, RT5670_RESET, 0); + regmap_update_bits(rt5670->regmap, RT5670_PWR_ANLG1, + RT5670_PWR_HP_L | RT5670_PWR_HP_R | + RT5670_PWR_VREF2, RT5670_PWR_VREF2); + msleep(100); + + regmap_write(rt5670->regmap, RT5670_RESET, 0); + + ret = regmap_register_patch(rt5670->regmap, init_list, + ARRAY_SIZE(init_list)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); + + if (rt5670->pdata.in2_diff) + regmap_update_bits(rt5670->regmap, RT5670_IN2, + RT5670_IN_DF2, RT5670_IN_DF2); + + if (i2c->irq) { + regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1, + RT5670_GP1_PIN_MASK, RT5670_GP1_PIN_IRQ); + regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL2, + RT5670_GP1_PF_MASK, RT5670_GP1_PF_OUT); + + } + + if (rt5670->pdata.jd_mode) { + regmap_update_bits(rt5670->regmap, RT5670_PWR_ANLG1, + RT5670_PWR_MB, RT5670_PWR_MB); + regmap_update_bits(rt5670->regmap, RT5670_PWR_ANLG2, + RT5670_PWR_JD1, RT5670_PWR_JD1); + regmap_update_bits(rt5670->regmap, RT5670_IRQ_CTRL1, + RT5670_JD1_1_EN_MASK, RT5670_JD1_1_EN); + regmap_update_bits(rt5670->regmap, RT5670_JD_CTRL3, + RT5670_JD_TRI_CBJ_SEL_MASK | + RT5670_JD_TRI_HPO_SEL_MASK, + RT5670_JD_CBJ_JD1_1 | RT5670_JD_HPO_JD1_1); + switch (rt5670->pdata.jd_mode) { + case 1: + regmap_update_bits(rt5670->regmap, RT5670_A_JD_CTRL1, + RT5670_JD1_MODE_MASK, + RT5670_JD1_MODE_0); + break; + case 2: + regmap_update_bits(rt5670->regmap, RT5670_A_JD_CTRL1, + RT5670_JD1_MODE_MASK, + RT5670_JD1_MODE_1); + break; + case 3: + regmap_update_bits(rt5670->regmap, RT5670_A_JD_CTRL1, + RT5670_JD1_MODE_MASK, + RT5670_JD1_MODE_2); + break; + default: + break; + } + } + + if (rt5670->pdata.dmic_en) { + regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1, + RT5670_GP2_PIN_MASK, + RT5670_GP2_PIN_DMIC1_SCL); + + switch (rt5670->pdata.dmic1_data_pin) { + case RT5670_DMIC_DATA_IN2P: + regmap_update_bits(rt5670->regmap, RT5670_DMIC_CTRL1, + RT5670_DMIC_1_DP_MASK, + RT5670_DMIC_1_DP_IN2P); + break; + + case RT5670_DMIC_DATA_GPIO6: + regmap_update_bits(rt5670->regmap, RT5670_DMIC_CTRL1, + RT5670_DMIC_1_DP_MASK, + RT5670_DMIC_1_DP_GPIO6); + regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1, + RT5670_GP6_PIN_MASK, + RT5670_GP6_PIN_DMIC1_SDA); + break; + + case RT5670_DMIC_DATA_GPIO7: + regmap_update_bits(rt5670->regmap, RT5670_DMIC_CTRL1, + RT5670_DMIC_1_DP_MASK, + RT5670_DMIC_1_DP_GPIO7); + regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1, + RT5670_GP7_PIN_MASK, + RT5670_GP7_PIN_DMIC1_SDA); + break; + + default: + break; + } + + switch (rt5670->pdata.dmic2_data_pin) { + case RT5670_DMIC_DATA_IN3N: + regmap_update_bits(rt5670->regmap, RT5670_DMIC_CTRL1, + RT5670_DMIC_2_DP_MASK, + RT5670_DMIC_2_DP_IN3N); + break; + + case RT5670_DMIC_DATA_GPIO8: + regmap_update_bits(rt5670->regmap, RT5670_DMIC_CTRL1, + RT5670_DMIC_2_DP_MASK, + RT5670_DMIC_2_DP_GPIO8); + regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1, + RT5670_GP8_PIN_MASK, + RT5670_GP8_PIN_DMIC2_SDA); + break; + + default: + break; + } + + switch (rt5670->pdata.dmic3_data_pin) { + case RT5670_DMIC_DATA_GPIO5: + regmap_update_bits(rt5670->regmap, RT5670_DMIC_CTRL2, + RT5670_DMIC_3_DP_MASK, + RT5670_DMIC_3_DP_GPIO5); + regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1, + RT5670_GP5_PIN_MASK, + RT5670_GP5_PIN_DMIC3_SDA); + break; + + case RT5670_DMIC_DATA_GPIO9: + case RT5670_DMIC_DATA_GPIO10: + dev_err(&i2c->dev, + "Always use GPIO5 as DMIC3 data pin\n"); + break; + + default: + break; + } + + } + + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5670, + rt5670_dai, ARRAY_SIZE(rt5670_dai)); + if (ret < 0) + goto err; + + return 0; +err: + return ret; +} + +static int rt5670_i2c_remove(struct i2c_client *i2c) +{ + snd_soc_unregister_codec(&i2c->dev); + + return 0; +} + +struct i2c_driver rt5670_i2c_driver = { + .driver = { + .name = "rt5670", + .owner = THIS_MODULE, + }, + .probe = rt5670_i2c_probe, + .remove = rt5670_i2c_remove, + .id_table = rt5670_i2c_id, +}; + +module_i2c_driver(rt5670_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5670 driver"); +MODULE_AUTHOR("Bard Liao "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5670.h b/sound/soc/codecs/rt5670.h new file mode 100644 index 000000000000..a0b5c855b492 --- /dev/null +++ b/sound/soc/codecs/rt5670.h @@ -0,0 +1,2000 @@ +/* + * rt5670.h -- RT5670 ALSA SoC audio driver + * + * Copyright 2014 Realtek Microelectronics + * Author: Bard Liao + * + * 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 __RT5670_H__ +#define __RT5670_H__ + +#include + +/* Info */ +#define RT5670_RESET 0x00 +#define RT5670_VENDOR_ID 0xfd +#define RT5670_VENDOR_ID1 0xfe +#define RT5670_VENDOR_ID2 0xff +/* I/O - Output */ +#define RT5670_HP_VOL 0x02 +#define RT5670_LOUT1 0x03 +/* I/O - Input */ +#define RT5670_CJ_CTRL1 0x0a +#define RT5670_CJ_CTRL2 0x0b +#define RT5670_CJ_CTRL3 0x0c +#define RT5670_IN2 0x0e +#define RT5670_INL1_INR1_VOL 0x0f +/* I/O - ADC/DAC/DMIC */ +#define RT5670_DAC1_DIG_VOL 0x19 +#define RT5670_DAC2_DIG_VOL 0x1a +#define RT5670_DAC_CTRL 0x1b +#define RT5670_STO1_ADC_DIG_VOL 0x1c +#define RT5670_MONO_ADC_DIG_VOL 0x1d +#define RT5670_ADC_BST_VOL1 0x1e +#define RT5670_STO2_ADC_DIG_VOL 0x1f +/* Mixer - D-D */ +#define RT5670_ADC_BST_VOL2 0x20 +#define RT5670_STO2_ADC_MIXER 0x26 +#define RT5670_STO1_ADC_MIXER 0x27 +#define RT5670_MONO_ADC_MIXER 0x28 +#define RT5670_AD_DA_MIXER 0x29 +#define RT5670_STO_DAC_MIXER 0x2a +#define RT5670_DD_MIXER 0x2b +#define RT5670_DIG_MIXER 0x2c +#define RT5670_DSP_PATH1 0x2d +#define RT5670_DSP_PATH2 0x2e +#define RT5670_DIG_INF1_DATA 0x2f +#define RT5670_DIG_INF2_DATA 0x30 +/* Mixer - PDM */ +#define RT5670_PDM_OUT_CTRL 0x31 +#define RT5670_PDM_DATA_CTRL1 0x32 +#define RT5670_PDM1_DATA_CTRL2 0x33 +#define RT5670_PDM1_DATA_CTRL3 0x34 +#define RT5670_PDM1_DATA_CTRL4 0x35 +#define RT5670_PDM2_DATA_CTRL2 0x36 +#define RT5670_PDM2_DATA_CTRL3 0x37 +#define RT5670_PDM2_DATA_CTRL4 0x38 +/* Mixer - ADC */ +#define RT5670_REC_L1_MIXER 0x3b +#define RT5670_REC_L2_MIXER 0x3c +#define RT5670_REC_R1_MIXER 0x3d +#define RT5670_REC_R2_MIXER 0x3e +/* Mixer - DAC */ +#define RT5670_HPO_MIXER 0x45 +#define RT5670_MONO_MIXER 0x4c +#define RT5670_OUT_L1_MIXER 0x4f +#define RT5670_OUT_R1_MIXER 0x52 +#define RT5670_LOUT_MIXER 0x53 +/* Power */ +#define RT5670_PWR_DIG1 0x61 +#define RT5670_PWR_DIG2 0x62 +#define RT5670_PWR_ANLG1 0x63 +#define RT5670_PWR_ANLG2 0x64 +#define RT5670_PWR_MIXER 0x65 +#define RT5670_PWR_VOL 0x66 +/* Private Register Control */ +#define RT5670_PRIV_INDEX 0x6a +#define RT5670_PRIV_DATA 0x6c +/* Format - ADC/DAC */ +#define RT5670_I2S4_SDP 0x6f +#define RT5670_I2S1_SDP 0x70 +#define RT5670_I2S2_SDP 0x71 +#define RT5670_I2S3_SDP 0x72 +#define RT5670_ADDA_CLK1 0x73 +#define RT5670_ADDA_CLK2 0x74 +#define RT5670_DMIC_CTRL1 0x75 +#define RT5670_DMIC_CTRL2 0x76 +/* Format - TDM Control */ +#define RT5670_TDM_CTRL_1 0x77 +#define RT5670_TDM_CTRL_2 0x78 +#define RT5670_TDM_CTRL_3 0x79 + +/* Function - Analog */ +#define RT5670_DSP_CLK 0x7f +#define RT5670_GLB_CLK 0x80 +#define RT5670_PLL_CTRL1 0x81 +#define RT5670_PLL_CTRL2 0x82 +#define RT5670_ASRC_1 0x83 +#define RT5670_ASRC_2 0x84 +#define RT5670_ASRC_3 0x85 +#define RT5670_ASRC_4 0x86 +#define RT5670_ASRC_5 0x87 +#define RT5670_ASRC_7 0x89 +#define RT5670_ASRC_8 0x8a +#define RT5670_ASRC_9 0x8b +#define RT5670_ASRC_10 0x8c +#define RT5670_ASRC_11 0x8d +#define RT5670_DEPOP_M1 0x8e +#define RT5670_DEPOP_M2 0x8f +#define RT5670_DEPOP_M3 0x90 +#define RT5670_CHARGE_PUMP 0x91 +#define RT5670_MICBIAS 0x93 +#define RT5670_A_JD_CTRL1 0x94 +#define RT5670_A_JD_CTRL2 0x95 +#define RT5670_ASRC_12 0x97 +#define RT5670_ASRC_13 0x98 +#define RT5670_ASRC_14 0x99 +#define RT5670_VAD_CTRL1 0x9a +#define RT5670_VAD_CTRL2 0x9b +#define RT5670_VAD_CTRL3 0x9c +#define RT5670_VAD_CTRL4 0x9d +#define RT5670_VAD_CTRL5 0x9e +/* Function - Digital */ +#define RT5670_ADC_EQ_CTRL1 0xae +#define RT5670_ADC_EQ_CTRL2 0xaf +#define RT5670_EQ_CTRL1 0xb0 +#define RT5670_EQ_CTRL2 0xb1 +#define RT5670_ALC_DRC_CTRL1 0xb2 +#define RT5670_ALC_DRC_CTRL2 0xb3 +#define RT5670_ALC_CTRL_1 0xb4 +#define RT5670_ALC_CTRL_2 0xb5 +#define RT5670_ALC_CTRL_3 0xb6 +#define RT5670_ALC_CTRL_4 0xb7 +#define RT5670_JD_CTRL 0xbb +#define RT5670_IRQ_CTRL1 0xbd +#define RT5670_IRQ_CTRL2 0xbe +#define RT5670_INT_IRQ_ST 0xbf +#define RT5670_GPIO_CTRL1 0xc0 +#define RT5670_GPIO_CTRL2 0xc1 +#define RT5670_GPIO_CTRL3 0xc2 +#define RT5670_SCRABBLE_FUN 0xcd +#define RT5670_SCRABBLE_CTRL 0xce +#define RT5670_BASE_BACK 0xcf +#define RT5670_MP3_PLUS1 0xd0 +#define RT5670_MP3_PLUS2 0xd1 +#define RT5670_ADJ_HPF1 0xd3 +#define RT5670_ADJ_HPF2 0xd4 +#define RT5670_HP_CALIB_AMP_DET 0xd6 +#define RT5670_SV_ZCD1 0xd9 +#define RT5670_SV_ZCD2 0xda +#define RT5670_IL_CMD 0xdb +#define RT5670_IL_CMD2 0xdc +#define RT5670_IL_CMD3 0xdd +#define RT5670_DRC_HL_CTRL1 0xe6 +#define RT5670_DRC_HL_CTRL2 0xe7 +#define RT5670_ADC_MONO_HP_CTRL1 0xec +#define RT5670_ADC_MONO_HP_CTRL2 0xed +#define RT5670_ADC_STO2_HP_CTRL1 0xee +#define RT5670_ADC_STO2_HP_CTRL2 0xef +#define RT5670_JD_CTRL3 0xf8 +#define RT5670_JD_CTRL4 0xf9 +/* General Control */ +#define RT5670_DIG_MISC 0xfa +#define RT5670_GEN_CTRL2 0xfb +#define RT5670_GEN_CTRL3 0xfc + + +/* Index of Codec Private Register definition */ +#define RT5670_DIG_VOL 0x00 +#define RT5670_PR_ALC_CTRL_1 0x01 +#define RT5670_PR_ALC_CTRL_2 0x02 +#define RT5670_PR_ALC_CTRL_3 0x03 +#define RT5670_PR_ALC_CTRL_4 0x04 +#define RT5670_PR_ALC_CTRL_5 0x05 +#define RT5670_PR_ALC_CTRL_6 0x06 +#define RT5670_BIAS_CUR1 0x12 +#define RT5670_BIAS_CUR3 0x14 +#define RT5670_CLSD_INT_REG1 0x1c +#define RT5670_MAMP_INT_REG2 0x37 +#define RT5670_CHOP_DAC_ADC 0x3d +#define RT5670_MIXER_INT_REG 0x3f +#define RT5670_3D_SPK 0x63 +#define RT5670_WND_1 0x6c +#define RT5670_WND_2 0x6d +#define RT5670_WND_3 0x6e +#define RT5670_WND_4 0x6f +#define RT5670_WND_5 0x70 +#define RT5670_WND_8 0x73 +#define RT5670_DIP_SPK_INF 0x75 +#define RT5670_HP_DCC_INT1 0x77 +#define RT5670_EQ_BW_LOP 0xa0 +#define RT5670_EQ_GN_LOP 0xa1 +#define RT5670_EQ_FC_BP1 0xa2 +#define RT5670_EQ_BW_BP1 0xa3 +#define RT5670_EQ_GN_BP1 0xa4 +#define RT5670_EQ_FC_BP2 0xa5 +#define RT5670_EQ_BW_BP2 0xa6 +#define RT5670_EQ_GN_BP2 0xa7 +#define RT5670_EQ_FC_BP3 0xa8 +#define RT5670_EQ_BW_BP3 0xa9 +#define RT5670_EQ_GN_BP3 0xaa +#define RT5670_EQ_FC_BP4 0xab +#define RT5670_EQ_BW_BP4 0xac +#define RT5670_EQ_GN_BP4 0xad +#define RT5670_EQ_FC_HIP1 0xae +#define RT5670_EQ_GN_HIP1 0xaf +#define RT5670_EQ_FC_HIP2 0xb0 +#define RT5670_EQ_BW_HIP2 0xb1 +#define RT5670_EQ_GN_HIP2 0xb2 +#define RT5670_EQ_PRE_VOL 0xb3 +#define RT5670_EQ_PST_VOL 0xb4 + + +/* global definition */ +#define RT5670_L_MUTE (0x1 << 15) +#define RT5670_L_MUTE_SFT 15 +#define RT5670_VOL_L_MUTE (0x1 << 14) +#define RT5670_VOL_L_SFT 14 +#define RT5670_R_MUTE (0x1 << 7) +#define RT5670_R_MUTE_SFT 7 +#define RT5670_VOL_R_MUTE (0x1 << 6) +#define RT5670_VOL_R_SFT 6 +#define RT5670_L_VOL_MASK (0x3f << 8) +#define RT5670_L_VOL_SFT 8 +#define RT5670_R_VOL_MASK (0x3f) +#define RT5670_R_VOL_SFT 0 + +/* Combo Jack Control 1 (0x0a) */ +#define RT5670_CBJ_BST1_MASK (0xf << 12) +#define RT5670_CBJ_BST1_SFT (12) +#define RT5670_CBJ_JD_HP_EN (0x1 << 9) +#define RT5670_CBJ_JD_MIC_EN (0x1 << 8) +#define RT5670_CBJ_BST1_EN (0x1 << 2) + +/* Combo Jack Control 1 (0x0b) */ +#define RT5670_CBJ_MN_JD (0x1 << 12) +#define RT5670_CAPLESS_EN (0x1 << 11) +#define RT5670_CBJ_DET_MODE (0x1 << 7) + +/* IN2 Control (0x0e) */ +#define RT5670_BST_MASK1 (0xf<<12) +#define RT5670_BST_SFT1 12 +#define RT5670_BST_MASK2 (0xf<<8) +#define RT5670_BST_SFT2 8 +#define RT5670_IN_DF1 (0x1 << 7) +#define RT5670_IN_SFT1 7 +#define RT5670_IN_DF2 (0x1 << 6) +#define RT5670_IN_SFT2 6 + +/* INL and INR Volume Control (0x0f) */ +#define RT5670_INL_SEL_MASK (0x1 << 15) +#define RT5670_INL_SEL_SFT 15 +#define RT5670_INL_SEL_IN4P (0x0 << 15) +#define RT5670_INL_SEL_MONOP (0x1 << 15) +#define RT5670_INL_VOL_MASK (0x1f << 8) +#define RT5670_INL_VOL_SFT 8 +#define RT5670_INR_SEL_MASK (0x1 << 7) +#define RT5670_INR_SEL_SFT 7 +#define RT5670_INR_SEL_IN4N (0x0 << 7) +#define RT5670_INR_SEL_MONON (0x1 << 7) +#define RT5670_INR_VOL_MASK (0x1f) +#define RT5670_INR_VOL_SFT 0 + +/* Sidetone Control (0x18) */ +#define RT5670_ST_SEL_MASK (0x7 << 9) +#define RT5670_ST_SEL_SFT 9 +#define RT5670_M_ST_DACR2 (0x1 << 8) +#define RT5670_M_ST_DACR2_SFT 8 +#define RT5670_M_ST_DACL2 (0x1 << 7) +#define RT5670_M_ST_DACL2_SFT 7 +#define RT5670_ST_EN (0x1 << 6) +#define RT5670_ST_EN_SFT 6 + +/* DAC1 Digital Volume (0x19) */ +#define RT5670_DAC_L1_VOL_MASK (0xff << 8) +#define RT5670_DAC_L1_VOL_SFT 8 +#define RT5670_DAC_R1_VOL_MASK (0xff) +#define RT5670_DAC_R1_VOL_SFT 0 + +/* DAC2 Digital Volume (0x1a) */ +#define RT5670_DAC_L2_VOL_MASK (0xff << 8) +#define RT5670_DAC_L2_VOL_SFT 8 +#define RT5670_DAC_R2_VOL_MASK (0xff) +#define RT5670_DAC_R2_VOL_SFT 0 + +/* DAC2 Control (0x1b) */ +#define RT5670_M_DAC_L2_VOL (0x1 << 13) +#define RT5670_M_DAC_L2_VOL_SFT 13 +#define RT5670_M_DAC_R2_VOL (0x1 << 12) +#define RT5670_M_DAC_R2_VOL_SFT 12 +#define RT5670_DAC2_L_SEL_MASK (0x7 << 4) +#define RT5670_DAC2_L_SEL_SFT 4 +#define RT5670_DAC2_R_SEL_MASK (0x7 << 0) +#define RT5670_DAC2_R_SEL_SFT 0 + +/* ADC Digital Volume Control (0x1c) */ +#define RT5670_ADC_L_VOL_MASK (0x7f << 8) +#define RT5670_ADC_L_VOL_SFT 8 +#define RT5670_ADC_R_VOL_MASK (0x7f) +#define RT5670_ADC_R_VOL_SFT 0 + +/* Mono ADC Digital Volume Control (0x1d) */ +#define RT5670_MONO_ADC_L_VOL_MASK (0x7f << 8) +#define RT5670_MONO_ADC_L_VOL_SFT 8 +#define RT5670_MONO_ADC_R_VOL_MASK (0x7f) +#define RT5670_MONO_ADC_R_VOL_SFT 0 + +/* ADC Boost Volume Control (0x1e) */ +#define RT5670_STO1_ADC_L_BST_MASK (0x3 << 14) +#define RT5670_STO1_ADC_L_BST_SFT 14 +#define RT5670_STO1_ADC_R_BST_MASK (0x3 << 12) +#define RT5670_STO1_ADC_R_BST_SFT 12 +#define RT5670_STO1_ADC_COMP_MASK (0x3 << 10) +#define RT5670_STO1_ADC_COMP_SFT 10 +#define RT5670_STO2_ADC_L_BST_MASK (0x3 << 8) +#define RT5670_STO2_ADC_L_BST_SFT 8 +#define RT5670_STO2_ADC_R_BST_MASK (0x3 << 6) +#define RT5670_STO2_ADC_R_BST_SFT 6 +#define RT5670_STO2_ADC_COMP_MASK (0x3 << 4) +#define RT5670_STO2_ADC_COMP_SFT 4 + +/* Stereo2 ADC Mixer Control (0x26) */ +#define RT5670_STO2_ADC_SRC_MASK (0x1 << 15) +#define RT5670_STO2_ADC_SRC_SFT 15 + +/* Stereo ADC Mixer Control (0x26 0x27) */ +#define RT5670_M_ADC_L1 (0x1 << 14) +#define RT5670_M_ADC_L1_SFT 14 +#define RT5670_M_ADC_L2 (0x1 << 13) +#define RT5670_M_ADC_L2_SFT 13 +#define RT5670_ADC_1_SRC_MASK (0x1 << 12) +#define RT5670_ADC_1_SRC_SFT 12 +#define RT5670_ADC_1_SRC_ADC (0x1 << 12) +#define RT5670_ADC_1_SRC_DACMIX (0x0 << 12) +#define RT5670_ADC_2_SRC_MASK (0x1 << 11) +#define RT5670_ADC_2_SRC_SFT 11 +#define RT5670_ADC_SRC_MASK (0x1 << 10) +#define RT5670_ADC_SRC_SFT 10 +#define RT5670_DMIC_SRC_MASK (0x3 << 8) +#define RT5670_DMIC_SRC_SFT 8 +#define RT5670_M_ADC_R1 (0x1 << 6) +#define RT5670_M_ADC_R1_SFT 6 +#define RT5670_M_ADC_R2 (0x1 << 5) +#define RT5670_M_ADC_R2_SFT 5 +#define RT5670_DMIC3_SRC_MASK (0x1 << 1) +#define RT5670_DMIC3_SRC_SFT 0 + +/* Mono ADC Mixer Control (0x28) */ +#define RT5670_M_MONO_ADC_L1 (0x1 << 14) +#define RT5670_M_MONO_ADC_L1_SFT 14 +#define RT5670_M_MONO_ADC_L2 (0x1 << 13) +#define RT5670_M_MONO_ADC_L2_SFT 13 +#define RT5670_MONO_ADC_L1_SRC_MASK (0x1 << 12) +#define RT5670_MONO_ADC_L1_SRC_SFT 12 +#define RT5670_MONO_ADC_L1_SRC_DACMIXL (0x0 << 12) +#define RT5670_MONO_ADC_L1_SRC_ADCL (0x1 << 12) +#define RT5670_MONO_ADC_L2_SRC_MASK (0x1 << 11) +#define RT5670_MONO_ADC_L2_SRC_SFT 11 +#define RT5670_MONO_ADC_L_SRC_MASK (0x1 << 10) +#define RT5670_MONO_ADC_L_SRC_SFT 10 +#define RT5670_MONO_DMIC_L_SRC_MASK (0x3 << 8) +#define RT5670_MONO_DMIC_L_SRC_SFT 8 +#define RT5670_M_MONO_ADC_R1 (0x1 << 6) +#define RT5670_M_MONO_ADC_R1_SFT 6 +#define RT5670_M_MONO_ADC_R2 (0x1 << 5) +#define RT5670_M_MONO_ADC_R2_SFT 5 +#define RT5670_MONO_ADC_R1_SRC_MASK (0x1 << 4) +#define RT5670_MONO_ADC_R1_SRC_SFT 4 +#define RT5670_MONO_ADC_R1_SRC_ADCR (0x1 << 4) +#define RT5670_MONO_ADC_R1_SRC_DACMIXR (0x0 << 4) +#define RT5670_MONO_ADC_R2_SRC_MASK (0x1 << 3) +#define RT5670_MONO_ADC_R2_SRC_SFT 3 +#define RT5670_MONO_DMIC_R_SRC_MASK (0x3) +#define RT5670_MONO_DMIC_R_SRC_SFT 0 + +/* ADC Mixer to DAC Mixer Control (0x29) */ +#define RT5670_M_ADCMIX_L (0x1 << 15) +#define RT5670_M_ADCMIX_L_SFT 15 +#define RT5670_M_DAC1_L (0x1 << 14) +#define RT5670_M_DAC1_L_SFT 14 +#define RT5670_DAC1_R_SEL_MASK (0x3 << 10) +#define RT5670_DAC1_R_SEL_SFT 10 +#define RT5670_DAC1_R_SEL_IF1 (0x0 << 10) +#define RT5670_DAC1_R_SEL_IF2 (0x1 << 10) +#define RT5670_DAC1_R_SEL_IF3 (0x2 << 10) +#define RT5670_DAC1_R_SEL_IF4 (0x3 << 10) +#define RT5670_DAC1_L_SEL_MASK (0x3 << 8) +#define RT5670_DAC1_L_SEL_SFT 8 +#define RT5670_DAC1_L_SEL_IF1 (0x0 << 8) +#define RT5670_DAC1_L_SEL_IF2 (0x1 << 8) +#define RT5670_DAC1_L_SEL_IF3 (0x2 << 8) +#define RT5670_DAC1_L_SEL_IF4 (0x3 << 8) +#define RT5670_M_ADCMIX_R (0x1 << 7) +#define RT5670_M_ADCMIX_R_SFT 7 +#define RT5670_M_DAC1_R (0x1 << 6) +#define RT5670_M_DAC1_R_SFT 6 + +/* Stereo DAC Mixer Control (0x2a) */ +#define RT5670_M_DAC_L1 (0x1 << 14) +#define RT5670_M_DAC_L1_SFT 14 +#define RT5670_DAC_L1_STO_L_VOL_MASK (0x1 << 13) +#define RT5670_DAC_L1_STO_L_VOL_SFT 13 +#define RT5670_M_DAC_L2 (0x1 << 12) +#define RT5670_M_DAC_L2_SFT 12 +#define RT5670_DAC_L2_STO_L_VOL_MASK (0x1 << 11) +#define RT5670_DAC_L2_STO_L_VOL_SFT 11 +#define RT5670_M_DAC_R1_STO_L (0x1 << 9) +#define RT5670_M_DAC_R1_STO_L_SFT 9 +#define RT5670_DAC_R1_STO_L_VOL_MASK (0x1 << 8) +#define RT5670_DAC_R1_STO_L_VOL_SFT 8 +#define RT5670_M_DAC_R1 (0x1 << 6) +#define RT5670_M_DAC_R1_SFT 6 +#define RT5670_DAC_R1_STO_R_VOL_MASK (0x1 << 5) +#define RT5670_DAC_R1_STO_R_VOL_SFT 5 +#define RT5670_M_DAC_R2 (0x1 << 4) +#define RT5670_M_DAC_R2_SFT 4 +#define RT5670_DAC_R2_STO_R_VOL_MASK (0x1 << 3) +#define RT5670_DAC_R2_STO_R_VOL_SFT 3 +#define RT5670_M_DAC_L1_STO_R (0x1 << 1) +#define RT5670_M_DAC_L1_STO_R_SFT 1 +#define RT5670_DAC_L1_STO_R_VOL_MASK (0x1) +#define RT5670_DAC_L1_STO_R_VOL_SFT 0 + +/* Mono DAC Mixer Control (0x2b) */ +#define RT5670_M_DAC_L1_MONO_L (0x1 << 14) +#define RT5670_M_DAC_L1_MONO_L_SFT 14 +#define RT5670_DAC_L1_MONO_L_VOL_MASK (0x1 << 13) +#define RT5670_DAC_L1_MONO_L_VOL_SFT 13 +#define RT5670_M_DAC_L2_MONO_L (0x1 << 12) +#define RT5670_M_DAC_L2_MONO_L_SFT 12 +#define RT5670_DAC_L2_MONO_L_VOL_MASK (0x1 << 11) +#define RT5670_DAC_L2_MONO_L_VOL_SFT 11 +#define RT5670_M_DAC_R2_MONO_L (0x1 << 10) +#define RT5670_M_DAC_R2_MONO_L_SFT 10 +#define RT5670_DAC_R2_MONO_L_VOL_MASK (0x1 << 9) +#define RT5670_DAC_R2_MONO_L_VOL_SFT 9 +#define RT5670_M_DAC_R1_MONO_R (0x1 << 6) +#define RT5670_M_DAC_R1_MONO_R_SFT 6 +#define RT5670_DAC_R1_MONO_R_VOL_MASK (0x1 << 5) +#define RT5670_DAC_R1_MONO_R_VOL_SFT 5 +#define RT5670_M_DAC_R2_MONO_R (0x1 << 4) +#define RT5670_M_DAC_R2_MONO_R_SFT 4 +#define RT5670_DAC_R2_MONO_R_VOL_MASK (0x1 << 3) +#define RT5670_DAC_R2_MONO_R_VOL_SFT 3 +#define RT5670_M_DAC_L2_MONO_R (0x1 << 2) +#define RT5670_M_DAC_L2_MONO_R_SFT 2 +#define RT5670_DAC_L2_MONO_R_VOL_MASK (0x1 << 1) +#define RT5670_DAC_L2_MONO_R_VOL_SFT 1 + +/* Digital Mixer Control (0x2c) */ +#define RT5670_M_STO_L_DAC_L (0x1 << 15) +#define RT5670_M_STO_L_DAC_L_SFT 15 +#define RT5670_STO_L_DAC_L_VOL_MASK (0x1 << 14) +#define RT5670_STO_L_DAC_L_VOL_SFT 14 +#define RT5670_M_DAC_L2_DAC_L (0x1 << 13) +#define RT5670_M_DAC_L2_DAC_L_SFT 13 +#define RT5670_DAC_L2_DAC_L_VOL_MASK (0x1 << 12) +#define RT5670_DAC_L2_DAC_L_VOL_SFT 12 +#define RT5670_M_STO_R_DAC_R (0x1 << 11) +#define RT5670_M_STO_R_DAC_R_SFT 11 +#define RT5670_STO_R_DAC_R_VOL_MASK (0x1 << 10) +#define RT5670_STO_R_DAC_R_VOL_SFT 10 +#define RT5670_M_DAC_R2_DAC_R (0x1 << 9) +#define RT5670_M_DAC_R2_DAC_R_SFT 9 +#define RT5670_DAC_R2_DAC_R_VOL_MASK (0x1 << 8) +#define RT5670_DAC_R2_DAC_R_VOL_SFT 8 +#define RT5670_M_DAC_R2_DAC_L (0x1 << 7) +#define RT5670_M_DAC_R2_DAC_L_SFT 7 +#define RT5670_DAC_R2_DAC_L_VOL_MASK (0x1 << 6) +#define RT5670_DAC_R2_DAC_L_VOL_SFT 6 +#define RT5670_M_DAC_L2_DAC_R (0x1 << 5) +#define RT5670_M_DAC_L2_DAC_R_SFT 5 +#define RT5670_DAC_L2_DAC_R_VOL_MASK (0x1 << 4) +#define RT5670_DAC_L2_DAC_R_VOL_SFT 4 + +/* DSP Path Control 1 (0x2d) */ +#define RT5670_RXDP_SEL_MASK (0x7 << 13) +#define RT5670_RXDP_SEL_SFT 13 +#define RT5670_RXDP_SRC_MASK (0x3 << 11) +#define RT5670_RXDP_SRC_SFT 11 +#define RT5670_RXDP_SRC_NOR (0x0 << 11) +#define RT5670_RXDP_SRC_DIV2 (0x1 << 11) +#define RT5670_RXDP_SRC_DIV3 (0x2 << 11) +#define RT5670_TXDP_SRC_MASK (0x3 << 4) +#define RT5670_TXDP_SRC_SFT 4 +#define RT5670_TXDP_SRC_NOR (0x0 << 4) +#define RT5670_TXDP_SRC_DIV2 (0x1 << 4) +#define RT5670_TXDP_SRC_DIV3 (0x2 << 4) +#define RT5670_TXDP_SLOT_SEL_MASK (0x3 << 2) +#define RT5670_TXDP_SLOT_SEL_SFT 2 +#define RT5670_DSP_UL_SEL (0x1 << 1) +#define RT5670_DSP_UL_SFT 1 +#define RT5670_DSP_DL_SEL 0x1 +#define RT5670_DSP_DL_SFT 0 + +/* DSP Path Control 2 (0x2e) */ +#define RT5670_TXDP_L_VOL_MASK (0x7f << 8) +#define RT5670_TXDP_L_VOL_SFT 8 +#define RT5670_TXDP_R_VOL_MASK (0x7f) +#define RT5670_TXDP_R_VOL_SFT 0 + +/* Digital Interface Data Control (0x2f) */ +#define RT5670_IF1_ADC2_IN_SEL (0x1 << 15) +#define RT5670_IF1_ADC2_IN_SFT 15 +#define RT5670_IF2_ADC_IN_MASK (0x7 << 12) +#define RT5670_IF2_ADC_IN_SFT 12 +#define RT5670_IF2_DAC_SEL_MASK (0x3 << 10) +#define RT5670_IF2_DAC_SEL_SFT 10 +#define RT5670_IF2_ADC_SEL_MASK (0x3 << 8) +#define RT5670_IF2_ADC_SEL_SFT 8 + +/* Digital Interface Data Control (0x30) */ +#define RT5670_IF4_ADC_IN_MASK (0x3 << 4) +#define RT5670_IF4_ADC_IN_SFT 4 + +/* PDM Output Control (0x31) */ +#define RT5670_PDM1_L_MASK (0x1 << 15) +#define RT5670_PDM1_L_SFT 15 +#define RT5670_M_PDM1_L (0x1 << 14) +#define RT5670_M_PDM1_L_SFT 14 +#define RT5670_PDM1_R_MASK (0x1 << 13) +#define RT5670_PDM1_R_SFT 13 +#define RT5670_M_PDM1_R (0x1 << 12) +#define RT5670_M_PDM1_R_SFT 12 +#define RT5670_PDM2_L_MASK (0x1 << 11) +#define RT5670_PDM2_L_SFT 11 +#define RT5670_M_PDM2_L (0x1 << 10) +#define RT5670_M_PDM2_L_SFT 10 +#define RT5670_PDM2_R_MASK (0x1 << 9) +#define RT5670_PDM2_R_SFT 9 +#define RT5670_M_PDM2_R (0x1 << 8) +#define RT5670_M_PDM2_R_SFT 8 +#define RT5670_PDM2_BUSY (0x1 << 7) +#define RT5670_PDM1_BUSY (0x1 << 6) +#define RT5670_PDM_PATTERN (0x1 << 5) +#define RT5670_PDM_GAIN (0x1 << 4) +#define RT5670_PDM_DIV_MASK (0x3) + +/* REC Left Mixer Control 1 (0x3b) */ +#define RT5670_G_HP_L_RM_L_MASK (0x7 << 13) +#define RT5670_G_HP_L_RM_L_SFT 13 +#define RT5670_G_IN_L_RM_L_MASK (0x7 << 10) +#define RT5670_G_IN_L_RM_L_SFT 10 +#define RT5670_G_BST4_RM_L_MASK (0x7 << 7) +#define RT5670_G_BST4_RM_L_SFT 7 +#define RT5670_G_BST3_RM_L_MASK (0x7 << 4) +#define RT5670_G_BST3_RM_L_SFT 4 +#define RT5670_G_BST2_RM_L_MASK (0x7 << 1) +#define RT5670_G_BST2_RM_L_SFT 1 + +/* REC Left Mixer Control 2 (0x3c) */ +#define RT5670_G_BST1_RM_L_MASK (0x7 << 13) +#define RT5670_G_BST1_RM_L_SFT 13 +#define RT5670_M_IN_L_RM_L (0x1 << 5) +#define RT5670_M_IN_L_RM_L_SFT 5 +#define RT5670_M_BST2_RM_L (0x1 << 3) +#define RT5670_M_BST2_RM_L_SFT 3 +#define RT5670_M_BST1_RM_L (0x1 << 1) +#define RT5670_M_BST1_RM_L_SFT 1 + +/* REC Right Mixer Control 1 (0x3d) */ +#define RT5670_G_HP_R_RM_R_MASK (0x7 << 13) +#define RT5670_G_HP_R_RM_R_SFT 13 +#define RT5670_G_IN_R_RM_R_MASK (0x7 << 10) +#define RT5670_G_IN_R_RM_R_SFT 10 +#define RT5670_G_BST4_RM_R_MASK (0x7 << 7) +#define RT5670_G_BST4_RM_R_SFT 7 +#define RT5670_G_BST3_RM_R_MASK (0x7 << 4) +#define RT5670_G_BST3_RM_R_SFT 4 +#define RT5670_G_BST2_RM_R_MASK (0x7 << 1) +#define RT5670_G_BST2_RM_R_SFT 1 + +/* REC Right Mixer Control 2 (0x3e) */ +#define RT5670_G_BST1_RM_R_MASK (0x7 << 13) +#define RT5670_G_BST1_RM_R_SFT 13 +#define RT5670_M_IN_R_RM_R (0x1 << 5) +#define RT5670_M_IN_R_RM_R_SFT 5 +#define RT5670_M_BST2_RM_R (0x1 << 3) +#define RT5670_M_BST2_RM_R_SFT 3 +#define RT5670_M_BST1_RM_R (0x1 << 1) +#define RT5670_M_BST1_RM_R_SFT 1 + +/* HPMIX Control (0x45) */ +#define RT5670_M_DAC2_HM (0x1 << 15) +#define RT5670_M_DAC2_HM_SFT 15 +#define RT5670_M_HPVOL_HM (0x1 << 14) +#define RT5670_M_HPVOL_HM_SFT 14 +#define RT5670_M_DAC1_HM (0x1 << 13) +#define RT5670_M_DAC1_HM_SFT 13 +#define RT5670_G_HPOMIX_MASK (0x1 << 12) +#define RT5670_G_HPOMIX_SFT 12 +#define RT5670_M_INR1_HMR (0x1 << 3) +#define RT5670_M_INR1_HMR_SFT 3 +#define RT5670_M_DACR1_HMR (0x1 << 2) +#define RT5670_M_DACR1_HMR_SFT 2 +#define RT5670_M_INL1_HML (0x1 << 1) +#define RT5670_M_INL1_HML_SFT 1 +#define RT5670_M_DACL1_HML (0x1) +#define RT5670_M_DACL1_HML_SFT 0 + +/* Mono Output Mixer Control (0x4c) */ +#define RT5670_M_DAC_R2_MA (0x1 << 15) +#define RT5670_M_DAC_R2_MA_SFT 15 +#define RT5670_M_DAC_L2_MA (0x1 << 14) +#define RT5670_M_DAC_L2_MA_SFT 14 +#define RT5670_M_OV_R_MM (0x1 << 13) +#define RT5670_M_OV_R_MM_SFT 13 +#define RT5670_M_OV_L_MM (0x1 << 12) +#define RT5670_M_OV_L_MM_SFT 12 +#define RT5670_G_MONOMIX_MASK (0x1 << 10) +#define RT5670_G_MONOMIX_SFT 10 +#define RT5670_M_DAC_R2_MM (0x1 << 9) +#define RT5670_M_DAC_R2_MM_SFT 9 +#define RT5670_M_DAC_L2_MM (0x1 << 8) +#define RT5670_M_DAC_L2_MM_SFT 8 +#define RT5670_M_BST4_MM (0x1 << 7) +#define RT5670_M_BST4_MM_SFT 7 + +/* Output Left Mixer Control 1 (0x4d) */ +#define RT5670_G_BST3_OM_L_MASK (0x7 << 13) +#define RT5670_G_BST3_OM_L_SFT 13 +#define RT5670_G_BST2_OM_L_MASK (0x7 << 10) +#define RT5670_G_BST2_OM_L_SFT 10 +#define RT5670_G_BST1_OM_L_MASK (0x7 << 7) +#define RT5670_G_BST1_OM_L_SFT 7 +#define RT5670_G_IN_L_OM_L_MASK (0x7 << 4) +#define RT5670_G_IN_L_OM_L_SFT 4 +#define RT5670_G_RM_L_OM_L_MASK (0x7 << 1) +#define RT5670_G_RM_L_OM_L_SFT 1 + +/* Output Left Mixer Control 2 (0x4e) */ +#define RT5670_G_DAC_R2_OM_L_MASK (0x7 << 13) +#define RT5670_G_DAC_R2_OM_L_SFT 13 +#define RT5670_G_DAC_L2_OM_L_MASK (0x7 << 10) +#define RT5670_G_DAC_L2_OM_L_SFT 10 +#define RT5670_G_DAC_L1_OM_L_MASK (0x7 << 7) +#define RT5670_G_DAC_L1_OM_L_SFT 7 + +/* Output Left Mixer Control 3 (0x4f) */ +#define RT5670_M_BST1_OM_L (0x1 << 5) +#define RT5670_M_BST1_OM_L_SFT 5 +#define RT5670_M_IN_L_OM_L (0x1 << 4) +#define RT5670_M_IN_L_OM_L_SFT 4 +#define RT5670_M_DAC_L2_OM_L (0x1 << 1) +#define RT5670_M_DAC_L2_OM_L_SFT 1 +#define RT5670_M_DAC_L1_OM_L (0x1) +#define RT5670_M_DAC_L1_OM_L_SFT 0 + +/* Output Right Mixer Control 1 (0x50) */ +#define RT5670_G_BST4_OM_R_MASK (0x7 << 13) +#define RT5670_G_BST4_OM_R_SFT 13 +#define RT5670_G_BST2_OM_R_MASK (0x7 << 10) +#define RT5670_G_BST2_OM_R_SFT 10 +#define RT5670_G_BST1_OM_R_MASK (0x7 << 7) +#define RT5670_G_BST1_OM_R_SFT 7 +#define RT5670_G_IN_R_OM_R_MASK (0x7 << 4) +#define RT5670_G_IN_R_OM_R_SFT 4 +#define RT5670_G_RM_R_OM_R_MASK (0x7 << 1) +#define RT5670_G_RM_R_OM_R_SFT 1 + +/* Output Right Mixer Control 2 (0x51) */ +#define RT5670_G_DAC_L2_OM_R_MASK (0x7 << 13) +#define RT5670_G_DAC_L2_OM_R_SFT 13 +#define RT5670_G_DAC_R2_OM_R_MASK (0x7 << 10) +#define RT5670_G_DAC_R2_OM_R_SFT 10 +#define RT5670_G_DAC_R1_OM_R_MASK (0x7 << 7) +#define RT5670_G_DAC_R1_OM_R_SFT 7 + +/* Output Right Mixer Control 3 (0x52) */ +#define RT5670_M_BST2_OM_R (0x1 << 6) +#define RT5670_M_BST2_OM_R_SFT 6 +#define RT5670_M_IN_R_OM_R (0x1 << 4) +#define RT5670_M_IN_R_OM_R_SFT 4 +#define RT5670_M_DAC_R2_OM_R (0x1 << 1) +#define RT5670_M_DAC_R2_OM_R_SFT 1 +#define RT5670_M_DAC_R1_OM_R (0x1) +#define RT5670_M_DAC_R1_OM_R_SFT 0 + +/* LOUT Mixer Control (0x53) */ +#define RT5670_M_DAC_L1_LM (0x1 << 15) +#define RT5670_M_DAC_L1_LM_SFT 15 +#define RT5670_M_DAC_R1_LM (0x1 << 14) +#define RT5670_M_DAC_R1_LM_SFT 14 +#define RT5670_M_OV_L_LM (0x1 << 13) +#define RT5670_M_OV_L_LM_SFT 13 +#define RT5670_M_OV_R_LM (0x1 << 12) +#define RT5670_M_OV_R_LM_SFT 12 +#define RT5670_G_LOUTMIX_MASK (0x1 << 11) +#define RT5670_G_LOUTMIX_SFT 11 + +/* Power Management for Digital 1 (0x61) */ +#define RT5670_PWR_I2S1 (0x1 << 15) +#define RT5670_PWR_I2S1_BIT 15 +#define RT5670_PWR_I2S2 (0x1 << 14) +#define RT5670_PWR_I2S2_BIT 14 +#define RT5670_PWR_DAC_L1 (0x1 << 12) +#define RT5670_PWR_DAC_L1_BIT 12 +#define RT5670_PWR_DAC_R1 (0x1 << 11) +#define RT5670_PWR_DAC_R1_BIT 11 +#define RT5670_PWR_DAC_L2 (0x1 << 7) +#define RT5670_PWR_DAC_L2_BIT 7 +#define RT5670_PWR_DAC_R2 (0x1 << 6) +#define RT5670_PWR_DAC_R2_BIT 6 +#define RT5670_PWR_ADC_L (0x1 << 2) +#define RT5670_PWR_ADC_L_BIT 2 +#define RT5670_PWR_ADC_R (0x1 << 1) +#define RT5670_PWR_ADC_R_BIT 1 +#define RT5670_PWR_CLS_D (0x1) +#define RT5670_PWR_CLS_D_BIT 0 + +/* Power Management for Digital 2 (0x62) */ +#define RT5670_PWR_ADC_S1F (0x1 << 15) +#define RT5670_PWR_ADC_S1F_BIT 15 +#define RT5670_PWR_ADC_MF_L (0x1 << 14) +#define RT5670_PWR_ADC_MF_L_BIT 14 +#define RT5670_PWR_ADC_MF_R (0x1 << 13) +#define RT5670_PWR_ADC_MF_R_BIT 13 +#define RT5670_PWR_I2S_DSP (0x1 << 12) +#define RT5670_PWR_I2S_DSP_BIT 12 +#define RT5670_PWR_DAC_S1F (0x1 << 11) +#define RT5670_PWR_DAC_S1F_BIT 11 +#define RT5670_PWR_DAC_MF_L (0x1 << 10) +#define RT5670_PWR_DAC_MF_L_BIT 10 +#define RT5670_PWR_DAC_MF_R (0x1 << 9) +#define RT5670_PWR_DAC_MF_R_BIT 9 +#define RT5670_PWR_ADC_S2F (0x1 << 8) +#define RT5670_PWR_ADC_S2F_BIT 8 +#define RT5670_PWR_PDM1 (0x1 << 7) +#define RT5670_PWR_PDM1_BIT 7 +#define RT5670_PWR_PDM2 (0x1 << 6) +#define RT5670_PWR_PDM2_BIT 6 + +/* Power Management for Analog 1 (0x63) */ +#define RT5670_PWR_VREF1 (0x1 << 15) +#define RT5670_PWR_VREF1_BIT 15 +#define RT5670_PWR_FV1 (0x1 << 14) +#define RT5670_PWR_FV1_BIT 14 +#define RT5670_PWR_MB (0x1 << 13) +#define RT5670_PWR_MB_BIT 13 +#define RT5670_PWR_LM (0x1 << 12) +#define RT5670_PWR_LM_BIT 12 +#define RT5670_PWR_BG (0x1 << 11) +#define RT5670_PWR_BG_BIT 11 +#define RT5670_PWR_HP_L (0x1 << 7) +#define RT5670_PWR_HP_L_BIT 7 +#define RT5670_PWR_HP_R (0x1 << 6) +#define RT5670_PWR_HP_R_BIT 6 +#define RT5670_PWR_HA (0x1 << 5) +#define RT5670_PWR_HA_BIT 5 +#define RT5670_PWR_VREF2 (0x1 << 4) +#define RT5670_PWR_VREF2_BIT 4 +#define RT5670_PWR_FV2 (0x1 << 3) +#define RT5670_PWR_FV2_BIT 3 +#define RT5670_LDO_SEL_MASK (0x3) +#define RT5670_LDO_SEL_SFT 0 + +/* Power Management for Analog 2 (0x64) */ +#define RT5670_PWR_BST1 (0x1 << 15) +#define RT5670_PWR_BST1_BIT 15 +#define RT5670_PWR_BST2 (0x1 << 13) +#define RT5670_PWR_BST2_BIT 13 +#define RT5670_PWR_MB1 (0x1 << 11) +#define RT5670_PWR_MB1_BIT 11 +#define RT5670_PWR_MB2 (0x1 << 10) +#define RT5670_PWR_MB2_BIT 10 +#define RT5670_PWR_PLL (0x1 << 9) +#define RT5670_PWR_PLL_BIT 9 +#define RT5670_PWR_BST1_P (0x1 << 6) +#define RT5670_PWR_BST1_P_BIT 6 +#define RT5670_PWR_BST2_P (0x1 << 4) +#define RT5670_PWR_BST2_P_BIT 4 +#define RT5670_PWR_JD1 (0x1 << 2) +#define RT5670_PWR_JD1_BIT 2 +#define RT5670_PWR_JD (0x1 << 1) +#define RT5670_PWR_JD_BIT 1 + +/* Power Management for Mixer (0x65) */ +#define RT5670_PWR_OM_L (0x1 << 15) +#define RT5670_PWR_OM_L_BIT 15 +#define RT5670_PWR_OM_R (0x1 << 14) +#define RT5670_PWR_OM_R_BIT 14 +#define RT5670_PWR_RM_L (0x1 << 11) +#define RT5670_PWR_RM_L_BIT 11 +#define RT5670_PWR_RM_R (0x1 << 10) +#define RT5670_PWR_RM_R_BIT 10 + +/* Power Management for Volume (0x66) */ +#define RT5670_PWR_HV_L (0x1 << 11) +#define RT5670_PWR_HV_L_BIT 11 +#define RT5670_PWR_HV_R (0x1 << 10) +#define RT5670_PWR_HV_R_BIT 10 +#define RT5670_PWR_IN_L (0x1 << 9) +#define RT5670_PWR_IN_L_BIT 9 +#define RT5670_PWR_IN_R (0x1 << 8) +#define RT5670_PWR_IN_R_BIT 8 +#define RT5670_PWR_MIC_DET (0x1 << 5) +#define RT5670_PWR_MIC_DET_BIT 5 + +/* I2S1/2/3 Audio Serial Data Port Control (0x70 0x71 0x72) */ +#define RT5670_I2S_MS_MASK (0x1 << 15) +#define RT5670_I2S_MS_SFT 15 +#define RT5670_I2S_MS_M (0x0 << 15) +#define RT5670_I2S_MS_S (0x1 << 15) +#define RT5670_I2S_IF_MASK (0x7 << 12) +#define RT5670_I2S_IF_SFT 12 +#define RT5670_I2S_O_CP_MASK (0x3 << 10) +#define RT5670_I2S_O_CP_SFT 10 +#define RT5670_I2S_O_CP_OFF (0x0 << 10) +#define RT5670_I2S_O_CP_U_LAW (0x1 << 10) +#define RT5670_I2S_O_CP_A_LAW (0x2 << 10) +#define RT5670_I2S_I_CP_MASK (0x3 << 8) +#define RT5670_I2S_I_CP_SFT 8 +#define RT5670_I2S_I_CP_OFF (0x0 << 8) +#define RT5670_I2S_I_CP_U_LAW (0x1 << 8) +#define RT5670_I2S_I_CP_A_LAW (0x2 << 8) +#define RT5670_I2S_BP_MASK (0x1 << 7) +#define RT5670_I2S_BP_SFT 7 +#define RT5670_I2S_BP_NOR (0x0 << 7) +#define RT5670_I2S_BP_INV (0x1 << 7) +#define RT5670_I2S_DL_MASK (0x3 << 2) +#define RT5670_I2S_DL_SFT 2 +#define RT5670_I2S_DL_16 (0x0 << 2) +#define RT5670_I2S_DL_20 (0x1 << 2) +#define RT5670_I2S_DL_24 (0x2 << 2) +#define RT5670_I2S_DL_8 (0x3 << 2) +#define RT5670_I2S_DF_MASK (0x3) +#define RT5670_I2S_DF_SFT 0 +#define RT5670_I2S_DF_I2S (0x0) +#define RT5670_I2S_DF_LEFT (0x1) +#define RT5670_I2S_DF_PCM_A (0x2) +#define RT5670_I2S_DF_PCM_B (0x3) + +/* I2S2 Audio Serial Data Port Control (0x71) */ +#define RT5670_I2S2_SDI_MASK (0x1 << 6) +#define RT5670_I2S2_SDI_SFT 6 +#define RT5670_I2S2_SDI_I2S1 (0x0 << 6) +#define RT5670_I2S2_SDI_I2S2 (0x1 << 6) + +/* ADC/DAC Clock Control 1 (0x73) */ +#define RT5670_I2S_BCLK_MS1_MASK (0x1 << 15) +#define RT5670_I2S_BCLK_MS1_SFT 15 +#define RT5670_I2S_BCLK_MS1_32 (0x0 << 15) +#define RT5670_I2S_BCLK_MS1_64 (0x1 << 15) +#define RT5670_I2S_PD1_MASK (0x7 << 12) +#define RT5670_I2S_PD1_SFT 12 +#define RT5670_I2S_PD1_1 (0x0 << 12) +#define RT5670_I2S_PD1_2 (0x1 << 12) +#define RT5670_I2S_PD1_3 (0x2 << 12) +#define RT5670_I2S_PD1_4 (0x3 << 12) +#define RT5670_I2S_PD1_6 (0x4 << 12) +#define RT5670_I2S_PD1_8 (0x5 << 12) +#define RT5670_I2S_PD1_12 (0x6 << 12) +#define RT5670_I2S_PD1_16 (0x7 << 12) +#define RT5670_I2S_BCLK_MS2_MASK (0x1 << 11) +#define RT5670_I2S_BCLK_MS2_SFT 11 +#define RT5670_I2S_BCLK_MS2_32 (0x0 << 11) +#define RT5670_I2S_BCLK_MS2_64 (0x1 << 11) +#define RT5670_I2S_PD2_MASK (0x7 << 8) +#define RT5670_I2S_PD2_SFT 8 +#define RT5670_I2S_PD2_1 (0x0 << 8) +#define RT5670_I2S_PD2_2 (0x1 << 8) +#define RT5670_I2S_PD2_3 (0x2 << 8) +#define RT5670_I2S_PD2_4 (0x3 << 8) +#define RT5670_I2S_PD2_6 (0x4 << 8) +#define RT5670_I2S_PD2_8 (0x5 << 8) +#define RT5670_I2S_PD2_12 (0x6 << 8) +#define RT5670_I2S_PD2_16 (0x7 << 8) +#define RT5670_I2S_BCLK_MS3_MASK (0x1 << 7) +#define RT5670_I2S_BCLK_MS3_SFT 7 +#define RT5670_I2S_BCLK_MS3_32 (0x0 << 7) +#define RT5670_I2S_BCLK_MS3_64 (0x1 << 7) +#define RT5670_I2S_PD3_MASK (0x7 << 4) +#define RT5670_I2S_PD3_SFT 4 +#define RT5670_I2S_PD3_1 (0x0 << 4) +#define RT5670_I2S_PD3_2 (0x1 << 4) +#define RT5670_I2S_PD3_3 (0x2 << 4) +#define RT5670_I2S_PD3_4 (0x3 << 4) +#define RT5670_I2S_PD3_6 (0x4 << 4) +#define RT5670_I2S_PD3_8 (0x5 << 4) +#define RT5670_I2S_PD3_12 (0x6 << 4) +#define RT5670_I2S_PD3_16 (0x7 << 4) +#define RT5670_DAC_OSR_MASK (0x3 << 2) +#define RT5670_DAC_OSR_SFT 2 +#define RT5670_DAC_OSR_128 (0x0 << 2) +#define RT5670_DAC_OSR_64 (0x1 << 2) +#define RT5670_DAC_OSR_32 (0x2 << 2) +#define RT5670_DAC_OSR_16 (0x3 << 2) +#define RT5670_ADC_OSR_MASK (0x3) +#define RT5670_ADC_OSR_SFT 0 +#define RT5670_ADC_OSR_128 (0x0) +#define RT5670_ADC_OSR_64 (0x1) +#define RT5670_ADC_OSR_32 (0x2) +#define RT5670_ADC_OSR_16 (0x3) + +/* ADC/DAC Clock Control 2 (0x74) */ +#define RT5670_DAC_L_OSR_MASK (0x3 << 14) +#define RT5670_DAC_L_OSR_SFT 14 +#define RT5670_DAC_L_OSR_128 (0x0 << 14) +#define RT5670_DAC_L_OSR_64 (0x1 << 14) +#define RT5670_DAC_L_OSR_32 (0x2 << 14) +#define RT5670_DAC_L_OSR_16 (0x3 << 14) +#define RT5670_ADC_R_OSR_MASK (0x3 << 12) +#define RT5670_ADC_R_OSR_SFT 12 +#define RT5670_ADC_R_OSR_128 (0x0 << 12) +#define RT5670_ADC_R_OSR_64 (0x1 << 12) +#define RT5670_ADC_R_OSR_32 (0x2 << 12) +#define RT5670_ADC_R_OSR_16 (0x3 << 12) +#define RT5670_DAHPF_EN (0x1 << 11) +#define RT5670_DAHPF_EN_SFT 11 +#define RT5670_ADHPF_EN (0x1 << 10) +#define RT5670_ADHPF_EN_SFT 10 + +/* Digital Microphone Control (0x75) */ +#define RT5670_DMIC_1_EN_MASK (0x1 << 15) +#define RT5670_DMIC_1_EN_SFT 15 +#define RT5670_DMIC_1_DIS (0x0 << 15) +#define RT5670_DMIC_1_EN (0x1 << 15) +#define RT5670_DMIC_2_EN_MASK (0x1 << 14) +#define RT5670_DMIC_2_EN_SFT 14 +#define RT5670_DMIC_2_DIS (0x0 << 14) +#define RT5670_DMIC_2_EN (0x1 << 14) +#define RT5670_DMIC_1L_LH_MASK (0x1 << 13) +#define RT5670_DMIC_1L_LH_SFT 13 +#define RT5670_DMIC_1L_LH_FALLING (0x0 << 13) +#define RT5670_DMIC_1L_LH_RISING (0x1 << 13) +#define RT5670_DMIC_1R_LH_MASK (0x1 << 12) +#define RT5670_DMIC_1R_LH_SFT 12 +#define RT5670_DMIC_1R_LH_FALLING (0x0 << 12) +#define RT5670_DMIC_1R_LH_RISING (0x1 << 12) +#define RT5670_DMIC_2_DP_MASK (0x1 << 10) +#define RT5670_DMIC_2_DP_SFT 10 +#define RT5670_DMIC_2_DP_GPIO8 (0x0 << 10) +#define RT5670_DMIC_2_DP_IN3N (0x1 << 10) +#define RT5670_DMIC_2L_LH_MASK (0x1 << 9) +#define RT5670_DMIC_2L_LH_SFT 9 +#define RT5670_DMIC_2L_LH_FALLING (0x0 << 9) +#define RT5670_DMIC_2L_LH_RISING (0x1 << 9) +#define RT5670_DMIC_2R_LH_MASK (0x1 << 8) +#define RT5670_DMIC_2R_LH_SFT 8 +#define RT5670_DMIC_2R_LH_FALLING (0x0 << 8) +#define RT5670_DMIC_2R_LH_RISING (0x1 << 8) +#define RT5670_DMIC_CLK_MASK (0x7 << 5) +#define RT5670_DMIC_CLK_SFT 5 +#define RT5670_DMIC_3_EN_MASK (0x1 << 4) +#define RT5670_DMIC_3_EN_SFT 4 +#define RT5670_DMIC_3_DIS (0x0 << 4) +#define RT5670_DMIC_3_EN (0x1 << 4) +#define RT5670_DMIC_1_DP_MASK (0x3 << 0) +#define RT5670_DMIC_1_DP_SFT 0 +#define RT5670_DMIC_1_DP_GPIO6 (0x0 << 0) +#define RT5670_DMIC_1_DP_IN2P (0x1 << 0) +#define RT5670_DMIC_1_DP_GPIO7 (0x2 << 0) + +/* Digital Microphone Control2 (0x76) */ +#define RT5670_DMIC_3_DP_MASK (0x3 << 6) +#define RT5670_DMIC_3_DP_SFT 6 +#define RT5670_DMIC_3_DP_GPIO9 (0x0 << 6) +#define RT5670_DMIC_3_DP_GPIO10 (0x1 << 6) +#define RT5670_DMIC_3_DP_GPIO5 (0x2 << 6) + +/* Global Clock Control (0x80) */ +#define RT5670_SCLK_SRC_MASK (0x3 << 14) +#define RT5670_SCLK_SRC_SFT 14 +#define RT5670_SCLK_SRC_MCLK (0x0 << 14) +#define RT5670_SCLK_SRC_PLL1 (0x1 << 14) +#define RT5670_SCLK_SRC_RCCLK (0x2 << 14) /* 15MHz */ +#define RT5670_PLL1_SRC_MASK (0x3 << 12) +#define RT5670_PLL1_SRC_SFT 12 +#define RT5670_PLL1_SRC_MCLK (0x0 << 12) +#define RT5670_PLL1_SRC_BCLK1 (0x1 << 12) +#define RT5670_PLL1_SRC_BCLK2 (0x2 << 12) +#define RT5670_PLL1_SRC_BCLK3 (0x3 << 12) +#define RT5670_PLL1_PD_MASK (0x1 << 3) +#define RT5670_PLL1_PD_SFT 3 +#define RT5670_PLL1_PD_1 (0x0 << 3) +#define RT5670_PLL1_PD_2 (0x1 << 3) + +#define RT5670_PLL_INP_MAX 40000000 +#define RT5670_PLL_INP_MIN 256000 +/* PLL M/N/K Code Control 1 (0x81) */ +#define RT5670_PLL_N_MAX 0x1ff +#define RT5670_PLL_N_MASK (RT5670_PLL_N_MAX << 7) +#define RT5670_PLL_N_SFT 7 +#define RT5670_PLL_K_MAX 0x1f +#define RT5670_PLL_K_MASK (RT5670_PLL_K_MAX) +#define RT5670_PLL_K_SFT 0 + +/* PLL M/N/K Code Control 2 (0x82) */ +#define RT5670_PLL_M_MAX 0xf +#define RT5670_PLL_M_MASK (RT5670_PLL_M_MAX << 12) +#define RT5670_PLL_M_SFT 12 +#define RT5670_PLL_M_BP (0x1 << 11) +#define RT5670_PLL_M_BP_SFT 11 + +/* ASRC Control 1 (0x83) */ +#define RT5670_STO_T_MASK (0x1 << 15) +#define RT5670_STO_T_SFT 15 +#define RT5670_STO_T_SCLK (0x0 << 15) +#define RT5670_STO_T_LRCK1 (0x1 << 15) +#define RT5670_M1_T_MASK (0x1 << 14) +#define RT5670_M1_T_SFT 14 +#define RT5670_M1_T_I2S2 (0x0 << 14) +#define RT5670_M1_T_I2S2_D3 (0x1 << 14) +#define RT5670_I2S2_F_MASK (0x1 << 12) +#define RT5670_I2S2_F_SFT 12 +#define RT5670_I2S2_F_I2S2_D2 (0x0 << 12) +#define RT5670_I2S2_F_I2S1_TCLK (0x1 << 12) +#define RT5670_DMIC_1_M_MASK (0x1 << 9) +#define RT5670_DMIC_1_M_SFT 9 +#define RT5670_DMIC_1_M_NOR (0x0 << 9) +#define RT5670_DMIC_1_M_ASYN (0x1 << 9) +#define RT5670_DMIC_2_M_MASK (0x1 << 8) +#define RT5670_DMIC_2_M_SFT 8 +#define RT5670_DMIC_2_M_NOR (0x0 << 8) +#define RT5670_DMIC_2_M_ASYN (0x1 << 8) + +/* ASRC Control 2 (0x84) */ +#define RT5670_MDA_L_M_MASK (0x1 << 15) +#define RT5670_MDA_L_M_SFT 15 +#define RT5670_MDA_L_M_NOR (0x0 << 15) +#define RT5670_MDA_L_M_ASYN (0x1 << 15) +#define RT5670_MDA_R_M_MASK (0x1 << 14) +#define RT5670_MDA_R_M_SFT 14 +#define RT5670_MDA_R_M_NOR (0x0 << 14) +#define RT5670_MDA_R_M_ASYN (0x1 << 14) +#define RT5670_MAD_L_M_MASK (0x1 << 13) +#define RT5670_MAD_L_M_SFT 13 +#define RT5670_MAD_L_M_NOR (0x0 << 13) +#define RT5670_MAD_L_M_ASYN (0x1 << 13) +#define RT5670_MAD_R_M_MASK (0x1 << 12) +#define RT5670_MAD_R_M_SFT 12 +#define RT5670_MAD_R_M_NOR (0x0 << 12) +#define RT5670_MAD_R_M_ASYN (0x1 << 12) +#define RT5670_ADC_M_MASK (0x1 << 11) +#define RT5670_ADC_M_SFT 11 +#define RT5670_ADC_M_NOR (0x0 << 11) +#define RT5670_ADC_M_ASYN (0x1 << 11) +#define RT5670_STO_DAC_M_MASK (0x1 << 5) +#define RT5670_STO_DAC_M_SFT 5 +#define RT5670_STO_DAC_M_NOR (0x0 << 5) +#define RT5670_STO_DAC_M_ASYN (0x1 << 5) +#define RT5670_I2S1_R_D_MASK (0x1 << 4) +#define RT5670_I2S1_R_D_SFT 4 +#define RT5670_I2S1_R_D_DIS (0x0 << 4) +#define RT5670_I2S1_R_D_EN (0x1 << 4) +#define RT5670_I2S2_R_D_MASK (0x1 << 3) +#define RT5670_I2S2_R_D_SFT 3 +#define RT5670_I2S2_R_D_DIS (0x0 << 3) +#define RT5670_I2S2_R_D_EN (0x1 << 3) +#define RT5670_PRE_SCLK_MASK (0x3) +#define RT5670_PRE_SCLK_SFT 0 +#define RT5670_PRE_SCLK_512 (0x0) +#define RT5670_PRE_SCLK_1024 (0x1) +#define RT5670_PRE_SCLK_2048 (0x2) + +/* ASRC Control 3 (0x85) */ +#define RT5670_I2S1_RATE_MASK (0xf << 12) +#define RT5670_I2S1_RATE_SFT 12 +#define RT5670_I2S2_RATE_MASK (0xf << 8) +#define RT5670_I2S2_RATE_SFT 8 + +/* ASRC Control 4 (0x89) */ +#define RT5670_I2S1_PD_MASK (0x7 << 12) +#define RT5670_I2S1_PD_SFT 12 +#define RT5670_I2S2_PD_MASK (0x7 << 8) +#define RT5670_I2S2_PD_SFT 8 + +/* HPOUT Over Current Detection (0x8b) */ +#define RT5670_HP_OVCD_MASK (0x1 << 10) +#define RT5670_HP_OVCD_SFT 10 +#define RT5670_HP_OVCD_DIS (0x0 << 10) +#define RT5670_HP_OVCD_EN (0x1 << 10) +#define RT5670_HP_OC_TH_MASK (0x3 << 8) +#define RT5670_HP_OC_TH_SFT 8 +#define RT5670_HP_OC_TH_90 (0x0 << 8) +#define RT5670_HP_OC_TH_105 (0x1 << 8) +#define RT5670_HP_OC_TH_120 (0x2 << 8) +#define RT5670_HP_OC_TH_135 (0x3 << 8) + +/* Class D Over Current Control (0x8c) */ +#define RT5670_CLSD_OC_MASK (0x1 << 9) +#define RT5670_CLSD_OC_SFT 9 +#define RT5670_CLSD_OC_PU (0x0 << 9) +#define RT5670_CLSD_OC_PD (0x1 << 9) +#define RT5670_AUTO_PD_MASK (0x1 << 8) +#define RT5670_AUTO_PD_SFT 8 +#define RT5670_AUTO_PD_DIS (0x0 << 8) +#define RT5670_AUTO_PD_EN (0x1 << 8) +#define RT5670_CLSD_OC_TH_MASK (0x3f) +#define RT5670_CLSD_OC_TH_SFT 0 + +/* Class D Output Control (0x8d) */ +#define RT5670_CLSD_RATIO_MASK (0xf << 12) +#define RT5670_CLSD_RATIO_SFT 12 +#define RT5670_CLSD_OM_MASK (0x1 << 11) +#define RT5670_CLSD_OM_SFT 11 +#define RT5670_CLSD_OM_MONO (0x0 << 11) +#define RT5670_CLSD_OM_STO (0x1 << 11) +#define RT5670_CLSD_SCH_MASK (0x1 << 10) +#define RT5670_CLSD_SCH_SFT 10 +#define RT5670_CLSD_SCH_L (0x0 << 10) +#define RT5670_CLSD_SCH_S (0x1 << 10) + +/* Depop Mode Control 1 (0x8e) */ +#define RT5670_SMT_TRIG_MASK (0x1 << 15) +#define RT5670_SMT_TRIG_SFT 15 +#define RT5670_SMT_TRIG_DIS (0x0 << 15) +#define RT5670_SMT_TRIG_EN (0x1 << 15) +#define RT5670_HP_L_SMT_MASK (0x1 << 9) +#define RT5670_HP_L_SMT_SFT 9 +#define RT5670_HP_L_SMT_DIS (0x0 << 9) +#define RT5670_HP_L_SMT_EN (0x1 << 9) +#define RT5670_HP_R_SMT_MASK (0x1 << 8) +#define RT5670_HP_R_SMT_SFT 8 +#define RT5670_HP_R_SMT_DIS (0x0 << 8) +#define RT5670_HP_R_SMT_EN (0x1 << 8) +#define RT5670_HP_CD_PD_MASK (0x1 << 7) +#define RT5670_HP_CD_PD_SFT 7 +#define RT5670_HP_CD_PD_DIS (0x0 << 7) +#define RT5670_HP_CD_PD_EN (0x1 << 7) +#define RT5670_RSTN_MASK (0x1 << 6) +#define RT5670_RSTN_SFT 6 +#define RT5670_RSTN_DIS (0x0 << 6) +#define RT5670_RSTN_EN (0x1 << 6) +#define RT5670_RSTP_MASK (0x1 << 5) +#define RT5670_RSTP_SFT 5 +#define RT5670_RSTP_DIS (0x0 << 5) +#define RT5670_RSTP_EN (0x1 << 5) +#define RT5670_HP_CO_MASK (0x1 << 4) +#define RT5670_HP_CO_SFT 4 +#define RT5670_HP_CO_DIS (0x0 << 4) +#define RT5670_HP_CO_EN (0x1 << 4) +#define RT5670_HP_CP_MASK (0x1 << 3) +#define RT5670_HP_CP_SFT 3 +#define RT5670_HP_CP_PD (0x0 << 3) +#define RT5670_HP_CP_PU (0x1 << 3) +#define RT5670_HP_SG_MASK (0x1 << 2) +#define RT5670_HP_SG_SFT 2 +#define RT5670_HP_SG_DIS (0x0 << 2) +#define RT5670_HP_SG_EN (0x1 << 2) +#define RT5670_HP_DP_MASK (0x1 << 1) +#define RT5670_HP_DP_SFT 1 +#define RT5670_HP_DP_PD (0x0 << 1) +#define RT5670_HP_DP_PU (0x1 << 1) +#define RT5670_HP_CB_MASK (0x1) +#define RT5670_HP_CB_SFT 0 +#define RT5670_HP_CB_PD (0x0) +#define RT5670_HP_CB_PU (0x1) + +/* Depop Mode Control 2 (0x8f) */ +#define RT5670_DEPOP_MASK (0x1 << 13) +#define RT5670_DEPOP_SFT 13 +#define RT5670_DEPOP_AUTO (0x0 << 13) +#define RT5670_DEPOP_MAN (0x1 << 13) +#define RT5670_RAMP_MASK (0x1 << 12) +#define RT5670_RAMP_SFT 12 +#define RT5670_RAMP_DIS (0x0 << 12) +#define RT5670_RAMP_EN (0x1 << 12) +#define RT5670_BPS_MASK (0x1 << 11) +#define RT5670_BPS_SFT 11 +#define RT5670_BPS_DIS (0x0 << 11) +#define RT5670_BPS_EN (0x1 << 11) +#define RT5670_FAST_UPDN_MASK (0x1 << 10) +#define RT5670_FAST_UPDN_SFT 10 +#define RT5670_FAST_UPDN_DIS (0x0 << 10) +#define RT5670_FAST_UPDN_EN (0x1 << 10) +#define RT5670_MRES_MASK (0x3 << 8) +#define RT5670_MRES_SFT 8 +#define RT5670_MRES_15MO (0x0 << 8) +#define RT5670_MRES_25MO (0x1 << 8) +#define RT5670_MRES_35MO (0x2 << 8) +#define RT5670_MRES_45MO (0x3 << 8) +#define RT5670_VLO_MASK (0x1 << 7) +#define RT5670_VLO_SFT 7 +#define RT5670_VLO_3V (0x0 << 7) +#define RT5670_VLO_32V (0x1 << 7) +#define RT5670_DIG_DP_MASK (0x1 << 6) +#define RT5670_DIG_DP_SFT 6 +#define RT5670_DIG_DP_DIS (0x0 << 6) +#define RT5670_DIG_DP_EN (0x1 << 6) +#define RT5670_DP_TH_MASK (0x3 << 4) +#define RT5670_DP_TH_SFT 4 + +/* Depop Mode Control 3 (0x90) */ +#define RT5670_CP_SYS_MASK (0x7 << 12) +#define RT5670_CP_SYS_SFT 12 +#define RT5670_CP_FQ1_MASK (0x7 << 8) +#define RT5670_CP_FQ1_SFT 8 +#define RT5670_CP_FQ2_MASK (0x7 << 4) +#define RT5670_CP_FQ2_SFT 4 +#define RT5670_CP_FQ3_MASK (0x7) +#define RT5670_CP_FQ3_SFT 0 +#define RT5670_CP_FQ_1_5_KHZ 0 +#define RT5670_CP_FQ_3_KHZ 1 +#define RT5670_CP_FQ_6_KHZ 2 +#define RT5670_CP_FQ_12_KHZ 3 +#define RT5670_CP_FQ_24_KHZ 4 +#define RT5670_CP_FQ_48_KHZ 5 +#define RT5670_CP_FQ_96_KHZ 6 +#define RT5670_CP_FQ_192_KHZ 7 + +/* HPOUT charge pump (0x91) */ +#define RT5670_OSW_L_MASK (0x1 << 11) +#define RT5670_OSW_L_SFT 11 +#define RT5670_OSW_L_DIS (0x0 << 11) +#define RT5670_OSW_L_EN (0x1 << 11) +#define RT5670_OSW_R_MASK (0x1 << 10) +#define RT5670_OSW_R_SFT 10 +#define RT5670_OSW_R_DIS (0x0 << 10) +#define RT5670_OSW_R_EN (0x1 << 10) +#define RT5670_PM_HP_MASK (0x3 << 8) +#define RT5670_PM_HP_SFT 8 +#define RT5670_PM_HP_LV (0x0 << 8) +#define RT5670_PM_HP_MV (0x1 << 8) +#define RT5670_PM_HP_HV (0x2 << 8) +#define RT5670_IB_HP_MASK (0x3 << 6) +#define RT5670_IB_HP_SFT 6 +#define RT5670_IB_HP_125IL (0x0 << 6) +#define RT5670_IB_HP_25IL (0x1 << 6) +#define RT5670_IB_HP_5IL (0x2 << 6) +#define RT5670_IB_HP_1IL (0x3 << 6) + +/* PV detection and SPK gain control (0x92) */ +#define RT5670_PVDD_DET_MASK (0x1 << 15) +#define RT5670_PVDD_DET_SFT 15 +#define RT5670_PVDD_DET_DIS (0x0 << 15) +#define RT5670_PVDD_DET_EN (0x1 << 15) +#define RT5670_SPK_AG_MASK (0x1 << 14) +#define RT5670_SPK_AG_SFT 14 +#define RT5670_SPK_AG_DIS (0x0 << 14) +#define RT5670_SPK_AG_EN (0x1 << 14) + +/* Micbias Control (0x93) */ +#define RT5670_MIC1_BS_MASK (0x1 << 15) +#define RT5670_MIC1_BS_SFT 15 +#define RT5670_MIC1_BS_9AV (0x0 << 15) +#define RT5670_MIC1_BS_75AV (0x1 << 15) +#define RT5670_MIC2_BS_MASK (0x1 << 14) +#define RT5670_MIC2_BS_SFT 14 +#define RT5670_MIC2_BS_9AV (0x0 << 14) +#define RT5670_MIC2_BS_75AV (0x1 << 14) +#define RT5670_MIC1_CLK_MASK (0x1 << 13) +#define RT5670_MIC1_CLK_SFT 13 +#define RT5670_MIC1_CLK_DIS (0x0 << 13) +#define RT5670_MIC1_CLK_EN (0x1 << 13) +#define RT5670_MIC2_CLK_MASK (0x1 << 12) +#define RT5670_MIC2_CLK_SFT 12 +#define RT5670_MIC2_CLK_DIS (0x0 << 12) +#define RT5670_MIC2_CLK_EN (0x1 << 12) +#define RT5670_MIC1_OVCD_MASK (0x1 << 11) +#define RT5670_MIC1_OVCD_SFT 11 +#define RT5670_MIC1_OVCD_DIS (0x0 << 11) +#define RT5670_MIC1_OVCD_EN (0x1 << 11) +#define RT5670_MIC1_OVTH_MASK (0x3 << 9) +#define RT5670_MIC1_OVTH_SFT 9 +#define RT5670_MIC1_OVTH_600UA (0x0 << 9) +#define RT5670_MIC1_OVTH_1500UA (0x1 << 9) +#define RT5670_MIC1_OVTH_2000UA (0x2 << 9) +#define RT5670_MIC2_OVCD_MASK (0x1 << 8) +#define RT5670_MIC2_OVCD_SFT 8 +#define RT5670_MIC2_OVCD_DIS (0x0 << 8) +#define RT5670_MIC2_OVCD_EN (0x1 << 8) +#define RT5670_MIC2_OVTH_MASK (0x3 << 6) +#define RT5670_MIC2_OVTH_SFT 6 +#define RT5670_MIC2_OVTH_600UA (0x0 << 6) +#define RT5670_MIC2_OVTH_1500UA (0x1 << 6) +#define RT5670_MIC2_OVTH_2000UA (0x2 << 6) +#define RT5670_PWR_MB_MASK (0x1 << 5) +#define RT5670_PWR_MB_SFT 5 +#define RT5670_PWR_MB_PD (0x0 << 5) +#define RT5670_PWR_MB_PU (0x1 << 5) +#define RT5670_PWR_CLK25M_MASK (0x1 << 4) +#define RT5670_PWR_CLK25M_SFT 4 +#define RT5670_PWR_CLK25M_PD (0x0 << 4) +#define RT5670_PWR_CLK25M_PU (0x1 << 4) + +/* Analog JD Control 1 (0x94) */ +#define RT5670_JD1_MODE_MASK (0x3 << 0) +#define RT5670_JD1_MODE_0 (0x0 << 0) +#define RT5670_JD1_MODE_1 (0x1 << 0) +#define RT5670_JD1_MODE_2 (0x2 << 0) + +/* VAD Control 4 (0x9d) */ +#define RT5670_VAD_SEL_MASK (0x3 << 8) +#define RT5670_VAD_SEL_SFT 8 + +/* EQ Control 1 (0xb0) */ +#define RT5670_EQ_SRC_MASK (0x1 << 15) +#define RT5670_EQ_SRC_SFT 15 +#define RT5670_EQ_SRC_DAC (0x0 << 15) +#define RT5670_EQ_SRC_ADC (0x1 << 15) +#define RT5670_EQ_UPD (0x1 << 14) +#define RT5670_EQ_UPD_BIT 14 +#define RT5670_EQ_CD_MASK (0x1 << 13) +#define RT5670_EQ_CD_SFT 13 +#define RT5670_EQ_CD_DIS (0x0 << 13) +#define RT5670_EQ_CD_EN (0x1 << 13) +#define RT5670_EQ_DITH_MASK (0x3 << 8) +#define RT5670_EQ_DITH_SFT 8 +#define RT5670_EQ_DITH_NOR (0x0 << 8) +#define RT5670_EQ_DITH_LSB (0x1 << 8) +#define RT5670_EQ_DITH_LSB_1 (0x2 << 8) +#define RT5670_EQ_DITH_LSB_2 (0x3 << 8) + +/* EQ Control 2 (0xb1) */ +#define RT5670_EQ_HPF1_M_MASK (0x1 << 8) +#define RT5670_EQ_HPF1_M_SFT 8 +#define RT5670_EQ_HPF1_M_HI (0x0 << 8) +#define RT5670_EQ_HPF1_M_1ST (0x1 << 8) +#define RT5670_EQ_LPF1_M_MASK (0x1 << 7) +#define RT5670_EQ_LPF1_M_SFT 7 +#define RT5670_EQ_LPF1_M_LO (0x0 << 7) +#define RT5670_EQ_LPF1_M_1ST (0x1 << 7) +#define RT5670_EQ_HPF2_MASK (0x1 << 6) +#define RT5670_EQ_HPF2_SFT 6 +#define RT5670_EQ_HPF2_DIS (0x0 << 6) +#define RT5670_EQ_HPF2_EN (0x1 << 6) +#define RT5670_EQ_HPF1_MASK (0x1 << 5) +#define RT5670_EQ_HPF1_SFT 5 +#define RT5670_EQ_HPF1_DIS (0x0 << 5) +#define RT5670_EQ_HPF1_EN (0x1 << 5) +#define RT5670_EQ_BPF4_MASK (0x1 << 4) +#define RT5670_EQ_BPF4_SFT 4 +#define RT5670_EQ_BPF4_DIS (0x0 << 4) +#define RT5670_EQ_BPF4_EN (0x1 << 4) +#define RT5670_EQ_BPF3_MASK (0x1 << 3) +#define RT5670_EQ_BPF3_SFT 3 +#define RT5670_EQ_BPF3_DIS (0x0 << 3) +#define RT5670_EQ_BPF3_EN (0x1 << 3) +#define RT5670_EQ_BPF2_MASK (0x1 << 2) +#define RT5670_EQ_BPF2_SFT 2 +#define RT5670_EQ_BPF2_DIS (0x0 << 2) +#define RT5670_EQ_BPF2_EN (0x1 << 2) +#define RT5670_EQ_BPF1_MASK (0x1 << 1) +#define RT5670_EQ_BPF1_SFT 1 +#define RT5670_EQ_BPF1_DIS (0x0 << 1) +#define RT5670_EQ_BPF1_EN (0x1 << 1) +#define RT5670_EQ_LPF_MASK (0x1) +#define RT5670_EQ_LPF_SFT 0 +#define RT5670_EQ_LPF_DIS (0x0) +#define RT5670_EQ_LPF_EN (0x1) +#define RT5670_EQ_CTRL_MASK (0x7f) + +/* Memory Test (0xb2) */ +#define RT5670_MT_MASK (0x1 << 15) +#define RT5670_MT_SFT 15 +#define RT5670_MT_DIS (0x0 << 15) +#define RT5670_MT_EN (0x1 << 15) + +/* DRC/AGC Control 1 (0xb4) */ +#define RT5670_DRC_AGC_P_MASK (0x1 << 15) +#define RT5670_DRC_AGC_P_SFT 15 +#define RT5670_DRC_AGC_P_DAC (0x0 << 15) +#define RT5670_DRC_AGC_P_ADC (0x1 << 15) +#define RT5670_DRC_AGC_MASK (0x1 << 14) +#define RT5670_DRC_AGC_SFT 14 +#define RT5670_DRC_AGC_DIS (0x0 << 14) +#define RT5670_DRC_AGC_EN (0x1 << 14) +#define RT5670_DRC_AGC_UPD (0x1 << 13) +#define RT5670_DRC_AGC_UPD_BIT 13 +#define RT5670_DRC_AGC_AR_MASK (0x1f << 8) +#define RT5670_DRC_AGC_AR_SFT 8 +#define RT5670_DRC_AGC_R_MASK (0x7 << 5) +#define RT5670_DRC_AGC_R_SFT 5 +#define RT5670_DRC_AGC_R_48K (0x1 << 5) +#define RT5670_DRC_AGC_R_96K (0x2 << 5) +#define RT5670_DRC_AGC_R_192K (0x3 << 5) +#define RT5670_DRC_AGC_R_441K (0x5 << 5) +#define RT5670_DRC_AGC_R_882K (0x6 << 5) +#define RT5670_DRC_AGC_R_1764K (0x7 << 5) +#define RT5670_DRC_AGC_RC_MASK (0x1f) +#define RT5670_DRC_AGC_RC_SFT 0 + +/* DRC/AGC Control 2 (0xb5) */ +#define RT5670_DRC_AGC_POB_MASK (0x3f << 8) +#define RT5670_DRC_AGC_POB_SFT 8 +#define RT5670_DRC_AGC_CP_MASK (0x1 << 7) +#define RT5670_DRC_AGC_CP_SFT 7 +#define RT5670_DRC_AGC_CP_DIS (0x0 << 7) +#define RT5670_DRC_AGC_CP_EN (0x1 << 7) +#define RT5670_DRC_AGC_CPR_MASK (0x3 << 5) +#define RT5670_DRC_AGC_CPR_SFT 5 +#define RT5670_DRC_AGC_CPR_1_1 (0x0 << 5) +#define RT5670_DRC_AGC_CPR_1_2 (0x1 << 5) +#define RT5670_DRC_AGC_CPR_1_3 (0x2 << 5) +#define RT5670_DRC_AGC_CPR_1_4 (0x3 << 5) +#define RT5670_DRC_AGC_PRB_MASK (0x1f) +#define RT5670_DRC_AGC_PRB_SFT 0 + +/* DRC/AGC Control 3 (0xb6) */ +#define RT5670_DRC_AGC_NGB_MASK (0xf << 12) +#define RT5670_DRC_AGC_NGB_SFT 12 +#define RT5670_DRC_AGC_TAR_MASK (0x1f << 7) +#define RT5670_DRC_AGC_TAR_SFT 7 +#define RT5670_DRC_AGC_NG_MASK (0x1 << 6) +#define RT5670_DRC_AGC_NG_SFT 6 +#define RT5670_DRC_AGC_NG_DIS (0x0 << 6) +#define RT5670_DRC_AGC_NG_EN (0x1 << 6) +#define RT5670_DRC_AGC_NGH_MASK (0x1 << 5) +#define RT5670_DRC_AGC_NGH_SFT 5 +#define RT5670_DRC_AGC_NGH_DIS (0x0 << 5) +#define RT5670_DRC_AGC_NGH_EN (0x1 << 5) +#define RT5670_DRC_AGC_NGT_MASK (0x1f) +#define RT5670_DRC_AGC_NGT_SFT 0 + +/* Jack Detect Control (0xbb) */ +#define RT5670_JD_MASK (0x7 << 13) +#define RT5670_JD_SFT 13 +#define RT5670_JD_DIS (0x0 << 13) +#define RT5670_JD_GPIO1 (0x1 << 13) +#define RT5670_JD_JD1_IN4P (0x2 << 13) +#define RT5670_JD_JD2_IN4N (0x3 << 13) +#define RT5670_JD_GPIO2 (0x4 << 13) +#define RT5670_JD_GPIO3 (0x5 << 13) +#define RT5670_JD_GPIO4 (0x6 << 13) +#define RT5670_JD_HP_MASK (0x1 << 11) +#define RT5670_JD_HP_SFT 11 +#define RT5670_JD_HP_DIS (0x0 << 11) +#define RT5670_JD_HP_EN (0x1 << 11) +#define RT5670_JD_HP_TRG_MASK (0x1 << 10) +#define RT5670_JD_HP_TRG_SFT 10 +#define RT5670_JD_HP_TRG_LO (0x0 << 10) +#define RT5670_JD_HP_TRG_HI (0x1 << 10) +#define RT5670_JD_SPL_MASK (0x1 << 9) +#define RT5670_JD_SPL_SFT 9 +#define RT5670_JD_SPL_DIS (0x0 << 9) +#define RT5670_JD_SPL_EN (0x1 << 9) +#define RT5670_JD_SPL_TRG_MASK (0x1 << 8) +#define RT5670_JD_SPL_TRG_SFT 8 +#define RT5670_JD_SPL_TRG_LO (0x0 << 8) +#define RT5670_JD_SPL_TRG_HI (0x1 << 8) +#define RT5670_JD_SPR_MASK (0x1 << 7) +#define RT5670_JD_SPR_SFT 7 +#define RT5670_JD_SPR_DIS (0x0 << 7) +#define RT5670_JD_SPR_EN (0x1 << 7) +#define RT5670_JD_SPR_TRG_MASK (0x1 << 6) +#define RT5670_JD_SPR_TRG_SFT 6 +#define RT5670_JD_SPR_TRG_LO (0x0 << 6) +#define RT5670_JD_SPR_TRG_HI (0x1 << 6) +#define RT5670_JD_MO_MASK (0x1 << 5) +#define RT5670_JD_MO_SFT 5 +#define RT5670_JD_MO_DIS (0x0 << 5) +#define RT5670_JD_MO_EN (0x1 << 5) +#define RT5670_JD_MO_TRG_MASK (0x1 << 4) +#define RT5670_JD_MO_TRG_SFT 4 +#define RT5670_JD_MO_TRG_LO (0x0 << 4) +#define RT5670_JD_MO_TRG_HI (0x1 << 4) +#define RT5670_JD_LO_MASK (0x1 << 3) +#define RT5670_JD_LO_SFT 3 +#define RT5670_JD_LO_DIS (0x0 << 3) +#define RT5670_JD_LO_EN (0x1 << 3) +#define RT5670_JD_LO_TRG_MASK (0x1 << 2) +#define RT5670_JD_LO_TRG_SFT 2 +#define RT5670_JD_LO_TRG_LO (0x0 << 2) +#define RT5670_JD_LO_TRG_HI (0x1 << 2) +#define RT5670_JD1_IN4P_MASK (0x1 << 1) +#define RT5670_JD1_IN4P_SFT 1 +#define RT5670_JD1_IN4P_DIS (0x0 << 1) +#define RT5670_JD1_IN4P_EN (0x1 << 1) +#define RT5670_JD2_IN4N_MASK (0x1) +#define RT5670_JD2_IN4N_SFT 0 +#define RT5670_JD2_IN4N_DIS (0x0) +#define RT5670_JD2_IN4N_EN (0x1) + +/* IRQ Control 1 (0xbd) */ +#define RT5670_IRQ_JD_MASK (0x1 << 15) +#define RT5670_IRQ_JD_SFT 15 +#define RT5670_IRQ_JD_BP (0x0 << 15) +#define RT5670_IRQ_JD_NOR (0x1 << 15) +#define RT5670_IRQ_OT_MASK (0x1 << 14) +#define RT5670_IRQ_OT_SFT 14 +#define RT5670_IRQ_OT_BP (0x0 << 14) +#define RT5670_IRQ_OT_NOR (0x1 << 14) +#define RT5670_JD_STKY_MASK (0x1 << 13) +#define RT5670_JD_STKY_SFT 13 +#define RT5670_JD_STKY_DIS (0x0 << 13) +#define RT5670_JD_STKY_EN (0x1 << 13) +#define RT5670_OT_STKY_MASK (0x1 << 12) +#define RT5670_OT_STKY_SFT 12 +#define RT5670_OT_STKY_DIS (0x0 << 12) +#define RT5670_OT_STKY_EN (0x1 << 12) +#define RT5670_JD_P_MASK (0x1 << 11) +#define RT5670_JD_P_SFT 11 +#define RT5670_JD_P_NOR (0x0 << 11) +#define RT5670_JD_P_INV (0x1 << 11) +#define RT5670_OT_P_MASK (0x1 << 10) +#define RT5670_OT_P_SFT 10 +#define RT5670_OT_P_NOR (0x0 << 10) +#define RT5670_OT_P_INV (0x1 << 10) +#define RT5670_JD1_1_EN_MASK (0x1 << 9) +#define RT5670_JD1_1_EN_SFT 9 +#define RT5670_JD1_1_DIS (0x0 << 9) +#define RT5670_JD1_1_EN (0x1 << 9) + +/* IRQ Control 2 (0xbe) */ +#define RT5670_IRQ_MB1_OC_MASK (0x1 << 15) +#define RT5670_IRQ_MB1_OC_SFT 15 +#define RT5670_IRQ_MB1_OC_BP (0x0 << 15) +#define RT5670_IRQ_MB1_OC_NOR (0x1 << 15) +#define RT5670_IRQ_MB2_OC_MASK (0x1 << 14) +#define RT5670_IRQ_MB2_OC_SFT 14 +#define RT5670_IRQ_MB2_OC_BP (0x0 << 14) +#define RT5670_IRQ_MB2_OC_NOR (0x1 << 14) +#define RT5670_MB1_OC_STKY_MASK (0x1 << 11) +#define RT5670_MB1_OC_STKY_SFT 11 +#define RT5670_MB1_OC_STKY_DIS (0x0 << 11) +#define RT5670_MB1_OC_STKY_EN (0x1 << 11) +#define RT5670_MB2_OC_STKY_MASK (0x1 << 10) +#define RT5670_MB2_OC_STKY_SFT 10 +#define RT5670_MB2_OC_STKY_DIS (0x0 << 10) +#define RT5670_MB2_OC_STKY_EN (0x1 << 10) +#define RT5670_MB1_OC_P_MASK (0x1 << 7) +#define RT5670_MB1_OC_P_SFT 7 +#define RT5670_MB1_OC_P_NOR (0x0 << 7) +#define RT5670_MB1_OC_P_INV (0x1 << 7) +#define RT5670_MB2_OC_P_MASK (0x1 << 6) +#define RT5670_MB2_OC_P_SFT 6 +#define RT5670_MB2_OC_P_NOR (0x0 << 6) +#define RT5670_MB2_OC_P_INV (0x1 << 6) +#define RT5670_MB1_OC_CLR (0x1 << 3) +#define RT5670_MB1_OC_CLR_SFT 3 +#define RT5670_MB2_OC_CLR (0x1 << 2) +#define RT5670_MB2_OC_CLR_SFT 2 + +/* GPIO Control 1 (0xc0) */ +#define RT5670_GP1_PIN_MASK (0x1 << 15) +#define RT5670_GP1_PIN_SFT 15 +#define RT5670_GP1_PIN_GPIO1 (0x0 << 15) +#define RT5670_GP1_PIN_IRQ (0x1 << 15) +#define RT5670_GP2_PIN_MASK (0x1 << 14) +#define RT5670_GP2_PIN_SFT 14 +#define RT5670_GP2_PIN_GPIO2 (0x0 << 14) +#define RT5670_GP2_PIN_DMIC1_SCL (0x1 << 14) +#define RT5670_GP3_PIN_MASK (0x3 << 12) +#define RT5670_GP3_PIN_SFT 12 +#define RT5670_GP3_PIN_GPIO3 (0x0 << 12) +#define RT5670_GP3_PIN_DMIC1_SDA (0x1 << 12) +#define RT5670_GP3_PIN_IRQ (0x2 << 12) +#define RT5670_GP4_PIN_MASK (0x1 << 11) +#define RT5670_GP4_PIN_SFT 11 +#define RT5670_GP4_PIN_GPIO4 (0x0 << 11) +#define RT5670_GP4_PIN_DMIC2_SDA (0x1 << 11) +#define RT5670_DP_SIG_MASK (0x1 << 10) +#define RT5670_DP_SIG_SFT 10 +#define RT5670_DP_SIG_TEST (0x0 << 10) +#define RT5670_DP_SIG_AP (0x1 << 10) +#define RT5670_GPIO_M_MASK (0x1 << 9) +#define RT5670_GPIO_M_SFT 9 +#define RT5670_GPIO_M_FLT (0x0 << 9) +#define RT5670_GPIO_M_PH (0x1 << 9) +#define RT5670_I2S2_PIN_MASK (0x1 << 8) +#define RT5670_I2S2_PIN_SFT 8 +#define RT5670_I2S2_PIN_I2S (0x0 << 8) +#define RT5670_I2S2_PIN_GPIO (0x1 << 8) +#define RT5670_GP5_PIN_MASK (0x1 << 7) +#define RT5670_GP5_PIN_SFT 7 +#define RT5670_GP5_PIN_GPIO5 (0x0 << 7) +#define RT5670_GP5_PIN_DMIC3_SDA (0x1 << 7) +#define RT5670_GP6_PIN_MASK (0x1 << 6) +#define RT5670_GP6_PIN_SFT 6 +#define RT5670_GP6_PIN_GPIO6 (0x0 << 6) +#define RT5670_GP6_PIN_DMIC1_SDA (0x1 << 6) +#define RT5670_GP7_PIN_MASK (0x3 << 4) +#define RT5670_GP7_PIN_SFT 4 +#define RT5670_GP7_PIN_GPIO7 (0x0 << 4) +#define RT5670_GP7_PIN_DMIC1_SDA (0x1 << 4) +#define RT5670_GP7_PIN_PDM_SCL2 (0x2 << 4) +#define RT5670_GP8_PIN_MASK (0x1 << 3) +#define RT5670_GP8_PIN_SFT 3 +#define RT5670_GP8_PIN_GPIO8 (0x0 << 3) +#define RT5670_GP8_PIN_DMIC2_SDA (0x1 << 3) +#define RT5670_GP9_PIN_MASK (0x1 << 2) +#define RT5670_GP9_PIN_SFT 2 +#define RT5670_GP9_PIN_GPIO9 (0x0 << 2) +#define RT5670_GP9_PIN_DMIC3_SDA (0x1 << 2) +#define RT5670_GP10_PIN_MASK (0x3) +#define RT5670_GP10_PIN_SFT 0 +#define RT5670_GP10_PIN_GPIO9 (0x0) +#define RT5670_GP10_PIN_DMIC3_SDA (0x1) +#define RT5670_GP10_PIN_PDM_ADT2 (0x2) + +/* GPIO Control 2 (0xc1) */ +#define RT5670_GP4_PF_MASK (0x1 << 11) +#define RT5670_GP4_PF_SFT 11 +#define RT5670_GP4_PF_IN (0x0 << 11) +#define RT5670_GP4_PF_OUT (0x1 << 11) +#define RT5670_GP4_OUT_MASK (0x1 << 10) +#define RT5670_GP4_OUT_SFT 10 +#define RT5670_GP4_OUT_LO (0x0 << 10) +#define RT5670_GP4_OUT_HI (0x1 << 10) +#define RT5670_GP4_P_MASK (0x1 << 9) +#define RT5670_GP4_P_SFT 9 +#define RT5670_GP4_P_NOR (0x0 << 9) +#define RT5670_GP4_P_INV (0x1 << 9) +#define RT5670_GP3_PF_MASK (0x1 << 8) +#define RT5670_GP3_PF_SFT 8 +#define RT5670_GP3_PF_IN (0x0 << 8) +#define RT5670_GP3_PF_OUT (0x1 << 8) +#define RT5670_GP3_OUT_MASK (0x1 << 7) +#define RT5670_GP3_OUT_SFT 7 +#define RT5670_GP3_OUT_LO (0x0 << 7) +#define RT5670_GP3_OUT_HI (0x1 << 7) +#define RT5670_GP3_P_MASK (0x1 << 6) +#define RT5670_GP3_P_SFT 6 +#define RT5670_GP3_P_NOR (0x0 << 6) +#define RT5670_GP3_P_INV (0x1 << 6) +#define RT5670_GP2_PF_MASK (0x1 << 5) +#define RT5670_GP2_PF_SFT 5 +#define RT5670_GP2_PF_IN (0x0 << 5) +#define RT5670_GP2_PF_OUT (0x1 << 5) +#define RT5670_GP2_OUT_MASK (0x1 << 4) +#define RT5670_GP2_OUT_SFT 4 +#define RT5670_GP2_OUT_LO (0x0 << 4) +#define RT5670_GP2_OUT_HI (0x1 << 4) +#define RT5670_GP2_P_MASK (0x1 << 3) +#define RT5670_GP2_P_SFT 3 +#define RT5670_GP2_P_NOR (0x0 << 3) +#define RT5670_GP2_P_INV (0x1 << 3) +#define RT5670_GP1_PF_MASK (0x1 << 2) +#define RT5670_GP1_PF_SFT 2 +#define RT5670_GP1_PF_IN (0x0 << 2) +#define RT5670_GP1_PF_OUT (0x1 << 2) +#define RT5670_GP1_OUT_MASK (0x1 << 1) +#define RT5670_GP1_OUT_SFT 1 +#define RT5670_GP1_OUT_LO (0x0 << 1) +#define RT5670_GP1_OUT_HI (0x1 << 1) +#define RT5670_GP1_P_MASK (0x1) +#define RT5670_GP1_P_SFT 0 +#define RT5670_GP1_P_NOR (0x0) +#define RT5670_GP1_P_INV (0x1) + +/* Scramble Function (0xcd) */ +#define RT5670_SCB_KEY_MASK (0xff) +#define RT5670_SCB_KEY_SFT 0 + +/* Scramble Control (0xce) */ +#define RT5670_SCB_SWAP_MASK (0x1 << 15) +#define RT5670_SCB_SWAP_SFT 15 +#define RT5670_SCB_SWAP_DIS (0x0 << 15) +#define RT5670_SCB_SWAP_EN (0x1 << 15) +#define RT5670_SCB_MASK (0x1 << 14) +#define RT5670_SCB_SFT 14 +#define RT5670_SCB_DIS (0x0 << 14) +#define RT5670_SCB_EN (0x1 << 14) + +/* Baseback Control (0xcf) */ +#define RT5670_BB_MASK (0x1 << 15) +#define RT5670_BB_SFT 15 +#define RT5670_BB_DIS (0x0 << 15) +#define RT5670_BB_EN (0x1 << 15) +#define RT5670_BB_CT_MASK (0x7 << 12) +#define RT5670_BB_CT_SFT 12 +#define RT5670_BB_CT_A (0x0 << 12) +#define RT5670_BB_CT_B (0x1 << 12) +#define RT5670_BB_CT_C (0x2 << 12) +#define RT5670_BB_CT_D (0x3 << 12) +#define RT5670_M_BB_L_MASK (0x1 << 9) +#define RT5670_M_BB_L_SFT 9 +#define RT5670_M_BB_R_MASK (0x1 << 8) +#define RT5670_M_BB_R_SFT 8 +#define RT5670_M_BB_HPF_L_MASK (0x1 << 7) +#define RT5670_M_BB_HPF_L_SFT 7 +#define RT5670_M_BB_HPF_R_MASK (0x1 << 6) +#define RT5670_M_BB_HPF_R_SFT 6 +#define RT5670_G_BB_BST_MASK (0x3f) +#define RT5670_G_BB_BST_SFT 0 + +/* MP3 Plus Control 1 (0xd0) */ +#define RT5670_M_MP3_L_MASK (0x1 << 15) +#define RT5670_M_MP3_L_SFT 15 +#define RT5670_M_MP3_R_MASK (0x1 << 14) +#define RT5670_M_MP3_R_SFT 14 +#define RT5670_M_MP3_MASK (0x1 << 13) +#define RT5670_M_MP3_SFT 13 +#define RT5670_M_MP3_DIS (0x0 << 13) +#define RT5670_M_MP3_EN (0x1 << 13) +#define RT5670_EG_MP3_MASK (0x1f << 8) +#define RT5670_EG_MP3_SFT 8 +#define RT5670_MP3_HLP_MASK (0x1 << 7) +#define RT5670_MP3_HLP_SFT 7 +#define RT5670_MP3_HLP_DIS (0x0 << 7) +#define RT5670_MP3_HLP_EN (0x1 << 7) +#define RT5670_M_MP3_ORG_L_MASK (0x1 << 6) +#define RT5670_M_MP3_ORG_L_SFT 6 +#define RT5670_M_MP3_ORG_R_MASK (0x1 << 5) +#define RT5670_M_MP3_ORG_R_SFT 5 + +/* MP3 Plus Control 2 (0xd1) */ +#define RT5670_MP3_WT_MASK (0x1 << 13) +#define RT5670_MP3_WT_SFT 13 +#define RT5670_MP3_WT_1_4 (0x0 << 13) +#define RT5670_MP3_WT_1_2 (0x1 << 13) +#define RT5670_OG_MP3_MASK (0x1f << 8) +#define RT5670_OG_MP3_SFT 8 +#define RT5670_HG_MP3_MASK (0x3f) +#define RT5670_HG_MP3_SFT 0 + +/* 3D HP Control 1 (0xd2) */ +#define RT5670_3D_CF_MASK (0x1 << 15) +#define RT5670_3D_CF_SFT 15 +#define RT5670_3D_CF_DIS (0x0 << 15) +#define RT5670_3D_CF_EN (0x1 << 15) +#define RT5670_3D_HP_MASK (0x1 << 14) +#define RT5670_3D_HP_SFT 14 +#define RT5670_3D_HP_DIS (0x0 << 14) +#define RT5670_3D_HP_EN (0x1 << 14) +#define RT5670_3D_BT_MASK (0x1 << 13) +#define RT5670_3D_BT_SFT 13 +#define RT5670_3D_BT_DIS (0x0 << 13) +#define RT5670_3D_BT_EN (0x1 << 13) +#define RT5670_3D_1F_MIX_MASK (0x3 << 11) +#define RT5670_3D_1F_MIX_SFT 11 +#define RT5670_3D_HP_M_MASK (0x1 << 10) +#define RT5670_3D_HP_M_SFT 10 +#define RT5670_3D_HP_M_SUR (0x0 << 10) +#define RT5670_3D_HP_M_FRO (0x1 << 10) +#define RT5670_M_3D_HRTF_MASK (0x1 << 9) +#define RT5670_M_3D_HRTF_SFT 9 +#define RT5670_M_3D_D2H_MASK (0x1 << 8) +#define RT5670_M_3D_D2H_SFT 8 +#define RT5670_M_3D_D2R_MASK (0x1 << 7) +#define RT5670_M_3D_D2R_SFT 7 +#define RT5670_M_3D_REVB_MASK (0x1 << 6) +#define RT5670_M_3D_REVB_SFT 6 + +/* Adjustable high pass filter control 1 (0xd3) */ +#define RT5670_2ND_HPF_MASK (0x1 << 15) +#define RT5670_2ND_HPF_SFT 15 +#define RT5670_2ND_HPF_DIS (0x0 << 15) +#define RT5670_2ND_HPF_EN (0x1 << 15) +#define RT5670_HPF_CF_L_MASK (0x7 << 12) +#define RT5670_HPF_CF_L_SFT 12 +#define RT5670_1ST_HPF_MASK (0x1 << 11) +#define RT5670_1ST_HPF_SFT 11 +#define RT5670_1ST_HPF_DIS (0x0 << 11) +#define RT5670_1ST_HPF_EN (0x1 << 11) +#define RT5670_HPF_CF_R_MASK (0x7 << 8) +#define RT5670_HPF_CF_R_SFT 8 +#define RT5670_ZD_T_MASK (0x3 << 6) +#define RT5670_ZD_T_SFT 6 +#define RT5670_ZD_F_MASK (0x3 << 4) +#define RT5670_ZD_F_SFT 4 +#define RT5670_ZD_F_IM (0x0 << 4) +#define RT5670_ZD_F_ZC_IM (0x1 << 4) +#define RT5670_ZD_F_ZC_IOD (0x2 << 4) +#define RT5670_ZD_F_UN (0x3 << 4) + +/* HP calibration control and Amp detection (0xd6) */ +#define RT5670_SI_DAC_MASK (0x1 << 11) +#define RT5670_SI_DAC_SFT 11 +#define RT5670_SI_DAC_AUTO (0x0 << 11) +#define RT5670_SI_DAC_TEST (0x1 << 11) +#define RT5670_DC_CAL_M_MASK (0x1 << 10) +#define RT5670_DC_CAL_M_SFT 10 +#define RT5670_DC_CAL_M_CAL (0x0 << 10) +#define RT5670_DC_CAL_M_NOR (0x1 << 10) +#define RT5670_DC_CAL_MASK (0x1 << 9) +#define RT5670_DC_CAL_SFT 9 +#define RT5670_DC_CAL_DIS (0x0 << 9) +#define RT5670_DC_CAL_EN (0x1 << 9) +#define RT5670_HPD_RCV_MASK (0x7 << 6) +#define RT5670_HPD_RCV_SFT 6 +#define RT5670_HPD_PS_MASK (0x1 << 5) +#define RT5670_HPD_PS_SFT 5 +#define RT5670_HPD_PS_DIS (0x0 << 5) +#define RT5670_HPD_PS_EN (0x1 << 5) +#define RT5670_CAL_M_MASK (0x1 << 4) +#define RT5670_CAL_M_SFT 4 +#define RT5670_CAL_M_DEP (0x0 << 4) +#define RT5670_CAL_M_CAL (0x1 << 4) +#define RT5670_CAL_MASK (0x1 << 3) +#define RT5670_CAL_SFT 3 +#define RT5670_CAL_DIS (0x0 << 3) +#define RT5670_CAL_EN (0x1 << 3) +#define RT5670_CAL_TEST_MASK (0x1 << 2) +#define RT5670_CAL_TEST_SFT 2 +#define RT5670_CAL_TEST_DIS (0x0 << 2) +#define RT5670_CAL_TEST_EN (0x1 << 2) +#define RT5670_CAL_P_MASK (0x3) +#define RT5670_CAL_P_SFT 0 +#define RT5670_CAL_P_NONE (0x0) +#define RT5670_CAL_P_CAL (0x1) +#define RT5670_CAL_P_DAC_CAL (0x2) + +/* Soft volume and zero cross control 1 (0xd9) */ +#define RT5670_SV_MASK (0x1 << 15) +#define RT5670_SV_SFT 15 +#define RT5670_SV_DIS (0x0 << 15) +#define RT5670_SV_EN (0x1 << 15) +#define RT5670_SPO_SV_MASK (0x1 << 14) +#define RT5670_SPO_SV_SFT 14 +#define RT5670_SPO_SV_DIS (0x0 << 14) +#define RT5670_SPO_SV_EN (0x1 << 14) +#define RT5670_OUT_SV_MASK (0x1 << 13) +#define RT5670_OUT_SV_SFT 13 +#define RT5670_OUT_SV_DIS (0x0 << 13) +#define RT5670_OUT_SV_EN (0x1 << 13) +#define RT5670_HP_SV_MASK (0x1 << 12) +#define RT5670_HP_SV_SFT 12 +#define RT5670_HP_SV_DIS (0x0 << 12) +#define RT5670_HP_SV_EN (0x1 << 12) +#define RT5670_ZCD_DIG_MASK (0x1 << 11) +#define RT5670_ZCD_DIG_SFT 11 +#define RT5670_ZCD_DIG_DIS (0x0 << 11) +#define RT5670_ZCD_DIG_EN (0x1 << 11) +#define RT5670_ZCD_MASK (0x1 << 10) +#define RT5670_ZCD_SFT 10 +#define RT5670_ZCD_PD (0x0 << 10) +#define RT5670_ZCD_PU (0x1 << 10) +#define RT5670_M_ZCD_MASK (0x3f << 4) +#define RT5670_M_ZCD_SFT 4 +#define RT5670_M_ZCD_RM_L (0x1 << 9) +#define RT5670_M_ZCD_RM_R (0x1 << 8) +#define RT5670_M_ZCD_SM_L (0x1 << 7) +#define RT5670_M_ZCD_SM_R (0x1 << 6) +#define RT5670_M_ZCD_OM_L (0x1 << 5) +#define RT5670_M_ZCD_OM_R (0x1 << 4) +#define RT5670_SV_DLY_MASK (0xf) +#define RT5670_SV_DLY_SFT 0 + +/* Soft volume and zero cross control 2 (0xda) */ +#define RT5670_ZCD_HP_MASK (0x1 << 15) +#define RT5670_ZCD_HP_SFT 15 +#define RT5670_ZCD_HP_DIS (0x0 << 15) +#define RT5670_ZCD_HP_EN (0x1 << 15) + + +/* Codec Private Register definition */ +/* 3D Speaker Control (0x63) */ +#define RT5670_3D_SPK_MASK (0x1 << 15) +#define RT5670_3D_SPK_SFT 15 +#define RT5670_3D_SPK_DIS (0x0 << 15) +#define RT5670_3D_SPK_EN (0x1 << 15) +#define RT5670_3D_SPK_M_MASK (0x3 << 13) +#define RT5670_3D_SPK_M_SFT 13 +#define RT5670_3D_SPK_CG_MASK (0x1f << 8) +#define RT5670_3D_SPK_CG_SFT 8 +#define RT5670_3D_SPK_SG_MASK (0x1f) +#define RT5670_3D_SPK_SG_SFT 0 + +/* Wind Noise Detection Control 1 (0x6c) */ +#define RT5670_WND_MASK (0x1 << 15) +#define RT5670_WND_SFT 15 +#define RT5670_WND_DIS (0x0 << 15) +#define RT5670_WND_EN (0x1 << 15) + +/* Wind Noise Detection Control 2 (0x6d) */ +#define RT5670_WND_FC_NW_MASK (0x3f << 10) +#define RT5670_WND_FC_NW_SFT 10 +#define RT5670_WND_FC_WK_MASK (0x3f << 4) +#define RT5670_WND_FC_WK_SFT 4 + +/* Wind Noise Detection Control 3 (0x6e) */ +#define RT5670_HPF_FC_MASK (0x3f << 6) +#define RT5670_HPF_FC_SFT 6 +#define RT5670_WND_FC_ST_MASK (0x3f) +#define RT5670_WND_FC_ST_SFT 0 + +/* Wind Noise Detection Control 4 (0x6f) */ +#define RT5670_WND_TH_LO_MASK (0x3ff) +#define RT5670_WND_TH_LO_SFT 0 + +/* Wind Noise Detection Control 5 (0x70) */ +#define RT5670_WND_TH_HI_MASK (0x3ff) +#define RT5670_WND_TH_HI_SFT 0 + +/* Wind Noise Detection Control 8 (0x73) */ +#define RT5670_WND_WIND_MASK (0x1 << 13) /* Read-Only */ +#define RT5670_WND_WIND_SFT 13 +#define RT5670_WND_STRONG_MASK (0x1 << 12) /* Read-Only */ +#define RT5670_WND_STRONG_SFT 12 +enum { + RT5670_NO_WIND, + RT5670_BREEZE, + RT5670_STORM, +}; + +/* Dipole Speaker Interface (0x75) */ +#define RT5670_DP_ATT_MASK (0x3 << 14) +#define RT5670_DP_ATT_SFT 14 +#define RT5670_DP_SPK_MASK (0x1 << 10) +#define RT5670_DP_SPK_SFT 10 +#define RT5670_DP_SPK_DIS (0x0 << 10) +#define RT5670_DP_SPK_EN (0x1 << 10) + +/* EQ Pre Volume Control (0xb3) */ +#define RT5670_EQ_PRE_VOL_MASK (0xffff) +#define RT5670_EQ_PRE_VOL_SFT 0 + +/* EQ Post Volume Control (0xb4) */ +#define RT5670_EQ_PST_VOL_MASK (0xffff) +#define RT5670_EQ_PST_VOL_SFT 0 + +/* Jack Detect Control 3 (0xf8) */ +#define RT5670_CMP_MIC_IN_DET_MASK (0x7 << 12) +#define RT5670_JD_CBJ_EN (0x1 << 7) +#define RT5670_JD_CBJ_POL (0x1 << 6) +#define RT5670_JD_TRI_CBJ_SEL_MASK (0x7 << 3) +#define RT5670_JD_TRI_CBJ_SEL_SFT (3) +#define RT5670_JD_CBJ_GPIO_JD1 (0x0 << 3) +#define RT5670_JD_CBJ_JD1_1 (0x1 << 3) +#define RT5670_JD_CBJ_JD1_2 (0x2 << 3) +#define RT5670_JD_CBJ_JD2 (0x3 << 3) +#define RT5670_JD_CBJ_JD3 (0x4 << 3) +#define RT5670_JD_CBJ_GPIO_JD2 (0x5 << 3) +#define RT5670_JD_CBJ_MX0B_12 (0x6 << 3) +#define RT5670_JD_TRI_HPO_SEL_MASK (0x7 << 3) +#define RT5670_JD_TRI_HPO_SEL_SFT (0) +#define RT5670_JD_HPO_GPIO_JD1 (0x0) +#define RT5670_JD_HPO_JD1_1 (0x1) +#define RT5670_JD_HPO_JD1_2 (0x2) +#define RT5670_JD_HPO_JD2 (0x3) +#define RT5670_JD_HPO_JD3 (0x4) +#define RT5670_JD_HPO_GPIO_JD2 (0x5) +#define RT5670_JD_HPO_MX0B_12 (0x6) + +/* Digital Misc Control (0xfa) */ +#define RT5670_RST_DSP (0x1 << 13) +#define RT5670_IF1_ADC1_IN1_SEL (0x1 << 12) +#define RT5670_IF1_ADC1_IN1_SFT 12 +#define RT5670_IF1_ADC1_IN2_SEL (0x1 << 11) +#define RT5670_IF1_ADC1_IN2_SFT 11 +#define RT5670_IF1_ADC2_IN1_SEL (0x1 << 10) +#define RT5670_IF1_ADC2_IN1_SFT 10 + +/* General Control2 (0xfb) */ +#define RT5670_RXDC_SRC_MASK (0x1 << 7) +#define RT5670_RXDC_SRC_STO (0x0 << 7) +#define RT5670_RXDC_SRC_MONO (0x1 << 7) +#define RT5670_RXDC_SRC_SFT (7) +#define RT5670_RXDP2_SEL_MASK (0x1 << 3) +#define RT5670_RXDP2_SEL_IF2 (0x0 << 3) +#define RT5670_RXDP2_SEL_ADC (0x1 << 3) +#define RT5670_RXDP2_SEL_SFT (3) + +/* System Clock Source */ +enum { + RT5670_SCLK_S_MCLK, + RT5670_SCLK_S_PLL1, + RT5670_SCLK_S_RCCLK, +}; + +/* PLL1 Source */ +enum { + RT5670_PLL1_S_MCLK, + RT5670_PLL1_S_BCLK1, + RT5670_PLL1_S_BCLK2, + RT5670_PLL1_S_BCLK3, + RT5670_PLL1_S_BCLK4, +}; + +enum { + RT5670_AIF1, + RT5670_AIF2, + RT5670_AIF3, + RT5670_AIF4, + RT5670_AIFS, +}; + +enum { + RT5670_DMIC_DATA_GPIO6, + RT5670_DMIC_DATA_IN2P, + RT5670_DMIC_DATA_GPIO7, +}; + +enum { + RT5670_DMIC_DATA_GPIO8, + RT5670_DMIC_DATA_IN3N, +}; + +enum { + RT5670_DMIC_DATA_GPIO9, + RT5670_DMIC_DATA_GPIO10, + RT5670_DMIC_DATA_GPIO5, +}; + +struct rt5670_priv { + struct snd_soc_codec *codec; + struct rt5670_platform_data pdata; + struct regmap *regmap; + + int sysclk; + int sysclk_src; + int lrck[RT5670_AIFS]; + int bclk[RT5670_AIFS]; + int master[RT5670_AIFS]; + + int pll_src; + int pll_in; + int pll_out; + + int dsp_sw; /* expected parameter setting */ + int dsp_rate; + int jack_type; +}; + +#endif /* __RT5670_H__ */ -- cgit v1.2.3 From fb2b3c9f68574738c70b9df5fc2bea40f91dd8be Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Thu, 26 Jun 2014 18:00:53 +0300 Subject: clk: define and export clk_debugs_add_file Define and export a new function clk_debugs_add_file which adds a file to a existing clock's debugfs directory. This can be used by clock providers to add debugfs entries which are not related to a specific clock type. Examples include the ability to measure the rate of a clock. It can also be used by modules to create new debugfs entries. This is useful if you want to expose features for testing which can potentially cause system instability such as allowing to change a clock's rate from userspace. Signed-off-by: Peter De Schrijver Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 12 ++++++++++++ include/linux/clk-provider.h | 5 +++++ 2 files changed, 17 insertions(+) (limited to 'include') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 958967d141ee..9ad397050471 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -358,6 +358,18 @@ static void clk_debug_unregister(struct clk *clk) debugfs_remove_recursive(clk->dentry); } +struct dentry *clk_debugfs_add_file(struct clk *clk, char *name, umode_t mode, + void *data, const struct file_operations *fops) +{ + struct dentry *d = NULL; + + if (clk->dentry) + d = debugfs_create_file(name, mode, clk->dentry, data, fops); + + return d; +} +EXPORT_SYMBOL_GPL(clk_debugfs_add_file); + /** * clk_debug_init - lazily create the debugfs clk tree visualization * diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 0c287dbbb144..411dd7eb2653 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -619,5 +619,10 @@ static inline void clk_writel(u32 val, u32 __iomem *reg) #endif /* platform dependent I/O accessors */ +#ifdef CONFIG_DEBUG_FS +struct dentry *clk_debugfs_add_file(struct clk *clk, char *name, umode_t mode, + void *data, const struct file_operations *fops); +#endif + #endif /* CONFIG_COMMON_CLK */ #endif /* CLK_PROVIDER_H */ -- cgit v1.2.3 From 5925a0555bdaf0b396a84318cbc21ba085f6c0d3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 2 Jul 2014 02:39:38 -0700 Subject: net: fix sparse warning in sk_dst_set() sk_dst_cache has __rcu annotation, so we need a cast to avoid following sparse error : include/net/sock.h:1774:19: warning: incorrect type in initializer (different address spaces) include/net/sock.h:1774:19: expected struct dst_entry [noderef] *__ret include/net/sock.h:1774:19: got struct dst_entry *dst Signed-off-by: Eric Dumazet Reported-by: kbuild test robot Fixes: 7f502361531e ("ipv4: irq safe sk_dst_[re]set() and ipv4_sk_update_pmtu() fix") 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 c556fd9b05ac..156350745700 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1771,7 +1771,7 @@ sk_dst_set(struct sock *sk, struct dst_entry *dst) struct dst_entry *old_dst; sk_tx_queue_clear(sk); - old_dst = xchg(&sk->sk_dst_cache, dst); + old_dst = xchg((__force struct dst_entry **)&sk->sk_dst_cache, dst); dst_release(old_dst); } -- cgit v1.2.3 From 35f6f45368632f21bd27559c44dbb1cab51d8947 Mon Sep 17 00:00:00 2001 From: Amir Vadai Date: Sun, 29 Jun 2014 11:54:55 +0300 Subject: net/mlx4_en: Don't use irq_affinity_notifier to track changes in IRQ affinity map IRQ affinity notifier can only have a single notifier - cpu_rmap notifier. Can't use it to track changes in IRQ affinity map. Detect IRQ affinity changes by comparing CPU to current IRQ affinity map during NAPI poll thread. CC: Thomas Gleixner CC: Ben Hutchings Fixes: 2eacc23 ("net/mlx4_core: Enforce irq affinity changes immediatly") Signed-off-by: Amir Vadai Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/cq.c | 2 - drivers/net/ethernet/mellanox/mlx4/en_cq.c | 4 ++ drivers/net/ethernet/mellanox/mlx4/en_rx.c | 16 +++++-- drivers/net/ethernet/mellanox/mlx4/en_tx.c | 6 --- drivers/net/ethernet/mellanox/mlx4/eq.c | 69 ++++------------------------ drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 1 + include/linux/mlx4/device.h | 4 +- 7 files changed, 28 insertions(+), 74 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/cq.c b/drivers/net/ethernet/mellanox/mlx4/cq.c index 80f725228f5b..56022d647837 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cq.c +++ b/drivers/net/ethernet/mellanox/mlx4/cq.c @@ -294,8 +294,6 @@ int mlx4_cq_alloc(struct mlx4_dev *dev, int nent, init_completion(&cq->free); cq->irq = priv->eq_table.eq[cq->vector].irq; - cq->irq_affinity_change = false; - return 0; err_radix: diff --git a/drivers/net/ethernet/mellanox/mlx4/en_cq.c b/drivers/net/ethernet/mellanox/mlx4/en_cq.c index 4b2130760eed..1213cc71348c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_cq.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_cq.c @@ -128,6 +128,10 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq, mlx4_warn(mdev, "Failed assigning an EQ to %s, falling back to legacy EQ's\n", name); } + + cq->irq_desc = + irq_to_desc(mlx4_eq_get_irq(mdev->dev, + cq->vector)); } } else { cq->vector = (cq->ring + 1 + priv->port) % diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index d2d415732d99..96724170308a 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "mlx4_en.h" @@ -896,16 +897,25 @@ int mlx4_en_poll_rx_cq(struct napi_struct *napi, int budget) /* If we used up all the quota - we're probably not done yet... */ if (done == budget) { + int cpu_curr; + const struct cpumask *aff; + INC_PERF_COUNTER(priv->pstats.napi_quota); - if (unlikely(cq->mcq.irq_affinity_change)) { - cq->mcq.irq_affinity_change = false; + + cpu_curr = smp_processor_id(); + aff = irq_desc_get_irq_data(cq->irq_desc)->affinity; + + if (unlikely(!cpumask_test_cpu(cpu_curr, aff))) { + /* Current cpu is not according to smp_irq_affinity - + * probably affinity changed. need to stop this NAPI + * poll, and restart it on the right CPU + */ napi_complete(napi); mlx4_en_arm_cq(priv, cq); return 0; } } else { /* Done for now */ - cq->mcq.irq_affinity_change = false; napi_complete(napi); mlx4_en_arm_cq(priv, cq); } diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c index 8be7483f8236..ac3dead3792c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c @@ -474,15 +474,9 @@ int mlx4_en_poll_tx_cq(struct napi_struct *napi, int budget) /* If we used up all the quota - we're probably not done yet... */ if (done < budget) { /* Done for now */ - cq->mcq.irq_affinity_change = false; napi_complete(napi); mlx4_en_arm_cq(priv, cq); return done; - } else if (unlikely(cq->mcq.irq_affinity_change)) { - cq->mcq.irq_affinity_change = false; - napi_complete(napi); - mlx4_en_arm_cq(priv, cq); - return 0; } return budget; } diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c index d954ec1eac17..2a004b347e1d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/eq.c +++ b/drivers/net/ethernet/mellanox/mlx4/eq.c @@ -53,11 +53,6 @@ enum { MLX4_EQ_ENTRY_SIZE = 0x20 }; -struct mlx4_irq_notify { - void *arg; - struct irq_affinity_notify notify; -}; - #define MLX4_EQ_STATUS_OK ( 0 << 28) #define MLX4_EQ_STATUS_WRITE_FAIL (10 << 28) #define MLX4_EQ_OWNER_SW ( 0 << 24) @@ -1088,57 +1083,6 @@ static void mlx4_unmap_clr_int(struct mlx4_dev *dev) iounmap(priv->clr_base); } -static void mlx4_irq_notifier_notify(struct irq_affinity_notify *notify, - const cpumask_t *mask) -{ - struct mlx4_irq_notify *n = container_of(notify, - struct mlx4_irq_notify, - notify); - struct mlx4_priv *priv = (struct mlx4_priv *)n->arg; - struct radix_tree_iter iter; - void **slot; - - radix_tree_for_each_slot(slot, &priv->cq_table.tree, &iter, 0) { - struct mlx4_cq *cq = (struct mlx4_cq *)(*slot); - - if (cq->irq == notify->irq) - cq->irq_affinity_change = true; - } -} - -static void mlx4_release_irq_notifier(struct kref *ref) -{ - struct mlx4_irq_notify *n = container_of(ref, struct mlx4_irq_notify, - notify.kref); - kfree(n); -} - -static void mlx4_assign_irq_notifier(struct mlx4_priv *priv, - struct mlx4_dev *dev, int irq) -{ - struct mlx4_irq_notify *irq_notifier = NULL; - int err = 0; - - irq_notifier = kzalloc(sizeof(*irq_notifier), GFP_KERNEL); - if (!irq_notifier) { - mlx4_warn(dev, "Failed to allocate irq notifier. irq %d\n", - irq); - return; - } - - irq_notifier->notify.irq = irq; - irq_notifier->notify.notify = mlx4_irq_notifier_notify; - irq_notifier->notify.release = mlx4_release_irq_notifier; - irq_notifier->arg = priv; - err = irq_set_affinity_notifier(irq, &irq_notifier->notify); - if (err) { - kfree(irq_notifier); - irq_notifier = NULL; - mlx4_warn(dev, "Failed to set irq notifier. irq %d\n", irq); - } -} - - int mlx4_alloc_eq_table(struct mlx4_dev *dev) { struct mlx4_priv *priv = mlx4_priv(dev); @@ -1409,8 +1353,6 @@ int mlx4_assign_eq(struct mlx4_dev *dev, char *name, struct cpu_rmap *rmap, continue; /*we dont want to break here*/ } - mlx4_assign_irq_notifier(priv, dev, - priv->eq_table.eq[vec].irq); eq_set_ci(&priv->eq_table.eq[vec], 1); } @@ -1427,6 +1369,14 @@ int mlx4_assign_eq(struct mlx4_dev *dev, char *name, struct cpu_rmap *rmap, } EXPORT_SYMBOL(mlx4_assign_eq); +int mlx4_eq_get_irq(struct mlx4_dev *dev, int vec) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + + return priv->eq_table.eq[vec].irq; +} +EXPORT_SYMBOL(mlx4_eq_get_irq); + void mlx4_release_eq(struct mlx4_dev *dev, int vec) { struct mlx4_priv *priv = mlx4_priv(dev); @@ -1438,9 +1388,6 @@ void mlx4_release_eq(struct mlx4_dev *dev, int vec) Belonging to a legacy EQ*/ mutex_lock(&priv->msix_ctl.pool_lock); if (priv->msix_ctl.pool_bm & 1ULL << i) { - irq_set_affinity_notifier( - priv->eq_table.eq[vec].irq, - NULL); free_irq(priv->eq_table.eq[vec].irq, &priv->eq_table.eq[vec]); priv->msix_ctl.pool_bm &= ~(1ULL << i); diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 0e15295bedd6..624e1939e9ee 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -343,6 +343,7 @@ struct mlx4_en_cq { #define CQ_USER_PEND (MLX4_EN_CQ_STATE_POLL | MLX4_EN_CQ_STATE_POLL_YIELD) spinlock_t poll_lock; /* protects from LLS/napi conflicts */ #endif /* CONFIG_NET_RX_BUSY_POLL */ + struct irq_desc *irq_desc; }; struct mlx4_en_port_profile { diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index b12f4bbd064c..35b51e7af886 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -578,8 +578,6 @@ struct mlx4_cq { u32 cons_index; u16 irq; - bool irq_affinity_change; - __be32 *set_ci_db; __be32 *arm_db; int arm_sn; @@ -1167,6 +1165,8 @@ int mlx4_assign_eq(struct mlx4_dev *dev, char *name, struct cpu_rmap *rmap, int *vector); void mlx4_release_eq(struct mlx4_dev *dev, int vec); +int mlx4_eq_get_irq(struct mlx4_dev *dev, int vec); + int mlx4_get_phys_port_id(struct mlx4_dev *dev); int mlx4_wol_read(struct mlx4_dev *dev, u64 *config, int port); int mlx4_wol_write(struct mlx4_dev *dev, u64 config, int port); -- cgit v1.2.3 From 48bc03433cfdcac7a3bbb233d5c9f95297a0f5ab Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Sun, 29 Jun 2014 13:10:18 +0200 Subject: ieee802154: reassembly: fix possible buffer overflow The max_dsize attribute in ctl_table for lowpan_frags_ns_ctl_table is configured with integer accessing methods. This patch change the max_dsize attribute to int to avoid a possible buffer overflow. Signed-off-by: Alexander Aring Signed-off-by: David S. Miller --- include/net/netns/ieee802154_6lowpan.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/netns/ieee802154_6lowpan.h b/include/net/netns/ieee802154_6lowpan.h index 079030c853d8..e2070960bac0 100644 --- a/include/net/netns/ieee802154_6lowpan.h +++ b/include/net/netns/ieee802154_6lowpan.h @@ -16,7 +16,7 @@ struct netns_sysctl_lowpan { struct netns_ieee802154_lowpan { struct netns_sysctl_lowpan sysctl; struct netns_frags frags; - u16 max_dsize; + int max_dsize; }; #endif -- cgit v1.2.3 From 8f61059a96c2a29c1cc5a39dfe23d06ef5b4b065 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 30 Jun 2014 13:52:08 +0200 Subject: net: sctp: improve timer slack calculation for transport HBs RFC4960, section 8.3 says: On an idle destination address that is allowed to heartbeat, it is recommended that a HEARTBEAT chunk is sent once per RTO of that destination address plus the protocol parameter 'HB.interval', with jittering of +/- 50% of the RTO value, and exponential backoff of the RTO if the previous HEARTBEAT is unanswered. Currently, we calculate jitter via sctp_jitter() function first, and then add its result to the current RTO for the new timeout: TMO = RTO + (RAND() % RTO) - (RTO / 2) `------------------------^-=> sctp_jitter() Instead, we can just simplify all this by directly calculating: TMO = (RTO / 2) + (RAND() % RTO) With the help of prandom_u32_max(), we don't need to open code our own global PRNG, but can instead just make use of the per CPU implementation of prandom with better quality numbers. Also, we can now spare us the conditional for divide by zero check since no div or mod operation needs to be used. Note that prandom_u32_max() won't emit the same result as a mod operation, but we really don't care here as we only want to have a random number scaled into RTO interval. Note, exponential RTO backoff is handeled elsewhere, namely in sctp_do_8_2_transport_strike(). Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/net/sctp/sctp.h | 21 --------------------- net/sctp/transport.c | 17 +++++++++-------- 2 files changed, 9 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 8e4de46c052e..c2035c96a2ee 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -388,27 +388,6 @@ static inline int sctp_list_single_entry(struct list_head *head) return (head->next != head) && (head->next == head->prev); } -/* Generate a random jitter in the range of -50% ~ +50% of input RTO. */ -static inline __s32 sctp_jitter(__u32 rto) -{ - static __u32 sctp_rand; - __s32 ret; - - /* Avoid divide by zero. */ - if (!rto) - rto = 1; - - sctp_rand += jiffies; - sctp_rand ^= (sctp_rand << 12); - sctp_rand ^= (sctp_rand >> 20); - - /* Choose random number from 0 to rto, then move to -50% ~ +50% - * of rto. - */ - ret = sctp_rand % rto - (rto >> 1); - return ret; -} - /* Break down data chunks at this point. */ static inline int sctp_frag_point(const struct sctp_association *asoc, int pmtu) { diff --git a/net/sctp/transport.c b/net/sctp/transport.c index 7dd672fa651f..b10e047bbd15 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -594,15 +594,16 @@ void sctp_transport_burst_reset(struct sctp_transport *t) } /* What is the next timeout value for this transport? */ -unsigned long sctp_transport_timeout(struct sctp_transport *t) +unsigned long sctp_transport_timeout(struct sctp_transport *trans) { - unsigned long timeout; - timeout = t->rto + sctp_jitter(t->rto); - if ((t->state != SCTP_UNCONFIRMED) && - (t->state != SCTP_PF)) - timeout += t->hbinterval; - timeout += jiffies; - return timeout; + /* RTO + timer slack +/- 50% of RTO */ + unsigned long timeout = (trans->rto >> 1) + prandom_u32_max(trans->rto); + + if (trans->state != SCTP_UNCONFIRMED && + trans->state != SCTP_PF) + timeout += trans->hbinterval; + + return timeout + jiffies; } /* Reset transport variables to their initial values */ -- cgit v1.2.3 From 6955b58254c2bcee8a7b55ce06468a645dc98ec5 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 19 Apr 2014 11:18:01 +0100 Subject: component: add support for component match array Add support for generating a set of component matches at master probe time, and submitting them to the component layer. This allows the component layer to perform the matches internally without needing to call into the master driver, and allows for further restructuring of the component helper. Acked-by: Laurent Pinchart Signed-off-by: Russell King --- drivers/base/component.c | 120 ++++++++++++++++++++++++++++++++++++++++++++-- include/linux/component.h | 7 +++ 2 files changed, 124 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/base/component.c b/drivers/base/component.c index 55813e91bf0d..b4236daed4fa 100644 --- a/drivers/base/component.c +++ b/drivers/base/component.c @@ -18,6 +18,15 @@ #include #include +struct component_match { + size_t alloc; + size_t num; + struct { + void *data; + int (*fn)(struct device *, void *); + } compare[0]; +}; + struct master { struct list_head node; struct list_head components; @@ -25,6 +34,7 @@ struct master { const struct component_master_ops *ops; struct device *dev; + struct component_match *match; }; struct component { @@ -96,6 +106,34 @@ int component_master_add_child(struct master *master, } EXPORT_SYMBOL_GPL(component_master_add_child); +static int find_components(struct master *master) +{ + struct component_match *match = master->match; + size_t i; + int ret = 0; + + if (!match) { + /* + * Search the list of components, looking for components that + * belong to this master, and attach them to the master. + */ + return master->ops->add_components(master->dev, master); + } + + /* + * Scan the array of match functions and attach + * any components which are found to this master. + */ + for (i = 0; i < match->num; i++) { + ret = component_master_add_child(master, + match->compare[i].fn, + match->compare[i].data); + if (ret) + break; + } + return ret; +} + /* Detach all attached components from this master */ static void master_remove_components(struct master *master) { @@ -128,7 +166,7 @@ static int try_to_bring_up_master(struct master *master, * Search the list of components, looking for components that * belong to this master, and attach them to the master. */ - if (master->ops->add_components(master->dev, master)) { + if (find_components(master)) { /* Failed to find all components */ ret = 0; goto out; @@ -186,18 +224,87 @@ static void take_down_master(struct master *master) master_remove_components(master); } -int component_master_add(struct device *dev, - const struct component_master_ops *ops) +static size_t component_match_size(size_t num) +{ + return offsetof(struct component_match, compare[num]); +} + +static struct component_match *component_match_realloc(struct device *dev, + struct component_match *match, size_t num) +{ + struct component_match *new; + + if (match && match->alloc == num) + return match; + + new = devm_kmalloc(dev, component_match_size(num), GFP_KERNEL); + if (!new) + return ERR_PTR(-ENOMEM); + + if (match) { + memcpy(new, match, component_match_size(min(match->num, num))); + devm_kfree(dev, match); + } else { + new->num = 0; + } + + new->alloc = num; + + return new; +} + +/* + * Add a component to be matched. + * + * The match array is first created or extended if necessary. + */ +void component_match_add(struct device *dev, struct component_match **matchptr, + int (*compare)(struct device *, void *), void *compare_data) +{ + struct component_match *match = *matchptr; + + if (IS_ERR(match)) + return; + + if (!match || match->num == match->alloc) { + size_t new_size = match ? match->alloc + 16 : 15; + + match = component_match_realloc(dev, match, new_size); + + *matchptr = match; + + if (IS_ERR(match)) + return; + } + + match->compare[match->num].fn = compare; + match->compare[match->num].data = compare_data; + match->num++; +} +EXPORT_SYMBOL(component_match_add); + +int component_master_add_with_match(struct device *dev, + const struct component_master_ops *ops, + struct component_match *match) { struct master *master; int ret; + if (ops->add_components && match) + return -EINVAL; + + /* Reallocate the match array for its true size */ + match = component_match_realloc(dev, match, match->num); + if (IS_ERR(match)) + return PTR_ERR(match); + master = kzalloc(sizeof(*master), GFP_KERNEL); if (!master) return -ENOMEM; master->dev = dev; master->ops = ops; + master->match = match; INIT_LIST_HEAD(&master->components); /* Add to the list of available masters. */ @@ -215,6 +322,13 @@ int component_master_add(struct device *dev, return ret < 0 ? ret : 0; } +EXPORT_SYMBOL_GPL(component_master_add_with_match); + +int component_master_add(struct device *dev, + const struct component_master_ops *ops) +{ + return component_master_add_with_match(dev, ops, NULL); +} EXPORT_SYMBOL_GPL(component_master_add); void component_master_del(struct device *dev, diff --git a/include/linux/component.h b/include/linux/component.h index 68870182ca1e..c00dcc302611 100644 --- a/include/linux/component.h +++ b/include/linux/component.h @@ -29,4 +29,11 @@ void component_master_del(struct device *, int component_master_add_child(struct master *master, int (*compare)(struct device *, void *), void *compare_data); +struct component_match; + +int component_master_add_with_match(struct device *, + const struct component_master_ops *, struct component_match *); +void component_match_add(struct device *, struct component_match **, + int (*compare)(struct device *, void *), void *compare_data); + #endif -- cgit v1.2.3 From e7cf34ef396d6ceaa776661830d4ce332da650f0 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Mon, 30 Jun 2014 10:57:35 -0500 Subject: regulator: palmas: Rename reg_info to palmas_reg_info reg_info is a generic term which might cause conflict at a later point in time. To prevent such a thing from occuring in future, rename to palmas_reg_info. Signed-off-by: Nishanth Menon Acked-by: Lee Jones Tested-by: Stephen Warren Tested-by: Keerthy Signed-off-by: Mark Brown --- drivers/regulator/palmas-regulator.c | 4 ++-- include/linux/mfd/palmas.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c index 91f60fa91d4b..05f11b96e17f 100644 --- a/drivers/regulator/palmas-regulator.c +++ b/drivers/regulator/palmas-regulator.c @@ -41,7 +41,7 @@ static const struct regulator_linear_range smps_high_ranges[] = { REGULATOR_LINEAR_RANGE(3300000, 0x7A, 0x7f, 0), }; -static struct regs_info palmas_regs_info[] = { +static struct palmas_regs_info palmas_regs_info[] = { { .name = "SMPS12", .sname = "smps1-in", @@ -227,7 +227,7 @@ static struct regs_info palmas_regs_info[] = { }, }; -static struct regs_info tps65917_regs_info[] = { +static struct palmas_regs_info tps65917_regs_info[] = { { .name = "SMPS1", .sname = "smps1-in", diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h index 1a045ba32c13..fb0390a1a498 100644 --- a/include/linux/mfd/palmas.h +++ b/include/linux/mfd/palmas.h @@ -102,7 +102,7 @@ struct palmas_sleep_requestor_info { int bit_pos; }; -struct regs_info { +struct palmas_regs_info { char *name; char *sname; u8 vsel_addr; @@ -117,7 +117,7 @@ struct palmas_pmic_driver_data { int ldo_begin; int ldo_end; int max_reg; - struct regs_info *palmas_regs_info; + struct palmas_regs_info *palmas_regs_info; struct of_regulator_match *palmas_matches; struct palmas_sleep_requestor_info *sleep_req_info; int (*smps_register)(struct palmas_pmic *pmic, -- cgit v1.2.3 From 67f86a45bb82091a2775561a1e498010afff54ee Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 8 Jun 2014 10:05:31 +0200 Subject: Bluetooth: Use const for struct l2cap_ops field The struct l2cap_ops field should not allow any modifications and thus it is better declared as const. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/l2cap.h | 2 +- net/bluetooth/a2mp.c | 2 +- net/bluetooth/l2cap_sock.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 4abdcb220e3a..cf67420616e5 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -579,7 +579,7 @@ struct l2cap_chan { struct list_head global_l; void *data; - struct l2cap_ops *ops; + const struct l2cap_ops *ops; struct mutex lock; }; diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 9514cc9e850c..1996b4c4dfd5 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -704,7 +704,7 @@ static struct sk_buff *a2mp_chan_alloc_skb_cb(struct l2cap_chan *chan, return skb; } -static struct l2cap_ops a2mp_chan_ops = { +static const struct l2cap_ops a2mp_chan_ops = { .name = "L2CAP A2MP channel", .recv = a2mp_chan_recv_cb, .close = a2mp_chan_close_cb, diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index e1378693cc90..11e2207717b6 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1375,7 +1375,7 @@ static void l2cap_sock_suspend_cb(struct l2cap_chan *chan) sk->sk_state_change(sk); } -static struct l2cap_ops l2cap_chan_ops = { +static const struct l2cap_ops l2cap_chan_ops = { .name = "L2CAP Socket Interface", .new_connection = l2cap_sock_new_connection_cb, .recv = l2cap_sock_recv_cb, -- cgit v1.2.3 From 8d46321c4f63f7c2be9e3ba0bb26cb437fc5eded Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 5 Jun 2014 15:22:51 +0200 Subject: Bluetooth: Assign L2CAP socket priority when allocating SKB The SKB for L2CAP sockets are all allocated in a central callback in the socket support. Instead of having to pass around the socket priority all the time, assign it to skb->priority when actually allocating the SKB. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/l2cap.h | 3 +-- net/bluetooth/a2mp.c | 2 +- net/bluetooth/l2cap_core.c | 23 +++++++---------------- net/bluetooth/l2cap_sock.c | 4 +++- 4 files changed, 12 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index cf67420616e5..18f4f27e0f74 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -872,8 +872,7 @@ struct l2cap_chan *l2cap_chan_create(void); void l2cap_chan_close(struct l2cap_chan *chan, int reason); int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *dst, u8 dst_type); -int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, - u32 priority); +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); diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 1996b4c4dfd5..1a5b1eb96d1a 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -63,7 +63,7 @@ void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data) msg.msg_iov = (struct iovec *) &iv; msg.msg_iovlen = 1; - l2cap_chan_send(chan, &msg, total_len, 0); + l2cap_chan_send(chan, &msg, total_len); kfree(cmd); } diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 323f23cd2c37..3dca28246cbe 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -2141,8 +2141,6 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan, if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) return -EFAULT; - (*frag)->priority = skb->priority; - sent += count; len -= count; @@ -2156,16 +2154,15 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan, } static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, - struct msghdr *msg, size_t len, - u32 priority) + struct msghdr *msg, size_t len) { struct l2cap_conn *conn = chan->conn; struct sk_buff *skb; int err, count, hlen = L2CAP_HDR_SIZE + L2CAP_PSMLEN_SIZE; struct l2cap_hdr *lh; - BT_DBG("chan %p psm 0x%2.2x len %zu priority %u", chan, - __le16_to_cpu(chan->psm), len, priority); + BT_DBG("chan %p psm 0x%2.2x len %zu", chan, + __le16_to_cpu(chan->psm), len); count = min_t(unsigned int, (conn->mtu - hlen), len); @@ -2174,8 +2171,6 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, if (IS_ERR(skb)) return skb; - skb->priority = priority; - /* Create L2CAP header */ lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh->cid = cpu_to_le16(chan->dcid); @@ -2191,8 +2186,7 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, } static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, - struct msghdr *msg, size_t len, - u32 priority) + struct msghdr *msg, size_t len) { struct l2cap_conn *conn = chan->conn; struct sk_buff *skb; @@ -2208,8 +2202,6 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, if (IS_ERR(skb)) return skb; - skb->priority = priority; - /* Create L2CAP header */ lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh->cid = cpu_to_le16(chan->dcid); @@ -2430,8 +2422,7 @@ static int l2cap_segment_le_sdu(struct l2cap_chan *chan, return 0; } -int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, - u32 priority) +int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len) { struct sk_buff *skb; int err; @@ -2442,7 +2433,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, /* Connectionless channel */ if (chan->chan_type == L2CAP_CHAN_CONN_LESS) { - skb = l2cap_create_connless_pdu(chan, msg, len, priority); + skb = l2cap_create_connless_pdu(chan, msg, len); if (IS_ERR(skb)) return PTR_ERR(skb); @@ -2499,7 +2490,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, return -EMSGSIZE; /* Create a basic PDU */ - skb = l2cap_create_basic_pdu(chan, msg, len, priority); + skb = l2cap_create_basic_pdu(chan, msg, len); if (IS_ERR(skb)) return PTR_ERR(skb); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 11e2207717b6..d95964c9f91e 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -964,7 +964,7 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, return err; l2cap_chan_lock(chan); - err = l2cap_chan_send(chan, msg, len, sk->sk_priority); + err = l2cap_chan_send(chan, msg, len); l2cap_chan_unlock(chan); return err; @@ -1305,6 +1305,8 @@ static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan, if (!skb) return ERR_PTR(err); + skb->priority = sk->sk_priority; + bt_cb(skb)->chan = chan; return skb; -- cgit v1.2.3 From 0775899158d0e1436316f3bf451d78bf34b88d17 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 8 Jun 2014 10:26:57 +0200 Subject: Bluetooth: Shrink size of struct l2cap_ctrl fields The struct l2cap_ctrl fields are wasting an unsigned int when all the bits can fit into an __u8 field. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/bluetooth.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 904777c1cd24..373000de610d 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -260,15 +260,15 @@ 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; - __u16 reqseq; - __u16 txseq; - __u8 retries; + __u8 sframe:1, + poll:1, + final:1, + fcs:1, + sar:2, + super:2; + __u16 reqseq; + __u16 txseq; + __u8 retries; }; struct hci_dev; -- cgit v1.2.3 From d9fbd02be5c201c1659ee0d79c0820bb68d95c8c Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 8 Jun 2014 11:22:28 +0200 Subject: Bluetooth: Use explicit header and body length for L2CAP SKB allocation When allocating the L2CAP SKB for transmission, provide the upper layers with a clear distinction on what is the header and what is the body portion of the SKB. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/a2mp.c | 3 ++- net/bluetooth/l2cap_core.c | 10 +++++----- net/bluetooth/l2cap_sock.c | 3 ++- 4 files changed, 10 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 18f4f27e0f74..92511034d1d4 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -600,6 +600,7 @@ struct l2cap_ops { void (*set_shutdown) (struct l2cap_chan *chan); long (*get_sndtimeo) (struct l2cap_chan *chan); struct sk_buff *(*alloc_skb) (struct l2cap_chan *chan, + unsigned long hdr_len, unsigned long len, int nb); }; diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 1a5b1eb96d1a..0fd8d1dda709 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -693,11 +693,12 @@ static void a2mp_chan_state_change_cb(struct l2cap_chan *chan, int state, } static struct sk_buff *a2mp_chan_alloc_skb_cb(struct l2cap_chan *chan, + unsigned long hdr_len, unsigned long len, int nb) { struct sk_buff *skb; - skb = bt_skb_alloc(len, GFP_KERNEL); + skb = bt_skb_alloc(hdr_len + len, GFP_KERNEL); if (!skb) return ERR_PTR(-ENOMEM); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 3dca28246cbe..ac2461442f21 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -2131,7 +2131,7 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan, count = min_t(unsigned int, conn->mtu, len); - tmp = chan->ops->alloc_skb(chan, count, + tmp = chan->ops->alloc_skb(chan, 0, count, msg->msg_flags & MSG_DONTWAIT); if (IS_ERR(tmp)) return PTR_ERR(tmp); @@ -2166,7 +2166,7 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, count = min_t(unsigned int, (conn->mtu - hlen), len); - skb = chan->ops->alloc_skb(chan, count + hlen, + skb = chan->ops->alloc_skb(chan, hlen, count, msg->msg_flags & MSG_DONTWAIT); if (IS_ERR(skb)) return skb; @@ -2197,7 +2197,7 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, count = min_t(unsigned int, (conn->mtu - L2CAP_HDR_SIZE), len); - skb = chan->ops->alloc_skb(chan, count + L2CAP_HDR_SIZE, + skb = chan->ops->alloc_skb(chan, L2CAP_HDR_SIZE, count, msg->msg_flags & MSG_DONTWAIT); if (IS_ERR(skb)) return skb; @@ -2239,7 +2239,7 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, count = min_t(unsigned int, (conn->mtu - hlen), len); - skb = chan->ops->alloc_skb(chan, count + hlen, + skb = chan->ops->alloc_skb(chan, hlen, count, msg->msg_flags & MSG_DONTWAIT); if (IS_ERR(skb)) return skb; @@ -2360,7 +2360,7 @@ static struct sk_buff *l2cap_create_le_flowctl_pdu(struct l2cap_chan *chan, count = min_t(unsigned int, (conn->mtu - hlen), len); - skb = chan->ops->alloc_skb(chan, count + hlen, + skb = chan->ops->alloc_skb(chan, hlen, count, msg->msg_flags & MSG_DONTWAIT); if (IS_ERR(skb)) return skb; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index d95964c9f91e..55215ebf6547 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1292,6 +1292,7 @@ static void l2cap_sock_state_change_cb(struct l2cap_chan *chan, int state, } static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan, + unsigned long hdr_len, unsigned long len, int nb) { struct sock *sk = chan->data; @@ -1299,7 +1300,7 @@ static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan, int err; l2cap_chan_unlock(chan); - skb = bt_skb_send_alloc(sk, len, nb, &err); + skb = bt_skb_send_alloc(sk, hdr_len + len, nb, &err); l2cap_chan_lock(chan); if (!skb) -- cgit v1.2.3 From 65cc2b49db63adf1455a9783234383fbec5b8314 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 16 Jun 2014 12:30:56 +0200 Subject: Bluetooth: Use struct delayed_work for HCI command timeout Since the whole HCI command, event and data packet processing has been migrated to use workqueues instead of tasklets, it makes sense to use struct delayed_work instead of struct timer_list for the timeout handling. This patch converts the hdev->cmd_timer to use workqueue as well. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_core.c | 17 +++++++++-------- net/bluetooth/hci_event.c | 4 ++-- 3 files changed, 12 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b386bf17e6c2..de3bb22e83f9 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -273,7 +273,7 @@ struct hci_dev { struct delayed_work service_cache; - struct timer_list cmd_timer; + struct delayed_work cmd_timer; struct work_struct rx_work; struct work_struct cmd_work; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 0a43cce9a914..9e0368b02a11 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2432,7 +2432,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) hci_req_lock(hdev); if (!test_and_clear_bit(HCI_UP, &hdev->flags)) { - del_timer_sync(&hdev->cmd_timer); + cancel_delayed_work_sync(&hdev->cmd_timer); hci_req_unlock(hdev); return 0; } @@ -2488,7 +2488,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) /* Drop last sent command */ if (hdev->sent_cmd) { - del_timer_sync(&hdev->cmd_timer); + cancel_delayed_work_sync(&hdev->cmd_timer); kfree_skb(hdev->sent_cmd); hdev->sent_cmd = NULL; } @@ -3205,9 +3205,10 @@ void hci_remove_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type) } /* HCI command timer function */ -static void hci_cmd_timeout(unsigned long arg) +static void hci_cmd_timeout(struct work_struct *work) { - struct hci_dev *hdev = (void *) arg; + struct hci_dev *hdev = container_of(work, struct hci_dev, + cmd_timer.work); if (hdev->sent_cmd) { struct hci_command_hdr *sent = (void *) hdev->sent_cmd->data; @@ -3884,7 +3885,7 @@ struct hci_dev *hci_alloc_dev(void) init_waitqueue_head(&hdev->req_wait_q); - setup_timer(&hdev->cmd_timer, hci_cmd_timeout, (unsigned long) hdev); + INIT_DELAYED_WORK(&hdev->cmd_timer, hci_cmd_timeout); hci_init_sysfs(hdev); discovery_init(hdev); @@ -5287,10 +5288,10 @@ static void hci_cmd_work(struct work_struct *work) atomic_dec(&hdev->cmd_cnt); hci_send_frame(hdev, skb); if (test_bit(HCI_RESET, &hdev->flags)) - del_timer(&hdev->cmd_timer); + cancel_delayed_work(&hdev->cmd_timer); else - mod_timer(&hdev->cmd_timer, - jiffies + HCI_CMD_TIMEOUT); + schedule_delayed_work(&hdev->cmd_timer, + HCI_CMD_TIMEOUT); } else { skb_queue_head(&hdev->cmd_q, skb); queue_work(hdev->workqueue, &hdev->cmd_work); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 640c54ec1bd2..3d52be1d695d 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2709,7 +2709,7 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) } if (opcode != HCI_OP_NOP) - del_timer(&hdev->cmd_timer); + cancel_delayed_work(&hdev->cmd_timer); hci_req_cmd_complete(hdev, opcode, status); @@ -2800,7 +2800,7 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) } if (opcode != HCI_OP_NOP) - del_timer(&hdev->cmd_timer); + cancel_delayed_work(&hdev->cmd_timer); if (ev->status || (hdev->sent_cmd && !bt_cb(hdev->sent_cmd)->req.event)) -- cgit v1.2.3 From 7d5843b7b77cee26bd5e090bfa61780d75957648 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 16 Jun 2014 19:25:15 +0300 Subject: Bluetooth: Remove unnecessary SMP STK define We never store the "master" type of STKs since we request encryption directly with them so we only need one STK type (the one that's looked-up on the slave side). Simply remove the unnecessary define and rename the _SLAVE one to the shorter form. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 1 - net/bluetooth/hci_event.c | 2 +- net/bluetooth/smp.c | 6 +++++- 3 files changed, 6 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 16587dcd6a91..98f7520d2f57 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -349,7 +349,6 @@ enum { #define HCI_LK_AUTH_COMBINATION_P256 0x08 /* The spec doesn't define types for SMP keys, the _MASTER suffix is implied */ #define HCI_SMP_STK 0x80 -#define HCI_SMP_STK_SLAVE 0x81 #define HCI_SMP_LTK 0x82 #define HCI_SMP_LTK_SLAVE 0x83 diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 3d52be1d695d..8dde40430b5f 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4241,7 +4241,7 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb) * distribute the keys. Later, security can be re-established * using a distributed LTK. */ - if (ltk->type == HCI_SMP_STK_SLAVE) { + if (ltk->type == HCI_SMP_STK) { list_del(<k->list); kfree(ltk); } diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 1f4ed1e78e10..b9cac1deb19f 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -568,8 +568,12 @@ static u8 smp_random(struct smp_chan *smp) else auth = 0; + /* Even though there's no _SLAVE suffix this is the + * slave STK we're adding for later lookup (the master + * STK never needs to be stored). + */ hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, - HCI_SMP_STK_SLAVE, auth, stk, smp->enc_key_size, + HCI_SMP_STK, auth, stk, smp->enc_key_size, ediv, rand); } -- cgit v1.2.3 From 2ceba53936d6f2071659b87748d723021937d035 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 16 Jun 2014 19:25:16 +0300 Subject: Bluetooth: Remove HCI prefix from SMP LTK defines The LTK type has really nothing to do with HCI so it makes more sense to have these in smp.h than hci.h. This patch moves the defines to smp.h and removes the HCI_ prefix in the same go. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 4 ---- net/bluetooth/hci_core.c | 2 +- net/bluetooth/hci_event.c | 3 ++- net/bluetooth/mgmt.c | 6 +++--- net/bluetooth/smp.c | 7 +++---- net/bluetooth/smp.h | 7 +++++++ 6 files changed, 16 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 98f7520d2f57..6ec5b3bd1f67 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -347,10 +347,6 @@ enum { #define HCI_LK_CHANGED_COMBINATION 0x06 #define HCI_LK_UNAUTH_COMBINATION_P256 0x07 #define HCI_LK_AUTH_COMBINATION_P256 0x08 -/* The spec doesn't define types for SMP keys, the _MASTER suffix is implied */ -#define HCI_SMP_STK 0x80 -#define HCI_SMP_LTK 0x82 -#define HCI_SMP_LTK_SLAVE 0x83 /* Long Term Key types */ #define HCI_LTK_UNAUTH 0x00 diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 9e0368b02a11..6001b9293905 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2974,7 +2974,7 @@ static bool hci_persistent_key(struct hci_dev *hdev, struct hci_conn *conn, static bool ltk_type_master(u8 type) { - if (type == HCI_SMP_STK || type == HCI_SMP_LTK) + if (type == SMP_STK || type == SMP_LTK) return true; return false; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 8dde40430b5f..15a0a65bf447 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -32,6 +32,7 @@ #include "a2mp.h" #include "amp.h" +#include "smp.h" /* Handle HCI Event packets */ @@ -4241,7 +4242,7 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb) * distribute the keys. Later, security can be re-established * using a distributed LTK. */ - if (ltk->type == HCI_SMP_STK) { + if (ltk->type == SMP_STK) { list_del(<k->list); kfree(ltk); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index af8e0a6243b7..99eb845865ef 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -4550,9 +4550,9 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, addr_type = ADDR_LE_DEV_RANDOM; if (key->master) - type = HCI_SMP_LTK; + type = SMP_LTK; else - type = HCI_SMP_LTK_SLAVE; + type = SMP_LTK_SLAVE; switch (key->type) { case MGMT_LTK_UNAUTHENTICATED: @@ -5279,7 +5279,7 @@ void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent) ev.key.ediv = key->ediv; ev.key.rand = key->rand; - if (key->type == HCI_SMP_LTK) + if (key->type == SMP_LTK) ev.key.master = 1; memcpy(ev.key.val, key->val, sizeof(key->val)); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index b9cac1deb19f..78b9573f4a73 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -573,8 +573,7 @@ static u8 smp_random(struct smp_chan *smp) * STK never needs to be stored). */ hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, - HCI_SMP_STK, auth, stk, smp->enc_key_size, - ediv, rand); + SMP_STK, auth, stk, smp->enc_key_size, ediv, rand); } return 0; @@ -1027,7 +1026,7 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb) hci_dev_lock(hdev); authenticated = (hcon->sec_level == BT_SECURITY_HIGH); - ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, HCI_SMP_LTK, + ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, SMP_LTK, authenticated, smp->tk, smp->enc_key_size, rp->ediv, rp->rand); smp->ltk = ltk; @@ -1343,7 +1342,7 @@ int smp_distribute_keys(struct l2cap_conn *conn) authenticated = hcon->sec_level == BT_SECURITY_HIGH; ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, - HCI_SMP_LTK_SLAVE, authenticated, enc.ltk, + SMP_LTK_SLAVE, authenticated, enc.ltk, smp->enc_key_size, ediv, rand); smp->slave_ltk = ltk; diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h index 5a8dc36460a1..796f4f45f92f 100644 --- a/net/bluetooth/smp.h +++ b/net/bluetooth/smp.h @@ -116,6 +116,13 @@ struct smp_cmd_security_req { #define SMP_MIN_ENC_KEY_SIZE 7 #define SMP_MAX_ENC_KEY_SIZE 16 +/* LTK types used in internal storage (struct smp_ltk) */ +enum { + SMP_STK, + SMP_LTK, + SMP_LTK_SLAVE, +}; + /* SMP Commands */ bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level); int smp_conn_security(struct hci_conn *hcon, __u8 sec_level); -- cgit v1.2.3 From ddf4ba078f80415a514e175768bf67631beee7eb Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 16 Jun 2014 19:25:17 +0300 Subject: Bluetooth: Remove unused LTK authentication defines These defines were probably put in to track authenticated vs unauthenticated LTKs, however since the LTK struct has a separate boolean authenticated member these were never used. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 6ec5b3bd1f67..fe584d78008d 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -348,10 +348,6 @@ enum { #define HCI_LK_UNAUTH_COMBINATION_P256 0x07 #define HCI_LK_AUTH_COMBINATION_P256 0x08 -/* Long Term Key types */ -#define HCI_LTK_UNAUTH 0x00 -#define HCI_LTK_AUTH 0x01 - /* ---- HCI Error Codes ---- */ #define HCI_ERROR_AUTH_FAILURE 0x05 #define HCI_ERROR_MEMORY_EXCEEDED 0x07 -- cgit v1.2.3 From 111902f7236ff8139c30c2b9709c999fcb931399 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 21 Jun 2014 04:53:17 +0200 Subject: Bluetooth: Use separate dbg_flags to special debugfs options All the special settings configured via debugfs are either developer only options or temporary solutions. To not clutter the standard flags, move them to their own dbg_flags entry. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 12 +++++++++--- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 24 ++++++++++++------------ net/bluetooth/mgmt.c | 4 ++-- 4 files changed, 24 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index fe584d78008d..3e12e27afca1 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -104,6 +104,15 @@ enum { HCI_RESET, }; +/* BR/EDR and/or LE controller flags: the flags defined here should represent + * states configured via debugfs for debugging and testing purposes only. + */ +enum { + HCI_DUT_MODE, + HCI_FORCE_SC, + HCI_FORCE_STATIC_ADDR, +}; + /* * BR/EDR and/or LE controller flags: the flags defined here should represent * states from the controller. @@ -116,9 +125,6 @@ enum { HCI_PAIRABLE, HCI_SERVICE_CACHE, HCI_DEBUG_KEYS, - HCI_DUT_MODE, - HCI_FORCE_SC, - HCI_FORCE_STATIC_ADDR, HCI_UNREGISTER, HCI_USER_CHANNEL, diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index de3bb22e83f9..bf41e992a618 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -318,6 +318,7 @@ struct hci_dev { struct rfkill *rfkill; + unsigned long dbg_flags; unsigned long dev_flags; struct delayed_work le_scan_disable; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index c3d184fd24de..ddbb084250be 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -68,7 +68,7 @@ static ssize_t dut_mode_read(struct file *file, char __user *user_buf, struct hci_dev *hdev = file->private_data; char buf[3]; - buf[0] = test_bit(HCI_DUT_MODE, &hdev->dev_flags) ? 'Y': 'N'; + buf[0] = test_bit(HCI_DUT_MODE, &hdev->dbg_flags) ? 'Y': 'N'; buf[1] = '\n'; buf[2] = '\0'; return simple_read_from_buffer(user_buf, count, ppos, buf, 2); @@ -94,7 +94,7 @@ static ssize_t dut_mode_write(struct file *file, const char __user *user_buf, if (strtobool(buf, &enable)) return -EINVAL; - if (enable == test_bit(HCI_DUT_MODE, &hdev->dev_flags)) + if (enable == test_bit(HCI_DUT_MODE, &hdev->dbg_flags)) return -EALREADY; hci_req_lock(hdev); @@ -115,7 +115,7 @@ static ssize_t dut_mode_write(struct file *file, const char __user *user_buf, if (err < 0) return err; - change_bit(HCI_DUT_MODE, &hdev->dev_flags); + change_bit(HCI_DUT_MODE, &hdev->dbg_flags); return count; } @@ -407,7 +407,7 @@ static ssize_t force_sc_support_read(struct file *file, char __user *user_buf, struct hci_dev *hdev = file->private_data; char buf[3]; - buf[0] = test_bit(HCI_FORCE_SC, &hdev->dev_flags) ? 'Y': 'N'; + buf[0] = test_bit(HCI_FORCE_SC, &hdev->dbg_flags) ? 'Y': 'N'; buf[1] = '\n'; buf[2] = '\0'; return simple_read_from_buffer(user_buf, count, ppos, buf, 2); @@ -432,10 +432,10 @@ static ssize_t force_sc_support_write(struct file *file, if (strtobool(buf, &enable)) return -EINVAL; - if (enable == test_bit(HCI_FORCE_SC, &hdev->dev_flags)) + if (enable == test_bit(HCI_FORCE_SC, &hdev->dbg_flags)) return -EALREADY; - change_bit(HCI_FORCE_SC, &hdev->dev_flags); + change_bit(HCI_FORCE_SC, &hdev->dbg_flags); return count; } @@ -719,7 +719,7 @@ static ssize_t force_static_address_read(struct file *file, struct hci_dev *hdev = file->private_data; char buf[3]; - buf[0] = test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags) ? 'Y': 'N'; + buf[0] = test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) ? 'Y': 'N'; buf[1] = '\n'; buf[2] = '\0'; return simple_read_from_buffer(user_buf, count, ppos, buf, 2); @@ -744,10 +744,10 @@ static ssize_t force_static_address_write(struct file *file, if (strtobool(buf, &enable)) return -EINVAL; - if (enable == test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags)) + if (enable == test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags)) return -EALREADY; - change_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags); + change_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags); return count; } @@ -1752,7 +1752,7 @@ static void hci_init4_req(struct hci_request *req, unsigned long opt) /* Enable Secure Connections if supported and configured */ if ((lmp_sc_capable(hdev) || - test_bit(HCI_FORCE_SC, &hdev->dev_flags)) && + test_bit(HCI_FORCE_SC, &hdev->dbg_flags)) && test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) { u8 support = 0x01; hci_req_add(req, HCI_OP_WRITE_SC_SUPPORT, @@ -3782,7 +3782,7 @@ int hci_update_random_address(struct hci_request *req, bool require_privacy, * the HCI command if the current random address is already the * static one. */ - if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags) || + if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) || !bacmp(&hdev->bdaddr, BDADDR_ANY)) { *own_addr_type = ADDR_LE_DEV_RANDOM; if (bacmp(&hdev->static_addr, &hdev->random_addr)) @@ -3811,7 +3811,7 @@ int hci_update_random_address(struct hci_request *req, bool require_privacy, void hci_copy_identity_address(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *bdaddr_type) { - if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags) || + if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) || !bacmp(&hdev->bdaddr, BDADDR_ANY)) { bacpy(bdaddr, &hdev->static_addr); *bdaddr_type = ADDR_LE_DEV_RANDOM; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 5bf032d9bce1..e1651c3fc676 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -387,7 +387,7 @@ static u32 get_supported_settings(struct hci_dev *hdev) } if (lmp_sc_capable(hdev) || - test_bit(HCI_FORCE_SC, &hdev->dev_flags)) + test_bit(HCI_FORCE_SC, &hdev->dbg_flags)) settings |= MGMT_SETTING_SECURE_CONN; } @@ -4261,7 +4261,7 @@ static int set_secure_conn(struct sock *sk, struct hci_dev *hdev, status); if (!lmp_sc_capable(hdev) && - !test_bit(HCI_FORCE_SC, &hdev->dev_flags)) + !test_bit(HCI_FORCE_SC, &hdev->dbg_flags)) return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN, MGMT_STATUS_NOT_SUPPORTED); -- cgit v1.2.3 From 0498878b18993891f7b71c75b6adcb7c157501db Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 18 Jun 2014 16:37:07 +0300 Subject: Bluetooth: Provide L2CAP ops callback for memcpy_fromiovec The highly optimized TX path for L2CAP channels and its fragmentation within the HCI ACL packets requires to copy data from user provided IO vectors and also kernel provided memory buffers. This patch allows channel clients to provide a memcpy_fromiovec callback to keep this optimized behavior, but adapt it to kernel vs user memory for the TX path. For all kernel internal L2CAP channels, a default implementation is provided that can be referenced. In case of A2MP, this fixes a long-standing issue with wrongly accessing kernel memory as user memory. This patch originally by Marcel Holtmann. Signed-off-by: Jukka Rissanen Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 29 +++++++++++++++++++++++++++++ net/bluetooth/a2mp.c | 1 + net/bluetooth/l2cap_core.c | 6 ++++-- net/bluetooth/l2cap_sock.c | 34 +++++++++++++++++++++------------- 4 files changed, 55 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 92511034d1d4..1ee6f00d7096 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -602,6 +602,10 @@ struct l2cap_ops { struct sk_buff *(*alloc_skb) (struct l2cap_chan *chan, unsigned long hdr_len, unsigned long len, int nb); + int (*memcpy_fromiovec) (struct l2cap_chan *chan, + unsigned char *kdata, + struct iovec *iov, + int len); }; struct l2cap_conn { @@ -857,6 +861,31 @@ static inline long l2cap_chan_no_get_sndtimeo(struct l2cap_chan *chan) return 0; } +static inline int l2cap_chan_no_memcpy_fromiovec(struct l2cap_chan *chan, + unsigned char *kdata, + struct iovec *iov, + int len) +{ + /* Following is safe since for compiler definitions of kvec and + * iovec are identical, yielding the same in-core layout and alignment + */ + struct kvec *vec = (struct kvec *)iov; + + while (len > 0) { + if (vec->iov_len) { + int copy = min_t(unsigned int, len, vec->iov_len); + memcpy(kdata, vec->iov_base, copy); + len -= copy; + kdata += copy; + vec->iov_base += copy; + vec->iov_len -= copy; + } + vec++; + } + + return 0; +} + extern bool disable_ertm; int l2cap_init_sockets(void); diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 0fd8d1dda709..5dcade511fdb 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -720,6 +720,7 @@ static const struct l2cap_ops a2mp_chan_ops = { .resume = l2cap_chan_no_resume, .set_shutdown = l2cap_chan_no_set_shutdown, .get_sndtimeo = l2cap_chan_no_get_sndtimeo, + .memcpy_fromiovec = l2cap_chan_no_memcpy_fromiovec, }; static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index ac2461442f21..d0a5fde61a07 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -2118,7 +2118,8 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan, struct sk_buff **frag; int sent = 0; - if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) + if (chan->ops->memcpy_fromiovec(chan, skb_put(skb, count), + msg->msg_iov, count)) return -EFAULT; sent += count; @@ -2138,7 +2139,8 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan, *frag = tmp; - if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) + if (chan->ops->memcpy_fromiovec(chan, skb_put(*frag, count), + msg->msg_iov, count)) return -EFAULT; sent += count; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 55215ebf6547..bf72886de6ef 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1313,6 +1313,13 @@ static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan, return skb; } +static int l2cap_sock_memcpy_fromiovec_cb(struct l2cap_chan *chan, + unsigned char *kdata, + struct iovec *iov, int len) +{ + return memcpy_fromiovec(kdata, iov, len); +} + static void l2cap_sock_ready_cb(struct l2cap_chan *chan) { struct sock *sk = chan->data; @@ -1379,19 +1386,20 @@ static void l2cap_sock_suspend_cb(struct l2cap_chan *chan) } static const struct l2cap_ops l2cap_chan_ops = { - .name = "L2CAP Socket Interface", - .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, - .ready = l2cap_sock_ready_cb, - .defer = l2cap_sock_defer_cb, - .resume = l2cap_sock_resume_cb, - .suspend = l2cap_sock_suspend_cb, - .set_shutdown = l2cap_sock_set_shutdown_cb, - .get_sndtimeo = l2cap_sock_get_sndtimeo_cb, - .alloc_skb = l2cap_sock_alloc_skb_cb, + .name = "L2CAP Socket Interface", + .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, + .ready = l2cap_sock_ready_cb, + .defer = l2cap_sock_defer_cb, + .resume = l2cap_sock_resume_cb, + .suspend = l2cap_sock_suspend_cb, + .set_shutdown = l2cap_sock_set_shutdown_cb, + .get_sndtimeo = l2cap_sock_get_sndtimeo_cb, + .alloc_skb = l2cap_sock_alloc_skb_cb, + .memcpy_fromiovec = l2cap_sock_memcpy_fromiovec_cb, }; static void l2cap_sock_destruct(struct sock *sk) -- cgit v1.2.3 From 6b8d4a6a03144c5996f98db7f8256267b0d72a3a Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 18 Jun 2014 16:37:08 +0300 Subject: Bluetooth: 6LoWPAN: Use connected oriented channel instead of fixed one Create a CoC dynamically instead of one fixed channel for communication to peer devices. Signed-off-by: Jukka Rissanen Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 1 - include/net/bluetooth/hci_core.h | 1 - include/net/bluetooth/l2cap.h | 1 - net/bluetooth/6lowpan.c | 787 +++++++++++++++++++++++++++++---------- net/bluetooth/6lowpan.h | 47 --- net/bluetooth/hci_core.c | 45 --- net/bluetooth/hci_event.c | 3 - net/bluetooth/l2cap_core.c | 20 +- 8 files changed, 588 insertions(+), 317 deletions(-) delete mode 100644 net/bluetooth/6lowpan.h (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 3e12e27afca1..3f3a3f1399fb 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -145,7 +145,6 @@ enum { HCI_PERIODIC_INQ, HCI_FAST_CONNECTABLE, HCI_BREDR_ENABLED, - HCI_6LOWPAN_ENABLED, HCI_LE_SCAN_INTERRUPTED, }; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index bf41e992a618..c81de0d366df 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -521,7 +521,6 @@ enum { HCI_CONN_AES_CCM, HCI_CONN_POWER_SAVE, HCI_CONN_REMOTE_OOB, - HCI_CONN_6LOWPAN, }; static inline bool hci_conn_ssp_enabled(struct hci_conn *conn) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 1ee6f00d7096..e0c6a9abdb62 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -137,7 +137,6 @@ struct l2cap_conninfo { #define L2CAP_FC_L2CAP 0x02 #define L2CAP_FC_CONNLESS 0x04 #define L2CAP_FC_A2MP 0x08 -#define L2CAP_FC_6LOWPAN 0x3e /* reserved and temporary value */ /* L2CAP Control Field bit masks */ #define L2CAP_CTRL_SAR 0xC000 diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 8796ffa08b43..bdb01eb3bfcc 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2013 Intel Corp. + Copyright (c) 2013-2014 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 @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -25,16 +26,20 @@ #include #include -#include "6lowpan.h" - #include /* for the compression support */ +#define VERSION "0.1" + +static struct dentry *lowpan_psm_debugfs; +static struct dentry *lowpan_control_debugfs; + #define IFACE_NAME_TEMPLATE "bt%d" #define EUI64_ADDR_LEN 8 struct skb_cb { struct in6_addr addr; - struct l2cap_conn *conn; + struct l2cap_chan *chan; + int status; }; #define lowpan_cb(skb) ((struct skb_cb *)((skb)->cb)) @@ -48,9 +53,19 @@ struct skb_cb { static LIST_HEAD(bt_6lowpan_devices); static DEFINE_RWLOCK(devices_lock); +/* If psm is set to 0 (default value), then 6lowpan is disabled. + * Other values are used to indicate a Protocol Service Multiplexer + * value for 6lowpan. + */ +static u16 psm_6lowpan; + +/* We are listening incoming connections via this channel + */ +static struct l2cap_chan *listen_chan; + struct lowpan_peer { struct list_head list; - struct l2cap_conn *conn; + struct l2cap_chan *chan; /* peer addresses in various formats */ unsigned char eui64_addr[EUI64_ADDR_LEN]; @@ -101,13 +116,26 @@ static inline struct lowpan_peer *peer_lookup_ba(struct lowpan_dev *dev, ba, type); list_for_each_entry_safe(peer, tmp, &dev->peers, list) { - BT_DBG("addr %pMR type %d", - &peer->conn->hcon->dst, peer->conn->hcon->dst_type); + BT_DBG("dst addr %pMR dst type %d", + &peer->chan->dst, peer->chan->dst_type); - if (bacmp(&peer->conn->hcon->dst, ba)) + if (bacmp(&peer->chan->dst, ba)) continue; - if (type == peer->conn->hcon->dst_type) + if (type == peer->chan->dst_type) + return peer; + } + + return NULL; +} + +static inline struct lowpan_peer *peer_lookup_chan(struct lowpan_dev *dev, + struct l2cap_chan *chan) +{ + struct lowpan_peer *peer, *tmp; + + list_for_each_entry_safe(peer, tmp, &dev->peers, list) { + if (peer->chan == chan) return peer; } @@ -120,7 +148,7 @@ static inline struct lowpan_peer *peer_lookup_conn(struct lowpan_dev *dev, struct lowpan_peer *peer, *tmp; list_for_each_entry_safe(peer, tmp, &dev->peers, list) { - if (peer->conn == conn) + if (peer->chan->conn == conn) return peer; } @@ -176,16 +204,16 @@ static int give_skb_to_upper(struct sk_buff *skb, struct net_device *dev) return -ENOMEM; ret = netif_rx(skb_cp); - - BT_DBG("receive skb %d", ret); - if (ret < 0) + if (ret < 0) { + BT_DBG("receive skb %d", ret); return NET_RX_DROP; + } return ret; } static int process_data(struct sk_buff *skb, struct net_device *netdev, - struct l2cap_conn *conn) + struct l2cap_chan *chan) { const u8 *saddr, *daddr; u8 iphc0, iphc1; @@ -196,7 +224,7 @@ static int process_data(struct sk_buff *skb, struct net_device *netdev, dev = lowpan_dev(netdev); read_lock_irqsave(&devices_lock, flags); - peer = peer_lookup_conn(dev, conn); + peer = peer_lookup_chan(dev, chan); read_unlock_irqrestore(&devices_lock, flags); if (!peer) goto drop; @@ -225,7 +253,7 @@ drop: } static int recv_pkt(struct sk_buff *skb, struct net_device *dev, - struct l2cap_conn *conn) + struct l2cap_chan *chan) { struct sk_buff *local_skb; int ret; @@ -269,7 +297,7 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev, if (!local_skb) goto drop; - ret = process_data(local_skb, dev, conn); + ret = process_data(local_skb, dev, chan); if (ret != NET_RX_SUCCESS) goto drop; @@ -286,147 +314,39 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev, return NET_RX_SUCCESS; drop: + dev->stats.rx_dropped++; kfree_skb(skb); return NET_RX_DROP; } /* Packet from BT LE device */ -int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb) +static int chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) { struct lowpan_dev *dev; struct lowpan_peer *peer; int err; - peer = lookup_peer(conn); + peer = lookup_peer(chan->conn); if (!peer) return -ENOENT; - dev = lookup_dev(conn); + dev = lookup_dev(chan->conn); if (!dev || !dev->netdev) return -ENOENT; - err = recv_pkt(skb, dev->netdev, conn); - BT_DBG("recv pkt %d", err); - - return err; -} - -static inline int skbuff_copy(void *msg, int len, int count, int mtu, - struct sk_buff *skb, struct net_device *dev) -{ - struct sk_buff **frag; - int sent = 0; - - memcpy(skb_put(skb, count), msg, count); - - sent += count; - msg += count; - len -= count; - - dev->stats.tx_bytes += count; - dev->stats.tx_packets++; - - raw_dump_table(__func__, "Sending", skb->data, skb->len); - - /* Continuation fragments (no L2CAP header) */ - frag = &skb_shinfo(skb)->frag_list; - while (len > 0) { - struct sk_buff *tmp; - - count = min_t(unsigned int, mtu, len); - - tmp = bt_skb_alloc(count, GFP_ATOMIC); - if (!tmp) - return -ENOMEM; - - *frag = tmp; - - memcpy(skb_put(*frag, count), msg, count); - - raw_dump_table(__func__, "Sending fragment", - (*frag)->data, count); - - (*frag)->priority = skb->priority; - - sent += count; - msg += count; - len -= count; - - skb->len += (*frag)->len; - skb->data_len += (*frag)->len; - - frag = &(*frag)->next; - - dev->stats.tx_bytes += count; - dev->stats.tx_packets++; - } - - return sent; -} - -static struct sk_buff *create_pdu(struct l2cap_conn *conn, void *msg, - size_t len, u32 priority, - struct net_device *dev) -{ - struct sk_buff *skb; - int err, count; - struct l2cap_hdr *lh; - - /* FIXME: This mtu check should be not needed and atm is only used for - * testing purposes - */ - if (conn->mtu > (L2CAP_LE_MIN_MTU + L2CAP_HDR_SIZE)) - conn->mtu = L2CAP_LE_MIN_MTU + L2CAP_HDR_SIZE; - - count = min_t(unsigned int, (conn->mtu - L2CAP_HDR_SIZE), len); - - BT_DBG("conn %p len %zu mtu %d count %d", conn, len, conn->mtu, count); - - skb = bt_skb_alloc(count + L2CAP_HDR_SIZE, GFP_ATOMIC); - if (!skb) - return ERR_PTR(-ENOMEM); - - skb->priority = priority; - - lh = (struct l2cap_hdr *)skb_put(skb, L2CAP_HDR_SIZE); - lh->cid = cpu_to_le16(L2CAP_FC_6LOWPAN); - lh->len = cpu_to_le16(len); - - err = skbuff_copy(msg, len, count, conn->mtu, skb, dev); - if (unlikely(err < 0)) { - kfree_skb(skb); - BT_DBG("skbuff copy %d failed", err); - return ERR_PTR(err); + err = recv_pkt(skb, dev->netdev, chan); + if (err) { + BT_DBG("recv pkt %d", err); + err = -EAGAIN; } - return skb; -} - -static int conn_send(struct l2cap_conn *conn, - void *msg, size_t len, u32 priority, - struct net_device *dev) -{ - struct sk_buff *skb; - - skb = create_pdu(conn, msg, len, priority, dev); - if (IS_ERR(skb)) - return -EINVAL; - - BT_DBG("conn %p skb %p len %d priority %u", conn, skb, skb->len, - skb->priority); - - hci_send_acl(conn->hchan, skb, ACL_START); - - return 0; + return err; } static u8 get_addr_type_from_eui64(u8 byte) { - /* Is universal(0) or local(1) bit, */ - if (byte & 0x02) - return ADDR_LE_DEV_RANDOM; - - return ADDR_LE_DEV_PUBLIC; + /* Is universal(0) or local(1) bit */ + return ((byte & 0x02) ? BDADDR_LE_RANDOM : BDADDR_LE_PUBLIC); } static void copy_to_bdaddr(struct in6_addr *ip6_daddr, bdaddr_t *addr) @@ -475,7 +395,7 @@ static int header_create(struct sk_buff *skb, struct net_device *netdev, if (ipv6_addr_is_multicast(&hdr->daddr)) { memcpy(&lowpan_cb(skb)->addr, &hdr->daddr, sizeof(struct in6_addr)); - lowpan_cb(skb)->conn = NULL; + lowpan_cb(skb)->chan = NULL; } else { unsigned long flags; @@ -484,9 +404,8 @@ static int header_create(struct sk_buff *skb, struct net_device *netdev, */ convert_dest_bdaddr(&hdr->daddr, &addr, &addr_type); - BT_DBG("dest addr %pMR type %s IP %pI6c", &addr, - addr_type == ADDR_LE_DEV_PUBLIC ? "PUBLIC" : "RANDOM", - &hdr->daddr); + BT_DBG("dest addr %pMR type %d IP %pI6c", &addr, + addr_type, &hdr->daddr); read_lock_irqsave(&devices_lock, flags); peer = peer_lookup_ba(dev, &addr, addr_type); @@ -501,7 +420,7 @@ static int header_create(struct sk_buff *skb, struct net_device *netdev, memcpy(&lowpan_cb(skb)->addr, &hdr->daddr, sizeof(struct in6_addr)); - lowpan_cb(skb)->conn = peer->conn; + lowpan_cb(skb)->chan = peer->chan; } saddr = dev->netdev->dev_addr; @@ -510,14 +429,42 @@ static int header_create(struct sk_buff *skb, struct net_device *netdev, } /* Packet to BT LE device */ -static int send_pkt(struct l2cap_conn *conn, const void *saddr, - const void *daddr, struct sk_buff *skb, +static int send_pkt(struct l2cap_chan *chan, struct sk_buff *skb, struct net_device *netdev) { - raw_dump_table(__func__, "raw skb data dump before fragmentation", - skb->data, skb->len); + struct msghdr msg; + struct kvec iv; + int err; + + /* Remember the skb so that we can send EAGAIN to the caller if + * we run out of credits. + */ + chan->data = skb; + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = (struct iovec *) &iv; + msg.msg_iovlen = 1; + iv.iov_base = skb->data; + iv.iov_len = skb->len; + + err = l2cap_chan_send(chan, &msg, skb->len); + if (err > 0) { + netdev->stats.tx_bytes += err; + netdev->stats.tx_packets++; + return 0; + } + + if (!err) + err = lowpan_cb(skb)->status; + + if (err < 0) { + if (err == -EAGAIN) + netdev->stats.tx_dropped++; + else + netdev->stats.tx_errors++; + } - return conn_send(conn, skb->data, skb->len, 0, netdev); + return err; } static void send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev) @@ -540,8 +487,7 @@ static void send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev) list_for_each_entry_safe(pentry, ptmp, &dev->peers, list) { local_skb = skb_clone(skb, GFP_ATOMIC); - send_pkt(pentry->conn, netdev->dev_addr, - pentry->eui64_addr, local_skb, netdev); + send_pkt(pentry->chan, local_skb, netdev); kfree_skb(local_skb); } @@ -553,7 +499,6 @@ static void send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev) static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev) { int err = 0; - unsigned char *eui64_addr; struct lowpan_dev *dev; struct lowpan_peer *peer; bdaddr_t addr; @@ -568,21 +513,20 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev) unsigned long flags; convert_dest_bdaddr(&lowpan_cb(skb)->addr, &addr, &addr_type); - eui64_addr = lowpan_cb(skb)->addr.s6_addr + 8; dev = lowpan_dev(netdev); read_lock_irqsave(&devices_lock, flags); peer = peer_lookup_ba(dev, &addr, addr_type); read_unlock_irqrestore(&devices_lock, flags); - BT_DBG("xmit %s to %pMR type %s IP %pI6c peer %p", - netdev->name, &addr, - addr_type == ADDR_LE_DEV_PUBLIC ? "PUBLIC" : "RANDOM", + BT_DBG("xmit %s to %pMR type %d IP %pI6c peer %p", + netdev->name, &addr, addr_type, &lowpan_cb(skb)->addr, peer); - if (peer && peer->conn) - err = send_pkt(peer->conn, netdev->dev_addr, - eui64_addr, skb, netdev); + if (peer && peer->chan) + err = send_pkt(peer->chan, skb, netdev); + else + err = -ENOENT; } dev_kfree_skb(skb); @@ -634,7 +578,7 @@ static void set_addr(u8 *eui, u8 *addr, u8 addr_type) eui[7] = addr[0]; /* Universal/local bit set, BT 6lowpan draft ch. 3.2.1 */ - if (addr_type == ADDR_LE_DEV_PUBLIC) + if (addr_type == BDADDR_LE_PUBLIC) eui[0] &= ~0x02; else eui[0] |= 0x02; @@ -673,26 +617,64 @@ static bool is_bt_6lowpan(struct hci_conn *hcon) if (hcon->type != LE_LINK) return false; - return test_bit(HCI_CONN_6LOWPAN, &hcon->flags); + if (!psm_6lowpan) + return false; + + return true; } -static int add_peer_conn(struct l2cap_conn *conn, struct lowpan_dev *dev) +static struct l2cap_chan *chan_create(void) +{ + struct l2cap_chan *chan; + + chan = l2cap_chan_create(); + if (!chan) + return NULL; + + l2cap_chan_set_defaults(chan); + + chan->chan_type = L2CAP_CHAN_CONN_ORIENTED; + chan->mode = L2CAP_MODE_LE_FLOWCTL; + chan->omtu = 65535; + chan->imtu = chan->omtu; + + return chan; +} + +static struct l2cap_chan *chan_open(struct l2cap_chan *pchan) +{ + struct l2cap_chan *chan; + + chan = chan_create(); + if (!chan) + return NULL; + + chan->remote_mps = chan->omtu; + chan->mps = chan->omtu; + + chan->state = BT_CONNECTED; + + return chan; +} + +static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan, + struct lowpan_dev *dev) { struct lowpan_peer *peer; unsigned long flags; peer = kzalloc(sizeof(*peer), GFP_ATOMIC); if (!peer) - return -ENOMEM; + return NULL; - peer->conn = conn; + peer->chan = chan; memset(&peer->peer_addr, 0, sizeof(struct in6_addr)); /* RFC 2464 ch. 5 */ peer->peer_addr.s6_addr[0] = 0xFE; peer->peer_addr.s6_addr[1] = 0x80; - set_addr((u8 *)&peer->peer_addr.s6_addr + 8, conn->hcon->dst.b, - conn->hcon->dst_type); + set_addr((u8 *)&peer->peer_addr.s6_addr + 8, chan->dst.b, + chan->dst_type); memcpy(&peer->eui64_addr, (u8 *)&peer->peer_addr.s6_addr + 8, EUI64_ADDR_LEN); @@ -706,40 +688,24 @@ static int add_peer_conn(struct l2cap_conn *conn, struct lowpan_dev *dev) INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers); schedule_delayed_work(&dev->notify_peers, msecs_to_jiffies(100)); - return 0; + return peer->chan; } -/* This gets called when BT LE 6LoWPAN device is connected. We then - * create network device that acts as a proxy between BT LE device - * and kernel network stack. - */ -int bt_6lowpan_add_conn(struct l2cap_conn *conn) +static int setup_netdev(struct l2cap_chan *chan, struct lowpan_dev **dev) { - struct lowpan_peer *peer = NULL; - struct lowpan_dev *dev; struct net_device *netdev; int err = 0; unsigned long flags; - if (!is_bt_6lowpan(conn->hcon)) - return 0; - - peer = lookup_peer(conn); - if (peer) - return -EEXIST; - - dev = lookup_dev(conn); - if (dev) - return add_peer_conn(conn, dev); - - netdev = alloc_netdev(sizeof(*dev), IFACE_NAME_TEMPLATE, netdev_setup); + netdev = alloc_netdev(sizeof(struct lowpan_dev), IFACE_NAME_TEMPLATE, + netdev_setup); if (!netdev) return -ENOMEM; - set_dev_addr(netdev, &conn->hcon->src, conn->hcon->src_type); + set_dev_addr(netdev, &chan->src, chan->src_type); netdev->netdev_ops = &netdev_ops; - SET_NETDEV_DEV(netdev, &conn->hcon->dev); + SET_NETDEV_DEV(netdev, &chan->conn->hcon->dev); SET_NETDEV_DEVTYPE(netdev, &bt_type); err = register_netdev(netdev); @@ -749,28 +715,58 @@ int bt_6lowpan_add_conn(struct l2cap_conn *conn) goto out; } - BT_DBG("ifindex %d peer bdaddr %pMR my addr %pMR", - netdev->ifindex, &conn->hcon->dst, &conn->hcon->src); + BT_DBG("ifindex %d peer bdaddr %pMR type %d my addr %pMR type %d", + netdev->ifindex, &chan->dst, chan->dst_type, + &chan->src, chan->src_type); set_bit(__LINK_STATE_PRESENT, &netdev->state); - dev = netdev_priv(netdev); - dev->netdev = netdev; - dev->hdev = conn->hcon->hdev; - INIT_LIST_HEAD(&dev->peers); + *dev = netdev_priv(netdev); + (*dev)->netdev = netdev; + (*dev)->hdev = chan->conn->hcon->hdev; + INIT_LIST_HEAD(&(*dev)->peers); write_lock_irqsave(&devices_lock, flags); - INIT_LIST_HEAD(&dev->list); - list_add(&dev->list, &bt_6lowpan_devices); + INIT_LIST_HEAD(&(*dev)->list); + list_add(&(*dev)->list, &bt_6lowpan_devices); write_unlock_irqrestore(&devices_lock, flags); - ifup(netdev); - - return add_peer_conn(conn, dev); + return 0; out: return err; } +static inline void chan_ready_cb(struct l2cap_chan *chan) +{ + struct lowpan_dev *dev; + + dev = lookup_dev(chan->conn); + + BT_DBG("chan %p conn %p dev %p", chan, chan->conn, dev); + + if (!dev) { + if (setup_netdev(chan, &dev) < 0) { + l2cap_chan_del(chan, -ENOENT); + return; + } + } + + add_peer_chan(chan, dev); + ifup(dev->netdev); +} + +static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *chan) +{ + struct l2cap_chan *pchan; + + pchan = chan_open(chan); + pchan->ops = chan->ops; + + BT_DBG("chan %p pchan %p", chan, pchan); + + return pchan; +} + static void delete_netdev(struct work_struct *work) { struct lowpan_dev *entry = container_of(work, struct lowpan_dev, @@ -781,26 +777,43 @@ static void delete_netdev(struct work_struct *work) /* The entry pointer is deleted in device_event() */ } -int bt_6lowpan_del_conn(struct l2cap_conn *conn) +static void chan_close_cb(struct l2cap_chan *chan) { struct lowpan_dev *entry, *tmp; struct lowpan_dev *dev = NULL; struct lowpan_peer *peer; int err = -ENOENT; unsigned long flags; - bool last = false; + bool last = false, removed = true; - if (!conn || !is_bt_6lowpan(conn->hcon)) - return 0; + BT_DBG("chan %p conn %p", chan, chan->conn); + + if (chan->conn && chan->conn->hcon) { + if (!is_bt_6lowpan(chan->conn->hcon)) + return; + + /* If conn is set, then the netdev is also there and we should + * not remove it. + */ + removed = false; + } write_lock_irqsave(&devices_lock, flags); list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) { dev = lowpan_dev(entry->netdev); - peer = peer_lookup_conn(dev, conn); + peer = peer_lookup_chan(dev, chan); if (peer) { last = peer_del(dev, peer); err = 0; + + BT_DBG("dev %p removing %speer %p", dev, + last ? "last " : "1 ", peer); + BT_DBG("chan %p orig refcnt %d", chan, + atomic_read(&chan->kref.refcount)); + + l2cap_chan_put(chan); + kfree(peer); break; } } @@ -810,18 +823,363 @@ int bt_6lowpan_del_conn(struct l2cap_conn *conn) cancel_delayed_work_sync(&dev->notify_peers); - /* bt_6lowpan_del_conn() is called with hci dev lock held which - * means that we must delete the netdevice in worker thread. - */ - INIT_WORK(&entry->delete_netdev, delete_netdev); - schedule_work(&entry->delete_netdev); + if (!removed) { + INIT_WORK(&entry->delete_netdev, delete_netdev); + schedule_work(&entry->delete_netdev); + } } else { write_unlock_irqrestore(&devices_lock, flags); } + return; +} + +static void chan_state_change_cb(struct l2cap_chan *chan, int state, int err) +{ + BT_DBG("chan %p conn %p state %s err %d", chan, chan->conn, + state_to_string(state), err); +} + +static struct sk_buff *chan_alloc_skb_cb(struct l2cap_chan *chan, + unsigned long hdr_len, + unsigned long len, int nb) +{ + /* Note that we must allocate using GFP_ATOMIC here as + * this function is called originally from netdev hard xmit + * function in atomic context. + */ + return bt_skb_alloc(hdr_len + len, GFP_ATOMIC); +} + +static void chan_suspend_cb(struct l2cap_chan *chan) +{ + struct sk_buff *skb = chan->data; + + BT_DBG("chan %p conn %p skb %p", chan, chan->conn, skb); + + lowpan_cb(skb)->status = -EAGAIN; +} + +static void chan_resume_cb(struct l2cap_chan *chan) +{ + struct sk_buff *skb = chan->data; + + BT_DBG("chan %p conn %p skb %p", chan, chan->conn, skb); + + lowpan_cb(skb)->status = 0; +} + +static long chan_get_sndtimeo_cb(struct l2cap_chan *chan) +{ + return msecs_to_jiffies(1000); +} + +static const struct l2cap_ops bt_6lowpan_chan_ops = { + .name = "L2CAP 6LoWPAN channel", + .new_connection = chan_new_conn_cb, + .recv = chan_recv_cb, + .close = chan_close_cb, + .state_change = chan_state_change_cb, + .ready = chan_ready_cb, + .resume = chan_resume_cb, + .suspend = chan_suspend_cb, + .get_sndtimeo = chan_get_sndtimeo_cb, + .alloc_skb = chan_alloc_skb_cb, + .memcpy_fromiovec = l2cap_chan_no_memcpy_fromiovec, + + .teardown = l2cap_chan_no_teardown, + .defer = l2cap_chan_no_defer, + .set_shutdown = l2cap_chan_no_set_shutdown, +}; + +static inline __u8 bdaddr_type(__u8 type) +{ + if (type == ADDR_LE_DEV_PUBLIC) + return BDADDR_LE_PUBLIC; + else + return BDADDR_LE_RANDOM; +} + +static struct l2cap_chan *chan_get(void) +{ + struct l2cap_chan *pchan; + + pchan = chan_create(); + if (!pchan) + return NULL; + + pchan->ops = &bt_6lowpan_chan_ops; + + return pchan; +} + +static int bt_6lowpan_connect(bdaddr_t *addr, u8 dst_type) +{ + struct l2cap_chan *pchan; + int err; + + pchan = chan_get(); + if (!pchan) + return -EINVAL; + + err = l2cap_chan_connect(pchan, cpu_to_le16(psm_6lowpan), 0, + addr, dst_type); + + BT_DBG("chan %p err %d", pchan, err); + if (err < 0) + l2cap_chan_put(pchan); + return err; } +static int bt_6lowpan_disconnect(struct l2cap_conn *conn, u8 dst_type) +{ + struct lowpan_peer *peer; + + BT_DBG("conn %p dst type %d", conn, dst_type); + + peer = lookup_peer(conn); + if (!peer) + return -ENOENT; + + BT_DBG("peer %p chan %p", peer, peer->chan); + + l2cap_chan_close(peer->chan, ENOENT); + + return 0; +} + +static struct l2cap_chan *bt_6lowpan_listen(void) +{ + bdaddr_t *addr = BDADDR_ANY; + struct l2cap_chan *pchan; + int err; + + if (psm_6lowpan == 0) + return NULL; + + pchan = chan_get(); + if (!pchan) + return NULL; + + pchan->state = BT_LISTEN; + pchan->src_type = BDADDR_LE_PUBLIC; + + BT_DBG("psm 0x%04x chan %p src type %d", psm_6lowpan, pchan, + pchan->src_type); + + err = l2cap_add_psm(pchan, addr, cpu_to_le16(psm_6lowpan)); + if (err) { + l2cap_chan_put(pchan); + BT_ERR("psm cannot be added err %d", err); + return NULL; + } + + return pchan; +} + +static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type, + struct l2cap_conn **conn) +{ + struct hci_conn *hcon; + struct hci_dev *hdev; + bdaddr_t *src = BDADDR_ANY; + int n; + + n = sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %hhu", + &addr->b[5], &addr->b[4], &addr->b[3], + &addr->b[2], &addr->b[1], &addr->b[0], + addr_type); + + if (n < 7) + return -EINVAL; + + hdev = hci_get_route(addr, src); + if (!hdev) + return -ENOENT; + + hci_dev_lock(hdev); + hcon = hci_conn_hash_lookup_ba(hdev, LE_LINK, addr); + hci_dev_unlock(hdev); + + if (!hcon) + return -ENOENT; + + *conn = (struct l2cap_conn *)hcon->l2cap_data; + + BT_DBG("conn %p dst %pMR type %d", *conn, &hcon->dst, hcon->dst_type); + + return 0; +} + +static void disconnect_all_peers(void) +{ + struct lowpan_dev *entry, *tmp_dev; + struct lowpan_peer *peer, *tmp_peer, *new_peer; + struct list_head peers; + unsigned long flags; + + INIT_LIST_HEAD(&peers); + + /* We make a separate list of peers as the close_cb() will + * modify the device peers list so it is better not to mess + * with the same list at the same time. + */ + + read_lock_irqsave(&devices_lock, flags); + + list_for_each_entry_safe(entry, tmp_dev, &bt_6lowpan_devices, list) { + list_for_each_entry_safe(peer, tmp_peer, &entry->peers, list) { + new_peer = kmalloc(sizeof(*new_peer), GFP_ATOMIC); + if (!new_peer) + break; + + new_peer->chan = peer->chan; + INIT_LIST_HEAD(&new_peer->list); + + list_add(&new_peer->list, &peers); + } + } + + read_unlock_irqrestore(&devices_lock, flags); + + list_for_each_entry_safe(peer, tmp_peer, &peers, list) { + l2cap_chan_close(peer->chan, ENOENT); + kfree(peer); + } +} + +static int lowpan_psm_set(void *data, u64 val) +{ + u16 psm; + + psm = val; + if (psm == 0 || psm_6lowpan != psm) + /* Disconnect existing connections if 6lowpan is + * disabled (psm = 0), or if psm changes. + */ + disconnect_all_peers(); + + psm_6lowpan = psm; + + if (listen_chan) { + l2cap_chan_close(listen_chan, 0); + l2cap_chan_put(listen_chan); + } + + listen_chan = bt_6lowpan_listen(); + + return 0; +} + +static int lowpan_psm_get(void *data, u64 *val) +{ + *val = psm_6lowpan; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(lowpan_psm_fops, lowpan_psm_get, + lowpan_psm_set, "%llu\n"); + +static ssize_t lowpan_control_write(struct file *fp, + const char __user *user_buffer, + size_t count, + loff_t *position) +{ + char buf[32]; + size_t buf_size = min(count, sizeof(buf) - 1); + int ret; + bdaddr_t addr; + u8 addr_type; + struct l2cap_conn *conn = NULL; + + if (copy_from_user(buf, user_buffer, buf_size)) + return -EFAULT; + + buf[buf_size] = '\0'; + + if (memcmp(buf, "connect ", 8) == 0) { + ret = get_l2cap_conn(&buf[8], &addr, &addr_type, &conn); + if (ret == -EINVAL) + return ret; + + if (listen_chan) { + l2cap_chan_close(listen_chan, 0); + l2cap_chan_put(listen_chan); + listen_chan = NULL; + } + + if (conn) { + struct lowpan_peer *peer; + + if (!is_bt_6lowpan(conn->hcon)) + return -EINVAL; + + peer = lookup_peer(conn); + if (peer) { + BT_DBG("6LoWPAN connection already exists"); + return -EALREADY; + } + + BT_DBG("conn %p dst %pMR type %d user %d", conn, + &conn->hcon->dst, conn->hcon->dst_type, + addr_type); + } + + ret = bt_6lowpan_connect(&addr, addr_type); + if (ret < 0) + return ret; + + return count; + } + + if (memcmp(buf, "disconnect ", 11) == 0) { + ret = get_l2cap_conn(&buf[11], &addr, &addr_type, &conn); + if (ret < 0) + return ret; + + ret = bt_6lowpan_disconnect(conn, addr_type); + if (ret < 0) + return ret; + + return count; + } + + return count; +} + +static int lowpan_control_show(struct seq_file *f, void *ptr) +{ + struct lowpan_dev *entry, *tmp_dev; + struct lowpan_peer *peer, *tmp_peer; + unsigned long flags; + + read_lock_irqsave(&devices_lock, flags); + + list_for_each_entry_safe(entry, tmp_dev, &bt_6lowpan_devices, list) { + list_for_each_entry_safe(peer, tmp_peer, &entry->peers, list) + seq_printf(f, "%pMR (type %u)\n", + &peer->chan->dst, peer->chan->dst_type); + } + + read_unlock_irqrestore(&devices_lock, flags); + + return 0; +} + +static int lowpan_control_open(struct inode *inode, struct file *file) +{ + return single_open(file, lowpan_control_show, inode->i_private); +} + +static const struct file_operations lowpan_control_fops = { + .open = lowpan_control_open, + .read = seq_read, + .write = lowpan_control_write, + .llseek = seq_lseek, + .release = single_release, +}; + static int device_event(struct notifier_block *unused, unsigned long event, void *ptr) { @@ -856,10 +1214,25 @@ static struct notifier_block bt_6lowpan_dev_notifier = { int bt_6lowpan_init(void) { + lowpan_psm_debugfs = debugfs_create_file("6lowpan_psm", 0644, + bt_debugfs, NULL, + &lowpan_psm_fops); + lowpan_control_debugfs = debugfs_create_file("6lowpan_control", 0644, + bt_debugfs, NULL, + &lowpan_control_fops); + return register_netdevice_notifier(&bt_6lowpan_dev_notifier); } -void bt_6lowpan_cleanup(void) +void bt_6lowpan_exit(void) { + debugfs_remove(lowpan_psm_debugfs); + debugfs_remove(lowpan_control_debugfs); + + if (listen_chan) { + l2cap_chan_close(listen_chan, 0); + l2cap_chan_put(listen_chan); + } + unregister_netdevice_notifier(&bt_6lowpan_dev_notifier); } diff --git a/net/bluetooth/6lowpan.h b/net/bluetooth/6lowpan.h deleted file mode 100644 index 5d281f1eaf55..000000000000 --- a/net/bluetooth/6lowpan.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - Copyright (c) 2013 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 __6LOWPAN_H -#define __6LOWPAN_H - -#include -#include -#include - -#if IS_ENABLED(CONFIG_BT_6LOWPAN) -int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb); -int bt_6lowpan_add_conn(struct l2cap_conn *conn); -int bt_6lowpan_del_conn(struct l2cap_conn *conn); -int bt_6lowpan_init(void); -void bt_6lowpan_cleanup(void); -#else -static int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb) -{ - return -EOPNOTSUPP; -} -static int bt_6lowpan_add_conn(struct l2cap_conn *conn) -{ - return -EOPNOTSUPP; -} -int bt_6lowpan_del_conn(struct l2cap_conn *conn) -{ - return -EOPNOTSUPP; -} -static int bt_6lowpan_init(void) -{ - return -EOPNOTSUPP; -} -static void bt_6lowpan_cleanup(void) { } -#endif - -#endif /* __6LOWPAN_H */ diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index ddbb084250be..bbfc5455acec 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -928,49 +928,6 @@ static int adv_channel_map_get(void *data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(adv_channel_map_fops, adv_channel_map_get, adv_channel_map_set, "%llu\n"); -static ssize_t lowpan_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct hci_dev *hdev = file->private_data; - char buf[3]; - - buf[0] = test_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags) ? 'Y' : 'N'; - buf[1] = '\n'; - buf[2] = '\0'; - return simple_read_from_buffer(user_buf, count, ppos, buf, 2); -} - -static ssize_t lowpan_write(struct file *fp, const char __user *user_buffer, - size_t count, loff_t *position) -{ - struct hci_dev *hdev = fp->private_data; - bool enable; - char buf[32]; - size_t buf_size = min(count, (sizeof(buf)-1)); - - if (copy_from_user(buf, user_buffer, buf_size)) - return -EFAULT; - - buf[buf_size] = '\0'; - - if (strtobool(buf, &enable) < 0) - return -EINVAL; - - if (enable == test_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags)) - return -EALREADY; - - change_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags); - - return count; -} - -static const struct file_operations lowpan_debugfs_fops = { - .open = simple_open, - .read = lowpan_read, - .write = lowpan_write, - .llseek = default_llseek, -}; - static int le_auto_conn_show(struct seq_file *sf, void *ptr) { struct hci_dev *hdev = sf->private; @@ -1881,8 +1838,6 @@ static int __hci_init(struct hci_dev *hdev) hdev, &conn_max_interval_fops); debugfs_create_file("adv_channel_map", 0644, hdev->debugfs, hdev, &adv_channel_map_fops); - debugfs_create_file("6lowpan", 0644, hdev->debugfs, hdev, - &lowpan_debugfs_fops); debugfs_create_file("le_auto_conn", 0644, hdev->debugfs, hdev, &le_auto_conn_fops); debugfs_create_u16("discov_interleaved_timeout", 0644, diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 15a0a65bf447..e5b430113cfb 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4056,9 +4056,6 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) conn->handle = __le16_to_cpu(ev->handle); conn->state = BT_CONNECTED; - if (test_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags)) - set_bit(HCI_CONN_6LOWPAN, &conn->flags); - hci_conn_add_sysfs(conn); hci_proto_connect_cfm(conn, ev->status); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index d0a5fde61a07..cbf6b9d1c404 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -40,7 +40,6 @@ #include "smp.h" #include "a2mp.h" #include "amp.h" -#include "6lowpan.h" #define LE_FLOWCTL_MAX_CREDITS 65535 @@ -205,6 +204,7 @@ done: write_unlock(&chan_list_lock); return err; } +EXPORT_SYMBOL_GPL(l2cap_add_psm); int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid) { @@ -437,6 +437,7 @@ struct l2cap_chan *l2cap_chan_create(void) return chan; } +EXPORT_SYMBOL_GPL(l2cap_chan_create); static void l2cap_chan_destroy(struct kref *kref) { @@ -464,6 +465,7 @@ void l2cap_chan_put(struct l2cap_chan *c) kref_put(&c->kref, l2cap_chan_destroy); } +EXPORT_SYMBOL_GPL(l2cap_chan_put); void l2cap_chan_set_defaults(struct l2cap_chan *chan) { @@ -482,6 +484,7 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan) set_bit(FLAG_FORCE_ACTIVE, &chan->flags); } +EXPORT_SYMBOL_GPL(l2cap_chan_set_defaults); static void l2cap_le_flowctl_init(struct l2cap_chan *chan) { @@ -614,6 +617,7 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err) return; } +EXPORT_SYMBOL_GPL(l2cap_chan_del); void l2cap_conn_update_id_addr(struct hci_conn *hcon) { @@ -717,6 +721,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) break; } } +EXPORT_SYMBOL(l2cap_chan_close); static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan) { @@ -1460,8 +1465,6 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) BT_DBG(""); - bt_6lowpan_add_conn(conn); - /* Check if we have socket listening on cid */ pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_ATT, &hcon->src, &hcon->dst); @@ -2555,6 +2558,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len) return err; } +EXPORT_SYMBOL_GPL(l2cap_chan_send); static void l2cap_send_srej(struct l2cap_chan *chan, u16 txseq) { @@ -6933,10 +6937,6 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) l2cap_conn_del(conn->hcon, EACCES); break; - case L2CAP_FC_6LOWPAN: - bt_6lowpan_recv(conn, skb); - break; - default: l2cap_data_channel(conn, cid, skb); break; @@ -7183,6 +7183,7 @@ done: hci_dev_put(hdev); return err; } +EXPORT_SYMBOL_GPL(l2cap_chan_connect); /* ---- L2CAP interface with lower layer (HCI) ---- */ @@ -7245,8 +7246,6 @@ void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason) { BT_DBG("hcon %p reason %d", hcon, reason); - bt_6lowpan_del_conn(hcon->l2cap_data); - l2cap_conn_del(hcon, bt_to_errno(reason)); } @@ -7529,14 +7528,11 @@ int __init l2cap_init(void) debugfs_create_u16("l2cap_le_default_mps", 0644, bt_debugfs, &le_default_mps); - bt_6lowpan_init(); - return 0; } void l2cap_exit(void) { - bt_6lowpan_cleanup(); debugfs_remove(l2cap_debugfs); l2cap_cleanup_sockets(); } -- cgit v1.2.3 From e04fde60efabe27afdbe041e3e5a09ec752ec9d2 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 23 Jun 2014 11:40:04 +0200 Subject: Bluetooth: Store current LE connection parameters in hci_conn struct The LE connection parameters are needed later on to be able to decide if it is required to trigger connection update procedures. So when the connection has been established successfully, store the current used parameters in hci_conn struct. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 3 +++ net/bluetooth/hci_event.c | 4 ++++ 2 files changed, 7 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index c81de0d366df..cd73a82cc713 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -381,6 +381,9 @@ struct hci_conn { __u16 setting; __u16 le_conn_min_interval; __u16 le_conn_max_interval; + __u16 le_conn_interval; + __u16 le_conn_latency; + __u16 le_supv_timeout; __s8 rssi; __s8 tx_power; __s8 max_tx_power; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index e5b430113cfb..3d4741d789d3 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4056,6 +4056,10 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) conn->handle = __le16_to_cpu(ev->handle); conn->state = BT_CONNECTED; + conn->le_conn_interval = le16_to_cpu(ev->interval); + conn->le_conn_latency = le16_to_cpu(ev->latency); + conn->le_supv_timeout = le16_to_cpu(ev->supervision_timeout); + hci_conn_add_sysfs(conn); hci_proto_connect_cfm(conn, ev->status); -- cgit v1.2.3 From 1855d92dce0dc0ed81a78eacae710529600513f4 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 23 Jun 2014 11:40:05 +0200 Subject: Bluetooth: Track LE connection parameter update event When the LE controller changes its connection parameters, it will send a connection parameter update event. Make sure that the new set of parameters are stored in hci_conn struct and thus will properly update the previous values retrieved from the connection complete event. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 9 +++++++++ net/bluetooth/hci_event.c | 27 +++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 3f3a3f1399fb..6b8371d73d3d 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1666,6 +1666,15 @@ struct hci_ev_le_conn_complete { __u8 clk_accurancy; } __packed; +#define HCI_EV_LE_CONN_UPDATE_COMPLETE 0x03 +struct hci_ev_le_conn_update_complete { + __u8 status; + __le16 handle; + __le16 interval; + __le16 latency; + __le16 supervision_timeout; +} __packed; + #define HCI_EV_LE_LTK_REQ 0x05 struct hci_ev_le_ltk_req { __le16 handle; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 3d4741d789d3..e239435312dc 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4070,6 +4070,29 @@ unlock: hci_dev_unlock(hdev); } +static void hci_le_conn_update_complete_evt(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_ev_le_conn_update_complete *ev = (void *) skb->data; + struct hci_conn *conn; + + BT_DBG("%s status 0x%2.2x", hdev->name, ev->status); + + if (ev->status) + return; + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); + if (conn) { + conn->le_conn_interval = le16_to_cpu(ev->interval); + conn->le_conn_latency = le16_to_cpu(ev->latency); + conn->le_supv_timeout = le16_to_cpu(ev->supervision_timeout); + } + + hci_dev_unlock(hdev); +} + /* This function requires the caller holds hdev->lock */ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) @@ -4269,6 +4292,10 @@ static void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_le_conn_complete_evt(hdev, skb); break; + case HCI_EV_LE_CONN_UPDATE_COMPLETE: + hci_le_conn_update_complete_evt(hdev, skb); + break; + case HCI_EV_LE_ADVERTISING_REPORT: hci_le_adv_report_evt(hdev, skb); break; -- cgit v1.2.3 From 567fa2aa3dfad9848c25a226927a4ca5f94229ac Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 24 Jun 2014 13:15:48 +0300 Subject: Bluetooth: Update hci_add_link_key() to return pointer to key By returning the added (or updated) key we pave the way for further refactoring (in subsequent patches) that allows moving the mgmt event sending out from this function (and thereby removal of the awkward new_key parameter). Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 5 +++-- net/bluetooth/hci_core.c | 11 ++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index cd73a82cc713..7f81791a865d 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -859,8 +859,9 @@ void hci_uuids_clear(struct hci_dev *hdev); void hci_link_keys_clear(struct hci_dev *hdev); struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); -int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, - bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len); +struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, + int new_key, bdaddr_t *bdaddr, u8 *val, + u8 type, u8 pin_len); struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand, bool master); struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index bbfc5455acec..ee42788aed2c 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3001,8 +3001,9 @@ struct smp_irk *hci_find_irk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, return NULL; } -int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, - bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len) +struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, + int new_key, bdaddr_t *bdaddr, u8 *val, + u8 type, u8 pin_len) { struct link_key *key, *old_key; u8 old_key_type; @@ -3016,7 +3017,7 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, old_key_type = conn ? conn->key_type : 0xff; key = kzalloc(sizeof(*key), GFP_KERNEL); if (!key) - return -ENOMEM; + return NULL; list_add(&key->list, &hdev->link_keys); } @@ -3042,7 +3043,7 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, key->type = type; if (!new_key) - return 0; + return key; persistent = hci_persistent_key(hdev, conn, type, old_key_type); @@ -3051,7 +3052,7 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, if (conn) conn->flush_key = !persistent; - return 0; + return key; } struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, -- cgit v1.2.3 From 7652ff6aeaf0eeaec1f2e7e2f3ce0e588447dbd1 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 24 Jun 2014 13:15:49 +0300 Subject: Bluetooth: Move mgmt event sending out from hci_add_link_key() There are two callers of hci_add_link_key(). The first one is the HCI Link Key Notification event and the second one the mgmt code that receives a list of link keys from user space. Previously we've had the hci_add_link_key() function being responsible for also emitting a mgmt signal but for the latter use case this should not happen. Because of this a rather awkward new_key paramter has been passed to the function. This patch moves the mgmt event sending out from the hci_add_link_key() function, thereby making the code a bit more understandable. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 4 ++-- net/bluetooth/hci_core.c | 17 +++++------------ net/bluetooth/hci_event.c | 18 +++++++++++++++--- net/bluetooth/mgmt.c | 4 ++-- 4 files changed, 24 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 7f81791a865d..888911d205dc 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -860,8 +860,8 @@ void hci_uuids_clear(struct hci_dev *hdev); void hci_link_keys_clear(struct hci_dev *hdev); struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, - int new_key, bdaddr_t *bdaddr, u8 *val, - u8 type, u8 pin_len); + bdaddr_t *bdaddr, u8 *val, u8 type, + u8 pin_len, bool *persistent); struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand, bool master); struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index ee42788aed2c..159783e746c0 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3002,12 +3002,11 @@ struct smp_irk *hci_find_irk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, } struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, - int new_key, bdaddr_t *bdaddr, u8 *val, - u8 type, u8 pin_len) + bdaddr_t *bdaddr, u8 *val, u8 type, + u8 pin_len, bool *persistent) { struct link_key *key, *old_key; u8 old_key_type; - bool persistent; old_key = hci_find_link_key(hdev, bdaddr); if (old_key) { @@ -3042,15 +3041,9 @@ struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, else key->type = type; - if (!new_key) - return key; - - persistent = hci_persistent_key(hdev, conn, type, old_key_type); - - mgmt_new_link_key(hdev, key, persistent); - - if (conn) - conn->flush_key = !persistent; + if (persistent) + *persistent = hci_persistent_key(hdev, conn, type, + old_key_type); return key; } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index e1e1ecb4db10..47bd48a597bd 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3111,6 +3111,8 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_link_key_notify *ev = (void *) skb->data; struct hci_conn *conn; + struct link_key *key; + bool persistent; u8 pin_len = 0; BT_DBG("%s", hdev->name); @@ -3129,10 +3131,20 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_conn_drop(conn); } - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - hci_add_link_key(hdev, conn, 1, &ev->bdaddr, ev->link_key, - ev->key_type, pin_len); + if (!test_bit(HCI_MGMT, &hdev->dev_flags)) + goto unlock; + + key = hci_add_link_key(hdev, conn, &ev->bdaddr, ev->link_key, + ev->key_type, pin_len, &persistent); + if (!key) + goto unlock; + + mgmt_new_link_key(hdev, key, persistent); + if (conn) + conn->flush_key = !persistent; + +unlock: hci_dev_unlock(hdev); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index e1651c3fc676..bb5b04191253 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2424,8 +2424,8 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data, for (i = 0; i < key_count; i++) { struct mgmt_link_key_info *key = &cp->keys[i]; - hci_add_link_key(hdev, NULL, 0, &key->addr.bdaddr, key->val, - key->type, key->pin_len); + hci_add_link_key(hdev, NULL, &key->addr.bdaddr, key->val, + key->type, key->pin_len, NULL); } cmd_complete(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, 0, NULL, 0); -- cgit v1.2.3 From 0663b297f1953e5d84928722e44f71272f5ff058 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 24 Jun 2014 13:15:50 +0300 Subject: Bluetooth: Rename HCI_DEBUG_KEYS to HCI_KEEP_DEBUG_KEYS We're planning to add a flag to actively use debug keys in addition to simply just accepting them, which makes the current generically named DEBUG_KEYS flag a bit confusing. Since the flag in practice affects whether the kernel keeps debug keys around or not rename it to HCI_KEEP_DEBUG_KEYS. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 2 +- net/bluetooth/hci_event.c | 2 +- net/bluetooth/mgmt.c | 14 +++++++++----- 3 files changed, 11 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 6b8371d73d3d..ffb489e3946d 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -124,7 +124,7 @@ enum { HCI_MGMT, HCI_PAIRABLE, HCI_SERVICE_CACHE, - HCI_DEBUG_KEYS, + HCI_KEEP_DEBUG_KEYS, HCI_UNREGISTER, HCI_USER_CHANNEL, diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 47bd48a597bd..b0fdeec2f2ca 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3066,7 +3066,7 @@ static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s found key type %u for %pMR", hdev->name, key->type, &ev->bdaddr); - if (!test_bit(HCI_DEBUG_KEYS, &hdev->dev_flags) && + if (!test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags) && key->type == HCI_LK_DEBUG_COMBINATION) { BT_DBG("%s ignoring debug key", hdev->name); goto not_found; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index bb5b04191253..634b44ddc9f9 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -440,7 +440,7 @@ static u32 get_current_settings(struct hci_dev *hdev) if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) settings |= MGMT_SETTING_SECURE_CONN; - if (test_bit(HCI_DEBUG_KEYS, &hdev->dev_flags)) + if (test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags)) settings |= MGMT_SETTING_DEBUG_KEYS; if (test_bit(HCI_PRIVACY, &hdev->dev_flags)) @@ -2414,9 +2414,11 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data, hci_link_keys_clear(hdev); if (cp->debug_keys) - changed = !test_and_set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags); + changed = !test_and_set_bit(HCI_KEEP_DEBUG_KEYS, + &hdev->dev_flags); else - changed = test_and_clear_bit(HCI_DEBUG_KEYS, &hdev->dev_flags); + changed = test_and_clear_bit(HCI_KEEP_DEBUG_KEYS, + &hdev->dev_flags); if (changed) new_settings(hdev, NULL); @@ -4349,9 +4351,11 @@ static int set_debug_keys(struct sock *sk, struct hci_dev *hdev, hci_dev_lock(hdev); if (cp->val) - changed = !test_and_set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags); + changed = !test_and_set_bit(HCI_KEEP_DEBUG_KEYS, + &hdev->dev_flags); else - changed = test_and_clear_bit(HCI_DEBUG_KEYS, &hdev->dev_flags); + changed = test_and_clear_bit(HCI_KEEP_DEBUG_KEYS, + &hdev->dev_flags); err = send_settings_rsp(sk, MGMT_OP_SET_DEBUG_KEYS, hdev); if (err < 0) -- cgit v1.2.3 From af6a9c321384400efab1726636e8189737bbbd09 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 24 Jun 2014 13:15:53 +0300 Subject: Bluetooth: Convert hcon->flush_key to a proper flag There's no point in having boolean variables in the hci_conn struct since it already has a flags member. This patch converts the flush_key member into a proper flag. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_event.c | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 888911d205dc..4f70605e0399 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -394,7 +394,6 @@ struct hci_conn { __u8 remote_cap; __u8 remote_auth; __u8 remote_id; - bool flush_key; unsigned int sent; @@ -524,6 +523,7 @@ enum { HCI_CONN_AES_CCM, HCI_CONN_POWER_SAVE, HCI_CONN_REMOTE_OOB, + HCI_CONN_FLUSH_KEY, }; static inline bool hci_conn_ssp_enabled(struct hci_conn *conn) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index c92c5a020fe7..90cba6a8293b 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2159,7 +2159,8 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) mgmt_device_disconnected(hdev, &conn->dst, conn->type, conn->dst_type, reason, mgmt_connected); - if (conn->type == ACL_LINK && conn->flush_key) + if (conn->type == ACL_LINK && + test_bit(HCI_CONN_FLUSH_KEY, &conn->flags)) hci_remove_link_key(hdev, &conn->dst); params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); @@ -3145,7 +3146,10 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb) list_del(&key->list); kfree(key); } else if (conn) { - conn->flush_key = !persistent; + if (persistent) + clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags); + else + set_bit(HCI_CONN_FLUSH_KEY, &conn->flags); } unlock: -- cgit v1.2.3 From 3769972badcd542913c460ca2834312cdff9f16c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 24 Jun 2014 14:00:27 +0300 Subject: Bluetooth: Add a new HCI_USE_DEBUG_KEYS flag To pave the way for actively using debug keys for pairing this patch adds a new HCI_USE_DEBUG_KEYS flag for the purpose. When the flag is set we issue a HCI_Write_SSP_Debug mode whenever HCI_Write_SSP_Mode(0x01) has been issued as well as before issuing a HCI_Write_SSP_Mode(0x00) command. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 1 + net/bluetooth/mgmt.c | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index ffb489e3946d..cc2e88dd20ea 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -125,6 +125,7 @@ enum { HCI_PAIRABLE, HCI_SERVICE_CACHE, HCI_KEEP_DEBUG_KEYS, + HCI_USE_DEBUG_KEYS, HCI_UNREGISTER, HCI_USER_CHANNEL, diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 747746b0d2c4..69afbb2df133 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1877,6 +1877,10 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) goto failed; } + if (!cp->val && test_bit(HCI_USE_DEBUG_KEYS, &hdev->dev_flags)) + hci_send_cmd(hdev, HCI_OP_WRITE_SSP_DEBUG_MODE, + sizeof(cp->val), &cp->val); + err = hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, 1, &cp->val); if (err < 0) { mgmt_pending_remove(cmd); @@ -5784,10 +5788,14 @@ void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) hci_req_init(&req, hdev); - if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) + if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { + if (test_bit(HCI_USE_DEBUG_KEYS, &hdev->dev_flags)) + hci_req_add(&req, HCI_OP_WRITE_SSP_DEBUG_MODE, + sizeof(enable), &enable); update_eir(&req); - else + } else { clear_eir(&req); + } hci_req_run(&req, NULL); } -- cgit v1.2.3 From 4dae27983eaaee15c6867561eb2c8d7b2d28d6cc Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 24 Jun 2014 17:03:50 +0300 Subject: Bluetooth: Convert hci_conn->link_mode into flags Since the link_mode member of the hci_conn struct is a bit field and we already have a flags member as well it makes sense to merge these two together. This patch moves all used link_mode bits into corresponding flags. To keep backwards compatibility with user space we still need to provide a get_link_mode() helper function for the ioctl's that expect a link_mode style value. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 10 +++++++--- net/bluetooth/hci_conn.c | 43 ++++++++++++++++++++++++++++++---------- net/bluetooth/hci_event.c | 28 +++++++++++++------------- net/bluetooth/l2cap_core.c | 4 ++-- net/bluetooth/smp.c | 12 +++++------ 5 files changed, 62 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 4f70605e0399..0b0597d81827 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -367,7 +367,6 @@ struct hci_conn { __u8 features[HCI_MAX_PAGES][8]; __u16 pkt_type; __u16 link_policy; - __u32 link_mode; __u8 key_type; __u8 auth_type; __u8 sec_level; @@ -524,6 +523,11 @@ enum { HCI_CONN_POWER_SAVE, HCI_CONN_REMOTE_OOB, HCI_CONN_FLUSH_KEY, + HCI_CONN_MASTER, + HCI_CONN_ENCRYPT, + HCI_CONN_AUTH, + HCI_CONN_SECURE, + HCI_CONN_FIPS, }; static inline bool hci_conn_ssp_enabled(struct hci_conn *conn) @@ -1025,7 +1029,7 @@ static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status) if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) return; - encrypt = (conn->link_mode & HCI_LM_ENCRYPT) ? 0x01 : 0x00; + encrypt = test_bit(HCI_CONN_ENCRYPT, &conn->flags) ? 0x01 : 0x00; l2cap_security_cfm(conn, status, encrypt); if (conn->security_cfm_cb) @@ -1066,7 +1070,7 @@ static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status) if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) return; - encrypt = (conn->link_mode & HCI_LM_ENCRYPT) ? 0x01 : 0x00; + encrypt = test_bit(HCI_CONN_ENCRYPT, &conn->flags) ? 0x01 : 0x00; read_lock(&hci_cb_list_lock); list_for_each_entry(cb, &hci_cb_list, list) { diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index a7a27bc2c0b1..626160c37103 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -67,7 +67,7 @@ static void hci_acl_create_connection(struct hci_conn *conn) conn->state = BT_CONNECT; conn->out = true; - conn->link_mode = HCI_LM_MASTER; + set_bit(HCI_CONN_MASTER, &conn->flags); conn->attempt++; @@ -743,7 +743,7 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, } conn->out = true; - conn->link_mode |= HCI_LM_MASTER; + set_bit(HCI_CONN_MASTER, &conn->flags); params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); if (params) { @@ -865,7 +865,8 @@ int hci_conn_check_link_mode(struct hci_conn *conn) return 0; } - if (hci_conn_ssp_enabled(conn) && !(conn->link_mode & HCI_LM_ENCRYPT)) + if (hci_conn_ssp_enabled(conn) && + !test_bit(HCI_CONN_ENCRYPT, &conn->flags)) return 0; return 1; @@ -881,7 +882,7 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) if (sec_level > conn->sec_level) conn->pending_sec_level = sec_level; - else if (conn->link_mode & HCI_LM_AUTH) + else if (test_bit(HCI_CONN_AUTH, &conn->flags)) return 1; /* Make sure we preserve an existing MITM requirement*/ @@ -899,7 +900,7 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) /* If we're already encrypted set the REAUTH_PEND flag, * otherwise set the ENCRYPT_PEND. */ - if (conn->link_mode & HCI_LM_ENCRYPT) + if (test_bit(HCI_CONN_ENCRYPT, &conn->flags)) set_bit(HCI_CONN_REAUTH_PEND, &conn->flags); else set_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags); @@ -940,7 +941,7 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) return 1; /* For other security levels we need the link key. */ - if (!(conn->link_mode & HCI_LM_AUTH)) + if (!test_bit(HCI_CONN_AUTH, &conn->flags)) goto auth; /* An authenticated FIPS approved combination key has sufficient @@ -980,7 +981,7 @@ auth: return 0; encrypt: - if (conn->link_mode & HCI_LM_ENCRYPT) + if (test_bit(HCI_CONN_ENCRYPT, &conn->flags)) return 1; hci_conn_encrypt(conn); @@ -1027,7 +1028,7 @@ int hci_conn_switch_role(struct hci_conn *conn, __u8 role) { BT_DBG("hcon %p", conn); - if (!role && conn->link_mode & HCI_LM_MASTER) + if (!role && test_bit(HCI_CONN_MASTER, &conn->flags)) return 1; if (!test_and_set_bit(HCI_CONN_RSWITCH_PEND, &conn->flags)) { @@ -1101,6 +1102,28 @@ void hci_conn_check_pending(struct hci_dev *hdev) hci_dev_unlock(hdev); } +static u32 get_link_mode(struct hci_conn *conn) +{ + u32 link_mode = 0; + + if (test_bit(HCI_CONN_MASTER, &conn->flags)) + link_mode |= HCI_LM_MASTER; + + if (test_bit(HCI_CONN_ENCRYPT, &conn->flags)) + link_mode |= HCI_LM_ENCRYPT; + + if (test_bit(HCI_CONN_AUTH, &conn->flags)) + link_mode |= HCI_LM_AUTH; + + if (test_bit(HCI_CONN_SECURE, &conn->flags)) + link_mode |= HCI_LM_SECURE; + + if (test_bit(HCI_CONN_FIPS, &conn->flags)) + link_mode |= HCI_LM_FIPS; + + return link_mode; +} + int hci_get_conn_list(void __user *arg) { struct hci_conn *c; @@ -1136,7 +1159,7 @@ int hci_get_conn_list(void __user *arg) (ci + n)->type = c->type; (ci + n)->out = c->out; (ci + n)->state = c->state; - (ci + n)->link_mode = c->link_mode; + (ci + n)->link_mode = get_link_mode(c); if (++n >= req.conn_num) break; } @@ -1172,7 +1195,7 @@ int hci_get_conn_info(struct hci_dev *hdev, void __user *arg) ci.type = conn->type; ci.out = conn->out; ci.state = conn->state; - ci.link_mode = conn->link_mode; + ci.link_mode = get_link_mode(conn); } hci_dev_unlock(hdev); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 90cba6a8293b..7a23324eac39 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -103,9 +103,9 @@ static void hci_cc_role_discovery(struct hci_dev *hdev, struct sk_buff *skb) conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle)); if (conn) { if (rp->role) - conn->link_mode &= ~HCI_LM_MASTER; + clear_bit(HCI_CONN_MASTER, &conn->flags); else - conn->link_mode |= HCI_LM_MASTER; + set_bit(HCI_CONN_MASTER, &conn->flags); } hci_dev_unlock(hdev); @@ -1346,7 +1346,7 @@ static void hci_cs_create_conn(struct hci_dev *hdev, __u8 status) conn = hci_conn_add(hdev, ACL_LINK, &cp->bdaddr); if (conn) { conn->out = true; - conn->link_mode |= HCI_LM_MASTER; + set_bit(HCI_CONN_MASTER, &conn->flags); } else BT_ERR("No memory for new connection"); } @@ -1989,10 +1989,10 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_conn_add_sysfs(conn); if (test_bit(HCI_AUTH, &hdev->flags)) - conn->link_mode |= HCI_LM_AUTH; + set_bit(HCI_CONN_AUTH, &conn->flags); if (test_bit(HCI_ENCRYPT, &hdev->flags)) - conn->link_mode |= HCI_LM_ENCRYPT; + set_bit(HCI_CONN_ENCRYPT, &conn->flags); /* Get remote features */ if (conn->type == ACL_LINK) { @@ -2220,7 +2220,7 @@ static void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) test_bit(HCI_CONN_REAUTH_PEND, &conn->flags)) { BT_INFO("re-auth of legacy device is not possible."); } else { - conn->link_mode |= HCI_LM_AUTH; + set_bit(HCI_CONN_AUTH, &conn->flags); conn->sec_level = conn->pending_sec_level; } } else { @@ -2323,19 +2323,19 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb) if (!ev->status) { if (ev->encrypt) { /* Encryption implies authentication */ - conn->link_mode |= HCI_LM_AUTH; - conn->link_mode |= HCI_LM_ENCRYPT; + set_bit(HCI_CONN_AUTH, &conn->flags); + set_bit(HCI_CONN_ENCRYPT, &conn->flags); conn->sec_level = conn->pending_sec_level; /* P-256 authentication key implies FIPS */ if (conn->key_type == HCI_LK_AUTH_COMBINATION_P256) - conn->link_mode |= HCI_LM_FIPS; + set_bit(HCI_CONN_FIPS, &conn->flags); if ((conn->type == ACL_LINK && ev->encrypt == 0x02) || conn->type == LE_LINK) set_bit(HCI_CONN_AES_CCM, &conn->flags); } else { - conn->link_mode &= ~HCI_LM_ENCRYPT; + clear_bit(HCI_CONN_ENCRYPT, &conn->flags); clear_bit(HCI_CONN_AES_CCM, &conn->flags); } } @@ -2386,7 +2386,7 @@ static void hci_change_link_key_complete_evt(struct hci_dev *hdev, conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn) { if (!ev->status) - conn->link_mode |= HCI_LM_SECURE; + set_bit(HCI_CONN_SECURE, &conn->flags); clear_bit(HCI_CONN_AUTH_PEND, &conn->flags); @@ -2828,9 +2828,9 @@ static void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb) if (conn) { if (!ev->status) { if (ev->role) - conn->link_mode &= ~HCI_LM_MASTER; + clear_bit(HCI_CONN_MASTER, &conn->flags); else - conn->link_mode |= HCI_LM_MASTER; + set_bit(HCI_CONN_MASTER, &conn->flags); } clear_bit(HCI_CONN_RSWITCH_PEND, &conn->flags); @@ -4007,7 +4007,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) if (ev->role == LE_CONN_ROLE_MASTER) { conn->out = true; - conn->link_mode |= HCI_LM_MASTER; + set_bit(HCI_CONN_MASTER, &conn->flags); } /* If we didn't have a hci_conn object previously diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 565afb78296a..d015aa190fdc 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1486,7 +1486,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) * been configured for this connection. If not, then trigger * the connection update procedure. */ - if (!(hcon->link_mode & HCI_LM_MASTER) && + if (!test_bit(HCI_CONN_MASTER, &hcon->flags) && (hcon->le_conn_interval < hcon->le_conn_min_interval || hcon->le_conn_interval > hcon->le_conn_max_interval)) { struct l2cap_conn_param_update_req req; @@ -5244,7 +5244,7 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, u16 min, max, latency, to_multiplier; int err; - if (!(hcon->link_mode & HCI_LM_MASTER)) + if (!test_bit(HCI_CONN_MASTER, &hcon->flags)) return -EINVAL; if (cmd_len != sizeof(struct l2cap_conn_param_update_req)) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 28f4ef48095b..976fce2315fd 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -435,7 +435,7 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, * Confirms and the slave Enters the passkey. */ if (method == OVERLAP) { - if (hcon->link_mode & HCI_LM_MASTER) + if (test_bit(HCI_CONN_MASTER, &hcon->flags)) method = CFM_PASSKEY; else method = REQ_PASSKEY; @@ -683,7 +683,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) if (skb->len < sizeof(*req)) return SMP_INVALID_PARAMS; - if (conn->hcon->link_mode & HCI_LM_MASTER) + if (test_bit(HCI_CONN_MASTER, &conn->hcon->flags)) return SMP_CMD_NOTSUPP; if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags)) @@ -750,7 +750,7 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) if (skb->len < sizeof(*rsp)) return SMP_INVALID_PARAMS; - if (!(conn->hcon->link_mode & HCI_LM_MASTER)) + if (!test_bit(HCI_CONN_MASTER, &conn->hcon->flags)) return SMP_CMD_NOTSUPP; skb_pull(skb, sizeof(*rsp)); @@ -873,7 +873,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) if (skb->len < sizeof(*rp)) return SMP_INVALID_PARAMS; - if (!(conn->hcon->link_mode & HCI_LM_MASTER)) + if (!test_bit(HCI_CONN_MASTER, &conn->hcon->flags)) return SMP_CMD_NOTSUPP; sec_level = authreq_to_seclevel(rp->auth_req); @@ -937,7 +937,7 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level) if (sec_level > hcon->pending_sec_level) hcon->pending_sec_level = sec_level; - if (hcon->link_mode & HCI_LM_MASTER) + if (test_bit(HCI_CONN_MASTER, &hcon->flags)) if (smp_ltk_encrypt(conn, hcon->pending_sec_level)) return 0; @@ -957,7 +957,7 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level) hcon->pending_sec_level > BT_SECURITY_MEDIUM) authreq |= SMP_AUTH_MITM; - if (hcon->link_mode & HCI_LM_MASTER) { + if (test_bit(HCI_CONN_MASTER, &hcon->flags)) { struct smp_cmd_pairing cp; build_pairing_cmd(conn, &cp, NULL, authreq); -- cgit v1.2.3 From dbbfa2ab7a2a508fd6ad2ce08f7f34c3041f51ef Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Wed, 25 Jun 2014 16:44:45 -0300 Subject: Bluetooth: Use macro instead of hard-coded value This patch replaces the hard-coded value in hci_bdaddr_is_rpa() helper by the corresponding macro ADDR_LE_DEV_RANDOM. Signed-off-by: Andre Guedes Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 0b0597d81827..dda7f00c07c5 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1155,7 +1155,7 @@ static inline bool eir_has_data_type(u8 *data, size_t data_len, u8 type) static inline bool hci_bdaddr_is_rpa(bdaddr_t *bdaddr, u8 addr_type) { - if (addr_type != 0x01) + if (addr_type != ADDR_LE_DEV_RANDOM) return false; if ((bdaddr->b[5] & 0xc0) == 0x40) -- cgit v1.2.3 From b10e8017bd9d02a3c7975c06d8fa2fc39df1731c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 27 Jun 2014 14:23:07 +0300 Subject: Bluetooth: Remove unnecessary hcon->smp_conn variable The smp_conn member of struct hci_conn was simply a pointer to the l2cap_conn object. Since we already have hcon->l2cap_data that points to the same thing there's no need to have this second variable. This patch removes it and changes the single place that was using it to use hcon->l2cap_data instead. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 - net/bluetooth/smp.c | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index dda7f00c07c5..ca2a99807615 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -409,7 +409,6 @@ struct hci_conn { struct hci_dev *hdev; void *l2cap_data; void *sco_data; - void *smp_conn; struct amp_mgr *amp_mgr; struct hci_conn *link; diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 641ce8b69d2a..414c5151aa46 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -584,7 +584,6 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn) smp->conn = conn; conn->smp_chan = smp; - conn->hcon->smp_conn = conn; hci_conn_hold(conn->hcon); @@ -626,13 +625,12 @@ void smp_chan_destroy(struct l2cap_conn *conn) kfree(smp); conn->smp_chan = NULL; - conn->hcon->smp_conn = NULL; hci_conn_drop(conn->hcon); } int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey) { - struct l2cap_conn *conn = hcon->smp_conn; + struct l2cap_conn *conn = hcon->l2cap_data; struct smp_chan *smp; u32 value; -- cgit v1.2.3 From df935429be40b02568d4bcf9ebfacf8011b85a85 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 27 Jun 2014 14:32:16 +0200 Subject: Bluetooth: Send HCI_Read_Clock_Offset before disconnecting When the connection is in master role and it is going to be disconnected based on the disconnection timeout, then send the HCI_Read_Clock_Offset command in an attempt to update the clock offset value in the inquiry cache. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 5 +++++ net/bluetooth/hci_conn.c | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index cc2e88dd20ea..6933766e7215 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -533,6 +533,11 @@ struct hci_cp_read_remote_version { __le16 handle; } __packed; +#define HCI_OP_READ_CLOCK_OFFSET 0x041f +struct hci_cp_read_clock_offset { + __le16 handle; +} __packed; + #define HCI_OP_SETUP_SYNC_CONN 0x0428 struct hci_cp_setup_sync_conn { __le16 handle; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 6d0fe3df2fa5..8a0c7a0ac1b6 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -309,6 +309,25 @@ static void hci_conn_timeout(struct work_struct *work) hci_amp_disconn(conn); } else { __u8 reason = hci_proto_disconn_ind(conn); + + /* When we are master of an established connection + * and it enters the disconnect timeout, then go + * ahead and try to read the current clock offset. + * + * Processing of the result is done within the + * event handling and hci_clock_offset_evt function. + */ + if (conn->type == ACL_LINK && + test_bit(HCI_CONN_MASTER, &conn->flags)) { + struct hci_dev *hdev = conn->hdev; + struct hci_cp_read_clock_offset cp; + + cp.handle = cpu_to_le16(conn->handle); + + hci_send_cmd(hdev, HCI_OP_READ_CLOCK_OFFSET, + sizeof(cp), &cp); + } + hci_disconnect(conn, reason); } break; -- cgit v1.2.3 From 33f35721030185a2c5a1bb8afd4c3744709745b5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 28 Jun 2014 17:54:06 +0300 Subject: Bluetooth: Add tracking of local and piconet clock values This patch adds support for storing the local and piconet clock values from the HCI_Read_Clock command response to the hci_dev and hci_conn structs. This will be later used in another patch to implement support for the Get Clock Info mgmt command. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 12 ++++++++++++ include/net/bluetooth/hci_core.h | 4 ++++ net/bluetooth/hci_event.c | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 6933766e7215..58c5930dea60 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1087,6 +1087,18 @@ struct hci_rp_read_rssi { __s8 rssi; } __packed; +#define HCI_OP_READ_CLOCK 0x1407 +struct hci_cp_read_clock { + __le16 handle; + __u8 which; +} __packed; +struct hci_rp_read_clock { + __u8 status; + __le16 handle; + __le32 clock; + __le16 accuracy; +} __packed; + #define HCI_OP_READ_LOCAL_AMP_INFO 0x1409 struct hci_rp_read_local_amp_info { __u8 status; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index ca2a99807615..0906990dedd8 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -207,6 +207,7 @@ struct hci_dev { __u16 conn_info_min_age; __u16 conn_info_max_age; __u8 ssp_debug_mode; + __u32 clock; __u16 devid_source; __u16 devid_vendor; @@ -388,6 +389,9 @@ struct hci_conn { __s8 max_tx_power; unsigned long flags; + __u32 clock; + __u16 clock_accuracy; + unsigned long conn_info_timestamp; __u8 remote_cap; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 7a23324eac39..315d615ca3f9 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -721,6 +721,41 @@ static void hci_cc_read_data_block_size(struct hci_dev *hdev, hdev->block_cnt, hdev->block_len); } +static void hci_cc_read_clock(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_rp_read_clock *rp = (void *) skb->data; + struct hci_cp_read_clock *cp; + struct hci_conn *conn; + + BT_DBG("%s", hdev->name); + + if (skb->len < sizeof(*rp)) + return; + + if (rp->status) + return; + + hci_dev_lock(hdev); + + cp = hci_sent_cmd_data(hdev, HCI_OP_READ_CLOCK); + if (!cp) + goto unlock; + + if (cp->which == 0x00) { + hdev->clock = le32_to_cpu(rp->clock); + goto unlock; + } + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle)); + if (conn) { + conn->clock = le32_to_cpu(rp->clock); + conn->clock_accuracy = le16_to_cpu(rp->accuracy); + } + +unlock: + hci_dev_unlock(hdev); +} + static void hci_cc_read_local_amp_info(struct hci_dev *hdev, struct sk_buff *skb) { @@ -2597,6 +2632,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_read_local_amp_info(hdev, skb); break; + case HCI_OP_READ_CLOCK: + hci_cc_read_clock(hdev, skb); + break; + case HCI_OP_READ_LOCAL_AMP_ASSOC: hci_cc_read_local_amp_assoc(hdev, skb); break; -- cgit v1.2.3 From 958684263d3efbc721fb2b86f94876893eb638d2 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 28 Jun 2014 17:54:07 +0300 Subject: Bluetooth: Add support for Get Clock Info mgmt command This patch implements support for the Get Clock Information mgmt command. This is done by performing one or two HCI_Read_Clock commands and creating the response from the stored values in the hci_dev and hci_conn structs. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 12 ++++ net/bluetooth/mgmt.c | 144 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index bcffc9ae0c89..3109dec13409 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -424,6 +424,18 @@ struct mgmt_rp_get_conn_info { __s8 max_tx_power; } __packed; +#define MGMT_OP_GET_CLOCK_INFO 0x0032 +struct mgmt_cp_get_clock_info { + struct mgmt_addr_info addr; +} __packed; +#define MGMT_GET_CLOCK_INFO_SIZE MGMT_ADDR_INFO_SIZE +struct mgmt_rp_get_clock_info { + struct mgmt_addr_info addr; + __le32 local_clock; + __le32 piconet_clock; + __le16 accuracy; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 727ae15f9c36..6faa4616cbfe 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -85,6 +85,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_SET_PRIVACY, MGMT_OP_LOAD_IRKS, MGMT_OP_GET_CONN_INFO, + MGMT_OP_GET_CLOCK_INFO, }; static const u16 mgmt_events[] = { @@ -571,6 +572,22 @@ static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev) return NULL; } +static struct pending_cmd *mgmt_pending_find_data(u16 opcode, + struct hci_dev *hdev, + const void *data) +{ + struct pending_cmd *cmd; + + list_for_each_entry(cmd, &hdev->mgmt_pending, list) { + if (cmd->user_data != data) + continue; + if (cmd->opcode == opcode) + return cmd; + } + + return NULL; +} + static u8 create_scan_rsp_data(struct hci_dev *hdev, u8 *ptr) { u8 ad_len = 0; @@ -4820,6 +4837,132 @@ unlock: return err; } +static void get_clock_info_complete(struct hci_dev *hdev, u8 status) +{ + struct mgmt_cp_get_clock_info *cp; + struct mgmt_rp_get_clock_info rp; + struct hci_cp_read_clock *hci_cp; + struct pending_cmd *cmd; + struct hci_conn *conn; + + BT_DBG("%s status %u", hdev->name, status); + + hci_dev_lock(hdev); + + hci_cp = hci_sent_cmd_data(hdev, HCI_OP_READ_CLOCK); + if (!hci_cp) + goto unlock; + + if (hci_cp->which) { + u16 handle = __le16_to_cpu(hci_cp->handle); + conn = hci_conn_hash_lookup_handle(hdev, handle); + } else { + conn = NULL; + } + + cmd = mgmt_pending_find_data(MGMT_OP_GET_CLOCK_INFO, hdev, conn); + if (!cmd) + goto unlock; + + cp = cmd->param; + + memset(&rp, 0, sizeof(rp)); + memcpy(&rp.addr, &cp->addr, sizeof(rp.addr)); + + if (status) + goto send_rsp; + + rp.local_clock = cpu_to_le32(hdev->clock); + + if (conn) { + rp.piconet_clock = cpu_to_le32(conn->clock); + rp.accuracy = cpu_to_le16(conn->clock_accuracy); + } + +send_rsp: + cmd_complete(cmd->sk, cmd->index, cmd->opcode, mgmt_status(status), + &rp, sizeof(rp)); + mgmt_pending_remove(cmd); + if (conn) + hci_conn_drop(conn); + +unlock: + hci_dev_unlock(hdev); +} + +static int get_clock_info(struct sock *sk, struct hci_dev *hdev, void *data, + u16 len) +{ + struct mgmt_cp_get_clock_info *cp = data; + struct mgmt_rp_get_clock_info rp; + struct hci_cp_read_clock hci_cp; + struct pending_cmd *cmd; + struct hci_request req; + struct hci_conn *conn; + int err; + + BT_DBG("%s", hdev->name); + + memset(&rp, 0, sizeof(rp)); + bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); + rp.addr.type = cp->addr.type; + + if (cp->addr.type != BDADDR_BREDR) + return cmd_complete(sk, hdev->id, MGMT_OP_GET_CLOCK_INFO, + MGMT_STATUS_INVALID_PARAMS, + &rp, sizeof(rp)); + + hci_dev_lock(hdev); + + if (!hdev_is_powered(hdev)) { + err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CLOCK_INFO, + MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp)); + goto unlock; + } + + if (bacmp(&cp->addr.bdaddr, BDADDR_ANY)) { + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, + &cp->addr.bdaddr); + if (!conn || conn->state != BT_CONNECTED) { + err = cmd_complete(sk, hdev->id, + MGMT_OP_GET_CLOCK_INFO, + MGMT_STATUS_NOT_CONNECTED, + &rp, sizeof(rp)); + goto unlock; + } + } else { + conn = NULL; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_GET_CLOCK_INFO, hdev, data, len); + if (!cmd) { + err = -ENOMEM; + goto unlock; + } + + hci_req_init(&req, hdev); + + memset(&hci_cp, 0, sizeof(hci_cp)); + hci_req_add(&req, HCI_OP_READ_CLOCK, sizeof(hci_cp), &hci_cp); + + if (conn) { + hci_conn_hold(conn); + cmd->user_data = conn; + + hci_cp.handle = cpu_to_le16(conn->handle); + hci_cp.which = 0x01; /* Piconet clock */ + hci_req_add(&req, HCI_OP_READ_CLOCK, sizeof(hci_cp), &hci_cp); + } + + err = hci_req_run(&req, get_clock_info_complete); + if (err < 0) + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); + return err; +} + static const struct mgmt_handler { int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len); @@ -4876,6 +5019,7 @@ static const struct mgmt_handler { { set_privacy, false, MGMT_SET_PRIVACY_SIZE }, { load_irks, true, MGMT_LOAD_IRKS_SIZE }, { get_conn_info, false, MGMT_GET_CONN_INFO_SIZE }, + { get_clock_info, false, MGMT_GET_CLOCK_INFO_SIZE }, }; -- cgit v1.2.3 From f044eb0524a02ea7f921c9234fbdba43290da1e2 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 29 Jun 2014 16:43:26 +0200 Subject: Bluetooth: Store latency and supervision timeout in connection params When the slave updates the connection parameters, store also the connection latency and supervision timeout information in the internal list of connection parameters for known devices. Having these values available allowes the auto-connection procedure to use the correct values from the beginning without having to request an update on every connection establishment. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_conn.c | 16 ++++++++++++++-- net/bluetooth/hci_core.c | 2 ++ 3 files changed, 18 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 0906990dedd8..182044824495 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -439,6 +439,8 @@ struct hci_conn_params { u16 conn_min_interval; u16 conn_max_interval; + u16 conn_latency; + u16 supervision_timeout; enum { HCI_AUTO_CONN_DISABLED, diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 25ee27ddc882..adb413d77637 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -216,11 +216,23 @@ bool hci_setup_sync(struct hci_conn *conn, __u16 handle) void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, u16 to_multiplier) { - struct hci_cp_le_conn_update cp; struct hci_dev *hdev = conn->hdev; + struct hci_conn_params *params; + struct hci_cp_le_conn_update cp; - memset(&cp, 0, sizeof(cp)); + hci_dev_lock(hdev); + + params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); + if (params) { + params->conn_min_interval = min; + params->conn_max_interval = max; + params->conn_latency = latency; + params->supervision_timeout = to_multiplier; + } + hci_dev_unlock(hdev); + + memset(&cp, 0, sizeof(cp)); cp.handle = cpu_to_le16(conn->handle); cp.conn_interval_min = cpu_to_le16(min); cp.conn_interval_max = cpu_to_le16(max); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index c566b57610c9..97a6453bee30 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3514,6 +3514,8 @@ int hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, update: params->conn_min_interval = conn_min_interval; params->conn_max_interval = conn_max_interval; + params->conn_latency = 0x0000; + params->supervision_timeout = 0x002a; params->auto_connect = auto_connect; switch (auto_connect) { -- cgit v1.2.3 From 2faade53e65f276cf1c30a885fb64808a083714e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 29 Jun 2014 19:44:03 +0200 Subject: Bluetooth: Add support for Add/Remove Device management commands This allows adding or removing devices from the background scanning list the kernel maintains. Device flagged for auto-connection will be automatically connected if they are found. The passive scanning required for auto-connection will be started and stopped on demand. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 13 ++++++ net/bluetooth/mgmt.c | 99 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 111 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 3109dec13409..bc9b105f2b50 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -436,6 +436,19 @@ struct mgmt_rp_get_clock_info { __le16 accuracy; } __packed; +#define MGMT_OP_ADD_DEVICE 0x0033 +struct mgmt_cp_add_device { + struct mgmt_addr_info addr; + __u8 action; +} __packed; +#define MGMT_ADD_DEVICE_SIZE (MGMT_ADDR_INFO_SIZE + 1) + +#define MGMT_OP_REMOVE_DEVICE 0x0034 +struct mgmt_cp_remove_device { + struct mgmt_addr_info addr; +} __packed; +#define MGMT_REMOVE_DEVICE_SIZE MGMT_ADDR_INFO_SIZE + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 41b1aec0c5dc..64b55c7881ee 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -86,6 +86,8 @@ static const u16 mgmt_commands[] = { MGMT_OP_LOAD_IRKS, MGMT_OP_GET_CONN_INFO, MGMT_OP_GET_CLOCK_INFO, + MGMT_OP_ADD_DEVICE, + MGMT_OP_REMOVE_DEVICE, }; static const u16 mgmt_events[] = { @@ -4966,6 +4968,100 @@ unlock: return err; } +static int add_device(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_cp_add_device *cp = data; + u8 auto_conn, addr_type; + int err; + + BT_DBG("%s", hdev->name); + + if (!bdaddr_type_is_le(cp->addr.type) || + !bacmp(&cp->addr.bdaddr, BDADDR_ANY)) + return cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, + MGMT_STATUS_INVALID_PARAMS, + &cp->addr, sizeof(cp->addr)); + + if (cp->action != 0x00 && cp->action != 0x01) + return cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, + MGMT_STATUS_INVALID_PARAMS, + &cp->addr, sizeof(cp->addr)); + + hci_dev_lock(hdev); + + if (cp->addr.type == BDADDR_LE_PUBLIC) + addr_type = ADDR_LE_DEV_PUBLIC; + else + addr_type = ADDR_LE_DEV_RANDOM; + + if (cp->action) + auto_conn = HCI_AUTO_CONN_ALWAYS; + else + auto_conn = HCI_AUTO_CONN_DISABLED; + + if (hci_conn_params_add(hdev, &cp->addr.bdaddr, addr_type, auto_conn, + hdev->le_conn_min_interval, + hdev->le_conn_max_interval) < 0) { + err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, + MGMT_STATUS_FAILED, + &cp->addr, sizeof(cp->addr)); + goto unlock; + } + + err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, + MGMT_STATUS_SUCCESS, &cp->addr, sizeof(cp->addr)); + +unlock: + hci_dev_unlock(hdev); + return err; +} + +static int remove_device(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_cp_remove_device *cp = data; + int err; + + BT_DBG("%s", hdev->name); + + hci_dev_lock(hdev); + + if (bacmp(&cp->addr.bdaddr, BDADDR_ANY)) { + u8 addr_type; + + if (!bdaddr_type_is_le(cp->addr.type)) { + err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, + MGMT_STATUS_INVALID_PARAMS, + &cp->addr, sizeof(cp->addr)); + goto unlock; + } + + if (cp->addr.type == BDADDR_LE_PUBLIC) + addr_type = ADDR_LE_DEV_PUBLIC; + else + addr_type = ADDR_LE_DEV_RANDOM; + + hci_conn_params_del(hdev, &cp->addr.bdaddr, addr_type); + } else { + if (cp->addr.type) { + err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, + MGMT_STATUS_INVALID_PARAMS, + &cp->addr, sizeof(cp->addr)); + goto unlock; + } + + hci_conn_params_clear(hdev); + } + + err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, + MGMT_STATUS_SUCCESS, &cp->addr, sizeof(cp->addr)); + +unlock: + hci_dev_unlock(hdev); + return err; +} + static const struct mgmt_handler { int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len); @@ -5023,9 +5119,10 @@ static const struct mgmt_handler { { load_irks, true, MGMT_LOAD_IRKS_SIZE }, { get_conn_info, false, MGMT_GET_CONN_INFO_SIZE }, { get_clock_info, false, MGMT_GET_CLOCK_INFO_SIZE }, + { add_device, false, MGMT_ADD_DEVICE_SIZE }, + { remove_device, false, MGMT_REMOVE_DEVICE_SIZE }, }; - int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) { void *buf; -- cgit v1.2.3 From 8afef092a192cb946393bb11cc95b59739c1e57b Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 29 Jun 2014 22:28:34 +0200 Subject: Bluetooth: Add Device Added and Device Removed management events When devices are added or removed, then make sure that events are send out to all other clients so that the list of devices can be easily tracked. This is especially important when external clients are adding or removing devices within the auto-connection list. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 11 +++++++++++ net/bluetooth/mgmt.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index bc9b105f2b50..1f95ad4fce02 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -603,3 +603,14 @@ struct mgmt_ev_new_csrk { __u8 store_hint; struct mgmt_csrk_info key; } __packed; + +#define MGMT_EV_DEVICE_ADDED 0x001a +struct mgmt_ev_device_added { + struct mgmt_addr_info addr; + __u8 action; +} __packed; + +#define MGMT_EV_DEVICE_REMOVED 0x001b +struct mgmt_ev_device_removed { + struct mgmt_addr_info addr; +} __packed; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 64b55c7881ee..d83197f9e727 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -114,6 +114,8 @@ static const u16 mgmt_events[] = { MGMT_EV_PASSKEY_NOTIFY, MGMT_EV_NEW_IRK, MGMT_EV_NEW_CSRK, + MGMT_EV_DEVICE_ADDED, + MGMT_EV_DEVICE_REMOVED, }; #define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000) @@ -4968,6 +4970,18 @@ unlock: return err; } +static void device_added(struct sock *sk, struct hci_dev *hdev, + bdaddr_t *bdaddr, u8 type, u8 action) +{ + struct mgmt_ev_device_added ev; + + bacpy(&ev.addr.bdaddr, bdaddr); + ev.addr.type = type; + ev.action = action; + + mgmt_event(MGMT_EV_DEVICE_ADDED, hdev, &ev, sizeof(ev), sk); +} + static int add_device(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { @@ -5009,6 +5023,8 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, goto unlock; } + device_added(sk, hdev, &cp->addr.bdaddr, cp->addr.type, cp->action); + err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, MGMT_STATUS_SUCCESS, &cp->addr, sizeof(cp->addr)); @@ -5017,6 +5033,17 @@ unlock: return err; } +static void device_removed(struct sock *sk, struct hci_dev *hdev, + bdaddr_t *bdaddr, u8 type) +{ + struct mgmt_ev_device_removed ev; + + bacpy(&ev.addr.bdaddr, bdaddr); + ev.addr.type = type; + + mgmt_event(MGMT_EV_DEVICE_REMOVED, hdev, &ev, sizeof(ev), sk); +} + static int remove_device(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { @@ -5043,6 +5070,8 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, addr_type = ADDR_LE_DEV_RANDOM; hci_conn_params_del(hdev, &cp->addr.bdaddr, addr_type); + + device_removed(sk, hdev, &cp->addr.bdaddr, cp->addr.type); } else { if (cp->addr.type) { err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, -- cgit v1.2.3 From 04fb7d9066dd9173ef0d4ccea8fe3bb59bd94605 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 30 Jun 2014 12:34:36 +0200 Subject: Bluetooth: Provide defaults for LE connection latency and timeout Store the connection latency and supervision timeout default values with all the other controller defaults. And when needed use them for new connections. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_conn.c | 4 ++-- net/bluetooth/hci_core.c | 6 ++++-- 3 files changed, 8 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 182044824495..ed842c7e5cf1 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -203,6 +203,8 @@ struct hci_dev { __u16 le_scan_window; __u16 le_conn_min_interval; __u16 le_conn_max_interval; + __u16 le_conn_latency; + __u16 le_supv_timeout; __u16 discov_interleaved_timeout; __u16 conn_info_min_age; __u16 conn_info_max_age; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index e7ee7267f846..d00aaf976efc 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -773,8 +773,8 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, } else { conn->le_conn_min_interval = hdev->le_conn_min_interval; conn->le_conn_max_interval = hdev->le_conn_max_interval; - conn->le_conn_latency = 0x0000; - conn->le_supv_timeout = 0x002a; + conn->le_conn_latency = hdev->le_conn_latency; + conn->le_supv_timeout = hdev->le_supv_timeout; } /* If controller is scanning, we stop it since some controllers are diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 8d972023196b..94551c33c4c6 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3439,8 +3439,8 @@ int hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, update: params->conn_min_interval = conn_min_interval; params->conn_max_interval = conn_max_interval; - params->conn_latency = 0x0000; - params->supervision_timeout = 0x002a; + params->conn_latency = hdev->le_conn_latency; + params->supervision_timeout = hdev->le_supv_timeout; params->auto_connect = auto_connect; switch (auto_connect) { @@ -3706,6 +3706,8 @@ struct hci_dev *hci_alloc_dev(void) hdev->le_scan_window = 0x0030; hdev->le_conn_min_interval = 0x0028; hdev->le_conn_max_interval = 0x0038; + hdev->le_conn_latency = 0x0000; + hdev->le_supv_timeout = 0x002a; hdev->rpa_timeout = HCI_DEFAULT_RPA_TIMEOUT; hdev->discov_interleaved_timeout = DISCOV_INTERLEAVED_TIMEOUT; -- cgit v1.2.3 From bf5b3c8be07905c242bb7f751dcb890b94c22d93 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 30 Jun 2014 12:34:39 +0200 Subject: Bluetooth: Provide function to create and set connection parameters In some cases it is useful to not overwrite connection parametes and instead just create default ones if they don't exist. This function does exactly that. hci_conn_params_add will allow to create new default connection parameters. hci_conn_params_set will set the values and also create new parameters if they don't exist. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 3 ++- net/bluetooth/hci_core.c | 36 +++++++++++++++++++++++++++++++++++- net/bluetooth/mgmt.c | 5 ++++- 3 files changed, 41 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index ed842c7e5cf1..c0d2506e2019 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -852,7 +852,8 @@ int hci_white_list_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); -int hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, +int hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); +int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, u8 auto_connect, u16 conn_min_interval, u16 conn_max_interval); void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 6c28687c9286..adea7de95633 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3472,7 +3472,41 @@ void hci_pend_le_conns_clear(struct hci_dev *hdev) } /* This function requires the caller holds hdev->lock */ -int hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, +int hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) +{ + struct hci_conn_params *params; + + if (!is_identity_address(addr, addr_type)) + return -EINVAL; + + params = hci_conn_params_lookup(hdev, addr, addr_type); + if (params) + return 0; + + params = kzalloc(sizeof(*params), GFP_KERNEL); + if (!params) { + BT_ERR("Out of memory"); + return -ENOMEM; + } + + bacpy(¶ms->addr, addr); + params->addr_type = addr_type; + + list_add(¶ms->list, &hdev->le_conn_params); + + params->conn_min_interval = hdev->le_conn_min_interval; + params->conn_max_interval = hdev->le_conn_max_interval; + params->conn_latency = hdev->le_conn_latency; + params->supervision_timeout = hdev->le_supv_timeout; + params->auto_connect = HCI_AUTO_CONN_DISABLED; + + BT_DBG("addr %pMR (type %u)", addr, addr_type); + + return 0; +} + +/* This function requires the caller holds hdev->lock */ +int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, u8 auto_connect, u16 conn_min_interval, u16 conn_max_interval) { diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d83197f9e727..e30d0ebb5018 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5014,7 +5014,10 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, else auto_conn = HCI_AUTO_CONN_DISABLED; - if (hci_conn_params_add(hdev, &cp->addr.bdaddr, addr_type, auto_conn, + /* If the connection parameters don't exist for this device, + * they will be created and configured with defaults. + */ + if (hci_conn_params_set(hdev, &cp->addr.bdaddr, addr_type, auto_conn, hdev->le_conn_min_interval, hdev->le_conn_max_interval) < 0) { err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, -- cgit v1.2.3 From d4905f2453bd228d8ffc57f4e25bba0a8f52c805 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Wed, 25 Jun 2014 21:52:52 -0300 Subject: Bluetooth: Connection parameters check helper This patch renames l2cap_check_conn_param() to hci_check_conn_params() and moves it to hci_core.h so it can reused in others files. This helper will be reused in the next patch. Signed-off-by: Andre Guedes Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 21 +++++++++++++++++++++ net/bluetooth/l2cap_core.c | 23 +---------------------- 2 files changed, 22 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index c0d2506e2019..ec830871b9b8 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1181,6 +1181,27 @@ static inline struct smp_irk *hci_get_irk(struct hci_dev *hdev, return hci_find_irk_by_rpa(hdev, bdaddr); } +static inline int hci_check_conn_params(u16 min, u16 max, u16 latency, + u16 to_multiplier) +{ + u16 max_latency; + + if (min > max || min < 6 || max > 3200) + return -EINVAL; + + if (to_multiplier < 10 || to_multiplier > 3200) + return -EINVAL; + + if (max >= to_multiplier * 8) + return -EINVAL; + + max_latency = (to_multiplier * 8 / max) - 1; + if (latency > 499 || latency > max_latency) + return -EINVAL; + + return 0; +} + int hci_register_cb(struct hci_cb *hcb); int hci_unregister_cb(struct hci_cb *hcb); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index d015aa190fdc..e203a5fdf874 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -5213,27 +5213,6 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn, return 0; } -static inline int l2cap_check_conn_param(u16 min, u16 max, u16 latency, - u16 to_multiplier) -{ - u16 max_latency; - - if (min > max || min < 6 || max > 3200) - return -EINVAL; - - if (to_multiplier < 10 || to_multiplier > 3200) - return -EINVAL; - - if (max >= to_multiplier * 8) - return -EINVAL; - - max_latency = (to_multiplier * 8 / max) - 1; - if (latency > 499 || latency > max_latency) - return -EINVAL; - - return 0; -} - static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data) @@ -5261,7 +5240,7 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, memset(&rsp, 0, sizeof(rsp)); - err = l2cap_check_conn_param(min, max, latency, to_multiplier); + err = hci_check_conn_params(min, max, latency, to_multiplier); if (err) rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_REJECTED); else -- cgit v1.2.3 From 51d167c0972ef1496f2e6ab08aab602644d1f9bb Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 1 Jul 2014 12:11:04 +0200 Subject: Bluetooth: Change hci_conn_params_add to return the parameter struct When adding new connection parameters, it is useful to return either the existing struct or the newly created one. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 3 ++- net/bluetooth/hci_core.c | 11 ++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index ec830871b9b8..5a83621672bf 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -852,7 +852,8 @@ int hci_white_list_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); -int hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); +struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev, + bdaddr_t *addr, u8 addr_type); int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, u8 auto_connect, u16 conn_min_interval, u16 conn_max_interval); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 2312e77582b0..883ddd52344d 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3472,21 +3472,22 @@ void hci_pend_le_conns_clear(struct hci_dev *hdev) } /* This function requires the caller holds hdev->lock */ -int hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) +struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev, + bdaddr_t *addr, u8 addr_type) { struct hci_conn_params *params; if (!is_identity_address(addr, addr_type)) - return -EINVAL; + return NULL; params = hci_conn_params_lookup(hdev, addr, addr_type); if (params) - return 0; + return params; params = kzalloc(sizeof(*params), GFP_KERNEL); if (!params) { BT_ERR("Out of memory"); - return -ENOMEM; + return NULL; } bacpy(¶ms->addr, addr); @@ -3502,7 +3503,7 @@ int hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) BT_DBG("addr %pMR (type %u)", addr, addr_type); - return 0; + return params; } /* This function requires the caller holds hdev->lock */ -- cgit v1.2.3 From d06b50ce14119acb04773a9808ccff5d1767b7e4 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 1 Jul 2014 12:11:06 +0200 Subject: Bluetooth: Remove connection interval parameters from hci_conn_params_set The connection interval parameter of hci_conn_params_set are always used with the controller defaults. So just let hci_conn_params_add set the controller default and not bother resetting them to controller defaults every time the hci_conn_params_set is called. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 3 +-- net/bluetooth/hci_core.c | 10 +++------- net/bluetooth/mgmt.c | 5 ++--- 3 files changed, 6 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 5a83621672bf..61d4d265f42d 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -855,8 +855,7 @@ struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev, struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, - u8 auto_connect, u16 conn_min_interval, - u16 conn_max_interval); + u8 auto_connect); void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); void hci_conn_params_clear(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 9ae945d8ad7e..237963d5473c 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3508,8 +3508,7 @@ struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev, /* This function requires the caller holds hdev->lock */ int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, - u8 auto_connect, u16 conn_min_interval, - u16 conn_max_interval) + u8 auto_connect) { struct hci_conn_params *params; @@ -3517,8 +3516,6 @@ int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, if (!params) return -EIO; - params->conn_min_interval = conn_min_interval; - params->conn_max_interval = conn_max_interval; params->auto_connect = auto_connect; switch (auto_connect) { @@ -3532,9 +3529,8 @@ int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, break; } - BT_DBG("addr %pMR (type %u) auto_connect %u conn_min_interval 0x%.4x " - "conn_max_interval 0x%.4x", addr, addr_type, auto_connect, - conn_min_interval, conn_max_interval); + BT_DBG("addr %pMR (type %u) auto_connect %u", addr, addr_type, + auto_connect); return 0; } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 6baba309f9e2..c6e9b551242b 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5028,9 +5028,8 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, /* If the connection parameters don't exist for this device, * they will be created and configured with defaults. */ - if (hci_conn_params_set(hdev, &cp->addr.bdaddr, addr_type, auto_conn, - hdev->le_conn_min_interval, - hdev->le_conn_max_interval) < 0) { + if (hci_conn_params_set(hdev, &cp->addr.bdaddr, addr_type, + auto_conn) < 0) { err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, MGMT_STATUS_FAILED, &cp->addr, sizeof(cp->addr)); -- cgit v1.2.3 From af58925ca6175695e502fa792f43a946f7474765 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 1 Jul 2014 14:11:20 +0200 Subject: Bluetooth: Provide flags parameter direct to mgmt_device_found Providing the flags parameter directly to mgmt_device_found function makes the core simpler and more readable. With this it becomes a lot easier to add new flags in the future. This also changes hci_inquiry_cache_update to just return that flags needed for mgmt_device_found since that is its only use for the two return parameters anyway. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 9 ++++---- include/net/bluetooth/mgmt.h | 1 + net/bluetooth/hci_core.c | 24 ++++++++++++-------- net/bluetooth/hci_event.c | 48 ++++++++++++++++++++++------------------ net/bluetooth/mgmt.c | 10 +++------ 5 files changed, 50 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 61d4d265f42d..ab3d4dda071c 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -510,8 +510,8 @@ struct inquiry_entry *hci_inquiry_cache_lookup_resolve(struct hci_dev *hdev, int state); void hci_inquiry_cache_update_resolve(struct hci_dev *hdev, struct inquiry_entry *ie); -bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, - bool name_known, bool *ssp); +u32 hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, + bool name_known); void hci_inquiry_cache_flush(struct hci_dev *hdev); /* ----- HCI Connections ----- */ @@ -1318,9 +1318,8 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192, u8 *randomizer192, u8 *hash256, u8 *randomizer256, u8 status); void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, - u8 ssp, u8 *eir, u16 eir_len, u8 *scan_rsp, - u8 scan_rsp_len); + u8 addr_type, u8 *dev_class, s8 rssi, u32 flags, + u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len); void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, s8 rssi, u8 *name, u8 name_len); void mgmt_discovering(struct hci_dev *hdev, u8 discovering); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 1f95ad4fce02..2d88f361a016 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -547,6 +547,7 @@ struct mgmt_ev_auth_failed { #define MGMT_DEV_FOUND_CONFIRM_NAME 0x01 #define MGMT_DEV_FOUND_LEGACY_PAIRING 0x02 +#define MGMT_DEV_FOUND_NOT_CONNECTABLE 0x04 #define MGMT_EV_DEVICE_FOUND 0x0012 struct mgmt_ev_device_found { diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 237963d5473c..0aa392406629 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "smp.h" @@ -1970,22 +1971,24 @@ void hci_inquiry_cache_update_resolve(struct hci_dev *hdev, list_add(&ie->list, pos); } -bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, - bool name_known, bool *ssp) +u32 hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, + bool name_known) { struct discovery_state *cache = &hdev->discovery; struct inquiry_entry *ie; + u32 flags = 0; BT_DBG("cache %p, %pMR", cache, &data->bdaddr); hci_remove_remote_oob_data(hdev, &data->bdaddr); - *ssp = data->ssp_mode; + if (!data->ssp_mode) + flags |= MGMT_DEV_FOUND_LEGACY_PAIRING; ie = hci_inquiry_cache_lookup(hdev, &data->bdaddr); if (ie) { - if (ie->data.ssp_mode) - *ssp = true; + if (!ie->data.ssp_mode) + flags |= MGMT_DEV_FOUND_LEGACY_PAIRING; if (ie->name_state == NAME_NEEDED && data->rssi != ie->data.rssi) { @@ -1998,8 +2001,10 @@ bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, /* Entry not in the cache. Add new one. */ ie = kzalloc(sizeof(struct inquiry_entry), GFP_ATOMIC); - if (!ie) - return false; + if (!ie) { + flags |= MGMT_DEV_FOUND_CONFIRM_NAME; + goto done; + } list_add(&ie->all, &cache->all); @@ -2022,9 +2027,10 @@ update: cache->timestamp = jiffies; if (ie->name_state == NAME_NOT_KNOWN) - return false; + flags |= MGMT_DEV_FOUND_CONFIRM_NAME; - return true; +done: + return flags; } static int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index ea155183c1d6..a4854a5d9298 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1132,7 +1132,7 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK, d->last_adv_addr_type, NULL, - d->last_adv_rssi, 0, 1, + d->last_adv_rssi, 0, d->last_adv_data, d->last_adv_data_len, NULL, 0); } @@ -1965,7 +1965,7 @@ static void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_lock(hdev); for (; num_rsp; num_rsp--, info++) { - bool name_known, ssp; + u32 flags; bacpy(&data.bdaddr, &info->bdaddr); data.pscan_rep_mode = info->pscan_rep_mode; @@ -1976,10 +1976,10 @@ static void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb) data.rssi = 0x00; data.ssp_mode = 0x00; - name_known = hci_inquiry_cache_update(hdev, &data, false, &ssp); + flags = hci_inquiry_cache_update(hdev, &data, false); + mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, - info->dev_class, 0, !name_known, ssp, NULL, - 0, NULL, 0); + info->dev_class, 0, flags, NULL, 0, NULL, 0); } hci_dev_unlock(hdev); @@ -3257,7 +3257,6 @@ static void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, { struct inquiry_data data; int num_rsp = *((__u8 *) skb->data); - bool name_known, ssp; BT_DBG("%s num_rsp %d", hdev->name, num_rsp); @@ -3274,6 +3273,8 @@ static void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, info = (void *) (skb->data + 1); for (; num_rsp; num_rsp--, info++) { + u32 flags; + bacpy(&data.bdaddr, &info->bdaddr); data.pscan_rep_mode = info->pscan_rep_mode; data.pscan_period_mode = info->pscan_period_mode; @@ -3283,16 +3284,18 @@ static void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, data.rssi = info->rssi; data.ssp_mode = 0x00; - name_known = hci_inquiry_cache_update(hdev, &data, - false, &ssp); + flags = hci_inquiry_cache_update(hdev, &data, false); + mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, info->dev_class, info->rssi, - !name_known, ssp, NULL, 0, NULL, 0); + flags, NULL, 0, NULL, 0); } } else { struct inquiry_info_with_rssi *info = (void *) (skb->data + 1); for (; num_rsp; num_rsp--, info++) { + u32 flags; + bacpy(&data.bdaddr, &info->bdaddr); data.pscan_rep_mode = info->pscan_rep_mode; data.pscan_period_mode = info->pscan_period_mode; @@ -3301,11 +3304,12 @@ static void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, data.clock_offset = info->clock_offset; data.rssi = info->rssi; data.ssp_mode = 0x00; - name_known = hci_inquiry_cache_update(hdev, &data, - false, &ssp); + + flags = hci_inquiry_cache_update(hdev, &data, false); + mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, info->dev_class, info->rssi, - !name_known, ssp, NULL, 0, NULL, 0); + flags, NULL, 0, NULL, 0); } } @@ -3472,7 +3476,8 @@ static void hci_extended_inquiry_result_evt(struct hci_dev *hdev, hci_dev_lock(hdev); for (; num_rsp; num_rsp--, info++) { - bool name_known, ssp; + u32 flags; + bool name_known; bacpy(&data.bdaddr, &info->bdaddr); data.pscan_rep_mode = info->pscan_rep_mode; @@ -3490,12 +3495,13 @@ static void hci_extended_inquiry_result_evt(struct hci_dev *hdev, else name_known = true; - name_known = hci_inquiry_cache_update(hdev, &data, name_known, - &ssp); + flags = hci_inquiry_cache_update(hdev, &data, name_known); + eir_len = eir_get_length(info->data, sizeof(info->data)); + mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, - info->dev_class, info->rssi, !name_known, - ssp, info->data, eir_len, NULL, 0); + info->dev_class, info->rssi, + flags, info->data, eir_len, NULL, 0); } hci_dev_unlock(hdev); @@ -4226,7 +4232,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, } mgmt_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL, - rssi, 0, 1, data, len, NULL, 0); + rssi, 0, data, len, NULL, 0); return; } @@ -4243,7 +4249,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, if (!match) mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK, d->last_adv_addr_type, NULL, - d->last_adv_rssi, 0, 1, + d->last_adv_rssi, 0, d->last_adv_data, d->last_adv_data_len, NULL, 0); @@ -4261,7 +4267,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, */ clear_pending_adv_report(hdev); mgmt_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL, - rssi, 0, 1, data, len, NULL, 0); + rssi, 0, data, len, NULL, 0); return; } @@ -4270,7 +4276,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, * sending a merged device found event. */ mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK, - d->last_adv_addr_type, NULL, rssi, 0, 1, + d->last_adv_addr_type, NULL, rssi, 0, d->last_adv_data, d->last_adv_data_len, data, len); clear_pending_adv_report(hdev); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index c6e9b551242b..336a2311bdca 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6233,9 +6233,8 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192, } void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, - u8 ssp, u8 *eir, u16 eir_len, u8 *scan_rsp, - u8 scan_rsp_len) + u8 addr_type, u8 *dev_class, s8 rssi, u32 flags, + u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len) { char buf[512]; struct mgmt_ev_device_found *ev = (void *) buf; @@ -6263,10 +6262,7 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, } ev->rssi = rssi; - if (cfm_name) - ev->flags |= cpu_to_le32(MGMT_DEV_FOUND_CONFIRM_NAME); - if (!ssp) - ev->flags |= cpu_to_le32(MGMT_DEV_FOUND_LEGACY_PAIRING); + ev->flags = cpu_to_le32(flags); if (eir_len > 0) memcpy(ev->eir, eir, eir_len); -- cgit v1.2.3 From c70a7e4cc8d22cb1ce684637ef8a4bb3a80d15b7 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 1 Jul 2014 14:11:21 +0200 Subject: Bluetooth: Add support for Not Connectable flag for Device Found events The Device Found events of the management interface should indicate if it is possible to connect to a remote device or if it is broadcaster only advertising. To allow this differentation the Not Connectable flag is introduced that will be set when it is known that a device can not be connected. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_event.c | 40 ++++++++++++++++++++++++++++++++-------- 2 files changed, 33 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index ab3d4dda071c..eb0add396595 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -71,6 +71,7 @@ struct discovery_state { bdaddr_t last_adv_addr; u8 last_adv_addr_type; s8 last_adv_rssi; + u32 last_adv_flags; u8 last_adv_data[HCI_MAX_AD_LENGTH]; u8 last_adv_data_len; }; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index a4854a5d9298..8097559ebb48 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1089,13 +1089,15 @@ static void clear_pending_adv_report(struct hci_dev *hdev) } static void store_pending_adv_report(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 bdaddr_type, s8 rssi, u8 *data, u8 len) + u8 bdaddr_type, s8 rssi, u32 flags, + u8 *data, u8 len) { struct discovery_state *d = &hdev->discovery; bacpy(&d->last_adv_addr, bdaddr); d->last_adv_addr_type = bdaddr_type; d->last_adv_rssi = rssi; + d->last_adv_flags = flags; memcpy(d->last_adv_data, data, len); d->last_adv_data_len = len; } @@ -1132,7 +1134,7 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK, d->last_adv_addr_type, NULL, - d->last_adv_rssi, 0, + d->last_adv_rssi, d->last_adv_flags, d->last_adv_data, d->last_adv_data_len, NULL, 0); } @@ -4209,6 +4211,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, { struct discovery_state *d = &hdev->discovery; bool match; + u32 flags; /* Passive scanning shouldn't trigger any device found events */ if (hdev->le_scan_type == LE_SCAN_PASSIVE) { @@ -4217,6 +4220,27 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, return; } + /* When receiving non-connectable or scannable undirected + * advertising reports, this means that the remote device is + * not connectable and then clearly indicate this in the + * device found event. + * + * When receiving a scan response, then there is no way to + * know if the remote device is connectable or not. However + * since scan responses are merged with a previously seen + * advertising report, the flags field from that report + * will be used. + * + * In the really unlikely case that a controller get confused + * and just sends a scan response event, then it is marked as + * not connectable as well. + */ + if (type == LE_ADV_NONCONN_IND || type == LE_ADV_SCAN_IND || + type == LE_ADV_SCAN_RSP) + flags = MGMT_DEV_FOUND_NOT_CONNECTABLE; + else + flags = 0; + /* If there's nothing pending either store the data from this * event or send an immediate device found event if the data * should not be stored for later. @@ -4227,12 +4251,12 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, */ if (type == LE_ADV_IND || type == LE_ADV_SCAN_IND) { store_pending_adv_report(hdev, bdaddr, bdaddr_type, - rssi, data, len); + rssi, flags, data, len); return; } mgmt_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL, - rssi, 0, data, len, NULL, 0); + rssi, flags, data, len, NULL, 0); return; } @@ -4249,7 +4273,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, if (!match) mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK, d->last_adv_addr_type, NULL, - d->last_adv_rssi, 0, + d->last_adv_rssi, d->last_adv_flags, d->last_adv_data, d->last_adv_data_len, NULL, 0); @@ -4258,7 +4282,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, */ if (type == LE_ADV_IND || type == LE_ADV_SCAN_IND) { store_pending_adv_report(hdev, bdaddr, bdaddr_type, - rssi, data, len); + rssi, flags, data, len); return; } @@ -4267,7 +4291,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, */ clear_pending_adv_report(hdev); mgmt_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL, - rssi, 0, data, len, NULL, 0); + rssi, flags, data, len, NULL, 0); return; } @@ -4276,7 +4300,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, * sending a merged device found event. */ mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK, - d->last_adv_addr_type, NULL, rssi, 0, + d->last_adv_addr_type, NULL, rssi, d->last_adv_flags, d->last_adv_data, d->last_adv_data_len, data, len); clear_pending_adv_report(hdev); } -- cgit v1.2.3 From fe59a05f941dbeb14316449be42d059761bed62c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 1 Jul 2014 19:14:12 +0300 Subject: Bluetooth: Add flag to track STK encryption There are certain subtle differences in behavior when we're encrypted with the STK, such as allowing re-encryption even though the security level stays the same. Because of this, add a flag to track whether we're encrypted with an STK or not. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_event.c | 3 +++ net/bluetooth/smp.c | 4 ++++ 3 files changed, 8 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index eb0add396595..9078da681f16 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -536,6 +536,7 @@ enum { HCI_CONN_AUTH, HCI_CONN_SECURE, HCI_CONN_FIPS, + HCI_CONN_STK_ENCRYPT, }; static inline bool hci_conn_ssp_enabled(struct hci_conn *conn) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 8097559ebb48..b0b760dd66a3 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4365,8 +4365,11 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb) * using a distributed LTK. */ if (ltk->type == SMP_STK) { + set_bit(HCI_CONN_STK_ENCRYPT, &conn->flags); list_del(<k->list); kfree(ltk); + } else { + clear_bit(HCI_CONN_STK_ENCRYPT, &conn->flags); } hci_dev_unlock(hdev); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 6ce7785a2708..68e6f245581c 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -538,6 +538,7 @@ static u8 smp_random(struct smp_chan *smp) hci_le_start_enc(hcon, ediv, rand, stk); hcon->enc_key_size = smp->enc_key_size; + set_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags); } else { u8 stk[16], auth; __le64 rand = 0; @@ -856,6 +857,9 @@ static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level) hci_le_start_enc(hcon, key->ediv, key->rand, key->val); hcon->enc_key_size = key->enc_size; + /* We never store STKs for master role, so clear this flag */ + clear_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags); + return true; } -- cgit v1.2.3 From 2a8357f2393d89a34b5a77051d29951af9646406 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 1 Jul 2014 22:09:47 +0300 Subject: Bluetooth: Fix redundant device (un)blocked events For the Block/Unblock Device mgmt commands we should only emit the Blocked/Unblocked events on any socket except for the one which received the command. The code was previously incorrectly trying to look up a non-existent pending command and thereby ending up not skipping the command socket for the event. We can simplify the code a lot by simply sending the event directly from the command handler functions. We have the reference to the command socket available there which makes it easy to pass to the mgmt_event function for skipping. The only notable side-effect of this is that the old blacklisting ioctl's no-longer cause mgmt events to be emitted, however as user space versions using these ioctl's are not mgmt-aware this is acceptable. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 -- net/bluetooth/hci_core.c | 4 ++-- net/bluetooth/mgmt.c | 50 +++++++++++++--------------------------- 3 files changed, 18 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 9078da681f16..3404f9bd2da0 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1325,8 +1325,6 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, s8 rssi, u8 *name, u8 name_len); void mgmt_discovering(struct hci_dev *hdev, u8 discovering); -int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); -int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent); void mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk); void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk, diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 25ed6d3de410..72eb41424d04 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3275,7 +3275,7 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) list_add(&entry->list, &hdev->blacklist); - return mgmt_device_blocked(hdev, bdaddr, type); + return 0; } int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) @@ -3294,7 +3294,7 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) list_del(&entry->list); kfree(entry); - return mgmt_device_unblocked(hdev, bdaddr, type); + return 0; } struct bdaddr_list *hci_white_list_lookup(struct hci_dev *hdev, diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 408468c07a8a..ba5e215a7561 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3773,11 +3773,16 @@ static int block_device(struct sock *sk, struct hci_dev *hdev, void *data, hci_dev_lock(hdev); err = hci_blacklist_add(hdev, &cp->addr.bdaddr, cp->addr.type); - if (err < 0) + if (err < 0) { status = MGMT_STATUS_FAILED; - else - status = MGMT_STATUS_SUCCESS; + goto done; + } + + mgmt_event(MGMT_EV_DEVICE_BLOCKED, hdev, &cp->addr, sizeof(cp->addr), + sk); + status = MGMT_STATUS_SUCCESS; +done: err = cmd_complete(sk, hdev->id, MGMT_OP_BLOCK_DEVICE, status, &cp->addr, sizeof(cp->addr)); @@ -3803,11 +3808,16 @@ static int unblock_device(struct sock *sk, struct hci_dev *hdev, void *data, hci_dev_lock(hdev); err = hci_blacklist_del(hdev, &cp->addr.bdaddr, cp->addr.type); - if (err < 0) + if (err < 0) { status = MGMT_STATUS_INVALID_PARAMS; - else - status = MGMT_STATUS_SUCCESS; + goto done; + } + + mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &cp->addr, sizeof(cp->addr), + sk); + status = MGMT_STATUS_SUCCESS; +done: err = cmd_complete(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE, status, &cp->addr, sizeof(cp->addr)); @@ -6346,34 +6356,6 @@ void mgmt_discovering(struct hci_dev *hdev, u8 discovering) mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL); } -int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) -{ - struct pending_cmd *cmd; - struct mgmt_ev_device_blocked ev; - - cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, hdev); - - bacpy(&ev.addr.bdaddr, bdaddr); - ev.addr.type = type; - - return mgmt_event(MGMT_EV_DEVICE_BLOCKED, hdev, &ev, sizeof(ev), - cmd ? cmd->sk : NULL); -} - -int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) -{ - struct pending_cmd *cmd; - struct mgmt_ev_device_unblocked ev; - - cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, hdev); - - bacpy(&ev.addr.bdaddr, bdaddr); - ev.addr.type = type; - - return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &ev, sizeof(ev), - cmd ? cmd->sk : NULL); -} - static void adv_enable_complete(struct hci_dev *hdev, u8 status) { BT_DBG("%s status %u", hdev->name, status); -- cgit v1.2.3 From 8e75b46a4f5d2c71b2f3ea632df1b15502514948 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Tue, 1 Jul 2014 18:10:08 -0300 Subject: Bluetooth: Connection Parameter Update Procedure This patch adds support for LE Connection Parameters Request Link Layer control procedure introduced in Core spec 4.1. This procedure allows a Peripheral or Central to update the Link Layer connection parameters of an established connection. Regarding the acceptance of connection parameters, the LL procedure follows the same approach of L2CAP procedure (see l2cap_conn_param_ update_req function). We accept any connection parameters values as long as they are within the valid range. Signed-off-by: Andre Guedes Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 28 +++++++++++++++++++++++++ net/bluetooth/hci_event.c | 50 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 58c5930dea60..95b754785a7d 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -355,6 +355,7 @@ enum { #define HCI_LK_AUTH_COMBINATION_P256 0x08 /* ---- HCI Error Codes ---- */ +#define HCI_ERROR_UNKNOWN_CONN_ID 0x02 #define HCI_ERROR_AUTH_FAILURE 0x05 #define HCI_ERROR_MEMORY_EXCEEDED 0x07 #define HCI_ERROR_CONNECTION_TIMEOUT 0x08 @@ -364,6 +365,7 @@ enum { #define HCI_ERROR_REMOTE_POWER_OFF 0x15 #define HCI_ERROR_LOCAL_HOST_TERM 0x16 #define HCI_ERROR_PAIRING_NOT_ALLOWED 0x18 +#define HCI_ERROR_INVALID_LL_PARAMS 0x1E #define HCI_ERROR_ADVERTISING_TIMEOUT 0x3c /* Flow control modes */ @@ -1305,6 +1307,23 @@ struct hci_rp_le_read_supported_states { __u8 le_states[8]; } __packed; +#define HCI_OP_LE_CONN_PARAM_REQ_REPLY 0x2020 +struct hci_cp_le_conn_param_req_reply { + __le16 handle; + __le16 interval_min; + __le16 interval_max; + __le16 latency; + __le16 timeout; + __le16 min_ce_len; + __le16 max_ce_len; +} __packed; + +#define HCI_OP_LE_CONN_PARAM_REQ_NEG_REPLY 0x2021 +struct hci_cp_le_conn_param_req_neg_reply { + __le16 handle; + __u8 reason; +} __packed; + /* ---- HCI Events ---- */ #define HCI_EV_INQUIRY_COMPLETE 0x01 @@ -1700,6 +1719,15 @@ struct hci_ev_le_ltk_req { __le16 ediv; } __packed; +#define HCI_EV_LE_REMOTE_CONN_PARAM_REQ 0x06 +struct hci_ev_le_remote_conn_param_req { + __le16 handle; + __le16 interval_min; + __le16 interval_max; + __le16 latency; + __le16 timeout; +} __packed; + /* Advertising report event types */ #define LE_ADV_IND 0x00 #define LE_ADV_DIRECT_IND 0x01 diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index b0b760dd66a3..544e2ef85d82 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4382,6 +4382,52 @@ not_found: hci_dev_unlock(hdev); } +static void send_conn_param_neg_reply(struct hci_dev *hdev, u16 handle, + u8 reason) +{ + struct hci_cp_le_conn_param_req_neg_reply cp; + + cp.handle = cpu_to_le16(handle); + cp.reason = reason; + + hci_send_cmd(hdev, HCI_OP_LE_CONN_PARAM_REQ_NEG_REPLY, sizeof(cp), + &cp); +} + +static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_ev_le_remote_conn_param_req *ev = (void *) skb->data; + struct hci_cp_le_conn_param_req_reply cp; + struct hci_conn *hcon; + u16 handle, min, max, latency, timeout; + + handle = le16_to_cpu(ev->handle); + min = le16_to_cpu(ev->interval_min); + max = le16_to_cpu(ev->interval_max); + latency = le16_to_cpu(ev->latency); + timeout = le16_to_cpu(ev->timeout); + + hcon = hci_conn_hash_lookup_handle(hdev, handle); + if (!hcon || hcon->state != BT_CONNECTED) + return send_conn_param_neg_reply(hdev, handle, + HCI_ERROR_UNKNOWN_CONN_ID); + + if (hci_check_conn_params(min, max, latency, timeout)) + return send_conn_param_neg_reply(hdev, handle, + HCI_ERROR_INVALID_LL_PARAMS); + + cp.handle = ev->handle; + cp.interval_min = ev->interval_min; + cp.interval_max = ev->interval_max; + cp.latency = ev->latency; + cp.timeout = ev->timeout; + cp.min_ce_len = 0; + cp.max_ce_len = 0; + + hci_send_cmd(hdev, HCI_OP_LE_CONN_PARAM_REQ_REPLY, sizeof(cp), &cp); +} + static void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_le_meta *le_ev = (void *) skb->data; @@ -4405,6 +4451,10 @@ static void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_le_ltk_request_evt(hdev, skb); break; + case HCI_EV_LE_REMOTE_CONN_PARAM_REQ: + hci_le_remote_conn_param_req_evt(hdev, skb); + break; + default: break; } -- cgit v1.2.3 From 662bc2e63de765bb701e3d3eca6af9fe553d72ac Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Tue, 1 Jul 2014 18:10:10 -0300 Subject: Bluetooth: Enable new LE meta event The Bluetooth 4.1 introduces a new LE meta event called "LE Remote Connection Parameter Request" event. In order to the controller sends this event to host, we should enable it during controller initialization. Signed-off-by: Andre Guedes Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 3 +++ net/bluetooth/hci_core.c | 9 +++++++++ 2 files changed, 12 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 95b754785a7d..66358af6b1fc 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -307,6 +307,9 @@ enum { #define LMP_HOST_LE_BREDR 0x04 #define LMP_HOST_SC 0x08 +/* LE features */ +#define HCI_LE_CONN_PARAM_REQ_PROC 0x02 + /* Connection modes */ #define HCI_CM_ACTIVE 0x0000 #define HCI_CM_HOLD 0x0001 diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 5788e031b869..615d0cf5e511 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1611,6 +1611,15 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt) memset(events, 0, sizeof(events)); events[0] = 0x1f; + + /* If controller supports the Connection Parameters Request + * Link Layer Procedure, enable the corresponding event. + */ + if (hdev->le_features[0] & HCI_LE_CONN_PARAM_REQ_PROC) + events[0] |= 0x20; /* LE Remote Connection + * Parameter Request + */ + hci_req_add(req, HCI_OP_LE_SET_EVENT_MASK, sizeof(events), events); -- cgit v1.2.3 From ffb5a827d5ca5aef3f3fe5d64e42f3cf7fed4fc8 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Tue, 1 Jul 2014 18:10:11 -0300 Subject: Bluetooth: Introduce "New Connection Parameter" Event This patch introduces a new Mgmt event called "New Connection Parameter". This event indicates to userspace the connection parameters values the remote device requested. The user may store these values and load them into kernel. This way, next time a connection is established to that device, the kernel will use those parameters values instead of the default ones. This event is sent when the remote device requests new connection parameters through connection parameter update procedure. This event is not sent for slave connections. Signed-off-by: Andre Guedes Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 3 +++ include/net/bluetooth/mgmt.h | 10 ++++++++++ net/bluetooth/hci_event.c | 4 ++++ net/bluetooth/l2cap_core.c | 6 +++++- net/bluetooth/mgmt.c | 19 +++++++++++++++++++ 5 files changed, 41 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 3404f9bd2da0..01fbbe20defb 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1329,6 +1329,9 @@ void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent); void mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk); void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk, bool persistent); +void mgmt_new_conn_param(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 bdaddr_type, u16 min_interval, u16 max_interval, + u16 latency, u16 timeout); void mgmt_reenable_advertising(struct hci_dev *hdev); void mgmt_smp_complete(struct hci_conn *conn, bool complete); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 2d88f361a016..3c0f29614d1b 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -615,3 +615,13 @@ struct mgmt_ev_device_added { struct mgmt_ev_device_removed { struct mgmt_addr_info addr; } __packed; + +#define MGMT_EV_NEW_CONN_PARAM 0x001c +struct mgmt_ev_new_conn_param { + struct mgmt_addr_info addr; + __u8 store_hint; + __le16 min_interval; + __le16 max_interval; + __le16 latency; + __le16 timeout; +} __packed; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 544e2ef85d82..b9d16e0ed661 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4417,6 +4417,10 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev, return send_conn_param_neg_reply(hdev, handle, HCI_ERROR_INVALID_LL_PARAMS); + if (test_bit(HCI_CONN_MASTER, &hcon->flags)) + mgmt_new_conn_param(hdev, &hcon->dst, hcon->dst_type, min, max, + latency, timeout); + cp.handle = ev->handle; cp.interval_min = ev->interval_min; cp.interval_max = ev->interval_max; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index e203a5fdf874..058b3b2b59b5 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -5249,8 +5249,12 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_PARAM_UPDATE_RSP, sizeof(rsp), &rsp); - if (!err) + if (!err) { + mgmt_new_conn_param(hcon->hdev, &hcon->dst, hcon->dst_type, + min, max, latency, to_multiplier); + hci_le_conn_update(hcon, min, max, latency, to_multiplier); + } return 0; } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index ba5e215a7561..93cfefa260d5 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -116,6 +116,7 @@ static const u16 mgmt_events[] = { MGMT_EV_NEW_CSRK, MGMT_EV_DEVICE_ADDED, MGMT_EV_DEVICE_REMOVED, + MGMT_EV_NEW_CONN_PARAM, }; #define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000) @@ -5690,6 +5691,24 @@ void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk, mgmt_event(MGMT_EV_NEW_CSRK, hdev, &ev, sizeof(ev), NULL); } +void mgmt_new_conn_param(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 bdaddr_type, u16 min_interval, u16 max_interval, + u16 latency, u16 timeout) +{ + struct mgmt_ev_new_conn_param ev; + + memset(&ev, 0, sizeof(ev)); + bacpy(&ev.addr.bdaddr, bdaddr); + ev.addr.type = link_to_bdaddr(LE_LINK, bdaddr_type); + ev.store_hint = 0x00; + ev.min_interval = cpu_to_le16(min_interval); + ev.max_interval = cpu_to_le16(max_interval); + ev.latency = cpu_to_le16(latency); + ev.timeout = cpu_to_le16(timeout); + + mgmt_event(MGMT_EV_NEW_CONN_PARAM, hdev, &ev, sizeof(ev), NULL); +} + static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data, u8 data_len) { -- cgit v1.2.3 From 24c457e27076beb2a85b4213642a6388eb88f240 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 2 Jul 2014 00:53:47 +0200 Subject: Bluetooth: Add support for hdev->set_bdaddr callback handling Some embedded controllers allow the programming of a public address and this adds vendor support for supporting OEM confguration of such addresses. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_core.c | 11 +++++++++++ 2 files changed, 13 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 01fbbe20defb..ee480a86e558 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -171,6 +171,7 @@ struct hci_dev { __u8 bus; __u8 dev_type; bdaddr_t bdaddr; + bdaddr_t public_addr; bdaddr_t random_addr; bdaddr_t static_addr; __u8 adv_addr_type; @@ -344,6 +345,7 @@ struct hci_dev { int (*setup)(struct hci_dev *hdev); int (*send)(struct hci_dev *hdev, struct sk_buff *skb); void (*notify)(struct hci_dev *hdev, unsigned int evt); + int (*set_bdaddr)(struct hci_dev *hdev, const bdaddr_t *bdaddr); }; #define HCI_PHY_HANDLE(handle) (handle & 0xff) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 615d0cf5e511..63197d70d8eb 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2249,6 +2249,17 @@ static int hci_dev_do_open(struct hci_dev *hdev) if (hdev->setup && test_bit(HCI_SETUP, &hdev->dev_flags)) ret = hdev->setup(hdev); + /* If public address change is configured, ensure that the + * address gets programmed. If the driver does not support + * changing the public address, fail the power on procedure. + */ + if (!ret && bacmp(&hdev->public_addr, BDADDR_ANY)) { + if (hdev->set_bdaddr) + ret = hdev->set_bdaddr(hdev, &hdev->public_addr); + else + ret = -EADDRNOTAVAIL; + } + if (!ret) { if (!test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks) && !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) -- cgit v1.2.3 From 373110c5d30b0944b47cddbe586069b7457f8845 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 2 Jul 2014 17:37:25 +0300 Subject: Bluetooth: Rename hci_conn_params_clear to hci_conn_params_clear_all We'll soon have specific clear functions for clearing enabled or disabled entries, so rename the function that removes everything to clear_all(). Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_core.c | 4 ++-- net/bluetooth/mgmt.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index ee480a86e558..091934bcfd84 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -861,7 +861,7 @@ struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev, int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, u8 auto_connect); void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); -void hci_conn_params_clear(struct hci_dev *hdev); +void hci_conn_params_clear_all(struct hci_dev *hdev); struct bdaddr_list *hci_pend_le_conn_lookup(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 21a4c8dfb874..e6e169007fd2 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3580,7 +3580,7 @@ void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) } /* This function requires the caller holds hdev->lock */ -void hci_conn_params_clear(struct hci_dev *hdev) +void hci_conn_params_clear_all(struct hci_dev *hdev) { struct hci_conn_params *params, *tmp; @@ -4038,7 +4038,7 @@ void hci_unregister_dev(struct hci_dev *hdev) hci_smp_irks_clear(hdev); hci_remote_oob_data_clear(hdev); hci_white_list_clear(hdev); - hci_conn_params_clear(hdev); + hci_conn_params_clear_all(hdev); hci_dev_unlock(hdev); hci_dev_put(hdev); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 93cfefa260d5..29850e76ea3c 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5104,7 +5104,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, goto unlock; } - hci_conn_params_clear(hdev); + hci_conn_params_clear_all(hdev); } err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, -- cgit v1.2.3 From 55af49a8fe85278ea244e72d2d264cf5e0941c61 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 2 Jul 2014 17:37:26 +0300 Subject: Bluetooth: Add specific connection parameter clear functions In some circumstances we'll need to either clear only the enabled parameters or only the disabled ones. This patch adds convenience functions for this purpose. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_core.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 091934bcfd84..2091e0013b8c 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -862,6 +862,8 @@ int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, u8 auto_connect); void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); void hci_conn_params_clear_all(struct hci_dev *hdev); +void hci_conn_params_clear_disabled(struct hci_dev *hdev); +void hci_conn_params_clear_enabled(struct hci_dev *hdev); struct bdaddr_list *hci_pend_le_conn_lookup(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index e6e169007fd2..7e46a7c6092f 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3579,6 +3579,38 @@ void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) BT_DBG("addr %pMR (type %u)", addr, addr_type); } +/* This function requires the caller holds hdev->lock */ +void hci_conn_params_clear_disabled(struct hci_dev *hdev) +{ + struct hci_conn_params *params, *tmp; + + list_for_each_entry_safe(params, tmp, &hdev->le_conn_params, list) { + if (params->auto_connect != HCI_AUTO_CONN_DISABLED) + continue; + list_del(¶ms->list); + kfree(params); + } + + BT_DBG("All LE disabled connection parameters were removed"); +} + +/* This function requires the caller holds hdev->lock */ +void hci_conn_params_clear_enabled(struct hci_dev *hdev) +{ + struct hci_conn_params *params, *tmp; + + list_for_each_entry_safe(params, tmp, &hdev->le_conn_params, list) { + if (params->auto_connect == HCI_AUTO_CONN_DISABLED) + continue; + list_del(¶ms->list); + kfree(params); + } + + hci_pend_le_conns_clear(hdev); + + BT_DBG("All enabled LE connection parameters were removed"); +} + /* This function requires the caller holds hdev->lock */ void hci_conn_params_clear_all(struct hci_dev *hdev) { -- cgit v1.2.3 From a3451d279f839d987cbcf25b0f3be666aef99d0b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 2 Jul 2014 17:37:27 +0300 Subject: Bluetooth: Add new auto_conn value matching mgmt action 0x00 The 0x00 action value of mgmt means "scan and report" but do not connect. This is different from HCI_AUTO_CONN_DISABLED so we need a new value for it. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 1 + net/bluetooth/mgmt.c | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 2091e0013b8c..f4a2f50f30b5 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -449,6 +449,7 @@ struct hci_conn_params { enum { HCI_AUTO_CONN_DISABLED, + HCI_AUTO_CONN_REPORT, HCI_AUTO_CONN_ALWAYS, HCI_AUTO_CONN_LINK_LOSS, } auto_connect; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 7e46a7c6092f..a3cd0bbd3518 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3547,6 +3547,7 @@ int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, switch (auto_connect) { case HCI_AUTO_CONN_DISABLED: + case HCI_AUTO_CONN_REPORT: case HCI_AUTO_CONN_LINK_LOSS: hci_pend_le_conn_del(hdev, addr, addr_type); break; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 29850e76ea3c..f7217f9eda03 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5034,7 +5034,7 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, if (cp->action) auto_conn = HCI_AUTO_CONN_ALWAYS; else - auto_conn = HCI_AUTO_CONN_DISABLED; + auto_conn = HCI_AUTO_CONN_REPORT; /* If the connection parameters don't exist for this device, * they will be created and configured with defaults. -- cgit v1.2.3 From a26f3dcff2cf5890f33d883c98d90cdfa51ed460 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 2 Jul 2014 17:37:29 +0300 Subject: Bluetooth: Add Load Connection Parameters command This patch implements the new Load Connection Parameters mgmt command that's intended to load the desired connection parameters for LE devices. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 15 +++++++++ net/bluetooth/mgmt.c | 79 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 3c0f29614d1b..5b3e8009eddd 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -449,6 +449,21 @@ struct mgmt_cp_remove_device { } __packed; #define MGMT_REMOVE_DEVICE_SIZE MGMT_ADDR_INFO_SIZE +struct mgmt_conn_param { + struct mgmt_addr_info addr; + __le16 min_interval; + __le16 max_interval; + __le16 latency; + __le16 timeout; +} __packed; + +#define MGMT_OP_LOAD_CONN_PARAM 0x0035 +struct mgmt_cp_load_conn_param { + __le16 param_count; + struct mgmt_conn_param params[0]; +} __packed; +#define MGMT_LOAD_CONN_PARAM_SIZE 2 + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 574dd9f7c39e..59bf1ac41429 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -88,6 +88,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_GET_CLOCK_INFO, MGMT_OP_ADD_DEVICE, MGMT_OP_REMOVE_DEVICE, + MGMT_OP_LOAD_CONN_PARAM, }; static const u16 mgmt_events[] = { @@ -5134,6 +5135,83 @@ unlock: return err; } +static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data, + u16 len) +{ + struct mgmt_cp_load_conn_param *cp = data; + u16 param_count, expected_len; + int i; + + if (!lmp_le_capable(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM, + MGMT_STATUS_NOT_SUPPORTED); + + param_count = __le16_to_cpu(cp->param_count); + + expected_len = sizeof(*cp) + param_count * + sizeof(struct mgmt_conn_param); + if (expected_len != len) { + BT_ERR("load_conn_param: expected %u bytes, got %u bytes", + expected_len, len); + return cmd_status(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM, + MGMT_STATUS_INVALID_PARAMS); + } + + BT_DBG("%s param_count %u", hdev->name, param_count); + + hci_dev_lock(hdev); + + hci_conn_params_clear_disabled(hdev); + + for (i = 0; i < param_count; i++) { + struct mgmt_conn_param *param = &cp->params[i]; + struct hci_conn_params *hci_param; + u16 min, max, latency, timeout; + u8 addr_type; + + BT_DBG("Adding %pMR (type %u)", ¶m->addr.bdaddr, + param->addr.type); + + if (param->addr.type == BDADDR_LE_PUBLIC) { + addr_type = ADDR_LE_DEV_PUBLIC; + } else if (param->addr.type == BDADDR_LE_RANDOM) { + addr_type = ADDR_LE_DEV_RANDOM; + } else { + BT_ERR("Ignoring invalid connection parameters"); + continue; + } + + min = le16_to_cpu(param->min_interval); + max = le16_to_cpu(param->max_interval); + latency = le16_to_cpu(param->latency); + timeout = le16_to_cpu(param->timeout); + + BT_DBG("min 0x%04x max 0x%04x latency 0x%04x timeout 0x%04x", + min, max, latency, timeout); + + if (hci_check_conn_params(min, max, latency, timeout) < 0) { + BT_ERR("Ignoring invalid connection parameters"); + continue; + } + + hci_param = hci_conn_params_add(hdev, ¶m->addr.bdaddr, + addr_type); + if (!hci_param) { + BT_ERR("Failed to add connection parameters"); + continue; + } + + hci_param->conn_min_interval = min; + hci_param->conn_max_interval = max; + hci_param->conn_latency = latency; + hci_param->supervision_timeout = timeout; + } + + hci_dev_unlock(hdev); + + return cmd_complete(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM, 0, NULL, 0); +} + static const struct mgmt_handler { int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len); @@ -5193,6 +5271,7 @@ static const struct mgmt_handler { { get_clock_info, false, MGMT_GET_CLOCK_INFO_SIZE }, { add_device, false, MGMT_ADD_DEVICE_SIZE }, { remove_device, false, MGMT_REMOVE_DEVICE_SIZE }, + { load_conn_param, true, MGMT_LOAD_CONN_PARAM_SIZE }, }; int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) -- cgit v1.2.3 From 7d6ca6939cb2f701204317cbab15af1b98f7f501 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 2 Jul 2014 17:37:31 +0300 Subject: Bluetooth: Make hci_le_conn_update return the store hint The caller of hci_le_conn_update is directly interested in knowing what the best value is for the store_hint parameter of the corresponding mgmt event. Since hci_le_conn_update knows whether there were stored parameters that were updated or not we can have it return an initial store_hint value to the caller. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 4 ++-- net/bluetooth/hci_conn.c | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index f4a2f50f30b5..32c8e51f6b26 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1368,8 +1368,8 @@ struct hci_sec_filter { #define hci_req_lock(d) mutex_lock(&d->req_lock) #define hci_req_unlock(d) mutex_unlock(&d->req_lock) -void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, - u16 latency, u16 to_multiplier); +u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, + u16 to_multiplier); void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand, __u8 ltk[16]); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index d00aaf976efc..0d579d036833 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -213,8 +213,8 @@ bool hci_setup_sync(struct hci_conn *conn, __u16 handle) return true; } -void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, - u16 latency, u16 to_multiplier) +u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, + u16 to_multiplier) { struct hci_dev *hdev = conn->hdev; struct hci_conn_params *params; @@ -242,6 +242,11 @@ void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, cp.max_ce_len = cpu_to_le16(0x0000); hci_send_cmd(hdev, HCI_OP_LE_CONN_UPDATE, sizeof(cp), &cp); + + if (params) + return 0x01; + + return 0x00; } void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand, -- cgit v1.2.3 From f4869e2adb7ab9d09a9335b4e26a63ec413f2c6f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 2 Jul 2014 17:37:32 +0300 Subject: Bluetooth: Pass store hint to mgmt_new_conn_param The calling functions of mgmt_new_conn_param have more information about the parameters, such as whether the kernel is tracking them or not. It makes therefore sense to have them pass an initial store_hint value to the mgmt_new_conn_param function. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 4 ++-- net/bluetooth/hci_event.c | 8 ++++++-- net/bluetooth/l2cap_core.c | 8 ++++++-- net/bluetooth/mgmt.c | 6 +++--- 4 files changed, 17 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 32c8e51f6b26..d0bca13843ce 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1335,8 +1335,8 @@ void mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk); void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk, bool persistent); void mgmt_new_conn_param(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 bdaddr_type, u16 min_interval, u16 max_interval, - u16 latency, u16 timeout); + u8 bdaddr_type, u8 store_hint, u16 min_interval, + u16 max_interval, u16 latency, u16 timeout); void mgmt_reenable_advertising(struct hci_dev *hdev); void mgmt_smp_complete(struct hci_conn *conn, bool complete); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 3ad2576fd3f1..846f6a6af881 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4431,6 +4431,7 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev, if (test_bit(HCI_CONN_MASTER, &hcon->flags)) { struct hci_conn_params *params; + u8 store_hint; hci_dev_lock(hdev); @@ -4441,12 +4442,15 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev, params->conn_max_interval = max; params->conn_latency = latency; params->supervision_timeout = timeout; + store_hint = 0x01; + } else{ + store_hint = 0x00; } hci_dev_unlock(hdev); - mgmt_new_conn_param(hdev, &hcon->dst, hcon->dst_type, min, max, - latency, timeout); + mgmt_new_conn_param(hdev, &hcon->dst, hcon->dst_type, + store_hint, min, max, latency, timeout); } cp.handle = ev->handle; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index a6e276204ae9..dbef22d644e2 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -5250,10 +5250,14 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, sizeof(rsp), &rsp); if (!err) { + u8 store_hint; + + store_hint = hci_le_conn_update(hcon, min, max, latency, + to_multiplier); mgmt_new_conn_param(hcon->hdev, &hcon->dst, hcon->dst_type, - min, max, latency, to_multiplier); + store_hint, min, max, latency, + to_multiplier); - hci_le_conn_update(hcon, min, max, latency, to_multiplier); } return 0; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 59bf1ac41429..fb1aa0cac137 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5790,15 +5790,15 @@ void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk, } void mgmt_new_conn_param(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 bdaddr_type, u16 min_interval, u16 max_interval, - u16 latency, u16 timeout) + u8 bdaddr_type, u8 store_hint, u16 min_interval, + u16 max_interval, u16 latency, u16 timeout) { struct mgmt_ev_new_conn_param ev; memset(&ev, 0, sizeof(ev)); bacpy(&ev.addr.bdaddr, bdaddr); ev.addr.type = link_to_bdaddr(LE_LINK, bdaddr_type); - ev.store_hint = 0x00; + ev.store_hint = store_hint; ev.min_interval = cpu_to_le16(min_interval); ev.max_interval = cpu_to_le16(max_interval); ev.latency = cpu_to_le16(latency); -- cgit v1.2.3 From c46245b3efce80884acf65c01443582aec1f31ed Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 2 Jul 2014 17:37:33 +0300 Subject: Bluetooth: Make is_identity_address a global function There are more places that can take advantage of is_identity_address() besides hci_core.c. This patch moves the function to hci_core.h and gives it the appropriate hci_ prefix. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 12 ++++++++++++ net/bluetooth/hci_core.c | 14 +------------- 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index d0bca13843ce..92f1bad6e22d 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1179,6 +1179,18 @@ static inline bool hci_bdaddr_is_rpa(bdaddr_t *bdaddr, u8 addr_type) return false; } +static inline bool hci_is_identity_address(bdaddr_t *addr, u8 addr_type) +{ + if (addr_type == ADDR_LE_DEV_PUBLIC) + return true; + + /* Check for Random Static address type */ + if ((addr->b[5] & 0xc0) == 0xc0) + return true; + + return false; +} + static inline struct smp_irk *hci_get_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type) { diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index a3cd0bbd3518..1fff3d890f41 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3412,18 +3412,6 @@ static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type) return true; } -static bool is_identity_address(bdaddr_t *addr, u8 addr_type) -{ - if (addr_type == ADDR_LE_DEV_PUBLIC) - return true; - - /* Check for Random Static address type */ - if ((addr->b[5] & 0xc0) == 0xc0) - return true; - - return false; -} - /* This function requires the caller holds hdev->lock */ struct bdaddr_list *hci_pend_le_conn_lookup(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) @@ -3504,7 +3492,7 @@ struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev, { struct hci_conn_params *params; - if (!is_identity_address(addr, addr_type)) + if (!hci_is_identity_address(addr, addr_type)) return NULL; params = hci_conn_params_lookup(hdev, addr, addr_type); -- cgit v1.2.3 From 4a964404c08fed64d1afd8b0af1e7f2b8f7ae90e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 2 Jul 2014 19:10:33 +0200 Subject: Bluetooth: Introduce unconfigured controller state With the new unconfigured controller state it is possible to provide a fully functional HCI transport, but disable the higher level operations that would normally happen. This way userspace can try to configure the controller before releases the unconfigured state. The internal state is represented by HCI_UNCONFIGURED. This replaces the HCI_QUIRK_RAW_DEVICE quirk as internal state representation. This is now a real state and drivers can use the quirk to actually trigger this state. In the future this will allow a more fine grained switching from unconfigured state to configured state for controller inititialization. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 1 + net/bluetooth/hci_core.c | 43 ++++++++++++++++++++++++++----------------- net/bluetooth/hci_sock.c | 8 ++++---- net/bluetooth/mgmt.c | 6 +++--- 4 files changed, 34 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 66358af6b1fc..606a9b1466a3 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -127,6 +127,7 @@ enum { HCI_KEEP_DEBUG_KEYS, HCI_USE_DEBUG_KEYS, HCI_UNREGISTER, + HCI_UNCONFIGURED, HCI_USER_CHANNEL, HCI_LE_SCAN, diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 1fff3d890f41..395b014ad0e8 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2116,7 +2116,7 @@ int hci_inquiry(void __user *arg) goto done; } - if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) { + if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { err = -EOPNOTSUPP; goto done; } @@ -2261,7 +2261,7 @@ static int hci_dev_do_open(struct hci_dev *hdev) } if (!ret) { - if (!test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks) && + if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) && !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) ret = __hci_init(hdev); } @@ -2274,6 +2274,7 @@ static int hci_dev_do_open(struct hci_dev *hdev) set_bit(HCI_UP, &hdev->flags); hci_notify(hdev, HCI_DEV_UP); if (!test_bit(HCI_SETUP, &hdev->dev_flags) && + !test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) && !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags) && hdev->dev_type == HCI_BREDR) { hci_dev_lock(hdev); @@ -2317,7 +2318,7 @@ int hci_dev_open(__u16 dev) if (!hdev) return -ENODEV; - /* Devices that are marked for raw-only usage can only be powered + /* Devices that are marked as unconfigured can only be powered * up as user channel. Trying to bring them up as normal devices * will result into a failure. Only user channel operation is * possible. @@ -2326,7 +2327,7 @@ int hci_dev_open(__u16 dev) * HCI_USER_CHANNEL will be set first before attempting to * open the device. */ - if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks) && + if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) && !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { err = -EOPNOTSUPP; goto done; @@ -2401,8 +2402,8 @@ static int hci_dev_do_close(struct hci_dev *hdev) /* Reset device */ skb_queue_purge(&hdev->cmd_q); atomic_set(&hdev->cmd_cnt, 1); - if (!test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks) && - !test_bit(HCI_AUTO_OFF, &hdev->dev_flags) && + if (!test_bit(HCI_AUTO_OFF, &hdev->dev_flags) && + !test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) && test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks)) { set_bit(HCI_INIT, &hdev->flags); __hci_req_sync(hdev, hci_reset_req, 0, HCI_CMD_TIMEOUT); @@ -2501,7 +2502,7 @@ int hci_dev_reset(__u16 dev) goto done; } - if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) { + if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { ret = -EOPNOTSUPP; goto done; } @@ -2543,7 +2544,7 @@ int hci_dev_reset_stat(__u16 dev) goto done; } - if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) { + if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { ret = -EOPNOTSUPP; goto done; } @@ -2573,7 +2574,7 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg) goto done; } - if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) { + if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { err = -EOPNOTSUPP; goto done; } @@ -2791,6 +2792,7 @@ static void hci_power_on(struct work_struct *work) * valid, it is important to turn the device back off. */ if (test_bit(HCI_RFKILLED, &hdev->dev_flags) || + test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) || (hdev->dev_type == HCI_BREDR && !bacmp(&hdev->bdaddr, BDADDR_ANY) && !bacmp(&hdev->static_addr, BDADDR_ANY))) { @@ -2802,7 +2804,15 @@ static void hci_power_on(struct work_struct *work) } if (test_and_clear_bit(HCI_SETUP, &hdev->dev_flags)) { - if (!test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) + /* For unconfigured devices, set the HCI_RAW flag + * so that userspace can easily identify them. + * + * If the device is fully configured and ready for + * operation, announce it via management interface. + */ + if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) + set_bit(HCI_RAW, &hdev->flags); + else mgmt_index_added(hdev); } } @@ -3974,12 +3984,11 @@ int hci_register_dev(struct hci_dev *hdev) list_add(&hdev->list, &hci_dev_list); write_unlock(&hci_dev_list_lock); - /* Devices that are marked for raw-only usage need to set - * the HCI_RAW flag to indicate that only user channel is - * supported. + /* Devices that are marked for raw-only usage are unconfigured + * and should not be included in normal operation. */ if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) - set_bit(HCI_RAW, &hdev->flags); + set_bit(HCI_UNCONFIGURED, &hdev->dev_flags); hci_notify(hdev, HCI_DEV_REG); hci_dev_hold(hdev); @@ -4024,7 +4033,7 @@ void hci_unregister_dev(struct hci_dev *hdev) if (!test_bit(HCI_INIT, &hdev->flags) && !test_bit(HCI_SETUP, &hdev->dev_flags) && - !test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) { + !test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { hci_dev_lock(hdev); mgmt_index_removed(hdev); hci_dev_unlock(hdev); @@ -4788,7 +4797,7 @@ static inline int __get_blocks(struct hci_dev *hdev, struct sk_buff *skb) static void __check_timeout(struct hci_dev *hdev, unsigned int cnt) { - if (!test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) { + if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { /* ACL tx timeout must be longer than maximum * link supervision timeout (40.9 seconds) */ if (!cnt && time_after(jiffies, hdev->acl_last_tx + @@ -4971,7 +4980,7 @@ static void hci_sched_le(struct hci_dev *hdev) if (!hci_conn_num(hdev, LE_LINK)) return; - if (!test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) { + if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { /* LE tx timeout must be longer than maximum * link supervision timeout (40.9 seconds) */ if (!hdev->le_cnt && hdev->le_pkts && diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 54e4e8fd5d97..db9610323d4d 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -453,7 +453,7 @@ static int hci_sock_release(struct socket *sock) if (hdev) { if (hci_pi(sk)->channel == HCI_CHANNEL_USER) { - if (!test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) + if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) mgmt_index_added(hdev); clear_bit(HCI_USER_CHANNEL, &hdev->dev_flags); hci_dev_close(hdev->id); @@ -518,7 +518,7 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd, if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) return -EBUSY; - if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) + if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) return -EOPNOTSUPP; if (hdev->dev_type != HCI_BREDR) @@ -706,13 +706,13 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, goto done; } - if (!test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) + if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) mgmt_index_removed(hdev); err = hci_dev_open(hdev->id); if (err) { clear_bit(HCI_USER_CHANNEL, &hdev->dev_flags); - if (!test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) + if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) mgmt_index_added(hdev); hci_dev_put(hdev); goto done; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 50a0a3ec50b0..1ab98980054c 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -353,7 +353,7 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data, if (test_bit(HCI_USER_CHANNEL, &d->dev_flags)) continue; - if (test_bit(HCI_QUIRK_RAW_DEVICE, &d->quirks)) + if (test_bit(HCI_UNCONFIGURED, &d->dev_flags)) continue; if (d->dev_type == HCI_BREDR) { @@ -5317,8 +5317,8 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) } if (test_bit(HCI_SETUP, &hdev->dev_flags) || - test_bit(HCI_USER_CHANNEL, &hdev->dev_flags) || - test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) { + test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) || + test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { err = cmd_status(sk, index, opcode, MGMT_STATUS_INVALID_INDEX); goto done; -- cgit v1.2.3 From 0602a8adc3ce3f592d03df426c92d1f36229403c Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 2 Jul 2014 21:30:54 +0200 Subject: Bluetooth: Add support for Unconfigured Index Added events When a controller is in unconfigured state it is currently hidden from the management interface. This change now announces the new controller with an Unconfigured Index Added event and allows clients to easily detect the controller. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 2 ++ net/bluetooth/hci_core.c | 17 ++++++++++------- net/bluetooth/hci_sock.c | 9 +++------ net/bluetooth/mgmt.c | 12 +++++++++++- 4 files changed, 26 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 5b3e8009eddd..7da29fd748d8 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -640,3 +640,5 @@ struct mgmt_ev_new_conn_param { __le16 latency; __le16 timeout; } __packed; + +#define MGMT_EV_UNCONF_INDEX_ADDED 0x001d diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 395b014ad0e8..df25a8329ecc 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2806,14 +2806,18 @@ static void hci_power_on(struct work_struct *work) if (test_and_clear_bit(HCI_SETUP, &hdev->dev_flags)) { /* For unconfigured devices, set the HCI_RAW flag * so that userspace can easily identify them. - * - * If the device is fully configured and ready for - * operation, announce it via management interface. */ if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) set_bit(HCI_RAW, &hdev->flags); - else - mgmt_index_added(hdev); + + /* For fully configured devices, this will send + * the Index Added event. For unconfigured devices, + * it will send Unconfigued Index Added event. + * + * Devices with HCI_QUIRK_RAW_DEVICE are ignored + * and no event will be send. + */ + mgmt_index_added(hdev); } } @@ -4032,8 +4036,7 @@ void hci_unregister_dev(struct hci_dev *hdev) cancel_work_sync(&hdev->power_on); if (!test_bit(HCI_INIT, &hdev->flags) && - !test_bit(HCI_SETUP, &hdev->dev_flags) && - !test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { + !test_bit(HCI_SETUP, &hdev->dev_flags)) { hci_dev_lock(hdev); mgmt_index_removed(hdev); hci_dev_unlock(hdev); diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index db9610323d4d..ba13ad8e25c6 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -453,8 +453,7 @@ static int hci_sock_release(struct socket *sock) if (hdev) { if (hci_pi(sk)->channel == HCI_CHANNEL_USER) { - if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) - mgmt_index_added(hdev); + mgmt_index_added(hdev); clear_bit(HCI_USER_CHANNEL, &hdev->dev_flags); hci_dev_close(hdev->id); } @@ -706,14 +705,12 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, goto done; } - if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) - mgmt_index_removed(hdev); + mgmt_index_removed(hdev); err = hci_dev_open(hdev->id); if (err) { clear_bit(HCI_USER_CHANNEL, &hdev->dev_flags); - if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) - mgmt_index_added(hdev); + mgmt_index_added(hdev); hci_dev_put(hdev); goto done; } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 1ab98980054c..ab70d5858db9 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -118,6 +118,7 @@ static const u16 mgmt_events[] = { MGMT_EV_DEVICE_ADDED, MGMT_EV_DEVICE_REMOVED, MGMT_EV_NEW_CONN_PARAM, + MGMT_EV_UNCONF_INDEX_ADDED, }; #define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000) @@ -5373,7 +5374,13 @@ void mgmt_index_added(struct hci_dev *hdev) if (hdev->dev_type != HCI_BREDR) return; - mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL); + if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) + return; + + if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) + mgmt_event(MGMT_EV_UNCONF_INDEX_ADDED, hdev, NULL, 0, NULL); + else + mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL); } void mgmt_index_removed(struct hci_dev *hdev) @@ -5383,6 +5390,9 @@ void mgmt_index_removed(struct hci_dev *hdev) if (hdev->dev_type != HCI_BREDR) return; + if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) + return; + mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL); -- cgit v1.2.3 From edd3896bc41059fc064c4ec76da004a57203d88e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 2 Jul 2014 21:30:55 +0200 Subject: Bluetooth: Add support for Unconfigured Index Removed events When a controller in an unconfigured state gets removed, then send Unconfigured Index Removed events. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 2 ++ net/bluetooth/mgmt.c | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 7da29fd748d8..651993213bd9 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -642,3 +642,5 @@ struct mgmt_ev_new_conn_param { } __packed; #define MGMT_EV_UNCONF_INDEX_ADDED 0x001d + +#define MGMT_EV_UNCONF_INDEX_REMOVED 0x001e diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index ab70d5858db9..1a78d26b0049 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -119,6 +119,7 @@ static const u16 mgmt_events[] = { MGMT_EV_DEVICE_REMOVED, MGMT_EV_NEW_CONN_PARAM, MGMT_EV_UNCONF_INDEX_ADDED, + MGMT_EV_UNCONF_INDEX_REMOVED, }; #define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000) @@ -5395,7 +5396,10 @@ void mgmt_index_removed(struct hci_dev *hdev) mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); - mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL); + if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) + mgmt_event(MGMT_EV_UNCONF_INDEX_REMOVED, hdev, NULL, 0, NULL); + else + mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL); } /* This function requires the caller holds hdev->lock */ -- cgit v1.2.3 From 73d1df2a7a1036a1f000e5f0ece6ade3e082b854 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 2 Jul 2014 22:10:52 +0200 Subject: Bluetooth: Add support for Read Unconfigured Index List command This command allows to get the list of currently known controller that are in unconfigured state. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 7 ++++ net/bluetooth/mgmt.c | 91 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 88 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 651993213bd9..e0786cfa5490 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -464,6 +464,13 @@ struct mgmt_cp_load_conn_param { } __packed; #define MGMT_LOAD_CONN_PARAM_SIZE 2 +#define MGMT_OP_READ_UNCONF_INDEX_LIST 0x0036 +#define MGMT_READ_UNCONF_INDEX_LIST_SIZE 0 +struct mgmt_rp_read_unconf_index_list { + __le16 num_controllers; + __le16 index[0]; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 1a78d26b0049..325bb8136d2c 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -89,6 +89,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_ADD_DEVICE, MGMT_OP_REMOVE_DEVICE, MGMT_OP_LOAD_CONN_PARAM, + MGMT_OP_READ_UNCONF_INDEX_LIST, }; static const u16 mgmt_events[] = { @@ -336,7 +337,8 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data, count = 0; list_for_each_entry(d, &hci_dev_list, list) { - if (d->dev_type == HCI_BREDR) + if (d->dev_type == HCI_BREDR && + !test_bit(HCI_UNCONFIGURED, &d->dev_flags)) count++; } @@ -349,16 +351,18 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data, count = 0; list_for_each_entry(d, &hci_dev_list, list) { - if (test_bit(HCI_SETUP, &d->dev_flags)) + if (test_bit(HCI_SETUP, &d->dev_flags) || + test_bit(HCI_USER_CHANNEL, &d->dev_flags)) continue; - if (test_bit(HCI_USER_CHANNEL, &d->dev_flags)) - continue; - - if (test_bit(HCI_UNCONFIGURED, &d->dev_flags)) + /* Devices marked as raw-only are neither configured + * nor unconfigured controllers. + */ + if (test_bit(HCI_QUIRK_RAW_DEVICE, &d->quirks)) continue; - if (d->dev_type == HCI_BREDR) { + if (d->dev_type == HCI_BREDR && + !test_bit(HCI_UNCONFIGURED, &d->dev_flags)) { rp->index[count++] = cpu_to_le16(d->id); BT_DBG("Added hci%u", d->id); } @@ -377,6 +381,65 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data, return err; } +static int read_unconf_index_list(struct sock *sk, struct hci_dev *hdev, + void *data, u16 data_len) +{ + struct mgmt_rp_read_unconf_index_list *rp; + struct hci_dev *d; + size_t rp_len; + u16 count; + int err; + + BT_DBG("sock %p", sk); + + read_lock(&hci_dev_list_lock); + + count = 0; + list_for_each_entry(d, &hci_dev_list, list) { + if (d->dev_type == HCI_BREDR && + test_bit(HCI_UNCONFIGURED, &d->dev_flags)) + count++; + } + + rp_len = sizeof(*rp) + (2 * count); + rp = kmalloc(rp_len, GFP_ATOMIC); + if (!rp) { + read_unlock(&hci_dev_list_lock); + return -ENOMEM; + } + + count = 0; + list_for_each_entry(d, &hci_dev_list, list) { + if (test_bit(HCI_SETUP, &d->dev_flags) || + test_bit(HCI_USER_CHANNEL, &d->dev_flags)) + continue; + + /* Devices marked as raw-only are neither configured + * nor unconfigured controllers. + */ + if (test_bit(HCI_QUIRK_RAW_DEVICE, &d->quirks)) + continue; + + if (d->dev_type == HCI_BREDR && + test_bit(HCI_UNCONFIGURED, &d->dev_flags)) { + rp->index[count++] = cpu_to_le16(d->id); + BT_DBG("Added hci%u", d->id); + } + } + + rp->num_controllers = cpu_to_le16(count); + rp_len = sizeof(*rp) + (2 * count); + + read_unlock(&hci_dev_list_lock); + + err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_UNCONF_INDEX_LIST, + 0, rp, rp_len); + + kfree(rp); + + return err; +} + static u32 get_supported_settings(struct hci_dev *hdev) { u32 settings = 0; @@ -5273,7 +5336,8 @@ static const struct mgmt_handler { { get_clock_info, false, MGMT_GET_CLOCK_INFO_SIZE }, { add_device, false, MGMT_ADD_DEVICE_SIZE }, { remove_device, false, MGMT_REMOVE_DEVICE_SIZE }, - { load_conn_param, true, MGMT_LOAD_CONN_PARAM_SIZE }, + { load_conn_param, true, MGMT_LOAD_CONN_PARAM_SIZE }, + { read_unconf_index_list, false, MGMT_READ_UNCONF_INDEX_LIST_SIZE }, }; int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) @@ -5335,8 +5399,15 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) goto done; } - if ((hdev && opcode < MGMT_OP_READ_INFO) || - (!hdev && opcode >= MGMT_OP_READ_INFO)) { + if (hdev && (opcode <= MGMT_OP_READ_INDEX_LIST || + opcode == MGMT_OP_READ_UNCONF_INDEX_LIST)) { + err = cmd_status(sk, index, opcode, + MGMT_STATUS_INVALID_INDEX); + goto done; + } + + if (!hdev && (opcode > MGMT_OP_READ_INDEX_LIST && + opcode != MGMT_OP_READ_UNCONF_INDEX_LIST)) { err = cmd_status(sk, index, opcode, MGMT_STATUS_INVALID_INDEX); goto done; -- cgit v1.2.3 From 851efca8387e10a25ca259f7efcc47819b19bff9 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 2 Jul 2014 22:42:00 +0300 Subject: Bluetooth: Track number of added devices with HCI_AUTO_CONN_REPORT To be able to make the right choice of whether to start passive scanning or to send out a mgmt_device_found event we need to know if there are any devices in the le_conn_params list with the auto_connect value set to HCI_AUTO_CONN_REPORT. This patch adds a counter for this kind of devices. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 92f1bad6e22d..b5f4405b41c2 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -312,6 +312,7 @@ struct hci_dev { struct list_head le_white_list; struct list_head le_conn_params; struct list_head pend_le_conns; + unsigned int pend_le_reports; struct hci_dev_stats stat; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index df25a8329ecc..8e0061f72dd1 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3545,20 +3545,28 @@ int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, if (!params) return -EIO; - params->auto_connect = auto_connect; + if (params->auto_connect == HCI_AUTO_CONN_REPORT && + auto_connect != HCI_AUTO_CONN_REPORT) + hdev->pend_le_reports--; switch (auto_connect) { case HCI_AUTO_CONN_DISABLED: - case HCI_AUTO_CONN_REPORT: case HCI_AUTO_CONN_LINK_LOSS: hci_pend_le_conn_del(hdev, addr, addr_type); break; + case HCI_AUTO_CONN_REPORT: + if (params->auto_connect != HCI_AUTO_CONN_REPORT) + hdev->pend_le_reports++; + hci_pend_le_conn_del(hdev, addr, addr_type); + break; case HCI_AUTO_CONN_ALWAYS: if (!is_connected(hdev, addr, addr_type)) hci_pend_le_conn_add(hdev, addr, addr_type); break; } + params->auto_connect = auto_connect; + BT_DBG("addr %pMR (type %u) auto_connect %u", addr, addr_type, auto_connect); @@ -3574,6 +3582,9 @@ void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) if (!params) return; + if (params->auto_connect == HCI_AUTO_CONN_REPORT) + hdev->pend_le_reports--; + hci_pend_le_conn_del(hdev, addr, addr_type); list_del(¶ms->list); @@ -3605,6 +3616,8 @@ void hci_conn_params_clear_enabled(struct hci_dev *hdev) list_for_each_entry_safe(params, tmp, &hdev->le_conn_params, list) { if (params->auto_connect == HCI_AUTO_CONN_DISABLED) continue; + if (params->auto_connect == HCI_AUTO_CONN_REPORT) + hdev->pend_le_reports--; list_del(¶ms->list); kfree(params); } -- cgit v1.2.3 From 912b42ef05a1e9f72a82c21d678a29c5055045d5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 3 Jul 2014 19:33:49 +0300 Subject: Bluetooth: Use hci_conn_params in pend_le_conns Since the connection parameters are always a basis for adding entries to hdev->pend_le_conns (so far of type bdaddr_list) it's simpler and more efficient to have the parameters themselves be the entries in the pend_le_conns list. We do this by adding another list_head to the hci_conn_params struct. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 9 +++--- net/bluetooth/hci_core.c | 68 +++++++++++++--------------------------- net/bluetooth/hci_event.c | 7 +++-- net/bluetooth/mgmt.c | 4 +-- 4 files changed, 33 insertions(+), 55 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b5f4405b41c2..09f9fb85d8fd 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -439,6 +439,7 @@ struct hci_chan { struct hci_conn_params { struct list_head list; + struct list_head pend_le_conn; bdaddr_t addr; u8 addr_type; @@ -867,10 +868,10 @@ void hci_conn_params_clear_all(struct hci_dev *hdev); void hci_conn_params_clear_disabled(struct hci_dev *hdev); void hci_conn_params_clear_enabled(struct hci_dev *hdev); -struct bdaddr_list *hci_pend_le_conn_lookup(struct hci_dev *hdev, - bdaddr_t *addr, u8 addr_type); -void hci_pend_le_conn_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); -void hci_pend_le_conn_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); +struct hci_conn_params *hci_pend_le_conn_lookup(struct hci_dev *hdev, + bdaddr_t *addr, u8 addr_type); +void hci_pend_le_conn_add(struct hci_dev *hdev, struct hci_conn_params *params); +void hci_pend_le_conn_del(struct hci_dev *hdev, struct hci_conn_params *params); void hci_pend_le_conns_clear(struct hci_dev *hdev); void hci_update_background_scan(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 41cc64429ea1..8882a6cd2876 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3427,73 +3427,46 @@ static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type) } /* This function requires the caller holds hdev->lock */ -struct bdaddr_list *hci_pend_le_conn_lookup(struct hci_dev *hdev, - bdaddr_t *addr, u8 addr_type) +struct hci_conn_params *hci_pend_le_conn_lookup(struct hci_dev *hdev, + bdaddr_t *addr, u8 addr_type) { - struct bdaddr_list *entry; + struct hci_conn_params *param; - list_for_each_entry(entry, &hdev->pend_le_conns, list) { - if (bacmp(&entry->bdaddr, addr) == 0 && - entry->bdaddr_type == addr_type) - return entry; + list_for_each_entry(param, &hdev->pend_le_conns, pend_le_conn) { + if (bacmp(¶m->addr, addr) == 0 && + param->addr_type == addr_type) + return param; } return NULL; } /* This function requires the caller holds hdev->lock */ -void hci_pend_le_conn_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) +void hci_pend_le_conn_add(struct hci_dev *hdev, struct hci_conn_params *params) { - struct bdaddr_list *entry; - - entry = hci_pend_le_conn_lookup(hdev, addr, addr_type); - if (entry) - goto done; - - entry = kzalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) { - BT_ERR("Out of memory"); - return; - } + list_del_init(¶ms->pend_le_conn); + list_add(¶ms->pend_le_conn, &hdev->pend_le_conns); - bacpy(&entry->bdaddr, addr); - entry->bdaddr_type = addr_type; - - list_add(&entry->list, &hdev->pend_le_conns); - - BT_DBG("addr %pMR (type %u)", addr, addr_type); + BT_DBG("addr %pMR (type %u)", ¶ms->addr, params->addr_type); -done: hci_update_background_scan(hdev); } /* This function requires the caller holds hdev->lock */ -void hci_pend_le_conn_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) +void hci_pend_le_conn_del(struct hci_dev *hdev, struct hci_conn_params *params) { - struct bdaddr_list *entry; - - entry = hci_pend_le_conn_lookup(hdev, addr, addr_type); - if (!entry) - goto done; - - list_del(&entry->list); - kfree(entry); + list_del_init(¶ms->pend_le_conn); - BT_DBG("addr %pMR (type %u)", addr, addr_type); + BT_DBG("addr %pMR (type %u)", ¶ms->addr, params->addr_type); -done: hci_update_background_scan(hdev); } /* This function requires the caller holds hdev->lock */ void hci_pend_le_conns_clear(struct hci_dev *hdev) { - struct bdaddr_list *entry, *tmp; - - list_for_each_entry_safe(entry, tmp, &hdev->pend_le_conns, list) { - list_del(&entry->list); - kfree(entry); - } + while (!list_empty(&hdev->pend_le_conns)) + list_del_init(hdev->pend_le_conns.next); BT_DBG("All LE pending connections cleared"); @@ -3523,6 +3496,7 @@ struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev, params->addr_type = addr_type; list_add(¶ms->list, &hdev->le_conn_params); + INIT_LIST_HEAD(¶ms->pend_le_conn); params->conn_min_interval = hdev->le_conn_min_interval; params->conn_max_interval = hdev->le_conn_max_interval; @@ -3552,16 +3526,16 @@ int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, switch (auto_connect) { case HCI_AUTO_CONN_DISABLED: case HCI_AUTO_CONN_LINK_LOSS: - hci_pend_le_conn_del(hdev, addr, addr_type); + hci_pend_le_conn_del(hdev, params); break; case HCI_AUTO_CONN_REPORT: if (params->auto_connect != HCI_AUTO_CONN_REPORT) hdev->pend_le_reports++; - hci_pend_le_conn_del(hdev, addr, addr_type); + hci_pend_le_conn_del(hdev, params); break; case HCI_AUTO_CONN_ALWAYS: if (!is_connected(hdev, addr, addr_type)) - hci_pend_le_conn_add(hdev, addr, addr_type); + hci_pend_le_conn_add(hdev, params); break; } @@ -3585,7 +3559,7 @@ void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) if (params->auto_connect == HCI_AUTO_CONN_REPORT) hdev->pend_le_reports--; - hci_pend_le_conn_del(hdev, addr, addr_type); + hci_pend_le_conn_del(hdev, params); list_del(¶ms->list); kfree(params); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 643c5a8d4050..4ebfcedd9ae7 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2209,7 +2209,7 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) /* Fall through */ case HCI_AUTO_CONN_ALWAYS: - hci_pend_le_conn_add(hdev, &conn->dst, conn->dst_type); + hci_pend_le_conn_add(hdev, params); break; default: @@ -4036,6 +4036,7 @@ static void hci_disconn_phylink_complete_evt(struct hci_dev *hdev, static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_le_conn_complete *ev = (void *) skb->data; + struct hci_conn_params *params; struct hci_conn *conn; struct smp_irk *irk; u8 addr_type; @@ -4152,7 +4153,9 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_proto_connect_cfm(conn, ev->status); - hci_pend_le_conn_del(hdev, &conn->dst, conn->dst_type); + params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); + if (params) + hci_pend_le_conn_del(hdev, params); unlock: hci_dev_unlock(hdev); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 02a4d31fee30..59ca4057955c 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5201,7 +5201,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, if (params->auto_connect == HCI_AUTO_CONN_REPORT) hdev->pend_le_reports--; - hci_pend_le_conn_del(hdev, &cp->addr.bdaddr, addr_type); + hci_pend_le_conn_del(hdev, params); list_del(¶ms->list); kfree(params); @@ -5514,7 +5514,7 @@ static void restart_le_auto_conns(struct hci_dev *hdev) list_for_each_entry(p, &hdev->le_conn_params, list) { if (p->auto_connect == HCI_AUTO_CONN_ALWAYS) { - hci_pend_le_conn_add(hdev, &p->addr, p->addr_type); + hci_pend_le_conn_add(hdev, p); added = true; } } -- cgit v1.2.3 From 5616b0a46ed82eb9a093f752fc4d7bd3cc688583 Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Tue, 24 Jun 2014 16:59:35 +0200 Subject: [SCSI] use the scsi data buffer length to extract transfer size Commit 8846bab180fa introduced a helper that can be used to query the wire transfer size for a SCSI command taking protection information into account. However, some commands do not have a 1:1 mapping between the block range they work on and the payload size (discard, write same). After the scatterlist has been set up these requests use __data_len to store the number of bytes to report completion on. This means that callers of scsi_transfer_length() would get the wrong byte count for these types of requests. To overcome this we make scsi_transfer_length() use the scatterlist length in the scsi_data_buffer as basis for the wire transfer calculation instead of __data_len. Reported-by: Christoph Hellwig Debugged-by: Mike Christie Signed-off-by: Martin K. Petersen Signed-off-by: Christoph Hellwig Reviewed-by: Sagi Grimberg Fixes: d77e65350f2d82dfa0557707d505711f5a43c8fd Cc: stable@vger.kernel.org Signed-off-by: James Bottomley --- include/scsi/scsi_cmnd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h index 42ed789ebafc..e0ae71098144 100644 --- a/include/scsi/scsi_cmnd.h +++ b/include/scsi/scsi_cmnd.h @@ -318,7 +318,7 @@ static inline void set_driver_byte(struct scsi_cmnd *cmd, char status) static inline unsigned scsi_transfer_length(struct scsi_cmnd *scmd) { - unsigned int xfer_len = blk_rq_bytes(scmd->request); + unsigned int xfer_len = scsi_out(scmd)->length; unsigned int prot_op = scsi_get_prot_op(scmd); unsigned int sector_size = scmd->device->sector_size; -- cgit v1.2.3 From 8b37e1bef5a6b60e949e28a4db3006e4b00bd758 Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Sat, 14 Jun 2014 02:21:40 -0700 Subject: leds: convert blink timer to workqueue This patch converts the blink timer from led-core to workqueue which is more suitable for this kind of non-priority operations. Moreover, timer may lead to errors when a LED setting function use a scheduling function such as pinctrl which is using mutex. Signed-off-by: Vincent Donnefort Signed-off-by: Bryan Wu --- drivers/leds/led-class.c | 14 +++++++------- drivers/leds/led-core.c | 11 ++++++----- include/linux/leds.h | 3 +-- 3 files changed, 14 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index aa29198fca3e..129729d35478 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -15,10 +15,10 @@ #include #include #include -#include #include #include #include +#include #include "leds.h" static struct class *leds_class; @@ -97,9 +97,10 @@ static const struct attribute_group *led_groups[] = { NULL, }; -static void led_timer_function(unsigned long data) +static void led_work_function(struct work_struct *ws) { - struct led_classdev *led_cdev = (void *)data; + struct led_classdev *led_cdev = + container_of(ws, struct led_classdev, blink_work.work); unsigned long brightness; unsigned long delay; @@ -143,7 +144,8 @@ static void led_timer_function(unsigned long data) } } - mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay)); + queue_delayed_work(system_wq, &led_cdev->blink_work, + msecs_to_jiffies(delay)); } static void set_brightness_delayed(struct work_struct *ws) @@ -231,9 +233,7 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) INIT_WORK(&led_cdev->set_brightness_work, set_brightness_delayed); - init_timer(&led_cdev->blink_timer); - led_cdev->blink_timer.function = led_timer_function; - led_cdev->blink_timer.data = (unsigned long)led_cdev; + INIT_DELAYED_WORK(&led_cdev->blink_work, led_work_function); #ifdef CONFIG_LEDS_TRIGGERS led_trigger_set_default(led_cdev); diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index 71b40d3bf776..4bb116867b88 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "leds.h" DECLARE_RWSEM(leds_list_lock); @@ -51,7 +52,7 @@ static void led_set_software_blink(struct led_classdev *led_cdev, return; } - mod_timer(&led_cdev->blink_timer, jiffies + 1); + queue_delayed_work(system_wq, &led_cdev->blink_work, 1); } @@ -75,7 +76,7 @@ void led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on, unsigned long *delay_off) { - del_timer_sync(&led_cdev->blink_timer); + cancel_delayed_work_sync(&led_cdev->blink_work); led_cdev->flags &= ~LED_BLINK_ONESHOT; led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP; @@ -90,7 +91,7 @@ void led_blink_set_oneshot(struct led_classdev *led_cdev, int invert) { if ((led_cdev->flags & LED_BLINK_ONESHOT) && - timer_pending(&led_cdev->blink_timer)) + delayed_work_pending(&led_cdev->blink_work)) return; led_cdev->flags |= LED_BLINK_ONESHOT; @@ -107,7 +108,7 @@ EXPORT_SYMBOL(led_blink_set_oneshot); void led_stop_software_blink(struct led_classdev *led_cdev) { - del_timer_sync(&led_cdev->blink_timer); + cancel_delayed_work_sync(&led_cdev->blink_work); led_cdev->blink_delay_on = 0; led_cdev->blink_delay_off = 0; } @@ -116,7 +117,7 @@ EXPORT_SYMBOL_GPL(led_stop_software_blink); void led_set_brightness(struct led_classdev *led_cdev, enum led_brightness brightness) { - /* delay brightness setting if need to stop soft-blink timer */ + /* delay brightness setting if need to stop soft-blink work */ if (led_cdev->blink_delay_on || led_cdev->blink_delay_off) { led_cdev->delayed_set_value = brightness; schedule_work(&led_cdev->set_brightness_work); diff --git a/include/linux/leds.h b/include/linux/leds.h index e43686472197..6a599dce7f9d 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -15,7 +15,6 @@ #include #include #include -#include #include struct device; @@ -69,7 +68,7 @@ struct led_classdev { const char *default_trigger; /* Trigger to use */ unsigned long blink_delay_on, blink_delay_off; - struct timer_list blink_timer; + struct delayed_work blink_work; int blink_brightness; struct work_struct set_brightness_work; -- cgit v1.2.3 From 9e33002fd1791bcab626b19301670484c1cb6d50 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Thu, 19 Jun 2014 17:22:44 +1000 Subject: PCI: Make resetting secondary bus logic common Commit d92a208d086 ("powerpc/pci: Mask linkDown on resetting PCI bus") implemented same logic (resetting PCI secondary bus by bridge's config register PCI_BRIDGE_CTL_BUS_RESET) in PCI core and arch-dependent code. To avoid the duplication, move the logic to pci_reset_secondary_bus(). That commit did not declare the pcibios_reset_secondary_bus() interface in linux/include/pci.h. Add the declaration. No functional change. Signed-off-by: Gavin Shan Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 7 ++++++- include/linux/pci.h | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 63a54a340863..758f1d88f28d 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3193,7 +3193,7 @@ static int pci_pm_reset(struct pci_dev *dev, int probe) return 0; } -void __weak pcibios_reset_secondary_bus(struct pci_dev *dev) +void pci_reset_secondary_bus(struct pci_dev *dev) { u16 ctrl; @@ -3219,6 +3219,11 @@ void __weak pcibios_reset_secondary_bus(struct pci_dev *dev) ssleep(1); } +void __weak pcibios_reset_secondary_bus(struct pci_dev *dev) +{ + pci_reset_secondary_bus(dev); +} + /** * pci_reset_bridge_secondary_bus - Reset the secondary bus on a PCI bridge. * @dev: Bridge device diff --git a/include/linux/pci.h b/include/linux/pci.h index 466bcd111d85..340529d399b2 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -978,6 +978,8 @@ int pci_try_reset_slot(struct pci_slot *slot); int pci_probe_reset_bus(struct pci_bus *bus); int pci_reset_bus(struct pci_bus *bus); int pci_try_reset_bus(struct pci_bus *bus); +void pci_reset_secondary_bus(struct pci_dev *dev); +void pcibios_reset_secondary_bus(struct pci_dev *dev); void pci_reset_bridge_secondary_bus(struct pci_dev *dev); void pci_update_resource(struct pci_dev *dev, int resno); int __must_check pci_assign_resource(struct pci_dev *dev, int i); -- cgit v1.2.3 From 31ea5d4dfe21fb50276dcd70ce268e58d57eccb4 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Thu, 19 Jun 2014 16:30:30 +0800 Subject: PCI/MSI: Cache Multiple Message Capable in struct msi_desc The Multiple Message Capable field in the MSI Message Control register indicates how many vectors the device supports. This field is read-only, so cache it in msi_desc to avoid reading it repeatedly. Since we cache the extracted field (not the entire Message Control register), we can use msi_mask() instead of msi_capable_mask(), which is then unused, so remove it. [bhelgaas: fix whitespace, changelog] Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas --- drivers/pci/msi.c | 15 +++++---------- include/linux/msi.h | 3 ++- 2 files changed, 7 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 9c6995043fff..50a7e4e96da7 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -167,11 +167,6 @@ static inline __attribute_const__ u32 msi_mask(unsigned x) return (1 << (1 << x)) - 1; } -static inline __attribute_const__ u32 msi_capable_mask(u16 control) -{ - return msi_mask((control >> 1) & 7); -} - /* * PCI 2.3 does not specify mask bits for each MSI interrupt. Attempting to * mask all MSI interrupts by clearing the MSI enable bit does not work @@ -454,7 +449,8 @@ static void __pci_restore_msi_state(struct pci_dev *dev) arch_restore_msi_irqs(dev); pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control); - msi_mask_irq(entry, msi_capable_mask(control), entry->masked); + msi_mask_irq(entry, msi_mask(entry->msi_attrib.multi_cap), + entry->masked); control &= ~PCI_MSI_FLAGS_QSIZE; control |= (entry->msi_attrib.multiple << 4) | PCI_MSI_FLAGS_ENABLE; pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control); @@ -617,6 +613,7 @@ static int msi_capability_init(struct pci_dev *dev, int nvec) entry->msi_attrib.maskbit = !!(control & PCI_MSI_FLAGS_MASKBIT); entry->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */ entry->msi_attrib.pos = dev->msi_cap; + entry->msi_attrib.multi_cap = (control & PCI_MSI_FLAGS_QMASK) >> 1; if (control & PCI_MSI_FLAGS_64BIT) entry->mask_pos = dev->msi_cap + PCI_MSI_MASK_64; @@ -625,7 +622,7 @@ static int msi_capability_init(struct pci_dev *dev, int nvec) /* All MSIs are unmasked by default, Mask them all */ if (entry->msi_attrib.maskbit) pci_read_config_dword(dev, entry->mask_pos, &entry->masked); - mask = msi_capable_mask(control); + mask = msi_mask(entry->msi_attrib.multi_cap); msi_mask_irq(entry, mask, mask); list_add_tail(&entry->list, &dev->msi_list); @@ -870,7 +867,6 @@ void pci_msi_shutdown(struct pci_dev *dev) { struct msi_desc *desc; u32 mask; - u16 ctrl; if (!pci_msi_enable || !dev || !dev->msi_enabled) return; @@ -883,8 +879,7 @@ void pci_msi_shutdown(struct pci_dev *dev) dev->msi_enabled = 0; /* Return the device with MSI unmasked as initial states */ - pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &ctrl); - mask = msi_capable_mask(ctrl); + mask = msi_mask(desc->msi_attrib.multi_cap); /* Keep cached state to be restored */ arch_msi_mask_irq(desc, mask, ~mask); diff --git a/include/linux/msi.h b/include/linux/msi.h index 92a2f991262a..8103f32f6d87 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -25,7 +25,8 @@ void write_msi_msg(unsigned int irq, struct msi_msg *msg); struct msi_desc { struct { __u8 is_msix : 1; - __u8 multiple: 3; /* log2 number of messages */ + __u8 multiple: 3; /* log2 num of messages allocated */ + __u8 multi_cap : 3; /* log2 num of messages supported */ __u8 maskbit : 1; /* mask-pending bit supported ? */ __u8 is_64 : 1; /* Address size: 0=32bit 1=64bit */ __u8 pos; /* Location of the msi capability */ -- cgit v1.2.3 From b9cd18de4db3c9ffa7e17b0dc0ca99ed5aa4d43a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 3 Jul 2014 15:43:15 -0400 Subject: ptrace,x86: force IRET path after a ptrace_stop() The 'sysret' fastpath does not correctly restore even all regular registers, much less any segment registers or reflags values. That is very much part of why it's faster than 'iret'. Normally that isn't a problem, because the normal ptrace() interface catches the process using the signal handler infrastructure, which always returns with an iret. However, some paths can get caught using ptrace_event() instead of the signal path, and for those we need to make sure that we aren't going to return to user space using 'sysret'. Otherwise the modifications that may have been done to the register set by the tracer wouldn't necessarily take effect. Fix it by forcing IRET path by setting TIF_NOTIFY_RESUME from arch_ptrace_stop_needed() which is invoked from ptrace_stop(). Signed-off-by: Tejun Heo Reported-by: Andy Lutomirski Acked-by: Oleg Nesterov Suggested-by: Linus Torvalds Cc: stable@vger.kernel.org Signed-off-by: Linus Torvalds --- arch/x86/include/asm/ptrace.h | 16 ++++++++++++++++ include/linux/ptrace.h | 3 +++ 2 files changed, 19 insertions(+) (limited to 'include') diff --git a/arch/x86/include/asm/ptrace.h b/arch/x86/include/asm/ptrace.h index 14fd6fd75a19..6205f0c434db 100644 --- a/arch/x86/include/asm/ptrace.h +++ b/arch/x86/include/asm/ptrace.h @@ -231,6 +231,22 @@ static inline unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, #define ARCH_HAS_USER_SINGLE_STEP_INFO +/* + * When hitting ptrace_stop(), we cannot return using SYSRET because + * that does not restore the full CPU state, only a minimal set. The + * ptracer can change arbitrary register values, which is usually okay + * because the usual ptrace stops run off the signal delivery path which + * forces IRET; however, ptrace_event() stops happen in arbitrary places + * in the kernel and don't force IRET path. + * + * So force IRET path after a ptrace stop. + */ +#define arch_ptrace_stop_needed(code, info) \ +({ \ + set_thread_flag(TIF_NOTIFY_RESUME); \ + false; \ +}) + struct user_desc; extern int do_get_thread_area(struct task_struct *p, int idx, struct user_desc __user *info); diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index 077904c8b70d..cc79eff4a1ad 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -334,6 +334,9 @@ static inline void user_single_step_siginfo(struct task_struct *tsk, * calling arch_ptrace_stop() when it would be superfluous. For example, * if the thread has not been back to user mode since the last stop, the * thread state might indicate that nothing needs to be done. + * + * This is guaranteed to be invoked once before a task stops for ptrace and + * may include arch-specific operations necessary prior to a ptrace stop. */ #define arch_ptrace_stop_needed(code, info) (0) #endif -- cgit v1.2.3 From c149dcb5c60bfea8871f16dfcc0690255eeb825f Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 4 Jul 2014 10:00:37 +0800 Subject: drm/i915: provide interface for audio driver to query cdclk For Haswell and Broadwell, if the display power well has been disabled, the display audio controller divider values EM4 M VALUE and EM5 N VALUE will have been lost. The CDCLK frequency is required for reprogramming them to generate 24MHz HD-A link BCLK. So provide a private interface for the audio driver to query CDCLK. This is a stopgap solution until a more generic interface between audio and display drivers has been implemented. Signed-off-by: Jani Nikula Reviewed-by: Damien Lespiau Signed-off-by: Mengdong Lin Cc: Signed-off-by: Takashi Iwai --- drivers/gpu/drm/i915/intel_pm.c | 21 +++++++++++++++++++++ include/drm/i915_powerwell.h | 1 + 2 files changed, 22 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 6463f0201cf2..409d62676854 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -6053,6 +6053,27 @@ int i915_release_power_well(void) } EXPORT_SYMBOL_GPL(i915_release_power_well); +/* + * Private interface for the audio driver to get CDCLK in kHz. + * + * Caller must request power well using i915_request_power_well() prior to + * making the call. + */ +int i915_get_cdclk_freq(void) +{ + struct drm_i915_private *dev_priv; + + if (!hsw_pwr) + return -ENODEV; + + dev_priv = container_of(hsw_pwr, struct drm_i915_private, + power_domains); + + return intel_ddi_get_cdclk_freq(dev_priv); +} +EXPORT_SYMBOL_GPL(i915_get_cdclk_freq); + + #define POWER_DOMAIN_MASK (BIT(POWER_DOMAIN_NUM) - 1) #define HSW_ALWAYS_ON_POWER_DOMAINS ( \ diff --git a/include/drm/i915_powerwell.h b/include/drm/i915_powerwell.h index 2baba9996094..baa6f11b1837 100644 --- a/include/drm/i915_powerwell.h +++ b/include/drm/i915_powerwell.h @@ -32,5 +32,6 @@ /* For use by hda_i915 driver */ extern int i915_request_power_well(void); extern int i915_release_power_well(void); +extern int i915_get_cdclk_freq(void); #endif /* _I915_POWERWELL_H_ */ -- cgit v1.2.3 From 9fc3bfb681bdf59999f56072fff4632a5abea897 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 4 Jul 2014 00:46:56 +0200 Subject: Bluetooth: Add support for controller configuration info command The Read Controller Configuration Information command allows retrieving details about possible configurations option. The supported options are returned and also the missing options (if any). Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 11 +++++++++++ net/bluetooth/mgmt.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index e0786cfa5490..0579eb3952e3 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -97,6 +97,7 @@ struct mgmt_rp_read_index_list { #define MGMT_SETTING_SECURE_CONN 0x00000800 #define MGMT_SETTING_DEBUG_KEYS 0x00001000 #define MGMT_SETTING_PRIVACY 0x00002000 +#define MGMT_SETTING_CONFIGURATION 0x00004000 #define MGMT_OP_READ_INFO 0x0004 #define MGMT_READ_INFO_SIZE 0 @@ -471,6 +472,16 @@ struct mgmt_rp_read_unconf_index_list { __le16 index[0]; } __packed; +#define MGMT_OPTION_PUBLIC_ADDRESS 0x00000001 + +#define MGMT_OP_READ_CONFIG_INFO 0x0037 +#define MGMT_READ_CONFIG_INFO_SIZE 0 +struct mgmt_rp_read_config_info { + __le16 manufacturer; + __le32 supported_options; + __le32 missing_options; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 59ca4057955c..474b6dcdf665 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -90,6 +90,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_REMOVE_DEVICE, MGMT_OP_LOAD_CONN_PARAM, MGMT_OP_READ_UNCONF_INDEX_LIST, + MGMT_OP_READ_CONFIG_INFO, }; static const u16 mgmt_events[] = { @@ -440,6 +441,29 @@ static int read_unconf_index_list(struct sock *sk, struct hci_dev *hdev, return err; } +static int read_config_info(struct sock *sk, struct hci_dev *hdev, + void *data, u16 data_len) +{ + struct mgmt_rp_read_config_info rp; + + BT_DBG("sock %p %s", sk, hdev->name); + + hci_dev_lock(hdev); + + memset(&rp, 0, sizeof(rp)); + rp.manufacturer = cpu_to_le16(hdev->manufacturer); + if (hdev->set_bdaddr) + rp.supported_options = cpu_to_le32(MGMT_OPTION_PUBLIC_ADDRESS); + else + rp.supported_options = cpu_to_le32(0); + rp.missing_options = cpu_to_le32(0); + + hci_dev_unlock(hdev); + + return cmd_complete(sk, hdev->id, MGMT_OP_READ_CONFIG_INFO, 0, &rp, + sizeof(rp)); +} + static u32 get_supported_settings(struct hci_dev *hdev) { u32 settings = 0; @@ -472,6 +496,9 @@ static u32 get_supported_settings(struct hci_dev *hdev) settings |= MGMT_SETTING_PRIVACY; } + if (hdev->set_bdaddr) + settings |= MGMT_SETTING_CONFIGURATION; + return settings; } @@ -5371,6 +5398,7 @@ static const struct mgmt_handler { { remove_device, false, MGMT_REMOVE_DEVICE_SIZE }, { load_conn_param, true, MGMT_LOAD_CONN_PARAM_SIZE }, { read_unconf_index_list, false, MGMT_READ_UNCONF_INDEX_LIST_SIZE }, + { read_config_info, false, MGMT_READ_CONFIG_INFO_SIZE }, }; int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) -- cgit v1.2.3 From 8c071caa7e9c584a97ca3cd33015b1d97d4f2250 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 18 Jun 2014 12:04:29 +0300 Subject: OMAPDSS: add hdmi ops to hdmi_ops and omap_dss_driver Add two new ops for HDMI: set_infoframe() and set_hdmi_mode(). The first one can be used to specify the infoframe that should be sent to the HDMI monitor, and the second one is used to enable HDMI mode (versus DVI mode). Signed-off-by: Tomi Valkeinen --- include/video/omapdss.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'include') diff --git a/include/video/omapdss.h b/include/video/omapdss.h index fc06c5b5f12a..069dfca9549a 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h @@ -61,6 +61,7 @@ struct omap_overlay_manager; struct dss_lcd_mgr_config; struct snd_aes_iec958; struct snd_cea_861_aud_if; +struct hdmi_avi_infoframe; enum omap_display_type { OMAP_DISPLAY_TYPE_NONE = 0, @@ -631,6 +632,10 @@ struct omapdss_hdmi_ops { int (*read_edid)(struct omap_dss_device *dssdev, u8 *buf, int len); bool (*detect)(struct omap_dss_device *dssdev); + int (*set_hdmi_mode)(struct omap_dss_device *dssdev, bool hdmi_mode); + int (*set_infoframe)(struct omap_dss_device *dssdev, + const struct hdmi_avi_infoframe *avi); + /* * Note: These functions might sleep. Do not call while * holding a spinlock/readlock. @@ -850,6 +855,10 @@ struct omap_dss_driver { int (*read_edid)(struct omap_dss_device *dssdev, u8 *buf, int len); bool (*detect)(struct omap_dss_device *dssdev); + int (*set_hdmi_mode)(struct omap_dss_device *dssdev, bool hdmi_mode); + int (*set_hdmi_infoframe)(struct omap_dss_device *dssdev, + const struct hdmi_avi_infoframe *avi); + /* * For display drivers that support audio. This encompasses * HDMI and DisplayPort at the moment. -- cgit v1.2.3 From 93450c75448e370659ce7ca9c192298596fb055e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 4 Jul 2014 12:37:17 +0300 Subject: Bluetooth: Convert pend_le_conn list to a generic action list In preparation to store also HCI_AUTO_CONN_REPORT entries in a list it makes sense to convert the existing pend_le_conn list head of hci_conn_params into a more generically named "action". This makes sense because a parameter entry will never participate in more than one action list. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_core.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 09f9fb85d8fd..3b1143caa380 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -439,7 +439,7 @@ struct hci_chan { struct hci_conn_params { struct list_head list; - struct list_head pend_le_conn; + struct list_head action; bdaddr_t addr; u8 addr_type; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 750f969df8db..fed2a0648d3d 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3440,7 +3440,7 @@ struct hci_conn_params *hci_pend_le_conn_lookup(struct hci_dev *hdev, if (!hci_is_identity_address(addr, addr_type)) return NULL; - list_for_each_entry(param, &hdev->pend_le_conns, pend_le_conn) { + list_for_each_entry(param, &hdev->pend_le_conns, action) { if (bacmp(¶m->addr, addr) == 0 && param->addr_type == addr_type) return param; @@ -3452,8 +3452,8 @@ struct hci_conn_params *hci_pend_le_conn_lookup(struct hci_dev *hdev, /* This function requires the caller holds hdev->lock */ void hci_pend_le_conn_add(struct hci_dev *hdev, struct hci_conn_params *params) { - list_del_init(¶ms->pend_le_conn); - list_add(¶ms->pend_le_conn, &hdev->pend_le_conns); + list_del_init(¶ms->action); + list_add(¶ms->action, &hdev->pend_le_conns); BT_DBG("addr %pMR (type %u)", ¶ms->addr, params->addr_type); @@ -3463,7 +3463,7 @@ void hci_pend_le_conn_add(struct hci_dev *hdev, struct hci_conn_params *params) /* This function requires the caller holds hdev->lock */ void hci_pend_le_conn_del(struct hci_dev *hdev, struct hci_conn_params *params) { - list_del_init(¶ms->pend_le_conn); + list_del_init(¶ms->action); BT_DBG("addr %pMR (type %u)", ¶ms->addr, params->addr_type); @@ -3504,7 +3504,7 @@ struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev, params->addr_type = addr_type; list_add(¶ms->list, &hdev->le_conn_params); - INIT_LIST_HEAD(¶ms->pend_le_conn); + INIT_LIST_HEAD(¶ms->action); params->conn_min_interval = hdev->le_conn_min_interval; params->conn_max_interval = hdev->le_conn_max_interval; -- cgit v1.2.3 From 66f8455aeac3427110d451534567eb1b9aea6929 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 4 Jul 2014 12:37:18 +0300 Subject: Bluetooth: Convert pend_le_reports into a list To simplify manipulation and lookup of hci_conn_params entries of the type HCI_AUTO_CONN_REPORT it makes sense to store them in their own list. The new action list_head in hci_conn_params is used for this purpose. Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_core.c | 17 +++++++++++------ net/bluetooth/hci_event.c | 2 +- net/bluetooth/mgmt.c | 4 ++-- 4 files changed, 15 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 3b1143caa380..9670f4c66ee3 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -312,7 +312,7 @@ struct hci_dev { struct list_head le_white_list; struct list_head le_conn_params; struct list_head pend_le_conns; - unsigned int pend_le_reports; + struct list_head pend_le_reports; struct hci_dev_stats stat; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index fed2a0648d3d..296f44748636 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3529,7 +3529,7 @@ int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, if (params->auto_connect == HCI_AUTO_CONN_REPORT && auto_connect != HCI_AUTO_CONN_REPORT) - hdev->pend_le_reports--; + list_del_init(¶ms->action); switch (auto_connect) { case HCI_AUTO_CONN_DISABLED: @@ -3537,8 +3537,11 @@ int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, hci_pend_le_conn_del(hdev, params); break; case HCI_AUTO_CONN_REPORT: - if (params->auto_connect != HCI_AUTO_CONN_REPORT) - hdev->pend_le_reports++; + if (params->auto_connect != HCI_AUTO_CONN_REPORT) { + list_del_init(¶ms->action); + list_add(¶ms->action, + &hdev->pend_le_reports); + } hci_pend_le_conn_del(hdev, params); break; case HCI_AUTO_CONN_ALWAYS: @@ -3565,7 +3568,7 @@ void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) return; if (params->auto_connect == HCI_AUTO_CONN_REPORT) - hdev->pend_le_reports--; + list_del_init(¶ms->action); hci_pend_le_conn_del(hdev, params); @@ -3599,7 +3602,7 @@ void hci_conn_params_clear_enabled(struct hci_dev *hdev) if (params->auto_connect == HCI_AUTO_CONN_DISABLED) continue; if (params->auto_connect == HCI_AUTO_CONN_REPORT) - hdev->pend_le_reports--; + list_del_init(¶ms->action); list_del(¶ms->list); kfree(params); } @@ -3859,6 +3862,7 @@ struct hci_dev *hci_alloc_dev(void) INIT_LIST_HEAD(&hdev->le_white_list); INIT_LIST_HEAD(&hdev->le_conn_params); INIT_LIST_HEAD(&hdev->pend_le_conns); + INIT_LIST_HEAD(&hdev->pend_le_reports); INIT_LIST_HEAD(&hdev->conn_hash.list); INIT_WORK(&hdev->rx_work, hci_rx_work); @@ -5362,7 +5366,8 @@ void hci_update_background_scan(struct hci_dev *hdev) hci_req_init(&req, hdev); - if (list_empty(&hdev->pend_le_conns) && !hdev->pend_le_reports) { + if (list_empty(&hdev->pend_le_conns) && + list_empty(&hdev->pend_le_reports)) { /* If there is no pending LE connections or devices * to be scanned for, we should stop the background * scanning. diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index e0407e674061..2a2ef698e851 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4240,7 +4240,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, return; } - if (!hdev->pend_le_reports) + if (list_empty(&hdev->pend_le_reports)) return; if (type == LE_ADV_DIRECT_IND) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 474b6dcdf665..a823cccf81f1 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5226,7 +5226,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, } if (params->auto_connect == HCI_AUTO_CONN_REPORT) - hdev->pend_le_reports--; + list_del_init(¶ms->action); hci_pend_le_conn_del(hdev, params); list_del(¶ms->list); @@ -6540,7 +6540,7 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, if (!hci_discovery_active(hdev)) { if (link_type == ACL_LINK) return; - if (link_type == LE_LINK && !hdev->pend_le_reports) + if (link_type == LE_LINK && list_empty(&hdev->pend_le_reports)) return; } -- cgit v1.2.3 From ae44e5d19e870385d1e73ce248c19ea4761bb40c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 4 Jul 2014 12:37:22 +0300 Subject: Bluetooth: Remove unused hci_pend_le_conn_del() function Now that there are no-longer any users of the hci_pend_le_conn_del() function we can simply go ahead and remove it. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 - net/bluetooth/hci_core.c | 10 ---------- 2 files changed, 11 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 9670f4c66ee3..758146c513d3 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -871,7 +871,6 @@ void hci_conn_params_clear_enabled(struct hci_dev *hdev); struct hci_conn_params *hci_pend_le_conn_lookup(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); void hci_pend_le_conn_add(struct hci_dev *hdev, struct hci_conn_params *params); -void hci_pend_le_conn_del(struct hci_dev *hdev, struct hci_conn_params *params); void hci_pend_le_conns_clear(struct hci_dev *hdev); void hci_update_background_scan(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 3b0da6eeb449..6eadde820333 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3460,16 +3460,6 @@ void hci_pend_le_conn_add(struct hci_dev *hdev, struct hci_conn_params *params) hci_update_background_scan(hdev); } -/* This function requires the caller holds hdev->lock */ -void hci_pend_le_conn_del(struct hci_dev *hdev, struct hci_conn_params *params) -{ - list_del_init(¶ms->action); - - BT_DBG("addr %pMR (type %u)", ¶ms->addr, params->addr_type); - - hci_update_background_scan(hdev); -} - /* This function requires the caller holds hdev->lock */ void hci_pend_le_conns_clear(struct hci_dev *hdev) { -- cgit v1.2.3 From d7347f3cc2b63be0ea35b3239faf4b32fde2fb44 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 4 Jul 2014 12:37:23 +0300 Subject: Bluetooth: Fix clearing and restarting all LE actions on power cycle When powering off (hci_dev_do_close) we should clear both the pend_le_reports and pend_le_conns types of entries. When powering on respectively we should populate both lists. This patch converts the hci_pend_le_conns_clear() function into hci_pend_le_actions_clear() (which can now be static) and converts the restart_le_auto_conns() function into restart_le_actions(). Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 - net/bluetooth/hci_core.c | 23 ++++++++++++----------- net/bluetooth/mgmt.c | 33 ++++++++++++++++----------------- 3 files changed, 28 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 758146c513d3..9208b18cb1e9 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -871,7 +871,6 @@ void hci_conn_params_clear_enabled(struct hci_dev *hdev); struct hci_conn_params *hci_pend_le_conn_lookup(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); void hci_pend_le_conn_add(struct hci_dev *hdev, struct hci_conn_params *params); -void hci_pend_le_conns_clear(struct hci_dev *hdev); void hci_update_background_scan(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 6eadde820333..3a1a25dcb2bc 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2354,6 +2354,17 @@ done: return err; } +/* This function requires the caller holds hdev->lock */ +static void hci_pend_le_actions_clear(struct hci_dev *hdev) +{ + struct hci_conn_params *p; + + list_for_each_entry(p, &hdev->le_conn_params, list) + list_del_init(&p->action); + + BT_DBG("All LE pending actions cleared"); +} + static int hci_dev_do_close(struct hci_dev *hdev) { BT_DBG("%s %p", hdev->name, hdev); @@ -2391,7 +2402,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) hci_dev_lock(hdev); hci_inquiry_cache_flush(hdev); hci_conn_hash_flush(hdev); - hci_pend_le_conns_clear(hdev); + hci_pend_le_actions_clear(hdev); hci_dev_unlock(hdev); hci_notify(hdev, HCI_DEV_DOWN); @@ -3460,16 +3471,6 @@ void hci_pend_le_conn_add(struct hci_dev *hdev, struct hci_conn_params *params) hci_update_background_scan(hdev); } -/* This function requires the caller holds hdev->lock */ -void hci_pend_le_conns_clear(struct hci_dev *hdev) -{ - while (!list_empty(&hdev->pend_le_conns)) - list_del_init(hdev->pend_le_conns.next); - - BT_DBG("All LE pending connections cleared"); -} - -/* This function requires the caller holds hdev->lock */ struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) { diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 77c64b8cb7e2..f1672b15c0f3 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5533,29 +5533,28 @@ void mgmt_index_removed(struct hci_dev *hdev) } /* This function requires the caller holds hdev->lock */ -static void restart_le_auto_conns(struct hci_dev *hdev) +static void restart_le_actions(struct hci_dev *hdev) { struct hci_conn_params *p; - bool added = false; list_for_each_entry(p, &hdev->le_conn_params, list) { - if (p->auto_connect == HCI_AUTO_CONN_ALWAYS) { - hci_pend_le_conn_add(hdev, p); - added = true; + /* Needed for AUTO_OFF case where might not "really" + * have been powered off. + */ + list_del_init(&p->action); + + switch (p->auto_connect) { + case HCI_AUTO_CONN_ALWAYS: + list_add(&p->action, &hdev->pend_le_conns); + break; + case HCI_AUTO_CONN_REPORT: + list_add(&p->action, &hdev->pend_le_reports); + break; + default: + break; } } - /* Calling hci_pend_le_conn_add will actually already trigger - * background scanning when needed. So no need to trigger it - * just another time. - * - * This check is here to avoid an unneeded restart of the - * passive scanning. Since this is during the controller - * power up phase the duplicate filtering is not an issue. - */ - if (added) - return; - hci_update_background_scan(hdev); } @@ -5567,7 +5566,7 @@ static void powered_complete(struct hci_dev *hdev, u8 status) hci_dev_lock(hdev); - restart_le_auto_conns(hdev); + restart_le_actions(hdev); mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match); -- cgit v1.2.3 From d9b3ad7df101a6490272771f1aabbaa623a196f3 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 4 Jul 2014 12:37:25 +0300 Subject: Bluetooth: Remove unused hci_pend_le_conn_add function Since there are no more users of this function we can simply go ahead and remove it. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 - net/bluetooth/hci_core.c | 10 ---------- 2 files changed, 11 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 9208b18cb1e9..c2bc9180cb79 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -870,7 +870,6 @@ void hci_conn_params_clear_enabled(struct hci_dev *hdev); struct hci_conn_params *hci_pend_le_conn_lookup(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); -void hci_pend_le_conn_add(struct hci_dev *hdev, struct hci_conn_params *params); void hci_update_background_scan(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 3a1a25dcb2bc..38a0c5cd0ee4 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3461,16 +3461,6 @@ struct hci_conn_params *hci_pend_le_conn_lookup(struct hci_dev *hdev, } /* This function requires the caller holds hdev->lock */ -void hci_pend_le_conn_add(struct hci_dev *hdev, struct hci_conn_params *params) -{ - list_del_init(¶ms->action); - list_add(¶ms->action, &hdev->pend_le_conns); - - BT_DBG("addr %pMR (type %u)", ¶ms->addr, params->addr_type); - - hci_update_background_scan(hdev); -} - struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) { -- cgit v1.2.3 From 501f882741b139da22bb3ba4bc615a6eadce5038 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 4 Jul 2014 12:37:26 +0300 Subject: Bluetooth: Make hci_pend_le_conn_lookup more general purposed In some circumstances we need to look up entries in pend_le_conns and in other in pend_le_reports. This patch converts the existing lookup function for pend_le_conns to something that can be used for both lists. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 5 +++-- net/bluetooth/hci_core.c | 6 +++--- net/bluetooth/hci_event.c | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index c2bc9180cb79..1cae4d3a629d 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -868,8 +868,9 @@ void hci_conn_params_clear_all(struct hci_dev *hdev); void hci_conn_params_clear_disabled(struct hci_dev *hdev); void hci_conn_params_clear_enabled(struct hci_dev *hdev); -struct hci_conn_params *hci_pend_le_conn_lookup(struct hci_dev *hdev, - bdaddr_t *addr, u8 addr_type); +struct hci_conn_params *hci_pend_le_action_lookup(struct list_head *list, + bdaddr_t *addr, + u8 addr_type); void hci_update_background_scan(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 38a0c5cd0ee4..038b4748375b 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3442,8 +3442,8 @@ static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type) } /* This function requires the caller holds hdev->lock */ -struct hci_conn_params *hci_pend_le_conn_lookup(struct hci_dev *hdev, - bdaddr_t *addr, u8 addr_type) +struct hci_conn_params *hci_pend_le_action_lookup(struct list_head *list, + bdaddr_t *addr, u8 addr_type) { struct hci_conn_params *param; @@ -3451,7 +3451,7 @@ struct hci_conn_params *hci_pend_le_conn_lookup(struct hci_dev *hdev, if (!hci_is_identity_address(addr, addr_type)) return NULL; - list_for_each_entry(param, &hdev->pend_le_conns, action) { + list_for_each_entry(param, list, action) { if (bacmp(¶m->addr, addr) == 0 && param->addr_type == addr_type) return param; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 9e0b2e888bba..9274cd228995 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4194,7 +4194,7 @@ static bool check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, { struct hci_conn *conn; - if (!hci_pend_le_conn_lookup(hdev, addr, addr_type)) + if (!hci_pend_le_action_lookup(&hdev->pend_le_conns, addr, addr_type)) return false; conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW, -- cgit v1.2.3 From 104a1c13ac66e40cf8c6ae74d76ff14ff24b9b01 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 3 Jul 2014 09:51:18 -0600 Subject: iommu/core: Create central IOMMU group lookup/creation interface Currently each IOMMU driver that supports IOMMU groups has its own code for discovering the base device used in grouping. This code is generally not specific to the IOMMU hardware, but to the bus of the devices managed by the IOMMU. We can therefore create a common interface for supporting devices on different buses. Signed-off-by: Alex Williamson Signed-off-by: Joerg Roedel --- drivers/iommu/iommu.c | 182 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/iommu.h | 1 + 2 files changed, 183 insertions(+) (limited to 'include') diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index e5555fcfe703..d061c8677a81 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -29,6 +29,7 @@ #include #include #include +#include #include static struct kset *iommu_group_kset; @@ -514,6 +515,187 @@ int iommu_group_id(struct iommu_group *group) } EXPORT_SYMBOL_GPL(iommu_group_id); +/* + * To consider a PCI device isolated, we require ACS to support Source + * Validation, Request Redirection, Completer Redirection, and Upstream + * Forwarding. This effectively means that devices cannot spoof their + * requester ID, requests and completions cannot be redirected, and all + * transactions are forwarded upstream, even as it passes through a + * bridge where the target device is downstream. + */ +#define REQ_ACS_FLAGS (PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF) + +struct group_for_pci_data { + struct pci_dev *pdev; + struct iommu_group *group; +}; + +/* + * DMA alias iterator callback, return the last seen device. Stop and return + * the IOMMU group if we find one along the way. + */ +static int get_pci_alias_or_group(struct pci_dev *pdev, u16 alias, void *opaque) +{ + struct group_for_pci_data *data = opaque; + + data->pdev = pdev; + data->group = iommu_group_get(&pdev->dev); + + return data->group != NULL; +} + +/* + * Use standard PCI bus topology, isolation features, and DMA alias quirks + * to find or create an IOMMU group for a device. + */ +static struct iommu_group *iommu_group_get_for_pci_dev(struct pci_dev *pdev) +{ + struct group_for_pci_data data; + struct pci_bus *bus; + struct iommu_group *group = NULL; + struct pci_dev *tmp; + + /* + * Find the upstream DMA alias for the device. A device must not + * be aliased due to topology in order to have its own IOMMU group. + * If we find an alias along the way that already belongs to a + * group, use it. + */ + if (pci_for_each_dma_alias(pdev, get_pci_alias_or_group, &data)) + return data.group; + + pdev = data.pdev; + + /* + * Continue upstream from the point of minimum IOMMU granularity + * due to aliases to the point where devices are protected from + * peer-to-peer DMA by PCI ACS. Again, if we find an existing + * group, use it. + */ + for (bus = pdev->bus; !pci_is_root_bus(bus); bus = bus->parent) { + if (!bus->self) + continue; + + if (pci_acs_path_enabled(bus->self, NULL, REQ_ACS_FLAGS)) + break; + + pdev = bus->self; + + group = iommu_group_get(&pdev->dev); + if (group) + return group; + } + + /* + * Next we need to consider DMA alias quirks. If one device aliases + * to another, they should be grouped together. It's theoretically + * possible that aliases could create chains of devices where each + * device aliases another device. If we then factor in multifunction + * ACS grouping requirements, each alias could incorporate a new slot + * with multiple functions, each with aliases. This is all extremely + * unlikely as DMA alias quirks are typically only used for PCIe + * devices where we usually have a single slot per bus. Furthermore, + * the alias quirk is usually to another function within the slot + * (and ACS multifunction is not supported) or to a different slot + * that doesn't physically exist. The likely scenario is therefore + * that everything on the bus gets grouped together. To reduce the + * problem space, share the IOMMU group for all devices on the bus + * if a DMA alias quirk is present on the bus. + */ + tmp = NULL; + for_each_pci_dev(tmp) { + if (tmp->bus != pdev->bus || + !(tmp->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVFN)) + continue; + + pci_dev_put(tmp); + tmp = NULL; + + /* We have an alias quirk, search for an existing group */ + for_each_pci_dev(tmp) { + struct iommu_group *group_tmp; + + if (tmp->bus != pdev->bus) + continue; + + group_tmp = iommu_group_get(&tmp->dev); + if (!group) { + group = group_tmp; + continue; + } + + if (group_tmp) { + WARN_ON(group != group_tmp); + iommu_group_put(group_tmp); + } + } + + return group ? group : iommu_group_alloc(); + } + + /* + * Non-multifunction devices or multifunction devices supporting + * ACS get their own group. + */ + if (!pdev->multifunction || pci_acs_enabled(pdev, REQ_ACS_FLAGS)) + return iommu_group_alloc(); + + /* + * Multifunction devices not supporting ACS share a group with other + * similar devices in the same slot. + */ + tmp = NULL; + for_each_pci_dev(tmp) { + if (tmp == pdev || tmp->bus != pdev->bus || + PCI_SLOT(tmp->devfn) != PCI_SLOT(pdev->devfn) || + pci_acs_enabled(tmp, REQ_ACS_FLAGS)) + continue; + + group = iommu_group_get(&tmp->dev); + if (group) { + pci_dev_put(tmp); + return group; + } + } + + /* No shared group found, allocate new */ + return iommu_group_alloc(); +} + +/** + * iommu_group_get_for_dev - Find or create the IOMMU group for a device + * @dev: target device + * + * This function is intended to be called by IOMMU drivers and extended to + * support common, bus-defined algorithms when determining or creating the + * IOMMU group for a device. On success, the caller will hold a reference + * to the returned IOMMU group, which will already include the provided + * device. The reference should be released with iommu_group_put(). + */ +struct iommu_group *iommu_group_get_for_dev(struct device *dev) +{ + struct iommu_group *group = ERR_PTR(-EIO); + int ret; + + group = iommu_group_get(dev); + if (group) + return group; + + if (dev_is_pci(dev)) + group = iommu_group_get_for_pci_dev(to_pci_dev(dev)); + + if (IS_ERR(group)) + return group; + + ret = iommu_group_add_device(group, dev); + if (ret) { + iommu_group_put(group); + return ERR_PTR(ret); + } + + return group; +} + static int add_iommu_group(struct device *dev, void *data) { struct iommu_ops *ops = data; diff --git a/include/linux/iommu.h b/include/linux/iommu.h index b96a5b2136e4..a2e5843b0a22 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -181,6 +181,7 @@ extern int iommu_group_register_notifier(struct iommu_group *group, extern int iommu_group_unregister_notifier(struct iommu_group *group, struct notifier_block *nb); extern int iommu_group_id(struct iommu_group *group); +extern struct iommu_group *iommu_group_get_for_dev(struct device *dev); extern int iommu_domain_get_attr(struct iommu_domain *domain, enum iommu_attr, void *data); -- cgit v1.2.3 From c61959ecbbc6bf9034e65c8e8ef03fa9d1066f05 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 12 Jun 2014 16:12:24 -0600 Subject: iommu: Add sysfs support for IOMMUs IOMMUs currently have no common representation to userspace, most seem to have no representation at all aside from a few printks on bootup. There are however features of IOMMUs that are useful to know about. For instance the IOMMU might support superpages, making use of processor large/huge pages more important in a device assignment scenario. It's also useful to create cross links between devices and IOMMU hardware units, so that users might be able to load balance their devices to avoid thrashing a single hardware unit. This patch adds a device create and destroy interface as well as device linking, making it very lightweight for an IOMMU driver to add basic support. IOMMU drivers can provide additional attributes automatically by using an attribute_group. The attributes exposed are expected to be relatively device specific, the means to retrieve them certainly are, so there are currently no common attributes for the new class created here. Signed-off-by: Alex Williamson Signed-off-by: Joerg Roedel --- Documentation/ABI/testing/sysfs-class-iommu | 17 ++++ drivers/iommu/Makefile | 1 + drivers/iommu/iommu-sysfs.c | 133 ++++++++++++++++++++++++++++ include/linux/iommu.h | 26 ++++++ 4 files changed, 177 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-iommu create mode 100644 drivers/iommu/iommu-sysfs.c (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-class-iommu b/Documentation/ABI/testing/sysfs-class-iommu new file mode 100644 index 000000000000..6d0a1b4be82d --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-iommu @@ -0,0 +1,17 @@ +What: /sys/class/iommu//devices/ +Date: June 2014 +KernelVersion: 3.17 +Contact: Alex Williamson +Description: + IOMMU drivers are able to link devices managed by a + given IOMMU here to allow association of IOMMU to + device. + +What: /sys/devices/.../iommu +Date: June 2014 +KernelVersion: 3.17 +Contact: Alex Williamson +Description: + IOMMU drivers are able to link the IOMMU for a + given device here to allow association of device to + IOMMU. diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile index 8893bad048e0..7788ebd1f6e2 100644 --- a/drivers/iommu/Makefile +++ b/drivers/iommu/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_IOMMU_API) += iommu.o obj-$(CONFIG_IOMMU_API) += iommu-traces.o +obj-$(CONFIG_IOMMU_API) += iommu-sysfs.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 diff --git a/drivers/iommu/iommu-sysfs.c b/drivers/iommu/iommu-sysfs.c new file mode 100644 index 000000000000..d6939234a009 --- /dev/null +++ b/drivers/iommu/iommu-sysfs.c @@ -0,0 +1,133 @@ +/* + * IOMMU sysfs class support + * + * Copyright (C) 2014 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. + */ + +#include +#include +#include + +/* + * We provide a common class "devices" group which initially has no attributes. + * As devices are added to the IOMMU, we'll add links to the group. + */ +static struct attribute *devices_attr[] = { + NULL, +}; + +static const struct attribute_group iommu_devices_attr_group = { + .name = "devices", + .attrs = devices_attr, +}; + +static const struct attribute_group *iommu_dev_groups[] = { + &iommu_devices_attr_group, + NULL, +}; + +static void iommu_release_device(struct device *dev) +{ + kfree(dev); +} + +static struct class iommu_class = { + .name = "iommu", + .dev_release = iommu_release_device, + .dev_groups = iommu_dev_groups, +}; + +static int __init iommu_dev_init(void) +{ + return class_register(&iommu_class); +} +postcore_initcall(iommu_dev_init); + +/* + * Create an IOMMU device and return a pointer to it. IOMMU specific + * attributes can be provided as an attribute group, allowing a unique + * namespace per IOMMU type. + */ +struct device *iommu_device_create(struct device *parent, void *drvdata, + const struct attribute_group **groups, + const char *fmt, ...) +{ + struct device *dev; + va_list vargs; + int ret; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return ERR_PTR(-ENOMEM); + + device_initialize(dev); + + dev->class = &iommu_class; + dev->parent = parent; + dev->groups = groups; + dev_set_drvdata(dev, drvdata); + + va_start(vargs, fmt); + ret = kobject_set_name_vargs(&dev->kobj, fmt, vargs); + va_end(vargs); + if (ret) + goto error; + + ret = device_add(dev); + if (ret) + goto error; + + return dev; + +error: + put_device(dev); + return ERR_PTR(ret); +} + +void iommu_device_destroy(struct device *dev) +{ + if (!dev || IS_ERR(dev)) + return; + + device_unregister(dev); +} + +/* + * IOMMU drivers can indicate a device is managed by a given IOMMU using + * this interface. A link to the device will be created in the "devices" + * directory of the IOMMU device in sysfs and an "iommu" link will be + * created under the linked device, pointing back at the IOMMU device. + */ +int iommu_device_link(struct device *dev, struct device *link) +{ + int ret; + + if (!dev || IS_ERR(dev)) + return -ENODEV; + + ret = sysfs_add_link_to_group(&dev->kobj, "devices", + &link->kobj, dev_name(link)); + if (ret) + return ret; + + ret = sysfs_create_link_nowarn(&link->kobj, &dev->kobj, "iommu"); + if (ret) + sysfs_remove_link_from_group(&dev->kobj, "devices", + dev_name(link)); + + return ret; +} + +void iommu_device_unlink(struct device *dev, struct device *link) +{ + if (!dev || IS_ERR(dev)) + return; + + sysfs_remove_link(&link->kobj, "iommu"); + sysfs_remove_link_from_group(&dev->kobj, "devices", dev_name(link)); +} diff --git a/include/linux/iommu.h b/include/linux/iommu.h index a2e5843b0a22..7fd16e3d1f25 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -187,6 +187,12 @@ 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); +struct device *iommu_device_create(struct device *parent, void *drvdata, + const struct attribute_group **groups, + const char *fmt, ...); +void iommu_device_destroy(struct device *dev); +int iommu_device_link(struct device *dev, struct device *link); +void iommu_device_unlink(struct device *dev, struct device *link); /* Window handling function prototypes */ extern int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr, @@ -397,6 +403,26 @@ static inline int iommu_domain_set_attr(struct iommu_domain *domain, return -EINVAL; } +struct device *iommu_device_create(struct device *parent, void *drvdata, + const struct attribute_group **groups, + const char *fmt, ...) +{ + return ERR_PTR(-ENODEV); +} + +void iommu_device_destroy(struct device *dev) +{ +} + +int iommu_device_link(struct device *dev, struct device *link) +{ + return -EINVAL; +} + +void iommu_device_unlink(struct device *dev, struct device *link) +{ +} + #endif /* CONFIG_IOMMU_API */ #endif /* __LINUX_IOMMU_H */ -- cgit v1.2.3 From a5459cfece880e82778a60e6290ad6c0dd688a06 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 12 Jun 2014 16:12:31 -0600 Subject: iommu/vt-d: Make use of IOMMU sysfs support Register our DRHD IOMMUs, cross link devices, and provide a base set of attributes for the IOMMU. Note that IRQ remapping support parses the DMAR table very early in boot, well before the iommu_class can reasonably be setup, so our registration is split between intel_iommu_init(), which occurs later, and alloc_iommu(), which typically occurs much earlier, but may happen at any time later with IOMMU hot-add support. On a typical desktop system, this provides the following (pruned): $ find /sys | grep dmar /sys/devices/virtual/iommu/dmar0 /sys/devices/virtual/iommu/dmar0/devices /sys/devices/virtual/iommu/dmar0/devices/0000:00:02.0 /sys/devices/virtual/iommu/dmar0/intel-iommu /sys/devices/virtual/iommu/dmar0/intel-iommu/cap /sys/devices/virtual/iommu/dmar0/intel-iommu/ecap /sys/devices/virtual/iommu/dmar0/intel-iommu/address /sys/devices/virtual/iommu/dmar0/intel-iommu/version /sys/devices/virtual/iommu/dmar1 /sys/devices/virtual/iommu/dmar1/devices /sys/devices/virtual/iommu/dmar1/devices/0000:00:00.0 /sys/devices/virtual/iommu/dmar1/devices/0000:00:01.0 /sys/devices/virtual/iommu/dmar1/devices/0000:00:16.0 /sys/devices/virtual/iommu/dmar1/devices/0000:00:1a.0 /sys/devices/virtual/iommu/dmar1/devices/0000:00:1b.0 /sys/devices/virtual/iommu/dmar1/devices/0000:00:1c.0 ... /sys/devices/virtual/iommu/dmar1/intel-iommu /sys/devices/virtual/iommu/dmar1/intel-iommu/cap /sys/devices/virtual/iommu/dmar1/intel-iommu/ecap /sys/devices/virtual/iommu/dmar1/intel-iommu/address /sys/devices/virtual/iommu/dmar1/intel-iommu/version /sys/class/iommu/dmar0 /sys/class/iommu/dmar1 (devices also link back to the dmar units) This makes address, version, capabilities, and extended capabilities available, just like printed on boot. I've tried not to duplicate data that can be found in the DMAR table, with the exception of the address, which provides an easy way to associate the sysfs device with a DRHD entry in the DMAR. It's tempting to add scopes and RMRR data here, but the full DMAR table is already exposed under /sys/firmware/ and therefore already provides a way for userspace to learn such details. Signed-off-by: Alex Williamson Signed-off-by: Joerg Roedel --- .../ABI/testing/sysfs-class-iommu-intel-iommu | 32 +++++++++ drivers/iommu/dmar.c | 9 +++ drivers/iommu/intel-iommu.c | 77 +++++++++++++++++++++- include/linux/intel-iommu.h | 3 + 4 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 Documentation/ABI/testing/sysfs-class-iommu-intel-iommu (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-class-iommu-intel-iommu b/Documentation/ABI/testing/sysfs-class-iommu-intel-iommu new file mode 100644 index 000000000000..258cc246d98e --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-iommu-intel-iommu @@ -0,0 +1,32 @@ +What: /sys/class/iommu//intel-iommu/address +Date: June 2014 +KernelVersion: 3.17 +Contact: Alex Williamson +Description: + Physical address of the VT-d DRHD for this IOMMU. + Format: %llx. This allows association of a sysfs + intel-iommu with a DMAR DRHD table entry. + +What: /sys/class/iommu//intel-iommu/cap +Date: June 2014 +KernelVersion: 3.17 +Contact: Alex Williamson +Description: + The cached hardware capability register value + of this DRHD unit. Format: %llx. + +What: /sys/class/iommu//intel-iommu/ecap +Date: June 2014 +KernelVersion: 3.17 +Contact: Alex Williamson +Description: + The cached hardware extended capability register + value of this DRHD unit. Format: %llx. + +What: /sys/class/iommu//intel-iommu/version +Date: June 2014 +KernelVersion: 3.17 +Contact: Alex Williamson +Description: + The architecture version as reported from the + VT-d VER_REG. Format: %d:%d, major:minor diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index 9a4f05e5b23f..6744e2d4ff6f 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -980,6 +981,12 @@ static int alloc_iommu(struct dmar_drhd_unit *drhd) raw_spin_lock_init(&iommu->register_lock); drhd->iommu = iommu; + + if (intel_iommu_enabled) + iommu->iommu_dev = iommu_device_create(NULL, iommu, + intel_iommu_groups, + iommu->name); + return 0; err_unmap: @@ -991,6 +998,8 @@ static int alloc_iommu(struct dmar_drhd_unit *drhd) static void free_iommu(struct intel_iommu *iommu) { + iommu_device_destroy(iommu->iommu_dev); + if (iommu->irq) { free_irq(iommu->irq, iommu); irq_set_handler_data(iommu->irq, NULL); diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index f38ec7ab7673..f9e5f84ee952 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -3944,6 +3944,63 @@ static struct notifier_block intel_iommu_memory_nb = { .priority = 0 }; + +static ssize_t intel_iommu_show_version(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct intel_iommu *iommu = dev_get_drvdata(dev); + u32 ver = readl(iommu->reg + DMAR_VER_REG); + return sprintf(buf, "%d:%d\n", + DMAR_VER_MAJOR(ver), DMAR_VER_MINOR(ver)); +} +static DEVICE_ATTR(version, S_IRUGO, intel_iommu_show_version, NULL); + +static ssize_t intel_iommu_show_address(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct intel_iommu *iommu = dev_get_drvdata(dev); + return sprintf(buf, "%llx\n", iommu->reg_phys); +} +static DEVICE_ATTR(address, S_IRUGO, intel_iommu_show_address, NULL); + +static ssize_t intel_iommu_show_cap(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct intel_iommu *iommu = dev_get_drvdata(dev); + return sprintf(buf, "%llx\n", iommu->cap); +} +static DEVICE_ATTR(cap, S_IRUGO, intel_iommu_show_cap, NULL); + +static ssize_t intel_iommu_show_ecap(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct intel_iommu *iommu = dev_get_drvdata(dev); + return sprintf(buf, "%llx\n", iommu->ecap); +} +static DEVICE_ATTR(ecap, S_IRUGO, intel_iommu_show_ecap, NULL); + +static struct attribute *intel_iommu_attrs[] = { + &dev_attr_version.attr, + &dev_attr_address.attr, + &dev_attr_cap.attr, + &dev_attr_ecap.attr, + NULL, +}; + +static struct attribute_group intel_iommu_group = { + .name = "intel-iommu", + .attrs = intel_iommu_attrs, +}; + +const struct attribute_group *intel_iommu_groups[] = { + &intel_iommu_group, + NULL, +}; + int __init intel_iommu_init(void) { int ret = -ENODEV; @@ -4015,6 +4072,11 @@ int __init intel_iommu_init(void) init_iommu_pm_ops(); + for_each_active_iommu(iommu, drhd) + iommu->iommu_dev = iommu_device_create(NULL, iommu, + intel_iommu_groups, + iommu->name); + bus_set_iommu(&pci_bus_type, &intel_iommu_ops); bus_register_notifier(&pci_bus_type, &device_nb); if (si_domain && !hw_pass_through) @@ -4358,12 +4420,16 @@ static int intel_iommu_domain_has_cap(struct iommu_domain *domain, static int intel_iommu_add_device(struct device *dev) { + struct intel_iommu *iommu; struct iommu_group *group; u8 bus, devfn; - if (!device_to_iommu(dev, &bus, &devfn)) + iommu = device_to_iommu(dev, &bus, &devfn); + if (!iommu) return -ENODEV; + iommu_device_link(iommu->iommu_dev, dev); + group = iommu_group_get_for_dev(dev); if (IS_ERR(group)) @@ -4375,7 +4441,16 @@ static int intel_iommu_add_device(struct device *dev) static void intel_iommu_remove_device(struct device *dev) { + struct intel_iommu *iommu; + u8 bus, devfn; + + iommu = device_to_iommu(dev, &bus, &devfn); + if (!iommu) + return; + iommu_group_remove_device(dev); + + iommu_device_unlink(iommu->iommu_dev, dev); } static struct iommu_ops intel_iommu_ops = { diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h index 0a2da5188217..a65208a8fe18 100644 --- a/include/linux/intel-iommu.h +++ b/include/linux/intel-iommu.h @@ -336,6 +336,7 @@ struct intel_iommu { #ifdef CONFIG_IRQ_REMAP struct ir_table *ir_table; /* Interrupt remapping info */ #endif + struct device *iommu_dev; /* IOMMU-sysfs device */ int node; }; @@ -365,4 +366,6 @@ extern int qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu); extern int dmar_ir_support(void); +extern const struct attribute_group *intel_iommu_groups[]; + #endif -- cgit v1.2.3 From 3a044178cccfeb8664423c2950c499c3a209ed9f Mon Sep 17 00:00:00 2001 From: Jason Baron Date: Fri, 4 Jul 2014 13:27:04 +0200 Subject: readq/writeq: Add explicit lo_hi_[read|write]_q and hi_lo_[read|write]_q Even on x86-64, I've found the need to break up a readq() into 2 readl() calls. According to the Intel datasheet for the E3-1200 processor: " Software must not access B0/D0/F0 32-bit memory-mapped registers with requests that cross a DW boundary. " (http://www.intel.com/content/www/us/en/processors/xeon/xeon-e3-1200-family-vol-2-datasheet.html p. 16) I can confirm this is true via several hard machine lockups. Thus, add explicit hi_lo_[readq|write]_q and lo_hi_[read|write]_q so that these uses are spelled out. Signed-off-by: Jason Baron Link: http://lkml.kernel.org/r/281f09da7ad01e5cea99737ec34d2399bdbbbf63.1403818526.git.jbaron@akamai.com Signed-off-by: Borislav Petkov --- include/asm-generic/io-64-nonatomic-hi-lo.h | 14 +++++++++----- include/asm-generic/io-64-nonatomic-lo-hi.h | 14 +++++++++----- 2 files changed, 18 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/asm-generic/io-64-nonatomic-hi-lo.h b/include/asm-generic/io-64-nonatomic-hi-lo.h index a6806a94250d..2e29d13fc154 100644 --- a/include/asm-generic/io-64-nonatomic-hi-lo.h +++ b/include/asm-generic/io-64-nonatomic-hi-lo.h @@ -4,8 +4,7 @@ #include #include -#ifndef readq -static inline __u64 readq(const volatile void __iomem *addr) +static inline __u64 hi_lo_readq(const volatile void __iomem *addr) { const volatile u32 __iomem *p = addr; u32 low, high; @@ -15,14 +14,19 @@ static inline __u64 readq(const volatile void __iomem *addr) return low + ((u64)high << 32); } -#endif -#ifndef writeq -static inline void writeq(__u64 val, volatile void __iomem *addr) +static inline void hi_lo_writeq(__u64 val, volatile void __iomem *addr) { writel(val >> 32, addr + 4); writel(val, addr); } + +#ifndef readq +#define readq hi_lo_readq +#endif + +#ifndef writeq +#define writeq hi_lo_writeq #endif #endif /* _ASM_IO_64_NONATOMIC_HI_LO_H_ */ diff --git a/include/asm-generic/io-64-nonatomic-lo-hi.h b/include/asm-generic/io-64-nonatomic-lo-hi.h index ca546b1ff8b5..0efacff0a1ce 100644 --- a/include/asm-generic/io-64-nonatomic-lo-hi.h +++ b/include/asm-generic/io-64-nonatomic-lo-hi.h @@ -4,8 +4,7 @@ #include #include -#ifndef readq -static inline __u64 readq(const volatile void __iomem *addr) +static inline __u64 lo_hi_readq(const volatile void __iomem *addr) { const volatile u32 __iomem *p = addr; u32 low, high; @@ -15,14 +14,19 @@ static inline __u64 readq(const volatile void __iomem *addr) return low + ((u64)high << 32); } -#endif -#ifndef writeq -static inline void writeq(__u64 val, volatile void __iomem *addr) +static inline void lo_hi_writeq(__u64 val, volatile void __iomem *addr) { writel(val, addr); writel(val >> 32, addr + 4); } + +#ifndef readq +#define readq lo_hi_readq +#endif + +#ifndef writeq +#define writeq lo_hi_writeq #endif #endif /* _ASM_IO_64_NONATOMIC_LO_HI_H_ */ -- cgit v1.2.3 From 46ebeb26cd976b9902d074c9a51f091a5744f9f3 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 4 Jul 2014 13:23:38 +0200 Subject: Bluetooth: Fix constant for public address configuration The public address configuration option is value 0x02 since the generic external configuration is value 0x01. So adjust this accordingly and also add the value 0x01 to the list. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 0579eb3952e3..3984678ffab1 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -472,7 +472,8 @@ struct mgmt_rp_read_unconf_index_list { __le16 index[0]; } __packed; -#define MGMT_OPTION_PUBLIC_ADDRESS 0x00000001 +#define MGMT_OPTION_EXTERNAL_CONFIG 0x00000001 +#define MGMT_OPTION_PUBLIC_ADDRESS 0x00000002 #define MGMT_OP_READ_CONFIG_INFO 0x0037 #define MGMT_READ_CONFIG_INFO_SIZE 0 -- cgit v1.2.3 From 8c98716601bc05091ff49aa8ebf5299a0c6604a0 Mon Sep 17 00:00:00 2001 From: Stephan Mueller Date: Sat, 28 Jun 2014 21:58:24 +0200 Subject: crypto: drbg - use of kernel linked list The DRBG-style linked list to manage input data that is fed into the cipher invocations is replaced with the kernel linked list implementation. The change is transparent to users of the interfaces offered by the DRBG. Therefore, no changes to the testmgr code is needed. Reported-by: kbuild test robot Signed-off-by: Stephan Mueller Signed-off-by: Herbert Xu --- crypto/drbg.c | 233 +++++++++++++++++++++++++++----------------------- include/crypto/drbg.h | 7 +- 2 files changed, 128 insertions(+), 112 deletions(-) (limited to 'include') diff --git a/crypto/drbg.c b/crypto/drbg.c index 3f0b7e0f8bac..d6621a6181d7 100644 --- a/crypto/drbg.c +++ b/crypto/drbg.c @@ -370,13 +370,12 @@ static int drbg_fini_sym_kernel(struct drbg_state *drbg); /* BCC function for CTR DRBG as defined in 10.4.3 */ static int drbg_ctr_bcc(struct drbg_state *drbg, unsigned char *out, const unsigned char *key, - struct drbg_string *in) + struct list_head *in) { - int ret = -EFAULT; - struct drbg_string *curr = in; - size_t inpos = curr->len; - const unsigned char *pos = curr->buf; + int ret = 0; + struct drbg_string *curr = NULL; struct drbg_string data; + short cnt = 0; drbg_string_fill(&data, out, drbg_blocklen(drbg)); @@ -384,39 +383,29 @@ static int drbg_ctr_bcc(struct drbg_state *drbg, memset(out, 0, drbg_blocklen(drbg)); /* 10.4.3 step 2 / 4 */ - while (inpos) { - short cnt = 0; + list_for_each_entry(curr, in, list) { + const unsigned char *pos = curr->buf; + size_t len = curr->len; /* 10.4.3 step 4.1 */ - for (cnt = 0; cnt < drbg_blocklen(drbg); cnt++) { - out[cnt] ^= *pos; - pos++; inpos--; - /* - * The following branch implements the linked list - * iteration of drbg_string *in. If we are at the - * end of the current list member, we have to start - * using the next member if available. The inpos - * value always points to the current byte and will - * be zero if we have processed the last byte of - * the last linked list member. - */ - if (0 == inpos) { - curr = curr->next; - if (NULL != curr) { - pos = curr->buf; - inpos = curr->len; - } else { - inpos = 0; - break; - } + while (len) { + /* 10.4.3 step 4.2 */ + if (drbg_blocklen(drbg) == cnt) { + cnt = 0; + ret = drbg_kcapi_sym(drbg, key, out, &data); + if (ret) + return ret; } + out[cnt] ^= *pos; + pos++; + cnt++; + len--; } - /* 10.4.3 step 4.2 */ - ret = drbg_kcapi_sym(drbg, key, out, &data); - if (ret) - return ret; - /* 10.4.3 step 2 */ } - return 0; + /* 10.4.3 step 4.2 for last block */ + if (cnt) + ret = drbg_kcapi_sym(drbg, key, out, &data); + + return ret; } /* @@ -461,13 +450,13 @@ static int drbg_ctr_bcc(struct drbg_state *drbg, /* Derivation Function for CTR DRBG as defined in 10.4.2 */ static int drbg_ctr_df(struct drbg_state *drbg, unsigned char *df_data, size_t bytes_to_return, - struct drbg_string *addtl) + struct list_head *seedlist) { int ret = -EFAULT; unsigned char L_N[8]; /* S3 is input */ struct drbg_string S1, S2, S4, cipherin; - struct drbg_string *tempstr = addtl; + LIST_HEAD(bcc_list); unsigned char *pad = df_data + drbg_statelen(drbg); unsigned char *iv = pad + drbg_blocklen(drbg); unsigned char *temp = iv + drbg_blocklen(drbg); @@ -484,6 +473,7 @@ static int drbg_ctr_df(struct drbg_state *drbg, unsigned char *X; size_t generated_len = 0; size_t inputlen = 0; + struct drbg_string *seed = NULL; memset(pad, 0, drbg_blocklen(drbg)); memset(iv, 0, drbg_blocklen(drbg)); @@ -496,8 +486,8 @@ static int drbg_ctr_df(struct drbg_state *drbg, return -EINVAL; /* 10.4.2 step 2 -- calculate the entire length of all input data */ - for (; NULL != tempstr; tempstr = tempstr->next) - inputlen += tempstr->len; + list_for_each_entry(seed, seedlist, list) + inputlen += seed->len; drbg_int2byte(&L_N[0], inputlen, 4); /* 10.4.2 step 3 */ @@ -518,20 +508,12 @@ static int drbg_ctr_df(struct drbg_state *drbg, /* 10.4.2 step 4 -- first fill the linked list and then order it */ drbg_string_fill(&S1, iv, drbg_blocklen(drbg)); + list_add_tail(&S1.list, &bcc_list); drbg_string_fill(&S2, L_N, sizeof(L_N)); + list_add_tail(&S2.list, &bcc_list); + list_splice_tail(seedlist, &bcc_list); drbg_string_fill(&S4, pad, padlen); - S1.next = &S2; - S2.next = addtl; - - /* - * Splice in addtl between S2 and S4 -- we place S4 at the end - * of the input data chain. As this code is only triggered when - * addtl is not NULL, no NULL checks are necessary. - */ - tempstr = addtl; - while (tempstr->next) - tempstr = tempstr->next; - tempstr->next = &S4; + list_add_tail(&S4.list, &bcc_list); /* 10.4.2 step 9 */ while (templen < (drbg_keylen(drbg) + (drbg_blocklen(drbg)))) { @@ -542,7 +524,7 @@ static int drbg_ctr_df(struct drbg_state *drbg, */ drbg_int2byte(iv, i, 4); /* 10.4.2 step 9.2 -- BCC and concatenation with temp */ - ret = drbg_ctr_bcc(drbg, temp + templen, K, &S1); + ret = drbg_ctr_bcc(drbg, temp + templen, K, &bcc_list); if (ret) goto out; /* 10.4.2 step 9.3 */ @@ -586,8 +568,8 @@ out: } /* update function of CTR DRBG as defined in 10.2.1.2 */ -static int drbg_ctr_update(struct drbg_state *drbg, - struct drbg_string *addtl, int reseed) +static int drbg_ctr_update(struct drbg_state *drbg, struct list_head *seed, + int reseed) { int ret = -EFAULT; /* 10.2.1.2 step 1 */ @@ -603,9 +585,8 @@ static int drbg_ctr_update(struct drbg_state *drbg, memset(df_data, 0, drbg_statelen(drbg)); /* 10.2.1.3.2 step 2 and 10.2.1.4.2 step 2 */ - if (addtl && 0 < addtl->len) { - ret = drbg_ctr_df(drbg, df_data, drbg_statelen(drbg), - addtl); + if (seed) { + ret = drbg_ctr_df(drbg, df_data, drbg_statelen(drbg), seed); if (ret) goto out; } @@ -665,8 +646,10 @@ static int drbg_ctr_generate(struct drbg_state *drbg, /* 10.2.1.5.2 step 2 */ if (addtl && 0 < addtl->len) { - addtl->next = NULL; - ret = drbg_ctr_update(drbg, addtl, 1); + LIST_HEAD(addtllist); + + list_add_tail(&addtl->list, &addtllist); + ret = drbg_ctr_update(drbg, &addtllist, 1); if (ret) return 0; } @@ -697,16 +680,21 @@ static int drbg_ctr_generate(struct drbg_state *drbg, drbg_add_buf(drbg->V, drbg_blocklen(drbg), &prefix, 1); } - /* 10.2.1.5.2 step 6 */ - if (addtl) - addtl->next = NULL; /* + * 10.2.1.5.2 step 6 * The following call invokes the DF function again which could be * optimized. In step 2, the "additional_input" after step 2 is the * output of the DF function. If this result would be saved, the DF * function would not need to be invoked again at this point. */ - ret = drbg_ctr_update(drbg, addtl, 1); + if (addtl && 0 < addtl->len) { + LIST_HEAD(addtllist); + + list_add_tail(&addtl->list, &addtllist); + ret = drbg_ctr_update(drbg, &addtllist, 1); + } else { + ret = drbg_ctr_update(drbg, NULL, 1); + } if (ret) len = ret; @@ -729,19 +717,21 @@ static struct drbg_state_ops drbg_ctr_ops = { #if defined(CONFIG_CRYPTO_DRBG_HASH) || defined(CONFIG_CRYPTO_DRBG_HMAC) static int drbg_kcapi_hash(struct drbg_state *drbg, const unsigned char *key, - unsigned char *outval, const struct drbg_string *in); + unsigned char *outval, const struct list_head *in); static int drbg_init_hash_kernel(struct drbg_state *drbg); static int drbg_fini_hash_kernel(struct drbg_state *drbg); #endif /* (CONFIG_CRYPTO_DRBG_HASH || CONFIG_CRYPTO_DRBG_HMAC) */ #ifdef CONFIG_CRYPTO_DRBG_HMAC /* update function of HMAC DRBG as defined in 10.1.2.2 */ -static int drbg_hmac_update(struct drbg_state *drbg, - struct drbg_string *seed, int reseed) +static int drbg_hmac_update(struct drbg_state *drbg, struct list_head *seed, + int reseed) { int ret = -EFAULT; int i = 0; - struct drbg_string seed1, seed2, cipherin; + struct drbg_string seed1, seed2, vdata; + LIST_HEAD(seedlist); + LIST_HEAD(vdatalist); if (!reseed) { /* 10.1.2.3 step 2 */ @@ -750,13 +740,16 @@ static int drbg_hmac_update(struct drbg_state *drbg, } drbg_string_fill(&seed1, drbg->V, drbg_statelen(drbg)); + list_add_tail(&seed1.list, &seedlist); /* buffer of seed2 will be filled in for loop below with one byte */ drbg_string_fill(&seed2, NULL, 1); - seed1.next = &seed2; + list_add_tail(&seed2.list, &seedlist); /* input data of seed is allowed to be NULL at this point */ - seed2.next = seed; + if (seed) + list_splice_tail(seed, &seedlist); - drbg_string_fill(&cipherin, drbg->V, drbg_statelen(drbg)); + drbg_string_fill(&vdata, drbg->V, drbg_statelen(drbg)); + list_add_tail(&vdata.list, &vdatalist); for (i = 2; 0 < i; i--) { /* first round uses 0x0, second 0x1 */ unsigned char prefix = DRBG_PREFIX0; @@ -764,17 +757,17 @@ static int drbg_hmac_update(struct drbg_state *drbg, prefix = DRBG_PREFIX1; /* 10.1.2.2 step 1 and 4 -- concatenation and HMAC for key */ seed2.buf = &prefix; - ret = drbg_kcapi_hash(drbg, drbg->C, drbg->C, &seed1); + ret = drbg_kcapi_hash(drbg, drbg->C, drbg->C, &seedlist); if (ret) return ret; /* 10.1.2.2 step 2 and 5 -- HMAC for V */ - ret = drbg_kcapi_hash(drbg, drbg->C, drbg->V, &cipherin); + ret = drbg_kcapi_hash(drbg, drbg->C, drbg->V, &vdatalist); if (ret) return ret; /* 10.1.2.2 step 3 */ - if (!seed || 0 == seed->len) + if (!seed) return ret; } @@ -790,20 +783,24 @@ static int drbg_hmac_generate(struct drbg_state *drbg, int len = 0; int ret = 0; struct drbg_string data; + LIST_HEAD(datalist); /* 10.1.2.5 step 2 */ if (addtl && 0 < addtl->len) { - addtl->next = NULL; - ret = drbg_hmac_update(drbg, addtl, 1); + LIST_HEAD(addtllist); + + list_add_tail(&addtl->list, &addtllist); + ret = drbg_hmac_update(drbg, &addtllist, 1); if (ret) return ret; } drbg_string_fill(&data, drbg->V, drbg_statelen(drbg)); + list_add_tail(&data.list, &datalist); while (len < buflen) { unsigned int outlen = 0; /* 10.1.2.5 step 4.1 */ - ret = drbg_kcapi_hash(drbg, drbg->C, drbg->V, &data); + ret = drbg_kcapi_hash(drbg, drbg->C, drbg->V, &datalist); if (ret) return ret; outlen = (drbg_blocklen(drbg) < (buflen - len)) ? @@ -817,9 +814,14 @@ static int drbg_hmac_generate(struct drbg_state *drbg, } /* 10.1.2.5 step 6 */ - if (addtl) - addtl->next = NULL; - ret = drbg_hmac_update(drbg, addtl, 1); + if (addtl && 0 < addtl->len) { + LIST_HEAD(addtllist); + + list_add_tail(&addtl->list, &addtllist); + ret = drbg_hmac_update(drbg, &addtllist, 1); + } else { + ret = drbg_hmac_update(drbg, NULL, 1); + } if (ret) return ret; @@ -858,13 +860,13 @@ static struct drbg_state_ops drbg_hmac_ops = { /* Derivation Function for Hash DRBG as defined in 10.4.1 */ static int drbg_hash_df(struct drbg_state *drbg, unsigned char *outval, size_t outlen, - struct drbg_string *entropy) + struct list_head *entropylist) { int ret = 0; size_t len = 0; unsigned char input[5]; unsigned char *tmp = drbg->scratchpad + drbg_statelen(drbg); - struct drbg_string data1; + struct drbg_string data; memset(tmp, 0, drbg_blocklen(drbg)); @@ -873,14 +875,14 @@ static int drbg_hash_df(struct drbg_state *drbg, drbg_int2byte(&input[1], (outlen * 8), 4); /* 10.4.1 step 4.1 -- concatenation of data for input into hash */ - drbg_string_fill(&data1, input, 5); - data1.next = entropy; + drbg_string_fill(&data, input, 5); + list_add(&data.list, entropylist); /* 10.4.1 step 4 */ while (len < outlen) { short blocklen = 0; /* 10.4.1 step 4.1 */ - ret = drbg_kcapi_hash(drbg, NULL, tmp, &data1); + ret = drbg_kcapi_hash(drbg, NULL, tmp, entropylist); if (ret) goto out; /* 10.4.1 step 4.2 */ @@ -897,11 +899,13 @@ out: } /* update function for Hash DRBG as defined in 10.1.1.2 / 10.1.1.3 */ -static int drbg_hash_update(struct drbg_state *drbg, struct drbg_string *seed, +static int drbg_hash_update(struct drbg_state *drbg, struct list_head *seed, int reseed) { int ret = 0; struct drbg_string data1, data2; + LIST_HEAD(datalist); + LIST_HEAD(datalist2); unsigned char *V = drbg->scratchpad; unsigned char prefix = DRBG_PREFIX1; @@ -913,26 +917,25 @@ static int drbg_hash_update(struct drbg_state *drbg, struct drbg_string *seed, /* 10.1.1.3 step 1 */ memcpy(V, drbg->V, drbg_statelen(drbg)); drbg_string_fill(&data1, &prefix, 1); + list_add_tail(&data1.list, &datalist); drbg_string_fill(&data2, V, drbg_statelen(drbg)); - data1.next = &data2; - data2.next = seed; - } else { - drbg_string_fill(&data1, seed->buf, seed->len); - data1.next = seed->next; + list_add_tail(&data2.list, &datalist); } + list_splice_tail(seed, &datalist); /* 10.1.1.2 / 10.1.1.3 step 2 and 3 */ - ret = drbg_hash_df(drbg, drbg->V, drbg_statelen(drbg), &data1); + ret = drbg_hash_df(drbg, drbg->V, drbg_statelen(drbg), &datalist); if (ret) goto out; /* 10.1.1.2 / 10.1.1.3 step 4 */ prefix = DRBG_PREFIX0; drbg_string_fill(&data1, &prefix, 1); + list_add_tail(&data1.list, &datalist2); drbg_string_fill(&data2, drbg->V, drbg_statelen(drbg)); - data1.next = &data2; + list_add_tail(&data2.list, &datalist2); /* 10.1.1.2 / 10.1.1.3 step 4 */ - ret = drbg_hash_df(drbg, drbg->C, drbg_statelen(drbg), &data1); + ret = drbg_hash_df(drbg, drbg->C, drbg_statelen(drbg), &datalist2); out: memset(drbg->scratchpad, 0, drbg_statelen(drbg)); @@ -945,7 +948,7 @@ static int drbg_hash_process_addtl(struct drbg_state *drbg, { int ret = 0; struct drbg_string data1, data2; - struct drbg_string *data3; + LIST_HEAD(datalist); unsigned char prefix = DRBG_PREFIX2; /* this is value w as per documentation */ @@ -958,11 +961,10 @@ static int drbg_hash_process_addtl(struct drbg_state *drbg, /* 10.1.1.4 step 2a */ drbg_string_fill(&data1, &prefix, 1); drbg_string_fill(&data2, drbg->V, drbg_statelen(drbg)); - data3 = addtl; - data1.next = &data2; - data2.next = data3; - data3->next = NULL; - ret = drbg_kcapi_hash(drbg, NULL, drbg->scratchpad, &data1); + list_add_tail(&data1.list, &datalist); + list_add_tail(&data2.list, &datalist); + list_add_tail(&addtl->list, &datalist); + ret = drbg_kcapi_hash(drbg, NULL, drbg->scratchpad, &datalist); if (ret) goto out; @@ -985,6 +987,7 @@ static int drbg_hash_hashgen(struct drbg_state *drbg, unsigned char *src = drbg->scratchpad; unsigned char *dst = drbg->scratchpad + drbg_statelen(drbg); struct drbg_string data; + LIST_HEAD(datalist); unsigned char prefix = DRBG_PREFIX1; memset(src, 0, drbg_statelen(drbg)); @@ -994,10 +997,11 @@ static int drbg_hash_hashgen(struct drbg_state *drbg, memcpy(src, drbg->V, drbg_statelen(drbg)); drbg_string_fill(&data, src, drbg_statelen(drbg)); + list_add_tail(&data.list, &datalist); while (len < buflen) { unsigned int outlen = 0; /* 10.1.1.4 step hashgen 4.1 */ - ret = drbg_kcapi_hash(drbg, NULL, dst, &data); + ret = drbg_kcapi_hash(drbg, NULL, dst, &datalist); if (ret) { len = ret; goto out; @@ -1032,6 +1036,7 @@ static int drbg_hash_generate(struct drbg_state *drbg, unsigned char req[8]; unsigned char prefix = DRBG_PREFIX3; struct drbg_string data1, data2; + LIST_HEAD(datalist); /* 10.1.1.4 step 2 */ ret = drbg_hash_process_addtl(drbg, addtl); @@ -1044,9 +1049,10 @@ static int drbg_hash_generate(struct drbg_state *drbg, memset(drbg->scratchpad, 0, drbg_blocklen(drbg)); /* 10.1.1.4 step 4 */ drbg_string_fill(&data1, &prefix, 1); + list_add_tail(&data1.list, &datalist); drbg_string_fill(&data2, drbg->V, drbg_statelen(drbg)); - data1.next = &data2; - ret = drbg_kcapi_hash(drbg, NULL, drbg->scratchpad, &data1); + list_add_tail(&data2.list, &datalist); + ret = drbg_kcapi_hash(drbg, NULL, drbg->scratchpad, &datalist); if (ret) { len = ret; goto out; @@ -1099,6 +1105,7 @@ static int drbg_seed(struct drbg_state *drbg, struct drbg_string *pers, unsigned char *entropy = NULL; size_t entropylen = 0; struct drbg_string data1; + LIST_HEAD(seedlist); /* 9.1 / 9.2 / 9.3.1 step 3 */ if (pers && pers->len > (drbg_max_addtl(drbg))) { @@ -1133,18 +1140,19 @@ static int drbg_seed(struct drbg_state *drbg, struct drbg_string *pers, get_random_bytes(entropy, entropylen); drbg_string_fill(&data1, entropy, entropylen); } + list_add_tail(&data1.list, &seedlist); /* * concatenation of entropy with personalization str / addtl input) * the variable pers is directly handed in by the caller, so check its * contents whether it is appropriate */ - if (pers && pers->buf && 0 < pers->len && NULL == pers->next) { - data1.next = pers; + if (pers && pers->buf && 0 < pers->len) { + list_add_tail(&pers->list, &seedlist); pr_devel("DRBG: using personalization string\n"); } - ret = drbg->d_ops->update(drbg, &data1, reseed); + ret = drbg->d_ops->update(drbg, &seedlist, reseed); if (ret) goto out; @@ -1642,15 +1650,16 @@ static int drbg_fini_hash_kernel(struct drbg_state *drbg) } static int drbg_kcapi_hash(struct drbg_state *drbg, const unsigned char *key, - unsigned char *outval, const struct drbg_string *in) + unsigned char *outval, const struct list_head *in) { struct sdesc *sdesc = (struct sdesc *)drbg->priv_data; + struct drbg_string *input = NULL; if (key) crypto_shash_setkey(sdesc->shash.tfm, key, drbg_statelen(drbg)); crypto_shash_init(&sdesc->shash); - for (; NULL != in; in = in->next) - crypto_shash_update(&sdesc->shash, in->buf, in->len); + list_for_each_entry(input, in, list) + crypto_shash_update(&sdesc->shash, input->buf, input->len); return crypto_shash_final(&sdesc->shash, outval); } #endif /* (CONFIG_CRYPTO_DRBG_HASH || CONFIG_CRYPTO_DRBG_HMAC) */ @@ -1785,12 +1794,15 @@ static int drbg_kcapi_random(struct crypto_rng *tfm, u8 *rdata, return drbg_generate_long(drbg, rdata, dlen, NULL); } else { struct drbg_gen *data = (struct drbg_gen *)rdata; + struct drbg_string addtl; /* catch NULL pointer */ if (!data) return 0; drbg_set_testdata(drbg, data->test_data); + /* linked list variable is now local to allow modification */ + drbg_string_fill(&addtl, data->addtl->buf, data->addtl->len); return drbg_generate_long(drbg, data->outbuf, data->outlen, - data->addtl); + &addtl); } } @@ -1820,7 +1832,10 @@ static int drbg_kcapi_reset(struct crypto_rng *tfm, u8 *seed, unsigned int slen) if (!data) return drbg_instantiate(drbg, NULL, coreref, pr); drbg_set_testdata(drbg, data->test_data); - return drbg_instantiate(drbg, data->addtl, coreref, pr); + /* linked list variable is now local to allow modification */ + drbg_string_fill(&seed_string, data->addtl->buf, + data->addtl->len); + return drbg_instantiate(drbg, &seed_string, coreref, pr); } } diff --git a/include/crypto/drbg.h b/include/crypto/drbg.h index b507c5b6020a..4065dfca146a 100644 --- a/include/crypto/drbg.h +++ b/include/crypto/drbg.h @@ -50,6 +50,7 @@ #include #include #include +#include /* * Concatenation Helper and string operation helper @@ -64,7 +65,7 @@ struct drbg_string { const unsigned char *buf; size_t len; - struct drbg_string *next; + struct list_head list; }; static inline void drbg_string_fill(struct drbg_string *string, @@ -72,7 +73,7 @@ static inline void drbg_string_fill(struct drbg_string *string, { string->buf = buf; string->len = len; - string->next = NULL; + INIT_LIST_HEAD(&string->list); } struct drbg_state; @@ -97,7 +98,7 @@ struct drbg_core { }; struct drbg_state_ops { - int (*update)(struct drbg_state *drbg, struct drbg_string *seed, + int (*update)(struct drbg_state *drbg, struct list_head *seed, int reseed); int (*generate)(struct drbg_state *drbg, unsigned char *buf, unsigned int buflen, -- cgit v1.2.3 From 118305b50a73b46ff6c1c0453d7ae642b26dff82 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 4 Jul 2014 16:08:02 +0200 Subject: Bluetooth: Document the existing device quirks The current existing device quirks are not documented. So instead of spreading bits and pieces somewhere in the code, add proper comments on where these quirks can be used and what behavior they change. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 606a9b1466a3..744713cd5335 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -81,9 +81,34 @@ /* HCI device quirks */ enum { + /* When this quirk is set, the HCI Reset command is send when + * closing the transport instead of when opening it. + * + * This quirk must be set before hci_register_dev is called. + */ HCI_QUIRK_RESET_ON_CLOSE, + + /* When this quirk is set, the device is turned into a raw-only + * device and it will stay in unconfigured state. + * + * This quirk must be set before hci_register_dev is called. + */ HCI_QUIRK_RAW_DEVICE, + + /* When this quirk is set, the buffer sizes reported by + * HCI Read Buffer Size command are corrected if invalid. + * + * This quirk must be set before hci_register_dev is called. + */ HCI_QUIRK_FIXUP_BUFFER_SIZE, + + /* When this quirk is set, then no stored link key handling + * is performed. This is mainly due to the fact that the + * HCI Delete Stored Link Key command is advertised, but + * not supported. + * + * This quirk must be set before hci_register_dev is called. + */ HCI_QUIRK_BROKEN_STORED_LINK_KEY, }; -- cgit v1.2.3 From 89bc22d23f63c2d437f677d7eae0fa922bedcdcb Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 4 Jul 2014 16:54:37 +0200 Subject: Bluetooth: Add quirk for invalid controller address setting When a Bluetooth controller does not have a valid public Bluetooth address, then allow the driver to indicate this. If the quirk is set, the Bluetooth core will switch to unconfigured state first and will allow userspace to configure the address before starting the full initialization of the controller. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 10 ++++++++++ net/bluetooth/hci_core.c | 6 +++++- net/bluetooth/mgmt.c | 21 +++++++++++++++++---- 3 files changed, 32 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 744713cd5335..148b21699446 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -110,6 +110,16 @@ enum { * This quirk must be set before hci_register_dev is called. */ HCI_QUIRK_BROKEN_STORED_LINK_KEY, + + /* When this quirk is set, the public Bluetooth address + * initially reported by HCI Read BD Address command + * is considered invalid. Controller configuration is + * required before this device can be used. + * + * This quirk can be set before hci_register_dev is called or + * during the hdev->setup vendor callback. + */ + HCI_QUIRK_INVALID_BDADDR, }; /* HCI device flags */ diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 038b4748375b..c92bee84413f 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2246,9 +2246,13 @@ static int hci_dev_do_open(struct hci_dev *hdev) atomic_set(&hdev->cmd_cnt, 1); set_bit(HCI_INIT, &hdev->flags); - if (hdev->setup && test_bit(HCI_SETUP, &hdev->dev_flags)) + if (hdev->setup && test_bit(HCI_SETUP, &hdev->dev_flags)) { ret = hdev->setup(hdev); + if (test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks)) + set_bit(HCI_UNCONFIGURED, &hdev->dev_flags); + } + /* If public address change is configured, ensure that the * address gets programmed. If the driver does not support * changing the public address, fail the power on procedure. diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 90eabcae3ed2..c7e5d4651021 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -441,10 +441,22 @@ static int read_unconf_index_list(struct sock *sk, struct hci_dev *hdev, return err; } +static __le32 get_missing_options(struct hci_dev *hdev) +{ + u32 options = 0; + + if (test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks) && + !bacmp(&hdev->public_addr, BDADDR_ANY)) + options |= MGMT_OPTION_PUBLIC_ADDRESS; + + return cpu_to_le32(options); +} + static int read_config_info(struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len) { struct mgmt_rp_read_config_info rp; + u32 options = 0; BT_DBG("sock %p %s", sk, hdev->name); @@ -452,11 +464,12 @@ static int read_config_info(struct sock *sk, struct hci_dev *hdev, memset(&rp, 0, sizeof(rp)); rp.manufacturer = cpu_to_le16(hdev->manufacturer); + if (hdev->set_bdaddr) - rp.supported_options = cpu_to_le32(MGMT_OPTION_PUBLIC_ADDRESS); - else - rp.supported_options = cpu_to_le32(0); - rp.missing_options = cpu_to_le32(0); + options |= MGMT_OPTION_PUBLIC_ADDRESS; + + rp.supported_options = cpu_to_le32(options); + rp.missing_options = get_missing_options(hdev); hci_dev_unlock(hdev); -- cgit v1.2.3 From 07cf7cbadb4d97a78be61119a406de8fe446467e Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 20 Jun 2014 14:41:13 +0800 Subject: ASoC: add RT286 CODEC driver This patch adds Realtek ALC286 codec driver. ALC286 is a dual mode codec, which can run as HD-A or I2S mode. It is controlled by HD-A verb commands via I2C protocol. The following is the I/O difference between ALC286 and general I2S codecs. 1. A HD-A verb command contains three parts, NID, VID, and PID. And an I2S command contains only two parts: address and data. 2. Not only the register address is written, but the read command also includes the entire write command. 3. rt286 uses different registers for read and write the same bits. We map verb command to regmap structure. However, we read most registers from cache to prevent the asymmetry read/write issue in rt286. Signed-off-by: Bard Liao Signed-off-by: Gustaw Lewandowski Signed-off-by: Mark Brown --- include/sound/rt286.h | 19 + sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/rt286.c | 1208 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/rt286.h | 193 ++++++++ 5 files changed, 1426 insertions(+) create mode 100644 include/sound/rt286.h create mode 100644 sound/soc/codecs/rt286.c create mode 100644 sound/soc/codecs/rt286.h (limited to 'include') diff --git a/include/sound/rt286.h b/include/sound/rt286.h new file mode 100644 index 000000000000..eb773d1485f2 --- /dev/null +++ b/include/sound/rt286.h @@ -0,0 +1,19 @@ +/* + * linux/sound/rt286.h -- Platform data for RT286 + * + * Copyright 2013 Realtek Microelectronics + * + * 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_SND_RT286_H +#define __LINUX_SND_RT286_H + +struct rt286_platform_data { + bool cbj_en; /*combo jack enable*/ + bool gpio2_en; /*GPIO2 enable*/ +}; + +#endif diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index cbfa1e18f651..115e5de7b7eb 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -74,6 +74,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_PCM3008 select SND_SOC_PCM512x_I2C if I2C select SND_SOC_PCM512x_SPI if SPI_MASTER + select SND_SOC_RT286 if I2C select SND_SOC_RT5631 if I2C select SND_SOC_RT5640 if I2C select SND_SOC_RT5645 if I2C @@ -449,6 +450,9 @@ config SND_SOC_RL6231 default m if SND_SOC_RT5645=m default m if SND_SOC_RT5651=m +config SND_SOC_RT286 + tristate + config SND_SOC_RT5631 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index be3377b8d73f..c39449a862b3 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -68,6 +68,7 @@ snd-soc-pcm512x-objs := pcm512x.o snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o snd-soc-pcm512x-spi-objs := pcm512x-spi.o snd-soc-rl6231-objs := rl6231.o +snd-soc-rt286-objs := rt286.o snd-soc-rt5631-objs := rt5631.o snd-soc-rt5640-objs := rt5640.o snd-soc-rt5645-objs := rt5645.o @@ -233,6 +234,7 @@ obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o +obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o obj-$(CONFIG_SND_SOC_RT5645) += snd-soc-rt5645.o diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c new file mode 100644 index 000000000000..acfba9c74c52 --- /dev/null +++ b/sound/soc/codecs/rt286.c @@ -0,0 +1,1208 @@ +/* + * rt286.c -- RT286 ALSA SoC audio codec driver + * + * Copyright 2013 Realtek Semiconductor Corp. + * Author: Bard Liao + * + * 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 +#include +#include + +#include "rt286.h" + +#define RT286_VENDOR_ID 0x10ec0286 + +struct rt286_priv { + struct regmap *regmap; + struct snd_soc_codec *codec; + struct rt286_platform_data pdata; + struct i2c_client *i2c; + struct snd_soc_jack *jack; + struct delayed_work jack_detect_work; + int sys_clk; + struct reg_default *index_cache; +}; + +static struct reg_default rt286_index_def[] = { + { 0x01, 0xaaaa }, + { 0x02, 0x8aaa }, + { 0x03, 0x0002 }, + { 0x04, 0xaf01 }, + { 0x08, 0x000d }, + { 0x09, 0xd810 }, + { 0x0a, 0x0060 }, + { 0x0b, 0x0000 }, + { 0x0f, 0x0000 }, + { 0x19, 0x0a17 }, + { 0x20, 0x0020 }, + { 0x33, 0x0208 }, + { 0x49, 0x0004 }, + { 0x4f, 0x50e9 }, + { 0x50, 0x2c00 }, + { 0x63, 0x2902 }, +}; +#define INDEX_CACHE_SIZE ARRAY_SIZE(rt286_index_def) + +static const struct reg_default rt286_reg[] = { + { 0x00170500, 0x00000400 }, + { 0x00220000, 0x00000031 }, + { 0x00239000, 0x0000007f }, + { 0x0023a000, 0x0000007f }, + { 0x00270500, 0x00000400 }, + { 0x00370500, 0x00000400 }, + { 0x00870500, 0x00000400 }, + { 0x00920000, 0x00000031 }, + { 0x00935000, 0x000000c3 }, + { 0x00936000, 0x000000c3 }, + { 0x00970500, 0x00000400 }, + { 0x00b37000, 0x00000097 }, + { 0x00b37200, 0x00000097 }, + { 0x00b37300, 0x00000097 }, + { 0x00c37000, 0x00000000 }, + { 0x00c37100, 0x00000080 }, + { 0x01270500, 0x00000400 }, + { 0x01370500, 0x00000400 }, + { 0x01371f00, 0x411111f0 }, + { 0x01439000, 0x00000080 }, + { 0x0143a000, 0x00000080 }, + { 0x01470700, 0x00000000 }, + { 0x01470500, 0x00000400 }, + { 0x01470c00, 0x00000000 }, + { 0x01470100, 0x00000000 }, + { 0x01837000, 0x00000000 }, + { 0x01870500, 0x00000400 }, + { 0x02050000, 0x00000000 }, + { 0x02139000, 0x00000080 }, + { 0x0213a000, 0x00000080 }, + { 0x02170100, 0x00000000 }, + { 0x02170500, 0x00000400 }, + { 0x02170700, 0x00000000 }, + { 0x02270100, 0x00000000 }, + { 0x02370100, 0x00000000 }, + { 0x02040000, 0x00004002 }, + { 0x01870700, 0x00000020 }, + { 0x00830000, 0x000000c3 }, + { 0x00930000, 0x000000c3 }, + { 0x01270700, 0x00000000 }, +}; + +static bool rt286_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0 ... 0xff: + case RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID): + case RT286_GET_HP_SENSE: + case RT286_GET_MIC1_SENSE: + case RT286_PROC_COEF: + return true; + default: + return false; + } + + +} + +static bool rt286_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0 ... 0xff: + case RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID): + case RT286_GET_HP_SENSE: + case RT286_GET_MIC1_SENSE: + case RT286_SET_AUDIO_POWER: + case RT286_SET_HPO_POWER: + case RT286_SET_SPK_POWER: + case RT286_SET_DMIC1_POWER: + case RT286_SPK_MUX: + case RT286_HPO_MUX: + case RT286_ADC0_MUX: + case RT286_ADC1_MUX: + case RT286_SET_MIC1: + case RT286_SET_PIN_HPO: + case RT286_SET_PIN_SPK: + case RT286_SET_PIN_DMIC1: + case RT286_SPK_EAPD: + case RT286_SET_AMP_GAIN_HPO: + case RT286_SET_DMIC2_DEFAULT: + case RT286_DACL_GAIN: + case RT286_DACR_GAIN: + case RT286_ADCL_GAIN: + case RT286_ADCR_GAIN: + case RT286_MIC_GAIN: + case RT286_SPOL_GAIN: + case RT286_SPOR_GAIN: + case RT286_HPOL_GAIN: + case RT286_HPOR_GAIN: + case RT286_F_DAC_SWITCH: + case RT286_F_RECMIX_SWITCH: + case RT286_REC_MIC_SWITCH: + case RT286_REC_I2S_SWITCH: + case RT286_REC_LINE_SWITCH: + case RT286_REC_BEEP_SWITCH: + case RT286_DAC_FORMAT: + case RT286_ADC_FORMAT: + case RT286_COEF_INDEX: + case RT286_PROC_COEF: + case RT286_SET_AMP_GAIN_ADC_IN1: + case RT286_SET_AMP_GAIN_ADC_IN2: + case RT286_SET_POWER(RT286_DAC_OUT1): + case RT286_SET_POWER(RT286_DAC_OUT2): + case RT286_SET_POWER(RT286_ADC_IN1): + case RT286_SET_POWER(RT286_ADC_IN2): + case RT286_SET_POWER(RT286_DMIC2): + case RT286_SET_POWER(RT286_MIC1): + return true; + default: + return false; + } +} + +static int rt286_hw_write(void *context, unsigned int reg, unsigned int value) +{ + struct i2c_client *client = context; + struct rt286_priv *rt286 = i2c_get_clientdata(client); + u8 data[4]; + int ret, i; + + /*handle index registers*/ + if (reg <= 0xff) { + rt286_hw_write(client, RT286_COEF_INDEX, reg); + reg = RT286_PROC_COEF; + for (i = 0; i < INDEX_CACHE_SIZE; i++) { + if (reg == rt286->index_cache[i].reg) { + rt286->index_cache[i].def = value; + break; + } + + } + } + + data[0] = (reg >> 24) & 0xff; + data[1] = (reg >> 16) & 0xff; + /* + * 4 bit VID: reg should be 0 + * 12 bit VID: value should be 0 + * So we use an OR operator to handle it rather than use if condition. + */ + data[2] = ((reg >> 8) & 0xff) | ((value >> 8) & 0xff); + data[3] = value & 0xff; + + ret = i2c_master_send(client, data, 4); + + if (ret == 4) + return 0; + else + pr_err("ret=%d\n", ret); + if (ret < 0) + return ret; + else + return -EIO; +} + +static int rt286_hw_read(void *context, unsigned int reg, unsigned int *value) +{ + struct i2c_client *client = context; + struct i2c_msg xfer[2]; + int ret; + __be32 be_reg; + unsigned int index, vid, buf = 0x0; + + /*handle index registers*/ + if (reg <= 0xff) { + rt286_hw_write(client, RT286_COEF_INDEX, reg); + reg = RT286_PROC_COEF; + } + + reg = reg | 0x80000; + vid = (reg >> 8) & 0xfff; + + if (AC_VERB_GET_AMP_GAIN_MUTE == (vid & 0xf00)) { + index = (reg >> 8) & 0xf; + reg = (reg & ~0xf0f) | index; + } + be_reg = cpu_to_be32(reg); + + /* Write register */ + xfer[0].addr = client->addr; + xfer[0].flags = 0; + xfer[0].len = 4; + xfer[0].buf = (u8 *)&be_reg; + + /* Read data */ + xfer[1].addr = client->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = 4; + xfer[1].buf = (u8 *)&buf; + + ret = i2c_transfer(client->adapter, xfer, 2); + if (ret < 0) + return ret; + else if (ret != 2) + return -EIO; + + *value = be32_to_cpu(buf); + + return 0; +} + +static void rt286_index_sync(struct snd_soc_codec *codec) +{ + struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec); + int i; + + for (i = 0; i < INDEX_CACHE_SIZE; i++) { + snd_soc_write(codec, rt286->index_cache[i].reg, + rt286->index_cache[i].def); + } +} + +static int rt286_support_power_controls[] = { + RT286_DAC_OUT1, + RT286_DAC_OUT2, + RT286_ADC_IN1, + RT286_ADC_IN2, + RT286_MIC1, + RT286_DMIC1, + RT286_DMIC2, + RT286_SPK_OUT, + RT286_HP_OUT, +}; +#define RT286_POWER_REG_LEN ARRAY_SIZE(rt286_support_power_controls) + +static int rt286_jack_detect(struct snd_soc_codec *codec, bool *hp, bool *mic) +{ + struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec); + unsigned int val, buf; + int i; + + *hp = false; + *mic = false; + + if (rt286->pdata.cbj_en) { + buf = snd_soc_read(codec, RT286_GET_HP_SENSE); + *hp = buf & 0x80000000; + if (*hp) { + /* power on HV,VERF */ + snd_soc_update_bits(codec, + RT286_POWER_CTRL1, 0x1001, 0x0); + /* power LDO1 */ + snd_soc_update_bits(codec, + RT286_POWER_CTRL2, 0x4, 0x4); + snd_soc_write(codec, RT286_SET_MIC1, 0x24); + val = snd_soc_read(codec, RT286_CBJ_CTRL2); + + msleep(200); + i = 40; + while (((val & 0x0800) == 0) && (i > 0)) { + val = snd_soc_read(codec, + RT286_CBJ_CTRL2); + i--; + msleep(20); + } + + if (0x0400 == (val & 0x0700)) { + *mic = false; + + snd_soc_write(codec, + RT286_SET_MIC1, 0x20); + /* power off HV,VERF */ + snd_soc_update_bits(codec, + RT286_POWER_CTRL1, 0x1001, 0x1001); + snd_soc_update_bits(codec, + RT286_A_BIAS_CTRL3, 0xc000, 0x0000); + snd_soc_update_bits(codec, + RT286_CBJ_CTRL1, 0x0030, 0x0000); + snd_soc_update_bits(codec, + RT286_A_BIAS_CTRL2, 0xc000, 0x0000); + } else if ((0x0200 == (val & 0x0700)) || + (0x0100 == (val & 0x0700))) { + *mic = true; + snd_soc_update_bits(codec, + RT286_A_BIAS_CTRL3, 0xc000, 0x8000); + snd_soc_update_bits(codec, + RT286_CBJ_CTRL1, 0x0030, 0x0020); + snd_soc_update_bits(codec, + RT286_A_BIAS_CTRL2, 0xc000, 0x8000); + } else { + *mic = false; + } + + snd_soc_update_bits(codec, + RT286_MISC_CTRL1, + 0x0060, 0x0000); + } else { + snd_soc_update_bits(codec, + RT286_MISC_CTRL1, + 0x0060, 0x0020); + snd_soc_update_bits(codec, + RT286_A_BIAS_CTRL3, + 0xc000, 0x8000); + snd_soc_update_bits(codec, + RT286_CBJ_CTRL1, + 0x0030, 0x0020); + snd_soc_update_bits(codec, + RT286_A_BIAS_CTRL2, + 0xc000, 0x8000); + + *mic = false; + } + } else { + buf = snd_soc_read(codec, RT286_GET_HP_SENSE); + *hp = buf & 0x80000000; + buf = snd_soc_read(codec, RT286_GET_MIC1_SENSE); + *mic = buf & 0x80000000; + } + + return 0; +} + +static void rt286_jack_detect_work(struct work_struct *work) +{ + struct rt286_priv *rt286 = + container_of(work, struct rt286_priv, jack_detect_work.work); + int status = 0; + bool hp = false; + bool mic = false; + + rt286_jack_detect(rt286->codec, &hp, &mic); + + if (hp == true) + status |= SND_JACK_HEADPHONE; + + if (mic == true) + status |= SND_JACK_MICROPHONE; + + snd_soc_jack_report(rt286->jack, status, + SND_JACK_MICROPHONE | SND_JACK_HEADPHONE); +} + +int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack) +{ + struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec); + + rt286->jack = jack; + + /* Send an initial empty report */ + snd_soc_jack_report(rt286->jack, 0, + SND_JACK_MICROPHONE | SND_JACK_HEADPHONE); + + return 0; +} +EXPORT_SYMBOL_GPL(rt286_mic_detect); + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6350, 50, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); + +static const struct snd_kcontrol_new rt286_snd_controls[] = { + SOC_DOUBLE_R_TLV("DAC0 Playback Volume", RT286_DACL_GAIN, + RT286_DACR_GAIN, 0, 0x7f, 0, out_vol_tlv), + SOC_DOUBLE_R_TLV("ADC0 Capture Volume", RT286_ADCL_GAIN, + RT286_ADCR_GAIN, 0, 0x7f, 0, out_vol_tlv), + SOC_SINGLE_TLV("AMIC Volume", RT286_MIC_GAIN, + 0, 0x3, 0, mic_vol_tlv), + SOC_DOUBLE_R("Speaker Playback Switch", RT286_SPOL_GAIN, + RT286_SPOR_GAIN, RT286_MUTE_SFT, 1, 1), +}; + +/* Digital Mixer */ +static const struct snd_kcontrol_new rt286_front_mix[] = { + SOC_DAPM_SINGLE("DAC Switch", RT286_F_DAC_SWITCH, + RT286_MUTE_SFT, 1, 1), + SOC_DAPM_SINGLE("RECMIX Switch", RT286_F_RECMIX_SWITCH, + RT286_MUTE_SFT, 1, 1), +}; + +/* Analog Input Mixer */ +static const struct snd_kcontrol_new rt286_rec_mix[] = { + SOC_DAPM_SINGLE("Mic1 Switch", RT286_REC_MIC_SWITCH, + RT286_MUTE_SFT, 1, 1), + SOC_DAPM_SINGLE("I2S Switch", RT286_REC_I2S_SWITCH, + RT286_MUTE_SFT, 1, 1), + SOC_DAPM_SINGLE("Line1 Switch", RT286_REC_LINE_SWITCH, + RT286_MUTE_SFT, 1, 1), + SOC_DAPM_SINGLE("Beep Switch", RT286_REC_BEEP_SWITCH, + RT286_MUTE_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new spo_enable_control = + SOC_DAPM_SINGLE("Switch", RT286_SET_PIN_SPK, + RT286_SET_PIN_SFT, 1, 0); + +static const struct snd_kcontrol_new hpol_enable_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT286_HPOL_GAIN, + RT286_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new hpor_enable_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT286_HPOR_GAIN, + RT286_MUTE_SFT, 1, 1); + +/* ADC0 source */ +static const char * const rt286_adc_src[] = { + "Mic", "RECMIX", "Dmic" +}; + +static const int rt286_adc_values[] = { + 0, 4, 5, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL( + rt286_adc0_enum, RT286_ADC0_MUX, RT286_ADC_SEL_SFT, + RT286_ADC_SEL_MASK, rt286_adc_src, rt286_adc_values); + +static const struct snd_kcontrol_new rt286_adc0_mux = + SOC_DAPM_ENUM("ADC 0 source", rt286_adc0_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL( + rt286_adc1_enum, RT286_ADC1_MUX, RT286_ADC_SEL_SFT, + RT286_ADC_SEL_MASK, rt286_adc_src, rt286_adc_values); + +static const struct snd_kcontrol_new rt286_adc1_mux = + SOC_DAPM_ENUM("ADC 1 source", rt286_adc1_enum); + +static const char * const rt286_dac_src[] = { + "Front", "Surround" +}; +/* HP-OUT source */ +static SOC_ENUM_SINGLE_DECL(rt286_hpo_enum, RT286_HPO_MUX, + 0, rt286_dac_src); + +static const struct snd_kcontrol_new rt286_hpo_mux = +SOC_DAPM_ENUM("HPO source", rt286_hpo_enum); + +/* SPK-OUT source */ +static SOC_ENUM_SINGLE_DECL(rt286_spo_enum, RT286_SPK_MUX, + 0, rt286_dac_src); + +static const struct snd_kcontrol_new rt286_spo_mux = +SOC_DAPM_ENUM("SPO source", rt286_spo_enum); + +static int rt286_spk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_write(codec, + RT286_SPK_EAPD, RT286_SET_EAPD_HIGH); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_write(codec, + RT286_SPK_EAPD, RT286_SET_EAPD_LOW); + break; + + default: + return 0; + } + + return 0; +} + +static int rt286_set_dmic1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_write(codec, RT286_SET_PIN_DMIC1, 0x20); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_write(codec, RT286_SET_PIN_DMIC1, 0); + break; + default: + return 0; + } + + return 0; +} + +static int rt286_adc_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + unsigned int nid; + + nid = (w->reg >> 20) & 0xff; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, nid, 0), + 0x7080, 0x7000); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, nid, 0), + 0x7080, 0x7080); + break; + default: + return 0; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rt286_dapm_widgets[] = { + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC1 Pin"), + SND_SOC_DAPM_INPUT("DMIC2 Pin"), + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("LINE1"), + SND_SOC_DAPM_INPUT("Beep"), + + /* DMIC */ + SND_SOC_DAPM_PGA_E("DMIC1", RT286_SET_POWER(RT286_DMIC1), 0, 1, + NULL, 0, rt286_set_dmic1_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA("DMIC2", RT286_SET_POWER(RT286_DMIC2), 0, 1, + NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC Receiver", SND_SOC_NOPM, + 0, 0, NULL, 0), + + /* REC Mixer */ + SND_SOC_DAPM_MIXER("RECMIX", SND_SOC_NOPM, 0, 0, + rt286_rec_mix, ARRAY_SIZE(rt286_rec_mix)), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC 0", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC 1", NULL, SND_SOC_NOPM, 0, 0), + + /* ADC Mux */ + SND_SOC_DAPM_MUX_E("ADC 0 Mux", RT286_SET_POWER(RT286_ADC_IN1), 0, 1, + &rt286_adc0_mux, rt286_adc_event, SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("ADC 1 Mux", RT286_SET_POWER(RT286_ADC_IN2), 0, 1, + &rt286_adc1_mux, rt286_adc_event, SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMU), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0), + + /* Output Side */ + /* DACs */ + SND_SOC_DAPM_DAC("DAC 0", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC 1", NULL, SND_SOC_NOPM, 0, 0), + + /* Output Mux */ + SND_SOC_DAPM_MUX("SPK Mux", SND_SOC_NOPM, 0, 0, &rt286_spo_mux), + SND_SOC_DAPM_MUX("HPO Mux", SND_SOC_NOPM, 0, 0, &rt286_hpo_mux), + + SND_SOC_DAPM_SUPPLY("HP Power", RT286_SET_PIN_HPO, + RT286_SET_PIN_SFT, 0, NULL, 0), + + /* Output Mixer */ + SND_SOC_DAPM_MIXER("Front", RT286_SET_POWER(RT286_DAC_OUT1), 0, 1, + rt286_front_mix, ARRAY_SIZE(rt286_front_mix)), + SND_SOC_DAPM_PGA("Surround", RT286_SET_POWER(RT286_DAC_OUT2), 0, 1, + NULL, 0), + + /* Output Pga */ + SND_SOC_DAPM_SWITCH_E("SPO", SND_SOC_NOPM, 0, 0, + &spo_enable_control, rt286_spk_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SWITCH("HPO L", SND_SOC_NOPM, 0, 0, + &hpol_enable_control), + SND_SOC_DAPM_SWITCH("HPO R", SND_SOC_NOPM, 0, 0, + &hpor_enable_control), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("SPOL"), + SND_SOC_DAPM_OUTPUT("SPOR"), + SND_SOC_DAPM_OUTPUT("HPO Pin"), + SND_SOC_DAPM_OUTPUT("SPDIF"), +}; + +static const struct snd_soc_dapm_route rt286_dapm_routes[] = { + {"DMIC1", NULL, "DMIC1 Pin"}, + {"DMIC2", NULL, "DMIC2 Pin"}, + {"DMIC1", NULL, "DMIC Receiver"}, + {"DMIC2", NULL, "DMIC Receiver"}, + + {"RECMIX", "Beep Switch", "Beep"}, + {"RECMIX", "Line1 Switch", "LINE1"}, + {"RECMIX", "Mic1 Switch", "MIC1"}, + + {"ADC 0 Mux", "Dmic", "DMIC1"}, + {"ADC 0 Mux", "RECMIX", "RECMIX"}, + {"ADC 0 Mux", "Mic", "MIC1"}, + {"ADC 1 Mux", "Dmic", "DMIC2"}, + {"ADC 1 Mux", "RECMIX", "RECMIX"}, + {"ADC 1 Mux", "Mic", "MIC1"}, + + {"ADC 0", NULL, "ADC 0 Mux"}, + {"ADC 1", NULL, "ADC 1 Mux"}, + + {"AIF1TX", NULL, "ADC 0"}, + {"AIF2TX", NULL, "ADC 1"}, + + {"DAC 0", NULL, "AIF1RX"}, + {"DAC 1", NULL, "AIF2RX"}, + + {"Front", "DAC Switch", "DAC 0"}, + {"Front", "RECMIX Switch", "RECMIX"}, + + {"Surround", NULL, "DAC 1"}, + + {"SPK Mux", "Front", "Front"}, + {"SPK Mux", "Surround", "Surround"}, + + {"HPO Mux", "Front", "Front"}, + {"HPO Mux", "Surround", "Surround"}, + + {"SPO", "Switch", "SPK Mux"}, + {"HPO L", "Switch", "HPO Mux"}, + {"HPO R", "Switch", "HPO Mux"}, + {"HPO L", NULL, "HP Power"}, + {"HPO R", NULL, "HP Power"}, + + {"SPOL", NULL, "SPO"}, + {"SPOR", NULL, "SPO"}, + {"HPO Pin", NULL, "HPO L"}, + {"HPO Pin", NULL, "HPO R"}, +}; + +static int rt286_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec); + unsigned int val = 0; + int d_len_code; + + switch (params_rate(params)) { + /* bit 14 0:48K 1:44.1K */ + case 44100: + val |= 0x4000; + break; + case 48000: + break; + default: + dev_err(codec->dev, "Unsupported sample rate %d\n", + params_rate(params)); + return -EINVAL; + } + switch (rt286->sys_clk) { + case 12288000: + case 24576000: + if (params_rate(params) != 48000) { + dev_err(codec->dev, "Sys_clk is not matched (%d %d)\n", + params_rate(params), rt286->sys_clk); + return -EINVAL; + } + break; + case 11289600: + case 22579200: + if (params_rate(params) != 44100) { + dev_err(codec->dev, "Sys_clk is not matched (%d %d)\n", + params_rate(params), rt286->sys_clk); + return -EINVAL; + } + break; + } + + if (params_channels(params) <= 16) { + /* bit 3:0 Number of Channel */ + val |= (params_channels(params) - 1); + } else { + dev_err(codec->dev, "Unsupported channels %d\n", + params_channels(params)); + return -EINVAL; + } + + d_len_code = 0; + switch (params_width(params)) { + /* bit 6:4 Bits per Sample */ + case 16: + d_len_code = 0; + val |= (0x1 << 4); + break; + case 32: + d_len_code = 2; + val |= (0x4 << 4); + break; + case 20: + d_len_code = 1; + val |= (0x2 << 4); + break; + case 24: + d_len_code = 2; + val |= (0x3 << 4); + break; + case 8: + d_len_code = 3; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, + RT286_I2S_CTRL1, 0x0018, d_len_code << 3); + dev_dbg(codec->dev, "format val = 0x%x\n", val); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_update_bits(codec, RT286_DAC_FORMAT, 0x407f, val); + else + snd_soc_update_bits(codec, RT286_ADC_FORMAT, 0x407f, val); + + return 0; +} + +static int rt286_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + snd_soc_update_bits(codec, + RT286_I2S_CTRL1, 0x800, 0x800); + break; + case SND_SOC_DAIFMT_CBS_CFS: + snd_soc_update_bits(codec, + RT286_I2S_CTRL1, 0x800, 0x0); + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + snd_soc_update_bits(codec, + RT286_I2S_CTRL1, 0x300, 0x0); + break; + case SND_SOC_DAIFMT_LEFT_J: + snd_soc_update_bits(codec, + RT286_I2S_CTRL1, 0x300, 0x1 << 8); + break; + case SND_SOC_DAIFMT_DSP_A: + snd_soc_update_bits(codec, + RT286_I2S_CTRL1, 0x300, 0x2 << 8); + break; + case SND_SOC_DAIFMT_DSP_B: + snd_soc_update_bits(codec, + RT286_I2S_CTRL1, 0x300, 0x3 << 8); + break; + default: + return -EINVAL; + } + /* bit 15 Stream Type 0:PCM 1:Non-PCM */ + snd_soc_update_bits(codec, RT286_DAC_FORMAT, 0x8000, 0); + snd_soc_update_bits(codec, RT286_ADC_FORMAT, 0x8000, 0); + + return 0; +} + +static int rt286_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s freq=%d\n", __func__, freq); + + if (RT286_SCLK_S_MCLK == clk_id) { + snd_soc_update_bits(codec, + RT286_I2S_CTRL2, 0x0100, 0x0); + snd_soc_update_bits(codec, + RT286_PLL_CTRL1, 0x20, 0x20); + } else { + snd_soc_update_bits(codec, + RT286_I2S_CTRL2, 0x0100, 0x0100); + snd_soc_update_bits(codec, + RT286_PLL_CTRL, 0x4, 0x4); + snd_soc_update_bits(codec, + RT286_PLL_CTRL1, 0x20, 0x0); + } + + switch (freq) { + case 19200000: + if (RT286_SCLK_S_MCLK == clk_id) { + dev_err(codec->dev, "Should not use MCLK\n"); + return -EINVAL; + } + snd_soc_update_bits(codec, + RT286_I2S_CTRL2, 0x40, 0x40); + break; + case 24000000: + if (RT286_SCLK_S_MCLK == clk_id) { + dev_err(codec->dev, "Should not use MCLK\n"); + return -EINVAL; + } + snd_soc_update_bits(codec, + RT286_I2S_CTRL2, 0x40, 0x0); + break; + case 12288000: + case 11289600: + snd_soc_update_bits(codec, + RT286_I2S_CTRL2, 0x8, 0x0); + snd_soc_update_bits(codec, + RT286_CLK_DIV, 0xfc1e, 0x0004); + break; + case 24576000: + case 22579200: + snd_soc_update_bits(codec, + RT286_I2S_CTRL2, 0x8, 0x8); + snd_soc_update_bits(codec, + RT286_CLK_DIV, 0xfc1e, 0x5406); + break; + default: + dev_err(codec->dev, "Unsupported system clock\n"); + return -EINVAL; + } + + rt286->sys_clk = freq; + + return 0; +} + +static int rt286_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct snd_soc_codec *codec = dai->codec; + + dev_dbg(codec->dev, "%s ratio=%d\n", __func__, ratio); + if (50 == ratio) + snd_soc_update_bits(codec, + RT286_I2S_CTRL1, 0x1000, 0x1000); + else + snd_soc_update_bits(codec, + RT286_I2S_CTRL1, 0x1000, 0x0); + + + return 0; +} + +static int rt286_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) + snd_soc_write(codec, + RT286_SET_AUDIO_POWER, AC_PWRST_D0); + break; + + case SND_SOC_BIAS_STANDBY: + snd_soc_write(codec, + RT286_SET_AUDIO_POWER, AC_PWRST_D3); + break; + + default: + break; + } + codec->dapm.bias_level = level; + + return 0; +} + +static irqreturn_t rt286_irq(int irq, void *data) +{ + struct rt286_priv *rt286 = data; + bool hp = false; + bool mic = false; + int status = 0; + + rt286_jack_detect(rt286->codec, &hp, &mic); + + /* Clear IRQ */ + snd_soc_update_bits(rt286->codec, + RT286_IRQ_CTRL, 0x1, 0x1); + + if (hp == true) + status |= SND_JACK_HEADPHONE; + + if (mic == true) + status |= SND_JACK_MICROPHONE; + + snd_soc_jack_report(rt286->jack, status, + SND_JACK_MICROPHONE | SND_JACK_HEADPHONE); + + pm_wakeup_event(&rt286->i2c->dev, 300); + + return IRQ_HANDLED; +} + +static int rt286_probe(struct snd_soc_codec *codec) +{ + struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec); + int i, ret; + + ret = snd_soc_read(codec, + RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID)); + if (ret != RT286_VENDOR_ID) { + dev_err(codec->dev, + "Device with ID register %x is not rt286\n", ret); + return -ENODEV; + } + + snd_soc_write(codec, RT286_SET_AUDIO_POWER, AC_PWRST_D3); + + for (i = 0; i < RT286_POWER_REG_LEN; i++) + snd_soc_write(codec, + RT286_SET_POWER(rt286_support_power_controls[i]), + AC_PWRST_D1); + + if (!rt286->pdata.cbj_en) { + snd_soc_write(codec, RT286_CBJ_CTRL2, 0x0000); + snd_soc_write(codec, RT286_MIC1_DET_CTRL, 0x0816); + snd_soc_write(codec, RT286_MISC_CTRL1, 0x0000); + snd_soc_update_bits(codec, + RT286_CBJ_CTRL1, 0xf000, 0xb000); + } else { + snd_soc_update_bits(codec, + RT286_CBJ_CTRL1, 0xf000, 0x5000); + } + + mdelay(10); + + if (!rt286->pdata.gpio2_en) + snd_soc_write(codec, RT286_SET_DMIC2_DEFAULT, 0x4000); + else + snd_soc_write(codec, RT286_SET_DMIC2_DEFAULT, 0); + + mdelay(10); + + /*Power down LDO2*/ + snd_soc_update_bits(codec, RT286_POWER_CTRL2, 0x8, 0x0); + + codec->dapm.bias_level = SND_SOC_BIAS_OFF; + rt286->codec = codec; + + rt286->i2c->irq = 0; + if (rt286->i2c->irq) { + snd_soc_update_bits(codec, + RT286_IRQ_CTRL, 0x2, 0x2); + + INIT_DELAYED_WORK(&rt286->jack_detect_work, + rt286_jack_detect_work); + schedule_delayed_work(&rt286->jack_detect_work, + msecs_to_jiffies(1250)); + + ret = request_threaded_irq(rt286->i2c->irq, NULL, rt286_irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt286", rt286); + if (ret != 0) { + dev_err(codec->dev, + "Failed to reguest IRQ: %d\n", ret); + return ret; + } + } + + return 0; +} + +static int rt286_remove(struct snd_soc_codec *codec) +{ + struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec); + + cancel_delayed_work_sync(&rt286->jack_detect_work); + + return 0; +} + +#ifdef CONFIG_PM +static int rt286_suspend(struct snd_soc_codec *codec) +{ + struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec); + + regcache_cache_only(rt286->regmap, true); + regcache_mark_dirty(rt286->regmap); + + return 0; +} + +static int rt286_resume(struct snd_soc_codec *codec) +{ + struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec); + + regcache_cache_only(rt286->regmap, false); + rt286_index_sync(codec); + regcache_sync(rt286->regmap); + + return 0; +} +#else +#define rt286_suspend NULL +#define rt286_resume NULL +#endif + +#define RT286_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) +#define RT286_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt286_aif_dai_ops = { + .hw_params = rt286_hw_params, + .set_fmt = rt286_set_dai_fmt, + .set_sysclk = rt286_set_dai_sysclk, + .set_bclk_ratio = rt286_set_bclk_ratio, +}; + +static struct snd_soc_dai_driver rt286_dai[] = { + { + .name = "rt286-aif1", + .id = RT286_AIF1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT286_STEREO_RATES, + .formats = RT286_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT286_STEREO_RATES, + .formats = RT286_FORMATS, + }, + .ops = &rt286_aif_dai_ops, + .symmetric_rates = 1, + }, + { + .name = "rt286-aif2", + .id = RT286_AIF2, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT286_STEREO_RATES, + .formats = RT286_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT286_STEREO_RATES, + .formats = RT286_FORMATS, + }, + .ops = &rt286_aif_dai_ops, + .symmetric_rates = 1, + }, + +}; + +static struct snd_soc_codec_driver soc_codec_dev_rt286 = { + .probe = rt286_probe, + .remove = rt286_remove, + .suspend = rt286_suspend, + .resume = rt286_resume, + .set_bias_level = rt286_set_bias_level, + .idle_bias_off = true, + .controls = rt286_snd_controls, + .num_controls = ARRAY_SIZE(rt286_snd_controls), + .dapm_widgets = rt286_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt286_dapm_widgets), + .dapm_routes = rt286_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt286_dapm_routes), +}; + +static const struct regmap_config rt286_regmap = { + .reg_bits = 32, + .val_bits = 32, + .max_register = 0x02370100, + .volatile_reg = rt286_volatile_register, + .readable_reg = rt286_readable_register, + .reg_write = rt286_hw_write, + .reg_read = rt286_hw_read, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt286_reg, + .num_reg_defaults = ARRAY_SIZE(rt286_reg), +}; + +static const struct i2c_device_id rt286_i2c_id[] = { + {"rt286", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, rt286_i2c_id); + +static const struct acpi_device_id rt286_acpi_match[] = { + { "INT343A", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, rt286_acpi_match); + +static int rt286_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct rt286_platform_data *pdata = dev_get_platdata(&i2c->dev); + struct rt286_priv *rt286; + int ret; + + rt286 = devm_kzalloc(&i2c->dev, sizeof(*rt286), + GFP_KERNEL); + if (NULL == rt286) + return -ENOMEM; + + rt286->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt286_regmap); + if (IS_ERR(rt286->regmap)) { + ret = PTR_ERR(rt286->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + rt286->index_cache = rt286_index_def; + rt286->i2c = i2c; + i2c_set_clientdata(i2c, rt286); + + if (pdata) + rt286->pdata = *pdata; + + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt286, + rt286_dai, ARRAY_SIZE(rt286_dai)); + + return ret; +} + +static int rt286_i2c_remove(struct i2c_client *i2c) +{ + struct rt286_priv *rt286 = i2c_get_clientdata(i2c); + + if (i2c->irq) + free_irq(i2c->irq, rt286); + snd_soc_unregister_codec(&i2c->dev); + + return 0; +} + + +struct i2c_driver rt286_i2c_driver = { + .driver = { + .name = "rt286", + .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(rt286_acpi_match), + }, + .probe = rt286_i2c_probe, + .remove = rt286_i2c_remove, + .id_table = rt286_i2c_id, +}; + +module_i2c_driver(rt286_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT286 driver"); +MODULE_AUTHOR("Bard Liao "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt286.h b/sound/soc/codecs/rt286.h new file mode 100644 index 000000000000..21c570f88e9b --- /dev/null +++ b/sound/soc/codecs/rt286.h @@ -0,0 +1,193 @@ +/* + * rt286.h -- RT286 ALSA SoC audio driver + * + * Copyright 2011 Realtek Microelectronics + * Author: Johnny Hsu + * + * 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 __RT286_H__ +#define __RT286_H__ + +#define VERB_CMD(V, N, D) ((N << 20) | (V << 8) | D) + +#define RT286_AUDIO_FUNCTION_GROUP 0x01 +#define RT286_DAC_OUT1 0x02 +#define RT286_DAC_OUT2 0x03 +#define RT286_ADC_IN1 0x09 +#define RT286_ADC_IN2 0x08 +#define RT286_MIXER_IN 0x0b +#define RT286_MIXER_OUT1 0x0c +#define RT286_MIXER_OUT2 0x0d +#define RT286_DMIC1 0x12 +#define RT286_DMIC2 0x13 +#define RT286_SPK_OUT 0x14 +#define RT286_MIC1 0x18 +#define RT286_LINE1 0x1a +#define RT286_BEEP 0x1d +#define RT286_SPDIF 0x1e +#define RT286_VENDOR_REGISTERS 0x20 +#define RT286_HP_OUT 0x21 +#define RT286_MIXER_IN1 0x22 +#define RT286_MIXER_IN2 0x23 + +#define RT286_SET_PIN_SFT 6 +#define RT286_SET_PIN_ENABLE 0x40 +#define RT286_SET_PIN_DISABLE 0 +#define RT286_SET_EAPD_HIGH 0x2 +#define RT286_SET_EAPD_LOW 0 + +#define RT286_MUTE_SFT 7 + +/* Verb commands */ +#define RT286_GET_PARAM(NID, PARAM) VERB_CMD(AC_VERB_PARAMETERS, NID, PARAM) +#define RT286_SET_POWER(NID) VERB_CMD(AC_VERB_SET_POWER_STATE, NID, 0) +#define RT286_SET_AUDIO_POWER RT286_SET_POWER(RT286_AUDIO_FUNCTION_GROUP) +#define RT286_SET_HPO_POWER RT286_SET_POWER(RT286_HP_OUT) +#define RT286_SET_SPK_POWER RT286_SET_POWER(RT286_SPK_OUT) +#define RT286_SET_DMIC1_POWER RT286_SET_POWER(RT286_DMIC1) +#define RT286_SPK_MUX\ + VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_SPK_OUT, 0) +#define RT286_HPO_MUX\ + VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_HP_OUT, 0) +#define RT286_ADC0_MUX\ + VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_MIXER_IN1, 0) +#define RT286_ADC1_MUX\ + VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_MIXER_IN2, 0) +#define RT286_SET_MIC1\ + VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_MIC1, 0) +#define RT286_SET_PIN_HPO\ + VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_HP_OUT, 0) +#define RT286_SET_PIN_SPK\ + VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_SPK_OUT, 0) +#define RT286_SET_PIN_DMIC1\ + VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_DMIC1, 0) +#define RT286_SPK_EAPD\ + VERB_CMD(AC_VERB_SET_EAPD_BTLENABLE, RT286_SPK_OUT, 0) +#define RT286_SET_AMP_GAIN_HPO\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_HP_OUT, 0) +#define RT286_SET_AMP_GAIN_ADC_IN1\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN1, 0) +#define RT286_SET_AMP_GAIN_ADC_IN2\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN2, 0) +#define RT286_GET_HP_SENSE\ + VERB_CMD(AC_VERB_GET_PIN_SENSE, RT286_HP_OUT, 0) +#define RT286_GET_MIC1_SENSE\ + VERB_CMD(AC_VERB_GET_PIN_SENSE, RT286_MIC1, 0) +#define RT286_SET_DMIC2_DEFAULT\ + VERB_CMD(AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, RT286_DMIC2, 0) +#define RT286_DACL_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_DAC_OUT1, 0xa000) +#define RT286_DACR_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_DAC_OUT1, 0x9000) +#define RT286_ADCL_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN1, 0x6000) +#define RT286_ADCR_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN1, 0x5000) +#define RT286_MIC_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIC1, 0x7000) +#define RT286_SPOL_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_SPK_OUT, 0xa000) +#define RT286_SPOR_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_SPK_OUT, 0x9000) +#define RT286_HPOL_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_HP_OUT, 0xa000) +#define RT286_HPOR_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_HP_OUT, 0x9000) +#define RT286_F_DAC_SWITCH\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_OUT1, 0x7000) +#define RT286_F_RECMIX_SWITCH\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_OUT1, 0x7100) +#define RT286_REC_MIC_SWITCH\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7000) +#define RT286_REC_I2S_SWITCH\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7100) +#define RT286_REC_LINE_SWITCH\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7200) +#define RT286_REC_BEEP_SWITCH\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7300) +#define RT286_DAC_FORMAT\ + VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT286_DAC_OUT1, 0) +#define RT286_ADC_FORMAT\ + VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT286_ADC_IN1, 0) +#define RT286_COEF_INDEX\ + VERB_CMD(AC_VERB_SET_COEF_INDEX, RT286_VENDOR_REGISTERS, 0) +#define RT286_PROC_COEF\ + VERB_CMD(AC_VERB_SET_PROC_COEF, RT286_VENDOR_REGISTERS, 0) + +/* Index registers */ +#define RT286_A_BIAS_CTRL1 0x01 +#define RT286_A_BIAS_CTRL2 0x02 +#define RT286_POWER_CTRL1 0x03 +#define RT286_A_BIAS_CTRL3 0x04 +#define RT286_POWER_CTRL2 0x08 +#define RT286_I2S_CTRL1 0x09 +#define RT286_I2S_CTRL2 0x0a +#define RT286_CLK_DIV 0x0b +#define RT286_POWER_CTRL3 0x0f +#define RT286_MIC1_DET_CTRL 0x19 +#define RT286_MISC_CTRL1 0x20 +#define RT286_IRQ_CTRL 0x33 +#define RT286_PLL_CTRL1 0x49 +#define RT286_CBJ_CTRL1 0x4f +#define RT286_CBJ_CTRL2 0x50 +#define RT286_PLL_CTRL 0x63 + +/* SPDIF (0x06) */ +#define RT286_SPDIF_SEL_SFT 0 +#define RT286_SPDIF_SEL_PCM0 0 +#define RT286_SPDIF_SEL_PCM1 1 +#define RT286_SPDIF_SEL_SPOUT 2 +#define RT286_SPDIF_SEL_PP 3 + +/* RECMIX (0x0b) */ +#define RT286_M_REC_BEEP_SFT 0 +#define RT286_M_REC_LINE1_SFT 1 +#define RT286_M_REC_MIC1_SFT 2 +#define RT286_M_REC_I2S_SFT 3 + +/* Front (0x0c) */ +#define RT286_M_FRONT_DAC_SFT 0 +#define RT286_M_FRONT_REC_SFT 1 + +/* SPK-OUT (0x14) */ +#define RT286_M_SPK_MUX_SFT 14 +#define RT286_SPK_SEL_MASK 0x1 +#define RT286_SPK_SEL_SFT 0 +#define RT286_SPK_SEL_F 0 +#define RT286_SPK_SEL_S 1 + +/* HP-OUT (0x21) */ +#define RT286_M_HP_MUX_SFT 14 +#define RT286_HP_SEL_MASK 0x1 +#define RT286_HP_SEL_SFT 0 +#define RT286_HP_SEL_F 0 +#define RT286_HP_SEL_S 1 + +/* ADC (0x22) (0x23) */ +#define RT286_ADC_SEL_MASK 0x7 +#define RT286_ADC_SEL_SFT 0 +#define RT286_ADC_SEL_SURR 0 +#define RT286_ADC_SEL_FRONT 1 +#define RT286_ADC_SEL_DMIC 2 +#define RT286_ADC_SEL_BEEP 4 +#define RT286_ADC_SEL_LINE1 5 +#define RT286_ADC_SEL_I2S 6 +#define RT286_ADC_SEL_MIC1 7 + +#define RT286_SCLK_S_MCLK 0 +#define RT286_SCLK_S_PLL 1 + +enum { + RT286_AIF1, + RT286_AIF2, + RT286_AIFS, +}; + +int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack); + +#endif /* __RT286_H__ */ + -- cgit v1.2.3 From 93c6ee94c140eefb6f9d5b6e2ad1acc2e138e44c Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 3 Jul 2014 07:51:52 +0300 Subject: dma: Support for 3 bytes word size Add DMA_SLAVE_BUSWIDTH_3_BYTES to dma_slave_buswidth for engines and users to select 3 bytes as bus width. For example eDMA can be configured to use 3bytes mode and in audio we have formats stored on 3bytes in memory (_XXX_3LE) where this new bus width can be used. Signed-off-by: Peter Ujfalusi Acked-by: Vinod Koul Acked-by: Takashi Iwai Signed-off-by: Mark Brown --- include/linux/dmaengine.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index d2c5cc7c583c..3d1c2aa51530 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -299,6 +299,7 @@ enum dma_slave_buswidth { DMA_SLAVE_BUSWIDTH_UNDEFINED = 0, DMA_SLAVE_BUSWIDTH_1_BYTE = 1, DMA_SLAVE_BUSWIDTH_2_BYTES = 2, + DMA_SLAVE_BUSWIDTH_3_BYTES = 3, DMA_SLAVE_BUSWIDTH_4_BYTES = 4, DMA_SLAVE_BUSWIDTH_8_BYTES = 8, }; -- cgit v1.2.3 From eb1904f49d3e11468997e0667e6ec332a66697c9 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 4 Jul 2014 17:23:33 +0200 Subject: Bluetooth: Add quirk for external configuration requirement When a controller requires external configuration, then setting this quirk will allow indicating this. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 9 +++++++++ net/bluetooth/hci_core.c | 3 ++- net/bluetooth/mgmt.c | 9 ++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 148b21699446..4f1c4c7e2f00 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -111,6 +111,15 @@ enum { */ HCI_QUIRK_BROKEN_STORED_LINK_KEY, + /* When this quirk is set, an external configuration step + * is required and will be indicated with the controller + * configuation. + * + * This quirk can be set before hci_register_dev is called or + * during the hdev->setup vendor callback. + */ + HCI_QUIRK_EXTERNAL_CONFIG, + /* When this quirk is set, the public Bluetooth address * initially reported by HCI Read BD Address command * is considered invalid. Controller configuration is diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index c92bee84413f..c007b3543e70 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2249,7 +2249,8 @@ static int hci_dev_do_open(struct hci_dev *hdev) if (hdev->setup && test_bit(HCI_SETUP, &hdev->dev_flags)) { ret = hdev->setup(hdev); - if (test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks)) + if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) || + test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks)) set_bit(HCI_UNCONFIGURED, &hdev->dev_flags); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 766411e11ac7..d2d0e051e4f2 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -445,6 +445,9 @@ static __le32 get_missing_options(struct hci_dev *hdev) { u32 options = 0; + if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks)) + options |= MGMT_OPTION_EXTERNAL_CONFIG; + if (test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks) && !bacmp(&hdev->public_addr, BDADDR_ANY)) options |= MGMT_OPTION_PUBLIC_ADDRESS; @@ -465,6 +468,9 @@ static int read_config_info(struct sock *sk, struct hci_dev *hdev, memset(&rp, 0, sizeof(rp)); rp.manufacturer = cpu_to_le16(hdev->manufacturer); + if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks)) + options |= MGMT_OPTION_EXTERNAL_CONFIG; + if (hdev->set_bdaddr) options |= MGMT_OPTION_PUBLIC_ADDRESS; @@ -509,7 +515,8 @@ static u32 get_supported_settings(struct hci_dev *hdev) settings |= MGMT_SETTING_PRIVACY; } - if (hdev->set_bdaddr) + if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) || + hdev->set_bdaddr) settings |= MGMT_SETTING_CONFIGURATION; return settings; -- cgit v1.2.3 From dbece37a3233933ec89f77f04049e13ad9b29634 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 4 Jul 2014 18:11:55 +0200 Subject: Bluetooth: Add support for Set External Configuration management command The Set External Configuration management command allows for switching between configured and unconfigured start if HCI_QURIK_EXTERNAL_CONFIG is set by the transport driver. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 1 + include/net/bluetooth/mgmt.h | 6 ++++ net/bluetooth/mgmt.c | 77 ++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 82 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 4f1c4c7e2f00..80c5fc947fbc 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -173,6 +173,7 @@ enum { HCI_UNREGISTER, HCI_UNCONFIGURED, HCI_USER_CHANNEL, + HCI_EXT_CONFIGURED, HCI_LE_SCAN, HCI_SSP_ENABLED, diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 3984678ffab1..c7d537f1bd19 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -483,6 +483,12 @@ struct mgmt_rp_read_config_info { __le32 missing_options; } __packed; +#define MGMT_OP_SET_EXTERNAL_CONFIG 0x0038 +struct mgmt_cp_set_external_config { + __u8 config; +} __packed; +#define MGMT_SET_EXTERNAL_CONFIG_SIZE 1 + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d2d0e051e4f2..91bccef63f30 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -91,6 +91,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_LOAD_CONN_PARAM, MGMT_OP_READ_UNCONF_INDEX_LIST, MGMT_OP_READ_CONFIG_INFO, + MGMT_OP_SET_EXTERNAL_CONFIG, }; static const u16 mgmt_events[] = { @@ -441,11 +442,25 @@ static int read_unconf_index_list(struct sock *sk, struct hci_dev *hdev, return err; } +static bool is_configured(struct hci_dev *hdev) +{ + if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) && + !test_bit(HCI_EXT_CONFIGURED, &hdev->dev_flags)) + return false; + + if (test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks) && + !bacmp(&hdev->public_addr, BDADDR_ANY)) + return false; + + return true; +} + static __le32 get_missing_options(struct hci_dev *hdev) { u32 options = 0; - if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks)) + if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) && + !test_bit(HCI_EXT_CONFIGURED, &hdev->dev_flags)) options |= MGMT_OPTION_EXTERNAL_CONFIG; if (test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks) && @@ -455,6 +470,14 @@ static __le32 get_missing_options(struct hci_dev *hdev) return cpu_to_le32(options); } +static int send_options_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev) +{ + __le32 options = get_missing_options(hdev); + + return cmd_complete(sk, hdev->id, opcode, 0, &options, + sizeof(options)); +} + static int read_config_info(struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len) { @@ -5355,6 +5378,54 @@ static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data, return cmd_complete(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM, 0, NULL, 0); } +static int set_external_config(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_cp_set_external_config *cp = data; + bool changed; + int err; + + BT_DBG("%s", hdev->name); + + if (hdev_is_powered(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_EXTERNAL_CONFIG, + MGMT_STATUS_REJECTED); + + if (cp->config != 0x00 && cp->config != 0x01) + return cmd_status(sk, hdev->id, MGMT_OP_SET_EXTERNAL_CONFIG, + MGMT_STATUS_INVALID_PARAMS); + + if (!test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_EXTERNAL_CONFIG, + MGMT_STATUS_NOT_SUPPORTED); + + hci_dev_lock(hdev); + + if (cp->config) + changed = !test_and_set_bit(HCI_EXT_CONFIGURED, + &hdev->dev_flags); + else + changed = test_and_clear_bit(HCI_EXT_CONFIGURED, + &hdev->dev_flags); + + err = send_options_rsp(sk, MGMT_OP_SET_EXTERNAL_CONFIG, hdev); + if (err < 0) + goto unlock; + + if (!changed) + goto unlock; + + if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) == is_configured(hdev)) { + mgmt_index_removed(hdev); + change_bit(HCI_UNCONFIGURED, &hdev->dev_flags); + mgmt_index_added(hdev); + } + +unlock: + hci_dev_unlock(hdev); + return err; +} + static const struct mgmt_handler { int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len); @@ -5417,6 +5488,7 @@ static const struct mgmt_handler { { load_conn_param, true, MGMT_LOAD_CONN_PARAM_SIZE }, { read_unconf_index_list, false, MGMT_READ_UNCONF_INDEX_LIST_SIZE }, { read_config_info, false, MGMT_READ_CONFIG_INFO_SIZE }, + { set_external_config, false, MGMT_SET_EXTERNAL_CONFIG_SIZE }, }; int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) @@ -5469,7 +5541,8 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) } if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) && - opcode != MGMT_OP_READ_CONFIG_INFO) { + opcode != MGMT_OP_READ_CONFIG_INFO && + opcode != MGMT_OP_SET_EXTERNAL_CONFIG) { err = cmd_status(sk, index, opcode, MGMT_STATUS_INVALID_INDEX); goto done; -- cgit v1.2.3 From f4537c04d387eda86ed89e0eafe0352f7fa0c9d0 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 4 Jul 2014 19:06:23 +0200 Subject: Bluetooth: Add support for New Configuration Options management event When one or more of the missing configuration options change, then send this even to all the other management interface clients. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 2 ++ net/bluetooth/mgmt.c | 11 +++++++++++ 2 files changed, 13 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index c7d537f1bd19..80606d2fe086 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -669,3 +669,5 @@ struct mgmt_ev_new_conn_param { #define MGMT_EV_UNCONF_INDEX_ADDED 0x001d #define MGMT_EV_UNCONF_INDEX_REMOVED 0x001e + +#define MGMT_EV_NEW_CONFIG_OPTIONS 0x001f diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 4ef73523d95b..f514eb15e0fb 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -123,6 +123,7 @@ static const u16 mgmt_events[] = { MGMT_EV_NEW_CONN_PARAM, MGMT_EV_UNCONF_INDEX_ADDED, MGMT_EV_UNCONF_INDEX_REMOVED, + MGMT_EV_NEW_CONFIG_OPTIONS, }; #define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000) @@ -500,6 +501,14 @@ static __le32 get_missing_options(struct hci_dev *hdev) return cpu_to_le32(options); } +static int new_options(struct hci_dev *hdev, struct sock *skip) +{ + __le32 options = get_missing_options(hdev); + + return mgmt_event(MGMT_EV_NEW_CONFIG_OPTIONS, hdev, &options, + sizeof(options), skip); +} + static int send_options_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev) { __le32 options = get_missing_options(hdev); @@ -5415,6 +5424,8 @@ static int set_external_config(struct sock *sk, struct hci_dev *hdev, if (!changed) goto unlock; + err = new_options(hdev, sk); + if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) == is_configured(hdev)) { mgmt_index_removed(hdev); change_bit(HCI_UNCONFIGURED, &hdev->dev_flags); -- cgit v1.2.3 From dc3094a7135c2386d6ff465b13d008ea2262718d Mon Sep 17 00:00:00 2001 From: Ramakrishnan Muthukrishnan Date: Thu, 19 Jun 2014 14:22:59 -0300 Subject: [media] media: v4l2-dev.h: remove V4L2_FL_USE_FH_PRIO flag Since none of the drivers are using it, this flag can be removed. Signed-off-by: Ramakrishnan Muthukrishnan Reviewed-by: Hans Verkuil Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-dev.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index eec6e460f649..eb76cfd47189 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -44,8 +44,6 @@ struct v4l2_ctrl_handler; #define V4L2_FL_REGISTERED (0) /* file->private_data points to struct v4l2_fh */ #define V4L2_FL_USES_V4L2_FH (1) -/* Use the prio field of v4l2_fh for core priority checking */ -#define V4L2_FL_USE_FH_PRIO (2) /* Priority helper functions */ -- cgit v1.2.3 From e30d3f5fef378cd14ba8c331a5c7a2f9239c2770 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 5 Jul 2014 10:48:03 +0200 Subject: Bluetooth: Store Bluetooth address from controller setup During the setup phase of a controller, the Bluetooth address will be read and to have that original address available for later use, store it as setup address. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_event.c | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 1cae4d3a629d..aff285698c85 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -171,6 +171,7 @@ struct hci_dev { __u8 bus; __u8 dev_type; bdaddr_t bdaddr; + bdaddr_t setup_addr; bdaddr_t public_addr; bdaddr_t random_addr; bdaddr_t static_addr; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 013c371d3c87..c2ba79c8fe51 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -645,8 +645,14 @@ static void hci_cc_read_bd_addr(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); - if (!rp->status) + if (rp->status) + return; + + if (test_bit(HCI_INIT, &hdev->flags)) bacpy(&hdev->bdaddr, &rp->bdaddr); + + if (test_bit(HCI_SETUP, &hdev->dev_flags)) + bacpy(&hdev->setup_addr, &rp->bdaddr); } static void hci_cc_read_page_scan_activity(struct hci_dev *hdev, -- cgit v1.2.3 From 19de0825cd8acb1de6fa6a135b1f059446781049 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 6 Jul 2014 13:06:51 +0300 Subject: Bluetooth: Fix sending Device Removed when clearing all parameters When calling Device Remove with BDADDR_ANY we should in a similar way emit Device Removed events as we do when removing a single device. Since we have to iterate the list and call device_removed() the dedicated hci_conn_params_clear_enabled() is not really useful anymore. This patch removes the helper function and does the event emission and list item removal in a single loop. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 - net/bluetooth/hci_core.c | 18 ------------------ net/bluetooth/mgmt.c | 15 ++++++++++++++- 3 files changed, 14 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index aff285698c85..9d838a072db4 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -867,7 +867,6 @@ int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); void hci_conn_params_clear_all(struct hci_dev *hdev); void hci_conn_params_clear_disabled(struct hci_dev *hdev); -void hci_conn_params_clear_enabled(struct hci_dev *hdev); struct hci_conn_params *hci_pend_le_action_lookup(struct list_head *list, bdaddr_t *addr, diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index f996e2c4815c..d01bd043c231 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3621,24 +3621,6 @@ void hci_conn_params_clear_disabled(struct hci_dev *hdev) BT_DBG("All LE disabled connection parameters were removed"); } -/* This function requires the caller holds hdev->lock */ -void hci_conn_params_clear_enabled(struct hci_dev *hdev) -{ - struct hci_conn_params *params, *tmp; - - list_for_each_entry_safe(params, tmp, &hdev->le_conn_params, list) { - if (params->auto_connect == HCI_AUTO_CONN_DISABLED) - continue; - list_del(¶ms->action); - list_del(¶ms->list); - kfree(params); - } - - hci_update_background_scan(hdev); - - BT_DBG("All enabled LE connection parameters were removed"); -} - /* This function requires the caller holds hdev->lock */ void hci_conn_params_clear_all(struct hci_dev *hdev) { diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f514eb15e0fb..04a66429ad4d 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5284,6 +5284,8 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, device_removed(sk, hdev, &cp->addr.bdaddr, cp->addr.type); } else { + struct hci_conn_params *p, *tmp; + if (cp->addr.type) { err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, MGMT_STATUS_INVALID_PARAMS, @@ -5291,7 +5293,18 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, goto unlock; } - hci_conn_params_clear_enabled(hdev); + list_for_each_entry_safe(p, tmp, &hdev->le_conn_params, list) { + if (p->auto_connect == HCI_AUTO_CONN_DISABLED) + continue; + device_removed(sk, hdev, &p->addr, p->addr_type); + list_del(&p->action); + list_del(&p->list); + kfree(p); + } + + BT_DBG("All LE connection parameters were removed"); + + hci_update_background_scan(hdev); } err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, -- cgit v1.2.3 From d603b76b0c18c5adf4a3164dff50bb15948cd7bd Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 6 Jul 2014 12:11:14 +0200 Subject: Bluetooth: Run controller setup after external configuration When the external configuration triggers the switch to a configured controller, it means the setup needs to be run. Controllers that start out unconfigured have only run limited set of HCI commands. This is not enough for complete operation and thus run the setup procedure before announcing the new controller index. This introduces HCI_CONFIG flag as companion to HCI_SETUP flag. The HCI_SETUP flag is only used once for the initial setup procedure. And during that procedure hdev->setup driver callback is called. With the new HCI_CONFIG the switch from unconfigured to configured state is triggering the same setup procedure just without hdev->setup. This is required since bringing a controller back to unconfigured state from configured state is possible. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 1 + net/bluetooth/hci_core.c | 17 ++++++++++++++--- net/bluetooth/hci_sock.c | 3 ++- net/bluetooth/mgmt.c | 14 ++++++++++++-- 4 files changed, 29 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 80c5fc947fbc..46f2458e4bc9 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -163,6 +163,7 @@ enum { */ enum { HCI_SETUP, + HCI_CONFIG, HCI_AUTO_OFF, HCI_RFKILLED, HCI_MGMT, diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index d01bd043c231..bf7cf512df88 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2232,7 +2232,8 @@ static int hci_dev_do_open(struct hci_dev *hdev) goto done; } - if (!test_bit(HCI_SETUP, &hdev->dev_flags)) { + if (!test_bit(HCI_SETUP, &hdev->dev_flags) && + !test_bit(HCI_CONFIG, &hdev->dev_flags)) { /* Check for rfkill but allow the HCI setup stage to * proceed (which in itself doesn't cause any RF activity). */ @@ -2326,6 +2327,7 @@ static int hci_dev_do_open(struct hci_dev *hdev) set_bit(HCI_UP, &hdev->flags); hci_notify(hdev, HCI_DEV_UP); if (!test_bit(HCI_SETUP, &hdev->dev_flags) && + !test_bit(HCI_CONFIG, &hdev->dev_flags) && !test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) && !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags) && hdev->dev_type == HCI_BREDR) { @@ -2824,7 +2826,8 @@ static int hci_rfkill_set_block(void *data, bool blocked) if (blocked) { set_bit(HCI_RFKILLED, &hdev->dev_flags); - if (!test_bit(HCI_SETUP, &hdev->dev_flags)) + if (!test_bit(HCI_SETUP, &hdev->dev_flags) && + !test_bit(HCI_CONFIG, &hdev->dev_flags)) hci_dev_do_close(hdev); } else { clear_bit(HCI_RFKILLED, &hdev->dev_flags); @@ -2881,6 +2884,12 @@ static void hci_power_on(struct work_struct *work) * and no event will be send. */ mgmt_index_added(hdev); + } else if (test_and_clear_bit(HCI_CONFIG, &hdev->dev_flags)) { + /* Powering on the controller with HCI_CONFIG set only + * happens with the transition from unconfigured to + * configured. This will send the Index Added event. + */ + mgmt_index_added(hdev); } } @@ -4045,7 +4054,8 @@ void hci_unregister_dev(struct hci_dev *hdev) cancel_work_sync(&hdev->power_on); if (!test_bit(HCI_INIT, &hdev->flags) && - !test_bit(HCI_SETUP, &hdev->dev_flags)) { + !test_bit(HCI_SETUP, &hdev->dev_flags) && + !test_bit(HCI_CONFIG, &hdev->dev_flags)) { hci_dev_lock(hdev); mgmt_index_removed(hdev); hci_dev_unlock(hdev); @@ -5370,6 +5380,7 @@ void hci_update_background_scan(struct hci_dev *hdev) if (!test_bit(HCI_UP, &hdev->flags) || test_bit(HCI_INIT, &hdev->flags) || test_bit(HCI_SETUP, &hdev->dev_flags) || + test_bit(HCI_CONFIG, &hdev->dev_flags) || test_bit(HCI_AUTO_OFF, &hdev->dev_flags) || test_bit(HCI_UNREGISTER, &hdev->dev_flags)) return; diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index ba13ad8e25c6..802665751cc4 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -693,7 +693,8 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, if (test_bit(HCI_UP, &hdev->flags) || test_bit(HCI_INIT, &hdev->flags) || - test_bit(HCI_SETUP, &hdev->dev_flags)) { + test_bit(HCI_SETUP, &hdev->dev_flags) || + test_bit(HCI_CONFIG, &hdev->dev_flags)) { err = -EBUSY; hci_dev_put(hdev); goto done; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 04a66429ad4d..d66463a52280 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -385,6 +385,7 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data, count = 0; list_for_each_entry(d, &hci_dev_list, list) { if (test_bit(HCI_SETUP, &d->dev_flags) || + test_bit(HCI_CONFIG, &d->dev_flags) || test_bit(HCI_USER_CHANNEL, &d->dev_flags)) continue; @@ -444,6 +445,7 @@ static int read_unconf_index_list(struct sock *sk, struct hci_dev *hdev, count = 0; list_for_each_entry(d, &hci_dev_list, list) { if (test_bit(HCI_SETUP, &d->dev_flags) || + test_bit(HCI_CONFIG, &d->dev_flags) || test_bit(HCI_USER_CHANNEL, &d->dev_flags)) continue; @@ -5441,8 +5443,15 @@ static int set_external_config(struct sock *sk, struct hci_dev *hdev, if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) == is_configured(hdev)) { mgmt_index_removed(hdev); - change_bit(HCI_UNCONFIGURED, &hdev->dev_flags); - mgmt_index_added(hdev); + + if (test_and_change_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { + set_bit(HCI_CONFIG, &hdev->dev_flags); + set_bit(HCI_AUTO_OFF, &hdev->dev_flags); + + queue_work(hdev->req_workqueue, &hdev->power_on); + } else { + mgmt_index_added(hdev); + } } unlock: @@ -5558,6 +5567,7 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) } if (test_bit(HCI_SETUP, &hdev->dev_flags) || + test_bit(HCI_CONFIG, &hdev->dev_flags) || test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { err = cmd_status(sk, index, opcode, MGMT_STATUS_INVALID_INDEX); -- cgit v1.2.3 From 9713c17b086c1ebfe34ea4d34147a778276e2dab Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 6 Jul 2014 12:11:15 +0200 Subject: Bluetooth: Add support for changing the public device address This adds support for changing the public device address. This feature is required by controllers that do not provide a public address and have HCI_QUIRK_INVALID_BDADDR set. Even if a controller has a public device address, this is useful when an embedded system wants to use its own value. As long as the driver provides the set_bdaddr callback, this allows changing the device address before powering on the controller. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 6 +++++ net/bluetooth/hci_core.c | 14 ++++++----- net/bluetooth/mgmt.c | 57 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 70 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 80606d2fe086..623d5203c592 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -489,6 +489,12 @@ struct mgmt_cp_set_external_config { } __packed; #define MGMT_SET_EXTERNAL_CONFIG_SIZE 1 +#define MGMT_OP_SET_PUBLIC_ADDRESS 0x0039 +struct mgmt_cp_set_public_address { + bdaddr_t bdaddr; +} __packed; +#define MGMT_SET_PUBLIC_ADDRESS_SIZE 6 + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index bf7cf512df88..c96b96ca41ac 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2302,12 +2302,14 @@ static int hci_dev_do_open(struct hci_dev *hdev) ret = __hci_unconf_init(hdev); } - /* If public address change is configured, ensure that the - * address gets programmed. If the driver does not support - * changing the public address, fail the power on procedure. - */ - if (!ret && bacmp(&hdev->public_addr, BDADDR_ANY)) { - if (hdev->set_bdaddr) + if (test_bit(HCI_CONFIG, &hdev->dev_flags)) { + /* If public address change is configured, ensure that + * the address gets programmed. If the driver does not + * support changing the public address, fail the power + * on procedure. + */ + if (bacmp(&hdev->public_addr, BDADDR_ANY) && + hdev->set_bdaddr) ret = hdev->set_bdaddr(hdev, &hdev->public_addr); else ret = -EADDRNOTAVAIL; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d66463a52280..8275316ae099 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -92,6 +92,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_READ_UNCONF_INDEX_LIST, MGMT_OP_READ_CONFIG_INFO, MGMT_OP_SET_EXTERNAL_CONFIG, + MGMT_OP_SET_PUBLIC_ADDRESS, }; static const u16 mgmt_events[] = { @@ -5459,6 +5460,58 @@ unlock: return err; } +static int set_public_address(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_cp_set_public_address *cp = data; + bool changed; + int err; + + BT_DBG("%s", hdev->name); + + if (hdev_is_powered(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_PUBLIC_ADDRESS, + MGMT_STATUS_REJECTED); + + if (!bacmp(&cp->bdaddr, BDADDR_ANY)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_PUBLIC_ADDRESS, + MGMT_STATUS_INVALID_PARAMS); + + if (!hdev->set_bdaddr) + return cmd_status(sk, hdev->id, MGMT_OP_SET_PUBLIC_ADDRESS, + MGMT_STATUS_NOT_SUPPORTED); + + hci_dev_lock(hdev); + + changed = !!bacmp(&hdev->public_addr, &cp->bdaddr); + bacpy(&hdev->public_addr, &cp->bdaddr); + + err = send_options_rsp(sk, MGMT_OP_SET_PUBLIC_ADDRESS, hdev); + if (err < 0) + goto unlock; + + if (!changed) + goto unlock; + + if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) + err = new_options(hdev, sk); + + if (is_configured(hdev)) { + mgmt_index_removed(hdev); + + clear_bit(HCI_UNCONFIGURED, &hdev->dev_flags); + + set_bit(HCI_CONFIG, &hdev->dev_flags); + set_bit(HCI_AUTO_OFF, &hdev->dev_flags); + + queue_work(hdev->req_workqueue, &hdev->power_on); + } + +unlock: + hci_dev_unlock(hdev); + return err; +} + static const struct mgmt_handler { int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len); @@ -5522,6 +5575,7 @@ static const struct mgmt_handler { { read_unconf_index_list, false, MGMT_READ_UNCONF_INDEX_LIST_SIZE }, { read_config_info, false, MGMT_READ_CONFIG_INFO_SIZE }, { set_external_config, false, MGMT_SET_EXTERNAL_CONFIG_SIZE }, + { set_public_address, false, MGMT_SET_PUBLIC_ADDRESS_SIZE }, }; int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) @@ -5576,7 +5630,8 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) && opcode != MGMT_OP_READ_CONFIG_INFO && - opcode != MGMT_OP_SET_EXTERNAL_CONFIG) { + opcode != MGMT_OP_SET_EXTERNAL_CONFIG && + opcode != MGMT_OP_SET_PUBLIC_ADDRESS) { err = cmd_status(sk, index, opcode, MGMT_STATUS_INVALID_INDEX); goto done; -- cgit v1.2.3 From 09ae260ba452c2ed36ec295941a58cb75db213ed Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 6 Jul 2014 13:41:15 +0300 Subject: Bluetooth: Use lower timeout for LE auto-connections When we establish connections as a consequence of receiving an advertising report it makes no sense to wait the normal 20 second LE connection timeout. This patch modifies the hci_connect_le function to take an extra timeout value and uses a lower 2 second timeout for the auto-connection case. This timeout is intentionally chosen to be just a bit higher than the 1.28 second timeout that High Duty Cycle Advertising uses. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 1 + include/net/bluetooth/hci_core.h | 4 +++- net/bluetooth/hci_conn.c | 4 +++- net/bluetooth/hci_event.c | 6 +++--- net/bluetooth/l2cap_core.c | 2 +- net/bluetooth/mgmt.c | 3 ++- 6 files changed, 13 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 46f2458e4bc9..5481d1c8badb 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -238,6 +238,7 @@ enum { #define HCI_AUTO_OFF_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ #define HCI_POWER_OFF_TIMEOUT msecs_to_jiffies(5000) /* 5 seconds */ #define HCI_LE_CONN_TIMEOUT msecs_to_jiffies(20000) /* 20 seconds */ +#define HCI_LE_AUTOCONN_TIMEOUT msecs_to_jiffies(2000) /* 2 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 9d838a072db4..06039aae3010 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -385,6 +385,7 @@ struct hci_conn { __u32 passkey_notify; __u8 passkey_entered; __u16 disc_timeout; + __u16 conn_timeout; __u16 setting; __u16 le_conn_min_interval; __u16 le_conn_max_interval; @@ -703,7 +704,8 @@ void hci_chan_list_flush(struct hci_conn *conn); struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle); struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, - u8 dst_type, u8 sec_level, u8 auth_type); + u8 dst_type, u8 sec_level, u8 auth_type, + u16 conn_timeout); struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst, u8 sec_level, u8 auth_type); struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst, diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 0d579d036833..faa032fcdaee 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -700,7 +700,8 @@ static void hci_req_directed_advertising(struct hci_request *req, } struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, - u8 dst_type, u8 sec_level, u8 auth_type) + u8 dst_type, u8 sec_level, u8 auth_type, + u16 conn_timeout) { struct hci_conn_params *params; struct hci_conn *conn; @@ -758,6 +759,7 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, conn->sec_level = BT_SECURITY_LOW; conn->pending_sec_level = sec_level; conn->auth_type = auth_type; + conn->conn_timeout = conn_timeout; hci_req_init(&req, hdev); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index c2ba79c8fe51..f452e44eff3c 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1073,7 +1073,7 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb) if (conn) queue_delayed_work(hdev->workqueue, &conn->le_conn_timeout, - HCI_LE_CONN_TIMEOUT); + conn->conn_timeout); } mgmt_advertising(hdev, *sent); @@ -1913,7 +1913,7 @@ static void hci_cs_le_create_conn(struct hci_dev *hdev, u8 status) if (cp->filter_policy == HCI_LE_USE_PEER_ADDR) queue_delayed_work(conn->hdev->workqueue, &conn->le_conn_timeout, - HCI_LE_CONN_TIMEOUT); + conn->conn_timeout); unlock: hci_dev_unlock(hdev); @@ -4238,7 +4238,7 @@ static bool check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, return false; conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW, - HCI_AT_NO_BONDING); + HCI_AT_NO_BONDING, HCI_LE_AUTOCONN_TIMEOUT); if (!IS_ERR(conn)) return true; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index f1f544a12c3c..a219276d9f92 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -7135,7 +7135,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, dst_type = ADDR_LE_DEV_RANDOM; hcon = hci_connect_le(hdev, dst, dst_type, chan->sec_level, - auth_type); + auth_type, HCI_LE_CONN_TIMEOUT); } else { hcon = hci_connect_acl(hdev, dst, chan->sec_level, auth_type); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index e253f8e1fa47..be91d55c258b 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3116,7 +3116,8 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, hci_conn_params_add(hdev, &cp->addr.bdaddr, addr_type); conn = hci_connect_le(hdev, &cp->addr.bdaddr, addr_type, - sec_level, auth_type); + sec_level, auth_type, + HCI_LE_CONN_TIMEOUT); } if (IS_ERR(conn)) { -- cgit v1.2.3 From b22f6434cf48af001330e370e9d781aeb668f98c Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 27 Jun 2014 09:03:12 +0200 Subject: iommu: Constify struct iommu_ops This structure is read-only data and should never be modified. Signed-off-by: Thierry Reding Signed-off-by: Greg Kroah-Hartman Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 4 ++-- drivers/iommu/arm-smmu.c | 2 +- drivers/iommu/exynos-iommu.c | 2 +- drivers/iommu/fsl_pamu_domain.c | 2 +- drivers/iommu/intel-iommu.c | 4 ++-- drivers/iommu/iommu.c | 19 ++++++++++++++----- drivers/iommu/ipmmu-vmsa.c | 2 +- drivers/iommu/msm_iommu.c | 2 +- drivers/iommu/omap-iommu.c | 2 +- drivers/iommu/shmobile-iommu.c | 2 +- drivers/iommu/tegra-gart.c | 2 +- drivers/iommu/tegra-smmu.c | 2 +- include/linux/device.h | 2 +- include/linux/iommu.h | 4 ++-- 14 files changed, 30 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index c8f87a6c9b90..18405314168b 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -80,7 +80,7 @@ LIST_HEAD(hpet_map); */ static struct protection_domain *pt_domain; -static struct iommu_ops amd_iommu_ops; +static const struct iommu_ops amd_iommu_ops; static ATOMIC_NOTIFIER_HEAD(ppr_notifier); int amd_iommu_max_glx_val = -1; @@ -3395,7 +3395,7 @@ static int amd_iommu_domain_has_cap(struct iommu_domain *domain, return 0; } -static struct iommu_ops amd_iommu_ops = { +static const struct iommu_ops amd_iommu_ops = { .domain_init = amd_iommu_domain_init, .domain_destroy = amd_iommu_domain_destroy, .attach_dev = amd_iommu_attach_device, diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 1599354e974d..67727294e6b5 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -1609,7 +1609,7 @@ static void arm_smmu_remove_device(struct device *dev) iommu_group_remove_device(dev); } -static struct iommu_ops arm_smmu_ops = { +static const struct iommu_ops arm_smmu_ops = { .domain_init = arm_smmu_domain_init, .domain_destroy = arm_smmu_domain_destroy, .attach_dev = arm_smmu_attach_dev, diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 99054d2c040d..d037e87a1fe5 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -1170,7 +1170,7 @@ static void exynos_iommu_remove_device(struct device *dev) iommu_group_remove_device(dev); } -static struct iommu_ops exynos_iommu_ops = { +static const struct iommu_ops exynos_iommu_ops = { .domain_init = exynos_iommu_domain_init, .domain_destroy = exynos_iommu_domain_destroy, .attach_dev = exynos_iommu_attach_device, diff --git a/drivers/iommu/fsl_pamu_domain.c b/drivers/iommu/fsl_pamu_domain.c index d02d668c6ab1..fd5e9146aee6 100644 --- a/drivers/iommu/fsl_pamu_domain.c +++ b/drivers/iommu/fsl_pamu_domain.c @@ -1076,7 +1076,7 @@ static u32 fsl_pamu_get_windows(struct iommu_domain *domain) return dma_domain->win_cnt; } -static struct iommu_ops fsl_pamu_ops = { +static const struct iommu_ops fsl_pamu_ops = { .domain_init = fsl_pamu_domain_init, .domain_destroy = fsl_pamu_domain_destroy, .attach_dev = fsl_pamu_attach_device, diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index f9e5f84ee952..41d9a2dcab2e 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -450,7 +450,7 @@ EXPORT_SYMBOL_GPL(intel_iommu_gfx_mapped); static DEFINE_SPINLOCK(device_domain_lock); static LIST_HEAD(device_domain_list); -static struct iommu_ops intel_iommu_ops; +static const struct iommu_ops intel_iommu_ops; static int __init intel_iommu_setup(char *str) { @@ -4453,7 +4453,7 @@ static void intel_iommu_remove_device(struct device *dev) iommu_device_unlink(iommu->iommu_dev, dev); } -static struct iommu_ops intel_iommu_ops = { +static const struct iommu_ops intel_iommu_ops = { .domain_init = intel_iommu_domain_init, .domain_destroy = intel_iommu_domain_destroy, .attach_dev = intel_iommu_attach_device, diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index d061c8677a81..169836020208 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -36,6 +36,10 @@ static struct kset *iommu_group_kset; static struct ida iommu_group_ida; static struct mutex iommu_group_mutex; +struct iommu_callback_data { + const struct iommu_ops *ops; +}; + struct iommu_group { struct kobject kobj; struct kobject *devices_kobj; @@ -698,7 +702,8 @@ struct iommu_group *iommu_group_get_for_dev(struct device *dev) static int add_iommu_group(struct device *dev, void *data) { - struct iommu_ops *ops = data; + struct iommu_callback_data *cb = data; + const struct iommu_ops *ops = cb->ops; if (!ops->add_device) return -ENODEV; @@ -714,7 +719,7 @@ 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; + const struct iommu_ops *ops = dev->bus->iommu_ops; struct iommu_group *group; unsigned long group_action = 0; @@ -767,10 +772,14 @@ 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) +static void iommu_bus_init(struct bus_type *bus, const struct iommu_ops *ops) { + struct iommu_callback_data cb = { + .ops = ops, + }; + bus_register_notifier(bus, &iommu_bus_nb); - bus_for_each_dev(bus, NULL, ops, add_iommu_group); + bus_for_each_dev(bus, NULL, &cb, add_iommu_group); } /** @@ -786,7 +795,7 @@ static void iommu_bus_init(struct bus_type *bus, struct iommu_ops *ops) * is set up. With this function the iommu-driver can set the iommu-ops * afterwards. */ -int bus_set_iommu(struct bus_type *bus, struct iommu_ops *ops) +int bus_set_iommu(struct bus_type *bus, const struct iommu_ops *ops) { if (bus->iommu_ops != NULL) return -EBUSY; diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c index 53cde086e83b..7dab5cbcc775 100644 --- a/drivers/iommu/ipmmu-vmsa.c +++ b/drivers/iommu/ipmmu-vmsa.c @@ -1120,7 +1120,7 @@ static void ipmmu_remove_device(struct device *dev) dev->archdata.iommu = NULL; } -static struct iommu_ops ipmmu_ops = { +static const struct iommu_ops ipmmu_ops = { .domain_init = ipmmu_domain_init, .domain_destroy = ipmmu_domain_destroy, .attach_dev = ipmmu_attach_device, diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c index f5ff657f49fa..49f41d6e02f1 100644 --- a/drivers/iommu/msm_iommu.c +++ b/drivers/iommu/msm_iommu.c @@ -674,7 +674,7 @@ fail: return 0; } -static struct iommu_ops msm_iommu_ops = { +static const struct iommu_ops msm_iommu_ops = { .domain_init = msm_iommu_domain_init, .domain_destroy = msm_iommu_domain_destroy, .attach_dev = msm_iommu_attach_dev, diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c index 895af06a667f..031b2476bcc4 100644 --- a/drivers/iommu/omap-iommu.c +++ b/drivers/iommu/omap-iommu.c @@ -1291,7 +1291,7 @@ static void omap_iommu_remove_device(struct device *dev) kfree(arch_data); } -static struct iommu_ops omap_iommu_ops = { +static const struct iommu_ops omap_iommu_ops = { .domain_init = omap_iommu_domain_init, .domain_destroy = omap_iommu_domain_destroy, .attach_dev = omap_iommu_attach_dev, diff --git a/drivers/iommu/shmobile-iommu.c b/drivers/iommu/shmobile-iommu.c index 464acda0bbc4..1333e6fb3405 100644 --- a/drivers/iommu/shmobile-iommu.c +++ b/drivers/iommu/shmobile-iommu.c @@ -354,7 +354,7 @@ static int shmobile_iommu_add_device(struct device *dev) return 0; } -static struct iommu_ops shmobile_iommu_ops = { +static const struct iommu_ops shmobile_iommu_ops = { .domain_init = shmobile_iommu_domain_init, .domain_destroy = shmobile_iommu_domain_destroy, .attach_dev = shmobile_iommu_attach_device, diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c index dba1a9fd5070..b10a8ecede8e 100644 --- a/drivers/iommu/tegra-gart.c +++ b/drivers/iommu/tegra-gart.c @@ -309,7 +309,7 @@ static int gart_iommu_domain_has_cap(struct iommu_domain *domain, return 0; } -static struct iommu_ops gart_iommu_ops = { +static const struct iommu_ops gart_iommu_ops = { .domain_init = gart_iommu_domain_init, .domain_destroy = gart_iommu_domain_destroy, .attach_dev = gart_iommu_attach_dev, diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 605b5b46a903..792da5ea6d12 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -947,7 +947,7 @@ static void smmu_iommu_domain_destroy(struct iommu_domain *domain) dev_dbg(smmu->dev, "smmu_as@%p\n", as); } -static struct iommu_ops smmu_iommu_ops = { +static const struct iommu_ops smmu_iommu_ops = { .domain_init = smmu_iommu_domain_init, .domain_destroy = smmu_iommu_domain_destroy, .attach_dev = smmu_iommu_attach_dev, diff --git a/include/linux/device.h b/include/linux/device.h index af424acd393d..75f5bcb8930b 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -124,7 +124,7 @@ struct bus_type { const struct dev_pm_ops *pm; - struct iommu_ops *iommu_ops; + const struct iommu_ops *iommu_ops; struct subsys_private *p; struct lock_class_key lock_key; diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 7fd16e3d1f25..c7097d7f024c 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -50,7 +50,7 @@ struct iommu_domain_geometry { }; struct iommu_domain { - struct iommu_ops *ops; + const struct iommu_ops *ops; void *priv; iommu_fault_handler_t handler; void *handler_token; @@ -140,7 +140,7 @@ struct iommu_ops { #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 int bus_set_iommu(struct bus_type *bus, const struct iommu_ops *ops); extern bool iommu_present(struct bus_type *bus); extern struct iommu_domain *iommu_domain_alloc(struct bus_type *bus); extern struct iommu_group *iommu_group_get_by_id(int id); -- cgit v1.2.3 From 2d239c9e92087d5f4f667371ae350db9f76e3191 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 22 Jun 2014 20:59:00 +0100 Subject: iio:st sensors: remove custom sampling frequence attribute in favour of core support. This allows in kernel client drivers to access this Signed-off-by: Jonathan Cameron Cc: Denis Ciocca Reviewed-by: Hartmut Knaack --- drivers/iio/accel/st_accel_core.c | 12 ++++++++-- drivers/iio/common/st_sensors/st_sensors_core.c | 29 ------------------------- drivers/iio/gyro/st_gyro_core.c | 12 ++++++++-- drivers/iio/magnetometer/st_magn_core.c | 12 ++++++++-- drivers/iio/pressure/st_pressure_core.c | 27 +++++++++++++++++++++-- include/linux/iio/common/st_sensors.h | 12 +--------- 6 files changed, 56 insertions(+), 48 deletions(-) (limited to 'include') diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index a2abf7c2ce3b..087864854c61 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -393,6 +393,9 @@ static int st_accel_read_raw(struct iio_dev *indio_dev, *val = 0; *val2 = adata->current_fullscale->gain; return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = adata->odr; + return IIO_VAL_INT; default: return -EINVAL; } @@ -410,6 +413,13 @@ static int st_accel_write_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SCALE: err = st_sensors_set_fullscale_by_gain(indio_dev, val2); break; + case IIO_CHAN_INFO_SAMP_FREQ: + if (val2) + return -EINVAL; + mutex_lock(&indio_dev->mlock); + err = st_sensors_set_odr(indio_dev, val); + mutex_unlock(&indio_dev->mlock); + return err; default: return -EINVAL; } @@ -417,14 +427,12 @@ static int st_accel_write_raw(struct iio_dev *indio_dev, return err; } -static ST_SENSOR_DEV_ATTR_SAMP_FREQ(); static ST_SENSORS_DEV_ATTR_SAMP_FREQ_AVAIL(); static ST_SENSORS_DEV_ATTR_SCALE_AVAIL(in_accel_scale_available); static struct attribute *st_accel_attributes[] = { &iio_dev_attr_sampling_frequency_available.dev_attr.attr, &iio_dev_attr_in_accel_scale_available.dev_attr.attr, - &iio_dev_attr_sampling_frequency.dev_attr.attr, NULL, }; diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index e8b932fed70e..30fb6407fad0 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -463,35 +463,6 @@ read_wai_error: } EXPORT_SYMBOL(st_sensors_check_device_support); -ssize_t st_sensors_sysfs_get_sampling_frequency(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct st_sensor_data *adata = iio_priv(dev_get_drvdata(dev)); - - return sprintf(buf, "%d\n", adata->odr); -} -EXPORT_SYMBOL(st_sensors_sysfs_get_sampling_frequency); - -ssize_t st_sensors_sysfs_set_sampling_frequency(struct device *dev, - struct device_attribute *attr, const char *buf, size_t size) -{ - int err; - unsigned int odr; - struct iio_dev *indio_dev = dev_get_drvdata(dev); - - err = kstrtoint(buf, 10, &odr); - if (err < 0) - goto conversion_error; - - mutex_lock(&indio_dev->mlock); - err = st_sensors_set_odr(indio_dev, odr); - mutex_unlock(&indio_dev->mlock); - -conversion_error: - return err < 0 ? err : size; -} -EXPORT_SYMBOL(st_sensors_sysfs_set_sampling_frequency); - ssize_t st_sensors_sysfs_sampling_frequency_avail(struct device *dev, struct device_attribute *attr, char *buf) { diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c index ed74a9069989..f156fc6c5c6c 100644 --- a/drivers/iio/gyro/st_gyro_core.c +++ b/drivers/iio/gyro/st_gyro_core.c @@ -245,6 +245,9 @@ static int st_gyro_read_raw(struct iio_dev *indio_dev, *val = 0; *val2 = gdata->current_fullscale->gain; return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = gdata->odr; + return IIO_VAL_INT; default: return -EINVAL; } @@ -262,6 +265,13 @@ static int st_gyro_write_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SCALE: err = st_sensors_set_fullscale_by_gain(indio_dev, val2); break; + case IIO_CHAN_INFO_SAMP_FREQ: + if (val2) + return -EINVAL; + mutex_lock(&indio_dev->mlock); + err = st_sensors_set_odr(indio_dev, val); + mutex_unlock(&indio_dev->mlock); + return err; default: err = -EINVAL; } @@ -269,14 +279,12 @@ static int st_gyro_write_raw(struct iio_dev *indio_dev, return err; } -static ST_SENSOR_DEV_ATTR_SAMP_FREQ(); static ST_SENSORS_DEV_ATTR_SAMP_FREQ_AVAIL(); static ST_SENSORS_DEV_ATTR_SCALE_AVAIL(in_anglvel_scale_available); static struct attribute *st_gyro_attributes[] = { &iio_dev_attr_sampling_frequency_available.dev_attr.attr, &iio_dev_attr_in_anglvel_scale_available.dev_attr.attr, - &iio_dev_attr_sampling_frequency.dev_attr.attr, NULL, }; diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index 240a21dd0c61..a4b64130ac2f 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -299,6 +299,9 @@ static int st_magn_read_raw(struct iio_dev *indio_dev, else *val2 = mdata->current_fullscale->gain; return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = mdata->odr; + return IIO_VAL_INT; default: return -EINVAL; } @@ -316,6 +319,13 @@ static int st_magn_write_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SCALE: err = st_sensors_set_fullscale_by_gain(indio_dev, val2); break; + case IIO_CHAN_INFO_SAMP_FREQ: + if (val2) + return -EINVAL; + mutex_lock(&indio_dev->mlock); + err = st_sensors_set_odr(indio_dev, val); + mutex_unlock(&indio_dev->mlock); + return err; default: err = -EINVAL; } @@ -323,14 +333,12 @@ static int st_magn_write_raw(struct iio_dev *indio_dev, return err; } -static ST_SENSOR_DEV_ATTR_SAMP_FREQ(); static ST_SENSORS_DEV_ATTR_SAMP_FREQ_AVAIL(); static ST_SENSORS_DEV_ATTR_SCALE_AVAIL(in_magn_scale_available); static struct attribute *st_magn_attributes[] = { &iio_dev_attr_sampling_frequency_available.dev_attr.attr, &iio_dev_attr_in_magn_scale_available.dev_attr.attr, - &iio_dev_attr_sampling_frequency.dev_attr.attr, NULL, }; diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index cd7e01f3a93b..473d914ef470 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -307,6 +307,27 @@ static const struct st_sensors st_press_sensors[] = { }, }; +static int st_press_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *ch, + int val, + int val2, + long mask) +{ + int err; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + if (val2) + return -EINVAL; + mutex_lock(&indio_dev->mlock); + err = st_sensors_set_odr(indio_dev, val); + mutex_unlock(&indio_dev->mlock); + return err; + default: + return -EINVAL; + } +} + static int st_press_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *ch, int *val, int *val2, long mask) @@ -349,6 +370,9 @@ static int st_press_read_raw(struct iio_dev *indio_dev, } return IIO_VAL_FRACTIONAL; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = pdata->odr; + return IIO_VAL_INT; default: return -EINVAL; } @@ -357,12 +381,10 @@ read_error: return err; } -static ST_SENSOR_DEV_ATTR_SAMP_FREQ(); static ST_SENSORS_DEV_ATTR_SAMP_FREQ_AVAIL(); static struct attribute *st_press_attributes[] = { &iio_dev_attr_sampling_frequency_available.dev_attr.attr, - &iio_dev_attr_sampling_frequency.dev_attr.attr, NULL, }; @@ -374,6 +396,7 @@ static const struct iio_info press_info = { .driver_module = THIS_MODULE, .attrs = &st_press_attribute_group, .read_raw = &st_press_read_raw, + .write_raw = &st_press_write_raw, }; #ifdef CONFIG_IIO_TRIGGER diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h index 96f51f0e0096..d8257ab60bac 100644 --- a/include/linux/iio/common/st_sensors.h +++ b/include/linux/iio/common/st_sensors.h @@ -47,6 +47,7 @@ .type = device_type, \ .modified = mod, \ .info_mask_separate = mask, \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ .scan_index = index, \ .channel2 = ch2, \ .address = addr, \ @@ -59,11 +60,6 @@ }, \ } -#define ST_SENSOR_DEV_ATTR_SAMP_FREQ() \ - IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, \ - st_sensors_sysfs_get_sampling_frequency, \ - st_sensors_sysfs_set_sampling_frequency) - #define ST_SENSORS_DEV_ATTR_SAMP_FREQ_AVAIL() \ IIO_DEV_ATTR_SAMP_FREQ_AVAIL( \ st_sensors_sysfs_sampling_frequency_avail) @@ -285,12 +281,6 @@ int st_sensors_read_info_raw(struct iio_dev *indio_dev, int st_sensors_check_device_support(struct iio_dev *indio_dev, int num_sensors_list, const struct st_sensors *sensors); -ssize_t st_sensors_sysfs_get_sampling_frequency(struct device *dev, - struct device_attribute *attr, char *buf); - -ssize_t st_sensors_sysfs_set_sampling_frequency(struct device *dev, - struct device_attribute *attr, const char *buf, size_t size); - ssize_t st_sensors_sysfs_sampling_frequency_avail(struct device *dev, struct device_attribute *attr, char *buf); -- cgit v1.2.3 From 82695ef549b5299d3d9e088d6648289bda8ef3d8 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 22 Jun 2014 20:59:00 +0100 Subject: iio: adis: Switch sampling frequency attr to core support. By using the info_mask_shared_by_all element of the channel spec, acce to the sampling frequency becomes available to in kernel users of the driver. It also shortens and simplifies the code. This particular conversion was made more complicated by the shared library and the fact that a number of the drivers do not actually have support for setting or reading the sampling frequency. The hardware, in those cases investigated supports it. It's just never been implemented. Signed-off-by: Jonathan Cameron Reviewed-by: Hartmut Knaack Acked-by: Lars-Peter Clausen --- drivers/iio/gyro/adis16260.c | 124 +++++++++++------------------ drivers/staging/iio/accel/adis16201_core.c | 14 ++-- drivers/staging/iio/accel/adis16203_core.c | 11 +-- drivers/staging/iio/accel/adis16204_core.c | 12 +-- drivers/staging/iio/accel/adis16209_core.c | 18 +++-- drivers/staging/iio/accel/adis16240_core.c | 12 +-- include/linux/iio/imu/adis.h | 33 ++++---- 7 files changed, 99 insertions(+), 125 deletions(-) (limited to 'include') diff --git a/drivers/iio/gyro/adis16260.c b/drivers/iio/gyro/adis16260.c index 22b6fb80fa1a..75fe0edd3d0f 100644 --- a/drivers/iio/gyro/adis16260.c +++ b/drivers/iio/gyro/adis16260.c @@ -101,65 +101,6 @@ #define ADIS16260_SCAN_TEMP 3 #define ADIS16260_SCAN_ANGL 4 -static ssize_t adis16260_read_frequency(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct adis *adis = iio_priv(indio_dev); - int ret, len = 0; - u16 t; - int sps; - ret = adis_read_reg_16(adis, ADIS16260_SMPL_PRD, &t); - if (ret) - return ret; - - if (spi_get_device_id(adis->spi)->driver_data) /* If an adis16251 */ - sps = (t & ADIS16260_SMPL_PRD_TIME_BASE) ? 8 : 256; - else - sps = (t & ADIS16260_SMPL_PRD_TIME_BASE) ? 66 : 2048; - sps /= (t & ADIS16260_SMPL_PRD_DIV_MASK) + 1; - len = sprintf(buf, "%d\n", sps); - return len; -} - -static ssize_t adis16260_write_frequency(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct adis *adis = iio_priv(indio_dev); - unsigned int val; - int ret; - u8 t; - - ret = kstrtouint(buf, 10, &val); - if (ret) - return ret; - - mutex_lock(&indio_dev->mlock); - if (spi_get_device_id(adis->spi)->driver_data) - t = 256 / val; - else - t = 2048 / val; - - if (t > ADIS16260_SMPL_PRD_DIV_MASK) - t = ADIS16260_SMPL_PRD_DIV_MASK; - else if (t > 0) - t--; - - if (t >= 0x0A) - adis->spi->max_speed_hz = ADIS16260_SPI_SLOW; - else - adis->spi->max_speed_hz = ADIS16260_SPI_FAST; - ret = adis_write_reg_8(adis, ADIS16260_SMPL_PRD, t); - - mutex_unlock(&indio_dev->mlock); - - return ret ? ret : len; -} - /* Power down the device */ static int adis16260_stop_device(struct iio_dev *indio_dev) { @@ -174,18 +115,19 @@ static int adis16260_stop_device(struct iio_dev *indio_dev) return ret; } -static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, - adis16260_read_frequency, - adis16260_write_frequency); - static const struct iio_chan_spec adis16260_channels[] = { ADIS_GYRO_CHAN(X, ADIS16260_GYRO_OUT, ADIS16260_SCAN_GYRO, BIT(IIO_CHAN_INFO_CALIBBIAS) | - BIT(IIO_CHAN_INFO_CALIBSCALE), 14), - ADIS_INCLI_CHAN(X, ADIS16260_ANGL_OUT, ADIS16260_SCAN_ANGL, 0, 14), - ADIS_TEMP_CHAN(ADIS16260_TEMP_OUT, ADIS16260_SCAN_TEMP, 12), - ADIS_SUPPLY_CHAN(ADIS16260_SUPPLY_OUT, ADIS16260_SCAN_SUPPLY, 12), - ADIS_AUX_ADC_CHAN(ADIS16260_AUX_ADC, ADIS16260_SCAN_AUX_ADC, 12), + BIT(IIO_CHAN_INFO_CALIBSCALE), + BIT(IIO_CHAN_INFO_SAMP_FREQ), 14), + ADIS_INCLI_CHAN(X, ADIS16260_ANGL_OUT, ADIS16260_SCAN_ANGL, 0, + BIT(IIO_CHAN_INFO_SAMP_FREQ), 14), + ADIS_TEMP_CHAN(ADIS16260_TEMP_OUT, ADIS16260_SCAN_TEMP, + BIT(IIO_CHAN_INFO_SAMP_FREQ), 12), + ADIS_SUPPLY_CHAN(ADIS16260_SUPPLY_OUT, ADIS16260_SCAN_SUPPLY, + BIT(IIO_CHAN_INFO_SAMP_FREQ), 12), + ADIS_AUX_ADC_CHAN(ADIS16260_AUX_ADC, ADIS16260_SCAN_AUX_ADC, + BIT(IIO_CHAN_INFO_SAMP_FREQ), 12), IIO_CHAN_SOFT_TIMESTAMP(5), }; @@ -258,6 +200,20 @@ static int adis16260_read_raw(struct iio_dev *indio_dev, *val = val16; return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = adis_read_reg_16(adis, ADIS16260_SMPL_PRD, &val16); + if (ret) + return ret; + + if (spi_get_device_id(adis->spi)->driver_data) + /* If an adis16251 */ + *val = (val16 & ADIS16260_SMPL_PRD_TIME_BASE) ? + 8 : 256; + else + *val = (val16 & ADIS16260_SMPL_PRD_TIME_BASE) ? + 66 : 2048; + *val /= (val16 & ADIS16260_SMPL_PRD_DIV_MASK) + 1; + return IIO_VAL_INT; } return -EINVAL; } @@ -269,7 +225,9 @@ static int adis16260_write_raw(struct iio_dev *indio_dev, long mask) { struct adis *adis = iio_priv(indio_dev); + int ret; u8 addr; + u8 t; switch (mask) { case IIO_CHAN_INFO_CALIBBIAS: @@ -284,21 +242,31 @@ static int adis16260_write_raw(struct iio_dev *indio_dev, addr = adis16260_addresses[chan->scan_index][1]; return adis_write_reg_16(adis, addr, val); + case IIO_CHAN_INFO_SAMP_FREQ: + mutex_lock(&indio_dev->mlock); + if (spi_get_device_id(adis->spi)->driver_data) + t = 256 / val; + else + t = 2048 / val; + + if (t > ADIS16260_SMPL_PRD_DIV_MASK) + t = ADIS16260_SMPL_PRD_DIV_MASK; + else if (t > 0) + t--; + + if (t >= 0x0A) + adis->spi->max_speed_hz = ADIS16260_SPI_SLOW; + else + adis->spi->max_speed_hz = ADIS16260_SPI_FAST; + ret = adis_write_reg_8(adis, ADIS16260_SMPL_PRD, t); + + mutex_unlock(&indio_dev->mlock); + return ret; } return -EINVAL; } -static struct attribute *adis16260_attributes[] = { - &iio_dev_attr_sampling_frequency.dev_attr.attr, - NULL -}; - -static const struct attribute_group adis16260_attribute_group = { - .attrs = adis16260_attributes, -}; - static const struct iio_info adis16260_info = { - .attrs = &adis16260_attribute_group, .read_raw = &adis16260_read_raw, .write_raw = &adis16260_write_raw, .update_scan_mode = adis_update_scan_mode, diff --git a/drivers/staging/iio/accel/adis16201_core.c b/drivers/staging/iio/accel/adis16201_core.c index 2105576fa77c..50ba1fa7f98a 100644 --- a/drivers/staging/iio/accel/adis16201_core.c +++ b/drivers/staging/iio/accel/adis16201_core.c @@ -131,17 +131,17 @@ static int adis16201_write_raw(struct iio_dev *indio_dev, } static const struct iio_chan_spec adis16201_channels[] = { - ADIS_SUPPLY_CHAN(ADIS16201_SUPPLY_OUT, ADIS16201_SCAN_SUPPLY, 12), - ADIS_TEMP_CHAN(ADIS16201_TEMP_OUT, ADIS16201_SCAN_TEMP, 12), + ADIS_SUPPLY_CHAN(ADIS16201_SUPPLY_OUT, ADIS16201_SCAN_SUPPLY, 0, 12), + ADIS_TEMP_CHAN(ADIS16201_TEMP_OUT, ADIS16201_SCAN_TEMP, 0, 12), ADIS_ACCEL_CHAN(X, ADIS16201_XACCL_OUT, ADIS16201_SCAN_ACC_X, - BIT(IIO_CHAN_INFO_CALIBBIAS), 14), + BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), ADIS_ACCEL_CHAN(Y, ADIS16201_YACCL_OUT, ADIS16201_SCAN_ACC_Y, - BIT(IIO_CHAN_INFO_CALIBBIAS), 14), - ADIS_AUX_ADC_CHAN(ADIS16201_AUX_ADC, ADIS16201_SCAN_AUX_ADC, 12), + BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), + ADIS_AUX_ADC_CHAN(ADIS16201_AUX_ADC, ADIS16201_SCAN_AUX_ADC, 0, 12), ADIS_INCLI_CHAN(X, ADIS16201_XINCL_OUT, ADIS16201_SCAN_INCLI_X, - BIT(IIO_CHAN_INFO_CALIBBIAS), 14), + BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), ADIS_INCLI_CHAN(X, ADIS16201_YINCL_OUT, ADIS16201_SCAN_INCLI_Y, - BIT(IIO_CHAN_INFO_CALIBBIAS), 14), + BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), IIO_CHAN_SOFT_TIMESTAMP(7) }; diff --git a/drivers/staging/iio/accel/adis16203_core.c b/drivers/staging/iio/accel/adis16203_core.c index 409a28ed9043..f472137b0069 100644 --- a/drivers/staging/iio/accel/adis16203_core.c +++ b/drivers/staging/iio/accel/adis16203_core.c @@ -99,13 +99,14 @@ static int adis16203_read_raw(struct iio_dev *indio_dev, } static const struct iio_chan_spec adis16203_channels[] = { - ADIS_SUPPLY_CHAN(ADIS16203_SUPPLY_OUT, ADIS16203_SCAN_SUPPLY, 12), - ADIS_AUX_ADC_CHAN(ADIS16203_AUX_ADC, ADIS16203_SCAN_AUX_ADC, 12), + ADIS_SUPPLY_CHAN(ADIS16203_SUPPLY_OUT, ADIS16203_SCAN_SUPPLY, 0, 12), + ADIS_AUX_ADC_CHAN(ADIS16203_AUX_ADC, ADIS16203_SCAN_AUX_ADC, 0, 12), ADIS_INCLI_CHAN(X, ADIS16203_XINCL_OUT, ADIS16203_SCAN_INCLI_X, - BIT(IIO_CHAN_INFO_CALIBBIAS), 14), + BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), /* Fixme: Not what it appears to be - see data sheet */ - ADIS_INCLI_CHAN(Y, ADIS16203_YINCL_OUT, ADIS16203_SCAN_INCLI_Y, 0, 14), - ADIS_TEMP_CHAN(ADIS16203_TEMP_OUT, ADIS16203_SCAN_TEMP, 12), + ADIS_INCLI_CHAN(Y, ADIS16203_YINCL_OUT, ADIS16203_SCAN_INCLI_Y, + 0, 0, 14), + ADIS_TEMP_CHAN(ADIS16203_TEMP_OUT, ADIS16203_SCAN_TEMP, 0, 12), IIO_CHAN_SOFT_TIMESTAMP(5), }; diff --git a/drivers/staging/iio/accel/adis16204_core.c b/drivers/staging/iio/accel/adis16204_core.c index b8ea76857cd6..19eaebc77d7a 100644 --- a/drivers/staging/iio/accel/adis16204_core.c +++ b/drivers/staging/iio/accel/adis16204_core.c @@ -136,15 +136,15 @@ static int adis16204_write_raw(struct iio_dev *indio_dev, } static const struct iio_chan_spec adis16204_channels[] = { - ADIS_SUPPLY_CHAN(ADIS16204_SUPPLY_OUT, ADIS16204_SCAN_SUPPLY, 12), - ADIS_AUX_ADC_CHAN(ADIS16204_AUX_ADC, ADIS16204_SCAN_AUX_ADC, 12), - ADIS_TEMP_CHAN(ADIS16204_TEMP_OUT, ADIS16204_SCAN_TEMP, 12), + ADIS_SUPPLY_CHAN(ADIS16204_SUPPLY_OUT, ADIS16204_SCAN_SUPPLY, 0, 12), + ADIS_AUX_ADC_CHAN(ADIS16204_AUX_ADC, ADIS16204_SCAN_AUX_ADC, 0, 12), + ADIS_TEMP_CHAN(ADIS16204_TEMP_OUT, ADIS16204_SCAN_TEMP, 0, 12), ADIS_ACCEL_CHAN(X, ADIS16204_XACCL_OUT, ADIS16204_SCAN_ACC_X, - BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK), 14), + BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK), 0, 14), ADIS_ACCEL_CHAN(Y, ADIS16204_YACCL_OUT, ADIS16204_SCAN_ACC_Y, - BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK), 14), + BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK), 0, 14), ADIS_ACCEL_CHAN(ROOT_SUM_SQUARED_X_Y, ADIS16204_XY_RSS_OUT, - ADIS16204_SCAN_ACC_XY, BIT(IIO_CHAN_INFO_PEAK), 14), + ADIS16204_SCAN_ACC_XY, BIT(IIO_CHAN_INFO_PEAK), 0, 14), IIO_CHAN_SOFT_TIMESTAMP(5), }; diff --git a/drivers/staging/iio/accel/adis16209_core.c b/drivers/staging/iio/accel/adis16209_core.c index 4492e51d8886..374dc6edbcf5 100644 --- a/drivers/staging/iio/accel/adis16209_core.c +++ b/drivers/staging/iio/accel/adis16209_core.c @@ -130,16 +130,18 @@ static int adis16209_read_raw(struct iio_dev *indio_dev, } static const struct iio_chan_spec adis16209_channels[] = { - ADIS_SUPPLY_CHAN(ADIS16209_SUPPLY_OUT, ADIS16209_SCAN_SUPPLY, 14), - ADIS_TEMP_CHAN(ADIS16209_TEMP_OUT, ADIS16209_SCAN_TEMP, 12), + ADIS_SUPPLY_CHAN(ADIS16209_SUPPLY_OUT, ADIS16209_SCAN_SUPPLY, 0, 14), + ADIS_TEMP_CHAN(ADIS16209_TEMP_OUT, ADIS16209_SCAN_TEMP, 0, 12), ADIS_ACCEL_CHAN(X, ADIS16209_XACCL_OUT, ADIS16209_SCAN_ACC_X, - BIT(IIO_CHAN_INFO_CALIBBIAS), 14), + BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), ADIS_ACCEL_CHAN(Y, ADIS16209_YACCL_OUT, ADIS16209_SCAN_ACC_Y, - BIT(IIO_CHAN_INFO_CALIBBIAS), 14), - ADIS_AUX_ADC_CHAN(ADIS16209_AUX_ADC, ADIS16209_SCAN_AUX_ADC, 12), - ADIS_INCLI_CHAN(X, ADIS16209_XINCL_OUT, ADIS16209_SCAN_INCLI_X, 0, 14), - ADIS_INCLI_CHAN(Y, ADIS16209_YINCL_OUT, ADIS16209_SCAN_INCLI_Y, 0, 14), - ADIS_ROT_CHAN(X, ADIS16209_ROT_OUT, ADIS16209_SCAN_ROT, 0, 14), + BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), + ADIS_AUX_ADC_CHAN(ADIS16209_AUX_ADC, ADIS16209_SCAN_AUX_ADC, 0, 12), + ADIS_INCLI_CHAN(X, ADIS16209_XINCL_OUT, ADIS16209_SCAN_INCLI_X, + 0, 0, 14), + ADIS_INCLI_CHAN(Y, ADIS16209_YINCL_OUT, ADIS16209_SCAN_INCLI_Y, + 0, 0, 14), + ADIS_ROT_CHAN(X, ADIS16209_ROT_OUT, ADIS16209_SCAN_ROT, 0, 0, 14), IIO_CHAN_SOFT_TIMESTAMP(8) }; diff --git a/drivers/staging/iio/accel/adis16240_core.c b/drivers/staging/iio/accel/adis16240_core.c index 3a303a03d028..74ace2a8769d 100644 --- a/drivers/staging/iio/accel/adis16240_core.c +++ b/drivers/staging/iio/accel/adis16240_core.c @@ -173,15 +173,15 @@ static int adis16240_write_raw(struct iio_dev *indio_dev, } static const struct iio_chan_spec adis16240_channels[] = { - ADIS_SUPPLY_CHAN(ADIS16240_SUPPLY_OUT, ADIS16240_SCAN_SUPPLY, 10), - ADIS_AUX_ADC_CHAN(ADIS16240_AUX_ADC, ADIS16240_SCAN_AUX_ADC, 10), + ADIS_SUPPLY_CHAN(ADIS16240_SUPPLY_OUT, ADIS16240_SCAN_SUPPLY, 0, 10), + ADIS_AUX_ADC_CHAN(ADIS16240_AUX_ADC, ADIS16240_SCAN_AUX_ADC, 0, 10), ADIS_ACCEL_CHAN(X, ADIS16240_XACCL_OUT, ADIS16240_SCAN_ACC_X, - BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK), 10), + BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK), 0, 10), ADIS_ACCEL_CHAN(Y, ADIS16240_YACCL_OUT, ADIS16240_SCAN_ACC_Y, - BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK), 10), + BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK), 0, 10), ADIS_ACCEL_CHAN(Z, ADIS16240_ZACCL_OUT, ADIS16240_SCAN_ACC_Z, - BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK), 10), - ADIS_TEMP_CHAN(ADIS16240_TEMP_OUT, ADIS16240_SCAN_TEMP, 10), + BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK), 0, 10), + ADIS_TEMP_CHAN(ADIS16240_TEMP_OUT, ADIS16240_SCAN_TEMP, 0, 10), IIO_CHAN_SOFT_TIMESTAMP(6) }; diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h index b665dc7f017b..fa2d01ef8f55 100644 --- a/include/linux/iio/imu/adis.h +++ b/include/linux/iio/imu/adis.h @@ -157,13 +157,14 @@ int adis_single_conversion(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, unsigned int error_mask, int *val); -#define ADIS_VOLTAGE_CHAN(addr, si, chan, name, bits) { \ +#define ADIS_VOLTAGE_CHAN(addr, si, chan, name, info_all, bits) { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ .channel = (chan), \ .extend_name = name, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = info_all, \ .address = (addr), \ .scan_index = (si), \ .scan_type = { \ @@ -174,19 +175,20 @@ int adis_single_conversion(struct iio_dev *indio_dev, }, \ } -#define ADIS_SUPPLY_CHAN(addr, si, bits) \ - ADIS_VOLTAGE_CHAN(addr, si, 0, "supply", bits) +#define ADIS_SUPPLY_CHAN(addr, si, info_all, bits) \ + ADIS_VOLTAGE_CHAN(addr, si, 0, "supply", info_all, bits) -#define ADIS_AUX_ADC_CHAN(addr, si, bits) \ - ADIS_VOLTAGE_CHAN(addr, si, 1, NULL, bits) +#define ADIS_AUX_ADC_CHAN(addr, si, info_all, bits) \ + ADIS_VOLTAGE_CHAN(addr, si, 1, NULL, info_all, bits) -#define ADIS_TEMP_CHAN(addr, si, bits) { \ +#define ADIS_TEMP_CHAN(addr, si, info_all, bits) { \ .type = IIO_TEMP, \ .indexed = 1, \ .channel = 0, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_SCALE) | \ BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_all = info_all, \ .address = (addr), \ .scan_index = (si), \ .scan_type = { \ @@ -197,13 +199,14 @@ int adis_single_conversion(struct iio_dev *indio_dev, }, \ } -#define ADIS_MOD_CHAN(_type, mod, addr, si, info_sep, bits) { \ +#define ADIS_MOD_CHAN(_type, mod, addr, si, info_sep, info_all, bits) { \ .type = (_type), \ .modified = 1, \ .channel2 = IIO_MOD_ ## mod, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ info_sep, \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = info_all, \ .address = (addr), \ .scan_index = (si), \ .scan_type = { \ @@ -214,17 +217,17 @@ int adis_single_conversion(struct iio_dev *indio_dev, }, \ } -#define ADIS_ACCEL_CHAN(mod, addr, si, info_sep, bits) \ - ADIS_MOD_CHAN(IIO_ACCEL, mod, addr, si, info_sep, bits) +#define ADIS_ACCEL_CHAN(mod, addr, si, info_sep, info_all, bits) \ + ADIS_MOD_CHAN(IIO_ACCEL, mod, addr, si, info_sep, info_all, bits) -#define ADIS_GYRO_CHAN(mod, addr, si, info_sep, bits) \ - ADIS_MOD_CHAN(IIO_ANGL_VEL, mod, addr, si, info_sep, bits) +#define ADIS_GYRO_CHAN(mod, addr, si, info_sep, info_all, bits) \ + ADIS_MOD_CHAN(IIO_ANGL_VEL, mod, addr, si, info_sep, info_all, bits) -#define ADIS_INCLI_CHAN(mod, addr, si, info_sep, bits) \ - ADIS_MOD_CHAN(IIO_INCLI, mod, addr, si, info_sep, bits) +#define ADIS_INCLI_CHAN(mod, addr, si, info_sep, info_all, bits) \ + ADIS_MOD_CHAN(IIO_INCLI, mod, addr, si, info_sep, info_all, bits) -#define ADIS_ROT_CHAN(mod, addr, si, info_sep, bits) \ - ADIS_MOD_CHAN(IIO_ROT, mod, addr, si, info_sep, bits) +#define ADIS_ROT_CHAN(mod, addr, si, info_sep, info_all, bits) \ + ADIS_MOD_CHAN(IIO_ROT, mod, addr, si, info_sep, info_all, bits) #ifdef CONFIG_IIO_ADIS_LIB_BUFFER -- cgit v1.2.3 From f1b1dc845cb1418b2b0de35491b0da87498ea6a8 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 7 Jul 2014 14:07:38 +0200 Subject: ACPI / PNP: do ACPI binding directly PNPACPI uses acpi_bus_type to do ACPI binding for the PNPACPI devices. This is overkill because PNPACPI code already knows which ACPI device object to bind during PNPACPI device enumeration. This patch removes acpi_pnp_bus and does the binding by invoking acpi_bind_one() directly after device enumerated. This also fixes a bug in the previous code that some PNPACPI devices failed to be bound because 1. the ACPI device _HID is not pnpid, e.g. "MSFT0001", but its _CID is, e.g. "PNP0303", thus ACPI _CID is used as the pnp device device id. 2. device is bound only if the pnp device id matches the ACPI device _HID. Tested-by: Prigent Christophe Signed-off-by: Zhang Rui Signed-off-by: Rafael J. Wysocki --- drivers/acpi/internal.h | 2 -- drivers/pnp/pnpacpi/core.c | 41 +++-------------------------------------- include/acpi/acpi_bus.h | 2 ++ 3 files changed, 5 insertions(+), 40 deletions(-) (limited to 'include') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 7de5b603f272..151f3e76d47a 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -84,8 +84,6 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, int type, unsigned long long sta); void acpi_device_add_finalize(struct acpi_device *device); void acpi_free_pnp_ids(struct acpi_device_pnp *pnp); -int acpi_bind_one(struct device *dev, struct acpi_device *adev); -int acpi_unbind_one(struct device *dev); bool acpi_device_is_present(struct acpi_device *adev); bool acpi_device_is_battery(struct acpi_device *adev); diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c index b81448b2c75d..3bebedaee8e1 100644 --- a/drivers/pnp/pnpacpi/core.c +++ b/drivers/pnp/pnpacpi/core.c @@ -295,9 +295,11 @@ static int __init pnpacpi_add_device(struct acpi_device *device) return error; } + error = acpi_bind_one(&dev->dev, device); + num++; - return 0; + return error; } static acpi_status __init pnpacpi_add_device_handler(acpi_handle handle, @@ -313,41 +315,6 @@ static acpi_status __init pnpacpi_add_device_handler(acpi_handle handle, return AE_OK; } -static int __init acpi_pnp_match(struct device *dev, void *_pnp) -{ - struct acpi_device *acpi = to_acpi_device(dev); - struct pnp_dev *pnp = _pnp; - - /* true means it matched */ - return !acpi->physical_node_count - && compare_pnp_id(pnp->id, acpi_device_hid(acpi)); -} - -static struct acpi_device * __init acpi_pnp_find_companion(struct device *dev) -{ - dev = bus_find_device(&acpi_bus_type, NULL, to_pnp_dev(dev), - acpi_pnp_match); - if (!dev) - return NULL; - - put_device(dev); - return to_acpi_device(dev); -} - -/* complete initialization of a PNPACPI device includes having - * pnpdev->dev.archdata.acpi_handle point to its ACPI sibling. - */ -static bool acpi_pnp_bus_match(struct device *dev) -{ - return dev->bus == &pnp_bus_type; -} - -static struct acpi_bus_type __initdata acpi_pnp_bus = { - .name = "PNP", - .match = acpi_pnp_bus_match, - .find_companion = acpi_pnp_find_companion, -}; - int pnpacpi_disabled __initdata; static int __init pnpacpi_init(void) { @@ -357,10 +324,8 @@ static int __init pnpacpi_init(void) } printk(KERN_INFO "pnp: PnP ACPI init\n"); pnp_register_protocol(&pnpacpi_protocol); - register_acpi_bus_type(&acpi_pnp_bus); acpi_get_devices(NULL, pnpacpi_add_device_handler, NULL, NULL); printk(KERN_INFO "pnp: PnP ACPI: found %d devices\n", num); - unregister_acpi_bus_type(&acpi_pnp_bus); pnp_platform_devices = 1; return 0; } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index b5714580801a..4bcbb941bc09 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -487,6 +487,8 @@ struct acpi_bus_type { }; int register_acpi_bus_type(struct acpi_bus_type *); int unregister_acpi_bus_type(struct acpi_bus_type *); +int acpi_bind_one(struct device *dev, struct acpi_device *adev); +int acpi_unbind_one(struct device *dev); struct acpi_pci_root { struct acpi_device * device; -- cgit v1.2.3 From 75f353b61342b5847c7f6d8499fd6301dce09845 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Tue, 24 Jun 2014 16:13:47 +0100 Subject: of/platform: Fix of_platform_device_destroy iteration of devices of_platform_destroy does not work properly, since the tree population test was iterating on all devices having as its parent the given platform device. The check was intended to check whether any other platform or amba devices created by of_platform_populate were still populated, but instead checked for every kind of device. This is wrong, since platform devices typically create a subsystem regular device and set themselves as parents. Instead, go ahead and call the unregister functions for any devices created with of_platform_populate. The driver core will take care of unbinding drivers, and drivers are responsible for getting rid of any child devices that weren't created by of_platform_populate. Signed-off-by: Grant Likely Signed-off-by: Pantelis Antoniou --- drivers/of/platform.c | 32 +++++++++----------------------- include/linux/of.h | 1 + include/linux/of_platform.h | 7 ++----- 3 files changed, 12 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 500436f9be7f..0197725e033a 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -422,6 +422,7 @@ static int of_platform_bus_create(struct device_node *bus, break; } } + of_node_set_flag(bus, OF_POPULATED_BUS); return rc; } @@ -508,19 +509,13 @@ EXPORT_SYMBOL_GPL(of_platform_populate); static int of_platform_device_destroy(struct device *dev, void *data) { - bool *children_left = data; - /* Do not touch devices not populated from the device tree */ - if (!dev->of_node || !of_node_check_flag(dev->of_node, OF_POPULATED)) { - *children_left = true; + if (!dev->of_node || !of_node_check_flag(dev->of_node, OF_POPULATED)) return 0; - } - /* Recurse, but don't touch this device if it has any children left */ - if (of_platform_depopulate(dev) != 0) { - *children_left = true; - return 0; - } + /* Recurse for any nodes that were treated as busses */ + if (of_node_check_flag(dev->of_node, OF_POPULATED_BUS)) + device_for_each_child(dev, NULL, of_platform_device_destroy); if (dev->bus == &platform_bus_type) platform_device_unregister(to_platform_device(dev)); @@ -528,19 +523,15 @@ static int of_platform_device_destroy(struct device *dev, void *data) else if (dev->bus == &amba_bustype) amba_device_unregister(to_amba_device(dev)); #endif - else { - *children_left = true; - return 0; - } of_node_clear_flag(dev->of_node, OF_POPULATED); - + of_node_clear_flag(dev->of_node, OF_POPULATED_BUS); return 0; } /** * of_platform_depopulate() - Remove devices populated from device tree - * @parent: device which childred will be removed + * @parent: device which children will be removed * * Complementary to of_platform_populate(), this function removes children * of the given device (and, recurrently, their children) that have been @@ -550,14 +541,9 @@ static int of_platform_device_destroy(struct device *dev, void *data) * Returns 0 when all children devices have been removed or * -EBUSY when some children remained. */ -int of_platform_depopulate(struct device *parent) +void of_platform_depopulate(struct device *parent) { - bool children_left = false; - - device_for_each_child(parent, &children_left, - of_platform_device_destroy); - - return children_left ? -EBUSY : 0; + device_for_each_child(parent, NULL, of_platform_device_destroy); } EXPORT_SYMBOL_GPL(of_platform_depopulate); diff --git a/include/linux/of.h b/include/linux/of.h index 196b34c1ef4e..abf829a1f150 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -204,6 +204,7 @@ static inline unsigned long of_read_ulong(const __be32 *cell, int size) #define OF_DYNAMIC 1 /* node and properties were allocated via kmalloc */ #define OF_DETACHED 2 /* node has been detached from the device tree */ #define OF_POPULATED 3 /* device already created for the node */ +#define OF_POPULATED_BUS 4 /* of_platform_populate recursed to children of this node */ #define OF_IS_DYNAMIC(x) test_bit(OF_DYNAMIC, &x->_flags) #define OF_MARK_DYNAMIC(x) set_bit(OF_DYNAMIC, &x->_flags) diff --git a/include/linux/of_platform.h b/include/linux/of_platform.h index d96e1badbee0..c2b0627a2317 100644 --- a/include/linux/of_platform.h +++ b/include/linux/of_platform.h @@ -72,7 +72,7 @@ extern int of_platform_populate(struct device_node *root, const struct of_device_id *matches, const struct of_dev_auxdata *lookup, struct device *parent); -extern int of_platform_depopulate(struct device *parent); +extern void of_platform_depopulate(struct device *parent); #else static inline int of_platform_populate(struct device_node *root, const struct of_device_id *matches, @@ -81,10 +81,7 @@ static inline int of_platform_populate(struct device_node *root, { return -ENODEV; } -static inline int of_platform_depopulate(struct device *parent) -{ - return -ENODEV; -} +static inline void of_platform_depopulate(struct device *parent) { } #endif #endif /* _LINUX_OF_PLATFORM_H */ -- cgit v1.2.3 From d93375a82da10cb023afc945fa18471bf3c41704 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 7 Jul 2014 15:02:27 +0300 Subject: Bluetooth: Remove auth_type parameter from hci_connect_le() The auth_type value which gets assigned to hci_conn->auth_type is something that's only used for BR/EDR connections and is of no value for LE connections. It makes therefore little sense to pass it to the hci_connect_le() function. This patch removes the parameter from the function. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 3 +-- net/bluetooth/hci_conn.c | 5 +---- net/bluetooth/hci_event.c | 2 +- net/bluetooth/l2cap_core.c | 6 ++---- net/bluetooth/mgmt.c | 3 +-- 5 files changed, 6 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 06039aae3010..8752ac674db1 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -704,8 +704,7 @@ void hci_chan_list_flush(struct hci_conn *conn); struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle); struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, - u8 dst_type, u8 sec_level, u8 auth_type, - u16 conn_timeout); + u8 dst_type, u8 sec_level, u16 conn_timeout); struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst, u8 sec_level, u8 auth_type); struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst, diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index faa032fcdaee..9323044f01cd 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -700,8 +700,7 @@ static void hci_req_directed_advertising(struct hci_request *req, } struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, - u8 dst_type, u8 sec_level, u8 auth_type, - u16 conn_timeout) + u8 dst_type, u8 sec_level, u16 conn_timeout) { struct hci_conn_params *params; struct hci_conn *conn; @@ -721,7 +720,6 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst); if (conn) { conn->pending_sec_level = sec_level; - conn->auth_type = auth_type; goto done; } @@ -758,7 +756,6 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, conn->dst_type = dst_type; conn->sec_level = BT_SECURITY_LOW; conn->pending_sec_level = sec_level; - conn->auth_type = auth_type; conn->conn_timeout = conn_timeout; hci_req_init(&req, hdev); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 84f4659f136e..ad39d9ad6fbc 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4254,7 +4254,7 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, connect: conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW, - HCI_AT_NO_BONDING, HCI_LE_AUTOCONN_TIMEOUT); + HCI_LE_AUTOCONN_TIMEOUT); if (!IS_ERR(conn)) return; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index a219276d9f92..3daab4588522 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -7048,7 +7048,6 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, struct l2cap_conn *conn; struct hci_conn *hcon; struct hci_dev *hdev; - __u8 auth_type; int err; BT_DBG("%pMR -> %pMR (type %u) psm 0x%2.2x", &chan->src, dst, @@ -7124,8 +7123,6 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, chan->psm = psm; chan->dcid = cid; - auth_type = l2cap_get_auth_type(chan); - if (bdaddr_type_is_le(dst_type)) { /* Convert from L2CAP channel address type to HCI address type */ @@ -7135,8 +7132,9 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, dst_type = ADDR_LE_DEV_RANDOM; hcon = hci_connect_le(hdev, dst, dst_type, chan->sec_level, - auth_type, HCI_LE_CONN_TIMEOUT); + HCI_LE_CONN_TIMEOUT); } else { + u8 auth_type = l2cap_get_auth_type(chan); hcon = hci_connect_acl(hdev, dst, chan->sec_level, auth_type); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index ef675acbcfce..e7047de1ba11 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3118,8 +3118,7 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, hci_conn_params_add(hdev, &cp->addr.bdaddr, addr_type); conn = hci_connect_le(hdev, &cp->addr.bdaddr, addr_type, - sec_level, auth_type, - HCI_LE_CONN_TIMEOUT); + sec_level, HCI_LE_CONN_TIMEOUT); } if (IS_ERR(conn)) { -- cgit v1.2.3 From cdd6275e510bd86c44d3fc85a78306f514bbac9a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 7 Jul 2014 15:02:28 +0300 Subject: Bluetooth: Pass desired connection role to hci_connect_le() If we have both LE scanning and advertising simultaneously enabled we need a way to tell hci_connect_le() in which role to initiate a connection. This patch adds a new parameter to the function to give it the necessary information. For auto-connect and mgmt_pair_device we always use master role, whereas for L2CAP users (in practice sockets) we use slave role whenever HCI_ADVERTISING is set and master role otherwise. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 3 ++- net/bluetooth/hci_conn.c | 6 ++++-- net/bluetooth/hci_event.c | 3 ++- net/bluetooth/l2cap_core.c | 6 +++++- net/bluetooth/mgmt.c | 3 ++- 5 files changed, 15 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 8752ac674db1..5701d15779dd 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -704,7 +704,8 @@ void hci_chan_list_flush(struct hci_conn *conn); struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle); struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, - u8 dst_type, u8 sec_level, u16 conn_timeout); + u8 dst_type, u8 sec_level, u16 conn_timeout, + bool master); struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst, u8 sec_level, u8 auth_type); struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst, diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 9323044f01cd..16fd55da9c1d 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -700,7 +700,8 @@ static void hci_req_directed_advertising(struct hci_request *req, } struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, - u8 dst_type, u8 sec_level, u16 conn_timeout) + u8 dst_type, u8 sec_level, u16 conn_timeout, + bool master) { struct hci_conn_params *params; struct hci_conn *conn; @@ -760,7 +761,8 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, hci_req_init(&req, hdev); - if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) { + /* If requested to connect as slave use directed advertising */ + if (!master) { hci_req_directed_advertising(&req, conn); goto create_conn; } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index ad39d9ad6fbc..a6816498b0b9 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4253,8 +4253,9 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, return; connect: + /* Request connection in master = true role */ conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW, - HCI_LE_AUTOCONN_TIMEOUT); + HCI_LE_AUTOCONN_TIMEOUT, true); if (!IS_ERR(conn)) return; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 3daab4588522..bf379a379fa0 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -7124,6 +7124,8 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, chan->dcid = cid; if (bdaddr_type_is_le(dst_type)) { + bool master; + /* Convert from L2CAP channel address type to HCI address type */ if (dst_type == BDADDR_LE_PUBLIC) @@ -7131,8 +7133,10 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, else dst_type = ADDR_LE_DEV_RANDOM; + master = !test_bit(HCI_ADVERTISING, &hdev->dev_flags); + hcon = hci_connect_le(hdev, dst, dst_type, chan->sec_level, - HCI_LE_CONN_TIMEOUT); + HCI_LE_CONN_TIMEOUT, master); } else { u8 auth_type = l2cap_get_auth_type(chan); hcon = hci_connect_acl(hdev, dst, chan->sec_level, auth_type); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index e7047de1ba11..b391e2fef4b6 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3117,8 +3117,9 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, */ hci_conn_params_add(hdev, &cp->addr.bdaddr, addr_type); + /* Request a connection with master = true role */ conn = hci_connect_le(hdev, &cp->addr.bdaddr, addr_type, - sec_level, HCI_LE_CONN_TIMEOUT); + sec_level, HCI_LE_CONN_TIMEOUT, true); } if (IS_ERR(conn)) { -- cgit v1.2.3 From 1e4c0d7c9a2b44e18fe9e93712672741f70e36da Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 7 Jul 2014 13:16:54 +0100 Subject: ASoC: add SOC_VALUE_ENUM_EXT Adds an equivalent of SOC_ENUM_EXT for value enums Strictly speaking SOC_ENUM_EXT can also be used to define a value enum since the only difference is the get and set functions. But this doesn't look good in code because it is inconsistent with the normal control definitions. Adding a specific SOC_VALUE_ENUM_EXT is better for code clarity. Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown --- include/sound/soc.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index ed9e2d7e5fdc..5d812cfe9427 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -248,6 +248,8 @@ .info = snd_soc_info_enum_double, \ .get = xhandler_get, .put = xhandler_put, \ .private_value = (unsigned long)&xenum } +#define SOC_VALUE_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put) \ + SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put) #define SND_SOC_BYTES(xname, xbase, xregs) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ -- cgit v1.2.3 From 022ee6c558fc933679e151f00f84332974147fa2 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 26 Jun 2014 12:09:05 +0200 Subject: efi/x86: Move UEFI Runtime Services wrappers to generic code In order for other archs (such as arm64) to be able to reuse the virtual mode function call wrappers, move them to drivers/firmware/efi/runtime-wrappers.c. Signed-off-by: Ard Biesheuvel Signed-off-by: Matt Fleming --- arch/x86/Kconfig | 1 + arch/x86/platform/efi/efi.c | 144 +--------------------------- drivers/firmware/efi/Kconfig | 3 + drivers/firmware/efi/Makefile | 1 + drivers/firmware/efi/runtime-wrappers.c | 161 ++++++++++++++++++++++++++++++++ include/linux/efi.h | 2 + 6 files changed, 170 insertions(+), 142 deletions(-) create mode 100644 drivers/firmware/efi/runtime-wrappers.c (limited to 'include') diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index fcefdda5136d..801ed36c2e49 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1522,6 +1522,7 @@ config EFI bool "EFI runtime service support" depends on ACPI select UCS2_STRING + select EFI_RUNTIME_WRAPPERS ---help--- This enables the kernel to use EFI runtime services that are available (such as the EFI variable services). diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index f8524434bf65..135812b593cc 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -88,130 +88,6 @@ static int __init setup_add_efi_memmap(char *arg) } early_param("add_efi_memmap", setup_add_efi_memmap); -static efi_status_t virt_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); - status = efi_call_virt(get_time, tm, tc); - spin_unlock_irqrestore(&rtc_lock, flags); - return status; -} - -static efi_status_t virt_efi_set_time(efi_time_t *tm) -{ - unsigned long flags; - efi_status_t status; - - spin_lock_irqsave(&rtc_lock, flags); - status = efi_call_virt(set_time, tm); - spin_unlock_irqrestore(&rtc_lock, flags); - return status; -} - -static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled, - efi_bool_t *pending, - efi_time_t *tm) -{ - unsigned long flags; - efi_status_t status; - - spin_lock_irqsave(&rtc_lock, flags); - status = efi_call_virt(get_wakeup_time, enabled, pending, tm); - spin_unlock_irqrestore(&rtc_lock, flags); - return status; -} - -static efi_status_t virt_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm) -{ - unsigned long flags; - efi_status_t status; - - spin_lock_irqsave(&rtc_lock, flags); - status = efi_call_virt(set_wakeup_time, enabled, tm); - spin_unlock_irqrestore(&rtc_lock, flags); - return status; -} - -static efi_status_t virt_efi_get_variable(efi_char16_t *name, - efi_guid_t *vendor, - u32 *attr, - unsigned long *data_size, - void *data) -{ - return efi_call_virt(get_variable, - name, vendor, attr, - data_size, data); -} - -static efi_status_t virt_efi_get_next_variable(unsigned long *name_size, - efi_char16_t *name, - efi_guid_t *vendor) -{ - return efi_call_virt(get_next_variable, - name_size, name, vendor); -} - -static efi_status_t virt_efi_set_variable(efi_char16_t *name, - efi_guid_t *vendor, - u32 attr, - unsigned long data_size, - void *data) -{ - return efi_call_virt(set_variable, - name, vendor, attr, - data_size, data); -} - -static efi_status_t virt_efi_query_variable_info(u32 attr, - u64 *storage_space, - u64 *remaining_space, - u64 *max_variable_size) -{ - if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) - return EFI_UNSUPPORTED; - - return efi_call_virt(query_variable_info, attr, storage_space, - remaining_space, max_variable_size); -} - -static efi_status_t virt_efi_get_next_high_mono_count(u32 *count) -{ - return efi_call_virt(get_next_high_mono_count, count); -} - -static void virt_efi_reset_system(int reset_type, - efi_status_t status, - unsigned long data_size, - efi_char16_t *data) -{ - __efi_call_virt(reset_system, reset_type, status, - data_size, data); -} - -static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules, - unsigned long count, - unsigned long sg_list) -{ - if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) - return EFI_UNSUPPORTED; - - return efi_call_virt(update_capsule, capsules, count, sg_list); -} - -static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules, - unsigned long count, - u64 *max_size, - int *reset_type) -{ - if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) - return EFI_UNSUPPORTED; - - return efi_call_virt(query_capsule_caps, capsules, count, max_size, - reset_type); -} - static efi_status_t __init phys_efi_set_virtual_address_map( unsigned long memory_map_size, unsigned long descriptor_size, @@ -721,22 +597,6 @@ void __init old_map_region(efi_memory_desc_t *md) (unsigned long long)md->phys_addr); } -static void native_runtime_setup(void) -{ - efi.get_time = virt_efi_get_time; - efi.set_time = virt_efi_set_time; - efi.get_wakeup_time = virt_efi_get_wakeup_time; - efi.set_wakeup_time = virt_efi_set_wakeup_time; - efi.get_variable = virt_efi_get_variable; - efi.get_next_variable = virt_efi_get_next_variable; - efi.set_variable = virt_efi_set_variable; - efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count; - efi.reset_system = virt_efi_reset_system; - efi.query_variable_info = virt_efi_query_variable_info; - efi.update_capsule = virt_efi_update_capsule; - efi.query_capsule_caps = virt_efi_query_capsule_caps; -} - /* Merge contiguous regions of the same type and attribute */ static void __init efi_merge_regions(void) { @@ -923,7 +783,7 @@ static void __init kexec_enter_virtual_mode(void) */ efi.runtime_version = efi_systab.hdr.revision; - native_runtime_setup(); + efi_native_runtime_setup(); efi.set_virtual_address_map = NULL; @@ -1012,7 +872,7 @@ static void __init __efi_enter_virtual_mode(void) efi.runtime_version = efi_systab.hdr.revision; if (efi_is_native()) - native_runtime_setup(); + efi_native_runtime_setup(); else efi_thunk_runtime_setup(); diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index d420ae2d3413..588dc47e7075 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -54,6 +54,9 @@ config EFI_PARAMS_FROM_FDT the EFI runtime support gets system table address, memory map address, and other parameters from the device tree. +config EFI_RUNTIME_WRAPPERS + bool + endmenu config UEFI_CPER diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index 9553496b0f43..e1096539eedb 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_EFI_VARS) += efivars.o obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o obj-$(CONFIG_UEFI_CPER) += cper.o obj-$(CONFIG_EFI_RUNTIME_MAP) += runtime-map.o +obj-$(CONFIG_EFI_RUNTIME_WRAPPERS) += runtime-wrappers.o diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c new file mode 100644 index 000000000000..10daa4bbb258 --- /dev/null +++ b/drivers/firmware/efi/runtime-wrappers.c @@ -0,0 +1,161 @@ +/* + * runtime-wrappers.c - Runtime Services function call wrappers + * + * Copyright (C) 2014 Linaro Ltd. + * + * Split off from arch/x86/platform/efi/efi.c + * + * Copyright (C) 1999 VA Linux Systems + * Copyright (C) 1999 Walt Drummond + * Copyright (C) 1999-2002 Hewlett-Packard Co. + * Copyright (C) 2005-2008 Intel Co. + * Copyright (C) 2013 SuSE Labs + * + * This file is released under the GPLv2. + */ + +#include +#include /* spinlock_t */ +#include + +/* + * As per commit ef68c8f87ed1 ("x86: Serialize EFI time accesses on rtc_lock"), + * the EFI specification requires that callers of the time related runtime + * functions serialize with other CMOS accesses in the kernel, as the EFI time + * functions may choose to also use the legacy CMOS RTC. + */ +__weak DEFINE_SPINLOCK(rtc_lock); + +static efi_status_t virt_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); + status = efi_call_virt(get_time, tm, tc); + spin_unlock_irqrestore(&rtc_lock, flags); + return status; +} + +static efi_status_t virt_efi_set_time(efi_time_t *tm) +{ + unsigned long flags; + efi_status_t status; + + spin_lock_irqsave(&rtc_lock, flags); + status = efi_call_virt(set_time, tm); + spin_unlock_irqrestore(&rtc_lock, flags); + return status; +} + +static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled, + efi_bool_t *pending, + efi_time_t *tm) +{ + unsigned long flags; + efi_status_t status; + + spin_lock_irqsave(&rtc_lock, flags); + status = efi_call_virt(get_wakeup_time, enabled, pending, tm); + spin_unlock_irqrestore(&rtc_lock, flags); + return status; +} + +static efi_status_t virt_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm) +{ + unsigned long flags; + efi_status_t status; + + spin_lock_irqsave(&rtc_lock, flags); + status = efi_call_virt(set_wakeup_time, enabled, tm); + spin_unlock_irqrestore(&rtc_lock, flags); + return status; +} + +static efi_status_t virt_efi_get_variable(efi_char16_t *name, + efi_guid_t *vendor, + u32 *attr, + unsigned long *data_size, + void *data) +{ + return efi_call_virt(get_variable, name, vendor, attr, data_size, data); +} + +static efi_status_t virt_efi_get_next_variable(unsigned long *name_size, + efi_char16_t *name, + efi_guid_t *vendor) +{ + return efi_call_virt(get_next_variable, name_size, name, vendor); +} + +static efi_status_t virt_efi_set_variable(efi_char16_t *name, + efi_guid_t *vendor, + u32 attr, + unsigned long data_size, + void *data) +{ + return efi_call_virt(set_variable, name, vendor, attr, data_size, data); +} + +static efi_status_t virt_efi_query_variable_info(u32 attr, + u64 *storage_space, + u64 *remaining_space, + u64 *max_variable_size) +{ + if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) + return EFI_UNSUPPORTED; + + return efi_call_virt(query_variable_info, attr, storage_space, + remaining_space, max_variable_size); +} + +static efi_status_t virt_efi_get_next_high_mono_count(u32 *count) +{ + return efi_call_virt(get_next_high_mono_count, count); +} + +static void virt_efi_reset_system(int reset_type, + efi_status_t status, + unsigned long data_size, + efi_char16_t *data) +{ + __efi_call_virt(reset_system, reset_type, status, data_size, data); +} + +static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules, + unsigned long count, + unsigned long sg_list) +{ + if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) + return EFI_UNSUPPORTED; + + return efi_call_virt(update_capsule, capsules, count, sg_list); +} + +static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules, + unsigned long count, + u64 *max_size, + int *reset_type) +{ + if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) + return EFI_UNSUPPORTED; + + return efi_call_virt(query_capsule_caps, capsules, count, max_size, + reset_type); +} + +void efi_native_runtime_setup(void) +{ + efi.get_time = virt_efi_get_time; + efi.set_time = virt_efi_set_time; + efi.get_wakeup_time = virt_efi_get_wakeup_time; + efi.set_wakeup_time = virt_efi_set_wakeup_time; + efi.get_variable = virt_efi_get_variable; + efi.get_next_variable = virt_efi_get_next_variable; + efi.set_variable = virt_efi_set_variable; + efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count; + efi.reset_system = virt_efi_reset_system; + efi.query_variable_info = virt_efi_query_variable_info; + efi.update_capsule = virt_efi_update_capsule; + efi.query_capsule_caps = virt_efi_query_capsule_caps; +} diff --git a/include/linux/efi.h b/include/linux/efi.h index 41bbf8ba4ba8..0ceb816bdfc2 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -521,6 +521,8 @@ typedef efi_status_t efi_query_capsule_caps_t(efi_capsule_header_t **capsules, int *reset_type); typedef efi_status_t efi_query_variable_store_t(u32 attributes, unsigned long size); +void efi_native_runtime_setup(void); + /* * EFI Configuration Table and GUID definitions */ -- cgit v1.2.3 From bd669475d14e3279a7f96ed917a82df5da6ad52d Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 2 Jul 2014 14:54:42 +0200 Subject: efi: efistub: Refactor stub components In order to move from the #include "../../../xxxxx.c" anti-pattern used by both the x86 and arm64 versions of the stub to a static library linked into either the kernel proper (arm64) or a separate boot executable (x86), there is some prepatory work required. This patch does the following: - move forward declarations of functions shared between the arch specific and the generic parts of the stub to include/linux/efi.h - move forward declarations of functions shared between various .c files of the generic stub code to a new local header file called "efistub.h" - add #includes to all .c files which were formerly relying on the #includor to include the correct header files - remove all static modifiers from functions which will need to be externally visible once we move to a static library Signed-off-by: Ard Biesheuvel Signed-off-by: Matt Fleming --- arch/arm64/kernel/efi-stub.c | 29 ++++--------- arch/x86/boot/compressed/eboot.c | 13 +++--- drivers/firmware/efi/arm-stub.c | 32 +++++++++------ drivers/firmware/efi/efi-stub-helper.c | 74 +++++++++++++++++----------------- drivers/firmware/efi/efistub.h | 42 +++++++++++++++++++ drivers/firmware/efi/fdt.c | 20 +++++---- include/linux/efi.h | 42 +++++++++++++++++++ 7 files changed, 164 insertions(+), 88 deletions(-) create mode 100644 drivers/firmware/efi/efistub.h (limited to 'include') diff --git a/arch/arm64/kernel/efi-stub.c b/arch/arm64/kernel/efi-stub.c index 23cbde4324b1..e4999021b07d 100644 --- a/arch/arm64/kernel/efi-stub.c +++ b/arch/arm64/kernel/efi-stub.c @@ -11,36 +11,21 @@ */ #include #include -#include #include -static void efi_char16_printk(efi_system_table_t *sys_table_arg, - efi_char16_t *str); - -static efi_status_t efi_open_volume(efi_system_table_t *sys_table, - void *__image, void **__fh); -static efi_status_t efi_file_close(void *handle); - -static efi_status_t -efi_file_read(void *handle, unsigned long *size, void *addr); - -static efi_status_t -efi_file_size(efi_system_table_t *sys_table, void *__fh, - efi_char16_t *filename_16, void **handle, u64 *file_sz); - /* Include shared EFI stub code */ #include "../../../drivers/firmware/efi/efi-stub-helper.c" #include "../../../drivers/firmware/efi/fdt.c" #include "../../../drivers/firmware/efi/arm-stub.c" -static efi_status_t handle_kernel_image(efi_system_table_t *sys_table, - unsigned long *image_addr, - unsigned long *image_size, - unsigned long *reserve_addr, - unsigned long *reserve_size, - unsigned long dram_base, - efi_loaded_image_t *image) +efi_status_t handle_kernel_image(efi_system_table_t *sys_table, + unsigned long *image_addr, + unsigned long *image_size, + unsigned long *reserve_addr, + unsigned long *reserve_size, + unsigned long dram_base, + efi_loaded_image_t *image) { efi_status_t status; unsigned long kernel_size, kernel_memsize = 0; diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index c066bc4e3051..916bbdd7dd28 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -45,8 +45,7 @@ static void setup_boot_services##bits(struct efi_config *c) \ BOOT_SERVICES(32); BOOT_SERVICES(64); -static void efi_printk(efi_system_table_t *, char *); -static void efi_char16_printk(efi_system_table_t *, efi_char16_t *); +void efi_char16_printk(efi_system_table_t *, efi_char16_t *); static efi_status_t __file_size32(void *__fh, efi_char16_t *filename_16, @@ -153,7 +152,7 @@ grow: return status; } -static efi_status_t +efi_status_t efi_file_size(efi_system_table_t *sys_table, void *__fh, efi_char16_t *filename_16, void **handle, u64 *file_sz) { @@ -163,7 +162,7 @@ efi_file_size(efi_system_table_t *sys_table, void *__fh, return __file_size32(__fh, filename_16, handle, file_sz); } -static inline efi_status_t +efi_status_t efi_file_read(void *handle, unsigned long *size, void *addr) { unsigned long func; @@ -181,7 +180,7 @@ efi_file_read(void *handle, unsigned long *size, void *addr) } } -static inline efi_status_t efi_file_close(void *handle) +efi_status_t efi_file_close(void *handle) { if (efi_early->is64) { efi_file_handle_64_t *fh = handle; @@ -246,7 +245,7 @@ static inline efi_status_t __open_volume64(void *__image, void **__fh) return status; } -static inline efi_status_t +efi_status_t efi_open_volume(efi_system_table_t *sys_table, void *__image, void **__fh) { if (efi_early->is64) @@ -255,7 +254,7 @@ efi_open_volume(efi_system_table_t *sys_table, void *__image, void **__fh) return __open_volume32(__image, __fh); } -static void efi_char16_printk(efi_system_table_t *table, efi_char16_t *str) +void efi_char16_printk(efi_system_table_t *table, efi_char16_t *str) { unsigned long output_string; size_t offset; diff --git a/drivers/firmware/efi/arm-stub.c b/drivers/firmware/efi/arm-stub.c index 41114ce03b01..480339b6b110 100644 --- a/drivers/firmware/efi/arm-stub.c +++ b/drivers/firmware/efi/arm-stub.c @@ -12,6 +12,11 @@ * */ +#include +#include + +#include "efistub.h" + static int __init efi_secureboot_enabled(efi_system_table_t *sys_table_arg) { static efi_guid_t const var_guid __initconst = EFI_GLOBAL_VARIABLE_GUID; @@ -36,8 +41,8 @@ static int __init efi_secureboot_enabled(efi_system_table_t *sys_table_arg) } } -static efi_status_t efi_open_volume(efi_system_table_t *sys_table_arg, - void *__image, void **__fh) +efi_status_t efi_open_volume(efi_system_table_t *sys_table_arg, + void *__image, void **__fh) { efi_file_io_interface_t *io; efi_loaded_image_t *image = __image; @@ -60,14 +65,15 @@ static efi_status_t efi_open_volume(efi_system_table_t *sys_table_arg, *__fh = fh; return status; } -static efi_status_t efi_file_close(void *handle) + +efi_status_t efi_file_close(void *handle) { efi_file_handle_t *fh = handle; return fh->close(handle); } -static efi_status_t +efi_status_t efi_file_read(void *handle, unsigned long *size, void *addr) { efi_file_handle_t *fh = handle; @@ -76,7 +82,7 @@ efi_file_read(void *handle, unsigned long *size, void *addr) } -static efi_status_t +efi_status_t efi_file_size(efi_system_table_t *sys_table_arg, void *__fh, efi_char16_t *filename_16, void **handle, u64 *file_sz) { @@ -129,7 +135,7 @@ grow: -static void efi_char16_printk(efi_system_table_t *sys_table_arg, +void efi_char16_printk(efi_system_table_t *sys_table_arg, efi_char16_t *str) { struct efi_simple_text_output_protocol *out; @@ -145,13 +151,13 @@ static void efi_char16_printk(efi_system_table_t *sys_table_arg, * must be reserved. On failure it is required to free all * all allocations it has made. */ -static efi_status_t handle_kernel_image(efi_system_table_t *sys_table, - unsigned long *image_addr, - unsigned long *image_size, - unsigned long *reserve_addr, - unsigned long *reserve_size, - unsigned long dram_base, - efi_loaded_image_t *image); +efi_status_t handle_kernel_image(efi_system_table_t *sys_table, + unsigned long *image_addr, + unsigned long *image_size, + unsigned long *reserve_addr, + unsigned long *reserve_size, + unsigned long dram_base, + efi_loaded_image_t *image); /* * EFI entry point for the arm/arm64 EFI stubs. This is the entrypoint * that is described in the PE/COFF header. Most of the code is the same diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index eb6d4be9e722..32d5cca30f49 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -9,18 +9,20 @@ * under the terms of the GNU General Public License version 2. * */ -#define EFI_READ_CHUNK_SIZE (1024 * 1024) -/* error code which can't be mistaken for valid address */ -#define EFI_ERROR (~0UL) +#include +#include + +#include "efistub.h" +#define EFI_READ_CHUNK_SIZE (1024 * 1024) struct file_info { efi_file_handle_t *handle; u64 size; }; -static void efi_printk(efi_system_table_t *sys_table_arg, char *str) +void efi_printk(efi_system_table_t *sys_table_arg, char *str) { char *s8; @@ -37,16 +39,12 @@ static void efi_printk(efi_system_table_t *sys_table_arg, char *str) } } -#define pr_efi(sys_table, msg) efi_printk(sys_table, "EFI stub: "msg) -#define pr_efi_err(sys_table, msg) efi_printk(sys_table, "EFI stub: ERROR: "msg) - - -static efi_status_t efi_get_memory_map(efi_system_table_t *sys_table_arg, - efi_memory_desc_t **map, - unsigned long *map_size, - unsigned long *desc_size, - u32 *desc_ver, - unsigned long *key_ptr) +efi_status_t efi_get_memory_map(efi_system_table_t *sys_table_arg, + efi_memory_desc_t **map, + unsigned long *map_size, + unsigned long *desc_size, + u32 *desc_ver, + unsigned long *key_ptr) { efi_memory_desc_t *m = NULL; efi_status_t status; @@ -88,7 +86,7 @@ fail: } -static unsigned long __init get_dram_base(efi_system_table_t *sys_table_arg) +unsigned long __init get_dram_base(efi_system_table_t *sys_table_arg) { efi_status_t status; unsigned long map_size; @@ -116,9 +114,9 @@ static unsigned long __init get_dram_base(efi_system_table_t *sys_table_arg) /* * Allocate at the highest possible address that is not above 'max'. */ -static efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg, - unsigned long size, unsigned long align, - unsigned long *addr, unsigned long max) +efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg, + unsigned long size, unsigned long align, + unsigned long *addr, unsigned long max) { unsigned long map_size, desc_size; efi_memory_desc_t *map; @@ -202,9 +200,9 @@ fail: /* * Allocate at the lowest possible address. */ -static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg, - unsigned long size, unsigned long align, - unsigned long *addr) +efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg, + unsigned long size, unsigned long align, + unsigned long *addr) { unsigned long map_size, desc_size; efi_memory_desc_t *map; @@ -271,8 +269,8 @@ fail: return status; } -static void efi_free(efi_system_table_t *sys_table_arg, unsigned long size, - unsigned long addr) +void efi_free(efi_system_table_t *sys_table_arg, unsigned long size, + unsigned long addr) { unsigned long nr_pages; @@ -290,12 +288,12 @@ static void efi_free(efi_system_table_t *sys_table_arg, unsigned long size, * We only support loading a file from the same filesystem as * the kernel image. */ -static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, - efi_loaded_image_t *image, - char *cmd_line, char *option_string, - unsigned long max_addr, - unsigned long *load_addr, - unsigned long *load_size) +efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, + efi_loaded_image_t *image, + char *cmd_line, char *option_string, + unsigned long max_addr, + unsigned long *load_addr, + unsigned long *load_size) { struct file_info *files; unsigned long file_addr; @@ -477,12 +475,12 @@ fail: * address is not available the lowest available address will * be used. */ -static efi_status_t efi_relocate_kernel(efi_system_table_t *sys_table_arg, - unsigned long *image_addr, - unsigned long image_size, - unsigned long alloc_size, - unsigned long preferred_addr, - unsigned long alignment) +efi_status_t efi_relocate_kernel(efi_system_table_t *sys_table_arg, + unsigned long *image_addr, + unsigned long image_size, + unsigned long alloc_size, + unsigned long preferred_addr, + unsigned long alignment) { unsigned long cur_image_addr; unsigned long new_addr = 0; @@ -589,9 +587,9 @@ static u8 *efi_utf16_to_utf8(u8 *dst, const u16 *src, int n) * Size of memory allocated return in *cmd_line_len. * Returns NULL on error. */ -static char *efi_convert_cmdline(efi_system_table_t *sys_table_arg, - efi_loaded_image_t *image, - int *cmd_line_len) +char *efi_convert_cmdline(efi_system_table_t *sys_table_arg, + efi_loaded_image_t *image, + int *cmd_line_len) { const u16 *s2; u8 *s1 = NULL; diff --git a/drivers/firmware/efi/efistub.h b/drivers/firmware/efi/efistub.h new file mode 100644 index 000000000000..304ab295ca1a --- /dev/null +++ b/drivers/firmware/efi/efistub.h @@ -0,0 +1,42 @@ + +#ifndef _DRIVERS_FIRMWARE_EFI_EFISTUB_H +#define _DRIVERS_FIRMWARE_EFI_EFISTUB_H + +/* error code which can't be mistaken for valid address */ +#define EFI_ERROR (~0UL) + +void efi_char16_printk(efi_system_table_t *, efi_char16_t *); + +efi_status_t efi_open_volume(efi_system_table_t *sys_table_arg, void *__image, + void **__fh); + +efi_status_t efi_file_size(efi_system_table_t *sys_table_arg, void *__fh, + efi_char16_t *filename_16, void **handle, + u64 *file_sz); + +efi_status_t efi_file_read(void *handle, unsigned long *size, void *addr); + +efi_status_t efi_file_close(void *handle); + +unsigned long get_dram_base(efi_system_table_t *sys_table_arg); + +efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, + unsigned long orig_fdt_size, + void *fdt, int new_fdt_size, char *cmdline_ptr, + u64 initrd_addr, u64 initrd_size, + efi_memory_desc_t *memory_map, + unsigned long map_size, unsigned long desc_size, + u32 desc_ver); + +efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, + void *handle, + unsigned long *new_fdt_addr, + unsigned long max_addr, + u64 initrd_addr, u64 initrd_size, + char *cmdline_ptr, + unsigned long fdt_addr, + unsigned long fdt_size); + +void *get_fdt(efi_system_table_t *sys_table); + +#endif diff --git a/drivers/firmware/efi/fdt.c b/drivers/firmware/efi/fdt.c index 3aec36d7aae9..86d2934840e2 100644 --- a/drivers/firmware/efi/fdt.c +++ b/drivers/firmware/efi/fdt.c @@ -10,13 +10,17 @@ * */ -static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, - unsigned long orig_fdt_size, - void *fdt, int new_fdt_size, char *cmdline_ptr, - u64 initrd_addr, u64 initrd_size, - efi_memory_desc_t *memory_map, - unsigned long map_size, unsigned long desc_size, - u32 desc_ver) +#include +#include +#include + +efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, + unsigned long orig_fdt_size, + void *fdt, int new_fdt_size, char *cmdline_ptr, + u64 initrd_addr, u64 initrd_size, + efi_memory_desc_t *memory_map, + unsigned long map_size, unsigned long desc_size, + u32 desc_ver) { int node, prev; int status; @@ -255,7 +259,7 @@ fail: return EFI_LOAD_ERROR; } -static void *get_fdt(efi_system_table_t *sys_table) +void *get_fdt(efi_system_table_t *sys_table) { efi_guid_t fdt_guid = DEVICE_TREE_GUID; efi_config_table_t *tables; diff --git a/include/linux/efi.h b/include/linux/efi.h index 0ceb816bdfc2..3a64f2f85821 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -1163,4 +1163,46 @@ static inline void efi_runtime_map_setup(void *map, int nr_entries, u32 desc_size) {} #endif +/* prototypes shared between arch specific and generic stub code */ + +#define pr_efi(sys_table, msg) efi_printk(sys_table, "EFI stub: "msg) +#define pr_efi_err(sys_table, msg) efi_printk(sys_table, "EFI stub: ERROR: "msg) + +void efi_printk(efi_system_table_t *sys_table_arg, char *str); + +void efi_free(efi_system_table_t *sys_table_arg, unsigned long size, + unsigned long addr); + +char *efi_convert_cmdline(efi_system_table_t *sys_table_arg, + efi_loaded_image_t *image, int *cmd_line_len); + +efi_status_t efi_get_memory_map(efi_system_table_t *sys_table_arg, + efi_memory_desc_t **map, + unsigned long *map_size, + unsigned long *desc_size, + u32 *desc_ver, + unsigned long *key_ptr); + +efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg, + unsigned long size, unsigned long align, + unsigned long *addr); + +efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg, + unsigned long size, unsigned long align, + unsigned long *addr, unsigned long max); + +efi_status_t efi_relocate_kernel(efi_system_table_t *sys_table_arg, + unsigned long *image_addr, + unsigned long image_size, + unsigned long alloc_size, + unsigned long preferred_addr, + unsigned long alignment); + +efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, + efi_loaded_image_t *image, + char *cmd_line, char *option_string, + unsigned long max_addr, + unsigned long *load_addr, + unsigned long *load_size); + #endif /* _LINUX_EFI_H */ -- cgit v1.2.3 From f473832fece16611520bf54ad52b16c3f6db0a94 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Sat, 5 Jul 2014 01:10:41 +0200 Subject: bcma: add driver for PCIe Gen 2 core MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New Broadcom PCIe devices (802.11ac ones?) use Gen2 and have to be initialized differently. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/bcma/Makefile | 1 + drivers/bcma/driver_pcie2.c | 175 +++++++++++++++++++++++++++++++++ drivers/bcma/main.c | 8 ++ include/linux/bcma/bcma.h | 2 + include/linux/bcma/bcma_driver_pcie2.h | 158 +++++++++++++++++++++++++++++ 5 files changed, 344 insertions(+) create mode 100644 drivers/bcma/driver_pcie2.c create mode 100644 include/linux/bcma/bcma_driver_pcie2.h (limited to 'include') diff --git a/drivers/bcma/Makefile b/drivers/bcma/Makefile index 734b32f09c0a..91290f7f61b8 100644 --- a/drivers/bcma/Makefile +++ b/drivers/bcma/Makefile @@ -3,6 +3,7 @@ bcma-y += driver_chipcommon.o driver_chipcommon_pmu.o bcma-$(CONFIG_BCMA_SFLASH) += driver_chipcommon_sflash.o bcma-$(CONFIG_BCMA_NFLASH) += driver_chipcommon_nflash.o bcma-y += driver_pci.o +bcma-y += driver_pcie2.o bcma-$(CONFIG_BCMA_DRIVER_PCI_HOSTMODE) += driver_pci_host.o bcma-$(CONFIG_BCMA_DRIVER_MIPS) += driver_mips.o bcma-$(CONFIG_BCMA_DRIVER_GMAC_CMN) += driver_gmac_cmn.o diff --git a/drivers/bcma/driver_pcie2.c b/drivers/bcma/driver_pcie2.c new file mode 100644 index 000000000000..e4be537b0c66 --- /dev/null +++ b/drivers/bcma/driver_pcie2.c @@ -0,0 +1,175 @@ +/* + * Broadcom specific AMBA + * PCIe Gen 2 Core + * + * Copyright 2014, Broadcom Corporation + * Copyright 2014, Rafał Miłecki + * + * Licensed under the GNU/GPL. See COPYING for details. + */ + +#include "bcma_private.h" +#include + +/************************************************** + * R/W ops. + **************************************************/ + +#if 0 +static u32 bcma_core_pcie2_cfg_read(struct bcma_drv_pcie2 *pcie2, u32 addr) +{ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, addr); + pcie2_read32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR); + return pcie2_read32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA); +} +#endif + +static void bcma_core_pcie2_cfg_write(struct bcma_drv_pcie2 *pcie2, u32 addr, + u32 val) +{ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, addr); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, val); +} + +/************************************************** + * Init. + **************************************************/ + +static u32 bcma_core_pcie2_war_delay_perst_enab(struct bcma_drv_pcie2 *pcie2, + bool enable) +{ + u32 val; + + /* restore back to default */ + val = pcie2_read32(pcie2, BCMA_CORE_PCIE2_CLK_CONTROL); + val |= PCIE2_CLKC_DLYPERST; + val &= ~PCIE2_CLKC_DISSPROMLD; + if (enable) { + val &= ~PCIE2_CLKC_DLYPERST; + val |= PCIE2_CLKC_DISSPROMLD; + } + pcie2_write32(pcie2, (BCMA_CORE_PCIE2_CLK_CONTROL), val); + /* flush */ + return pcie2_read32(pcie2, BCMA_CORE_PCIE2_CLK_CONTROL); +} + +static void bcma_core_pcie2_set_ltr_vals(struct bcma_drv_pcie2 *pcie2) +{ + /* LTR0 */ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, 0x844); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x883c883c); + /* LTR1 */ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, 0x848); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x88648864); + /* LTR2 */ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, 0x84C); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x90039003); +} + +static void bcma_core_pcie2_hw_ltr_war(struct bcma_drv_pcie2 *pcie2) +{ + u8 core_rev = pcie2->core->id.rev; + u32 devstsctr2; + + if (core_rev < 2 || core_rev == 10 || core_rev > 13) + return; + + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, + PCIE2_CAP_DEVSTSCTRL2_OFFSET); + devstsctr2 = pcie2_read32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA); + if (devstsctr2 & PCIE2_CAP_DEVSTSCTRL2_LTRENAB) { + /* force the right LTR values */ + bcma_core_pcie2_set_ltr_vals(pcie2); + + /* TODO: + si_core_wrapperreg(pcie2, 3, 0x60, 0x8080, 0); */ + + /* enable the LTR */ + devstsctr2 |= PCIE2_CAP_DEVSTSCTRL2_LTRENAB; + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, + PCIE2_CAP_DEVSTSCTRL2_OFFSET); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, devstsctr2); + + /* set the LTR state to be active */ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_LTR_STATE, + PCIE2_LTR_ACTIVE); + usleep_range(1000, 2000); + + /* set the LTR state to be sleep */ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_LTR_STATE, + PCIE2_LTR_SLEEP); + usleep_range(1000, 2000); + } +} + +static void pciedev_crwlpciegen2(struct bcma_drv_pcie2 *pcie2) +{ + u8 core_rev = pcie2->core->id.rev; + bool pciewar160, pciewar162; + + pciewar160 = core_rev == 7 || core_rev == 9 || core_rev == 11; + pciewar162 = core_rev == 5 || core_rev == 7 || core_rev == 8 || + core_rev == 9 || core_rev == 11; + + if (!pciewar160 && !pciewar162) + return; + +/* TODO */ +#if 0 + pcie2_set32(pcie2, BCMA_CORE_PCIE2_CLK_CONTROL, + PCIE_DISABLE_L1CLK_GATING); +#if 0 + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, + PCIEGEN2_COE_PVT_TL_CTRL_0); + pcie2_mask32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, + ~(1 << COE_PVT_TL_CTRL_0_PM_DIS_L1_REENTRY_BIT)); +#endif +#endif +} + +static void pciedev_crwlpciegen2_180(struct bcma_drv_pcie2 *pcie2) +{ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, PCIE2_PMCR_REFUP); + pcie2_set32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x1f); +} + +static void pciedev_crwlpciegen2_182(struct bcma_drv_pcie2 *pcie2) +{ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, PCIE2_SBMBX); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 1 << 0); +} + +static void pciedev_reg_pm_clk_period(struct bcma_drv_pcie2 *pcie2) +{ + struct bcma_drv_cc *drv_cc = &pcie2->core->bus->drv_cc; + u8 core_rev = pcie2->core->id.rev; + u32 alp_khz, pm_value; + + if (core_rev <= 13) { + alp_khz = bcma_pmu_get_alp_clock(drv_cc) / 1000; + pm_value = (1000000 * 2) / alp_khz; + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, + PCIE2_PVT_REG_PM_CLK_PERIOD); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, pm_value); + } +} + +void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2) +{ + struct bcma_chipinfo *ci = &pcie2->core->bus->chipinfo; + u32 tmp; + + tmp = pcie2_read32(pcie2, BCMA_CORE_PCIE2_SPROM(54)); + if ((tmp & 0xe) >> 1 == 2) + bcma_core_pcie2_cfg_write(pcie2, 0x4e0, 0x17); + + /* TODO: Do we need pcie_reqsize? */ + + if (ci->id == BCMA_CHIP_ID_BCM4360 && ci->rev > 3) + bcma_core_pcie2_war_delay_perst_enab(pcie2, true); + bcma_core_pcie2_hw_ltr_war(pcie2); + pciedev_crwlpciegen2(pcie2); + pciedev_reg_pm_clk_period(pcie2); + pciedev_crwlpciegen2_180(pcie2); + pciedev_crwlpciegen2_182(pcie2); +} diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index 34ea4c588d36..0ff8d58831ef 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -132,6 +132,7 @@ static int bcma_register_cores(struct bcma_bus *bus) case BCMA_CORE_CHIPCOMMON: case BCMA_CORE_PCI: case BCMA_CORE_PCIE: + case BCMA_CORE_PCIE2: case BCMA_CORE_MIPS_74K: case BCMA_CORE_4706_MAC_GBIT_COMMON: continue; @@ -281,6 +282,13 @@ int bcma_bus_register(struct bcma_bus *bus) bcma_core_pci_init(&bus->drv_pci[1]); } + /* Init PCIe Gen 2 core */ + core = bcma_find_core_unit(bus, BCMA_CORE_PCIE2, 0); + if (core) { + bus->drv_pcie2.core = core; + bcma_core_pcie2_init(&bus->drv_pcie2); + } + /* Init GBIT MAC COMMON core */ core = bcma_find_core(bus, BCMA_CORE_4706_MAC_GBIT_COMMON); if (core) { diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 0b3bb16c705a..452286a38b2b 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -6,6 +6,7 @@ #include #include +#include #include #include #include /* SPROM sharing */ @@ -333,6 +334,7 @@ struct bcma_bus { struct bcma_drv_cc drv_cc; struct bcma_drv_pci drv_pci[2]; + struct bcma_drv_pcie2 drv_pcie2; struct bcma_drv_mips drv_mips; struct bcma_drv_gmac_cmn drv_gmac_cmn; diff --git a/include/linux/bcma/bcma_driver_pcie2.h b/include/linux/bcma/bcma_driver_pcie2.h new file mode 100644 index 000000000000..5988b05781c3 --- /dev/null +++ b/include/linux/bcma/bcma_driver_pcie2.h @@ -0,0 +1,158 @@ +#ifndef LINUX_BCMA_DRIVER_PCIE2_H_ +#define LINUX_BCMA_DRIVER_PCIE2_H_ + +#define BCMA_CORE_PCIE2_CLK_CONTROL 0x0000 +#define PCIE2_CLKC_RST_OE 0x0001 /* When set, drives PCI_RESET out to pin */ +#define PCIE2_CLKC_RST 0x0002 /* Value driven out to pin */ +#define PCIE2_CLKC_SPERST 0x0004 /* SurvivePeRst */ +#define PCIE2_CLKC_DISABLE_L1CLK_GATING 0x0010 +#define PCIE2_CLKC_DLYPERST 0x0100 /* Delay PeRst to CoE Core */ +#define PCIE2_CLKC_DISSPROMLD 0x0200 /* DisableSpromLoadOnPerst */ +#define PCIE2_CLKC_WAKE_MODE_L2 0x1000 /* Wake on L2 */ +#define BCMA_CORE_PCIE2_RC_PM_CONTROL 0x0004 +#define BCMA_CORE_PCIE2_RC_PM_STATUS 0x0008 +#define BCMA_CORE_PCIE2_EP_PM_CONTROL 0x000C +#define BCMA_CORE_PCIE2_EP_PM_STATUS 0x0010 +#define BCMA_CORE_PCIE2_EP_LTR_CONTROL 0x0014 +#define BCMA_CORE_PCIE2_EP_LTR_STATUS 0x0018 +#define BCMA_CORE_PCIE2_EP_OBFF_STATUS 0x001C +#define BCMA_CORE_PCIE2_PCIE_ERR_STATUS 0x0020 +#define BCMA_CORE_PCIE2_RC_AXI_CONFIG 0x0100 +#define BCMA_CORE_PCIE2_EP_AXI_CONFIG 0x0104 +#define BCMA_CORE_PCIE2_RXDEBUG_STATUS0 0x0108 +#define BCMA_CORE_PCIE2_RXDEBUG_CONTROL0 0x010C +#define BCMA_CORE_PCIE2_CONFIGINDADDR 0x0120 +#define BCMA_CORE_PCIE2_CONFIGINDDATA 0x0124 +#define BCMA_CORE_PCIE2_MDIOCONTROL 0x0128 +#define BCMA_CORE_PCIE2_MDIOWRDATA 0x012C +#define BCMA_CORE_PCIE2_MDIORDDATA 0x0130 +#define BCMA_CORE_PCIE2_DATAINTF 0x0180 +#define BCMA_CORE_PCIE2_D2H_INTRLAZY_0 0x0188 +#define BCMA_CORE_PCIE2_H2D_INTRLAZY_0 0x018c +#define BCMA_CORE_PCIE2_H2D_INTSTAT_0 0x0190 +#define BCMA_CORE_PCIE2_H2D_INTMASK_0 0x0194 +#define BCMA_CORE_PCIE2_D2H_INTSTAT_0 0x0198 +#define BCMA_CORE_PCIE2_D2H_INTMASK_0 0x019c +#define BCMA_CORE_PCIE2_LTR_STATE 0x01A0 /* Latency Tolerance Reporting */ +#define PCIE2_LTR_ACTIVE 2 +#define PCIE2_LTR_ACTIVE_IDLE 1 +#define PCIE2_LTR_SLEEP 0 +#define PCIE2_LTR_FINAL_MASK 0x300 +#define PCIE2_LTR_FINAL_SHIFT 8 +#define BCMA_CORE_PCIE2_PWR_INT_STATUS 0x01A4 +#define BCMA_CORE_PCIE2_PWR_INT_MASK 0x01A8 +#define BCMA_CORE_PCIE2_CFG_ADDR 0x01F8 +#define BCMA_CORE_PCIE2_CFG_DATA 0x01FC +#define BCMA_CORE_PCIE2_SYS_EQ_PAGE 0x0200 +#define BCMA_CORE_PCIE2_SYS_MSI_PAGE 0x0204 +#define BCMA_CORE_PCIE2_SYS_MSI_INTREN 0x0208 +#define BCMA_CORE_PCIE2_SYS_MSI_CTRL0 0x0210 +#define BCMA_CORE_PCIE2_SYS_MSI_CTRL1 0x0214 +#define BCMA_CORE_PCIE2_SYS_MSI_CTRL2 0x0218 +#define BCMA_CORE_PCIE2_SYS_MSI_CTRL3 0x021C +#define BCMA_CORE_PCIE2_SYS_MSI_CTRL4 0x0220 +#define BCMA_CORE_PCIE2_SYS_MSI_CTRL5 0x0224 +#define BCMA_CORE_PCIE2_SYS_EQ_HEAD0 0x0250 +#define BCMA_CORE_PCIE2_SYS_EQ_TAIL0 0x0254 +#define BCMA_CORE_PCIE2_SYS_EQ_HEAD1 0x0258 +#define BCMA_CORE_PCIE2_SYS_EQ_TAIL1 0x025C +#define BCMA_CORE_PCIE2_SYS_EQ_HEAD2 0x0260 +#define BCMA_CORE_PCIE2_SYS_EQ_TAIL2 0x0264 +#define BCMA_CORE_PCIE2_SYS_EQ_HEAD3 0x0268 +#define BCMA_CORE_PCIE2_SYS_EQ_TAIL3 0x026C +#define BCMA_CORE_PCIE2_SYS_EQ_HEAD4 0x0270 +#define BCMA_CORE_PCIE2_SYS_EQ_TAIL4 0x0274 +#define BCMA_CORE_PCIE2_SYS_EQ_HEAD5 0x0278 +#define BCMA_CORE_PCIE2_SYS_EQ_TAIL5 0x027C +#define BCMA_CORE_PCIE2_SYS_RC_INTX_EN 0x0330 +#define BCMA_CORE_PCIE2_SYS_RC_INTX_CSR 0x0334 +#define BCMA_CORE_PCIE2_SYS_MSI_REQ 0x0340 +#define BCMA_CORE_PCIE2_SYS_HOST_INTR_EN 0x0344 +#define BCMA_CORE_PCIE2_SYS_HOST_INTR_CSR 0x0348 +#define BCMA_CORE_PCIE2_SYS_HOST_INTR0 0x0350 +#define BCMA_CORE_PCIE2_SYS_HOST_INTR1 0x0354 +#define BCMA_CORE_PCIE2_SYS_HOST_INTR2 0x0358 +#define BCMA_CORE_PCIE2_SYS_HOST_INTR3 0x035C +#define BCMA_CORE_PCIE2_SYS_EP_INT_EN0 0x0360 +#define BCMA_CORE_PCIE2_SYS_EP_INT_EN1 0x0364 +#define BCMA_CORE_PCIE2_SYS_EP_INT_CSR0 0x0370 +#define BCMA_CORE_PCIE2_SYS_EP_INT_CSR1 0x0374 +#define BCMA_CORE_PCIE2_SPROM(wordoffset) (0x0800 + ((wordoffset) * 2)) +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_0 0x0C00 +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_1 0x0C04 +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_2 0x0C08 +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_3 0x0C0C +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_4 0x0C10 +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_5 0x0C14 +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_6 0x0C18 +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_7 0x0C1C +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_0 0x0C20 +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_1 0x0C24 +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_2 0x0C28 +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_3 0x0C2C +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_4 0x0C30 +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_5 0x0C34 +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_6 0x0C38 +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_7 0x0C3C +#define BCMA_CORE_PCIE2_FUNC0_IMAP1 0x0C80 +#define BCMA_CORE_PCIE2_FUNC1_IMAP1 0x0C88 +#define BCMA_CORE_PCIE2_FUNC0_IMAP2 0x0CC0 +#define BCMA_CORE_PCIE2_FUNC1_IMAP2 0x0CC8 +#define BCMA_CORE_PCIE2_IARR0_LOWER 0x0D00 +#define BCMA_CORE_PCIE2_IARR0_UPPER 0x0D04 +#define BCMA_CORE_PCIE2_IARR1_LOWER 0x0D08 +#define BCMA_CORE_PCIE2_IARR1_UPPER 0x0D0C +#define BCMA_CORE_PCIE2_IARR2_LOWER 0x0D10 +#define BCMA_CORE_PCIE2_IARR2_UPPER 0x0D14 +#define BCMA_CORE_PCIE2_OARR0 0x0D20 +#define BCMA_CORE_PCIE2_OARR1 0x0D28 +#define BCMA_CORE_PCIE2_OARR2 0x0D30 +#define BCMA_CORE_PCIE2_OMAP0_LOWER 0x0D40 +#define BCMA_CORE_PCIE2_OMAP0_UPPER 0x0D44 +#define BCMA_CORE_PCIE2_OMAP1_LOWER 0x0D48 +#define BCMA_CORE_PCIE2_OMAP1_UPPER 0x0D4C +#define BCMA_CORE_PCIE2_OMAP2_LOWER 0x0D50 +#define BCMA_CORE_PCIE2_OMAP2_UPPER 0x0D54 +#define BCMA_CORE_PCIE2_FUNC1_IARR1_SIZE 0x0D58 +#define BCMA_CORE_PCIE2_FUNC1_IARR2_SIZE 0x0D5C +#define BCMA_CORE_PCIE2_MEM_CONTROL 0x0F00 +#define BCMA_CORE_PCIE2_MEM_ECC_ERRLOG0 0x0F04 +#define BCMA_CORE_PCIE2_MEM_ECC_ERRLOG1 0x0F08 +#define BCMA_CORE_PCIE2_LINK_STATUS 0x0F0C +#define BCMA_CORE_PCIE2_STRAP_STATUS 0x0F10 +#define BCMA_CORE_PCIE2_RESET_STATUS 0x0F14 +#define BCMA_CORE_PCIE2_RESETEN_IN_LINKDOWN 0x0F18 +#define BCMA_CORE_PCIE2_MISC_INTR_EN 0x0F1C +#define BCMA_CORE_PCIE2_TX_DEBUG_CFG 0x0F20 +#define BCMA_CORE_PCIE2_MISC_CONFIG 0x0F24 +#define BCMA_CORE_PCIE2_MISC_STATUS 0x0F28 +#define BCMA_CORE_PCIE2_INTR_EN 0x0F30 +#define BCMA_CORE_PCIE2_INTR_CLEAR 0x0F34 +#define BCMA_CORE_PCIE2_INTR_STATUS 0x0F38 + +/* PCIE gen2 config regs */ +#define PCIE2_INTSTATUS 0x090 +#define PCIE2_INTMASK 0x094 +#define PCIE2_SBMBX 0x098 + +#define PCIE2_PMCR_REFUP 0x1814 /* Trefup time */ + +#define PCIE2_CAP_DEVSTSCTRL2_OFFSET 0xD4 +#define PCIE2_CAP_DEVSTSCTRL2_LTRENAB 0x400 +#define PCIE2_PVT_REG_PM_CLK_PERIOD 0x184c + +struct bcma_drv_pcie2 { + struct bcma_device *core; +}; + +#define pcie2_read16(pcie2, offset) bcma_read16((pcie2)->core, offset) +#define pcie2_read32(pcie2, offset) bcma_read32((pcie2)->core, offset) +#define pcie2_write16(pcie2, offset, val) bcma_write16((pcie2)->core, offset, val) +#define pcie2_write32(pcie2, offset, val) bcma_write32((pcie2)->core, offset, val) + +#define pcie2_set32(pcie2, offset, set) bcma_set32((pcie2)->core, offset, set) +#define pcie2_mask32(pcie2, offset, mask) bcma_mask32((pcie2)->core, offset, mask) + +void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2); + +#endif /* LINUX_BCMA_DRIVER_PCIE2_H_ */ -- cgit v1.2.3 From 0b9f7d93ca6109048a4eb06332b666b6e29df4fe Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Mon, 7 Jul 2014 15:43:51 +0800 Subject: ACPI / i915: ignore firmware requests for backlight change Some Thinkpad laptops' firmware will initiate a backlight level change request through operation region on the events of AC plug/unplug, but since we are not using firmware's interface to do the backlight setting on these affected laptops, we do not want the firmware to use some arbitrary value from its ASL variable to set the backlight level on AC plug/unplug either. Link: https://bugzilla.kernel.org/show_bug.cgi?id=76491 Link: https://bugzilla.kernel.org/show_bug.cgi?id=77091 Reported-and-tested-by: Igor Gnatenko Reported-and-tested-by: Anton Gubarkov Signed-off-by: Aaron Lu Acked-by: Jani Nikula Signed-off-by: Rafael J. Wysocki --- drivers/acpi/video.c | 3 ++- drivers/gpu/drm/i915/intel_opregion.c | 9 +++++++++ include/acpi/video.h | 2 ++ 3 files changed, 13 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index fb9ffe9adc64..5bb1278e9324 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -241,13 +241,14 @@ static bool acpi_video_use_native_backlight(void) return use_native_backlight_dmi; } -static bool acpi_video_verify_backlight_support(void) +bool acpi_video_verify_backlight_support(void) { if (acpi_osi_is_win8() && acpi_video_use_native_backlight() && backlight_device_registered(BACKLIGHT_RAW)) return false; return acpi_video_backlight_support(); } +EXPORT_SYMBOL_GPL(acpi_video_verify_backlight_support); /* backlight device sysfs support */ static int acpi_video_get_brightness(struct backlight_device *bd) diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index 2e2c71fcc9ed..4f6b53998d79 100644 --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -403,6 +403,15 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) DRM_DEBUG_DRIVER("bclp = 0x%08x\n", bclp); + /* + * If the acpi_video interface is not supposed to be used, don't + * bother processing backlight level change requests from firmware. + */ + if (!acpi_video_verify_backlight_support()) { + DRM_DEBUG_KMS("opregion backlight request ignored\n"); + return 0; + } + if (!(bclp & ASLE_BCLP_VALID)) return ASLC_BACKLIGHT_FAILED; diff --git a/include/acpi/video.h b/include/acpi/video.h index ea4c7bbded4d..843ef1adfbfa 100644 --- a/include/acpi/video.h +++ b/include/acpi/video.h @@ -22,6 +22,7 @@ extern void acpi_video_unregister(void); extern void acpi_video_unregister_backlight(void); extern int acpi_video_get_edid(struct acpi_device *device, int type, int device_id, void **edid); +extern bool acpi_video_verify_backlight_support(void); #else static inline int acpi_video_register(void) { return 0; } static inline void acpi_video_unregister(void) { return; } @@ -31,6 +32,7 @@ static inline int acpi_video_get_edid(struct acpi_device *device, int type, { return -ENODEV; } +static inline bool acpi_video_verify_backlight_support(void) { return false; } #endif #endif -- cgit v1.2.3 From 0cdbe8ac696b5399327f972a1c91263c1a44f1d9 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 25 May 2014 12:59:47 +0200 Subject: drm/gem: remove misleading gfp parameter to get_pages() drm_gem_get_pages() currently allows passing a 'gfp' parameter that is passed to shmem combined with mapping_gfp_mask(). Given that the default mapping_gfp_mask() is GFP_HIGHUSER, it is _very_ unlikely that anyone will ever make use of that parameter. In fact, all drivers currently pass redundant flags or 0. This patch removes the 'gfp' parameter. The only reason to keep it is to remove flags like __GFP_WAIT. But in its current form, it can only be used to add flags. So to remove __GFP_WAIT, you'd have to drop it from the mapping_gfp_mask, which again is stupid as this mask is used by shmem-core for other allocations, too. If any driver ever requires that parameter, we can introduce a new helper that takes the raw 'gfp' parameter. The caller'd be responsible to combine it with mapping_gfp_mask() in a suitable way. The current drm_gem_get_pages() helper would then simply use mapping_gfp_mask() and call the new helper. This is what shmem_read_mapping_pages{_gfp,} does right now. Moreover, the gfp-zone flag-usage is not obvious: If you pass a modified zone, shmem core will WARN() or even BUG(). In other words, the following must be true for 'gfp' passed to shmem_read_mapping_pages_gfp(): gfp_zone(mapping_gfp_mask(mapping)) == gfp_zone(gfp) Add a comment to drm_gem_read_pages() explaining that constraint. Signed-off-by: David Herrmann --- drivers/gpu/drm/drm_gem.c | 29 ++++++++++++++++++++--------- drivers/gpu/drm/gma500/gtt.c | 2 +- drivers/gpu/drm/msm/msm_gem.c | 2 +- drivers/gpu/drm/omapdrm/omap_gem.c | 2 +- drivers/gpu/drm/udl/udl_gem.c | 8 ++++---- include/drm/drmP.h | 2 +- 6 files changed, 28 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index f7d71190aad5..6adee4c2afc0 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -441,18 +441,31 @@ EXPORT_SYMBOL(drm_gem_create_mmap_offset); * drm_gem_get_pages - helper to allocate backing pages for a GEM object * from shmem * @obj: obj in question - * @gfpmask: gfp mask of requested pages + * + * This reads the page-array of the shmem-backing storage of the given gem + * object. An array of pages is returned. If a page is not allocated or + * swapped-out, this will allocate/swap-in the required pages. Note that the + * whole object is covered by the page-array and pinned in memory. + * + * Use drm_gem_put_pages() to release the array and unpin all pages. + * + * This uses the GFP-mask set on the shmem-mapping (see mapping_set_gfp_mask()). + * If you require other GFP-masks, you have to do those allocations yourself. + * + * Note that you are not allowed to change gfp-zones during runtime. That is, + * shmem_read_mapping_page_gfp() must be called with the same gfp_zone(gfp) as + * set during initialization. If you have special zone constraints, set them + * after drm_gem_init_object() via mapping_set_gfp_mask(). shmem-core takes care + * to keep pages in the required zone during swap-in. */ -struct page **drm_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask) +struct page **drm_gem_get_pages(struct drm_gem_object *obj) { - struct inode *inode; struct address_space *mapping; struct page *p, **pages; int i, npages; /* This is the shared memory object that backs the GEM resource */ - inode = file_inode(obj->filp); - mapping = inode->i_mapping; + mapping = file_inode(obj->filp)->i_mapping; /* We already BUG_ON() for non-page-aligned sizes in * drm_gem_object_init(), so we should never hit this unless @@ -466,10 +479,8 @@ struct page **drm_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask) if (pages == NULL) return ERR_PTR(-ENOMEM); - gfpmask |= mapping_gfp_mask(mapping); - for (i = 0; i < npages; i++) { - p = shmem_read_mapping_page_gfp(mapping, i, gfpmask); + p = shmem_read_mapping_page(mapping, i); if (IS_ERR(p)) goto fail; pages[i] = p; @@ -479,7 +490,7 @@ struct page **drm_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask) * __GFP_DMA32 to be set in mapping_gfp_mask(inode->i_mapping) * so shmem can relocate pages during swapin if required. */ - BUG_ON((gfpmask & __GFP_DMA32) && + BUG_ON((mapping_gfp_mask(mapping) & __GFP_DMA32) && (page_to_pfn(p) >= 0x00100000UL)); } diff --git a/drivers/gpu/drm/gma500/gtt.c b/drivers/gpu/drm/gma500/gtt.c index 592d205a0089..ce015db59dc6 100644 --- a/drivers/gpu/drm/gma500/gtt.c +++ b/drivers/gpu/drm/gma500/gtt.c @@ -206,7 +206,7 @@ static int psb_gtt_attach_pages(struct gtt_range *gt) WARN_ON(gt->pages); - pages = drm_gem_get_pages(>->gem, 0); + pages = drm_gem_get_pages(>->gem); if (IS_ERR(pages)) return PTR_ERR(pages); diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index bb8026daebc9..6866879efa3b 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -73,7 +73,7 @@ static struct page **get_pages(struct drm_gem_object *obj) int npages = obj->size >> PAGE_SHIFT; if (iommu_present(&platform_bus_type)) - p = drm_gem_get_pages(obj, 0); + p = drm_gem_get_pages(obj); else p = get_pages_vram(obj, npages); diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c index 1331fd538398..7c68c8a18939 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem.c +++ b/drivers/gpu/drm/omapdrm/omap_gem.c @@ -233,7 +233,7 @@ static int omap_gem_attach_pages(struct drm_gem_object *obj) WARN_ON(omap_obj->pages); - pages = drm_gem_get_pages(obj, GFP_KERNEL); + pages = drm_gem_get_pages(obj); if (IS_ERR(pages)) { dev_err(obj->dev->dev, "could not get pages: %ld\n", PTR_ERR(pages)); return PTR_ERR(pages); diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c index c041cd73f399..8044f5fb7c49 100644 --- a/drivers/gpu/drm/udl/udl_gem.c +++ b/drivers/gpu/drm/udl/udl_gem.c @@ -107,14 +107,14 @@ int udl_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) } } -static int udl_gem_get_pages(struct udl_gem_object *obj, gfp_t gfpmask) +static int udl_gem_get_pages(struct udl_gem_object *obj) { struct page **pages; if (obj->pages) return 0; - pages = drm_gem_get_pages(&obj->base, gfpmask); + pages = drm_gem_get_pages(&obj->base); if (IS_ERR(pages)) return PTR_ERR(pages); @@ -147,7 +147,7 @@ int udl_gem_vmap(struct udl_gem_object *obj) return 0; } - ret = udl_gem_get_pages(obj, GFP_KERNEL); + ret = udl_gem_get_pages(obj); if (ret) return ret; @@ -205,7 +205,7 @@ int udl_gem_mmap(struct drm_file *file, struct drm_device *dev, } gobj = to_udl_bo(obj); - ret = udl_gem_get_pages(gobj, GFP_KERNEL); + ret = udl_gem_get_pages(gobj); if (ret) goto out; ret = drm_gem_create_mmap_offset(obj); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 8af71a8e2c00..57c07cd89a6a 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1574,7 +1574,7 @@ void drm_gem_free_mmap_offset(struct drm_gem_object *obj); int drm_gem_create_mmap_offset(struct drm_gem_object *obj); int drm_gem_create_mmap_offset_size(struct drm_gem_object *obj, size_t size); -struct page **drm_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask); +struct page **drm_gem_get_pages(struct drm_gem_object *obj); void drm_gem_put_pages(struct drm_gem_object *obj, struct page **pages, bool dirty, bool accessed); -- cgit v1.2.3 From 15f8f65751f081bd2d57627c453fe8986a3a0718 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Tue, 17 Jun 2014 15:39:18 +0900 Subject: drm/ttm: remove declaration of ttm_tt_cache_flush ttm_tt_cache_flush's implementation was removed in 2009 by commit c9c97b8c, but its declaration has been hiding in ttm_bo_driver.h since then. It has been surviving in the dark for too long now ; give it the mercy blow. Reviewed-by: Thierry Reding Reviewed-by: David Herrmann Signed-off-by: Alexandre Courbot Signed-off-by: David Herrmann --- include/drm/ttm/ttm_bo_driver.h | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'include') diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index a5183da3ef92..e3f8c99a8a9d 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -652,18 +652,6 @@ extern void ttm_tt_unbind(struct ttm_tt *ttm); */ extern int ttm_tt_swapin(struct ttm_tt *ttm); -/** - * ttm_tt_cache_flush: - * - * @pages: An array of pointers to struct page:s to flush. - * @num_pages: Number of pages to flush. - * - * Flush the data of the indicated pages from the cpu caches. - * This is used when changing caching attributes of the pages from - * cache-coherent. - */ -extern void ttm_tt_cache_flush(struct page *pages[], unsigned long num_pages); - /** * ttm_tt_set_placement_caching: * -- cgit v1.2.3 From 6d6dfcfb883818b40b58bac61cc72cab428a7a03 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 16 Mar 2014 14:38:40 +0100 Subject: drm: enable render-nodes by default We introduced render-nodes about 1/2 year ago and no problems showed up. Remove the drm_rnodes argument and enable them by default now. Acked-by: Daniel Vetter Acked-by: Alex Deucher Signed-off-by: David Herrmann --- drivers/gpu/drm/drm_stub.c | 7 +------ include/drm/drmP.h | 1 - 2 files changed, 1 insertion(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 14d16464000a..07bf70121e9a 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -37,9 +37,6 @@ unsigned int drm_debug = 0; /* 1 to enable debug output */ EXPORT_SYMBOL(drm_debug); -unsigned int drm_rnodes = 0; /* 1 to enable experimental render nodes API */ -EXPORT_SYMBOL(drm_rnodes); - /* 1 to allow user space to request universal planes (experimental) */ unsigned int drm_universal_planes = 0; EXPORT_SYMBOL(drm_universal_planes); @@ -60,13 +57,11 @@ MODULE_AUTHOR(CORE_AUTHOR); MODULE_DESCRIPTION(CORE_DESC); MODULE_LICENSE("GPL and additional rights"); MODULE_PARM_DESC(debug, "Enable debug output"); -MODULE_PARM_DESC(rnodes, "Enable experimental render nodes API"); MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs]"); MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]"); MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps"); module_param_named(debug, drm_debug, int, 0600); -module_param_named(rnodes, drm_rnodes, int, 0600); module_param_named(universal_planes, drm_universal_planes, int, 0600); module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600); module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600); @@ -588,7 +583,7 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver, goto err_minors; } - if (drm_core_check_feature(dev, DRIVER_RENDER) && drm_rnodes) { + if (drm_core_check_feature(dev, DRIVER_RENDER)) { ret = drm_minor_alloc(dev, DRM_MINOR_RENDER); if (ret) goto err_minors; diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 57c07cd89a6a..dc74c971b992 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1395,7 +1395,6 @@ extern void drm_master_put(struct drm_master **master); extern void drm_put_dev(struct drm_device *dev); extern void drm_unplug_dev(struct drm_device *dev); extern unsigned int drm_debug; -extern unsigned int drm_rnodes; extern unsigned int drm_universal_planes; extern unsigned int drm_vblank_offdelay; -- cgit v1.2.3 From ae5c6c6d7bcadfbedefb5fc8ff0ebe2bfa83a0a1 Mon Sep 17 00:00:00 2001 From: Stefan Sørensen Date: Fri, 27 Jun 2014 11:59:10 +0200 Subject: ptp: Classify ptp over ip over vlan packets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This extends the ptp bpf to also match ptp over ip over vlan packets. The ptp classes are changed to orthogonal bitfields representing version, transport and vlan values to simplify matching. Signed-off-by: Stefan Sørensen Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpts.c | 24 ++++++++-------- drivers/net/phy/dp83640.c | 46 ++++++++++++++---------------- include/linux/ptp_classify.h | 5 ++-- net/core/ptp_classifier.c | 64 ++++++++++++++++++++++++++++++++++++++---- 4 files changed, 92 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index 6b56f85951e5..ab92f67da035 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -256,23 +256,21 @@ static int cpts_match(struct sk_buff *skb, unsigned int ptp_class, u16 ts_seqid, u8 ts_msgtype) { u16 *seqid; - unsigned int offset; + unsigned int offset = 0; u8 *msgtype, *data = skb->data; - switch (ptp_class) { - case PTP_CLASS_V1_IPV4: - case PTP_CLASS_V2_IPV4: - offset = ETH_HLEN + IPV4_HLEN(data) + UDP_HLEN; - break; - case PTP_CLASS_V1_IPV6: - case PTP_CLASS_V2_IPV6: - offset = OFF_PTP6; + if (ptp_class & PTP_CLASS_VLAN) + offset += VLAN_HLEN; + + switch (ptp_class & PTP_CLASS_PMASK) { + case PTP_CLASS_IPV4: + offset += ETH_HLEN + IPV4_HLEN(data) + UDP_HLEN; break; - case PTP_CLASS_V2_L2: - offset = ETH_HLEN; + case PTP_CLASS_IPV6: + offset += ETH_HLEN + IP6_HLEN + UDP_HLEN; break; - case PTP_CLASS_V2_VLAN: - offset = ETH_HLEN + VLAN_HLEN; + case PTP_CLASS_L2: + offset += ETH_HLEN; break; default: return 0; diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c index 293ad064905d..53bd1af68422 100644 --- a/drivers/net/phy/dp83640.c +++ b/drivers/net/phy/dp83640.c @@ -856,20 +856,18 @@ static int is_sync(struct sk_buff *skb, int type) u8 *data = skb->data, *msgtype; unsigned int offset = 0; - switch (type) { - case PTP_CLASS_V1_IPV4: - case PTP_CLASS_V2_IPV4: - offset = ETH_HLEN + IPV4_HLEN(data) + UDP_HLEN; - break; - case PTP_CLASS_V1_IPV6: - case PTP_CLASS_V2_IPV6: - offset = OFF_PTP6; + if (type & PTP_CLASS_VLAN) + offset += VLAN_HLEN; + + switch (type & PTP_CLASS_PMASK) { + case PTP_CLASS_IPV4: + offset += ETH_HLEN + IPV4_HLEN(data) + UDP_HLEN; break; - case PTP_CLASS_V2_L2: - offset = ETH_HLEN; + case PTP_CLASS_IPV6: + offset += ETH_HLEN + IP6_HLEN + UDP_HLEN; break; - case PTP_CLASS_V2_VLAN: - offset = ETH_HLEN + VLAN_HLEN; + case PTP_CLASS_L2: + offset += ETH_HLEN; break; default: return 0; @@ -889,25 +887,23 @@ static int is_sync(struct sk_buff *skb, int type) static int match(struct sk_buff *skb, unsigned int type, struct rxts *rxts) { u16 *seqid; - unsigned int offset; + unsigned int offset = 0; u8 *msgtype, *data = skb_mac_header(skb); /* check sequenceID, messageType, 12 bit hash of offset 20-29 */ - switch (type) { - case PTP_CLASS_V1_IPV4: - case PTP_CLASS_V2_IPV4: - offset = ETH_HLEN + IPV4_HLEN(data) + UDP_HLEN; - break; - case PTP_CLASS_V1_IPV6: - case PTP_CLASS_V2_IPV6: - offset = OFF_PTP6; + if (type & PTP_CLASS_VLAN) + offset += VLAN_HLEN; + + switch (type & PTP_CLASS_PMASK) { + case PTP_CLASS_IPV4: + offset += ETH_HLEN + IPV4_HLEN(data) + UDP_HLEN; break; - case PTP_CLASS_V2_L2: - offset = ETH_HLEN; + case PTP_CLASS_IPV6: + offset += ETH_HLEN + IP6_HLEN + UDP_HLEN; break; - case PTP_CLASS_V2_VLAN: - offset = ETH_HLEN + VLAN_HLEN; + case PTP_CLASS_L2: + offset += ETH_HLEN; break; default: return 0; diff --git a/include/linux/ptp_classify.h b/include/linux/ptp_classify.h index 7dfed71d76a6..159c987b1853 100644 --- a/include/linux/ptp_classify.h +++ b/include/linux/ptp_classify.h @@ -33,8 +33,8 @@ #define PTP_CLASS_IPV4 0x10 /* event in an IPV4 UDP packet */ #define PTP_CLASS_IPV6 0x20 /* event in an IPV6 UDP packet */ #define PTP_CLASS_L2 0x30 /* event in a L2 packet */ -#define PTP_CLASS_VLAN 0x40 /* event in a VLAN tagged L2 packet */ -#define PTP_CLASS_PMASK 0xf0 /* mask for the packet type field */ +#define PTP_CLASS_PMASK 0x30 /* mask for the packet type field */ +#define PTP_CLASS_VLAN 0x40 /* event in a VLAN tagged packet */ #define PTP_CLASS_V1_IPV4 (PTP_CLASS_V1 | PTP_CLASS_IPV4) #define PTP_CLASS_V1_IPV6 (PTP_CLASS_V1 | PTP_CLASS_IPV6) /* probably DNE */ @@ -54,7 +54,6 @@ #define IP6_HLEN 40 #define UDP_HLEN 8 #define OFF_IHL 14 -#define OFF_PTP6 (ETH_HLEN + IP6_HLEN + UDP_HLEN) #define IPV4_HLEN(data) (((struct iphdr *)(data + OFF_IHL))->ihl << 2) #if defined(CONFIG_NET_PTP_CLASSIFY) diff --git a/net/core/ptp_classifier.c b/net/core/ptp_classifier.c index d3027a73fd4b..12ab7b4be609 100644 --- a/net/core/ptp_classifier.c +++ b/net/core/ptp_classifier.c @@ -52,14 +52,43 @@ * test_8021q: * jneq #0x8100, test_ieee1588 ; ETH_P_8021Q ? * ldh [16] ; load inner type - * jneq #0x88f7, drop_ieee1588 ; ETH_P_1588 ? + * jneq #0x88f7, test_8021q_ipv4 ; ETH_P_1588 ? * ldb [18] ; load payload * and #0x8 ; as we don't have ports here, test * jneq #0x0, drop_ieee1588 ; for PTP_GEN_BIT and drop these * ldh [18] ; reload payload * and #0xf ; mask PTP_CLASS_VMASK - * or #0x40 ; PTP_CLASS_V2_VLAN + * or #0x70 ; PTP_CLASS_VLAN|PTP_CLASS_L2 + * ret a ; return PTP class + * + * ; PTP over UDP over IPv4 over 802.1Q over Ethernet + * test_8021q_ipv4: + * jneq #0x800, test_8021q_ipv6 ; ETH_P_IP ? + * ldb [27] ; load proto + * jneq #17, drop_8021q_ipv4 ; IPPROTO_UDP ? + * ldh [24] ; load frag offset field + * jset #0x1fff, drop_8021q_ipv4; don't allow fragments + * ldxb 4*([18]&0xf) ; load IP header len + * ldh [x + 20] ; load UDP dst port + * jneq #319, drop_8021q_ipv4 ; is port PTP_EV_PORT ? + * ldh [x + 26] ; load payload + * and #0xf ; mask PTP_CLASS_VMASK + * or #0x50 ; PTP_CLASS_VLAN|PTP_CLASS_IPV4 + * ret a ; return PTP class + * drop_8021q_ipv4: ret #0x0 ; PTP_CLASS_NONE + * + * ; PTP over UDP over IPv6 over 802.1Q over Ethernet + * test_8021q_ipv6: + * jneq #0x86dd, drop_8021q_ipv6 ; ETH_P_IPV6 ? + * ldb [24] ; load proto + * jneq #17, drop_8021q_ipv6 ; IPPROTO_UDP ? + * ldh [60] ; load UDP dst port + * jneq #319, drop_8021q_ipv6 ; is port PTP_EV_PORT ? + * ldh [66] ; load payload + * and #0xf ; mask PTP_CLASS_VMASK + * or #0x60 ; PTP_CLASS_VLAN|PTP_CLASS_IPV6 * ret a ; return PTP class + * drop_8021q_ipv6: ret #0x0 ; PTP_CLASS_NONE * * ; PTP over Ethernet * test_ieee1588: @@ -113,16 +142,39 @@ void __init ptp_classifier_init(void) { 0x44, 0, 0, 0x00000020 }, { 0x16, 0, 0, 0x00000000 }, { 0x06, 0, 0, 0x00000000 }, - { 0x15, 0, 9, 0x00008100 }, + { 0x15, 0, 32, 0x00008100 }, { 0x28, 0, 0, 0x00000010 }, - { 0x15, 0, 15, 0x000088f7 }, + { 0x15, 0, 7, 0x000088f7 }, { 0x30, 0, 0, 0x00000012 }, { 0x54, 0, 0, 0x00000008 }, - { 0x15, 0, 12, 0x00000000 }, + { 0x15, 0, 35, 0x00000000 }, { 0x28, 0, 0, 0x00000012 }, { 0x54, 0, 0, 0x0000000f }, - { 0x44, 0, 0, 0x00000040 }, + { 0x44, 0, 0, 0x00000070 }, + { 0x16, 0, 0, 0x00000000 }, + { 0x15, 0, 12, 0x00000800 }, + { 0x30, 0, 0, 0x0000001b }, + { 0x15, 0, 9, 0x00000011 }, + { 0x28, 0, 0, 0x00000018 }, + { 0x45, 7, 0, 0x00001fff }, + { 0xb1, 0, 0, 0x00000012 }, + { 0x48, 0, 0, 0x00000014 }, + { 0x15, 0, 4, 0x0000013f }, + { 0x48, 0, 0, 0x0000001a }, + { 0x54, 0, 0, 0x0000000f }, + { 0x44, 0, 0, 0x00000050 }, + { 0x16, 0, 0, 0x00000000 }, + { 0x06, 0, 0, 0x00000000 }, + { 0x15, 0, 8, 0x000086dd }, + { 0x30, 0, 0, 0x00000018 }, + { 0x15, 0, 6, 0x00000011 }, + { 0x28, 0, 0, 0x0000003c }, + { 0x15, 0, 4, 0x0000013f }, + { 0x28, 0, 0, 0x00000042 }, + { 0x54, 0, 0, 0x0000000f }, + { 0x44, 0, 0, 0x00000060 }, { 0x16, 0, 0, 0x00000000 }, + { 0x06, 0, 0, 0x00000000 }, { 0x15, 0, 7, 0x000088f7 }, { 0x30, 0, 0, 0x0000000e }, { 0x54, 0, 0, 0x00000008 }, -- cgit v1.2.3 From e3f202798aaa808e7a38faa8c3a9f0aa93b85cc0 Mon Sep 17 00:00:00 2001 From: Christian König Date: Thu, 3 Jul 2014 09:02:23 +0200 Subject: drm/ttm: fix handling of TTM_PL_FLAG_TOPDOWN v2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bo->mem.placement is not initialized when ttm_bo_man_get_node is called, so the flag had no effect at all. v2: change nouveau and vmwgfx as well Signed-off-by: Christian König Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie --- drivers/gpu/drm/nouveau/nouveau_ttm.c | 3 +++ drivers/gpu/drm/ttm/ttm_bo.c | 6 +++--- drivers/gpu/drm/ttm/ttm_bo_manager.c | 3 ++- drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c | 1 + include/drm/ttm/ttm_bo_driver.h | 2 ++ 5 files changed, 11 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/nouveau/nouveau_ttm.c b/drivers/gpu/drm/nouveau/nouveau_ttm.c index ab0228f640a5..7e185c122750 100644 --- a/drivers/gpu/drm/nouveau/nouveau_ttm.c +++ b/drivers/gpu/drm/nouveau/nouveau_ttm.c @@ -76,6 +76,7 @@ static int nouveau_vram_manager_new(struct ttm_mem_type_manager *man, struct ttm_buffer_object *bo, struct ttm_placement *placement, + uint32_t flags, struct ttm_mem_reg *mem) { struct nouveau_drm *drm = nouveau_bdev(man->bdev); @@ -162,6 +163,7 @@ static int nouveau_gart_manager_new(struct ttm_mem_type_manager *man, struct ttm_buffer_object *bo, struct ttm_placement *placement, + uint32_t flags, struct ttm_mem_reg *mem) { struct nouveau_drm *drm = nouveau_bdev(bo->bdev); @@ -242,6 +244,7 @@ static int nv04_gart_manager_new(struct ttm_mem_type_manager *man, struct ttm_buffer_object *bo, struct ttm_placement *placement, + uint32_t flags, struct ttm_mem_reg *mem) { struct nouveau_mem *node; diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 4ab9f7171c4f..a13a10025ec7 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -784,7 +784,7 @@ static int ttm_bo_mem_force_space(struct ttm_buffer_object *bo, int ret; do { - ret = (*man->func->get_node)(man, bo, placement, mem); + ret = (*man->func->get_node)(man, bo, placement, 0, mem); if (unlikely(ret != 0)) return ret; if (mem->mm_node) @@ -897,7 +897,8 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo, if (man->has_type && man->use_type) { type_found = true; - ret = (*man->func->get_node)(man, bo, placement, mem); + ret = (*man->func->get_node)(man, bo, placement, + cur_flags, mem); if (unlikely(ret)) return ret; } @@ -937,7 +938,6 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo, ttm_flag_masked(&cur_flags, placement->busy_placement[i], ~TTM_PL_MASK_MEMTYPE); - if (mem_type == TTM_PL_SYSTEM) { mem->mem_type = mem_type; mem->placement = cur_flags; diff --git a/drivers/gpu/drm/ttm/ttm_bo_manager.c b/drivers/gpu/drm/ttm/ttm_bo_manager.c index bd850c9f4bca..9e103a4875c8 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_manager.c +++ b/drivers/gpu/drm/ttm/ttm_bo_manager.c @@ -50,6 +50,7 @@ struct ttm_range_manager { static int ttm_bo_man_get_node(struct ttm_mem_type_manager *man, struct ttm_buffer_object *bo, struct ttm_placement *placement, + uint32_t flags, struct ttm_mem_reg *mem) { struct ttm_range_manager *rman = (struct ttm_range_manager *) man->priv; @@ -67,7 +68,7 @@ static int ttm_bo_man_get_node(struct ttm_mem_type_manager *man, if (!node) return -ENOMEM; - if (bo->mem.placement & TTM_PL_FLAG_TOPDOWN) + if (flags & TTM_PL_FLAG_TOPDOWN) aflags = DRM_MM_CREATE_TOP; spin_lock(&rman->lock); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c index b1273e8e9a69..26f8bdde3529 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c @@ -47,6 +47,7 @@ struct vmwgfx_gmrid_man { static int vmw_gmrid_man_get_node(struct ttm_mem_type_manager *man, struct ttm_buffer_object *bo, struct ttm_placement *placement, + uint32_t flags, struct ttm_mem_reg *mem) { struct vmwgfx_gmrid_man *gman = diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index e3f8c99a8a9d..202f0a7171e8 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -182,6 +182,7 @@ struct ttm_mem_type_manager_func { * @man: Pointer to a memory type manager. * @bo: Pointer to the buffer object we're allocating space for. * @placement: Placement details. + * @flags: Additional placement flags. * @mem: Pointer to a struct ttm_mem_reg to be filled in. * * This function should allocate space in the memory type managed @@ -206,6 +207,7 @@ struct ttm_mem_type_manager_func { int (*get_node)(struct ttm_mem_type_manager *man, struct ttm_buffer_object *bo, struct ttm_placement *placement, + uint32_t flags, struct ttm_mem_reg *mem); /** -- cgit v1.2.3 From 3a4938799dec32783759b0c66ee3bb8c82dd9937 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 27 Jun 2014 17:19:23 +0200 Subject: drm: Constify struct drm_fb_helper_funcs There's no need for this to be modifiable. Make it const so that it can be put into the .rodata section. Reviewed-by: Daniel Vetter Signed-off-by: Thierry Reding Acked-by: Russell King Signed-off-by: Dave Airlie --- drivers/gpu/drm/armada/armada_fbdev.c | 2 +- drivers/gpu/drm/ast/ast_fb.c | 2 +- drivers/gpu/drm/bochs/bochs_fbdev.c | 2 +- drivers/gpu/drm/cirrus/cirrus_fbdev.c | 2 +- drivers/gpu/drm/drm_fb_cma_helper.c | 2 +- drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 2 +- drivers/gpu/drm/gma500/framebuffer.c | 2 +- drivers/gpu/drm/i915/intel_fbdev.c | 2 +- drivers/gpu/drm/mgag200/mgag200_fb.c | 2 +- drivers/gpu/drm/msm/msm_fbdev.c | 2 +- drivers/gpu/drm/nouveau/nouveau_fbcon.c | 2 +- drivers/gpu/drm/omapdrm/omap_fbdev.c | 2 +- drivers/gpu/drm/qxl/qxl_fb.c | 2 +- drivers/gpu/drm/radeon/radeon_fb.c | 2 +- drivers/gpu/drm/tegra/fb.c | 2 +- drivers/gpu/drm/udl/udl_fb.c | 2 +- include/drm/drm_fb_helper.h | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c index fd166f532ab9..a7c947cd9386 100644 --- a/drivers/gpu/drm/armada/armada_fbdev.c +++ b/drivers/gpu/drm/armada/armada_fbdev.c @@ -131,7 +131,7 @@ static int armada_fb_probe(struct drm_fb_helper *fbh, return ret; } -static struct drm_fb_helper_funcs armada_fb_helper_funcs = { +static const struct drm_fb_helper_funcs armada_fb_helper_funcs = { .gamma_set = armada_drm_crtc_gamma_set, .gamma_get = armada_drm_crtc_gamma_get, .fb_probe = armada_fb_probe, diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c index a28640f47c27..2113894e4ff8 100644 --- a/drivers/gpu/drm/ast/ast_fb.c +++ b/drivers/gpu/drm/ast/ast_fb.c @@ -287,7 +287,7 @@ static void ast_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, *blue = ast_crtc->lut_b[regno] << 8; } -static struct drm_fb_helper_funcs ast_fb_helper_funcs = { +static const struct drm_fb_helper_funcs ast_fb_helper_funcs = { .gamma_set = ast_fb_gamma_set, .gamma_get = ast_fb_gamma_get, .fb_probe = astfb_create, diff --git a/drivers/gpu/drm/bochs/bochs_fbdev.c b/drivers/gpu/drm/bochs/bochs_fbdev.c index 561b84474122..17e5c17f2730 100644 --- a/drivers/gpu/drm/bochs/bochs_fbdev.c +++ b/drivers/gpu/drm/bochs/bochs_fbdev.c @@ -179,7 +179,7 @@ void bochs_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, *blue = regno; } -static struct drm_fb_helper_funcs bochs_fb_helper_funcs = { +static const struct drm_fb_helper_funcs bochs_fb_helper_funcs = { .gamma_set = bochs_fb_gamma_set, .gamma_get = bochs_fb_gamma_get, .fb_probe = bochsfb_create, diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c index 32bbba0a787b..2bd0291168e4 100644 --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c @@ -288,7 +288,7 @@ static int cirrus_fbdev_destroy(struct drm_device *dev, return 0; } -static struct drm_fb_helper_funcs cirrus_fb_helper_funcs = { +static const struct drm_fb_helper_funcs cirrus_fb_helper_funcs = { .gamma_set = cirrus_crtc_fb_gamma_set, .gamma_get = cirrus_crtc_fb_gamma_get, .fb_probe = cirrusfb_create, diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index f27c883be391..cb01e1606384 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -327,7 +327,7 @@ err_drm_gem_cma_free_object: return ret; } -static struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = { +static const struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = { .fb_probe = drm_fbdev_cma_create, }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index d771b467cf0c..fc25fe75aa77 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -225,7 +225,7 @@ out: return ret; } -static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = { +static const struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = { .fb_probe = exynos_drm_fbdev_create, }; diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index e7fcc148f333..76e4d777d01d 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -561,7 +561,7 @@ static int psbfb_probe(struct drm_fb_helper *helper, return psbfb_create(psb_fbdev, sizes); } -static struct drm_fb_helper_funcs psb_fb_helper_funcs = { +static const struct drm_fb_helper_funcs psb_fb_helper_funcs = { .gamma_set = psbfb_gamma_set, .gamma_get = psbfb_gamma_get, .fb_probe = psbfb_probe, diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index 088fe9378a4c..c942d13cd5cb 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -452,7 +452,7 @@ out: return true; } -static struct drm_fb_helper_funcs intel_fb_helper_funcs = { +static const struct drm_fb_helper_funcs intel_fb_helper_funcs = { .initial_config = intel_fb_initial_config, .gamma_set = intel_crtc_fb_gamma_set, .gamma_get = intel_crtc_fb_gamma_get, diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c index 13b7dd83faa9..a4319aba9180 100644 --- a/drivers/gpu/drm/mgag200/mgag200_fb.c +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c @@ -272,7 +272,7 @@ static int mga_fbdev_destroy(struct drm_device *dev, return 0; } -static struct drm_fb_helper_funcs mga_fb_helper_funcs = { +static const struct drm_fb_helper_funcs mga_fb_helper_funcs = { .gamma_set = mga_crtc_fb_gamma_set, .gamma_get = mga_crtc_fb_gamma_get, .fb_probe = mgag200fb_create, diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c index 5107fc4826bc..7e706c4cf033 100644 --- a/drivers/gpu/drm/msm/msm_fbdev.c +++ b/drivers/gpu/drm/msm/msm_fbdev.c @@ -177,7 +177,7 @@ static void msm_crtc_fb_gamma_get(struct drm_crtc *crtc, DBG("fbdev: get gamma"); } -static struct drm_fb_helper_funcs msm_fb_helper_funcs = { +static const struct drm_fb_helper_funcs msm_fb_helper_funcs = { .gamma_set = msm_crtc_fb_gamma_set, .gamma_get = msm_crtc_fb_gamma_get, .fb_probe = msm_fbdev_create, diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index 64a42cfd3717..8e9c07b7fc89 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -438,7 +438,7 @@ void nouveau_fbcon_gpu_lockup(struct fb_info *info) info->flags |= FBINFO_HWACCEL_DISABLED; } -static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = { +static const struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = { .gamma_set = nouveau_fbcon_gamma_set, .gamma_get = nouveau_fbcon_gamma_get, .fb_probe = nouveau_fbcon_create, diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c index 1388ca7f87e8..4cb12083eb12 100644 --- a/drivers/gpu/drm/omapdrm/omap_fbdev.c +++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c @@ -281,7 +281,7 @@ fail: return ret; } -static struct drm_fb_helper_funcs omap_fb_helper_funcs = { +static const struct drm_fb_helper_funcs omap_fb_helper_funcs = { .fb_probe = omap_fbdev_create, }; diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c index f437b30ce689..cf89614c72be 100644 --- a/drivers/gpu/drm/qxl/qxl_fb.c +++ b/drivers/gpu/drm/qxl/qxl_fb.c @@ -660,7 +660,7 @@ static int qxl_fbdev_destroy(struct drm_device *dev, struct qxl_fbdev *qfbdev) return 0; } -static struct drm_fb_helper_funcs qxl_fb_helper_funcs = { +static const struct drm_fb_helper_funcs qxl_fb_helper_funcs = { .fb_probe = qxl_fb_find_or_create_single, }; diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index 665ced3b7313..ad97afdbc4c7 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -331,7 +331,7 @@ static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfb return 0; } -static struct drm_fb_helper_funcs radeon_fb_helper_funcs = { +static const struct drm_fb_helper_funcs radeon_fb_helper_funcs = { .gamma_set = radeon_crtc_fb_gamma_set, .gamma_get = radeon_crtc_fb_gamma_get, .fb_probe = radeonfb_create, diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index 9798a7080322..f7cf47bf0afb 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -267,7 +267,7 @@ release: return err; } -static struct drm_fb_helper_funcs tegra_fb_helper_funcs = { +static const struct drm_fb_helper_funcs tegra_fb_helper_funcs = { .fb_probe = tegra_fbdev_probe, }; diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index 377176372da8..0647c8cc368b 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -550,7 +550,7 @@ out: return ret; } -static struct drm_fb_helper_funcs udl_fb_helper_funcs = { +static const struct drm_fb_helper_funcs udl_fb_helper_funcs = { .fb_probe = udlfb_create, }; diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 7997246d4039..a47df7ca38e8 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -87,7 +87,7 @@ struct drm_fb_helper { struct drm_fb_helper_crtc *crtc_info; int connector_count; struct drm_fb_helper_connector **connector_info; - struct drm_fb_helper_funcs *funcs; + const struct drm_fb_helper_funcs *funcs; struct fb_info *fbdev; u32 pseudo_palette[17]; struct list_head kernel_fb_list; -- cgit v1.2.3 From 10a231026574f9ec9761b6435394b8830b485e4e Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 27 Jun 2014 17:19:24 +0200 Subject: drm: Introduce drm_fb_helper_prepare() To implement hotplug detection in a race-free manner, drivers must call drm_kms_helper_poll_init() before hotplug events can be triggered. Such events can be triggered right after any of the encoders or connectors are initialized. At the same time, if the drm_fb_helper_hotplug_event() helper is used by a driver, then the poll helper requires some parts of the FB helper to be initialized to prevent a crash. At the same time, drm_fb_helper_init() requires information that is not necessarily available at such an early stage (number of CRTCs and connectors), so it cannot be used yet. Add a new helper, drm_fb_helper_prepare(), that initializes the bare minimum needed to allow drm_kms_helper_poll_init() to execute and any subsequent hotplug events to be processed properly. Reviewed-by: Daniel Vetter Signed-off-by: Thierry Reding Signed-off-by: Dave Airlie --- drivers/gpu/drm/armada/armada_fbdev.c | 2 +- drivers/gpu/drm/ast/ast_fb.c | 4 ++- drivers/gpu/drm/bochs/bochs_fbdev.c | 3 +- drivers/gpu/drm/cirrus/cirrus_fbdev.c | 4 ++- drivers/gpu/drm/drm_fb_cma_helper.c | 3 +- drivers/gpu/drm/drm_fb_helper.c | 47 ++++++++++++++++++++++++------- drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 3 +- drivers/gpu/drm/gma500/framebuffer.c | 3 +- drivers/gpu/drm/i915/intel_fbdev.c | 3 +- drivers/gpu/drm/mgag200/mgag200_fb.c | 3 +- drivers/gpu/drm/msm/msm_fbdev.c | 2 +- drivers/gpu/drm/nouveau/nouveau_fbcon.c | 3 +- drivers/gpu/drm/omapdrm/omap_fbdev.c | 2 +- drivers/gpu/drm/qxl/qxl_fb.c | 5 +++- drivers/gpu/drm/radeon/radeon_fb.c | 4 ++- drivers/gpu/drm/tegra/fb.c | 4 +-- drivers/gpu/drm/udl/udl_fb.c | 3 +- include/drm/drm_fb_helper.h | 2 ++ 18 files changed, 72 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c index a7c947cd9386..7838e731b0de 100644 --- a/drivers/gpu/drm/armada/armada_fbdev.c +++ b/drivers/gpu/drm/armada/armada_fbdev.c @@ -149,7 +149,7 @@ int armada_fbdev_init(struct drm_device *dev) priv->fbdev = fbh; - fbh->funcs = &armada_fb_helper_funcs; + drm_fb_helper_prepare(dev, fbh, &armada_fb_helper_funcs); ret = drm_fb_helper_init(dev, fbh, 1, 1); if (ret) { diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c index 2113894e4ff8..cba45c774552 100644 --- a/drivers/gpu/drm/ast/ast_fb.c +++ b/drivers/gpu/drm/ast/ast_fb.c @@ -328,8 +328,10 @@ int ast_fbdev_init(struct drm_device *dev) return -ENOMEM; ast->fbdev = afbdev; - afbdev->helper.funcs = &ast_fb_helper_funcs; spin_lock_init(&afbdev->dirty_lock); + + drm_fb_helper_prepare(dev, &afbdev->helper, &ast_fb_helper_funcs); + ret = drm_fb_helper_init(dev, &afbdev->helper, 1, 1); if (ret) { diff --git a/drivers/gpu/drm/bochs/bochs_fbdev.c b/drivers/gpu/drm/bochs/bochs_fbdev.c index 17e5c17f2730..19cf3e9413b6 100644 --- a/drivers/gpu/drm/bochs/bochs_fbdev.c +++ b/drivers/gpu/drm/bochs/bochs_fbdev.c @@ -189,7 +189,8 @@ int bochs_fbdev_init(struct bochs_device *bochs) { int ret; - bochs->fb.helper.funcs = &bochs_fb_helper_funcs; + drm_fb_helper_prepare(bochs->dev, &bochs->fb.helper, + &bochs_fb_helper_funcs); ret = drm_fb_helper_init(bochs->dev, &bochs->fb.helper, 1, 1); diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c index 2bd0291168e4..2a135f253e29 100644 --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c @@ -306,9 +306,11 @@ int cirrus_fbdev_init(struct cirrus_device *cdev) return -ENOMEM; cdev->mode_info.gfbdev = gfbdev; - gfbdev->helper.funcs = &cirrus_fb_helper_funcs; spin_lock_init(&gfbdev->dirty_lock); + drm_fb_helper_prepare(cdev->dev, &gfbdev->helper, + &cirrus_fb_helper_funcs); + ret = drm_fb_helper_init(cdev->dev, &gfbdev->helper, cdev->num_crtc, CIRRUSFB_CONN_LIMIT); if (ret) { diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index cb01e1606384..cc0ae047ed3b 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -354,9 +354,10 @@ struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev, return ERR_PTR(-ENOMEM); } - fbdev_cma->fb_helper.funcs = &drm_fb_cma_helper_funcs; helper = &fbdev_cma->fb_helper; + drm_fb_helper_prepare(dev, helper, &drm_fb_cma_helper_funcs); + ret = drm_fb_helper_init(dev, helper, num_crtc, max_conn_count); if (ret < 0) { dev_err(dev->dev, "Failed to initialize drm fb helper.\n"); diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 6cd687a20cd5..bdee6eb0b24a 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -49,10 +49,11 @@ static LIST_HEAD(kernel_fb_helper_list); * helper functions used by many drivers to implement the kernel mode setting * interfaces. * - * Initialization is done as a three-step process with drm_fb_helper_init(), - * drm_fb_helper_single_add_all_connectors() and drm_fb_helper_initial_config(). - * Drivers with fancier requirements than the default behaviour can override the - * second step with their own code. Teardown is done with drm_fb_helper_fini(). + * Initialization is done as a four-step process with drm_fb_helper_prepare(), + * drm_fb_helper_init(), drm_fb_helper_single_add_all_connectors() and + * drm_fb_helper_initial_config(). Drivers with fancier requirements than the + * default behaviour can override the third step with their own code. + * Teardown is done with drm_fb_helper_fini(). * * At runtime drivers should restore the fbdev console by calling * drm_fb_helper_restore_fbdev_mode() from their ->lastclose callback. They @@ -63,6 +64,19 @@ static LIST_HEAD(kernel_fb_helper_list); * * All other functions exported by the fb helper library can be used to * implement the fbdev driver interface by the driver. + * + * It is possible, though perhaps somewhat tricky, to implement race-free + * hotplug detection using the fbdev helpers. The drm_fb_helper_prepare() + * helper must be called first to initialize the minimum required to make + * hotplug detection work. Drivers also need to make sure to properly set up + * the dev->mode_config.funcs member. After calling drm_kms_helper_poll_init() + * it is safe to enable interrupts and start processing hotplug events. At the + * same time, drivers should initialize all modeset objects such as CRTCs, + * encoders and connectors. To finish up the fbdev helper initialization, the + * drm_fb_helper_init() function is called. To probe for all attached displays + * and set up an initial configuration using the detected hardware, drivers + * should call drm_fb_helper_single_add_all_connectors() followed by + * drm_fb_helper_initial_config(). */ /** @@ -527,6 +541,24 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) kfree(helper->crtc_info); } +/** + * drm_fb_helper_prepare - setup a drm_fb_helper structure + * @dev: DRM device + * @helper: driver-allocated fbdev helper structure to set up + * @funcs: pointer to structure of functions associate with this helper + * + * Sets up the bare minimum to make the framebuffer helper usable. This is + * useful to implement race-free initialization of the polling helpers. + */ +void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper, + const struct drm_fb_helper_funcs *funcs) +{ + INIT_LIST_HEAD(&helper->kernel_fb_list); + helper->funcs = funcs; + helper->dev = dev; +} +EXPORT_SYMBOL(drm_fb_helper_prepare); + /** * drm_fb_helper_init - initialize a drm_fb_helper structure * @dev: drm device @@ -539,8 +571,7 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) * nor register the fbdev. This is only done in drm_fb_helper_initial_config() * to allow driver writes more control over the exact init sequence. * - * Drivers must set fb_helper->funcs before calling - * drm_fb_helper_initial_config(). + * Drivers must call drm_fb_helper_prepare() before calling this function. * * RETURNS: * Zero if everything went ok, nonzero otherwise. @@ -555,10 +586,6 @@ int drm_fb_helper_init(struct drm_device *dev, if (!max_conn_count) return -EINVAL; - fb_helper->dev = dev; - - INIT_LIST_HEAD(&fb_helper->kernel_fb_list); - fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL); if (!fb_helper->crtc_info) return -ENOMEM; diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index fc25fe75aa77..32e63f60e1d1 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -266,7 +266,8 @@ int exynos_drm_fbdev_init(struct drm_device *dev) return -ENOMEM; private->fb_helper = helper = &fbdev->drm_fb_helper; - helper->funcs = &exynos_drm_fb_helper_funcs; + + drm_fb_helper_prepare(dev, helper, &exynos_drm_fb_helper_funcs); num_crtc = dev->mode_config.num_crtc; diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index 76e4d777d01d..d0dd3bea8aa5 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -600,7 +600,8 @@ int psb_fbdev_init(struct drm_device *dev) } dev_priv->fbdev = fbdev; - fbdev->psb_fb_helper.funcs = &psb_fb_helper_funcs; + + drm_fb_helper_prepare(dev, &fbdev->psb_fb_helper, &psb_fb_helper_funcs); drm_fb_helper_init(dev, &fbdev->psb_fb_helper, dev_priv->ops->crtcs, INTELFB_CONN_LIMIT); diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index c942d13cd5cb..347d16220cd0 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -623,7 +623,8 @@ int intel_fbdev_init(struct drm_device *dev) if (ifbdev == NULL) return -ENOMEM; - ifbdev->helper.funcs = &intel_fb_helper_funcs; + drm_fb_helper_prepare(dev, &ifbdev->helper, &intel_fb_helper_funcs); + if (!intel_fbdev_init_bios(dev, ifbdev)) ifbdev->preferred_bpp = 32; diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c index a4319aba9180..5451dc58eff1 100644 --- a/drivers/gpu/drm/mgag200/mgag200_fb.c +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c @@ -293,9 +293,10 @@ int mgag200_fbdev_init(struct mga_device *mdev) return -ENOMEM; mdev->mfbdev = mfbdev; - mfbdev->helper.funcs = &mga_fb_helper_funcs; spin_lock_init(&mfbdev->dirty_lock); + drm_fb_helper_prepare(mdev->dev, &mfbdev->helper, &mga_fb_helper_funcs); + ret = drm_fb_helper_init(mdev->dev, &mfbdev->helper, mdev->num_crtc, MGAG200FB_CONN_LIMIT); if (ret) diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c index 7e706c4cf033..c437065933e3 100644 --- a/drivers/gpu/drm/msm/msm_fbdev.c +++ b/drivers/gpu/drm/msm/msm_fbdev.c @@ -197,7 +197,7 @@ struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev) helper = &fbdev->base; - helper->funcs = &msm_fb_helper_funcs; + drm_fb_helper_prepare(dev, helper, &msm_fb_helper_funcs); ret = drm_fb_helper_init(dev, helper, priv->num_crtcs, priv->num_connectors); diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index 8e9c07b7fc89..afe706a20f97 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -464,7 +464,8 @@ nouveau_fbcon_init(struct drm_device *dev) fbcon->dev = dev; drm->fbcon = fbcon; - fbcon->helper.funcs = &nouveau_fbcon_helper_funcs; + + drm_fb_helper_prepare(dev, &fbcon->helper, &nouveau_fbcon_helper_funcs); ret = drm_fb_helper_init(dev, &fbcon->helper, dev->mode_config.num_crtc, 4); diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c index 4cb12083eb12..8436c6857cda 100644 --- a/drivers/gpu/drm/omapdrm/omap_fbdev.c +++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c @@ -325,7 +325,7 @@ struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev) helper = &fbdev->base; - helper->funcs = &omap_fb_helper_funcs; + drm_fb_helper_prepare(dev, helper, &omap_fb_helper_funcs); ret = drm_fb_helper_init(dev, helper, priv->num_crtcs, priv->num_connectors); diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c index cf89614c72be..df567888bb1e 100644 --- a/drivers/gpu/drm/qxl/qxl_fb.c +++ b/drivers/gpu/drm/qxl/qxl_fb.c @@ -676,9 +676,12 @@ int qxl_fbdev_init(struct qxl_device *qdev) qfbdev->qdev = qdev; qdev->mode_info.qfbdev = qfbdev; - qfbdev->helper.funcs = &qxl_fb_helper_funcs; spin_lock_init(&qfbdev->delayed_ops_lock); INIT_LIST_HEAD(&qfbdev->delayed_ops); + + drm_fb_helper_prepare(qdev->ddev, &qfbdev->helper, + &qxl_fb_helper_funcs); + ret = drm_fb_helper_init(qdev->ddev, &qfbdev->helper, qxl_num_crtc /* num_crtc - QXL supports just 1 */, QXLFB_CONN_LIMIT); diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index ad97afdbc4c7..db598d712901 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -353,7 +353,9 @@ int radeon_fbdev_init(struct radeon_device *rdev) rfbdev->rdev = rdev; rdev->mode_info.rfbdev = rfbdev; - rfbdev->helper.funcs = &radeon_fb_helper_funcs; + + drm_fb_helper_prepare(rdev->ddev, &rfbdev->helper, + &radeon_fb_helper_funcs); ret = drm_fb_helper_init(rdev->ddev, &rfbdev->helper, rdev->num_crtc, diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index f7cf47bf0afb..d5d53aa79ced 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -276,7 +276,6 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm, unsigned int num_crtc, unsigned int max_connectors) { - struct drm_fb_helper *helper; struct tegra_fbdev *fbdev; int err; @@ -286,8 +285,7 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm, return ERR_PTR(-ENOMEM); } - fbdev->base.funcs = &tegra_fb_helper_funcs; - helper = &fbdev->base; + drm_fb_helper_prepare(drm, &fbdev->base, &tegra_fb_helper_funcs); err = drm_fb_helper_init(drm, &fbdev->base, num_crtc, max_connectors); if (err < 0) { diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index 0647c8cc368b..d1da339843ca 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -583,7 +583,8 @@ int udl_fbdev_init(struct drm_device *dev) return -ENOMEM; udl->fbdev = ufbdev; - ufbdev->helper.funcs = &udl_fb_helper_funcs; + + drm_fb_helper_prepare(dev, &ufbdev->helper, &udl_fb_helper_funcs); ret = drm_fb_helper_init(dev, &ufbdev->helper, 1, 1); diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index a47df7ca38e8..1cf587f1f927 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -97,6 +97,8 @@ struct drm_fb_helper { bool delayed_hotplug; }; +void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper, + const struct drm_fb_helper_funcs *funcs); int drm_fb_helper_init(struct drm_device *dev, struct drm_fb_helper *helper, int crtc_count, int max_conn); -- cgit v1.2.3 From 2390cd11bfbe8d2b1b28c4e0f01fe7e122f7196d Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 5 Jun 2014 14:01:29 +1000 Subject: drm/crtc: add interface to reinitialise the legacy mode group This can be called to update things after dynamic connectors/encoders are created/deleted. Reviewed-by: Todd Previte Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_crtc.c | 9 +++++++++ include/drm/drm_crtc.h | 1 + 2 files changed, 10 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 7db6251c38e7..f939b058953c 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1513,6 +1513,15 @@ int drm_mode_group_init_legacy_group(struct drm_device *dev, } EXPORT_SYMBOL(drm_mode_group_init_legacy_group); +void drm_reinit_primary_mode_group(struct drm_device *dev) +{ + drm_modeset_lock_all(dev); + drm_mode_group_destroy(&dev->primary->mode_group); + drm_mode_group_init_legacy_group(dev, &dev->primary->mode_group); + drm_modeset_unlock_all(dev); +} +EXPORT_SYMBOL(drm_reinit_primary_mode_group); + /** * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo * @out: drm_mode_modeinfo struct to return to the user diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 857bbb1551f8..eb0f6f36385e 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -942,6 +942,7 @@ extern const char *drm_get_tv_select_name(int val); extern void drm_fb_release(struct drm_file *file_priv); extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group); extern void drm_mode_group_destroy(struct drm_mode_group *group); +extern void drm_reinit_primary_mode_group(struct drm_device *dev); extern bool drm_probe_ddc(struct i2c_adapter *adapter); extern struct edid *drm_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter); -- cgit v1.2.3 From 65c2a89c30ed63db15fbbdba41b74e5f47278897 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 5 Jun 2014 14:01:30 +1000 Subject: drm/fb_helper: allow adding/removing connectors later This is required to get fbcon probing to work on new connectors, callers should acquire the mode config lock before calling these. Reviewed-by: Todd Previte Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_fb_helper.c | 53 +++++++++++++++++++++++++++++++++++++++++ include/drm/drm_fb_helper.h | 4 ++++ 2 files changed, 57 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index bdee6eb0b24a..3144db9dc0f1 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -119,6 +119,58 @@ fail: } EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors); +int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_connector *connector) +{ + struct drm_fb_helper_connector **temp; + struct drm_fb_helper_connector *fb_helper_connector; + + WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex)); + if (fb_helper->connector_count + 1 > fb_helper->connector_info_alloc_count) { + temp = krealloc(fb_helper->connector_info, sizeof(struct drm_fb_helper_connector) * (fb_helper->connector_count + 1), GFP_KERNEL); + if (!temp) + return -ENOMEM; + + fb_helper->connector_info_alloc_count = fb_helper->connector_count + 1; + fb_helper->connector_info = temp; + } + + + fb_helper_connector = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL); + if (!fb_helper_connector) + return -ENOMEM; + + fb_helper_connector->connector = connector; + fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector; + return 0; +} +EXPORT_SYMBOL(drm_fb_helper_add_one_connector); + +int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, + struct drm_connector *connector) +{ + struct drm_fb_helper_connector *fb_helper_connector; + int i, j; + + WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex)); + + for (i = 0; i < fb_helper->connector_count; i++) { + if (fb_helper->connector_info[i]->connector == connector) + break; + } + + if (i == fb_helper->connector_count) + return -EINVAL; + fb_helper_connector = fb_helper->connector_info[i]; + + for (j = i + 1; j < fb_helper->connector_count; j++) { + fb_helper->connector_info[j - 1] = fb_helper->connector_info[j]; + } + fb_helper->connector_count--; + kfree(fb_helper_connector); + return 0; +} +EXPORT_SYMBOL(drm_fb_helper_remove_one_connector); + static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper) { struct drm_fb_helper_connector *fb_helper_conn; @@ -596,6 +648,7 @@ int drm_fb_helper_init(struct drm_device *dev, kfree(fb_helper->crtc_info); return -ENOMEM; } + fb_helper->connector_info_alloc_count = dev->mode_config.num_connector; fb_helper->connector_count = 0; for (i = 0; i < crtc_count; i++) { diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 1cf587f1f927..bfd329d613c4 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -86,6 +86,7 @@ struct drm_fb_helper { int crtc_count; struct drm_fb_helper_crtc *crtc_info; int connector_count; + int connector_info_alloc_count; struct drm_fb_helper_connector **connector_info; const struct drm_fb_helper_funcs *funcs; struct fb_info *fbdev; @@ -130,4 +131,7 @@ struct drm_display_mode * drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn, int width, int height); +int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_connector *connector); +int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, + struct drm_connector *connector); #endif -- cgit v1.2.3 From 43aba7eb0dc052d84e88f3569ea3f030868d49b2 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 5 Jun 2014 14:01:31 +1000 Subject: drm: add a path blob property This property will be used by the MST code to provide userspace with a path to parse so it can recognise connectors around hotplugs. Reviewed-by: Todd Previte Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_crtc.c | 26 ++++++++++++++++++++++++++ include/drm/drm_crtc.h | 5 +++++ 2 files changed, 31 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index f939b058953c..bd7422676638 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1257,6 +1257,7 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev) { struct drm_property *edid; struct drm_property *dpms; + struct drm_property *dev_path; /* * Standard properties (apply to all connectors) @@ -1271,6 +1272,12 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev) ARRAY_SIZE(drm_dpms_enum_list)); dev->mode_config.dpms_property = dpms; + dev_path = drm_property_create(dev, + DRM_MODE_PROP_BLOB | + DRM_MODE_PROP_IMMUTABLE, + "PATH", 0); + dev->mode_config.path_property = dev_path; + return 0; } @@ -3755,6 +3762,25 @@ done: return ret; } +int drm_mode_connector_set_path_property(struct drm_connector *connector, + char *path) +{ + struct drm_device *dev = connector->dev; + int ret, size; + size = strlen(path) + 1; + + connector->path_blob_ptr = drm_property_create_blob(connector->dev, + size, path); + if (!connector->path_blob_ptr) + return -EINVAL; + + ret = drm_object_property_set_value(&connector->base, + dev->mode_config.path_property, + connector->path_blob_ptr->base.id); + return ret; +} +EXPORT_SYMBOL(drm_mode_connector_set_path_property); + /** * drm_mode_connector_update_edid_property - update the edid property of a connector * @connector: drm connector diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index eb0f6f36385e..4da62072701c 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -524,6 +524,8 @@ struct drm_connector { struct drm_property_blob *edid_blob_ptr; struct drm_object_properties properties; + struct drm_property_blob *path_blob_ptr; + uint8_t polled; /* DRM_CONNECTOR_POLL_* */ /* requested DPMS state */ @@ -803,6 +805,7 @@ struct drm_mode_config { struct list_head property_blob_list; struct drm_property *edid_property; struct drm_property *dpms_property; + struct drm_property *path_property; struct drm_property *plane_type_property; /* DVI-I properties */ @@ -952,6 +955,8 @@ extern void drm_mode_config_init(struct drm_device *dev); extern void drm_mode_config_reset(struct drm_device *dev); extern void drm_mode_config_cleanup(struct drm_device *dev); +extern int drm_mode_connector_set_path_property(struct drm_connector *connector, + char *path); extern int drm_mode_connector_update_edid_property(struct drm_connector *connector, struct edid *edid); -- cgit v1.2.3 From ad7f8a1f9ced7f049f9b66d588723f243a7034cd Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 5 Jun 2014 14:01:32 +1000 Subject: drm/helper: add Displayport multi-stream helper (v0.6) This is the initial import of the helper for displayport multistream. It consists of a topology manager, init/destroy/set mst state It supports DP 1.2 MST sideband msg protocol handler - via hpd irqs connector detect and edid retrieval interface. It supports i2c device over DP 1.2 sideband msg protocol (EDID reads only) bandwidth manager API via vcpi allocation and payload updating, along with a helper to check the ACT status. Objects: MST topology manager - one per toplevel MST capable GPU port - not sure if this should be higher level again MST branch unit - one instance per plugged branching unit - one at top of hierarchy - others hanging from ports MST port - one port per port reported by branching units, can have MST units hanging from them as well. Changes since initial posting: a) add a mutex responsbile for the queues, it locks the sideband and msg slots, and msgs to transmit state b) add worker to handle connection state change events, for MST device chaining and hotplug c) add a payload spinlock d) add path sideband msg support e) fixup enum path resources transmit f) reduce max dpcd msg to 16, as per DP1.2 spec. g) separate tx queue kicking from irq processing and move irq acking back to drivers. Changes since v0.2: a) reorganise code, b) drop ACT forcing code c) add connector naming interface using path property d) add topology dumper helper e) proper reference counting and lookup for ports and mstbs. f) move tx kicking into a workq g) add aux locking - this should be redone h) split teardown into two parts i) start working on documentation on interface. Changes since v0.3: a) vc payload locking and tracking fixes b) add hotplug callback into driver - replaces crazy return 1 scheme c) txmsg + mst branch device refcount fixes d) don't bail on mst shutdown if device is gone e) change irq handler to take all 4 bytes of SINK_COUNT + ESI vectors f) make DP payload updates timeout longer - observed on docking station redock g) add more info to debugfs dumper Changes since v0.4: a) suspend/resume support b) more debugging in debugfs Changes since v0.5: a) use byte * to avoid unnecessary stack usage b) fix num_sdp_streams interpretation. c) init payload state for unplug events d) remove lenovo dock sink count hack e) drop aux lock - post rebase f) call hotplug on port destroy TODO: misc features Reviewed-by: Todd Previte Signed-off-by: Dave Airlie --- Documentation/DocBook/drm.tmpl | 6 + drivers/gpu/drm/Makefile | 2 +- drivers/gpu/drm/drm_dp_mst_topology.c | 2712 +++++++++++++++++++++++++++++++++ include/drm/drm_dp_mst_helper.h | 509 +++++++ 4 files changed, 3228 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/drm_dp_mst_topology.c create mode 100644 include/drm/drm_dp_mst_helper.h (limited to 'include') diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index b314a42bb18c..4890d94ec062 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -2336,6 +2336,12 @@ void intel_crt_init(struct drm_device *dev) !Pdrivers/gpu/drm/drm_dp_helper.c dp helpers !Iinclude/drm/drm_dp_helper.h !Edrivers/gpu/drm/drm_dp_helper.c + + + Display Port MST Helper Functions Reference +!Pdrivers/gpu/drm/drm_dp_mst_topology.c dp mst helper +!Iinclude/drm/drm_dp_mst_helper.h +!Edrivers/gpu/drm/drm_dp_mst_topology.c EDID Helper Functions Reference diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index dd2ba4269740..af9a609861c2 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -24,7 +24,7 @@ drm-$(CONFIG_DRM_PANEL) += drm_panel.o drm-usb-y := drm_usb.o drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \ - drm_plane_helper.o + drm_plane_helper.o drm_dp_mst_topology.o drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o drm_kms_helper-$(CONFIG_DRM_KMS_FB_HELPER) += drm_fb_helper.o drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c new file mode 100644 index 000000000000..73bc773513f2 --- /dev/null +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -0,0 +1,2712 @@ +/* + * Copyright © 2014 Red Hat + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/** + * DOC: dp mst helper + * + * These functions contain parts of the DisplayPort 1.2a MultiStream Transport + * protocol. The helpers contain a topology manager and bandwidth manager. + * The helpers encapsulate the sending and received of sideband msgs. + */ +static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr, + char *buf); +static int test_calc_pbn_mode(void); + +static void drm_dp_put_port(struct drm_dp_mst_port *port); + +static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr, + int id, + struct drm_dp_payload *payload); + +static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, + int offset, int size, u8 *bytes); + +static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_branch *mstb); +static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_branch *mstb, + struct drm_dp_mst_port *port); +static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr, + u8 *guid); + +static int drm_dp_mst_register_i2c_bus(struct drm_dp_aux *aux); +static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_aux *aux); +static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr); +/* sideband msg handling */ +static u8 drm_dp_msg_header_crc4(const uint8_t *data, size_t num_nibbles) +{ + u8 bitmask = 0x80; + u8 bitshift = 7; + u8 array_index = 0; + int number_of_bits = num_nibbles * 4; + u8 remainder = 0; + + while (number_of_bits != 0) { + number_of_bits--; + remainder <<= 1; + remainder |= (data[array_index] & bitmask) >> bitshift; + bitmask >>= 1; + bitshift--; + if (bitmask == 0) { + bitmask = 0x80; + bitshift = 7; + array_index++; + } + if ((remainder & 0x10) == 0x10) + remainder ^= 0x13; + } + + number_of_bits = 4; + while (number_of_bits != 0) { + number_of_bits--; + remainder <<= 1; + if ((remainder & 0x10) != 0) + remainder ^= 0x13; + } + + return remainder; +} + +static u8 drm_dp_msg_data_crc4(const uint8_t *data, u8 number_of_bytes) +{ + u8 bitmask = 0x80; + u8 bitshift = 7; + u8 array_index = 0; + int number_of_bits = number_of_bytes * 8; + u16 remainder = 0; + + while (number_of_bits != 0) { + number_of_bits--; + remainder <<= 1; + remainder |= (data[array_index] & bitmask) >> bitshift; + bitmask >>= 1; + bitshift--; + if (bitmask == 0) { + bitmask = 0x80; + bitshift = 7; + array_index++; + } + if ((remainder & 0x100) == 0x100) + remainder ^= 0xd5; + } + + number_of_bits = 8; + while (number_of_bits != 0) { + number_of_bits--; + remainder <<= 1; + if ((remainder & 0x100) != 0) + remainder ^= 0xd5; + } + + return remainder & 0xff; +} +static inline u8 drm_dp_calc_sb_hdr_size(struct drm_dp_sideband_msg_hdr *hdr) +{ + u8 size = 3; + size += (hdr->lct / 2); + return size; +} + +static void drm_dp_encode_sideband_msg_hdr(struct drm_dp_sideband_msg_hdr *hdr, + u8 *buf, int *len) +{ + int idx = 0; + int i; + u8 crc4; + buf[idx++] = ((hdr->lct & 0xf) << 4) | (hdr->lcr & 0xf); + for (i = 0; i < (hdr->lct / 2); i++) + buf[idx++] = hdr->rad[i]; + buf[idx++] = (hdr->broadcast << 7) | (hdr->path_msg << 6) | + (hdr->msg_len & 0x3f); + buf[idx++] = (hdr->somt << 7) | (hdr->eomt << 6) | (hdr->seqno << 4); + + crc4 = drm_dp_msg_header_crc4(buf, (idx * 2) - 1); + buf[idx - 1] |= (crc4 & 0xf); + + *len = idx; +} + +static bool drm_dp_decode_sideband_msg_hdr(struct drm_dp_sideband_msg_hdr *hdr, + u8 *buf, int buflen, u8 *hdrlen) +{ + u8 crc4; + u8 len; + int i; + u8 idx; + if (buf[0] == 0) + return false; + len = 3; + len += ((buf[0] & 0xf0) >> 4) / 2; + if (len > buflen) + return false; + crc4 = drm_dp_msg_header_crc4(buf, (len * 2) - 1); + + if ((crc4 & 0xf) != (buf[len - 1] & 0xf)) { + DRM_DEBUG_KMS("crc4 mismatch 0x%x 0x%x\n", crc4, buf[len - 1]); + return false; + } + + hdr->lct = (buf[0] & 0xf0) >> 4; + hdr->lcr = (buf[0] & 0xf); + idx = 1; + for (i = 0; i < (hdr->lct / 2); i++) + hdr->rad[i] = buf[idx++]; + hdr->broadcast = (buf[idx] >> 7) & 0x1; + hdr->path_msg = (buf[idx] >> 6) & 0x1; + hdr->msg_len = buf[idx] & 0x3f; + idx++; + hdr->somt = (buf[idx] >> 7) & 0x1; + hdr->eomt = (buf[idx] >> 6) & 0x1; + hdr->seqno = (buf[idx] >> 4) & 0x1; + idx++; + *hdrlen = idx; + return true; +} + +static void drm_dp_encode_sideband_req(struct drm_dp_sideband_msg_req_body *req, + struct drm_dp_sideband_msg_tx *raw) +{ + int idx = 0; + int i; + u8 *buf = raw->msg; + buf[idx++] = req->req_type & 0x7f; + + switch (req->req_type) { + case DP_ENUM_PATH_RESOURCES: + buf[idx] = (req->u.port_num.port_number & 0xf) << 4; + idx++; + break; + case DP_ALLOCATE_PAYLOAD: + buf[idx] = (req->u.allocate_payload.port_number & 0xf) << 4 | + (req->u.allocate_payload.number_sdp_streams & 0xf); + idx++; + buf[idx] = (req->u.allocate_payload.vcpi & 0x7f); + idx++; + buf[idx] = (req->u.allocate_payload.pbn >> 8); + idx++; + buf[idx] = (req->u.allocate_payload.pbn & 0xff); + idx++; + for (i = 0; i < req->u.allocate_payload.number_sdp_streams / 2; i++) { + buf[idx] = ((req->u.allocate_payload.sdp_stream_sink[i * 2] & 0xf) << 4) | + (req->u.allocate_payload.sdp_stream_sink[i * 2 + 1] & 0xf); + idx++; + } + if (req->u.allocate_payload.number_sdp_streams & 1) { + i = req->u.allocate_payload.number_sdp_streams - 1; + buf[idx] = (req->u.allocate_payload.sdp_stream_sink[i] & 0xf) << 4; + idx++; + } + break; + case DP_QUERY_PAYLOAD: + buf[idx] = (req->u.query_payload.port_number & 0xf) << 4; + idx++; + buf[idx] = (req->u.query_payload.vcpi & 0x7f); + idx++; + break; + case DP_REMOTE_DPCD_READ: + buf[idx] = (req->u.dpcd_read.port_number & 0xf) << 4; + buf[idx] |= ((req->u.dpcd_read.dpcd_address & 0xf0000) >> 16) & 0xf; + idx++; + buf[idx] = (req->u.dpcd_read.dpcd_address & 0xff00) >> 8; + idx++; + buf[idx] = (req->u.dpcd_read.dpcd_address & 0xff); + idx++; + buf[idx] = (req->u.dpcd_read.num_bytes); + idx++; + break; + + case DP_REMOTE_DPCD_WRITE: + buf[idx] = (req->u.dpcd_write.port_number & 0xf) << 4; + buf[idx] |= ((req->u.dpcd_write.dpcd_address & 0xf0000) >> 16) & 0xf; + idx++; + buf[idx] = (req->u.dpcd_write.dpcd_address & 0xff00) >> 8; + idx++; + buf[idx] = (req->u.dpcd_write.dpcd_address & 0xff); + idx++; + buf[idx] = (req->u.dpcd_write.num_bytes); + idx++; + memcpy(&buf[idx], req->u.dpcd_write.bytes, req->u.dpcd_write.num_bytes); + idx += req->u.dpcd_write.num_bytes; + break; + case DP_REMOTE_I2C_READ: + buf[idx] = (req->u.i2c_read.port_number & 0xf) << 4; + buf[idx] |= (req->u.i2c_read.num_transactions & 0x3); + idx++; + for (i = 0; i < (req->u.i2c_read.num_transactions & 0x3); i++) { + buf[idx] = req->u.i2c_read.transactions[i].i2c_dev_id & 0x7f; + idx++; + buf[idx] = req->u.i2c_read.transactions[i].num_bytes; + idx++; + memcpy(&buf[idx], req->u.i2c_read.transactions[i].bytes, req->u.i2c_read.transactions[i].num_bytes); + idx += req->u.i2c_read.transactions[i].num_bytes; + + buf[idx] = (req->u.i2c_read.transactions[i].no_stop_bit & 0x1) << 5; + buf[idx] |= (req->u.i2c_read.transactions[i].i2c_transaction_delay & 0xf); + idx++; + } + buf[idx] = (req->u.i2c_read.read_i2c_device_id) & 0x7f; + idx++; + buf[idx] = (req->u.i2c_read.num_bytes_read); + idx++; + break; + + case DP_REMOTE_I2C_WRITE: + buf[idx] = (req->u.i2c_write.port_number & 0xf) << 4; + idx++; + buf[idx] = (req->u.i2c_write.write_i2c_device_id) & 0x7f; + idx++; + buf[idx] = (req->u.i2c_write.num_bytes); + idx++; + memcpy(&buf[idx], req->u.i2c_write.bytes, req->u.i2c_write.num_bytes); + idx += req->u.i2c_write.num_bytes; + break; + } + raw->cur_len = idx; +} + +static void drm_dp_crc_sideband_chunk_req(u8 *msg, u8 len) +{ + u8 crc4; + crc4 = drm_dp_msg_data_crc4(msg, len); + msg[len] = crc4; +} + +static void drm_dp_encode_sideband_reply(struct drm_dp_sideband_msg_reply_body *rep, + struct drm_dp_sideband_msg_tx *raw) +{ + int idx = 0; + u8 *buf = raw->msg; + + buf[idx++] = (rep->reply_type & 0x1) << 7 | (rep->req_type & 0x7f); + + raw->cur_len = idx; +} + +/* this adds a chunk of msg to the builder to get the final msg */ +static bool drm_dp_sideband_msg_build(struct drm_dp_sideband_msg_rx *msg, + u8 *replybuf, u8 replybuflen, bool hdr) +{ + int ret; + u8 crc4; + + if (hdr) { + u8 hdrlen; + struct drm_dp_sideband_msg_hdr recv_hdr; + ret = drm_dp_decode_sideband_msg_hdr(&recv_hdr, replybuf, replybuflen, &hdrlen); + if (ret == false) { + print_hex_dump(KERN_DEBUG, "failed hdr", DUMP_PREFIX_NONE, 16, 1, replybuf, replybuflen, false); + return false; + } + + /* get length contained in this portion */ + msg->curchunk_len = recv_hdr.msg_len; + msg->curchunk_hdrlen = hdrlen; + + /* we have already gotten an somt - don't bother parsing */ + if (recv_hdr.somt && msg->have_somt) + return false; + + if (recv_hdr.somt) { + memcpy(&msg->initial_hdr, &recv_hdr, sizeof(struct drm_dp_sideband_msg_hdr)); + msg->have_somt = true; + } + if (recv_hdr.eomt) + msg->have_eomt = true; + + /* copy the bytes for the remainder of this header chunk */ + msg->curchunk_idx = min(msg->curchunk_len, (u8)(replybuflen - hdrlen)); + memcpy(&msg->chunk[0], replybuf + hdrlen, msg->curchunk_idx); + } else { + memcpy(&msg->chunk[msg->curchunk_idx], replybuf, replybuflen); + msg->curchunk_idx += replybuflen; + } + + if (msg->curchunk_idx >= msg->curchunk_len) { + /* do CRC */ + crc4 = drm_dp_msg_data_crc4(msg->chunk, msg->curchunk_len - 1); + /* copy chunk into bigger msg */ + memcpy(&msg->msg[msg->curlen], msg->chunk, msg->curchunk_len - 1); + msg->curlen += msg->curchunk_len - 1; + } + return true; +} + +static bool drm_dp_sideband_parse_link_address(struct drm_dp_sideband_msg_rx *raw, + struct drm_dp_sideband_msg_reply_body *repmsg) +{ + int idx = 1; + int i; + memcpy(repmsg->u.link_addr.guid, &raw->msg[idx], 16); + idx += 16; + repmsg->u.link_addr.nports = raw->msg[idx] & 0xf; + idx++; + if (idx > raw->curlen) + goto fail_len; + for (i = 0; i < repmsg->u.link_addr.nports; i++) { + if (raw->msg[idx] & 0x80) + repmsg->u.link_addr.ports[i].input_port = 1; + + repmsg->u.link_addr.ports[i].peer_device_type = (raw->msg[idx] >> 4) & 0x7; + repmsg->u.link_addr.ports[i].port_number = (raw->msg[idx] & 0xf); + + idx++; + if (idx > raw->curlen) + goto fail_len; + repmsg->u.link_addr.ports[i].mcs = (raw->msg[idx] >> 7) & 0x1; + repmsg->u.link_addr.ports[i].ddps = (raw->msg[idx] >> 6) & 0x1; + if (repmsg->u.link_addr.ports[i].input_port == 0) + repmsg->u.link_addr.ports[i].legacy_device_plug_status = (raw->msg[idx] >> 5) & 0x1; + idx++; + if (idx > raw->curlen) + goto fail_len; + if (repmsg->u.link_addr.ports[i].input_port == 0) { + repmsg->u.link_addr.ports[i].dpcd_revision = (raw->msg[idx]); + idx++; + if (idx > raw->curlen) + goto fail_len; + memcpy(repmsg->u.link_addr.ports[i].peer_guid, &raw->msg[idx], 16); + idx += 16; + if (idx > raw->curlen) + goto fail_len; + repmsg->u.link_addr.ports[i].num_sdp_streams = (raw->msg[idx] >> 4) & 0xf; + repmsg->u.link_addr.ports[i].num_sdp_stream_sinks = (raw->msg[idx] & 0xf); + idx++; + + } + if (idx > raw->curlen) + goto fail_len; + } + + return true; +fail_len: + DRM_DEBUG_KMS("link address reply parse length fail %d %d\n", idx, raw->curlen); + return false; +} + +static bool drm_dp_sideband_parse_remote_dpcd_read(struct drm_dp_sideband_msg_rx *raw, + struct drm_dp_sideband_msg_reply_body *repmsg) +{ + int idx = 1; + repmsg->u.remote_dpcd_read_ack.port_number = raw->msg[idx] & 0xf; + idx++; + if (idx > raw->curlen) + goto fail_len; + repmsg->u.remote_dpcd_read_ack.num_bytes = raw->msg[idx]; + if (idx > raw->curlen) + goto fail_len; + + memcpy(repmsg->u.remote_dpcd_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_dpcd_read_ack.num_bytes); + return true; +fail_len: + DRM_DEBUG_KMS("link address reply parse length fail %d %d\n", idx, raw->curlen); + return false; +} + +static bool drm_dp_sideband_parse_remote_dpcd_write(struct drm_dp_sideband_msg_rx *raw, + struct drm_dp_sideband_msg_reply_body *repmsg) +{ + int idx = 1; + repmsg->u.remote_dpcd_write_ack.port_number = raw->msg[idx] & 0xf; + idx++; + if (idx > raw->curlen) + goto fail_len; + return true; +fail_len: + DRM_DEBUG_KMS("parse length fail %d %d\n", idx, raw->curlen); + return false; +} + +static bool drm_dp_sideband_parse_remote_i2c_read_ack(struct drm_dp_sideband_msg_rx *raw, + struct drm_dp_sideband_msg_reply_body *repmsg) +{ + int idx = 1; + + repmsg->u.remote_i2c_read_ack.port_number = (raw->msg[idx] & 0xf); + idx++; + if (idx > raw->curlen) + goto fail_len; + repmsg->u.remote_i2c_read_ack.num_bytes = raw->msg[idx]; + idx++; + /* TODO check */ + memcpy(repmsg->u.remote_i2c_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_i2c_read_ack.num_bytes); + return true; +fail_len: + DRM_DEBUG_KMS("remote i2c reply parse length fail %d %d\n", idx, raw->curlen); + return false; +} + +static bool drm_dp_sideband_parse_enum_path_resources_ack(struct drm_dp_sideband_msg_rx *raw, + struct drm_dp_sideband_msg_reply_body *repmsg) +{ + int idx = 1; + repmsg->u.path_resources.port_number = (raw->msg[idx] >> 4) & 0xf; + idx++; + if (idx > raw->curlen) + goto fail_len; + repmsg->u.path_resources.full_payload_bw_number = (raw->msg[idx] << 8) | (raw->msg[idx+1]); + idx += 2; + if (idx > raw->curlen) + goto fail_len; + repmsg->u.path_resources.avail_payload_bw_number = (raw->msg[idx] << 8) | (raw->msg[idx+1]); + idx += 2; + if (idx > raw->curlen) + goto fail_len; + return true; +fail_len: + DRM_DEBUG_KMS("enum resource parse length fail %d %d\n", idx, raw->curlen); + return false; +} + +static bool drm_dp_sideband_parse_allocate_payload_ack(struct drm_dp_sideband_msg_rx *raw, + struct drm_dp_sideband_msg_reply_body *repmsg) +{ + int idx = 1; + repmsg->u.allocate_payload.port_number = (raw->msg[idx] >> 4) & 0xf; + idx++; + if (idx > raw->curlen) + goto fail_len; + repmsg->u.allocate_payload.vcpi = raw->msg[idx]; + idx++; + if (idx > raw->curlen) + goto fail_len; + repmsg->u.allocate_payload.allocated_pbn = (raw->msg[idx] << 8) | (raw->msg[idx+1]); + idx += 2; + if (idx > raw->curlen) + goto fail_len; + return true; +fail_len: + DRM_DEBUG_KMS("allocate payload parse length fail %d %d\n", idx, raw->curlen); + return false; +} + +static bool drm_dp_sideband_parse_query_payload_ack(struct drm_dp_sideband_msg_rx *raw, + struct drm_dp_sideband_msg_reply_body *repmsg) +{ + int idx = 1; + repmsg->u.query_payload.port_number = (raw->msg[idx] >> 4) & 0xf; + idx++; + if (idx > raw->curlen) + goto fail_len; + repmsg->u.query_payload.allocated_pbn = (raw->msg[idx] << 8) | (raw->msg[idx + 1]); + idx += 2; + if (idx > raw->curlen) + goto fail_len; + return true; +fail_len: + DRM_DEBUG_KMS("query payload parse length fail %d %d\n", idx, raw->curlen); + return false; +} + +static bool drm_dp_sideband_parse_reply(struct drm_dp_sideband_msg_rx *raw, + struct drm_dp_sideband_msg_reply_body *msg) +{ + memset(msg, 0, sizeof(*msg)); + msg->reply_type = (raw->msg[0] & 0x80) >> 7; + msg->req_type = (raw->msg[0] & 0x7f); + + if (msg->reply_type) { + memcpy(msg->u.nak.guid, &raw->msg[1], 16); + msg->u.nak.reason = raw->msg[17]; + msg->u.nak.nak_data = raw->msg[18]; + return false; + } + + switch (msg->req_type) { + case DP_LINK_ADDRESS: + return drm_dp_sideband_parse_link_address(raw, msg); + case DP_QUERY_PAYLOAD: + return drm_dp_sideband_parse_query_payload_ack(raw, msg); + case DP_REMOTE_DPCD_READ: + return drm_dp_sideband_parse_remote_dpcd_read(raw, msg); + case DP_REMOTE_DPCD_WRITE: + return drm_dp_sideband_parse_remote_dpcd_write(raw, msg); + case DP_REMOTE_I2C_READ: + return drm_dp_sideband_parse_remote_i2c_read_ack(raw, msg); + case DP_ENUM_PATH_RESOURCES: + return drm_dp_sideband_parse_enum_path_resources_ack(raw, msg); + case DP_ALLOCATE_PAYLOAD: + return drm_dp_sideband_parse_allocate_payload_ack(raw, msg); + default: + DRM_ERROR("Got unknown reply 0x%02x\n", msg->req_type); + return false; + } +} + +static bool drm_dp_sideband_parse_connection_status_notify(struct drm_dp_sideband_msg_rx *raw, + struct drm_dp_sideband_msg_req_body *msg) +{ + int idx = 1; + + msg->u.conn_stat.port_number = (raw->msg[idx] & 0xf0) >> 4; + idx++; + if (idx > raw->curlen) + goto fail_len; + + memcpy(msg->u.conn_stat.guid, &raw->msg[idx], 16); + idx += 16; + if (idx > raw->curlen) + goto fail_len; + + msg->u.conn_stat.legacy_device_plug_status = (raw->msg[idx] >> 6) & 0x1; + msg->u.conn_stat.displayport_device_plug_status = (raw->msg[idx] >> 5) & 0x1; + msg->u.conn_stat.message_capability_status = (raw->msg[idx] >> 4) & 0x1; + msg->u.conn_stat.input_port = (raw->msg[idx] >> 3) & 0x1; + msg->u.conn_stat.peer_device_type = (raw->msg[idx] & 0x7); + idx++; + return true; +fail_len: + DRM_DEBUG_KMS("connection status reply parse length fail %d %d\n", idx, raw->curlen); + return false; +} + +static bool drm_dp_sideband_parse_resource_status_notify(struct drm_dp_sideband_msg_rx *raw, + struct drm_dp_sideband_msg_req_body *msg) +{ + int idx = 1; + + msg->u.resource_stat.port_number = (raw->msg[idx] & 0xf0) >> 4; + idx++; + if (idx > raw->curlen) + goto fail_len; + + memcpy(msg->u.resource_stat.guid, &raw->msg[idx], 16); + idx += 16; + if (idx > raw->curlen) + goto fail_len; + + msg->u.resource_stat.available_pbn = (raw->msg[idx] << 8) | (raw->msg[idx + 1]); + idx++; + return true; +fail_len: + DRM_DEBUG_KMS("resource status reply parse length fail %d %d\n", idx, raw->curlen); + return false; +} + +static bool drm_dp_sideband_parse_req(struct drm_dp_sideband_msg_rx *raw, + struct drm_dp_sideband_msg_req_body *msg) +{ + memset(msg, 0, sizeof(*msg)); + msg->req_type = (raw->msg[0] & 0x7f); + + switch (msg->req_type) { + case DP_CONNECTION_STATUS_NOTIFY: + return drm_dp_sideband_parse_connection_status_notify(raw, msg); + case DP_RESOURCE_STATUS_NOTIFY: + return drm_dp_sideband_parse_resource_status_notify(raw, msg); + default: + DRM_ERROR("Got unknown request 0x%02x\n", msg->req_type); + return false; + } +} + +static int build_dpcd_write(struct drm_dp_sideband_msg_tx *msg, u8 port_num, u32 offset, u8 num_bytes, u8 *bytes) +{ + struct drm_dp_sideband_msg_req_body req; + + req.req_type = DP_REMOTE_DPCD_WRITE; + req.u.dpcd_write.port_number = port_num; + req.u.dpcd_write.dpcd_address = offset; + req.u.dpcd_write.num_bytes = num_bytes; + req.u.dpcd_write.bytes = bytes; + drm_dp_encode_sideband_req(&req, msg); + + return 0; +} + +static int build_link_address(struct drm_dp_sideband_msg_tx *msg) +{ + struct drm_dp_sideband_msg_req_body req; + + req.req_type = DP_LINK_ADDRESS; + drm_dp_encode_sideband_req(&req, msg); + return 0; +} + +static int build_enum_path_resources(struct drm_dp_sideband_msg_tx *msg, int port_num) +{ + struct drm_dp_sideband_msg_req_body req; + + req.req_type = DP_ENUM_PATH_RESOURCES; + req.u.port_num.port_number = port_num; + drm_dp_encode_sideband_req(&req, msg); + msg->path_msg = true; + return 0; +} + +static int build_allocate_payload(struct drm_dp_sideband_msg_tx *msg, int port_num, + u8 vcpi, uint16_t pbn) +{ + struct drm_dp_sideband_msg_req_body req; + memset(&req, 0, sizeof(req)); + req.req_type = DP_ALLOCATE_PAYLOAD; + req.u.allocate_payload.port_number = port_num; + req.u.allocate_payload.vcpi = vcpi; + req.u.allocate_payload.pbn = pbn; + drm_dp_encode_sideband_req(&req, msg); + msg->path_msg = true; + return 0; +} + +static int drm_dp_mst_assign_payload_id(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_vcpi *vcpi) +{ + int ret; + + mutex_lock(&mgr->payload_lock); + ret = find_first_zero_bit(&mgr->payload_mask, mgr->max_payloads + 1); + if (ret > mgr->max_payloads) { + ret = -EINVAL; + DRM_DEBUG_KMS("out of payload ids %d\n", ret); + goto out_unlock; + } + + set_bit(ret, &mgr->payload_mask); + vcpi->vcpi = ret; + mgr->proposed_vcpis[ret - 1] = vcpi; +out_unlock: + mutex_unlock(&mgr->payload_lock); + return ret; +} + +static void drm_dp_mst_put_payload_id(struct drm_dp_mst_topology_mgr *mgr, + int id) +{ + if (id == 0) + return; + + mutex_lock(&mgr->payload_lock); + DRM_DEBUG_KMS("putting payload %d\n", id); + clear_bit(id, &mgr->payload_mask); + mgr->proposed_vcpis[id - 1] = NULL; + mutex_unlock(&mgr->payload_lock); +} + +static bool check_txmsg_state(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_sideband_msg_tx *txmsg) +{ + bool ret; + mutex_lock(&mgr->qlock); + ret = (txmsg->state == DRM_DP_SIDEBAND_TX_RX || + txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT); + mutex_unlock(&mgr->qlock); + return ret; +} + +static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb, + struct drm_dp_sideband_msg_tx *txmsg) +{ + struct drm_dp_mst_topology_mgr *mgr = mstb->mgr; + int ret; + + ret = wait_event_timeout(mgr->tx_waitq, + check_txmsg_state(mgr, txmsg), + (4 * HZ)); + mutex_lock(&mstb->mgr->qlock); + if (ret > 0) { + if (txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT) { + ret = -EIO; + goto out; + } + } else { + DRM_DEBUG_KMS("timedout msg send %p %d %d\n", txmsg, txmsg->state, txmsg->seqno); + + /* dump some state */ + ret = -EIO; + + /* remove from q */ + if (txmsg->state == DRM_DP_SIDEBAND_TX_QUEUED || + txmsg->state == DRM_DP_SIDEBAND_TX_START_SEND) { + list_del(&txmsg->next); + } + + if (txmsg->state == DRM_DP_SIDEBAND_TX_START_SEND || + txmsg->state == DRM_DP_SIDEBAND_TX_SENT) { + mstb->tx_slots[txmsg->seqno] = NULL; + } + } +out: + mutex_unlock(&mgr->qlock); + + return ret; +} + +static struct drm_dp_mst_branch *drm_dp_add_mst_branch_device(u8 lct, u8 *rad) +{ + struct drm_dp_mst_branch *mstb; + + mstb = kzalloc(sizeof(*mstb), GFP_KERNEL); + if (!mstb) + return NULL; + + mstb->lct = lct; + if (lct > 1) + memcpy(mstb->rad, rad, lct / 2); + INIT_LIST_HEAD(&mstb->ports); + kref_init(&mstb->kref); + return mstb; +} + +static void drm_dp_destroy_mst_branch_device(struct kref *kref) +{ + struct drm_dp_mst_branch *mstb = container_of(kref, struct drm_dp_mst_branch, kref); + struct drm_dp_mst_port *port, *tmp; + bool wake_tx = false; + + cancel_work_sync(&mstb->mgr->work); + + /* + * destroy all ports - don't need lock + * as there are no more references to the mst branch + * device at this point. + */ + list_for_each_entry_safe(port, tmp, &mstb->ports, next) { + list_del(&port->next); + drm_dp_put_port(port); + } + + /* drop any tx slots msg */ + mutex_lock(&mstb->mgr->qlock); + if (mstb->tx_slots[0]) { + mstb->tx_slots[0]->state = DRM_DP_SIDEBAND_TX_TIMEOUT; + mstb->tx_slots[0] = NULL; + wake_tx = true; + } + if (mstb->tx_slots[1]) { + mstb->tx_slots[1]->state = DRM_DP_SIDEBAND_TX_TIMEOUT; + mstb->tx_slots[1] = NULL; + wake_tx = true; + } + mutex_unlock(&mstb->mgr->qlock); + + if (wake_tx) + wake_up(&mstb->mgr->tx_waitq); + kfree(mstb); +} + +static void drm_dp_put_mst_branch_device(struct drm_dp_mst_branch *mstb) +{ + kref_put(&mstb->kref, drm_dp_destroy_mst_branch_device); +} + + +static void drm_dp_port_teardown_pdt(struct drm_dp_mst_port *port, int old_pdt) +{ + switch (old_pdt) { + case DP_PEER_DEVICE_DP_LEGACY_CONV: + case DP_PEER_DEVICE_SST_SINK: + /* remove i2c over sideband */ + drm_dp_mst_unregister_i2c_bus(&port->aux); + break; + case DP_PEER_DEVICE_MST_BRANCHING: + drm_dp_put_mst_branch_device(port->mstb); + port->mstb = NULL; + break; + } +} + +static void drm_dp_destroy_port(struct kref *kref) +{ + struct drm_dp_mst_port *port = container_of(kref, struct drm_dp_mst_port, kref); + struct drm_dp_mst_topology_mgr *mgr = port->mgr; + if (!port->input) { + port->vcpi.num_slots = 0; + if (port->connector) + (*port->mgr->cbs->destroy_connector)(mgr, port->connector); + drm_dp_port_teardown_pdt(port, port->pdt); + + if (!port->input && port->vcpi.vcpi > 0) + drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi); + } + kfree(port); + + (*mgr->cbs->hotplug)(mgr); +} + +static void drm_dp_put_port(struct drm_dp_mst_port *port) +{ + kref_put(&port->kref, drm_dp_destroy_port); +} + +static struct drm_dp_mst_branch *drm_dp_mst_get_validated_mstb_ref_locked(struct drm_dp_mst_branch *mstb, struct drm_dp_mst_branch *to_find) +{ + struct drm_dp_mst_port *port; + struct drm_dp_mst_branch *rmstb; + if (to_find == mstb) { + kref_get(&mstb->kref); + return mstb; + } + list_for_each_entry(port, &mstb->ports, next) { + if (port->mstb) { + rmstb = drm_dp_mst_get_validated_mstb_ref_locked(port->mstb, to_find); + if (rmstb) + return rmstb; + } + } + return NULL; +} + +static struct drm_dp_mst_branch *drm_dp_get_validated_mstb_ref(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_branch *mstb) +{ + struct drm_dp_mst_branch *rmstb = NULL; + mutex_lock(&mgr->lock); + if (mgr->mst_primary) + rmstb = drm_dp_mst_get_validated_mstb_ref_locked(mgr->mst_primary, mstb); + mutex_unlock(&mgr->lock); + return rmstb; +} + +static struct drm_dp_mst_port *drm_dp_mst_get_port_ref_locked(struct drm_dp_mst_branch *mstb, struct drm_dp_mst_port *to_find) +{ + struct drm_dp_mst_port *port, *mport; + + list_for_each_entry(port, &mstb->ports, next) { + if (port == to_find) { + kref_get(&port->kref); + return port; + } + if (port->mstb) { + mport = drm_dp_mst_get_port_ref_locked(port->mstb, to_find); + if (mport) + return mport; + } + } + return NULL; +} + +static struct drm_dp_mst_port *drm_dp_get_validated_port_ref(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port) +{ + struct drm_dp_mst_port *rport = NULL; + mutex_lock(&mgr->lock); + if (mgr->mst_primary) + rport = drm_dp_mst_get_port_ref_locked(mgr->mst_primary, port); + mutex_unlock(&mgr->lock); + return rport; +} + +static struct drm_dp_mst_port *drm_dp_get_port(struct drm_dp_mst_branch *mstb, u8 port_num) +{ + struct drm_dp_mst_port *port; + + list_for_each_entry(port, &mstb->ports, next) { + if (port->port_num == port_num) { + kref_get(&port->kref); + return port; + } + } + + return NULL; +} + +/* + * calculate a new RAD for this MST branch device + * if parent has an LCT of 2 then it has 1 nibble of RAD, + * if parent has an LCT of 3 then it has 2 nibbles of RAD, + */ +static u8 drm_dp_calculate_rad(struct drm_dp_mst_port *port, + u8 *rad) +{ + int lct = port->parent->lct; + int shift = 4; + int idx = lct / 2; + if (lct > 1) { + memcpy(rad, port->parent->rad, idx); + shift = (lct % 2) ? 4 : 0; + } else + rad[0] = 0; + + rad[idx] |= port->port_num << shift; + return lct + 1; +} + +/* + * return sends link address for new mstb + */ +static bool drm_dp_port_setup_pdt(struct drm_dp_mst_port *port) +{ + int ret; + u8 rad[6], lct; + bool send_link = false; + switch (port->pdt) { + case DP_PEER_DEVICE_DP_LEGACY_CONV: + case DP_PEER_DEVICE_SST_SINK: + /* add i2c over sideband */ + ret = drm_dp_mst_register_i2c_bus(&port->aux); + break; + case DP_PEER_DEVICE_MST_BRANCHING: + lct = drm_dp_calculate_rad(port, rad); + + port->mstb = drm_dp_add_mst_branch_device(lct, rad); + port->mstb->mgr = port->mgr; + port->mstb->port_parent = port; + + send_link = true; + break; + } + return send_link; +} + +static void drm_dp_check_port_guid(struct drm_dp_mst_branch *mstb, + struct drm_dp_mst_port *port) +{ + int ret; + if (port->dpcd_rev >= 0x12) { + port->guid_valid = drm_dp_validate_guid(mstb->mgr, port->guid); + if (!port->guid_valid) { + ret = drm_dp_send_dpcd_write(mstb->mgr, + port, + DP_GUID, + 16, port->guid); + port->guid_valid = true; + } + } +} + +static void build_mst_prop_path(struct drm_dp_mst_port *port, + struct drm_dp_mst_branch *mstb, + char *proppath) +{ + int i; + char temp[8]; + snprintf(proppath, 255, "mst:%d", mstb->mgr->conn_base_id); + for (i = 0; i < (mstb->lct - 1); i++) { + int shift = (i % 2) ? 0 : 4; + int port_num = mstb->rad[i / 2] >> shift; + snprintf(temp, 8, "-%d", port_num); + strncat(proppath, temp, 255); + } + snprintf(temp, 8, "-%d", port->port_num); + strncat(proppath, temp, 255); +} + +static void drm_dp_add_port(struct drm_dp_mst_branch *mstb, + struct device *dev, + struct drm_dp_link_addr_reply_port *port_msg) +{ + struct drm_dp_mst_port *port; + bool ret; + bool created = false; + int old_pdt = 0; + int old_ddps = 0; + port = drm_dp_get_port(mstb, port_msg->port_number); + if (!port) { + port = kzalloc(sizeof(*port), GFP_KERNEL); + if (!port) + return; + kref_init(&port->kref); + port->parent = mstb; + port->port_num = port_msg->port_number; + port->mgr = mstb->mgr; + port->aux.name = "DPMST"; + port->aux.dev = dev; + created = true; + } else { + old_pdt = port->pdt; + old_ddps = port->ddps; + } + + port->pdt = port_msg->peer_device_type; + port->input = port_msg->input_port; + port->mcs = port_msg->mcs; + port->ddps = port_msg->ddps; + port->ldps = port_msg->legacy_device_plug_status; + port->dpcd_rev = port_msg->dpcd_revision; + port->num_sdp_streams = port_msg->num_sdp_streams; + port->num_sdp_stream_sinks = port_msg->num_sdp_stream_sinks; + memcpy(port->guid, port_msg->peer_guid, 16); + + /* manage mstb port lists with mgr lock - take a reference + for this list */ + if (created) { + mutex_lock(&mstb->mgr->lock); + kref_get(&port->kref); + list_add(&port->next, &mstb->ports); + mutex_unlock(&mstb->mgr->lock); + } + + if (old_ddps != port->ddps) { + if (port->ddps) { + drm_dp_check_port_guid(mstb, port); + if (!port->input) + drm_dp_send_enum_path_resources(mstb->mgr, mstb, port); + } else { + port->guid_valid = false; + port->available_pbn = 0; + } + } + + if (old_pdt != port->pdt && !port->input) { + drm_dp_port_teardown_pdt(port, old_pdt); + + ret = drm_dp_port_setup_pdt(port); + if (ret == true) { + drm_dp_send_link_address(mstb->mgr, port->mstb); + port->mstb->link_address_sent = true; + } + } + + if (created && !port->input) { + char proppath[255]; + build_mst_prop_path(port, mstb, proppath); + port->connector = (*mstb->mgr->cbs->add_connector)(mstb->mgr, port, proppath); + } + + /* put reference to this port */ + drm_dp_put_port(port); +} + +static void drm_dp_update_port(struct drm_dp_mst_branch *mstb, + struct drm_dp_connection_status_notify *conn_stat) +{ + struct drm_dp_mst_port *port; + int old_pdt; + int old_ddps; + bool dowork = false; + port = drm_dp_get_port(mstb, conn_stat->port_number); + if (!port) + return; + + old_ddps = port->ddps; + old_pdt = port->pdt; + port->pdt = conn_stat->peer_device_type; + port->mcs = conn_stat->message_capability_status; + port->ldps = conn_stat->legacy_device_plug_status; + port->ddps = conn_stat->displayport_device_plug_status; + + if (old_ddps != port->ddps) { + if (port->ddps) { + drm_dp_check_port_guid(mstb, port); + dowork = true; + } else { + port->guid_valid = false; + port->available_pbn = 0; + } + } + if (old_pdt != port->pdt && !port->input) { + drm_dp_port_teardown_pdt(port, old_pdt); + + if (drm_dp_port_setup_pdt(port)) + dowork = true; + } + + drm_dp_put_port(port); + if (dowork) + queue_work(system_long_wq, &mstb->mgr->work); + +} + +static struct drm_dp_mst_branch *drm_dp_get_mst_branch_device(struct drm_dp_mst_topology_mgr *mgr, + u8 lct, u8 *rad) +{ + struct drm_dp_mst_branch *mstb; + struct drm_dp_mst_port *port; + int i; + /* find the port by iterating down */ + mstb = mgr->mst_primary; + + for (i = 0; i < lct - 1; i++) { + int shift = (i % 2) ? 0 : 4; + int port_num = rad[i / 2] >> shift; + + list_for_each_entry(port, &mstb->ports, next) { + if (port->port_num == port_num) { + if (!port->mstb) { + DRM_ERROR("failed to lookup MSTB with lct %d, rad %02x\n", lct, rad[0]); + return NULL; + } + + mstb = port->mstb; + break; + } + } + } + kref_get(&mstb->kref); + return mstb; +} + +static void drm_dp_check_and_send_link_address(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_branch *mstb) +{ + struct drm_dp_mst_port *port; + + if (!mstb->link_address_sent) { + drm_dp_send_link_address(mgr, mstb); + mstb->link_address_sent = true; + } + list_for_each_entry(port, &mstb->ports, next) { + if (port->input) + continue; + + if (!port->ddps) + continue; + + if (!port->available_pbn) + drm_dp_send_enum_path_resources(mgr, mstb, port); + + if (port->mstb) + drm_dp_check_and_send_link_address(mgr, port->mstb); + } +} + +static void drm_dp_mst_link_probe_work(struct work_struct *work) +{ + struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, work); + + drm_dp_check_and_send_link_address(mgr, mgr->mst_primary); + +} + +static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr, + u8 *guid) +{ + static u8 zero_guid[16]; + + if (!memcmp(guid, zero_guid, 16)) { + u64 salt = get_jiffies_64(); + memcpy(&guid[0], &salt, sizeof(u64)); + memcpy(&guid[8], &salt, sizeof(u64)); + return false; + } + return true; +} + +#if 0 +static int build_dpcd_read(struct drm_dp_sideband_msg_tx *msg, u8 port_num, u32 offset, u8 num_bytes) +{ + struct drm_dp_sideband_msg_req_body req; + + req.req_type = DP_REMOTE_DPCD_READ; + req.u.dpcd_read.port_number = port_num; + req.u.dpcd_read.dpcd_address = offset; + req.u.dpcd_read.num_bytes = num_bytes; + drm_dp_encode_sideband_req(&req, msg); + + return 0; +} +#endif + +static int drm_dp_send_sideband_msg(struct drm_dp_mst_topology_mgr *mgr, + bool up, u8 *msg, int len) +{ + int ret; + int regbase = up ? DP_SIDEBAND_MSG_UP_REP_BASE : DP_SIDEBAND_MSG_DOWN_REQ_BASE; + int tosend, total, offset; + int retries = 0; + +retry: + total = len; + offset = 0; + do { + tosend = min3(mgr->max_dpcd_transaction_bytes, 16, total); + + ret = drm_dp_dpcd_write(mgr->aux, regbase + offset, + &msg[offset], + tosend); + if (ret != tosend) { + if (ret == -EIO && retries < 5) { + retries++; + goto retry; + } + DRM_DEBUG_KMS("failed to dpcd write %d %d\n", tosend, ret); + WARN(1, "fail\n"); + + return -EIO; + } + offset += tosend; + total -= tosend; + } while (total > 0); + return 0; +} + +static int set_hdr_from_dst_qlock(struct drm_dp_sideband_msg_hdr *hdr, + struct drm_dp_sideband_msg_tx *txmsg) +{ + struct drm_dp_mst_branch *mstb = txmsg->dst; + + /* both msg slots are full */ + if (txmsg->seqno == -1) { + if (mstb->tx_slots[0] && mstb->tx_slots[1]) { + DRM_DEBUG_KMS("%s: failed to find slot\n", __func__); + return -EAGAIN; + } + if (mstb->tx_slots[0] == NULL && mstb->tx_slots[1] == NULL) { + txmsg->seqno = mstb->last_seqno; + mstb->last_seqno ^= 1; + } else if (mstb->tx_slots[0] == NULL) + txmsg->seqno = 0; + else + txmsg->seqno = 1; + mstb->tx_slots[txmsg->seqno] = txmsg; + } + hdr->broadcast = 0; + hdr->path_msg = txmsg->path_msg; + hdr->lct = mstb->lct; + hdr->lcr = mstb->lct - 1; + if (mstb->lct > 1) + memcpy(hdr->rad, mstb->rad, mstb->lct / 2); + hdr->seqno = txmsg->seqno; + return 0; +} +/* + * process a single block of the next message in the sideband queue + */ +static int process_single_tx_qlock(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_sideband_msg_tx *txmsg, + bool up) +{ + u8 chunk[48]; + struct drm_dp_sideband_msg_hdr hdr; + int len, space, idx, tosend; + int ret; + + if (txmsg->state == DRM_DP_SIDEBAND_TX_QUEUED) { + txmsg->seqno = -1; + txmsg->state = DRM_DP_SIDEBAND_TX_START_SEND; + } + + /* make hdr from dst mst - for replies use seqno + otherwise assign one */ + ret = set_hdr_from_dst_qlock(&hdr, txmsg); + if (ret < 0) + return ret; + + /* amount left to send in this message */ + len = txmsg->cur_len - txmsg->cur_offset; + + /* 48 - sideband msg size - 1 byte for data CRC, x header bytes */ + space = 48 - 1 - drm_dp_calc_sb_hdr_size(&hdr); + + tosend = min(len, space); + if (len == txmsg->cur_len) + hdr.somt = 1; + if (space >= len) + hdr.eomt = 1; + + + hdr.msg_len = tosend + 1; + drm_dp_encode_sideband_msg_hdr(&hdr, chunk, &idx); + memcpy(&chunk[idx], &txmsg->msg[txmsg->cur_offset], tosend); + /* add crc at end */ + drm_dp_crc_sideband_chunk_req(&chunk[idx], tosend); + idx += tosend + 1; + + ret = drm_dp_send_sideband_msg(mgr, up, chunk, idx); + if (ret) { + DRM_DEBUG_KMS("sideband msg failed to send\n"); + return ret; + } + + txmsg->cur_offset += tosend; + if (txmsg->cur_offset == txmsg->cur_len) { + txmsg->state = DRM_DP_SIDEBAND_TX_SENT; + return 1; + } + return 0; +} + +/* must be called holding qlock */ +static void process_single_down_tx_qlock(struct drm_dp_mst_topology_mgr *mgr) +{ + struct drm_dp_sideband_msg_tx *txmsg; + int ret; + + /* construct a chunk from the first msg in the tx_msg queue */ + if (list_empty(&mgr->tx_msg_downq)) { + mgr->tx_down_in_progress = false; + return; + } + mgr->tx_down_in_progress = true; + + txmsg = list_first_entry(&mgr->tx_msg_downq, struct drm_dp_sideband_msg_tx, next); + ret = process_single_tx_qlock(mgr, txmsg, false); + if (ret == 1) { + /* txmsg is sent it should be in the slots now */ + list_del(&txmsg->next); + } else if (ret) { + DRM_DEBUG_KMS("failed to send msg in q %d\n", ret); + list_del(&txmsg->next); + if (txmsg->seqno != -1) + txmsg->dst->tx_slots[txmsg->seqno] = NULL; + txmsg->state = DRM_DP_SIDEBAND_TX_TIMEOUT; + wake_up(&mgr->tx_waitq); + } + if (list_empty(&mgr->tx_msg_downq)) { + mgr->tx_down_in_progress = false; + return; + } +} + +/* called holding qlock */ +static void process_single_up_tx_qlock(struct drm_dp_mst_topology_mgr *mgr) +{ + struct drm_dp_sideband_msg_tx *txmsg; + int ret; + + /* construct a chunk from the first msg in the tx_msg queue */ + if (list_empty(&mgr->tx_msg_upq)) { + mgr->tx_up_in_progress = false; + return; + } + + txmsg = list_first_entry(&mgr->tx_msg_upq, struct drm_dp_sideband_msg_tx, next); + ret = process_single_tx_qlock(mgr, txmsg, true); + if (ret == 1) { + /* up txmsgs aren't put in slots - so free after we send it */ + list_del(&txmsg->next); + kfree(txmsg); + } else if (ret) + DRM_DEBUG_KMS("failed to send msg in q %d\n", ret); + mgr->tx_up_in_progress = true; +} + +static void drm_dp_queue_down_tx(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_sideband_msg_tx *txmsg) +{ + mutex_lock(&mgr->qlock); + list_add_tail(&txmsg->next, &mgr->tx_msg_downq); + if (!mgr->tx_down_in_progress) + process_single_down_tx_qlock(mgr); + mutex_unlock(&mgr->qlock); +} + +static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_branch *mstb) +{ + int len; + struct drm_dp_sideband_msg_tx *txmsg; + int ret; + + txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); + if (!txmsg) + return -ENOMEM; + + txmsg->dst = mstb; + len = build_link_address(txmsg); + + drm_dp_queue_down_tx(mgr, txmsg); + + ret = drm_dp_mst_wait_tx_reply(mstb, txmsg); + if (ret > 0) { + int i; + + if (txmsg->reply.reply_type == 1) + DRM_DEBUG_KMS("link address nak received\n"); + else { + DRM_DEBUG_KMS("link address reply: %d\n", txmsg->reply.u.link_addr.nports); + for (i = 0; i < txmsg->reply.u.link_addr.nports; i++) { + DRM_DEBUG_KMS("port %d: input %d, pdt: %d, pn: %d, dpcd_rev: %02x, mcs: %d, ddps: %d, ldps %d, sdp %d/%d\n", i, + txmsg->reply.u.link_addr.ports[i].input_port, + txmsg->reply.u.link_addr.ports[i].peer_device_type, + txmsg->reply.u.link_addr.ports[i].port_number, + txmsg->reply.u.link_addr.ports[i].dpcd_revision, + txmsg->reply.u.link_addr.ports[i].mcs, + txmsg->reply.u.link_addr.ports[i].ddps, + txmsg->reply.u.link_addr.ports[i].legacy_device_plug_status, + txmsg->reply.u.link_addr.ports[i].num_sdp_streams, + txmsg->reply.u.link_addr.ports[i].num_sdp_stream_sinks); + } + for (i = 0; i < txmsg->reply.u.link_addr.nports; i++) { + drm_dp_add_port(mstb, mgr->dev, &txmsg->reply.u.link_addr.ports[i]); + } + (*mgr->cbs->hotplug)(mgr); + } + } else + DRM_DEBUG_KMS("link address failed %d\n", ret); + + kfree(txmsg); + return 0; +} + +static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_branch *mstb, + struct drm_dp_mst_port *port) +{ + int len; + struct drm_dp_sideband_msg_tx *txmsg; + int ret; + + txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); + if (!txmsg) + return -ENOMEM; + + txmsg->dst = mstb; + len = build_enum_path_resources(txmsg, port->port_num); + + drm_dp_queue_down_tx(mgr, txmsg); + + ret = drm_dp_mst_wait_tx_reply(mstb, txmsg); + if (ret > 0) { + if (txmsg->reply.reply_type == 1) + DRM_DEBUG_KMS("enum path resources nak received\n"); + else { + if (port->port_num != txmsg->reply.u.path_resources.port_number) + DRM_ERROR("got incorrect port in response\n"); + DRM_DEBUG_KMS("enum path resources %d: %d %d\n", txmsg->reply.u.path_resources.port_number, txmsg->reply.u.path_resources.full_payload_bw_number, + txmsg->reply.u.path_resources.avail_payload_bw_number); + port->available_pbn = txmsg->reply.u.path_resources.avail_payload_bw_number; + } + } + + kfree(txmsg); + return 0; +} + +int drm_dp_payload_send_msg(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, + int id, + int pbn) +{ + struct drm_dp_sideband_msg_tx *txmsg; + struct drm_dp_mst_branch *mstb; + int len, ret; + + mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent); + if (!mstb) + return -EINVAL; + + txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); + if (!txmsg) { + ret = -ENOMEM; + goto fail_put; + } + + txmsg->dst = mstb; + len = build_allocate_payload(txmsg, port->port_num, + id, + pbn); + + drm_dp_queue_down_tx(mgr, txmsg); + + ret = drm_dp_mst_wait_tx_reply(mstb, txmsg); + if (ret > 0) { + if (txmsg->reply.reply_type == 1) { + ret = -EINVAL; + } else + ret = 0; + } + kfree(txmsg); +fail_put: + drm_dp_put_mst_branch_device(mstb); + return ret; +} + +static int drm_dp_create_payload_step1(struct drm_dp_mst_topology_mgr *mgr, + int id, + struct drm_dp_payload *payload) +{ + int ret; + + ret = drm_dp_dpcd_write_payload(mgr, id, payload); + if (ret < 0) { + payload->payload_state = 0; + return ret; + } + payload->payload_state = DP_PAYLOAD_LOCAL; + return 0; +} + +int drm_dp_create_payload_step2(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, + int id, + struct drm_dp_payload *payload) +{ + int ret; + ret = drm_dp_payload_send_msg(mgr, port, id, port->vcpi.pbn); + if (ret < 0) + return ret; + payload->payload_state = DP_PAYLOAD_REMOTE; + return ret; +} + +int drm_dp_destroy_payload_step1(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, + int id, + struct drm_dp_payload *payload) +{ + DRM_DEBUG_KMS("\n"); + /* its okay for these to fail */ + if (port) { + drm_dp_payload_send_msg(mgr, port, id, 0); + } + + drm_dp_dpcd_write_payload(mgr, id, payload); + payload->payload_state = 0; + return 0; +} + +int drm_dp_destroy_payload_step2(struct drm_dp_mst_topology_mgr *mgr, + int id, + struct drm_dp_payload *payload) +{ + payload->payload_state = 0; + return 0; +} + +/** + * drm_dp_update_payload_part1() - Execute payload update part 1 + * @mgr: manager to use. + * + * This iterates over all proposed virtual channels, and tries to + * allocate space in the link for them. For 0->slots transitions, + * this step just writes the VCPI to the MST device. For slots->0 + * transitions, this writes the updated VCPIs and removes the + * remote VC payloads. + * + * after calling this the driver should generate ACT and payload + * packets. + */ +int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr) +{ + int i; + int cur_slots = 1; + struct drm_dp_payload req_payload; + struct drm_dp_mst_port *port; + + mutex_lock(&mgr->payload_lock); + for (i = 0; i < mgr->max_payloads; i++) { + /* solve the current payloads - compare to the hw ones + - update the hw view */ + req_payload.start_slot = cur_slots; + if (mgr->proposed_vcpis[i]) { + port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi); + req_payload.num_slots = mgr->proposed_vcpis[i]->num_slots; + } else { + port = NULL; + req_payload.num_slots = 0; + } + /* work out what is required to happen with this payload */ + if (mgr->payloads[i].start_slot != req_payload.start_slot || + mgr->payloads[i].num_slots != req_payload.num_slots) { + + /* need to push an update for this payload */ + if (req_payload.num_slots) { + drm_dp_create_payload_step1(mgr, i + 1, &req_payload); + mgr->payloads[i].num_slots = req_payload.num_slots; + } else if (mgr->payloads[i].num_slots) { + mgr->payloads[i].num_slots = 0; + drm_dp_destroy_payload_step1(mgr, port, i + 1, &mgr->payloads[i]); + req_payload.payload_state = mgr->payloads[i].payload_state; + } else + req_payload.payload_state = 0; + + mgr->payloads[i].start_slot = req_payload.start_slot; + mgr->payloads[i].payload_state = req_payload.payload_state; + } + cur_slots += req_payload.num_slots; + } + mutex_unlock(&mgr->payload_lock); + + return 0; +} +EXPORT_SYMBOL(drm_dp_update_payload_part1); + +/** + * drm_dp_update_payload_part2() - Execute payload update part 2 + * @mgr: manager to use. + * + * This iterates over all proposed virtual channels, and tries to + * allocate space in the link for them. For 0->slots transitions, + * this step writes the remote VC payload commands. For slots->0 + * this just resets some internal state. + */ +int drm_dp_update_payload_part2(struct drm_dp_mst_topology_mgr *mgr) +{ + struct drm_dp_mst_port *port; + int i; + int ret; + mutex_lock(&mgr->payload_lock); + for (i = 0; i < mgr->max_payloads; i++) { + + if (!mgr->proposed_vcpis[i]) + continue; + + port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi); + + DRM_DEBUG_KMS("payload %d %d\n", i, mgr->payloads[i].payload_state); + if (mgr->payloads[i].payload_state == DP_PAYLOAD_LOCAL) { + ret = drm_dp_create_payload_step2(mgr, port, i + 1, &mgr->payloads[i]); + } else if (mgr->payloads[i].payload_state == DP_PAYLOAD_DELETE_LOCAL) { + ret = drm_dp_destroy_payload_step2(mgr, i + 1, &mgr->payloads[i]); + } + if (ret) { + mutex_unlock(&mgr->payload_lock); + return ret; + } + } + mutex_unlock(&mgr->payload_lock); + return 0; +} +EXPORT_SYMBOL(drm_dp_update_payload_part2); + +#if 0 /* unused as of yet */ +static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, + int offset, int size) +{ + int len; + struct drm_dp_sideband_msg_tx *txmsg; + + txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); + if (!txmsg) + return -ENOMEM; + + len = build_dpcd_read(txmsg, port->port_num, 0, 8); + txmsg->dst = port->parent; + + drm_dp_queue_down_tx(mgr, txmsg); + + return 0; +} +#endif + +static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, + int offset, int size, u8 *bytes) +{ + int len; + int ret; + struct drm_dp_sideband_msg_tx *txmsg; + struct drm_dp_mst_branch *mstb; + + mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent); + if (!mstb) + return -EINVAL; + + txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); + if (!txmsg) { + ret = -ENOMEM; + goto fail_put; + } + + len = build_dpcd_write(txmsg, port->port_num, offset, size, bytes); + txmsg->dst = mstb; + + drm_dp_queue_down_tx(mgr, txmsg); + + ret = drm_dp_mst_wait_tx_reply(mstb, txmsg); + if (ret > 0) { + if (txmsg->reply.reply_type == 1) { + ret = -EINVAL; + } else + ret = 0; + } + kfree(txmsg); +fail_put: + drm_dp_put_mst_branch_device(mstb); + return ret; +} + +static int drm_dp_encode_up_ack_reply(struct drm_dp_sideband_msg_tx *msg, u8 req_type) +{ + struct drm_dp_sideband_msg_reply_body reply; + + reply.reply_type = 1; + reply.req_type = req_type; + drm_dp_encode_sideband_reply(&reply, msg); + return 0; +} + +static int drm_dp_send_up_ack_reply(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_branch *mstb, + int req_type, int seqno, bool broadcast) +{ + struct drm_dp_sideband_msg_tx *txmsg; + + txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); + if (!txmsg) + return -ENOMEM; + + txmsg->dst = mstb; + txmsg->seqno = seqno; + drm_dp_encode_up_ack_reply(txmsg, req_type); + + mutex_lock(&mgr->qlock); + list_add_tail(&txmsg->next, &mgr->tx_msg_upq); + if (!mgr->tx_up_in_progress) { + process_single_up_tx_qlock(mgr); + } + mutex_unlock(&mgr->qlock); + return 0; +} + +static int drm_dp_get_vc_payload_bw(int dp_link_bw, int dp_link_count) +{ + switch (dp_link_bw) { + case DP_LINK_BW_1_62: + return 3 * dp_link_count; + case DP_LINK_BW_2_7: + return 5 * dp_link_count; + case DP_LINK_BW_5_4: + return 10 * dp_link_count; + } + return 0; +} + +/** + * drm_dp_mst_topology_mgr_set_mst() - Set the MST state for a topology manager + * @mgr: manager to set state for + * @mst_state: true to enable MST on this connector - false to disable. + * + * This is called by the driver when it detects an MST capable device plugged + * into a DP MST capable port, or when a DP MST capable device is unplugged. + */ +int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool mst_state) +{ + int ret = 0; + struct drm_dp_mst_branch *mstb = NULL; + + mutex_lock(&mgr->lock); + if (mst_state == mgr->mst_state) + goto out_unlock; + + mgr->mst_state = mst_state; + /* set the device into MST mode */ + if (mst_state) { + WARN_ON(mgr->mst_primary); + + /* get dpcd info */ + ret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, mgr->dpcd, DP_RECEIVER_CAP_SIZE); + if (ret != DP_RECEIVER_CAP_SIZE) { + DRM_DEBUG_KMS("failed to read DPCD\n"); + goto out_unlock; + } + + mgr->pbn_div = drm_dp_get_vc_payload_bw(mgr->dpcd[1], mgr->dpcd[2] & DP_MAX_LANE_COUNT_MASK); + mgr->total_pbn = 2560; + mgr->total_slots = DIV_ROUND_UP(mgr->total_pbn, mgr->pbn_div); + mgr->avail_slots = mgr->total_slots; + + /* add initial branch device at LCT 1 */ + mstb = drm_dp_add_mst_branch_device(1, NULL); + if (mstb == NULL) { + ret = -ENOMEM; + goto out_unlock; + } + mstb->mgr = mgr; + + /* give this the main reference */ + mgr->mst_primary = mstb; + kref_get(&mgr->mst_primary->kref); + + { + struct drm_dp_payload reset_pay; + reset_pay.start_slot = 0; + reset_pay.num_slots = 0x3f; + drm_dp_dpcd_write_payload(mgr, 0, &reset_pay); + } + + ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, + DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC); + if (ret < 0) { + goto out_unlock; + } + + + /* sort out guid */ + ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, mgr->guid, 16); + if (ret != 16) { + DRM_DEBUG_KMS("failed to read DP GUID %d\n", ret); + goto out_unlock; + } + + mgr->guid_valid = drm_dp_validate_guid(mgr, mgr->guid); + if (!mgr->guid_valid) { + ret = drm_dp_dpcd_write(mgr->aux, DP_GUID, mgr->guid, 16); + mgr->guid_valid = true; + } + + queue_work(system_long_wq, &mgr->work); + + ret = 0; + } else { + /* disable MST on the device */ + mstb = mgr->mst_primary; + mgr->mst_primary = NULL; + /* this can fail if the device is gone */ + drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, 0); + ret = 0; + memset(mgr->payloads, 0, mgr->max_payloads * sizeof(struct drm_dp_payload)); + mgr->payload_mask = 0; + set_bit(0, &mgr->payload_mask); + } + +out_unlock: + mutex_unlock(&mgr->lock); + if (mstb) + drm_dp_put_mst_branch_device(mstb); + return ret; + +} +EXPORT_SYMBOL(drm_dp_mst_topology_mgr_set_mst); + +/** + * drm_dp_mst_topology_mgr_suspend() - suspend the MST manager + * @mgr: manager to suspend + * + * This function tells the MST device that we can't handle UP messages + * anymore. This should stop it from sending any since we are suspended. + */ +void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr) +{ + mutex_lock(&mgr->lock); + drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, + DP_MST_EN | DP_UPSTREAM_IS_SRC); + mutex_unlock(&mgr->lock); +} +EXPORT_SYMBOL(drm_dp_mst_topology_mgr_suspend); + +/** + * drm_dp_mst_topology_mgr_resume() - resume the MST manager + * @mgr: manager to resume + * + * This will fetch DPCD and see if the device is still there, + * if it is, it will rewrite the MSTM control bits, and return. + * + * if the device fails this returns -1, and the driver should do + * a full MST reprobe, in case we were undocked. + */ +int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr) +{ + int ret = 0; + + mutex_lock(&mgr->lock); + + if (mgr->mst_primary) { + int sret; + sret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, mgr->dpcd, DP_RECEIVER_CAP_SIZE); + if (sret != DP_RECEIVER_CAP_SIZE) { + DRM_DEBUG_KMS("dpcd read failed - undocked during suspend?\n"); + ret = -1; + goto out_unlock; + } + + ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, + DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC); + if (ret < 0) { + DRM_DEBUG_KMS("mst write failed - undocked during suspend?\n"); + ret = -1; + goto out_unlock; + } + ret = 0; + } else + ret = -1; + +out_unlock: + mutex_unlock(&mgr->lock); + return ret; +} +EXPORT_SYMBOL(drm_dp_mst_topology_mgr_resume); + +static void drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up) +{ + int len; + u8 replyblock[32]; + int replylen, origlen, curreply; + int ret; + struct drm_dp_sideband_msg_rx *msg; + int basereg = up ? DP_SIDEBAND_MSG_UP_REQ_BASE : DP_SIDEBAND_MSG_DOWN_REP_BASE; + msg = up ? &mgr->up_req_recv : &mgr->down_rep_recv; + + len = min(mgr->max_dpcd_transaction_bytes, 16); + ret = drm_dp_dpcd_read(mgr->aux, basereg, + replyblock, len); + if (ret != len) { + DRM_DEBUG_KMS("failed to read DPCD down rep %d %d\n", len, ret); + return; + } + ret = drm_dp_sideband_msg_build(msg, replyblock, len, true); + if (!ret) { + DRM_DEBUG_KMS("sideband msg build failed %d\n", replyblock[0]); + return; + } + replylen = msg->curchunk_len + msg->curchunk_hdrlen; + + origlen = replylen; + replylen -= len; + curreply = len; + while (replylen > 0) { + len = min3(replylen, mgr->max_dpcd_transaction_bytes, 16); + ret = drm_dp_dpcd_read(mgr->aux, basereg + curreply, + replyblock, len); + if (ret != len) { + DRM_DEBUG_KMS("failed to read a chunk\n"); + } + ret = drm_dp_sideband_msg_build(msg, replyblock, len, false); + if (ret == false) + DRM_DEBUG_KMS("failed to build sideband msg\n"); + curreply += len; + replylen -= len; + } +} + +static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr) +{ + int ret = 0; + + drm_dp_get_one_sb_msg(mgr, false); + + if (mgr->down_rep_recv.have_eomt) { + struct drm_dp_sideband_msg_tx *txmsg; + struct drm_dp_mst_branch *mstb; + int slot = -1; + mstb = drm_dp_get_mst_branch_device(mgr, + mgr->down_rep_recv.initial_hdr.lct, + mgr->down_rep_recv.initial_hdr.rad); + + if (!mstb) { + DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->down_rep_recv.initial_hdr.lct); + memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx)); + return 0; + } + + /* find the message */ + slot = mgr->down_rep_recv.initial_hdr.seqno; + mutex_lock(&mgr->qlock); + txmsg = mstb->tx_slots[slot]; + /* remove from slots */ + mutex_unlock(&mgr->qlock); + + if (!txmsg) { + DRM_DEBUG_KMS("Got MST reply with no msg %p %d %d %02x %02x\n", + mstb, + mgr->down_rep_recv.initial_hdr.seqno, + mgr->down_rep_recv.initial_hdr.lct, + mgr->down_rep_recv.initial_hdr.rad[0], + mgr->down_rep_recv.msg[0]); + drm_dp_put_mst_branch_device(mstb); + memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx)); + return 0; + } + + drm_dp_sideband_parse_reply(&mgr->down_rep_recv, &txmsg->reply); + if (txmsg->reply.reply_type == 1) { + DRM_DEBUG_KMS("Got NAK reply: req 0x%02x, reason 0x%02x, nak data 0x%02x\n", txmsg->reply.req_type, txmsg->reply.u.nak.reason, txmsg->reply.u.nak.nak_data); + } + + memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx)); + drm_dp_put_mst_branch_device(mstb); + + mutex_lock(&mgr->qlock); + txmsg->state = DRM_DP_SIDEBAND_TX_RX; + mstb->tx_slots[slot] = NULL; + mutex_unlock(&mgr->qlock); + + wake_up(&mgr->tx_waitq); + } + return ret; +} + +static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr) +{ + int ret = 0; + drm_dp_get_one_sb_msg(mgr, true); + + if (mgr->up_req_recv.have_eomt) { + struct drm_dp_sideband_msg_req_body msg; + struct drm_dp_mst_branch *mstb; + bool seqno; + mstb = drm_dp_get_mst_branch_device(mgr, + mgr->up_req_recv.initial_hdr.lct, + mgr->up_req_recv.initial_hdr.rad); + if (!mstb) { + DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->up_req_recv.initial_hdr.lct); + memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx)); + return 0; + } + + seqno = mgr->up_req_recv.initial_hdr.seqno; + drm_dp_sideband_parse_req(&mgr->up_req_recv, &msg); + + if (msg.req_type == DP_CONNECTION_STATUS_NOTIFY) { + drm_dp_send_up_ack_reply(mgr, mstb, msg.req_type, seqno, false); + drm_dp_update_port(mstb, &msg.u.conn_stat); + DRM_DEBUG_KMS("Got CSN: pn: %d ldps:%d ddps: %d mcs: %d ip: %d pdt: %d\n", msg.u.conn_stat.port_number, msg.u.conn_stat.legacy_device_plug_status, msg.u.conn_stat.displayport_device_plug_status, msg.u.conn_stat.message_capability_status, msg.u.conn_stat.input_port, msg.u.conn_stat.peer_device_type); + (*mgr->cbs->hotplug)(mgr); + + } else if (msg.req_type == DP_RESOURCE_STATUS_NOTIFY) { + drm_dp_send_up_ack_reply(mgr, mstb, msg.req_type, seqno, false); + DRM_DEBUG_KMS("Got RSN: pn: %d avail_pbn %d\n", msg.u.resource_stat.port_number, msg.u.resource_stat.available_pbn); + } + + drm_dp_put_mst_branch_device(mstb); + memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx)); + } + return ret; +} + +/** + * drm_dp_mst_hpd_irq() - MST hotplug IRQ notify + * @mgr: manager to notify irq for. + * @esi: 4 bytes from SINK_COUNT_ESI + * + * This should be called from the driver when it detects a short IRQ, + * along with the value of the DEVICE_SERVICE_IRQ_VECTOR_ESI0. The + * topology manager will process the sideband messages received as a result + * of this. + */ +int drm_dp_mst_hpd_irq(struct drm_dp_mst_topology_mgr *mgr, u8 *esi, bool *handled) +{ + int ret = 0; + int sc; + *handled = false; + sc = esi[0] & 0x3f; + + if (sc != mgr->sink_count) { + mgr->sink_count = sc; + *handled = true; + } + + if (esi[1] & DP_DOWN_REP_MSG_RDY) { + ret = drm_dp_mst_handle_down_rep(mgr); + *handled = true; + } + + if (esi[1] & DP_UP_REQ_MSG_RDY) { + ret |= drm_dp_mst_handle_up_req(mgr); + *handled = true; + } + + drm_dp_mst_kick_tx(mgr); + return ret; +} +EXPORT_SYMBOL(drm_dp_mst_hpd_irq); + +/** + * drm_dp_mst_detect_port() - get connection status for an MST port + * @mgr: manager for this port + * @port: unverified pointer to a port + * + * This returns the current connection state for a port. It validates the + * port pointer still exists so the caller doesn't require a reference + */ +enum drm_connector_status drm_dp_mst_detect_port(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port) +{ + enum drm_connector_status status = connector_status_disconnected; + + /* we need to search for the port in the mgr in case its gone */ + port = drm_dp_get_validated_port_ref(mgr, port); + if (!port) + return connector_status_disconnected; + + if (!port->ddps) + goto out; + + switch (port->pdt) { + case DP_PEER_DEVICE_NONE: + case DP_PEER_DEVICE_MST_BRANCHING: + break; + + case DP_PEER_DEVICE_SST_SINK: + status = connector_status_connected; + break; + case DP_PEER_DEVICE_DP_LEGACY_CONV: + if (port->ldps) + status = connector_status_connected; + break; + } +out: + drm_dp_put_port(port); + return status; +} +EXPORT_SYMBOL(drm_dp_mst_detect_port); + +/** + * drm_dp_mst_get_edid() - get EDID for an MST port + * @connector: toplevel connector to get EDID for + * @mgr: manager for this port + * @port: unverified pointer to a port. + * + * This returns an EDID for the port connected to a connector, + * It validates the pointer still exists so the caller doesn't require a + * reference. + */ +struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port) +{ + struct edid *edid = NULL; + + /* we need to search for the port in the mgr in case its gone */ + port = drm_dp_get_validated_port_ref(mgr, port); + if (!port) + return NULL; + + edid = drm_get_edid(connector, &port->aux.ddc); + drm_dp_put_port(port); + return edid; +} +EXPORT_SYMBOL(drm_dp_mst_get_edid); + +/** + * drm_dp_find_vcpi_slots() - find slots for this PBN value + * @mgr: manager to use + * @pbn: payload bandwidth to convert into slots. + */ +int drm_dp_find_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, + int pbn) +{ + int num_slots; + + num_slots = DIV_ROUND_UP(pbn, mgr->pbn_div); + + if (num_slots > mgr->avail_slots) + return -ENOSPC; + return num_slots; +} +EXPORT_SYMBOL(drm_dp_find_vcpi_slots); + +static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_vcpi *vcpi, int pbn) +{ + int num_slots; + int ret; + + num_slots = DIV_ROUND_UP(pbn, mgr->pbn_div); + + if (num_slots > mgr->avail_slots) + return -ENOSPC; + + vcpi->pbn = pbn; + vcpi->aligned_pbn = num_slots * mgr->pbn_div; + vcpi->num_slots = num_slots; + + ret = drm_dp_mst_assign_payload_id(mgr, vcpi); + if (ret < 0) + return ret; + return 0; +} + +/** + * drm_dp_mst_allocate_vcpi() - Allocate a virtual channel + * @mgr: manager for this port + * @port: port to allocate a virtual channel for. + * @pbn: payload bandwidth number to request + * @slots: returned number of slots for this PBN. + */ +bool drm_dp_mst_allocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, int pbn, int *slots) +{ + int ret; + + port = drm_dp_get_validated_port_ref(mgr, port); + if (!port) + return false; + + if (port->vcpi.vcpi > 0) { + DRM_DEBUG_KMS("payload: vcpi %d already allocated for pbn %d - requested pbn %d\n", port->vcpi.vcpi, port->vcpi.pbn, pbn); + if (pbn == port->vcpi.pbn) { + *slots = port->vcpi.num_slots; + return true; + } + } + + ret = drm_dp_init_vcpi(mgr, &port->vcpi, pbn); + if (ret) { + DRM_DEBUG_KMS("failed to init vcpi %d %d %d\n", DIV_ROUND_UP(pbn, mgr->pbn_div), mgr->avail_slots, ret); + goto out; + } + DRM_DEBUG_KMS("initing vcpi for %d %d\n", pbn, port->vcpi.num_slots); + *slots = port->vcpi.num_slots; + + drm_dp_put_port(port); + return true; +out: + return false; +} +EXPORT_SYMBOL(drm_dp_mst_allocate_vcpi); + +/** + * drm_dp_mst_reset_vcpi_slots() - Reset number of slots to 0 for VCPI + * @mgr: manager for this port + * @port: unverified pointer to a port. + * + * This just resets the number of slots for the ports VCPI for later programming. + */ +void drm_dp_mst_reset_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port) +{ + port = drm_dp_get_validated_port_ref(mgr, port); + if (!port) + return; + port->vcpi.num_slots = 0; + drm_dp_put_port(port); +} +EXPORT_SYMBOL(drm_dp_mst_reset_vcpi_slots); + +/** + * drm_dp_mst_deallocate_vcpi() - deallocate a VCPI + * @mgr: manager for this port + * @port: unverified port to deallocate vcpi for + */ +void drm_dp_mst_deallocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port) +{ + port = drm_dp_get_validated_port_ref(mgr, port); + if (!port) + return; + + drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi); + port->vcpi.num_slots = 0; + port->vcpi.pbn = 0; + port->vcpi.aligned_pbn = 0; + port->vcpi.vcpi = 0; + drm_dp_put_port(port); +} +EXPORT_SYMBOL(drm_dp_mst_deallocate_vcpi); + +static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr, + int id, struct drm_dp_payload *payload) +{ + u8 payload_alloc[3], status; + int ret; + int retries = 0; + + drm_dp_dpcd_writeb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS, + DP_PAYLOAD_TABLE_UPDATED); + + payload_alloc[0] = id; + payload_alloc[1] = payload->start_slot; + payload_alloc[2] = payload->num_slots; + + ret = drm_dp_dpcd_write(mgr->aux, DP_PAYLOAD_ALLOCATE_SET, payload_alloc, 3); + if (ret != 3) { + DRM_DEBUG_KMS("failed to write payload allocation %d\n", ret); + goto fail; + } + +retry: + ret = drm_dp_dpcd_readb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS, &status); + if (ret < 0) { + DRM_DEBUG_KMS("failed to read payload table status %d\n", ret); + goto fail; + } + + if (!(status & DP_PAYLOAD_TABLE_UPDATED)) { + retries++; + if (retries < 20) { + usleep_range(10000, 20000); + goto retry; + } + DRM_DEBUG_KMS("status not set after read payload table status %d\n", status); + ret = -EINVAL; + goto fail; + } + ret = 0; +fail: + return ret; +} + + +/** + * drm_dp_check_act_status() - Check ACT handled status. + * @mgr: manager to use + * + * Check the payload status bits in the DPCD for ACT handled completion. + */ +int drm_dp_check_act_status(struct drm_dp_mst_topology_mgr *mgr) +{ + u8 status; + int ret; + int count = 0; + + do { + ret = drm_dp_dpcd_readb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS, &status); + + if (ret < 0) { + DRM_DEBUG_KMS("failed to read payload table status %d\n", ret); + goto fail; + } + + if (status & DP_PAYLOAD_ACT_HANDLED) + break; + count++; + udelay(100); + + } while (count < 30); + + if (!(status & DP_PAYLOAD_ACT_HANDLED)) { + DRM_DEBUG_KMS("failed to get ACT bit %d after %d retries\n", status, count); + ret = -EINVAL; + goto fail; + } + return 0; +fail: + return ret; +} +EXPORT_SYMBOL(drm_dp_check_act_status); + +/** + * drm_dp_calc_pbn_mode() - Calculate the PBN for a mode. + * @clock: dot clock for the mode + * @bpp: bpp for the mode. + * + * This uses the formula in the spec to calculate the PBN value for a mode. + */ +int drm_dp_calc_pbn_mode(int clock, int bpp) +{ + fixed20_12 pix_bw; + fixed20_12 fbpp; + fixed20_12 result; + fixed20_12 margin, tmp; + u32 res; + + pix_bw.full = dfixed_const(clock); + fbpp.full = dfixed_const(bpp); + tmp.full = dfixed_const(8); + fbpp.full = dfixed_div(fbpp, tmp); + + result.full = dfixed_mul(pix_bw, fbpp); + margin.full = dfixed_const(54); + tmp.full = dfixed_const(64); + margin.full = dfixed_div(margin, tmp); + result.full = dfixed_div(result, margin); + + margin.full = dfixed_const(1006); + tmp.full = dfixed_const(1000); + margin.full = dfixed_div(margin, tmp); + result.full = dfixed_mul(result, margin); + + result.full = dfixed_div(result, tmp); + result.full = dfixed_ceil(result); + res = dfixed_trunc(result); + return res; +} +EXPORT_SYMBOL(drm_dp_calc_pbn_mode); + +static int test_calc_pbn_mode(void) +{ + int ret; + ret = drm_dp_calc_pbn_mode(154000, 30); + if (ret != 689) + return -EINVAL; + ret = drm_dp_calc_pbn_mode(234000, 30); + if (ret != 1047) + return -EINVAL; + return 0; +} + +/* we want to kick the TX after we've ack the up/down IRQs. */ +static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr) +{ + queue_work(system_long_wq, &mgr->tx_work); +} + +static void drm_dp_mst_dump_mstb(struct seq_file *m, + struct drm_dp_mst_branch *mstb) +{ + struct drm_dp_mst_port *port; + int tabs = mstb->lct; + char prefix[10]; + int i; + + for (i = 0; i < tabs; i++) + prefix[i] = '\t'; + prefix[i] = '\0'; + + seq_printf(m, "%smst: %p, %d\n", prefix, mstb, mstb->num_ports); + list_for_each_entry(port, &mstb->ports, next) { + seq_printf(m, "%sport: %d: ddps: %d ldps: %d, %p, conn: %p\n", prefix, port->port_num, port->ddps, port->ldps, port, port->connector); + if (port->mstb) + drm_dp_mst_dump_mstb(m, port->mstb); + } +} + +static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr, + char *buf) +{ + int ret; + int i; + for (i = 0; i < 4; i++) { + ret = drm_dp_dpcd_read(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS + (i * 16), &buf[i * 16], 16); + if (ret != 16) + break; + } + if (i == 4) + return true; + return false; +} + +/** + * drm_dp_mst_dump_topology(): dump topology to seq file. + * @m: seq_file to dump output to + * @mgr: manager to dump current topology for. + * + * helper to dump MST topology to a seq file for debugfs. + */ +void drm_dp_mst_dump_topology(struct seq_file *m, + struct drm_dp_mst_topology_mgr *mgr) +{ + int i; + struct drm_dp_mst_port *port; + mutex_lock(&mgr->lock); + if (mgr->mst_primary) + drm_dp_mst_dump_mstb(m, mgr->mst_primary); + + /* dump VCPIs */ + mutex_unlock(&mgr->lock); + + mutex_lock(&mgr->payload_lock); + seq_printf(m, "vcpi: %lx\n", mgr->payload_mask); + + for (i = 0; i < mgr->max_payloads; i++) { + if (mgr->proposed_vcpis[i]) { + port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi); + seq_printf(m, "vcpi %d: %d %d %d\n", i, port->port_num, port->vcpi.vcpi, port->vcpi.num_slots); + } else + seq_printf(m, "vcpi %d:unsed\n", i); + } + for (i = 0; i < mgr->max_payloads; i++) { + seq_printf(m, "payload %d: %d, %d, %d\n", + i, + mgr->payloads[i].payload_state, + mgr->payloads[i].start_slot, + mgr->payloads[i].num_slots); + + + } + mutex_unlock(&mgr->payload_lock); + + mutex_lock(&mgr->lock); + if (mgr->mst_primary) { + u8 buf[64]; + bool bret; + int ret; + ret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, buf, DP_RECEIVER_CAP_SIZE); + seq_printf(m, "dpcd: "); + for (i = 0; i < DP_RECEIVER_CAP_SIZE; i++) + seq_printf(m, "%02x ", buf[i]); + seq_printf(m, "\n"); + ret = drm_dp_dpcd_read(mgr->aux, DP_FAUX_CAP, buf, 2); + seq_printf(m, "faux/mst: "); + for (i = 0; i < 2; i++) + seq_printf(m, "%02x ", buf[i]); + seq_printf(m, "\n"); + ret = drm_dp_dpcd_read(mgr->aux, DP_MSTM_CTRL, buf, 1); + seq_printf(m, "mst ctrl: "); + for (i = 0; i < 1; i++) + seq_printf(m, "%02x ", buf[i]); + seq_printf(m, "\n"); + + bret = dump_dp_payload_table(mgr, buf); + if (bret == true) { + seq_printf(m, "payload table: "); + for (i = 0; i < 63; i++) + seq_printf(m, "%02x ", buf[i]); + seq_printf(m, "\n"); + } + + } + + mutex_unlock(&mgr->lock); + +} +EXPORT_SYMBOL(drm_dp_mst_dump_topology); + +static void drm_dp_tx_work(struct work_struct *work) +{ + struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, tx_work); + + mutex_lock(&mgr->qlock); + if (mgr->tx_down_in_progress) + process_single_down_tx_qlock(mgr); + mutex_unlock(&mgr->qlock); +} + +/** + * drm_dp_mst_topology_mgr_init - initialise a topology manager + * @mgr: manager struct to initialise + * @dev: device providing this structure - for i2c addition. + * @aux: DP helper aux channel to talk to this device + * @max_dpcd_transaction_bytes: hw specific DPCD transaction limit + * @max_payloads: maximum number of payloads this GPU can source + * @conn_base_id: the connector object ID the MST device is connected to. + * + * Return 0 for success, or negative error code on failure + */ +int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr, + struct device *dev, struct drm_dp_aux *aux, + int max_dpcd_transaction_bytes, + int max_payloads, int conn_base_id) +{ + mutex_init(&mgr->lock); + mutex_init(&mgr->qlock); + mutex_init(&mgr->payload_lock); + INIT_LIST_HEAD(&mgr->tx_msg_upq); + INIT_LIST_HEAD(&mgr->tx_msg_downq); + INIT_WORK(&mgr->work, drm_dp_mst_link_probe_work); + INIT_WORK(&mgr->tx_work, drm_dp_tx_work); + init_waitqueue_head(&mgr->tx_waitq); + mgr->dev = dev; + mgr->aux = aux; + mgr->max_dpcd_transaction_bytes = max_dpcd_transaction_bytes; + mgr->max_payloads = max_payloads; + mgr->conn_base_id = conn_base_id; + mgr->payloads = kcalloc(max_payloads, sizeof(struct drm_dp_payload), GFP_KERNEL); + if (!mgr->payloads) + return -ENOMEM; + mgr->proposed_vcpis = kcalloc(max_payloads, sizeof(struct drm_dp_vcpi *), GFP_KERNEL); + if (!mgr->proposed_vcpis) + return -ENOMEM; + set_bit(0, &mgr->payload_mask); + test_calc_pbn_mode(); + return 0; +} +EXPORT_SYMBOL(drm_dp_mst_topology_mgr_init); + +/** + * drm_dp_mst_topology_mgr_destroy() - destroy topology manager. + * @mgr: manager to destroy + */ +void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr) +{ + mutex_lock(&mgr->payload_lock); + kfree(mgr->payloads); + mgr->payloads = NULL; + kfree(mgr->proposed_vcpis); + mgr->proposed_vcpis = NULL; + mutex_unlock(&mgr->payload_lock); + mgr->dev = NULL; + mgr->aux = NULL; +} +EXPORT_SYMBOL(drm_dp_mst_topology_mgr_destroy); + +/* I2C device */ +static int drm_dp_mst_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, + int num) +{ + struct drm_dp_aux *aux = adapter->algo_data; + struct drm_dp_mst_port *port = container_of(aux, struct drm_dp_mst_port, aux); + struct drm_dp_mst_branch *mstb; + struct drm_dp_mst_topology_mgr *mgr = port->mgr; + unsigned int i; + bool reading = false; + struct drm_dp_sideband_msg_req_body msg; + struct drm_dp_sideband_msg_tx *txmsg = NULL; + int ret; + + mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent); + if (!mstb) + return -EREMOTEIO; + + /* construct i2c msg */ + /* see if last msg is a read */ + if (msgs[num - 1].flags & I2C_M_RD) + reading = true; + + if (!reading) { + DRM_DEBUG_KMS("Unsupported I2C transaction for MST device\n"); + ret = -EIO; + goto out; + } + + msg.req_type = DP_REMOTE_I2C_READ; + msg.u.i2c_read.num_transactions = num - 1; + msg.u.i2c_read.port_number = port->port_num; + for (i = 0; i < num - 1; i++) { + msg.u.i2c_read.transactions[i].i2c_dev_id = msgs[i].addr; + msg.u.i2c_read.transactions[i].num_bytes = msgs[i].len; + msg.u.i2c_read.transactions[i].bytes = msgs[i].buf; + } + msg.u.i2c_read.read_i2c_device_id = msgs[num - 1].addr; + msg.u.i2c_read.num_bytes_read = msgs[num - 1].len; + + txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); + if (!txmsg) { + ret = -ENOMEM; + goto out; + } + + txmsg->dst = mstb; + drm_dp_encode_sideband_req(&msg, txmsg); + + drm_dp_queue_down_tx(mgr, txmsg); + + ret = drm_dp_mst_wait_tx_reply(mstb, txmsg); + if (ret > 0) { + + if (txmsg->reply.reply_type == 1) { /* got a NAK back */ + ret = -EREMOTEIO; + goto out; + } + if (txmsg->reply.u.remote_i2c_read_ack.num_bytes != msgs[num - 1].len) { + ret = -EIO; + goto out; + } + memcpy(msgs[num - 1].buf, txmsg->reply.u.remote_i2c_read_ack.bytes, msgs[num - 1].len); + ret = num; + } +out: + kfree(txmsg); + drm_dp_put_mst_branch_device(mstb); + return ret; +} + +static u32 drm_dp_mst_i2c_functionality(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | + I2C_FUNC_SMBUS_READ_BLOCK_DATA | + I2C_FUNC_SMBUS_BLOCK_PROC_CALL | + I2C_FUNC_10BIT_ADDR; +} + +static const struct i2c_algorithm drm_dp_mst_i2c_algo = { + .functionality = drm_dp_mst_i2c_functionality, + .master_xfer = drm_dp_mst_i2c_xfer, +}; + +/** + * drm_dp_mst_register_i2c_bus() - register an I2C adapter for I2C-over-AUX + * @aux: DisplayPort AUX channel + * + * Returns 0 on success or a negative error code on failure. + */ +static int drm_dp_mst_register_i2c_bus(struct drm_dp_aux *aux) +{ + aux->ddc.algo = &drm_dp_mst_i2c_algo; + aux->ddc.algo_data = aux; + aux->ddc.retries = 3; + + aux->ddc.class = I2C_CLASS_DDC; + aux->ddc.owner = THIS_MODULE; + aux->ddc.dev.parent = aux->dev; + aux->ddc.dev.of_node = aux->dev->of_node; + + strlcpy(aux->ddc.name, aux->name ? aux->name : dev_name(aux->dev), + sizeof(aux->ddc.name)); + + return i2c_add_adapter(&aux->ddc); +} + +/** + * drm_dp_mst_unregister_i2c_bus() - unregister an I2C-over-AUX adapter + * @aux: DisplayPort AUX channel + */ +static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_aux *aux) +{ + i2c_del_adapter(&aux->ddc); +} diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h new file mode 100644 index 000000000000..9b446ada2532 --- /dev/null +++ b/include/drm/drm_dp_mst_helper.h @@ -0,0 +1,509 @@ +/* + * Copyright © 2014 Red Hat. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR 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. + */ +#ifndef _DRM_DP_MST_HELPER_H_ +#define _DRM_DP_MST_HELPER_H_ + +#include +#include + +struct drm_dp_mst_branch; + +/** + * struct drm_dp_vcpi - Virtual Channel Payload Identifer + * @vcpi: Virtual channel ID. + * @pbn: Payload Bandwidth Number for this channel + * @aligned_pbn: PBN aligned with slot size + * @num_slots: number of slots for this PBN + */ +struct drm_dp_vcpi { + int vcpi; + int pbn; + int aligned_pbn; + int num_slots; +}; + +/** + * struct drm_dp_mst_port - MST port + * @kref: reference count for this port. + * @guid_valid: for DP 1.2 devices if we have validated the GUID. + * @guid: guid for DP 1.2 device on this port. + * @port_num: port number + * @input: if this port is an input port. + * @mcs: message capability status - DP 1.2 spec. + * @ddps: DisplayPort Device Plug Status - DP 1.2 + * @pdt: Peer Device Type + * @ldps: Legacy Device Plug Status + * @dpcd_rev: DPCD revision of device on this port + * @num_sdp_streams: Number of simultaneous streams + * @num_sdp_stream_sinks: Number of stream sinks + * @available_pbn: Available bandwidth for this port. + * @next: link to next port on this branch device + * @mstb: branch device attach below this port + * @aux: i2c aux transport to talk to device connected to this port. + * @parent: branch device parent of this port + * @vcpi: Virtual Channel Payload info for this port. + * @connector: DRM connector this port is connected to. + * @mgr: topology manager this port lives under. + * + * This structure represents an MST port endpoint on a device somewhere + * in the MST topology. + */ +struct drm_dp_mst_port { + struct kref kref; + + /* if dpcd 1.2 device is on this port - its GUID info */ + bool guid_valid; + u8 guid[16]; + + u8 port_num; + bool input; + bool mcs; + bool ddps; + u8 pdt; + bool ldps; + u8 dpcd_rev; + u8 num_sdp_streams; + u8 num_sdp_stream_sinks; + uint16_t available_pbn; + struct list_head next; + struct drm_dp_mst_branch *mstb; /* pointer to an mstb if this port has one */ + struct drm_dp_aux aux; /* i2c bus for this port? */ + struct drm_dp_mst_branch *parent; + + struct drm_dp_vcpi vcpi; + struct drm_connector *connector; + struct drm_dp_mst_topology_mgr *mgr; +}; + +/** + * struct drm_dp_mst_branch - MST branch device. + * @kref: reference count for this port. + * @rad: Relative Address to talk to this branch device. + * @lct: Link count total to talk to this branch device. + * @num_ports: number of ports on the branch. + * @msg_slots: one bit per transmitted msg slot. + * @ports: linked list of ports on this branch. + * @port_parent: pointer to the port parent, NULL if toplevel. + * @mgr: topology manager for this branch device. + * @tx_slots: transmission slots for this device. + * @last_seqno: last sequence number used to talk to this. + * @link_address_sent: if a link address message has been sent to this device yet. + * + * This structure represents an MST branch device, there is one + * primary branch device at the root, along with any others connected + * to downstream ports + */ +struct drm_dp_mst_branch { + struct kref kref; + u8 rad[8]; + u8 lct; + int num_ports; + + int msg_slots; + struct list_head ports; + + /* list of tx ops queue for this port */ + struct drm_dp_mst_port *port_parent; + struct drm_dp_mst_topology_mgr *mgr; + + /* slots are protected by mstb->mgr->qlock */ + struct drm_dp_sideband_msg_tx *tx_slots[2]; + int last_seqno; + bool link_address_sent; +}; + + +/* sideband msg header - not bit struct */ +struct drm_dp_sideband_msg_hdr { + u8 lct; + u8 lcr; + u8 rad[8]; + bool broadcast; + bool path_msg; + u8 msg_len; + bool somt; + bool eomt; + bool seqno; +}; + +struct drm_dp_nak_reply { + u8 guid[16]; + u8 reason; + u8 nak_data; +}; + +struct drm_dp_link_address_ack_reply { + u8 guid[16]; + u8 nports; + struct drm_dp_link_addr_reply_port { + bool input_port; + u8 peer_device_type; + u8 port_number; + bool mcs; + bool ddps; + bool legacy_device_plug_status; + u8 dpcd_revision; + u8 peer_guid[16]; + u8 num_sdp_streams; + u8 num_sdp_stream_sinks; + } ports[16]; +}; + +struct drm_dp_remote_dpcd_read_ack_reply { + u8 port_number; + u8 num_bytes; + u8 bytes[255]; +}; + +struct drm_dp_remote_dpcd_write_ack_reply { + u8 port_number; +}; + +struct drm_dp_remote_dpcd_write_nak_reply { + u8 port_number; + u8 reason; + u8 bytes_written_before_failure; +}; + +struct drm_dp_remote_i2c_read_ack_reply { + u8 port_number; + u8 num_bytes; + u8 bytes[255]; +}; + +struct drm_dp_remote_i2c_read_nak_reply { + u8 port_number; + u8 nak_reason; + u8 i2c_nak_transaction; +}; + +struct drm_dp_remote_i2c_write_ack_reply { + u8 port_number; +}; + + +struct drm_dp_sideband_msg_rx { + u8 chunk[48]; + u8 msg[256]; + u8 curchunk_len; + u8 curchunk_idx; /* chunk we are parsing now */ + u8 curchunk_hdrlen; + u8 curlen; /* total length of the msg */ + bool have_somt; + bool have_eomt; + struct drm_dp_sideband_msg_hdr initial_hdr; +}; + + +struct drm_dp_allocate_payload { + u8 port_number; + u8 number_sdp_streams; + u8 vcpi; + u16 pbn; + u8 sdp_stream_sink[8]; +}; + +struct drm_dp_allocate_payload_ack_reply { + u8 port_number; + u8 vcpi; + u16 allocated_pbn; +}; + +struct drm_dp_connection_status_notify { + u8 guid[16]; + u8 port_number; + bool legacy_device_plug_status; + bool displayport_device_plug_status; + bool message_capability_status; + bool input_port; + u8 peer_device_type; +}; + +struct drm_dp_remote_dpcd_read { + u8 port_number; + u32 dpcd_address; + u8 num_bytes; +}; + +struct drm_dp_remote_dpcd_write { + u8 port_number; + u32 dpcd_address; + u8 num_bytes; + u8 *bytes; +}; + +struct drm_dp_remote_i2c_read { + u8 num_transactions; + u8 port_number; + struct { + u8 i2c_dev_id; + u8 num_bytes; + u8 *bytes; + u8 no_stop_bit; + u8 i2c_transaction_delay; + } transactions[4]; + u8 read_i2c_device_id; + u8 num_bytes_read; +}; + +struct drm_dp_remote_i2c_write { + u8 port_number; + u8 write_i2c_device_id; + u8 num_bytes; + u8 *bytes; +}; + +/* this covers ENUM_RESOURCES, POWER_DOWN_PHY, POWER_UP_PHY */ +struct drm_dp_port_number_req { + u8 port_number; +}; + +struct drm_dp_enum_path_resources_ack_reply { + u8 port_number; + u16 full_payload_bw_number; + u16 avail_payload_bw_number; +}; + +/* covers POWER_DOWN_PHY, POWER_UP_PHY */ +struct drm_dp_port_number_rep { + u8 port_number; +}; + +struct drm_dp_query_payload { + u8 port_number; + u8 vcpi; +}; + +struct drm_dp_resource_status_notify { + u8 port_number; + u8 guid[16]; + u16 available_pbn; +}; + +struct drm_dp_query_payload_ack_reply { + u8 port_number; + u8 allocated_pbn; +}; + +struct drm_dp_sideband_msg_req_body { + u8 req_type; + union ack_req { + struct drm_dp_connection_status_notify conn_stat; + struct drm_dp_port_number_req port_num; + struct drm_dp_resource_status_notify resource_stat; + + struct drm_dp_query_payload query_payload; + struct drm_dp_allocate_payload allocate_payload; + + struct drm_dp_remote_dpcd_read dpcd_read; + struct drm_dp_remote_dpcd_write dpcd_write; + + struct drm_dp_remote_i2c_read i2c_read; + struct drm_dp_remote_i2c_write i2c_write; + } u; +}; + +struct drm_dp_sideband_msg_reply_body { + u8 reply_type; + u8 req_type; + union ack_replies { + struct drm_dp_nak_reply nak; + struct drm_dp_link_address_ack_reply link_addr; + struct drm_dp_port_number_rep port_number; + + struct drm_dp_enum_path_resources_ack_reply path_resources; + struct drm_dp_allocate_payload_ack_reply allocate_payload; + struct drm_dp_query_payload_ack_reply query_payload; + + struct drm_dp_remote_dpcd_read_ack_reply remote_dpcd_read_ack; + struct drm_dp_remote_dpcd_write_ack_reply remote_dpcd_write_ack; + struct drm_dp_remote_dpcd_write_nak_reply remote_dpcd_write_nack; + + struct drm_dp_remote_i2c_read_ack_reply remote_i2c_read_ack; + struct drm_dp_remote_i2c_read_nak_reply remote_i2c_read_nack; + struct drm_dp_remote_i2c_write_ack_reply remote_i2c_write_ack; + } u; +}; + +/* msg is queued to be put into a slot */ +#define DRM_DP_SIDEBAND_TX_QUEUED 0 +/* msg has started transmitting on a slot - still on msgq */ +#define DRM_DP_SIDEBAND_TX_START_SEND 1 +/* msg has finished transmitting on a slot - removed from msgq only in slot */ +#define DRM_DP_SIDEBAND_TX_SENT 2 +/* msg has received a response - removed from slot */ +#define DRM_DP_SIDEBAND_TX_RX 3 +#define DRM_DP_SIDEBAND_TX_TIMEOUT 4 + +struct drm_dp_sideband_msg_tx { + u8 msg[256]; + u8 chunk[48]; + u8 cur_offset; + u8 cur_len; + struct drm_dp_mst_branch *dst; + struct list_head next; + int seqno; + int state; + bool path_msg; + struct drm_dp_sideband_msg_reply_body reply; +}; + +/* sideband msg handler */ +struct drm_dp_mst_topology_mgr; +struct drm_dp_mst_topology_cbs { + /* create a connector for a port */ + struct drm_connector *(*add_connector)(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, char *path); + void (*destroy_connector)(struct drm_dp_mst_topology_mgr *mgr, + struct drm_connector *connector); + void (*hotplug)(struct drm_dp_mst_topology_mgr *mgr); + +}; + +#define DP_MAX_PAYLOAD (sizeof(unsigned long) * 8) + +#define DP_PAYLOAD_LOCAL 1 +#define DP_PAYLOAD_REMOTE 2 +#define DP_PAYLOAD_DELETE_LOCAL 3 + +struct drm_dp_payload { + int payload_state; + int start_slot; + int num_slots; +}; + +/** + * struct drm_dp_mst_topology_mgr - DisplayPort MST manager + * @dev: device pointer for adding i2c devices etc. + * @cbs: callbacks for connector addition and destruction. + * @max_dpcd_transaction_bytes - maximum number of bytes to read/write in one go. + * @aux: aux channel for the DP connector. + * @max_payloads: maximum number of payloads the GPU can generate. + * @conn_base_id: DRM connector ID this mgr is connected to. + * @down_rep_recv: msg receiver state for down replies. + * @up_req_recv: msg receiver state for up requests. + * @lock: protects mst state, primary, guid, dpcd. + * @mst_state: if this manager is enabled for an MST capable port. + * @mst_primary: pointer to the primary branch device. + * @guid_valid: GUID valid for the primary branch device. + * @guid: GUID for primary port. + * @dpcd: cache of DPCD for primary port. + * @pbn_div: PBN to slots divisor. + * + * This struct represents the toplevel displayport MST topology manager. + * There should be one instance of this for every MST capable DP connector + * on the GPU. + */ +struct drm_dp_mst_topology_mgr { + + struct device *dev; + struct drm_dp_mst_topology_cbs *cbs; + int max_dpcd_transaction_bytes; + struct drm_dp_aux *aux; /* auxch for this topology mgr to use */ + int max_payloads; + int conn_base_id; + + /* only ever accessed from the workqueue - which should be serialised */ + struct drm_dp_sideband_msg_rx down_rep_recv; + struct drm_dp_sideband_msg_rx up_req_recv; + + /* pointer to info about the initial MST device */ + struct mutex lock; /* protects mst_state + primary + guid + dpcd */ + + bool mst_state; + struct drm_dp_mst_branch *mst_primary; + /* primary MST device GUID */ + bool guid_valid; + u8 guid[16]; + u8 dpcd[DP_RECEIVER_CAP_SIZE]; + u8 sink_count; + int pbn_div; + int total_slots; + int avail_slots; + int total_pbn; + + /* messages to be transmitted */ + /* qlock protects the upq/downq and in_progress, + the mstb tx_slots and txmsg->state once they are queued */ + struct mutex qlock; + struct list_head tx_msg_downq; + struct list_head tx_msg_upq; + bool tx_down_in_progress; + bool tx_up_in_progress; + + /* payload info + lock for it */ + struct mutex payload_lock; + struct drm_dp_vcpi **proposed_vcpis; + struct drm_dp_payload *payloads; + unsigned long payload_mask; + + wait_queue_head_t tx_waitq; + struct work_struct work; + + struct work_struct tx_work; +}; + +int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr, struct device *dev, struct drm_dp_aux *aux, int max_dpcd_transaction_bytes, int max_payloads, int conn_base_id); + +void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr); + + +int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool mst_state); + + +int drm_dp_mst_hpd_irq(struct drm_dp_mst_topology_mgr *mgr, u8 *esi, bool *handled); + + +enum drm_connector_status drm_dp_mst_detect_port(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port); + +struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port); + + +int drm_dp_calc_pbn_mode(int clock, int bpp); + + +bool drm_dp_mst_allocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, int pbn, int *slots); + + +void drm_dp_mst_reset_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port); + + +void drm_dp_mst_deallocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port); + + +int drm_dp_find_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, + int pbn); + + +int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr); + + +int drm_dp_update_payload_part2(struct drm_dp_mst_topology_mgr *mgr); + +int drm_dp_check_act_status(struct drm_dp_mst_topology_mgr *mgr); + +void drm_dp_mst_dump_topology(struct seq_file *m, + struct drm_dp_mst_topology_mgr *mgr); + +void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr); +int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr); +#endif -- cgit v1.2.3 From c7dbc6c9ae5c3baa3be755a228a349374d043b5b Mon Sep 17 00:00:00 2001 From: Matt Roper Date: Mon, 7 Jul 2014 11:19:57 -0700 Subject: drm: Remove command line guard for universal planes Universal planes are ready to leave 'experimental' state so drop the kernel command line parameter that we've been hiding them behind. Userspace clients that wish to receive universal planes will still need to opt-in by setting the appropriate capability bit, so this should have no impact on existing userspace. Signed-off-by: Matt Roper Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_ioctl.c | 2 -- drivers/gpu/drm/drm_stub.c | 4 ---- include/drm/drmP.h | 1 - 3 files changed, 7 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 69c61f392e66..ad66f961170e 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -342,8 +342,6 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv) file_priv->stereo_allowed = req->value; break; case DRM_CLIENT_CAP_UNIVERSAL_PLANES: - if (!drm_universal_planes) - return -EINVAL; if (req->value > 1) return -EINVAL; file_priv->universal_planes = req->value; diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 4e862b467740..233ea208c9fe 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -37,9 +37,6 @@ unsigned int drm_debug = 0; /* 1 to enable debug output */ EXPORT_SYMBOL(drm_debug); -/* 1 to allow user space to request universal planes (experimental) */ -unsigned int drm_universal_planes = 0; - unsigned int drm_vblank_offdelay = 5000; /* Default to 5000 msecs. */ unsigned int drm_timestamp_precision = 20; /* Default to 20 usecs. */ @@ -59,7 +56,6 @@ MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]"); MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps"); module_param_named(debug, drm_debug, int, 0600); -module_param_named(universal_planes, drm_universal_planes, int, 0600); module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600); module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600); module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index a1344793f4a9..9b6a445f8602 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1395,7 +1395,6 @@ extern void drm_master_put(struct drm_master **master); extern void drm_put_dev(struct drm_device *dev); extern void drm_unplug_dev(struct drm_device *dev); extern unsigned int drm_debug; -extern unsigned int drm_universal_planes; extern unsigned int drm_vblank_offdelay; extern unsigned int drm_timestamp_precision; -- cgit v1.2.3 From 86c6a2c75ab97fe31844985169e26aea335432f9 Mon Sep 17 00:00:00 2001 From: Neal Cardwell Date: Mon, 30 Jun 2014 15:09:49 -0400 Subject: tcp: switch snt_synack back to measuring transmit time of first SYNACK Always store in snt_synack the time at which the server received the first client SYN and attempted to send the first SYNACK. Recent commit aa27fc501 ("tcp: tcp_v[46]_conn_request: fix snt_synack initialization") resolved an inconsistency between IPv4 and IPv6 in the initialization of snt_synack. This commit brings back the idea from 843f4a55e (tcp: use tcp_v4_send_synack on first SYN-ACK), which was going for the original behavior of snt_synack from the commit where it was added in 9ad7c049f0f79 ("tcp: RFC2988bis + taking RTT sample from 3WHS for the passive open side") in v3.1. In addition to being simpler (and probably a tiny bit faster), unconditionally storing the time of the first SYNACK attempt has been useful because it allows calculating a performance metric quantifying how long it took to establish a passive TCP connection. Signed-off-by: Neal Cardwell Signed-off-by: Yuchung Cheng Cc: Octavian Purdila Cc: Jerry Chu Acked-by: Octavian Purdila Signed-off-by: David S. Miller --- include/net/tcp.h | 2 +- net/ipv4/tcp_ipv4.c | 2 -- net/ipv6/tcp_ipv6.c | 2 -- 3 files changed, 1 insertion(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 0d5389aecf18..c9a75dbba0c7 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1093,7 +1093,7 @@ static inline void tcp_openreq_init(struct request_sock *req, req->cookie_ts = 0; tcp_rsk(req)->rcv_isn = TCP_SKB_CB(skb)->seq; tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->seq + 1; - tcp_rsk(req)->snt_synack = 0; + tcp_rsk(req)->snt_synack = tcp_time_stamp; req->mss = rx_opt->mss_clamp; req->ts_recent = rx_opt->saw_tstamp ? rx_opt->rcv_tsval : 0; ireq->tstamp_ok = rx_opt->tstamp_ok; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 5dfebd2f2e38..52d0f6a1ec2c 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -838,8 +838,6 @@ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, ireq->ir_rmt_addr, ireq->opt); err = net_xmit_eval(err); - if (!tcp_rsk(req)->snt_synack && !err) - tcp_rsk(req)->snt_synack = tcp_time_stamp; } return err; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index bc24ee21339a..a97c95585da8 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -498,8 +498,6 @@ static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst, skb_set_queue_mapping(skb, queue_mapping); err = ip6_xmit(sk, skb, fl6, np->opt, np->tclass); err = net_xmit_eval(err); - if (!tcp_rsk(req)->snt_synack && !err) - tcp_rsk(req)->snt_synack = tcp_time_stamp; } done: -- cgit v1.2.3 From 5ed20a68cd6ca4adc0aa2d240913d604a2eb3e25 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Tue, 1 Jul 2014 21:32:05 -0700 Subject: flow_dissector: Abstract out hash computation Move the hash computation located in __skb_get_hash to be a separate function which takes flow_keys as input. This will allow flow hash computation in other contexts where we only have addresses and ports. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- include/net/flow_keys.h | 1 + net/core/flow_dissector.c | 44 ++++++++++++++++++++++++++++---------------- 2 files changed, 29 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/net/flow_keys.h b/include/net/flow_keys.h index fbefdca5e283..6667a054763a 100644 --- a/include/net/flow_keys.h +++ b/include/net/flow_keys.h @@ -29,4 +29,5 @@ struct flow_keys { bool skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow); __be32 skb_flow_get_ports(const struct sk_buff *skb, int thoff, u8 ip_proto); +u32 flow_hash_from_keys(struct flow_keys *keys); #endif diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index c2b53c1b21d2..2ff8cd4dfc5f 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -202,6 +202,33 @@ static __always_inline u32 __flow_hash_1word(u32 a) return jhash_1word(a, hashrnd); } +static inline u32 __flow_hash_from_keys(struct flow_keys *keys) +{ + u32 hash; + + /* get a consistent hash (same value on both flow directions) */ + if (((__force u32)keys->dst < (__force u32)keys->src) || + (((__force u32)keys->dst == (__force u32)keys->src) && + ((__force u16)keys->port16[1] < (__force u16)keys->port16[0]))) { + swap(keys->dst, keys->src); + swap(keys->port16[0], keys->port16[1]); + } + + hash = __flow_hash_3words((__force u32)keys->dst, + (__force u32)keys->src, + (__force u32)keys->ports); + if (!hash) + hash = 1; + + return hash; +} + +u32 flow_hash_from_keys(struct flow_keys *keys) +{ + return __flow_hash_from_keys(keys); +} +EXPORT_SYMBOL(flow_hash_from_keys); + /* * __skb_get_hash: calculate a flow hash based on src/dst addresses * and src/dst port numbers. Sets hash in skb to non-zero hash value @@ -211,7 +238,6 @@ static __always_inline u32 __flow_hash_1word(u32 a) void __skb_get_hash(struct sk_buff *skb) { struct flow_keys keys; - u32 hash; if (!skb_flow_dissect(skb, &keys)) return; @@ -219,21 +245,7 @@ void __skb_get_hash(struct sk_buff *skb) if (keys.ports) skb->l4_hash = 1; - /* get a consistent hash (same value on both flow directions) */ - if (((__force u32)keys.dst < (__force u32)keys.src) || - (((__force u32)keys.dst == (__force u32)keys.src) && - ((__force u16)keys.port16[1] < (__force u16)keys.port16[0]))) { - swap(keys.dst, keys.src); - swap(keys.port16[0], keys.port16[1]); - } - - hash = __flow_hash_3words((__force u32)keys.dst, - (__force u32)keys.src, - (__force u32)keys.ports); - if (!hash) - hash = 1; - - skb->hash = hash; + skb->hash = __flow_hash_from_keys(&keys); } EXPORT_SYMBOL(__skb_get_hash); -- cgit v1.2.3 From b73c3d0e4f0e1961e15bec18720e48aabebe2109 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Tue, 1 Jul 2014 21:32:17 -0700 Subject: net: Save TX flow hash in sock and set in skbuf on xmit For a connected socket we can precompute the flow hash for setting in skb->hash on output. This is a performance advantage over calculating the skb->hash for every packet on the connection. The computation is done using the common hash algorithm to be consistent with computations done for packets of the connection in other states where thers is no socket (e.g. time-wait, syn-recv, syn-cookies). This patch adds sk_txhash to the sock structure. inet_set_txhash and ip6_set_txhash functions are added which are called from points in TCP and UDP where socket moves to established state. skb_set_hash_from_sk is a function which sets skb->hash from the sock txhash value. This is called in UDP and TCP transmit path when transmitting within the context of a socket. Tested: ran super_netperf with 200 TCP_RR streams over a vxlan interface (in this case skb_get_hash called on every TX packet to create a UDP source port). Before fix: 95.02% CPU utilization 154/256/505 90/95/99% latencies 1.13042e+06 tps Time in functions: 0.28% skb_flow_dissect 0.21% __skb_get_hash After fix: 94.95% CPU utilization 156/254/485 90/95/99% latencies 1.15447e+06 Neither __skb_get_hash nor skb_flow_dissect appear in perf Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- include/net/ip.h | 14 ++++++++++++++ include/net/ipv6.h | 15 +++++++++++++++ include/net/sock.h | 11 +++++++++++ net/ipv4/datagram.c | 1 + net/ipv4/tcp_ipv4.c | 3 +++ net/ipv4/tcp_output.c | 1 + net/ipv6/datagram.c | 1 + net/ipv6/tcp_ipv6.c | 4 ++++ 8 files changed, 50 insertions(+) (limited to 'include') diff --git a/include/net/ip.h b/include/net/ip.h index 0e795df05ec9..2e8f055989c3 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -31,6 +31,7 @@ #include #include #include +#include struct sock; @@ -353,6 +354,19 @@ static inline __wsum inet_compute_pseudo(struct sk_buff *skb, int proto) skb->len, proto, 0); } +static inline void inet_set_txhash(struct sock *sk) +{ + struct inet_sock *inet = inet_sk(sk); + struct flow_keys keys; + + keys.src = inet->inet_saddr; + keys.dst = inet->inet_daddr; + keys.port16[0] = inet->inet_sport; + keys.port16[1] = inet->inet_dport; + + sk->sk_txhash = flow_hash_from_keys(&keys); +} + /* * Map a multicast IP onto multicast MAC for type ethernet. */ diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 574337fe72dd..2aa86e1135a1 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #define SIN6_LEN_RFC2133 24 @@ -684,6 +685,20 @@ static inline int ip6_sk_dst_hoplimit(struct ipv6_pinfo *np, struct flowi6 *fl6, return hlimit; } +static inline void ip6_set_txhash(struct sock *sk) +{ + struct inet_sock *inet = inet_sk(sk); + struct ipv6_pinfo *np = inet6_sk(sk); + struct flow_keys keys; + + keys.src = (__force __be32)ipv6_addr_hash(&np->saddr); + keys.dst = (__force __be32)ipv6_addr_hash(&sk->sk_v6_daddr); + keys.port16[0] = inet->inet_sport; + keys.port16[1] = inet->inet_dport; + + sk->sk_txhash = flow_hash_from_keys(&keys); +} + /* * Header manipulation */ diff --git a/include/net/sock.h b/include/net/sock.h index 8d4c9473e7d7..cb84b2f1ad8f 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -273,6 +273,7 @@ struct cg_proto; * @sk_rcvtimeo: %SO_RCVTIMEO setting * @sk_sndtimeo: %SO_SNDTIMEO setting * @sk_rxhash: flow hash received from netif layer + * @sk_txhash: computed flow hash for use on transmit * @sk_filter: socket filtering instructions * @sk_protinfo: private area, net family specific, when not using slab * @sk_timer: sock cleanup timer @@ -347,6 +348,7 @@ struct sock { #ifdef CONFIG_RPS __u32 sk_rxhash; #endif + __u32 sk_txhash; #ifdef CONFIG_NET_RX_BUSY_POLL unsigned int sk_napi_id; unsigned int sk_ll_usec; @@ -1980,6 +1982,14 @@ static inline void sock_poll_wait(struct file *filp, } } +static inline void skb_set_hash_from_sk(struct sk_buff *skb, struct sock *sk) +{ + if (sk->sk_txhash) { + skb->l4_hash = 1; + skb->hash = sk->sk_txhash; + } +} + /* * Queue a received datagram if it will fit. Stream and sequenced * protocols can't normally use this as they need to fit buffers in @@ -1994,6 +2004,7 @@ static inline void skb_set_owner_w(struct sk_buff *skb, struct sock *sk) skb_orphan(skb); skb->sk = sk; skb->destructor = sock_wfree; + skb_set_hash_from_sk(skb, sk); /* * We used to take a refcount on sk, but following operation * is enough to guarantee sk_free() wont free this sock until diff --git a/net/ipv4/datagram.c b/net/ipv4/datagram.c index a3095fdefbed..90c0e8386116 100644 --- a/net/ipv4/datagram.c +++ b/net/ipv4/datagram.c @@ -76,6 +76,7 @@ int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) inet->inet_daddr = fl4->daddr; inet->inet_dport = usin->sin_port; sk->sk_state = TCP_ESTABLISHED; + inet_set_txhash(sk); inet->inet_id = jiffies; sk_dst_set(sk, &rt->dst); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 52d0f6a1ec2c..1edc739b9da5 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -208,6 +208,8 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) inet->inet_dport = usin->sin_port; inet->inet_daddr = daddr; + inet_set_txhash(sk); + inet_csk(sk)->icsk_ext_hdr_len = 0; if (inet_opt) inet_csk(sk)->icsk_ext_hdr_len = inet_opt->opt.optlen; @@ -1334,6 +1336,7 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, newinet->mc_ttl = ip_hdr(skb)->ttl; newinet->rcv_tos = ip_hdr(skb)->tos; inet_csk(newsk)->icsk_ext_hdr_len = 0; + inet_set_txhash(newsk); if (inet_opt) inet_csk(newsk)->icsk_ext_hdr_len = inet_opt->opt.optlen; newinet->inet_id = newtp->write_seq ^ jiffies; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index f8f2a944a1ce..bcee13c4627c 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -916,6 +916,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, skb_orphan(skb); skb->sk = sk; skb->destructor = tcp_wfree; + skb_set_hash_from_sk(skb, sk); atomic_add(skb->truesize, &sk->sk_wmem_alloc); /* Build TCP header and checksum it. */ diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index c3bf2d2e519e..2753319524f1 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -199,6 +199,7 @@ ipv4_connected: NULL); sk->sk_state = TCP_ESTABLISHED; + ip6_set_txhash(sk); out: fl6_sock_release(flowlabel); return err; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index a97c95585da8..22055b098428 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -198,6 +198,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, sk->sk_v6_daddr = usin->sin6_addr; np->flow_label = fl6.flowlabel; + ip6_set_txhash(sk); + /* * TCP over IPv4 */ @@ -1132,6 +1134,8 @@ static struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, newsk->sk_v6_rcv_saddr = ireq->ir_v6_loc_addr; newsk->sk_bound_dev_if = ireq->ir_iif; + ip6_set_txhash(newsk); + /* Now IPv6 options... First: no IPv4 options. -- cgit v1.2.3 From 0e001614e849b68cff94cda8db8b550569d3dba6 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Tue, 1 Jul 2014 21:32:27 -0700 Subject: net: Call skb_get_hash in get_xps_queue and __skb_tx_hash Call standard function to get a packet hash instead of taking this from skb->sk->sk_hash or only using skb->protocol. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 +- include/linux/skbuff.h | 2 +- net/core/flow_dissector.c | 29 +++++------------------------ 3 files changed, 7 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 66f9a04ec270..8b43a28ee0bc 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2486,7 +2486,7 @@ static inline int netif_set_xps_queue(struct net_device *dev, * as a distribution range limit for the returned value. */ static inline u16 skb_tx_hash(const struct net_device *dev, - const struct sk_buff *skb) + struct sk_buff *skb) { return __skb_tx_hash(dev, skb, dev->real_num_tx_queues); } diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index ec89301ada41..b297af70ac30 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -3005,7 +3005,7 @@ static inline bool skb_rx_queue_recorded(const struct sk_buff *skb) return skb->queue_mapping != 0; } -u16 __skb_tx_hash(const struct net_device *dev, const struct sk_buff *skb, +u16 __skb_tx_hash(const struct net_device *dev, struct sk_buff *skb, unsigned int num_tx_queues); static inline struct sec_path *skb_sec_path(struct sk_buff *skb) diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 2ff8cd4dfc5f..62d1cb624f53 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -196,12 +196,6 @@ static __always_inline u32 __flow_hash_3words(u32 a, u32 b, u32 c) return jhash_3words(a, b, c, hashrnd); } -static __always_inline u32 __flow_hash_1word(u32 a) -{ - __flow_hash_secret_init(); - return jhash_1word(a, hashrnd); -} - static inline u32 __flow_hash_from_keys(struct flow_keys *keys) { u32 hash; @@ -253,7 +247,7 @@ EXPORT_SYMBOL(__skb_get_hash); * Returns a Tx hash based on the given packet descriptor a Tx queues' number * to be used as a distribution range. */ -u16 __skb_tx_hash(const struct net_device *dev, const struct sk_buff *skb, +u16 __skb_tx_hash(const struct net_device *dev, struct sk_buff *skb, unsigned int num_tx_queues) { u32 hash; @@ -273,13 +267,7 @@ u16 __skb_tx_hash(const struct net_device *dev, const struct sk_buff *skb, qcount = dev->tc_to_txq[tc].count; } - if (skb->sk && skb->sk->sk_hash) - hash = skb->sk->sk_hash; - else - hash = (__force u16) skb->protocol; - hash = __flow_hash_1word(hash); - - return (u16) (((u64) hash * qcount) >> 32) + qoffset; + return (u16) (((u64)skb_get_hash(skb) * qcount) >> 32) + qoffset; } EXPORT_SYMBOL(__skb_tx_hash); @@ -351,17 +339,10 @@ static inline int get_xps_queue(struct net_device *dev, struct sk_buff *skb) if (map) { if (map->len == 1) queue_index = map->queues[0]; - else { - u32 hash; - if (skb->sk && skb->sk->sk_hash) - hash = skb->sk->sk_hash; - else - hash = (__force u16) skb->protocol ^ - skb->hash; - hash = __flow_hash_1word(hash); + else queue_index = map->queues[ - ((u64)hash * map->len) >> 32]; - } + ((u64)skb_get_hash(skb) * map->len) >> 32]; + if (unlikely(queue_index >= dev->real_num_tx_queues)) queue_index = -1; } -- cgit v1.2.3 From b8f1a55639e6a76cfd274cc7de76eafac9a15ca9 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Tue, 1 Jul 2014 21:32:39 -0700 Subject: udp: Add function to make source port for UDP tunnels This patch adds udp_flow_src_port function which is intended to be a common function that UDP tunnel implementations call to set the source port. The source port is chosen so that a hash over the outer headers (IP addresses and UDP ports) acts as suitable hash for the flow of the encapsulated packet. In this manner, UDP encapsulation works with RSS and ECMP based wrt the inner flow. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- include/net/udp.h | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'include') diff --git a/include/net/udp.h b/include/net/udp.h index 68a1fefe3dfe..70f941368ace 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -176,6 +176,35 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum, int (*)(const struct sock *, const struct sock *), unsigned int hash2_nulladdr); +static inline __be16 udp_flow_src_port(struct net *net, struct sk_buff *skb, + int min, int max, bool use_eth) +{ + u32 hash; + + if (min >= max) { + /* Use default range */ + inet_get_local_port_range(net, &min, &max); + } + + hash = skb_get_hash(skb); + if (unlikely(!hash) && use_eth) { + /* Can't find a normal hash, caller has indicated an Ethernet + * packet so use that to compute a hash. + */ + hash = jhash(skb->data, 2 * ETH_ALEN, + (__force u32) skb->protocol); + } + + /* Since this is being sent on the wire obfuscate hash a bit + * to minimize possbility that any useful information to an + * attacker is leaked. Only upper 16 bits are relevant in the + * computation for 16 bit port value. + */ + hash ^= hash << 16; + + return htons((((u64) hash * (max - min)) >> 32) + min); +} + /* net/ipv4/udp.c */ void udp_v4_early_demux(struct sk_buff *skb); int udp_get_port(struct sock *sk, unsigned short snum, -- cgit v1.2.3 From 535fb8d006bc6a96d59558181a9a6f267be382c5 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Tue, 1 Jul 2014 21:32:49 -0700 Subject: vxlan: Call udp_flow_src_port In vxlan and OVS vport-vxlan call common function to get source port for a UDP tunnel. Removed vxlan_src_port since the functionality is now in udp_flow_src_port. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 26 ++------------------------ include/net/vxlan.h | 2 -- net/openvswitch/vport-vxlan.c | 5 +---- 3 files changed, 3 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index ade33ef82823..c2d360150804 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -1570,25 +1570,6 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb) return false; } -/* Compute source port for outgoing packet - * first choice to use L4 flow hash since it will spread - * better and maybe available from hardware - * secondary choice is to use jhash on the Ethernet header - */ -__be16 vxlan_src_port(__u16 port_min, __u16 port_max, struct sk_buff *skb) -{ - unsigned int range = (port_max - port_min) + 1; - u32 hash; - - hash = skb_get_hash(skb); - if (!hash) - hash = jhash(skb->data, 2 * ETH_ALEN, - (__force u32) skb->protocol); - - return htons((((u64) hash * range) >> 32) + port_min); -} -EXPORT_SYMBOL_GPL(vxlan_src_port); - static inline struct sk_buff *vxlan_handle_offloads(struct sk_buff *skb, bool udp_csum) { @@ -1807,7 +1788,8 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, if (tos == 1) tos = ip_tunnel_get_dsfield(old_iph, skb); - src_port = vxlan_src_port(vxlan->port_min, vxlan->port_max, skb); + src_port = udp_flow_src_port(dev_net(dev), skb, vxlan->port_min, + vxlan->port_max, true); if (dst->sa.sa_family == AF_INET) { memset(&fl4, 0, sizeof(fl4)); @@ -2235,7 +2217,6 @@ static void vxlan_setup(struct net_device *dev) { struct vxlan_dev *vxlan = netdev_priv(dev); unsigned int h; - int low, high; eth_hw_addr_random(dev); ether_setup(dev); @@ -2272,9 +2253,6 @@ static void vxlan_setup(struct net_device *dev) vxlan->age_timer.function = vxlan_cleanup; vxlan->age_timer.data = (unsigned long) vxlan; - inet_get_local_port_range(dev_net(dev), &low, &high); - vxlan->port_min = low; - vxlan->port_max = high; vxlan->dst_port = htons(vxlan_port); vxlan->dev = dev; diff --git a/include/net/vxlan.h b/include/net/vxlan.h index 12196ce661d9..d5f59f3fc35d 100644 --- a/include/net/vxlan.h +++ b/include/net/vxlan.h @@ -45,8 +45,6 @@ int vxlan_xmit_skb(struct vxlan_sock *vs, __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df, __be16 src_port, __be16 dst_port, __be32 vni, bool xnet); -__be16 vxlan_src_port(__u16 port_min, __u16 port_max, struct sk_buff *skb); - /* IP header + UDP + VXLAN + Ethernet header */ #define VXLAN_HEADROOM (20 + 8 + 8 + 14) /* IPv6 header + UDP + VXLAN + Ethernet header */ diff --git a/net/openvswitch/vport-vxlan.c b/net/openvswitch/vport-vxlan.c index 0edbd95c60e7..d8b7e247bebf 100644 --- a/net/openvswitch/vport-vxlan.c +++ b/net/openvswitch/vport-vxlan.c @@ -143,8 +143,6 @@ static int vxlan_tnl_send(struct vport *vport, struct sk_buff *skb) struct rtable *rt; struct flowi4 fl; __be16 src_port; - int port_min; - int port_max; __be16 df; int err; @@ -172,8 +170,7 @@ static int vxlan_tnl_send(struct vport *vport, struct sk_buff *skb) skb->ignore_df = 1; - inet_get_local_port_range(net, &port_min, &port_max); - src_port = vxlan_src_port(port_min, port_max, skb); + src_port = udp_flow_src_port(net, skb, 0, 0, true); err = vxlan_xmit_skb(vxlan_port->vs, rt, skb, fl.saddr, OVS_CB(skb)->tun_key->ipv4_dst, -- cgit v1.2.3 From cb1ce2ef387b01686469487edd45994872d52d73 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Tue, 1 Jul 2014 21:33:10 -0700 Subject: ipv6: Implement automatic flow label generation on transmit Automatically generate flow labels for IPv6 packets on transmit. The flow label is computed based on skb_get_hash. The flow label will only automatically be set when it is zero otherwise (i.e. flow label manager hasn't set one). This supports the transmit side functionality of RFC 6438. Added an IPv6 sysctl auto_flowlabels to enable/disable this behavior system wide, and added IPV6_AUTOFLOWLABEL socket option to enable this functionality per socket. By default, auto flowlabels are disabled to avoid possible conflicts with flow label manager, however if this feature proves useful we may want to enable it by default. It should also be noted that FreeBSD has already implemented automatic flow labels (including the sysctl and socket option). In FreeBSD, automatic flow labels default to enabled. Performance impact: Running super_netperf with 200 flows for TCP_RR and UDP_RR for IPv6. Note that in UDP case, __skb_get_hash will be called for every packet with explains slight regression. In the TCP case the hash is saved in the socket so there is no regression. Automatic flow labels disabled: TCP_RR: 86.53% CPU utilization 127/195/322 90/95/99% latencies 1.40498e+06 tps UDP_RR: 90.70% CPU utilization 118/168/243 90/95/99% latencies 1.50309e+06 tps Automatic flow labels enabled: TCP_RR: 85.90% CPU utilization 128/199/337 90/95/99% latencies 1.40051e+06 UDP_RR 92.61% CPU utilization 115/164/236 90/95/99% latencies 1.4687e+06 Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 9 +++++++++ include/linux/ipv6.h | 3 ++- include/net/ipv6.h | 20 ++++++++++++++++++++ include/net/netns/ipv6.h | 1 + include/uapi/linux/in6.h | 1 + net/ipv6/af_inet6.c | 1 + net/ipv6/ip6_gre.c | 7 +++++-- net/ipv6/ip6_output.c | 7 +++++-- net/ipv6/ip6_tunnel.c | 3 ++- net/ipv6/ipv6_sockglue.c | 8 ++++++++ net/ipv6/sysctl_net_ipv6.c | 8 ++++++++ 11 files changed, 62 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 10e216c6e05e..f35bfe43bf7a 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -1132,6 +1132,15 @@ flowlabel_consistency - BOOLEAN FALSE: disabled Default: TRUE +auto_flowlabels - BOOLEAN + Automatically generate flow labels based based on a flow hash + of the packet. This allows intermediate devices, such as routers, + to idenfify packet flows for mechanisms like Equal Cost Multipath + Routing (see RFC 6438). + TRUE: enabled + FALSE: disabled + Default: false + anycast_src_echo_reply - BOOLEAN Controls the use of anycast addresses as source addresses for ICMPv6 echo reply diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 5dc68c3ebcbd..ff560537dd61 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -199,7 +199,8 @@ struct ipv6_pinfo { * 010: prefer public address * 100: prefer care-of address */ - dontfrag:1; + dontfrag:1, + autoflowlabel:1; __u8 min_hopcount; __u8 tclass; __be32 rcv_flowinfo; diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 2aa86e1135a1..4308f2ada8b3 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -699,6 +699,26 @@ static inline void ip6_set_txhash(struct sock *sk) sk->sk_txhash = flow_hash_from_keys(&keys); } +static inline __be32 ip6_make_flowlabel(struct net *net, struct sk_buff *skb, + __be32 flowlabel, bool autolabel) +{ + if (!flowlabel && (autolabel || net->ipv6.sysctl.auto_flowlabels)) { + __be32 hash; + + hash = skb_get_hash(skb); + + /* Since this is being sent on the wire obfuscate hash a bit + * to minimize possbility that any useful information to an + * attacker is leaked. Only lower 20 bits are relevant. + */ + hash ^= hash >> 12; + + flowlabel = hash & IPV6_FLOWLABEL_MASK; + } + + return flowlabel; +} + /* * Header manipulation */ diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index 19d3446e59d2..eade27adecf3 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -28,6 +28,7 @@ struct netns_sysctl_ipv6 { int ip6_rt_mtu_expires; int ip6_rt_min_advmss; int flowlabel_consistency; + int auto_flowlabels; int icmpv6_time; int anycast_src_echo_reply; int fwmark_reflect; diff --git a/include/uapi/linux/in6.h b/include/uapi/linux/in6.h index 0d8e0f0342dc..22b7a69619d8 100644 --- a/include/uapi/linux/in6.h +++ b/include/uapi/linux/in6.h @@ -233,6 +233,7 @@ struct in6_flowlabel_req { #if 0 /* not yet */ #define IPV6_USE_MIN_MTU 63 #endif +#define IPV6_AUTOFLOWLABEL 64 /* * Netfilter (1) diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index a426cd7099bb..2daa3a133e49 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -765,6 +765,7 @@ static int __net_init inet6_net_init(struct net *net) net->ipv6.sysctl.bindv6only = 0; net->ipv6.sysctl.icmpv6_time = 1*HZ; net->ipv6.sysctl.flowlabel_consistency = 1; + net->ipv6.sysctl.auto_flowlabels = 0; atomic_set(&net->ipv6.rt_genid, 0); err = ipv6_init_mibs(net); diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 3873181ed856..365b2b6f3942 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -723,7 +723,8 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb, * Push down and install the IP header. */ ipv6h = ipv6_hdr(skb); - ip6_flow_hdr(ipv6h, INET_ECN_encapsulate(0, dsfield), fl6->flowlabel); + ip6_flow_hdr(ipv6h, INET_ECN_encapsulate(0, dsfield), + ip6_make_flowlabel(net, skb, fl6->flowlabel, false)); ipv6h->hop_limit = tunnel->parms.hop_limit; ipv6h->nexthdr = proto; ipv6h->saddr = fl6->saddr; @@ -1174,7 +1175,9 @@ static int ip6gre_header(struct sk_buff *skb, struct net_device *dev, struct ipv6hdr *ipv6h = (struct ipv6hdr *)skb_push(skb, t->hlen); __be16 *p = (__be16 *)(ipv6h+1); - ip6_flow_hdr(ipv6h, 0, t->fl.u.ip6.flowlabel); + ip6_flow_hdr(ipv6h, 0, + ip6_make_flowlabel(dev_net(dev), skb, + t->fl.u.ip6.flowlabel, false)); ipv6h->hop_limit = t->parms.hop_limit; ipv6h->nexthdr = NEXTHDR_GRE; ipv6h->saddr = t->parms.laddr; diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index cb9df0eb4023..fa83bdd4c3dd 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -205,7 +205,8 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6, if (hlimit < 0) hlimit = ip6_dst_hoplimit(dst); - ip6_flow_hdr(hdr, tclass, fl6->flowlabel); + ip6_flow_hdr(hdr, tclass, ip6_make_flowlabel(net, skb, fl6->flowlabel, + np->autoflowlabel)); hdr->payload_len = htons(seg_len); hdr->nexthdr = proto; @@ -1569,7 +1570,9 @@ int ip6_push_pending_frames(struct sock *sk) skb_reset_network_header(skb); hdr = ipv6_hdr(skb); - ip6_flow_hdr(hdr, np->cork.tclass, fl6->flowlabel); + ip6_flow_hdr(hdr, np->cork.tclass, + ip6_make_flowlabel(net, skb, fl6->flowlabel, + np->autoflowlabel)); hdr->hop_limit = np->cork.hop_limit; hdr->nexthdr = proto; hdr->saddr = fl6->saddr; diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index afa082458360..51a1eb185ea7 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -1046,7 +1046,8 @@ static int ip6_tnl_xmit2(struct sk_buff *skb, skb_push(skb, sizeof(struct ipv6hdr)); skb_reset_network_header(skb); ipv6h = ipv6_hdr(skb); - ip6_flow_hdr(ipv6h, INET_ECN_encapsulate(0, dsfield), fl6->flowlabel); + ip6_flow_hdr(ipv6h, INET_ECN_encapsulate(0, dsfield), + ip6_make_flowlabel(net, skb, fl6->flowlabel, false)); ipv6h->hop_limit = t->parms.hop_limit; ipv6h->nexthdr = proto; ipv6h->saddr = fl6->saddr; diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index cc34f65179e4..b50b9e54cf53 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -834,6 +834,10 @@ pref_skip_coa: np->dontfrag = valbool; retv = 0; break; + case IPV6_AUTOFLOWLABEL: + np->autoflowlabel = valbool; + retv = 0; + break; } release_sock(sk); @@ -1273,6 +1277,10 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, val = np->dontfrag; break; + case IPV6_AUTOFLOWLABEL: + val = np->autoflowlabel; + break; + default: return -ENOPROTOOPT; } diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c index 058f3eca2e53..5bf7b61f8ae8 100644 --- a/net/ipv6/sysctl_net_ipv6.c +++ b/net/ipv6/sysctl_net_ipv6.c @@ -38,6 +38,13 @@ static struct ctl_table ipv6_table_template[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "auto_flowlabels", + .data = &init_net.ipv6.sysctl.auto_flowlabels, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { .procname = "fwmark_reflect", .data = &init_net.ipv6.sysctl.fwmark_reflect, @@ -74,6 +81,7 @@ static int __net_init ipv6_sysctl_net_init(struct net *net) ipv6_table[0].data = &net->ipv6.sysctl.bindv6only; ipv6_table[1].data = &net->ipv6.sysctl.anycast_src_echo_reply; ipv6_table[2].data = &net->ipv6.sysctl.flowlabel_consistency; + ipv6_table[3].data = &net->ipv6.sysctl.auto_flowlabels; ipv6_route_table = ipv6_route_sysctl_init(net); if (!ipv6_route_table) -- cgit v1.2.3 From a3b18ddb9cc1056eea24e3edc1828cfb3fd0726f Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Tue, 1 Jul 2014 21:33:17 -0700 Subject: net: Only do flow_dissector hash computation once per packet Add sw_hash flag to skbuff to indicate that skb->hash was computed from flow_dissector. This flag is checked in skb_get_hash to avoid repeatedly trying to compute the hash (ie. in the case that no L4 hash can be computed). Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- include/linux/skbuff.h | 9 +++++++-- net/core/flow_dissector.c | 2 ++ 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index b297af70ac30..890fb3307dd6 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -455,6 +455,7 @@ static inline u32 skb_mstamp_us_delta(const struct skb_mstamp *t1, * @ooo_okay: allow the mapping of a socket to a queue to be changed * @l4_hash: indicate hash is a canonical 4-tuple hash over transport * ports. + * @sw_hash: indicates hash was computed in software stack * @wifi_acked_valid: wifi_acked was set * @wifi_acked: whether frame was acked on wifi or not * @no_fcs: Request NIC to treat last 4 bytes as Ethernet FCS @@ -562,6 +563,7 @@ struct sk_buff { __u8 pfmemalloc:1; __u8 ooo_okay:1; __u8 l4_hash:1; + __u8 sw_hash:1; __u8 wifi_acked_valid:1; __u8 wifi_acked:1; __u8 no_fcs:1; @@ -575,7 +577,7 @@ struct sk_buff { __u8 encap_hdr_csum:1; __u8 csum_valid:1; __u8 csum_complete_sw:1; - /* 3/5 bit hole (depending on ndisc_nodetype presence) */ + /* 2/4 bit hole (depending on ndisc_nodetype presence) */ kmemcheck_bitfield_end(flags2); #if defined CONFIG_NET_DMA || defined CONFIG_NET_RX_BUSY_POLL @@ -830,13 +832,14 @@ static inline void skb_set_hash(struct sk_buff *skb, __u32 hash, enum pkt_hash_types type) { skb->l4_hash = (type == PKT_HASH_TYPE_L4); + skb->sw_hash = 0; skb->hash = hash; } void __skb_get_hash(struct sk_buff *skb); static inline __u32 skb_get_hash(struct sk_buff *skb) { - if (!skb->l4_hash) + if (!skb->l4_hash && !skb->sw_hash) __skb_get_hash(skb); return skb->hash; @@ -850,6 +853,7 @@ static inline __u32 skb_get_hash_raw(const struct sk_buff *skb) static inline void skb_clear_hash(struct sk_buff *skb) { skb->hash = 0; + skb->sw_hash = 0; skb->l4_hash = 0; } @@ -862,6 +866,7 @@ static inline void skb_clear_hash_if_not_l4(struct sk_buff *skb) static inline void skb_copy_hash(struct sk_buff *to, const struct sk_buff *from) { to->hash = from->hash; + to->sw_hash = from->sw_hash; to->l4_hash = from->l4_hash; }; diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index c5f3912dad4c..5f362c1d0332 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -256,6 +256,8 @@ void __skb_get_hash(struct sk_buff *skb) if (keys.ports) skb->l4_hash = 1; + skb->sw_hash = 1; + skb->hash = __flow_hash_from_keys(&keys); } EXPORT_SYMBOL(__skb_get_hash); -- cgit v1.2.3 From 640985ec2ffd2a81f34dc5004f0951d2c6cb3d5e Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Thu, 3 Jul 2014 00:20:43 +0200 Subject: mac802154: at86rf230: add hw flags and merge ops This patch adds new mac802154 hw flags for transmit power, csma and listen before transmit (lbt). These flags indicates that the transceiver supports these features. If the flags are set and the driver doesn't implement the necessary functions, then ieee802154_register_device returns -ENOSYS "Function not implemented". This patch merges also all at86rf230 operations into one operations structure and set the right hw flags for the at86rf230 transceivers. Signed-off-by: Alexander Aring Signed-off-by: David S. Miller --- drivers/net/ieee802154/at86rf230.c | 81 +++++++++++++++++--------------------- include/net/mac802154.h | 19 +++++++++ net/mac802154/ieee802154_dev.c | 60 +++++++++++++++++++++------- 3 files changed, 102 insertions(+), 58 deletions(-) (limited to 'include') diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index 50899416f668..dca6bae1402c 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -784,7 +784,7 @@ at86rf230_set_hw_addr_filt(struct ieee802154_dev *dev, } static int -at86rf212_set_txpower(struct ieee802154_dev *dev, int db) +at86rf230_set_txpower(struct ieee802154_dev *dev, int db) { struct at86rf230_local *lp = dev->priv; @@ -803,7 +803,7 @@ at86rf212_set_txpower(struct ieee802154_dev *dev, int db) } static int -at86rf212_set_lbt(struct ieee802154_dev *dev, bool on) +at86rf230_set_lbt(struct ieee802154_dev *dev, bool on) { struct at86rf230_local *lp = dev->priv; @@ -811,7 +811,7 @@ at86rf212_set_lbt(struct ieee802154_dev *dev, bool on) } static int -at86rf212_set_cca_mode(struct ieee802154_dev *dev, u8 mode) +at86rf230_set_cca_mode(struct ieee802154_dev *dev, u8 mode) { struct at86rf230_local *lp = dev->priv; @@ -819,7 +819,7 @@ at86rf212_set_cca_mode(struct ieee802154_dev *dev, u8 mode) } static int -at86rf212_set_cca_ed_level(struct ieee802154_dev *dev, s32 level) +at86rf230_set_cca_ed_level(struct ieee802154_dev *dev, s32 level) { struct at86rf230_local *lp = dev->priv; int desens_steps; @@ -833,7 +833,7 @@ at86rf212_set_cca_ed_level(struct ieee802154_dev *dev, s32 level) } static int -at86rf212_set_csma_params(struct ieee802154_dev *dev, u8 min_be, u8 max_be, +at86rf230_set_csma_params(struct ieee802154_dev *dev, u8 min_be, u8 max_be, u8 retries) { struct at86rf230_local *lp = dev->priv; @@ -854,7 +854,7 @@ at86rf212_set_csma_params(struct ieee802154_dev *dev, u8 min_be, u8 max_be, } static int -at86rf212_set_frame_retries(struct ieee802154_dev *dev, s8 retries) +at86rf230_set_frame_retries(struct ieee802154_dev *dev, s8 retries) { struct at86rf230_local *lp = dev->priv; int rc = 0; @@ -878,22 +878,12 @@ static struct ieee802154_ops at86rf230_ops = { .start = at86rf230_start, .stop = at86rf230_stop, .set_hw_addr_filt = at86rf230_set_hw_addr_filt, -}; - -static struct ieee802154_ops at86rf212_ops = { - .owner = THIS_MODULE, - .xmit = at86rf230_xmit, - .ed = at86rf230_ed, - .set_channel = at86rf230_channel, - .start = at86rf230_start, - .stop = at86rf230_stop, - .set_hw_addr_filt = at86rf230_set_hw_addr_filt, - .set_txpower = at86rf212_set_txpower, - .set_lbt = at86rf212_set_lbt, - .set_cca_mode = at86rf212_set_cca_mode, - .set_cca_ed_level = at86rf212_set_cca_ed_level, - .set_csma_params = at86rf212_set_csma_params, - .set_frame_retries = at86rf212_set_frame_retries, + .set_txpower = at86rf230_set_txpower, + .set_lbt = at86rf230_set_lbt, + .set_cca_mode = at86rf230_set_cca_mode, + .set_cca_ed_level = at86rf230_set_cca_ed_level, + .set_csma_params = at86rf230_set_csma_params, + .set_frame_retries = at86rf230_set_frame_retries, }; static void at86rf230_irqwork(struct work_struct *work) @@ -1048,7 +1038,6 @@ static int at86rf230_probe(struct spi_device *spi) work_func_t irq_worker; int rc, irq_type; const char *chip; - struct ieee802154_ops *ops = NULL; if (!spi->irq) { dev_err(&spi->dev, "no IRQ specified\n"); @@ -1084,9 +1073,25 @@ static int at86rf230_probe(struct spi_device *spi) usleep_range(120, 240); } + dev = ieee802154_alloc_device(sizeof(*lp), &at86rf230_ops); + if (!dev) + return -ENOMEM; + + lp = dev->priv; + lp->dev = dev; + lp->part = part; + lp->vers = version; + + lp->spi = spi; + + dev->parent = &spi->dev; + dev->extra_tx_headroom = 0; + dev->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AACK | + IEEE802154_HW_TXPOWER | IEEE802154_HW_CSMA; + rc = __at86rf230_detect_device(spi, &man_id, &part, &version); if (rc < 0) - return rc; + goto free_dev; if (man_id != 0x001f) { dev_err(&spi->dev, "Non-Atmel dev found (MAN_ID %02x %02x)\n", @@ -1097,44 +1102,31 @@ static int at86rf230_probe(struct spi_device *spi) switch (part) { case 2: chip = "at86rf230"; + rc = -ENOTSUPP; /* FIXME: should be easy to support; */ break; case 3: chip = "at86rf231"; - ops = &at86rf230_ops; break; case 7: chip = "at86rf212"; if (version == 1) - ops = &at86rf212_ops; + dev->flags |= IEEE802154_HW_LBT; + else + rc = -ENOTSUPP; break; case 11: chip = "at86rf233"; - ops = &at86rf230_ops; break; default: chip = "UNKNOWN"; + rc = -ENOTSUPP; break; } dev_info(&spi->dev, "Detected %s chip version %d\n", chip, version); - if (!ops) - return -ENOTSUPP; - - dev = ieee802154_alloc_device(sizeof(*lp), ops); - if (!dev) - return -ENOMEM; - - lp = dev->priv; - lp->dev = dev; - lp->part = part; - lp->vers = version; - - lp->spi = spi; - - dev->parent = &spi->dev; - dev->extra_tx_headroom = 0; - dev->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AACK; + if (rc < 0) + goto free_dev; irq_type = irq_get_trigger_type(spi->irq); if (!irq_type) @@ -1185,6 +1177,7 @@ static int at86rf230_probe(struct spi_device *spi) err_hw_init: flush_work(&lp->irqwork); mutex_destroy(&lp->bmux); +free_dev: ieee802154_free_device(lp->dev); return rc; diff --git a/include/net/mac802154.h b/include/net/mac802154.h index a591053cae63..2e67cdd19cdc 100644 --- a/include/net/mac802154.h +++ b/include/net/mac802154.h @@ -80,6 +80,25 @@ struct ieee802154_dev { #define IEEE802154_HW_OMIT_CKSUM 0x00000001 /* Indicates that receiver will autorespond with ACK frames. */ #define IEEE802154_HW_AACK 0x00000002 +/* Indicates that transceiver will support transmit power setting. */ +#define IEEE802154_HW_TXPOWER 0x00000004 +/* Indicates that transceiver will support listen before transmit. */ +#define IEEE802154_HW_LBT 0x00000008 +/* Indicates that transceiver will support cca mode setting. */ +#define IEEE802154_HW_CCA_MODE 0x00000010 +/* Indicates that transceiver will support cca ed level setting. */ +#define IEEE802154_HW_CCA_ED_LEVEL 0x00000020 +/* Indicates that transceiver will support csma (max_be, min_be, csma retries) + * settings. */ +#define IEEE802154_HW_CSMA_PARAMS 0x00000040 +/* Indicates that transceiver will support ARET frame retries setting. */ +#define IEEE802154_HW_FRAME_RETRIES 0x00000080 + +/* This groups the most common CSMA support fields into one. */ +#define IEEE802154_HW_CSMA (IEEE802154_HW_CCA_MODE | \ + IEEE802154_HW_CCA_ED_LEVEL | \ + IEEE802154_HW_CSMA_PARAMS | \ + IEEE802154_HW_FRAME_RETRIES) /* struct ieee802154_ops - callbacks from mac802154 to the driver * diff --git a/net/mac802154/ieee802154_dev.c b/net/mac802154/ieee802154_dev.c index c4d4568611ca..9b54370f5e87 100644 --- a/net/mac802154/ieee802154_dev.c +++ b/net/mac802154/ieee802154_dev.c @@ -304,29 +304,61 @@ EXPORT_SYMBOL(ieee802154_free_device); int ieee802154_register_device(struct ieee802154_dev *dev) { struct mac802154_priv *priv = mac802154_to_priv(dev); - int rc = -ENOMEM; + int rc = -ENOSYS; + + if (dev->flags & IEEE802154_HW_TXPOWER) { + if (!priv->ops->set_txpower) + goto out; + + priv->phy->set_txpower = mac802154_set_txpower; + } + + if (dev->flags & IEEE802154_HW_LBT) { + if (!priv->ops->set_lbt) + goto out; + + priv->phy->set_lbt = mac802154_set_lbt; + } + + if (dev->flags & IEEE802154_HW_CCA_MODE) { + if (!priv->ops->set_cca_mode) + goto out; + + priv->phy->set_cca_mode = mac802154_set_cca_mode; + } + + if (dev->flags & IEEE802154_HW_CCA_ED_LEVEL) { + if (!priv->ops->set_cca_ed_level) + goto out; + + priv->phy->set_cca_ed_level = mac802154_set_cca_ed_level; + } + + if (dev->flags & IEEE802154_HW_CSMA_PARAMS) { + if (!priv->ops->set_csma_params) + goto out; + + priv->phy->set_csma_params = mac802154_set_csma_params; + } + + if (dev->flags & IEEE802154_HW_FRAME_RETRIES) { + if (!priv->ops->set_frame_retries) + goto out; + + priv->phy->set_frame_retries = mac802154_set_frame_retries; + } priv->dev_workqueue = create_singlethread_workqueue(wpan_phy_name(priv->phy)); - if (!priv->dev_workqueue) + if (!priv->dev_workqueue) { + rc = -ENOMEM; goto out; + } wpan_phy_set_dev(priv->phy, priv->hw.parent); priv->phy->add_iface = mac802154_add_iface; priv->phy->del_iface = mac802154_del_iface; - if (priv->ops->set_txpower) - priv->phy->set_txpower = mac802154_set_txpower; - if (priv->ops->set_lbt) - priv->phy->set_lbt = mac802154_set_lbt; - if (priv->ops->set_cca_mode) - priv->phy->set_cca_mode = mac802154_set_cca_mode; - if (priv->ops->set_cca_ed_level) - priv->phy->set_cca_ed_level = mac802154_set_cca_ed_level; - if (priv->ops->set_csma_params) - priv->phy->set_csma_params = mac802154_set_csma_params; - if (priv->ops->set_frame_retries) - priv->phy->set_frame_retries = mac802154_set_frame_retries; rc = wpan_phy_register(priv->phy); if (rc < 0) -- cgit v1.2.3 From db55b62cab8d458aba4e7a81dc4dd98b45c03d85 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 2 Jul 2014 14:12:01 +0200 Subject: net: arcnet: Remove "#define bool int" The header file include/linux/arcdevice.h #defines bool to int, if bool is not already #defined. However, the files which use that header file seem to rely on that #define (unconditionally) being in effect: the prototypes for the functions arcrimi_reset, com20020_reset, com90io_reset, com90xx_reset (whose addresses are assigned to the hw.reset member of struct arcnet_local) use int explicitly. Moreover, that #define is an accident waiting to happen (scenario: inclusion of arcdevice.h followed by inclusion of some header which declares function prototypes using bool). Also, #include must appear before #include (the compiler wouldn't like "typedef _Bool int"). Since none of the files using arcdevice.h declare variables of type "bool", the patch is actually quite simple, unlike the commit message. Signed-off-by: Rasmus Villemoes Signed-off-by: David S. Miller --- include/linux/arcdevice.h | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/arcdevice.h b/include/linux/arcdevice.h index 7216b0daf544..df0356220730 100644 --- a/include/linux/arcdevice.h +++ b/include/linux/arcdevice.h @@ -22,10 +22,6 @@ #ifdef __KERNEL__ #include -#ifndef bool -#define bool int -#endif - /* * RECON_THRESHOLD is the maximum number of RECON messages to receive * within one minute before printing a "cabling problem" warning. The @@ -285,9 +281,9 @@ struct arcnet_local { unsigned long first_recon; /* time of "first" RECON message to count */ unsigned long last_recon; /* time of most recent RECON */ int num_recons; /* number of RECONs between first and last. */ - bool network_down; /* do we think the network is down? */ + int network_down; /* do we think the network is down? */ - bool excnak_pending; /* We just got an excesive nak interrupt */ + int excnak_pending; /* We just got an excesive nak interrupt */ struct { uint16_t sequence; /* sequence number (incs with each packet) */ @@ -305,7 +301,7 @@ struct arcnet_local { void (*command) (struct net_device * dev, int cmd); int (*status) (struct net_device * dev); void (*intmask) (struct net_device * dev, int mask); - bool (*reset) (struct net_device * dev, bool really_reset); + int (*reset) (struct net_device * dev, int really_reset); void (*open) (struct net_device * dev); void (*close) (struct net_device * dev); -- cgit v1.2.3 From e09f8ea560490e941139d23b4c278d3e6e2c871a Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 7 Jul 2014 14:31:36 -0600 Subject: iommu: Fix IOMMU sysfs stubs 0-day kernel build testing reports: arch/x86/kvm/x86.o: In function `iommu_device_destroy': >> (.text+0x7a0a): multiple definition of `iommu_device_destroy' arch/x86/kvm/../../../virt/kvm/vfio.o:vfio.c:(.text+0x490): first defined here arch/x86/kvm/x86.o: In function `iommu_device_link': >> (.text+0x7a15): multiple definition of `iommu_device_link' arch/x86/kvm/../../../virt/kvm/vfio.o:vfio.c:(.text+0x49b): first defined here arch/x86/kvm/x86.o: In function `iommu_device_unlink': >> (.text+0x7a25): multiple definition of `iommu_device_unlink' arch/x86/kvm/../../../virt/kvm/vfio.o:vfio.c:(.text+0x4ab): first defined here arch/x86/kvm/x86.o: In function `iommu_device_create': >> (.text+0x79f8): multiple definition of `iommu_device_create' arch/x86/kvm/../../../virt/kvm/vfio.o:vfio.c:(.text+0x47e): first defined here These are due to failing to define the stubs as static inline. Fix. Signed-off-by: Alex Williamson Signed-off-by: Joerg Roedel --- include/linux/iommu.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/iommu.h b/include/linux/iommu.h index c7097d7f024c..20f9a527922a 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -403,23 +403,24 @@ static inline int iommu_domain_set_attr(struct iommu_domain *domain, return -EINVAL; } -struct device *iommu_device_create(struct device *parent, void *drvdata, - const struct attribute_group **groups, - const char *fmt, ...) +static inline struct device *iommu_device_create(struct device *parent, + void *drvdata, + const struct attribute_group **groups, + const char *fmt, ...) { return ERR_PTR(-ENODEV); } -void iommu_device_destroy(struct device *dev) +static inline void iommu_device_destroy(struct device *dev) { } -int iommu_device_link(struct device *dev, struct device *link) +static inline int iommu_device_link(struct device *dev, struct device *link) { return -EINVAL; } -void iommu_device_unlink(struct device *dev, struct device *link) +static inline void iommu_device_unlink(struct device *dev, struct device *link) { } -- cgit v1.2.3 From 1d0326b13bc9ecab5c784415165e6f78fb06ae5b Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Fri, 20 Jun 2014 14:14:41 +0400 Subject: libceph: rename ceph_osd_request::r_linger_osd to r_linger_osd_item So that: req->r_osd_item --> osd->o_requests list req->r_linger_osd_item --> osd->o_linger_requests list Signed-off-by: Ilya Dryomov Reviewed-by: Alex Elder --- include/linux/ceph/osd_client.h | 2 +- net/ceph/osd_client.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index 94ec69672164..7490a03ac163 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -117,7 +117,7 @@ struct ceph_osd_request { struct list_head r_req_lru_item; struct list_head r_osd_item; struct list_head r_linger_item; - struct list_head r_linger_osd; + struct list_head r_linger_osd_item; struct ceph_osd *r_osd; struct ceph_pg r_pgid; int r_pg_osds[CEPH_PG_MAX_SIZE]; diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 05be0c181695..d5d2be3bd113 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -364,7 +364,7 @@ struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, RB_CLEAR_NODE(&req->r_node); INIT_LIST_HEAD(&req->r_unsafe_item); INIT_LIST_HEAD(&req->r_linger_item); - INIT_LIST_HEAD(&req->r_linger_osd); + INIT_LIST_HEAD(&req->r_linger_osd_item); INIT_LIST_HEAD(&req->r_req_lru_item); INIT_LIST_HEAD(&req->r_osd_item); @@ -916,7 +916,7 @@ static void __kick_osd_requests(struct ceph_osd_client *osdc, * list at the end to keep things in tid order. */ list_for_each_entry_safe(req, nreq, &osd->o_linger_requests, - r_linger_osd) { + r_linger_osd_item) { /* * reregister request prior to unregistering linger so * that r_osd is preserved. @@ -1218,7 +1218,7 @@ static void __register_linger_request(struct ceph_osd_client *osdc, ceph_osdc_get_request(req); list_add_tail(&req->r_linger_item, &osdc->req_linger); if (req->r_osd) - list_add_tail(&req->r_linger_osd, + list_add_tail(&req->r_linger_osd_item, &req->r_osd->o_linger_requests); } @@ -1228,7 +1228,7 @@ static void __unregister_linger_request(struct ceph_osd_client *osdc, dout("__unregister_linger_request %p\n", req); list_del_init(&req->r_linger_item); if (req->r_osd) { - list_del_init(&req->r_linger_osd); + list_del_init(&req->r_linger_osd_item); if (list_empty(&req->r_osd->o_requests) && list_empty(&req->r_osd->o_linger_requests)) { -- cgit v1.2.3 From 0215e44bb390a968d01404aa2f35af56f9b55fc8 Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Fri, 20 Jun 2014 14:14:41 +0400 Subject: libceph: move and add dout()s to ceph_msg_{get,put}() Add dout()s to ceph_msg_{get,put}(). Also move them to .c and turn kref release callback into a static function. Signed-off-by: Ilya Dryomov Reviewed-by: Alex Elder --- include/linux/ceph/messenger.h | 14 ++------------ net/ceph/messenger.c | 31 ++++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index d21f2dba0731..40ae58e3e9db 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -285,19 +285,9 @@ extern void ceph_msg_data_add_bio(struct ceph_msg *msg, struct bio *bio, extern struct ceph_msg *ceph_msg_new(int type, int front_len, gfp_t flags, bool can_fail); -extern void ceph_msg_kfree(struct ceph_msg *m); - -static inline struct ceph_msg *ceph_msg_get(struct ceph_msg *msg) -{ - kref_get(&msg->kref); - return msg; -} -extern void ceph_msg_last_put(struct kref *kref); -static inline void ceph_msg_put(struct ceph_msg *msg) -{ - kref_put(&msg->kref, ceph_msg_last_put); -} +extern struct ceph_msg *ceph_msg_get(struct ceph_msg *msg); +extern void ceph_msg_put(struct ceph_msg *msg); extern void ceph_msg_dump(struct ceph_msg *msg); diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 1948d592aa54..8bffa5b90fef 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -3269,24 +3269,21 @@ static int ceph_con_in_msg_alloc(struct ceph_connection *con, int *skip) /* * Free a generically kmalloc'd message. */ -void ceph_msg_kfree(struct ceph_msg *m) +static void ceph_msg_free(struct ceph_msg *m) { - dout("msg_kfree %p\n", m); + dout("%s %p\n", __func__, m); ceph_kvfree(m->front.iov_base); kmem_cache_free(ceph_msg_cache, m); } -/* - * Drop a msg ref. Destroy as needed. - */ -void ceph_msg_last_put(struct kref *kref) +static void ceph_msg_release(struct kref *kref) { struct ceph_msg *m = container_of(kref, struct ceph_msg, kref); LIST_HEAD(data); struct list_head *links; struct list_head *next; - dout("ceph_msg_put last one on %p\n", m); + dout("%s %p\n", __func__, m); WARN_ON(!list_empty(&m->list_head)); /* drop middle, data, if any */ @@ -3308,9 +3305,25 @@ void ceph_msg_last_put(struct kref *kref) if (m->pool) ceph_msgpool_put(m->pool, m); else - ceph_msg_kfree(m); + ceph_msg_free(m); +} + +struct ceph_msg *ceph_msg_get(struct ceph_msg *msg) +{ + dout("%s %p (was %d)\n", __func__, msg, + atomic_read(&msg->kref.refcount)); + kref_get(&msg->kref); + return msg; +} +EXPORT_SYMBOL(ceph_msg_get); + +void ceph_msg_put(struct ceph_msg *msg) +{ + dout("%s %p (was %d)\n", __func__, msg, + atomic_read(&msg->kref.refcount)); + kref_put(&msg->kref, ceph_msg_release); } -EXPORT_SYMBOL(ceph_msg_last_put); +EXPORT_SYMBOL(ceph_msg_put); void ceph_msg_dump(struct ceph_msg *msg) { -- cgit v1.2.3 From 9e94af202afd961da39f82b55ba83edd4ad30e98 Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Fri, 20 Jun 2014 14:14:42 +0400 Subject: libceph: move and add dout()s to ceph_osdc_request_{get,put}() Add dout()s to ceph_osdc_request_{get,put}(). Also move them to .c and turn kref release callback into a static function. Signed-off-by: Ilya Dryomov Reviewed-by: Alex Elder --- include/linux/ceph/osd_client.h | 11 ++--------- net/ceph/osd_client.c | 26 ++++++++++++++++++++++---- 2 files changed, 24 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index 7490a03ac163..a8d5652f589d 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -328,15 +328,8 @@ extern void ceph_osdc_set_request_linger(struct ceph_osd_client *osdc, extern void ceph_osdc_unregister_linger_request(struct ceph_osd_client *osdc, struct ceph_osd_request *req); -static inline void ceph_osdc_get_request(struct ceph_osd_request *req) -{ - kref_get(&req->r_kref); -} -extern void ceph_osdc_release_request(struct kref *kref); -static inline void ceph_osdc_put_request(struct ceph_osd_request *req) -{ - kref_put(&req->r_kref, ceph_osdc_release_request); -} +extern void ceph_osdc_get_request(struct ceph_osd_request *req); +extern void ceph_osdc_put_request(struct ceph_osd_request *req); extern int ceph_osdc_start_request(struct ceph_osd_client *osdc, struct ceph_osd_request *req, diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 6202923b41ff..7406046212dc 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -297,12 +297,15 @@ static void osd_req_op_data_release(struct ceph_osd_request *osd_req, /* * requests */ -void ceph_osdc_release_request(struct kref *kref) +static void ceph_osdc_release_request(struct kref *kref) { - struct ceph_osd_request *req; + struct ceph_osd_request *req = container_of(kref, + struct ceph_osd_request, r_kref); unsigned int which; - req = container_of(kref, struct ceph_osd_request, r_kref); + dout("%s %p (r_request %p r_reply %p)\n", __func__, req, + req->r_request, req->r_reply); + if (req->r_request) ceph_msg_put(req->r_request); if (req->r_reply) { @@ -320,7 +323,22 @@ void ceph_osdc_release_request(struct kref *kref) kmem_cache_free(ceph_osd_request_cache, req); } -EXPORT_SYMBOL(ceph_osdc_release_request); + +void ceph_osdc_get_request(struct ceph_osd_request *req) +{ + dout("%s %p (was %d)\n", __func__, req, + atomic_read(&req->r_kref.refcount)); + kref_get(&req->r_kref); +} +EXPORT_SYMBOL(ceph_osdc_get_request); + +void ceph_osdc_put_request(struct ceph_osd_request *req) +{ + dout("%s %p (was %d)\n", __func__, req, + atomic_read(&req->r_kref.refcount)); + kref_put(&req->r_kref, ceph_osdc_release_request); +} +EXPORT_SYMBOL(ceph_osdc_put_request); struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, struct ceph_snap_context *snapc, -- cgit v1.2.3 From c9f9b93ddfd76498fe36d9f550bd26533a4ee6bf Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Thu, 19 Jun 2014 11:38:13 +0400 Subject: libceph: introduce ceph_osdc_cancel_request() Introduce ceph_osdc_cancel_request() intended for canceling requests from the higher layers (rbd and cephfs). Because higher layers are in charge and are supposed to know what and when they are canceling, the request is not completed, only unref'ed and removed from the libceph data structures. __cancel_request() is no longer called before __unregister_request(), because __unregister_request() unconditionally revokes r_request and there is no point in trying to do it twice. Signed-off-by: Ilya Dryomov Reviewed-by: Alex Elder --- include/linux/ceph/osd_client.h | 1 + net/ceph/osd_client.c | 31 +++++++++++++++++++++++++------ 2 files changed, 26 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index a8d5652f589d..de09cad7b7c7 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -334,6 +334,7 @@ extern void ceph_osdc_put_request(struct ceph_osd_request *req); extern int ceph_osdc_start_request(struct ceph_osd_client *osdc, struct ceph_osd_request *req, bool nofail); +extern void ceph_osdc_cancel_request(struct ceph_osd_request *req); extern int ceph_osdc_wait_request(struct ceph_osd_client *osdc, struct ceph_osd_request *req); extern void ceph_osdc_sync(struct ceph_osd_client *osdc); diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 89d7d8861d80..6c1ccf5590a3 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -2467,6 +2467,25 @@ int ceph_osdc_start_request(struct ceph_osd_client *osdc, } EXPORT_SYMBOL(ceph_osdc_start_request); +/* + * Unregister a registered request. The request is not completed (i.e. + * no callbacks or wakeups) - higher layers are supposed to know what + * they are canceling. + */ +void ceph_osdc_cancel_request(struct ceph_osd_request *req) +{ + struct ceph_osd_client *osdc = req->r_osdc; + + mutex_lock(&osdc->request_mutex); + if (req->r_linger) + __unregister_linger_request(osdc, req); + __unregister_request(osdc, req); + mutex_unlock(&osdc->request_mutex); + + dout("%s %p tid %llu canceled\n", __func__, req, req->r_tid); +} +EXPORT_SYMBOL(ceph_osdc_cancel_request); + /* * wait for a request to complete */ @@ -2475,18 +2494,18 @@ int ceph_osdc_wait_request(struct ceph_osd_client *osdc, { int rc; + dout("%s %p tid %llu\n", __func__, req, req->r_tid); + rc = wait_for_completion_interruptible(&req->r_completion); if (rc < 0) { - mutex_lock(&osdc->request_mutex); - __cancel_request(req); - __unregister_request(osdc, req); - mutex_unlock(&osdc->request_mutex); + dout("%s %p tid %llu interrupted\n", __func__, req, req->r_tid); + ceph_osdc_cancel_request(req); complete_request(req); - dout("wait_request tid %llu canceled/timed out\n", req->r_tid); return rc; } - dout("wait_request tid %llu result %d\n", req->r_tid, req->r_result); + dout("%s %p tid %llu result %d\n", __func__, req, req->r_tid, + req->r_result); return req->r_result; } EXPORT_SYMBOL(ceph_osdc_wait_request); -- cgit v1.2.3 From 2d05f082cbc73b837011225b165d64d25b47c940 Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Tue, 24 Jun 2014 16:21:45 +0400 Subject: libceph: nuke ceph_osdc_unregister_linger_request() Remove now unused ceph_osdc_unregister_linger_request(). Signed-off-by: Ilya Dryomov Reviewed-by: Alex Elder --- include/linux/ceph/osd_client.h | 2 -- net/ceph/osd_client.c | 10 ---------- 2 files changed, 12 deletions(-) (limited to 'include') diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index de09cad7b7c7..03aeb27fcc69 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -325,8 +325,6 @@ extern struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *, extern void ceph_osdc_set_request_linger(struct ceph_osd_client *osdc, struct ceph_osd_request *req); -extern void ceph_osdc_unregister_linger_request(struct ceph_osd_client *osdc, - struct ceph_osd_request *req); extern void ceph_osdc_get_request(struct ceph_osd_request *req); extern void ceph_osdc_put_request(struct ceph_osd_request *req); diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 6c1ccf5590a3..30f6faf3584f 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -1281,16 +1281,6 @@ static void __unregister_linger_request(struct ceph_osd_client *osdc, ceph_osdc_put_request(req); } -void ceph_osdc_unregister_linger_request(struct ceph_osd_client *osdc, - struct ceph_osd_request *req) -{ - mutex_lock(&osdc->request_mutex); - if (req->r_linger) - __unregister_linger_request(osdc, req); - mutex_unlock(&osdc->request_mutex); -} -EXPORT_SYMBOL(ceph_osdc_unregister_linger_request); - void ceph_osdc_set_request_linger(struct ceph_osd_client *osdc, struct ceph_osd_request *req) { -- cgit v1.2.3 From 66c417c1ee01398ac5ab1c749a20c4d8ba50e9a9 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 8 Jul 2014 15:07:47 +0300 Subject: Bluetooth: Add flag to track the real advertising state Having a single HCI_ADVERTISING flag is problematic since it tries to track both the real advertising state and the corresponding mgmt setting. To make the logic simpler and more reliable add a new flag that only tracks the actual advertising state that has been written to the controller. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 4 ++-- net/bluetooth/hci_event.c | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 5481d1c8badb..6ed1f7288f13 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -175,7 +175,7 @@ enum { HCI_UNCONFIGURED, HCI_USER_CHANNEL, HCI_EXT_CONFIGURED, - + HCI_LE_ADV, HCI_LE_SCAN, HCI_SSP_ENABLED, HCI_SC_ENABLED, @@ -200,7 +200,7 @@ enum { * or the HCI device is closed. */ #define HCI_PERSISTENT_MASK (BIT(HCI_LE_SCAN) | BIT(HCI_PERIODIC_INQ) | \ - BIT(HCI_FAST_CONNECTABLE)) + BIT(HCI_FAST_CONNECTABLE) | BIT(HCI_LE_ADV)) /* HCI ioctl defines */ #define HCIDEVUP _IOW('H', 201, int) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index a6816498b0b9..10f8de9400bc 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1069,11 +1069,15 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb) if (*sent) { struct hci_conn *conn; + set_bit(HCI_LE_ADV, &hdev->dev_flags); + conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT); if (conn) queue_delayed_work(hdev->workqueue, &conn->le_conn_timeout, conn->conn_timeout); + } else { + clear_bit(HCI_LE_ADV, &hdev->dev_flags); } mgmt_advertising(hdev, *sent); -- cgit v1.2.3 From c93bd15033027928709ee15bab2ce1f5582085c6 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 8 Jul 2014 15:07:48 +0300 Subject: Bluetooth: Remove unnecessary mgmt_advertising function Since the real advertising state is now tracked with its own flag we can simply set/unset the HCI_ADVERTISING flag in the set_advertising_complete function. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 - net/bluetooth/hci_event.c | 2 -- net/bluetooth/mgmt.c | 17 +++++------------ 3 files changed, 5 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 5701d15779dd..c98de309967e 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1292,7 +1292,6 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered); void mgmt_discoverable_timeout(struct hci_dev *hdev); void mgmt_discoverable(struct hci_dev *hdev, u8 discoverable); void mgmt_connectable(struct hci_dev *hdev, u8 connectable); -void mgmt_advertising(struct hci_dev *hdev, u8 advertising); void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status); void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, bool persistent); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 10f8de9400bc..8fbf604ba228 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1080,8 +1080,6 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb) clear_bit(HCI_LE_ADV, &hdev->dev_flags); } - mgmt_advertising(hdev, *sent); - hci_dev_unlock(hdev); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 9cc7108f4c45..dda1eb124208 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -4034,6 +4034,11 @@ static void set_advertising_complete(struct hci_dev *hdev, u8 status) return; } + if (test_bit(HCI_LE_ADV, &hdev->dev_flags)) + set_bit(HCI_ADVERTISING, &hdev->dev_flags); + else + clear_bit(HCI_ADVERTISING, &hdev->dev_flags); + mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING, hdev, settings_rsp, &match); @@ -5978,18 +5983,6 @@ void mgmt_connectable(struct hci_dev *hdev, u8 connectable) new_settings(hdev, NULL); } -void mgmt_advertising(struct hci_dev *hdev, u8 advertising) -{ - /* Powering off may stop advertising - don't let that interfere */ - if (!advertising && mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) - return; - - if (advertising) - set_bit(HCI_ADVERTISING, &hdev->dev_flags); - else - clear_bit(HCI_ADVERTISING, &hdev->dev_flags); -} - void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status) { u8 mgmt_err = mgmt_status(status); -- cgit v1.2.3 From afbdc6aece93edb6c8f14722782696ef7c439231 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Tue, 8 Jul 2014 10:06:31 +0800 Subject: ACPICA: OSL: Update environments to improve portability This patch contains some environment updates that will be used by acpidump because: 1. The follow-up commits will release osunixxf.c to the Linux kernel for acpidump to link, and 2. Such environment settings will be used to avoid linkage issues. Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- include/acpi/platform/acenv.h | 48 ++++++++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/acpi/platform/acenv.h b/include/acpi/platform/acenv.h index e863dd5c4e04..b6429e46847a 100644 --- a/include/acpi/platform/acenv.h +++ b/include/acpi/platform/acenv.h @@ -87,20 +87,14 @@ #define ACPI_DBG_TRACK_ALLOCATIONS #endif -/* acpi_names configuration. Single threaded with debugger output enabled. */ - -#ifdef ACPI_NAMES_APP -#define ACPI_DEBUGGER -#define ACPI_APPLICATION -#define ACPI_SINGLE_THREADED -#endif - /* - * acpi_bin/acpi_dump/acpi_src/acpi_xtract/Example configuration. All single - * threaded, with no debug output. + * acpi_bin/acpi_dump/acpi_help/acpi_names/acpi_src/acpi_xtract/Example configuration. + * All single threaded. */ #if (defined ACPI_BIN_APP) || \ (defined ACPI_DUMP_APP) || \ + (defined ACPI_HELP_APP) || \ + (defined ACPI_NAMES_APP) || \ (defined ACPI_SRC_APP) || \ (defined ACPI_XTRACT_APP) || \ (defined ACPI_EXAMPLE_APP) @@ -108,12 +102,39 @@ #define ACPI_SINGLE_THREADED #endif +/* acpi_help configuration. Error messages disabled. */ + #ifdef ACPI_HELP_APP -#define ACPI_APPLICATION -#define ACPI_SINGLE_THREADED #define ACPI_NO_ERROR_MESSAGES #endif +/* acpi_names configuration. Debug output enabled. */ + +#ifdef ACPI_NAMES_APP +#define ACPI_DEBUG_OUTPUT +#endif + +/* acpi_exec/acpi_names/Example configuration. Native RSDP used. */ + +#if (defined ACPI_EXEC_APP) || \ + (defined ACPI_EXAMPLE_APP) || \ + (defined ACPI_NAMES_APP) +#define ACPI_USE_NATIVE_RSDP_POINTER +#endif + +/* acpi_dump configuration. Native mapping used if provied by OSPMs */ + +#ifdef ACPI_DUMP_APP +#define ACPI_USE_NATIVE_MEMORY_MAPPING +#endif + +/* acpi_names/Example configuration. Hardware disabled */ + +#if (defined ACPI_EXAMPLE_APP) || \ + (defined ACPI_NAMES_APP) +#define ACPI_REDUCED_HARDWARE 1 +#endif + /* Linkable ACPICA library */ #ifdef ACPI_LIBRARY @@ -185,6 +206,9 @@ #elif defined(_AED_EFI) #include "acefi.h" +#elif defined(_GNU_EFI) +#include "acefi.h" + #elif defined(__HAIKU__) #include "achaiku.h" -- cgit v1.2.3 From 7824f44ecfef4f05caaa8f9536c523b8d645f102 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Tue, 8 Jul 2014 10:06:53 +0800 Subject: ACPICA: OSL: Add portable file IO to improve portability This patch adds portable file IO to generic OSL to improve the portability of the applications. A portable application may use different file IO interfaces than the standard C library ones. This patch thus introduces an abstract file IO layer into the generic OSL. Note that this patch does not introduce users of such interfaces, further patches should introduce users one by one carefully with build tests performed. Lv Zheng. Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- include/acpi/acpiosxf.h | 31 +++ include/acpi/actypes.h | 9 + .../acpi/os_specific/service_layers/oslibcfs.c | 214 +++++++++++++++++++++ 3 files changed, 254 insertions(+) create mode 100644 tools/power/acpi/os_specific/service_layers/oslibcfs.c (limited to 'include') diff --git a/include/acpi/acpiosxf.h b/include/acpi/acpiosxf.h index f6f5f8af2112..03b3e6d405ff 100644 --- a/include/acpi/acpiosxf.h +++ b/include/acpi/acpiosxf.h @@ -399,4 +399,35 @@ char *acpi_os_get_next_filename(void *dir_handle); void acpi_os_close_directory(void *dir_handle); #endif +/* + * File I/O and related support + */ +#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_open_file +ACPI_FILE acpi_os_open_file(const char *path, u8 modes); +#endif + +#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_close_file +void acpi_os_close_file(ACPI_FILE file); +#endif + +#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_read_file +int +acpi_os_read_file(ACPI_FILE file, + void *buffer, acpi_size size, acpi_size count); +#endif + +#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_write_file +int +acpi_os_write_file(ACPI_FILE file, + void *buffer, acpi_size size, acpi_size count); +#endif + +#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_get_file_offset +long acpi_os_get_file_offset(ACPI_FILE file); +#endif + +#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_set_file_offset +acpi_status acpi_os_set_file_offset(ACPI_FILE file, long offset, u8 from); +#endif + #endif /* __ACPIOSXF_H__ */ diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index 19b26bb69a70..5480209b4254 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -1244,4 +1244,13 @@ struct acpi_memory_list { #define ACPI_OSI_WIN_7 0x0B #define ACPI_OSI_WIN_8 0x0C +/* Definitions of file IO */ + +#define ACPI_FILE_READING 0x01 +#define ACPI_FILE_WRITING 0x02 +#define ACPI_FILE_BINARY 0x04 + +#define ACPI_FILE_BEGIN 0x01 +#define ACPI_FILE_END 0x02 + #endif /* __ACTYPES_H__ */ diff --git a/tools/power/acpi/os_specific/service_layers/oslibcfs.c b/tools/power/acpi/os_specific/service_layers/oslibcfs.c new file mode 100644 index 000000000000..2da0ce885c8c --- /dev/null +++ b/tools/power/acpi/os_specific/service_layers/oslibcfs.c @@ -0,0 +1,214 @@ +/****************************************************************************** + * + * Module Name: oslibcfs - C library OSL for file IO + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2014, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include +#include + +#define _COMPONENT ACPI_OS_SERVICES +ACPI_MODULE_NAME("oslibcfs") + +/******************************************************************************* + * + * FUNCTION: acpi_os_open_file + * + * PARAMETERS: path - File path + * modes - File operation type + * + * RETURN: File descriptor. + * + * DESCRIPTION: Open a file for reading (ACPI_FILE_READING) or/and writing + * (ACPI_FILE_WRITING). + * + ******************************************************************************/ +ACPI_FILE acpi_os_open_file(const char *path, u8 modes) +{ + ACPI_FILE file; + char modes_str[4]; + u32 i = 0; + + if (modes & ACPI_FILE_READING) { + modes_str[i++] = 'r'; + } + if (modes & ACPI_FILE_WRITING) { + modes_str[i++] = 'w'; + } + if (modes & ACPI_FILE_BINARY) { + modes_str[i++] = 'b'; + } + modes_str[i++] = '\0'; + + file = fopen(path, modes_str); + if (!file) { + perror("Could not open file"); + } + + return (file); +} + +/******************************************************************************* + * + * FUNCTION: acpi_os_close_file + * + * PARAMETERS: file - File descriptor + * + * RETURN: None. + * + * DESCRIPTION: Close a file. + * + ******************************************************************************/ + +void acpi_os_close_file(ACPI_FILE file) +{ + fclose(file); +} + +/******************************************************************************* + * + * FUNCTION: acpi_os_read_file + * + * PARAMETERS: file - File descriptor + * buffer - Data buffer + * size - Data block size + * count - Number of data blocks + * + * RETURN: Size of successfully read buffer. + * + * DESCRIPTION: Read a file. + * + ******************************************************************************/ + +int +acpi_os_read_file(ACPI_FILE file, void *buffer, acpi_size size, acpi_size count) +{ + int length; + + length = fread(buffer, size, count, file); + if (length < 0) { + perror("Error reading file"); + } + + return (length); +} + +/******************************************************************************* + * + * FUNCTION: acpi_os_write_file + * + * PARAMETERS: file - File descriptor + * buffer - Data buffer + * size - Data block size + * count - Number of data blocks + * + * RETURN: Size of successfully written buffer. + * + * DESCRIPTION: Write a file. + * + ******************************************************************************/ + +int +acpi_os_write_file(ACPI_FILE file, + void *buffer, acpi_size size, acpi_size count) +{ + int length; + + length = fwrite(buffer, size, count, file); + if (length < 0) { + perror("Error writing file"); + } + + return (length); +} + +/******************************************************************************* + * + * FUNCTION: acpi_os_get_file_offset + * + * PARAMETERS: file - File descriptor + * + * RETURN: Size of current position. + * + * DESCRIPTION: Get current file offset. + * + ******************************************************************************/ + +long acpi_os_get_file_offset(ACPI_FILE file) +{ + long offset; + + offset = ftell(file); + + return (offset); +} + +/******************************************************************************* + * + * FUNCTION: acpi_os_set_file_offset + * + * PARAMETERS: file - File descriptor + * offset - File offset + * from - From begin/end of file + * + * RETURN: Status + * + * DESCRIPTION: Set current file offset. + * + ******************************************************************************/ + +acpi_status acpi_os_set_file_offset(ACPI_FILE file, long offset, u8 from) +{ + int ret = 0; + + if (from == ACPI_FILE_BEGIN) { + ret = fseek(file, offset, SEEK_SET); + } + if (from == ACPI_FILE_END) { + ret = fseek(file, offset, SEEK_END); + } + + if (ret < 0) { + return (AE_ERROR); + } else { + return (AE_OK); + } +} -- cgit v1.2.3 From 80a648c12e4bda45c4f1bb93d83184fb3010ac12 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Tue, 8 Jul 2014 10:07:00 +0800 Subject: ACPICA: Utilities: Add formatted printing APIs This patch introduces formatted printing APIs to handle ACPICA specific formatted print requirements. Currently only specific OSPMs will use this customized printing support, Linux kernel doesn't use these APIs at this time. It will be enabled for Linux kernel resident ACPICA after being well tested. So currently this patch is a no-op. The specific formatted printing APIs are useful to ACPICA as: 1. Some portable applications do not link standard C library, so they cannot use standard formatted print APIs directly. 2. Platform specific printing format may differ and thus not portable, for example, u64 is %ull for Linux kernel and is %uI64 for some MSVC versions. 3. Platform specific printing format may conflict with ACPICA's usages while it is not possible for ACPICA developers to test their code for all platforms. For example, developers may generate %pRxxx while Linux kernel treats %pR as structured resource printing and decodes variable argument as a "struct resource" pointer. This patch solves above issues by introducing the new APIs. Note that users of such APIs are not introduced in this patch. Users of acpi_os_file_vprintf()/acpi_ut_file_printf() need to invoke acpi_os_initialize(), this should be taken care by the further patches where such users are introduced. Lv Zheng. Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/Makefile | 2 +- drivers/acpi/acpica/acglobal.h | 5 + drivers/acpi/acpica/acutils.h | 19 + drivers/acpi/acpica/utprint.c | 649 +++++++++++++++++++++ include/acpi/actypes.h | 1 + .../acpi/os_specific/service_layers/osunixxf.c | 7 + 6 files changed, 682 insertions(+), 1 deletion(-) create mode 100644 drivers/acpi/acpica/utprint.c (limited to 'include') diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile index 14b851f1a278..6b9ec239d578 100644 --- a/drivers/acpi/acpica/Makefile +++ b/drivers/acpi/acpica/Makefile @@ -175,5 +175,5 @@ acpi-y += \ utxferror.o \ utxfmutex.o -acpi-$(ACPI_FUTURE_USAGE) += utfileio.o uttrack.o utcache.o +acpi-$(ACPI_FUTURE_USAGE) += utfileio.o utprint.o uttrack.o utcache.o diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index 78cc366ca878..ebf02cc10a43 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -364,6 +364,11 @@ ACPI_GLOBAL(u32, acpi_gbl_num_objects); ACPI_INIT_GLOBAL(ACPI_FILE, acpi_gbl_debug_file, NULL); ACPI_INIT_GLOBAL(ACPI_FILE, acpi_gbl_output_file, NULL); +/* Print buffer */ + +ACPI_GLOBAL(acpi_spinlock, acpi_gbl_print_lock); /* For print buffer */ +ACPI_GLOBAL(char, acpi_gbl_print_buffer[1024]); + #endif /* ACPI_APPLICATION */ /***************************************************************************** diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h index e0adbc1f52ed..85871c639cc2 100644 --- a/drivers/acpi/acpica/acutils.h +++ b/drivers/acpi/acpica/acutils.h @@ -750,4 +750,23 @@ const struct ah_predefined_name *acpi_ah_match_predefined_name(char *nameseg); const struct ah_device_id *acpi_ah_match_hardware_id(char *hid); +/* + * utprint - printf/vprintf output functions + */ +const char *acpi_ut_scan_number(const char *string, u64 *number_ptr); + +const char *acpi_ut_print_number(char *string, u64 number); + +int +acpi_ut_vsnprintf(char *string, + acpi_size size, const char *format, va_list args); + +int acpi_ut_snprintf(char *string, acpi_size size, const char *format, ...); + +#ifdef ACPI_APPLICATION +int acpi_ut_file_vprintf(ACPI_FILE file, const char *format, va_list args); + +int acpi_ut_file_printf(ACPI_FILE file, const char *format, ...); +#endif + #endif /* _ACUTILS_H */ diff --git a/drivers/acpi/acpica/utprint.c b/drivers/acpi/acpica/utprint.c new file mode 100644 index 000000000000..9d5293fd992f --- /dev/null +++ b/drivers/acpi/acpica/utprint.c @@ -0,0 +1,649 @@ +/****************************************************************************** + * + * Module Name: utprint - Formatted printing routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2014, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utprint") + +#define ACPI_FORMAT_SIGN 0x01 +#define ACPI_FORMAT_SIGN_PLUS 0x02 +#define ACPI_FORMAT_SIGN_PLUS_SPACE 0x04 +#define ACPI_FORMAT_ZERO 0x08 +#define ACPI_FORMAT_LEFT 0x10 +#define ACPI_FORMAT_UPPER 0x20 +#define ACPI_FORMAT_PREFIX 0x40 +/* Local prototypes */ +static acpi_size +acpi_ut_bound_string_length(const char *string, acpi_size count); + +static char *acpi_ut_bound_string_output(char *string, const char *end, char c); + +static char *acpi_ut_format_number(char *string, + char *end, + u64 number, + u8 base, s32 width, s32 precision, u8 type); + +static char *acpi_ut_put_number(char *string, u64 number, u8 base, u8 upper); + +/******************************************************************************* + * + * FUNCTION: acpi_ut_bound_string_length + * + * PARAMETERS: string - String with boundary + * count - Boundary of the string + * + * RETURN: Length of the string. + * + * DESCRIPTION: Calculate the length of a string with boundary. + * + ******************************************************************************/ + +static acpi_size +acpi_ut_bound_string_length(const char *string, acpi_size count) +{ + u32 length = 0; + + while (*string && count) { + length++; + string++; + count--; + } + + return (length); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_bound_string_output + * + * PARAMETERS: string - String with boundary + * end - Boundary of the string + * c - Character to be output to the string + * + * RETURN: Updated position for next valid character + * + * DESCRIPTION: Output a character into a string with boundary check. + * + ******************************************************************************/ + +static char *acpi_ut_bound_string_output(char *string, const char *end, char c) +{ + + if (string < end) { + *string = c; + } + ++string; + + return (string); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_put_number + * + * PARAMETERS: string - Buffer to hold reverse-ordered string + * number - Integer to be converted + * base - Base of the integer + * upper - Whether or not using upper cased digits + * + * RETURN: Updated position for next valid character + * + * DESCRIPTION: Convert an integer into a string, note that, the string holds a + * reversed ordered number without the trailing zero. + * + ******************************************************************************/ + +static char *acpi_ut_put_number(char *string, u64 number, u8 base, u8 upper) +{ + const char lower_digits[] = "0123456789abcdef"; + const char upper_digits[] = "0123456789ABCDEF"; + const char *digits; + u64 digit_index; + char *pos; + + pos = string; + digits = upper ? upper_digits : lower_digits; + + if (number == 0) { + *(pos++) = '0'; + } else { + while (number) { + (void)acpi_ut_divide(number, base, &number, + &digit_index); + *(pos++) = digits[digit_index]; + } + } + /* *(Pos++) = '0'; */ + + return (pos); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_scan_number + * + * PARAMETERS: string - String buffer + * number_ptr - Where the number is returned + * + * RETURN: Updated position for next valid character + * + * DESCRIPTION: Scan a string for a decimal integer. + * + ******************************************************************************/ + +const char *acpi_ut_scan_number(const char *string, u64 *number_ptr) +{ + u64 number = 0; + + while (ACPI_IS_DIGIT(*string)) { + number *= 10; + number += *(string++) - '0'; + } + *number_ptr = number; + + return (string); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_print_number + * + * PARAMETERS: string - String buffer + * number - The number to be converted + * + * RETURN: Updated position for next valid character + * + * DESCRIPTION: Print a decimal integer into a string. + * + ******************************************************************************/ + +const char *acpi_ut_print_number(char *string, u64 number) +{ + char ascii_string[20]; + const char *pos1; + char *pos2; + + pos1 = acpi_ut_put_number(ascii_string, number, 10, FALSE); + pos2 = string; + + while (pos1 != ascii_string) { + *(pos2++) = *(--pos1); + } + *pos2 = 0; + + return (string); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_format_number + * + * PARAMETERS: string - String buffer with boundary + * end - Boundary of the string + * number - The number to be converted + * base - Base of the integer + * width - Field width + * precision - Precision of the integer + * type - Special printing flags + * + * RETURN: Updated position for next valid character + * + * DESCRIPTION: Print an integer into a string with any base and any precision. + * + ******************************************************************************/ + +static char *acpi_ut_format_number(char *string, + char *end, + u64 number, + u8 base, s32 width, s32 precision, u8 type) +{ + char sign; + char zero; + u8 need_prefix; + u8 upper; + s32 i; + char reversed_string[66]; + + /* Perform sanity checks */ + + if (base < 2 || base > 16) { + return NULL; + } + if (type & ACPI_FORMAT_LEFT) { + type &= ~ACPI_FORMAT_ZERO; + } + + need_prefix = ((type & ACPI_FORMAT_PREFIX) + && base != 10) ? TRUE : FALSE; + upper = (type & ACPI_FORMAT_UPPER) ? TRUE : FALSE; + zero = (type & ACPI_FORMAT_ZERO) ? '0' : ' '; + + /* Calculate size according to sign and prefix */ + + sign = '\0'; + if (type & ACPI_FORMAT_SIGN) { + if ((s64) number < 0) { + sign = '-'; + number = -(s64) number; + width--; + } else if (type & ACPI_FORMAT_SIGN_PLUS) { + sign = '+'; + width--; + } else if (type & ACPI_FORMAT_SIGN_PLUS_SPACE) { + sign = ' '; + width--; + } + } + if (need_prefix) { + width--; + if (base == 16) { + width--; + } + } + + /* Generate full string in reverse order */ + + i = ACPI_PTR_DIFF(acpi_ut_put_number + (reversed_string, number, base, upper), + reversed_string); + + /* Printing 100 using %2d gives "100", not "00" */ + + if (i > precision) { + precision = i; + } + width -= precision; + + /* Output the string */ + + if (!(type & (ACPI_FORMAT_ZERO | ACPI_FORMAT_LEFT))) { + while (--width >= 0) { + string = acpi_ut_bound_string_output(string, end, ' '); + } + } + if (sign) { + string = acpi_ut_bound_string_output(string, end, sign); + } + if (need_prefix) { + string = acpi_ut_bound_string_output(string, end, '0'); + if (base == 16) { + string = acpi_ut_bound_string_output(string, end, + upper ? 'X' : 'x'); + } + } + if (!(type & ACPI_FORMAT_LEFT)) { + while (--width >= 0) { + string = acpi_ut_bound_string_output(string, end, zero); + } + } + while (i <= --precision) { + string = acpi_ut_bound_string_output(string, end, '0'); + } + while (--i >= 0) { + string = acpi_ut_bound_string_output(string, end, + reversed_string[i]); + } + while (--width >= 0) { + string = acpi_ut_bound_string_output(string, end, ' '); + } + + return (string); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_vsnprintf + * + * PARAMETERS: string - String with boundary + * size - Boundary of the string + * format - Standard printf format + * args - Argument list + * + * RETURN: Size of successfully output bytes + * + * DESCRIPTION: Formatted output to a string using argument list pointer. + * + ******************************************************************************/ + +int +acpi_ut_vsnprintf(char *string, + acpi_size size, const char *format, va_list args) +{ + u8 base = 10; + u8 type = 0; + s32 width = -1; + s32 precision = -1; + char qualifier = 0; + u64 number; + char *pos; + char *end; + char c; + const char *s; + const void *p; + s32 length; + int i; + + pos = string; + end = string + size; + + for (; *format; ++format) { + if (*format != '%') { + pos = acpi_ut_bound_string_output(pos, end, *format); + continue; + } + + /* Process sign */ + + do { + ++format; + if (*format == '#') { + type |= ACPI_FORMAT_PREFIX; + } else if (*format == '0') { + type |= ACPI_FORMAT_ZERO; + } else if (*format == '+') { + type |= ACPI_FORMAT_SIGN_PLUS; + } else if (*format == ' ') { + type |= ACPI_FORMAT_SIGN_PLUS_SPACE; + } else if (*format == '-') { + type |= ACPI_FORMAT_LEFT; + } else { + break; + } + } while (1); + + /* Process width */ + + if (ACPI_IS_DIGIT(*format)) { + format = acpi_ut_scan_number(format, &number); + width = (s32) number; + } else if (*format == '*') { + ++format; + width = va_arg(args, int); + if (width < 0) { + width = -width; + type |= ACPI_FORMAT_LEFT; + } + } + + /* Process precision */ + + if (*format == '.') { + ++format; + if (ACPI_IS_DIGIT(*format)) { + format = acpi_ut_scan_number(format, &number); + precision = (s32) number; + } else if (*format == '*') { + ++format; + precision = va_arg(args, int); + } + if (precision < 0) { + precision = 0; + } + } + + /* Process qualifier */ + + if (*format == 'h' || *format == 'l' || *format == 'L') { + qualifier = *format; + ++format; + if (qualifier == 'l' && *format == 'l') { + qualifier = 'L'; + ++format; + } + } + + switch (*format) { + case '%': + + pos = acpi_ut_bound_string_output(pos, end, '%'); + continue; + + case 'c': + + if (!(type & ACPI_FORMAT_LEFT)) { + while (--width > 0) { + pos = + acpi_ut_bound_string_output(pos, + end, + ' '); + } + } + c = (char)va_arg(args, int); + pos = acpi_ut_bound_string_output(pos, end, c); + while (--width > 0) { + pos = + acpi_ut_bound_string_output(pos, end, ' '); + } + continue; + + case 's': + + s = va_arg(args, char *); + if (!s) { + s = ""; + } + length = acpi_ut_bound_string_length(s, precision); + if (!(type & ACPI_FORMAT_LEFT)) { + while (length < width--) { + pos = + acpi_ut_bound_string_output(pos, + end, + ' '); + } + } + for (i = 0; i < length; ++i) { + pos = acpi_ut_bound_string_output(pos, end, *s); + ++s; + } + while (length < width--) { + pos = + acpi_ut_bound_string_output(pos, end, ' '); + } + continue; + + case 'o': + + base = 8; + break; + + case 'X': + + type |= ACPI_FORMAT_UPPER; + + case 'x': + + base = 16; + break; + + case 'd': + case 'i': + + type |= ACPI_FORMAT_SIGN; + + case 'u': + + break; + + case 'p': + + if (width == -1) { + width = 2 * sizeof(void *); + type |= ACPI_FORMAT_ZERO; + } + p = va_arg(args, void *); + pos = acpi_ut_format_number(pos, end, + ACPI_TO_INTEGER(p), + 16, width, precision, type); + continue; + + default: + + pos = acpi_ut_bound_string_output(pos, end, '%'); + if (*format) { + pos = + acpi_ut_bound_string_output(pos, end, + *format); + } else { + --format; + } + continue; + } + + if (qualifier == 'L') { + number = va_arg(args, u64); + if (type & ACPI_FORMAT_SIGN) { + number = (s64) number; + } + } else if (qualifier == 'l') { + number = va_arg(args, unsigned long); + if (type & ACPI_FORMAT_SIGN) { + number = (s32) number; + } + } else if (qualifier == 'h') { + number = (u16)va_arg(args, int); + if (type & ACPI_FORMAT_SIGN) { + number = (s16) number; + } + } else { + number = va_arg(args, unsigned int); + if (type & ACPI_FORMAT_SIGN) { + number = (signed int)number; + } + } + pos = acpi_ut_format_number(pos, end, number, base, + width, precision, type); + } + + if (size > 0) { + if (pos < end) { + *pos = '\0'; + } else { + end[-1] = '\0'; + } + } + + return (ACPI_PTR_DIFF(pos, string)); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_snprintf + * + * PARAMETERS: string - String with boundary + * size - Boundary of the string + * Format, ... - Standard printf format + * + * RETURN: Size of successfully output bytes + * + * DESCRIPTION: Formatted output to a string. + * + ******************************************************************************/ + +int acpi_ut_snprintf(char *string, acpi_size size, const char *format, ...) +{ + va_list args; + int length; + + va_start(args, format); + length = acpi_ut_vsnprintf(string, size, format, args); + va_end(args); + + return (length); +} + +#ifdef ACPI_APPLICATION +/******************************************************************************* + * + * FUNCTION: acpi_ut_file_vprintf + * + * PARAMETERS: file - File descriptor + * format - Standard printf format + * args - Argument list + * + * RETURN: Size of successfully output bytes + * + * DESCRIPTION: Formatted output to a file using argument list pointer. + * + ******************************************************************************/ + +int acpi_ut_file_vprintf(ACPI_FILE file, const char *format, va_list args) +{ + acpi_cpu_flags flags; + int length; + + flags = acpi_os_acquire_lock(acpi_gbl_print_lock); + length = acpi_ut_vsnprintf(acpi_gbl_print_buffer, + sizeof(acpi_gbl_print_buffer), format, args); + (void)acpi_os_write_file(file, acpi_gbl_print_buffer, length, 1); + acpi_os_release_lock(acpi_gbl_print_lock, flags); + + return (length); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_file_printf + * + * PARAMETERS: file - File descriptor + * Format, ... - Standard printf format + * + * RETURN: Size of successfully output bytes + * + * DESCRIPTION: Formatted output to a file. + * + ******************************************************************************/ + +int acpi_ut_file_printf(ACPI_FILE file, const char *format, ...) +{ + va_list args; + int length; + + va_start(args, format); + length = acpi_ut_file_vprintf(file, format, args); + va_end(args); + + return (length); +} +#endif diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index 5480209b4254..165abcae5548 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -126,6 +126,7 @@ typedef unsigned char u8; typedef unsigned char u8; typedef unsigned short u16; +typedef short s16; typedef COMPILER_DEPENDENT_UINT64 u64; typedef COMPILER_DEPENDENT_INT64 s64; diff --git a/tools/power/acpi/os_specific/service_layers/osunixxf.c b/tools/power/acpi/os_specific/service_layers/osunixxf.c index 0e6a8a6be441..60b58cd18410 100644 --- a/tools/power/acpi/os_specific/service_layers/osunixxf.c +++ b/tools/power/acpi/os_specific/service_layers/osunixxf.c @@ -182,10 +182,17 @@ static void os_exit_line_edit_mode(void) acpi_status acpi_os_initialize(void) { + acpi_status status; acpi_gbl_output_file = stdout; os_enter_line_edit_mode(); + + status = acpi_os_create_lock(&acpi_gbl_print_lock); + if (ACPI_FAILURE(status)) { + return (status); + } + return (AE_OK); } -- cgit v1.2.3 From 2e70da4c51eb18a4d229aa48763c15f5dbc5b43d Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Tue, 8 Jul 2014 10:07:06 +0800 Subject: ACPICA: Utilities: Introduce acpi_log_error() to improve portability Invocations like fprintf(stderr) and perror() are not portable, this patch introduces acpi_log_error() as a replacement, it is implemented using new portable API - acpi_ut_file_vprintf(). Note that though acpi_os_initialize() need to be invoked prior than using this new API, since no users are introduced in this patch, such invocations are not added for applications that link utprint.c in this patch. Futher patches that introduce users of acpi_log_error() should take care of this. This patch is only useful for ACPICA applications, most of which are not shipped in the Linux kernel. Note that follow-up commits will update acpidump to use this new API to improve portability. Lv Zheng. Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/utdebug.c | 26 ++++++++++++++++++++++++++ include/acpi/acpixf.h | 20 ++++++++++++++++++++ include/acpi/platform/acenv.h | 4 ++++ 3 files changed, 50 insertions(+) (limited to 'include') diff --git a/drivers/acpi/acpica/utdebug.c b/drivers/acpi/acpica/utdebug.c index 21a20ac5b1e1..e516254c63b2 100644 --- a/drivers/acpi/acpica/utdebug.c +++ b/drivers/acpi/acpica/utdebug.c @@ -561,3 +561,29 @@ acpi_ut_ptr_exit(u32 line_number, } #endif + +#ifdef ACPI_APPLICATION +/******************************************************************************* + * + * FUNCTION: acpi_log_error + * + * PARAMETERS: format - Printf format field + * ... - Optional printf arguments + * + * RETURN: None + * + * DESCRIPTION: Print error message to the console, used by applications. + * + ******************************************************************************/ + +void ACPI_INTERNAL_VAR_XFACE acpi_log_error(const char *format, ...) +{ + va_list args; + + va_start(args, format); + (void)acpi_ut_file_vprintf(ACPI_FILE_ERR, format, args); + va_end(args); +} + +ACPI_EXPORT_SYMBOL(acpi_log_error) +#endif diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 35b525c19711..9858551fc84a 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -335,6 +335,23 @@ ACPI_GLOBAL(u8, acpi_gbl_system_awake_and_running); #endif /* ACPI_DEBUG_OUTPUT */ +/* + * Application prototypes + * + * All interfaces used by application will be configured + * out of the ACPICA build unless the ACPI_APPLICATION + * flag is defined. + */ +#ifdef ACPI_APPLICATION +#define ACPI_APP_DEPENDENT_RETURN_VOID(prototype) \ + prototype; + +#else +#define ACPI_APP_DEPENDENT_RETURN_VOID(prototype) \ + static ACPI_INLINE prototype {return;} + +#endif /* ACPI_APPLICATION */ + /***************************************************************************** * * ACPICA public interface prototypes @@ -861,6 +878,9 @@ ACPI_DBG_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(6) const char *module_name, u32 component_id, const char *format, ...)) +ACPI_APP_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(1) + void ACPI_INTERNAL_VAR_XFACE + acpi_log_error(const char *format, ...)) /* * Divergences diff --git a/include/acpi/platform/acenv.h b/include/acpi/platform/acenv.h index b6429e46847a..4390ebd99fda 100644 --- a/include/acpi/platform/acenv.h +++ b/include/acpi/platform/acenv.h @@ -423,8 +423,12 @@ typedef char *va_list; #ifdef ACPI_APPLICATION #include #define ACPI_FILE FILE * +#define ACPI_FILE_OUT stdout +#define ACPI_FILE_ERR stderr #else #define ACPI_FILE void * +#define ACPI_FILE_OUT NULL +#define ACPI_FILE_ERR NULL #endif /* ACPI_APPLICATION */ #endif /* ACPI_FILE */ -- cgit v1.2.3 From a92e95773d8e2cd58124aea147cb833f33f1e196 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Tue, 8 Jul 2014 10:07:26 +0800 Subject: ACPICA: Common: Enhance acpi_getopt() to improve portability This patch enhances acpi_getopt() by converting the standard C library invocations into portable ACPI string APIs and acpi_log_error() to improve portability. Lv Zheng. Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/acutils.h | 2 ++ include/acpi/actypes.h | 4 ++++ tools/power/acpi/common/getopt.c | 14 ++++++-------- tools/power/acpi/tools/acpidump/apmain.c | 3 ++- 4 files changed, 14 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h index 85871c639cc2..035dc29798bb 100644 --- a/drivers/acpi/acpica/acutils.h +++ b/drivers/acpi/acpica/acutils.h @@ -210,6 +210,8 @@ void acpi_ut_subsystem_shutdown(void); acpi_size acpi_ut_strlen(const char *string); +char *acpi_ut_strchr(const char *string, int ch); + char *acpi_ut_strcpy(char *dst_string, const char *src_string); char *acpi_ut_strncpy(char *dst_string, diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index 165abcae5548..608a04019372 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -1254,4 +1254,8 @@ struct acpi_memory_list { #define ACPI_FILE_BEGIN 0x01 #define ACPI_FILE_END 0x02 +/* Definitions of getopt */ + +#define ACPI_OPT_END -1 + #endif /* __ACTYPES_H__ */ diff --git a/tools/power/acpi/common/getopt.c b/tools/power/acpi/common/getopt.c index a302f52e4fd3..2f0f34a36db4 100644 --- a/tools/power/acpi/common/getopt.c +++ b/tools/power/acpi/common/getopt.c @@ -51,14 +51,12 @@ * "f|" - Option has required single-char sub-options */ -#include -#include #include #include "accommon.h" #include "acapps.h" #define ACPI_OPTION_ERROR(msg, badchar) \ - if (acpi_gbl_opterr) {fprintf (stderr, "%s%c\n", msg, badchar);} + if (acpi_gbl_opterr) {acpi_log_error ("%s%c\n", msg, badchar);} int acpi_gbl_opterr = 1; int acpi_gbl_optind = 1; @@ -113,7 +111,7 @@ int acpi_getopt_argument(int argc, char **argv) * PARAMETERS: argc, argv - from main * opts - options info list * - * RETURN: Option character or EOF + * RETURN: Option character or ACPI_OPT_END * * DESCRIPTION: Get the next option * @@ -128,10 +126,10 @@ int acpi_getopt(int argc, char **argv, char *opts) if (acpi_gbl_optind >= argc || argv[acpi_gbl_optind][0] != '-' || argv[acpi_gbl_optind][1] == '\0') { - return (EOF); - } else if (strcmp(argv[acpi_gbl_optind], "--") == 0) { + return (ACPI_OPT_END); + } else if (ACPI_STRCMP(argv[acpi_gbl_optind], "--") == 0) { acpi_gbl_optind++; - return (EOF); + return (ACPI_OPT_END); } } @@ -142,7 +140,7 @@ int acpi_getopt(int argc, char **argv, char *opts) /* Make sure that the option is legal */ if (current_char == ':' || - (opts_ptr = strchr(opts, current_char)) == NULL) { + (opts_ptr = ACPI_STRCHR(opts, current_char)) == NULL) { ACPI_OPTION_ERROR("Illegal option: -", current_char); if (argv[acpi_gbl_optind][++current_char_ptr] == '\0') { diff --git a/tools/power/acpi/tools/acpidump/apmain.c b/tools/power/acpi/tools/acpidump/apmain.c index 2a06a864c093..9c3b259aed2c 100644 --- a/tools/power/acpi/tools/acpidump/apmain.c +++ b/tools/power/acpi/tools/acpidump/apmain.c @@ -166,7 +166,8 @@ static int ap_do_options(int argc, char **argv) /* Command line options */ - while ((j = acpi_getopt(argc, argv, AP_SUPPORTED_OPTIONS)) != EOF) + while ((j = + acpi_getopt(argc, argv, AP_SUPPORTED_OPTIONS)) != ACPI_OPT_END) switch (j) { /* * Global options -- cgit v1.2.3 From fbee6b21a3820a55ec36a2aceb138f3973a955ac Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Tue, 8 Jul 2014 10:07:33 +0800 Subject: ACPICA: acpidump: Add memory/string OSL usage to improve portability This patch adds code to use generic OSL for acpidump to improve the portability of this tool. Lv Zheng. Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- include/acpi/platform/acenv.h | 1 + tools/power/acpi/tools/acpidump/apdump.c | 16 ++++++++-------- tools/power/acpi/tools/acpidump/apfiles.c | 11 ++++++----- 3 files changed, 15 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/acpi/platform/acenv.h b/include/acpi/platform/acenv.h index 4390ebd99fda..5f8cc1fa3278 100644 --- a/include/acpi/platform/acenv.h +++ b/include/acpi/platform/acenv.h @@ -126,6 +126,7 @@ #ifdef ACPI_DUMP_APP #define ACPI_USE_NATIVE_MEMORY_MAPPING +#define USE_NATIVE_ALLOCATE_ZEROED #endif /* acpi_names/Example configuration. Hardware disabled */ diff --git a/tools/power/acpi/tools/acpidump/apdump.c b/tools/power/acpi/tools/acpidump/apdump.c index e8729aa3a60e..ccb6262829d6 100644 --- a/tools/power/acpi/tools/acpidump/apdump.c +++ b/tools/power/acpi/tools/acpidump/apdump.c @@ -252,7 +252,7 @@ int ap_dump_all_tables(void) } table_status = ap_dump_table_buffer(table, instance, address); - free(table); + ACPI_FREE(table); if (table_status) { break; @@ -303,7 +303,7 @@ int ap_dump_table_by_address(char *ascii_address) } table_status = ap_dump_table_buffer(table, 0, address); - free(table); + ACPI_FREE(table); return (table_status); } @@ -329,7 +329,7 @@ int ap_dump_table_by_name(char *signature) acpi_status status; int table_status; - if (strlen(signature) != ACPI_NAME_SIZE) { + if (ACPI_STRLEN(signature) != ACPI_NAME_SIZE) { fprintf(stderr, "Invalid table signature [%s]: must be exactly 4 characters\n", signature); @@ -338,15 +338,15 @@ int ap_dump_table_by_name(char *signature) /* Table signatures are expected to be uppercase */ - strcpy(local_signature, signature); + ACPI_STRCPY(local_signature, signature); acpi_ut_strupr(local_signature); /* To be friendly, handle tables whose signatures do not match the name */ if (ACPI_COMPARE_NAME(local_signature, "FADT")) { - strcpy(local_signature, ACPI_SIG_FADT); + ACPI_STRCPY(local_signature, ACPI_SIG_FADT); } else if (ACPI_COMPARE_NAME(local_signature, "MADT")) { - strcpy(local_signature, ACPI_SIG_MADT); + ACPI_STRCPY(local_signature, ACPI_SIG_MADT); } /* Dump all instances of this signature (to handle multiple SSDTs) */ @@ -369,7 +369,7 @@ int ap_dump_table_by_name(char *signature) } table_status = ap_dump_table_buffer(table, instance, address); - free(table); + ACPI_FREE(table); if (table_status) { break; @@ -424,6 +424,6 @@ int ap_dump_table_from_file(char *pathname) table_status = ap_dump_table_buffer(table, 0, 0); exit: - free(table); + ACPI_FREE(table); return (table_status); } diff --git a/tools/power/acpi/tools/acpidump/apfiles.c b/tools/power/acpi/tools/acpidump/apfiles.c index 4488accc010b..1d12b9d3d44e 100644 --- a/tools/power/acpi/tools/acpidump/apfiles.c +++ b/tools/power/acpi/tools/acpidump/apfiles.c @@ -130,11 +130,12 @@ int ap_write_to_binary_file(struct acpi_table_header *table, u32 instance) /* Handle multiple SSDts - create different filenames for each */ if (instance > 0) { - sprintf(instance_str, "%u", instance); - strcat(filename, instance_str); + acpi_ut_snprintf(instance_str, sizeof(instance_str), "%u", + instance); + ACPI_STRCAT(filename, instance_str); } - strcat(filename, ACPI_TABLE_FILE_SUFFIX); + ACPI_STRCAT(filename, ACPI_TABLE_FILE_SUFFIX); if (gbl_verbose_mode) { fprintf(stderr, @@ -202,7 +203,7 @@ struct acpi_table_header *ap_get_table_from_file(char *pathname, /* Allocate a buffer for the entire file */ - buffer = calloc(1, file_size); + buffer = ACPI_ALLOCATE_ZEROED(file_size); if (!buffer) { fprintf(stderr, "Could not allocate file buffer of size: %u\n", @@ -215,7 +216,7 @@ struct acpi_table_header *ap_get_table_from_file(char *pathname, actual = fread(buffer, 1, file_size, file); if (actual != file_size) { fprintf(stderr, "Could not read input file: %s\n", pathname); - free(buffer); + ACPI_FREE(buffer); buffer = NULL; goto cleanup; } -- cgit v1.2.3 From 1538ac64f532431a40e2f6c528f1ee108d5fa431 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Tue, 8 Jul 2014 10:08:25 +0800 Subject: ACPICA: Tables: Merge DMAR table structure updates This patch is a back port result of the following Linux commit: Author: David Woodhouse Subject: iommu/vt-d: Add ACPI namespace device reporting structures ACPICA need to handle old compilers where u8 object_name[] is only allowed for an initialized variable. This patch reduces back port source code differences between Linux and ACPICA upstream. Cc: David Woodhouse Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- include/acpi/actbl2.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h index 860e5c883eb3..21314d37cb07 100644 --- a/include/acpi/actbl2.h +++ b/include/acpi/actbl2.h @@ -516,7 +516,7 @@ struct acpi_dmar_andd { struct acpi_dmar_header header; u8 reserved[3]; u8 device_number; - u8 object_name[]; + char object_name[1]; }; /******************************************************************************* -- cgit v1.2.3 From 587fc727ad7bbb475d7663921e4f731de6e21857 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Tue, 8 Jul 2014 10:08:31 +0800 Subject: ACPICA: Update version to 20140627 Version 20140627. Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- 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 9858551fc84a..3f6e14ff24b1 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -46,7 +46,7 @@ /* Current ACPICA subsystem version in YYYYMMDD format */ -#define ACPI_CA_VERSION 0x20140424 +#define ACPI_CA_VERSION 0x20140627 #include #include -- cgit v1.2.3 From 2e3fadbf730fd0d13c891d5e555af3e7f39ca3f4 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 1 Jul 2014 16:40:19 +0100 Subject: PKCS#7: Implement a parser [RFC 2315] Implement a parser for a PKCS#7 signed-data message as described in part of RFC 2315. Signed-off-by: David Howells Acked-by: Vivek Goyal Reviewed-by: Kees Cook --- crypto/asymmetric_keys/Kconfig | 9 + crypto/asymmetric_keys/Makefile | 13 ++ crypto/asymmetric_keys/pkcs7.asn1 | 127 +++++++++++ crypto/asymmetric_keys/pkcs7_parser.c | 396 ++++++++++++++++++++++++++++++++++ crypto/asymmetric_keys/pkcs7_parser.h | 61 ++++++ include/crypto/pkcs7.h | 23 ++ include/linux/oid_registry.h | 1 + 7 files changed, 630 insertions(+) create mode 100644 crypto/asymmetric_keys/pkcs7.asn1 create mode 100644 crypto/asymmetric_keys/pkcs7_parser.c create mode 100644 crypto/asymmetric_keys/pkcs7_parser.h create mode 100644 include/crypto/pkcs7.h (limited to 'include') diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 03a6eb95ab50..a7cec9dd6154 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -37,4 +37,13 @@ config X509_CERTIFICATE_PARSER data and provides the ability to instantiate a crypto key from a public key packet found inside the certificate. +config PKCS7_MESSAGE_PARSER + tristate "PKCS#7 message parser" + depends on X509_CERTIFICATE_PARSER + select ASN1 + select OID_REGISTRY + help + This option provides support for parsing PKCS#7 format messages for + signature data and provides the ability to verify the signature. + endif # ASYMMETRIC_KEY_TYPE diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index 0727204aab68..59d8cade5cfe 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -25,3 +25,16 @@ $(obj)/x509_rsakey-asn1.o: $(obj)/x509_rsakey-asn1.c $(obj)/x509_rsakey-asn1.h clean-files += x509-asn1.c x509-asn1.h clean-files += x509_rsakey-asn1.c x509_rsakey-asn1.h + +# +# PKCS#7 message handling +# +obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pkcs7_message.o +pkcs7_message-y := \ + pkcs7-asn1.o \ + pkcs7_parser.o + +$(obj)/pkcs7_parser.o: $(obj)/pkcs7-asn1.h +$(obj)/pkcs7-asn1.o: $(obj)/pkcs7-asn1.c $(obj)/pkcs7-asn1.h + +clean-files += pkcs7-asn1.c pkcs7-asn1.h diff --git a/crypto/asymmetric_keys/pkcs7.asn1 b/crypto/asymmetric_keys/pkcs7.asn1 new file mode 100644 index 000000000000..a5a14ef28c86 --- /dev/null +++ b/crypto/asymmetric_keys/pkcs7.asn1 @@ -0,0 +1,127 @@ +PKCS7ContentInfo ::= SEQUENCE { + contentType ContentType, + content [0] EXPLICIT SignedData OPTIONAL +} + +ContentType ::= OBJECT IDENTIFIER ({ pkcs7_note_OID }) + +SignedData ::= SEQUENCE { + version INTEGER, + digestAlgorithms DigestAlgorithmIdentifiers, + contentInfo ContentInfo, + certificates CHOICE { + certSet [0] IMPLICIT ExtendedCertificatesAndCertificates, + certSequence [2] IMPLICIT Certificates + } OPTIONAL ({ pkcs7_note_certificate_list }), + crls CHOICE { + crlSet [1] IMPLICIT CertificateRevocationLists, + crlSequence [3] IMPLICIT CRLSequence + } OPTIONAL, + signerInfos SignerInfos +} + +ContentInfo ::= SEQUENCE { + contentType ContentType, + content [0] EXPLICIT Data OPTIONAL +} + +Data ::= ANY ({ pkcs7_note_data }) + +DigestAlgorithmIdentifiers ::= CHOICE { + daSet SET OF DigestAlgorithmIdentifier, + daSequence SEQUENCE OF DigestAlgorithmIdentifier +} + +DigestAlgorithmIdentifier ::= SEQUENCE { + algorithm OBJECT IDENTIFIER ({ pkcs7_note_OID }), + parameters ANY OPTIONAL +} + +-- +-- Certificates and certificate lists +-- +ExtendedCertificatesAndCertificates ::= SET OF ExtendedCertificateOrCertificate + +ExtendedCertificateOrCertificate ::= CHOICE { + certificate Certificate, -- X.509 + extendedCertificate [0] IMPLICIT ExtendedCertificate -- PKCS#6 +} + +ExtendedCertificate ::= Certificate -- cheating + +Certificates ::= SEQUENCE OF Certificate + +CertificateRevocationLists ::= SET OF CertificateList + +CertificateList ::= SEQUENCE OF Certificate -- This may be defined incorrectly + +CRLSequence ::= SEQUENCE OF CertificateList + +Certificate ::= ANY ({ pkcs7_extract_cert }) -- X.509 + +-- +-- Signer information +-- +SignerInfos ::= CHOICE { + siSet SET OF SignerInfo, + siSequence SEQUENCE OF SignerInfo +} + +SignerInfo ::= SEQUENCE { + version INTEGER, + issuerAndSerialNumber IssuerAndSerialNumber, + digestAlgorithm DigestAlgorithmIdentifier ({ pkcs7_sig_note_digest_algo }), + authenticatedAttributes CHOICE { + aaSet [0] IMPLICIT SetOfAuthenticatedAttribute + ({ pkcs7_sig_note_set_of_authattrs }), + aaSequence [2] EXPLICIT SEQUENCE OF AuthenticatedAttribute + -- Explicit because easier to compute digest on + -- sequence of attributes and then reuse encoded + -- sequence in aaSequence. + } OPTIONAL, + digestEncryptionAlgorithm + DigestEncryptionAlgorithmIdentifier ({ pkcs7_sig_note_pkey_algo }), + encryptedDigest EncryptedDigest, + unauthenticatedAttributes CHOICE { + uaSet [1] IMPLICIT SET OF UnauthenticatedAttribute, + uaSequence [3] IMPLICIT SEQUENCE OF UnauthenticatedAttribute + } OPTIONAL +} ({ pkcs7_note_signed_info }) + +IssuerAndSerialNumber ::= SEQUENCE { + issuer Name ({ pkcs7_sig_note_issuer }), + serialNumber CertificateSerialNumber ({ pkcs7_sig_note_serial }) +} + +CertificateSerialNumber ::= INTEGER + +SetOfAuthenticatedAttribute ::= SET OF AuthenticatedAttribute + +AuthenticatedAttribute ::= SEQUENCE { + type OBJECT IDENTIFIER ({ pkcs7_note_OID }), + values SET OF ANY ({ pkcs7_sig_note_authenticated_attr }) +} + +UnauthenticatedAttribute ::= SEQUENCE { + type OBJECT IDENTIFIER ({ pkcs7_note_OID }), + values SET OF ANY +} + +DigestEncryptionAlgorithmIdentifier ::= SEQUENCE { + algorithm OBJECT IDENTIFIER ({ pkcs7_note_OID }), + parameters ANY OPTIONAL +} + +EncryptedDigest ::= OCTET STRING ({ pkcs7_sig_note_signature }) + +--- +--- X.500 Name +--- +Name ::= SEQUENCE OF RelativeDistinguishedName + +RelativeDistinguishedName ::= SET OF AttributeValueAssertion + +AttributeValueAssertion ::= SEQUENCE { + attributeType OBJECT IDENTIFIER ({ pkcs7_note_OID }), + attributeValue ANY +} diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c new file mode 100644 index 000000000000..42e56aa7d277 --- /dev/null +++ b/crypto/asymmetric_keys/pkcs7_parser.c @@ -0,0 +1,396 @@ +/* PKCS#7 parser + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "PKCS7: "fmt +#include +#include +#include +#include +#include +#include "public_key.h" +#include "pkcs7_parser.h" +#include "pkcs7-asn1.h" + +struct pkcs7_parse_context { + struct pkcs7_message *msg; /* Message being constructed */ + struct pkcs7_signed_info *sinfo; /* SignedInfo being constructed */ + struct pkcs7_signed_info **ppsinfo; + struct x509_certificate *certs; /* Certificate cache */ + struct x509_certificate **ppcerts; + unsigned long data; /* Start of data */ + enum OID last_oid; /* Last OID encountered */ + unsigned x509_index; + unsigned sinfo_index; +}; + +/** + * pkcs7_free_message - Free a PKCS#7 message + * @pkcs7: The PKCS#7 message to free + */ +void pkcs7_free_message(struct pkcs7_message *pkcs7) +{ + struct x509_certificate *cert; + struct pkcs7_signed_info *sinfo; + + if (pkcs7) { + while (pkcs7->certs) { + cert = pkcs7->certs; + pkcs7->certs = cert->next; + x509_free_certificate(cert); + } + while (pkcs7->crl) { + cert = pkcs7->crl; + pkcs7->crl = cert->next; + x509_free_certificate(cert); + } + while (pkcs7->signed_infos) { + sinfo = pkcs7->signed_infos; + pkcs7->signed_infos = sinfo->next; + mpi_free(sinfo->sig.mpi[0]); + kfree(sinfo->sig.digest); + kfree(sinfo); + } + kfree(pkcs7); + } +} +EXPORT_SYMBOL_GPL(pkcs7_free_message); + +/** + * pkcs7_parse_message - Parse a PKCS#7 message + * @data: The raw binary ASN.1 encoded message to be parsed + * @datalen: The size of the encoded message + */ +struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen) +{ + struct pkcs7_parse_context *ctx; + struct pkcs7_message *msg; + long ret; + + ret = -ENOMEM; + msg = kzalloc(sizeof(struct pkcs7_message), GFP_KERNEL); + if (!msg) + goto error_no_sig; + ctx = kzalloc(sizeof(struct pkcs7_parse_context), GFP_KERNEL); + if (!ctx) + goto error_no_ctx; + ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL); + if (!ctx->sinfo) + goto error_no_sinfo; + + ctx->msg = msg; + ctx->data = (unsigned long)data; + ctx->ppcerts = &ctx->certs; + ctx->ppsinfo = &ctx->msg->signed_infos; + + /* Attempt to decode the signature */ + ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen); + if (ret < 0) + goto error_decode; + + while (ctx->certs) { + struct x509_certificate *cert = ctx->certs; + ctx->certs = cert->next; + x509_free_certificate(cert); + } + mpi_free(ctx->sinfo->sig.mpi[0]); + kfree(ctx->sinfo->sig.digest); + kfree(ctx->sinfo); + kfree(ctx); + return msg; + +error_decode: + mpi_free(ctx->sinfo->sig.mpi[0]); + kfree(ctx->sinfo->sig.digest); + kfree(ctx->sinfo); +error_no_sinfo: + kfree(ctx); +error_no_ctx: + pkcs7_free_message(msg); +error_no_sig: + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(pkcs7_parse_message); + +/** + * pkcs7_get_content_data - Get access to the PKCS#7 content + * @pkcs7: The preparsed PKCS#7 message to access + * @_data: Place to return a pointer to the data + * @_data_len: Place to return the data length + * @want_wrapper: True if the ASN.1 object header should be included in the data + * + * Get access to the data content of the PKCS#7 message, including, optionally, + * the header of the ASN.1 object that contains it. Returns -ENODATA if the + * data object was missing from the message. + */ +int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, + const void **_data, size_t *_data_len, + bool want_wrapper) +{ + size_t wrapper; + + if (!pkcs7->data) + return -ENODATA; + + wrapper = want_wrapper ? pkcs7->data_hdrlen : 0; + *_data = pkcs7->data - wrapper; + *_data_len = pkcs7->data_len + wrapper; + return 0; +} +EXPORT_SYMBOL_GPL(pkcs7_get_content_data); + +/* + * Note an OID when we find one for later processing when we know how + * to interpret it. + */ +int pkcs7_note_OID(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + + ctx->last_oid = look_up_OID(value, vlen); + if (ctx->last_oid == OID__NR) { + char buffer[50]; + sprint_oid(value, vlen, buffer, sizeof(buffer)); + printk("PKCS7: Unknown OID: [%lu] %s\n", + (unsigned long)value - ctx->data, buffer); + } + return 0; +} + +/* + * Note the digest algorithm for the signature. + */ +int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + + switch (ctx->last_oid) { + case OID_md4: + ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_MD4; + break; + case OID_md5: + ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_MD5; + break; + case OID_sha1: + ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_SHA1; + break; + case OID_sha256: + ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_SHA256; + break; + default: + printk("Unsupported digest algo: %u\n", ctx->last_oid); + return -ENOPKG; + } + return 0; +} + +/* + * Note the public key algorithm for the signature. + */ +int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + + switch (ctx->last_oid) { + case OID_rsaEncryption: + ctx->sinfo->sig.pkey_algo = PKEY_ALGO_RSA; + break; + default: + printk("Unsupported pkey algo: %u\n", ctx->last_oid); + return -ENOPKG; + } + return 0; +} + +/* + * Extract a certificate and store it in the context. + */ +int pkcs7_extract_cert(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + struct x509_certificate *x509; + + if (tag != ((ASN1_UNIV << 6) | ASN1_CONS_BIT | ASN1_SEQ)) { + pr_debug("Cert began with tag %02x at %lu\n", + tag, (unsigned long)ctx - ctx->data); + return -EBADMSG; + } + + /* We have to correct for the header so that the X.509 parser can start + * from the beginning. Note that since X.509 stipulates DER, there + * probably shouldn't be an EOC trailer - but it is in PKCS#7 (which + * stipulates BER). + */ + value -= hdrlen; + vlen += hdrlen; + + if (((u8*)value)[1] == 0x80) + vlen += 2; /* Indefinite length - there should be an EOC */ + + x509 = x509_cert_parse(value, vlen); + if (IS_ERR(x509)) + return PTR_ERR(x509); + + pr_debug("Got cert for %s\n", x509->subject); + pr_debug("- fingerprint %s\n", x509->fingerprint); + + x509->index = ++ctx->x509_index; + *ctx->ppcerts = x509; + ctx->ppcerts = &x509->next; + return 0; +} + +/* + * Save the certificate list + */ +int pkcs7_note_certificate_list(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + + pr_devel("Got cert list (%02x)\n", tag); + + *ctx->ppcerts = ctx->msg->certs; + ctx->msg->certs = ctx->certs; + ctx->certs = NULL; + ctx->ppcerts = &ctx->certs; + return 0; +} + +/* + * Extract the data from the message and store that and its content type OID in + * the context. + */ +int pkcs7_note_data(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + + pr_debug("Got data\n"); + + ctx->msg->data = value; + ctx->msg->data_len = vlen; + ctx->msg->data_hdrlen = hdrlen; + ctx->msg->data_type = ctx->last_oid; + return 0; +} + +/* + * Parse authenticated attributes + */ +int pkcs7_sig_note_authenticated_attr(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + + pr_devel("AuthAttr: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value); + + switch (ctx->last_oid) { + case OID_messageDigest: + if (tag != ASN1_OTS) + return -EBADMSG; + ctx->sinfo->msgdigest = value; + ctx->sinfo->msgdigest_len = vlen; + return 0; + default: + return 0; + } +} + +/* + * Note the set of auth attributes for digestion purposes [RFC2315 9.3] + */ +int pkcs7_sig_note_set_of_authattrs(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + + /* We need to switch the 'CONT 0' to a 'SET OF' when we digest */ + ctx->sinfo->authattrs = value - (hdrlen - 1); + ctx->sinfo->authattrs_len = vlen + (hdrlen - 1); + return 0; +} + +/* + * Note the issuing certificate serial number + */ +int pkcs7_sig_note_serial(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + ctx->sinfo->raw_serial = value; + ctx->sinfo->raw_serial_size = vlen; + return 0; +} + +/* + * Note the issuer's name + */ +int pkcs7_sig_note_issuer(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + ctx->sinfo->raw_issuer = value; + ctx->sinfo->raw_issuer_size = vlen; + return 0; +} + +/* + * Note the signature data + */ +int pkcs7_sig_note_signature(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + MPI mpi; + + BUG_ON(ctx->sinfo->sig.pkey_algo != PKEY_ALGO_RSA); + + mpi = mpi_read_raw_data(value, vlen); + if (!mpi) + return -ENOMEM; + + ctx->sinfo->sig.mpi[0] = mpi; + ctx->sinfo->sig.nr_mpi = 1; + return 0; +} + +/* + * Note a signature information block + */ +int pkcs7_note_signed_info(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + + ctx->sinfo->index = ++ctx->sinfo_index; + *ctx->ppsinfo = ctx->sinfo; + ctx->ppsinfo = &ctx->sinfo->next; + ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL); + if (!ctx->sinfo) + return -ENOMEM; + return 0; +} diff --git a/crypto/asymmetric_keys/pkcs7_parser.h b/crypto/asymmetric_keys/pkcs7_parser.h new file mode 100644 index 000000000000..d25f4d15370f --- /dev/null +++ b/crypto/asymmetric_keys/pkcs7_parser.h @@ -0,0 +1,61 @@ +/* PKCS#7 crypto data parser internal definitions + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include +#include +#include "x509_parser.h" + +#define kenter(FMT, ...) \ + pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__) +#define kleave(FMT, ...) \ + pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__) + +struct pkcs7_signed_info { + struct pkcs7_signed_info *next; + struct x509_certificate *signer; /* Signing certificate (in msg->certs) */ + unsigned index; + bool trusted; + + /* Message digest - the digest of the Content Data (or NULL) */ + const void *msgdigest; + unsigned msgdigest_len; + + /* Authenticated Attribute data (or NULL) */ + unsigned authattrs_len; + const void *authattrs; + + /* Issuing cert serial number and issuer's name */ + const void *raw_serial; + unsigned raw_serial_size; + unsigned raw_issuer_size; + const void *raw_issuer; + + /* Message signature. + * + * This contains the generated digest of _either_ the Content Data or + * the Authenticated Attributes [RFC2315 9.3]. If the latter, one of + * the attributes contains the digest of the the Content Data within + * it. + */ + struct public_key_signature sig; +}; + +struct pkcs7_message { + struct x509_certificate *certs; /* Certificate list */ + struct x509_certificate *crl; /* Revocation list */ + struct pkcs7_signed_info *signed_infos; + + /* Content Data (or NULL) */ + enum OID data_type; /* Type of Data */ + size_t data_len; /* Length of Data */ + size_t data_hdrlen; /* Length of Data ASN.1 header */ + const void *data; /* Content Data (or 0) */ +}; diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h new file mode 100644 index 000000000000..8f2628fbdfce --- /dev/null +++ b/include/crypto/pkcs7.h @@ -0,0 +1,23 @@ +/* PKCS#7 crypto data parser + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +struct pkcs7_message; + +/* + * pkcs7_parser.c + */ +extern struct pkcs7_message *pkcs7_parse_message(const void *data, + size_t datalen); +extern void pkcs7_free_message(struct pkcs7_message *pkcs7); + +extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, + const void **_data, size_t *_datalen, + bool want_wrapper); diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h index 6926db724258..edeff85cb1e8 100644 --- a/include/linux/oid_registry.h +++ b/include/linux/oid_registry.h @@ -55,6 +55,7 @@ enum OID { OID_certAuthInfoAccess, /* 1.3.6.1.5.5.7.1.1 */ OID_msOutlookExpress, /* 1.3.6.1.4.1.311.16.4 */ OID_sha1, /* 1.3.14.3.2.26 */ + OID_sha256, /* 2.16.840.1.101.3.4.2.1 */ /* Distinguished Name attribute IDs [RFC 2256] */ OID_commonName, /* 2.5.4.3 */ -- cgit v1.2.3 From a4730357ee724f8c64f0292541ba3da8a95510fb Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 1 Jul 2014 16:40:19 +0100 Subject: PKCS#7: Find the right key in the PKCS#7 key list and verify the signature Find the appropriate key in the PKCS#7 key list and verify the signature with it. There may be several keys in there forming a chain. Any link in that chain or the root of that chain may be in our keyrings. Signed-off-by: David Howells Acked-by: Vivek Goyal Reviewed-by: Kees Cook --- crypto/asymmetric_keys/pkcs7_verify.c | 64 +++++++++++++++++++++++++++++++++++ include/crypto/pkcs7.h | 5 +++ 2 files changed, 69 insertions(+) (limited to 'include') diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c index 0bb408a5b64f..745e5c86a35e 100644 --- a/crypto/asymmetric_keys/pkcs7_verify.c +++ b/crypto/asymmetric_keys/pkcs7_verify.c @@ -120,6 +120,55 @@ error_no_desc: } /* + * Find the key (X.509 certificate) to use to verify a PKCS#7 message. PKCS#7 + * uses the issuer's name and the issuing certificate serial number for + * matching purposes. These must match the certificate issuer's name (not + * subject's name) and the certificate serial number [RFC 2315 6.7]. + */ +static int pkcs7_find_key(struct pkcs7_message *pkcs7, + struct pkcs7_signed_info *sinfo) +{ + struct x509_certificate *x509; + unsigned certix = 1; + + kenter("%u,%u,%u", + sinfo->index, sinfo->raw_serial_size, sinfo->raw_issuer_size); + + for (x509 = pkcs7->certs; x509; x509 = x509->next, certix++) { + /* I'm _assuming_ that the generator of the PKCS#7 message will + * encode the fields from the X.509 cert in the same way in the + * PKCS#7 message - but I can't be 100% sure of that. It's + * possible this will need element-by-element comparison. + */ + if (x509->raw_serial_size != sinfo->raw_serial_size || + memcmp(x509->raw_serial, sinfo->raw_serial, + sinfo->raw_serial_size) != 0) + continue; + pr_devel("Sig %u: Found cert serial match X.509[%u]\n", + sinfo->index, certix); + + if (x509->raw_issuer_size != sinfo->raw_issuer_size || + memcmp(x509->raw_issuer, sinfo->raw_issuer, + sinfo->raw_issuer_size) != 0) { + pr_warn("Sig %u: X.509 subject and PKCS#7 issuer don't match\n", + sinfo->index); + continue; + } + + if (x509->pub->pkey_algo != sinfo->sig.pkey_algo) { + pr_warn("Sig %u: X.509 algo and PKCS#7 sig algo don't match\n", + sinfo->index); + continue; + } + + sinfo->signer = x509; + return 0; + } + pr_warn("Sig %u: Issuing X.509 cert not found (#%*ph)\n", + sinfo->index, sinfo->raw_serial_size, sinfo->raw_serial); + return -ENOKEY; +} + /* * Verify one signed information block from a PKCS#7 message. */ @@ -137,6 +186,21 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7, if (ret < 0) return ret; + /* Find the key for the signature */ + ret = pkcs7_find_key(pkcs7, sinfo); + if (ret < 0) + return ret; + + pr_devel("Using X.509[%u] for sig %u\n", + sinfo->signer->index, sinfo->index); + + /* Verify the PKCS#7 binary against the key */ + ret = public_key_verify_signature(sinfo->signer->pub, &sinfo->sig); + if (ret < 0) + return ret; + + pr_devel("Verified signature %u\n", sinfo->index); + return 0; } diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h index 8f2628fbdfce..8ba0f3810d67 100644 --- a/include/crypto/pkcs7.h +++ b/include/crypto/pkcs7.h @@ -21,3 +21,8 @@ extern void pkcs7_free_message(struct pkcs7_message *pkcs7); extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, const void **_data, size_t *_datalen, bool want_wrapper); + +/* + * pkcs7_verify.c + */ +extern int pkcs7_verify(struct pkcs7_message *pkcs7); -- cgit v1.2.3 From 08815b62d700e4fbeb72a01986ad051c3dd84a15 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 1 Jul 2014 16:40:20 +0100 Subject: PKCS#7: Find intersection between PKCS#7 message and known, trusted keys Find the intersection between the X.509 certificate chain contained in a PKCS#7 message and a set of keys that we already know and trust. Signed-off-by: David Howells Acked-by: Vivek Goyal Reviewed-by: Kees Cook --- crypto/asymmetric_keys/Makefile | 1 + crypto/asymmetric_keys/pkcs7_trust.c | 219 +++++++++++++++++++++++++++++++++++ include/crypto/pkcs7.h | 8 ++ 3 files changed, 228 insertions(+) create mode 100644 crypto/asymmetric_keys/pkcs7_trust.c (limited to 'include') diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index b6b39e7bea01..d63cb4320b96 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pkcs7_message.o pkcs7_message-y := \ pkcs7-asn1.o \ pkcs7_parser.o \ + pkcs7_trust.o \ pkcs7_verify.o $(obj)/pkcs7_parser.o: $(obj)/pkcs7-asn1.h diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c new file mode 100644 index 000000000000..b6b045131403 --- /dev/null +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -0,0 +1,219 @@ +/* Validate the trust chain of a PKCS#7 message. + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "PKCS7: "fmt +#include +#include +#include +#include +#include +#include +#include +#include "public_key.h" +#include "pkcs7_parser.h" + +/* + * Request an asymmetric key. + */ +static struct key *pkcs7_request_asymmetric_key( + struct key *keyring, + const char *signer, size_t signer_len, + const char *authority, size_t auth_len) +{ + key_ref_t key; + char *id; + + kenter(",%zu,,%zu", signer_len, auth_len); + + /* Construct an identifier. */ + id = kmalloc(signer_len + 2 + auth_len + 1, GFP_KERNEL); + if (!id) + return ERR_PTR(-ENOMEM); + + memcpy(id, signer, signer_len); + id[signer_len + 0] = ':'; + id[signer_len + 1] = ' '; + memcpy(id + signer_len + 2, authority, auth_len); + id[signer_len + 2 + auth_len] = 0; + + pr_debug("Look up: \"%s\"\n", id); + + key = keyring_search(make_key_ref(keyring, 1), + &key_type_asymmetric, id); + if (IS_ERR(key)) + pr_debug("Request for module key '%s' err %ld\n", + id, PTR_ERR(key)); + kfree(id); + + if (IS_ERR(key)) { + switch (PTR_ERR(key)) { + /* Hide some search errors */ + case -EACCES: + case -ENOTDIR: + case -EAGAIN: + return ERR_PTR(-ENOKEY); + default: + return ERR_CAST(key); + } + } + + pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key_ref_to_ptr(key))); + return key_ref_to_ptr(key); +} + +/** + * Check the trust on one PKCS#7 SignedInfo block. + */ +int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, + struct pkcs7_signed_info *sinfo, + struct key *trust_keyring) +{ + struct public_key_signature *sig = &sinfo->sig; + struct x509_certificate *x509, *last = NULL, *p; + struct key *key; + bool trusted; + int ret; + + kenter(",%u,", sinfo->index); + + for (x509 = sinfo->signer; x509; x509 = x509->signer) { + if (x509->seen) { + if (x509->verified) { + trusted = x509->trusted; + goto verified; + } + kleave(" = -ENOKEY [cached]"); + return -ENOKEY; + } + x509->seen = true; + + /* Look to see if this certificate is present in the trusted + * keys. + */ + key = pkcs7_request_asymmetric_key( + trust_keyring, + x509->subject, strlen(x509->subject), + x509->fingerprint, strlen(x509->fingerprint)); + if (!IS_ERR(key)) + /* One of the X.509 certificates in the PKCS#7 message + * is apparently the same as one we already trust. + * Verify that the trusted variant can also validate + * the signature on the descendant. + */ + goto matched; + if (key == ERR_PTR(-ENOMEM)) + return -ENOMEM; + + /* Self-signed certificates form roots of their own, and if we + * don't know them, then we can't accept them. + */ + if (x509->next == x509) { + kleave(" = -ENOKEY [unknown self-signed]"); + return -ENOKEY; + } + + might_sleep(); + last = x509; + sig = &last->sig; + } + + /* No match - see if the root certificate has a signer amongst the + * trusted keys. + */ + if (!last || !last->issuer || !last->authority) { + kleave(" = -ENOKEY [no backref]"); + return -ENOKEY; + } + + key = pkcs7_request_asymmetric_key( + trust_keyring, + last->issuer, strlen(last->issuer), + last->authority, strlen(last->authority)); + if (IS_ERR(key)) + return PTR_ERR(key) == -ENOMEM ? -ENOMEM : -ENOKEY; + x509 = last; + +matched: + ret = verify_signature(key, sig); + trusted = test_bit(KEY_FLAG_TRUSTED, &key->flags); + key_put(key); + if (ret < 0) { + if (ret == -ENOMEM) + return ret; + kleave(" = -EKEYREJECTED [verify %d]", ret); + return -EKEYREJECTED; + } + +verified: + x509->verified = true; + for (p = sinfo->signer; p != x509; p = p->signer) { + p->verified = true; + p->trusted = trusted; + } + sinfo->trusted = trusted; + kleave(" = 0"); + return 0; +} + +/** + * pkcs7_validate_trust - Validate PKCS#7 trust chain + * @pkcs7: The PKCS#7 certificate to validate + * @trust_keyring: Signing certificates to use as starting points + * @_trusted: Set to true if trustworth, false otherwise + * + * Validate that the certificate chain inside the PKCS#7 message intersects + * keys we already know and trust. + * + * Returns, in order of descending priority: + * + * (*) -EKEYREJECTED if a signature failed to match for which we have a valid + * key, or: + * + * (*) 0 if at least one signature chain intersects with the keys in the trust + * keyring, or: + * + * (*) -ENOPKG if a suitable crypto module couldn't be found for a check on a + * chain. + * + * (*) -ENOKEY if we couldn't find a match for any of the signature chains in + * the message. + * + * May also return -ENOMEM. + */ +int pkcs7_validate_trust(struct pkcs7_message *pkcs7, + struct key *trust_keyring, + bool *_trusted) +{ + struct pkcs7_signed_info *sinfo; + struct x509_certificate *p; + int cached_ret = 0, ret; + + for (p = pkcs7->certs; p; p = p->next) + p->seen = false; + + for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { + ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring); + if (ret < 0) { + if (ret == -ENOPKG) { + cached_ret = -ENOPKG; + } else if (ret == -ENOKEY) { + if (cached_ret == 0) + cached_ret = -ENOKEY; + } else { + return ret; + } + } + *_trusted |= sinfo->trusted; + } + + return cached_ret; +} +EXPORT_SYMBOL_GPL(pkcs7_validate_trust); diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h index 8ba0f3810d67..691c79172a26 100644 --- a/include/crypto/pkcs7.h +++ b/include/crypto/pkcs7.h @@ -9,6 +9,7 @@ * 2 of the Licence, or (at your option) any later version. */ +struct key; struct pkcs7_message; /* @@ -22,6 +23,13 @@ extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, const void **_data, size_t *_datalen, bool want_wrapper); +/* + * pkcs7_trust.c + */ +extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7, + struct key *trust_keyring, + bool *_trusted); + /* * pkcs7_verify.c */ -- cgit v1.2.3 From 27e4de2bd1804c24e3e517dd54026c1f60e8fe27 Mon Sep 17 00:00:00 2001 From: Stephan Mueller Date: Sun, 6 Jul 2014 02:25:36 +0200 Subject: crypto: drbg - Mix a time stamp into DRBG state The current locking approach of the DRBG tries to keep the protected code paths very minimal. It is therefore possible that two threads query one DRBG instance at the same time. When thread A requests random numbers, a shadow copy of the DRBG state is created upon which the request for A is processed. After finishing the state for A's request is merged back into the DRBG state. If now thread B requests random numbers from the same DRBG after the request for thread A is received, but before A's shadow state is merged back, the random numbers for B will be identical to the ones for A. Please note that the time window is very small for this scenario. To prevent that there is even a theoretical chance for thread A and B having the same DRBG state, the current time stamp is provided as additional information string for each new request. The addition of the time stamp as additional information string implies that now all generate functions must be capable to process a linked list with additional information strings instead of a scalar. CC: Rafael Aquini Signed-off-by: Stephan Mueller Signed-off-by: Herbert Xu --- crypto/drbg.c | 59 ++++++++++++++++++++++++++++++--------------------- include/crypto/drbg.h | 2 +- 2 files changed, 36 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/crypto/drbg.c b/crypto/drbg.c index 2a7860f73288..a76b3cb03cf4 100644 --- a/crypto/drbg.c +++ b/crypto/drbg.c @@ -646,7 +646,7 @@ out: /* Generate function of CTR DRBG as defined in 10.2.1.5.2 */ static int drbg_ctr_generate(struct drbg_state *drbg, unsigned char *buf, unsigned int buflen, - struct drbg_string *addtl) + struct list_head *addtl) { int len = 0; int ret = 0; @@ -656,11 +656,8 @@ static int drbg_ctr_generate(struct drbg_state *drbg, memset(drbg->scratchpad, 0, drbg_blocklen(drbg)); /* 10.2.1.5.2 step 2 */ - if (addtl && 0 < addtl->len) { - LIST_HEAD(addtllist); - - list_add_tail(&addtl->list, &addtllist); - ret = drbg_ctr_update(drbg, &addtllist, 2); + if (addtl && !list_empty(addtl)) { + ret = drbg_ctr_update(drbg, addtl, 2); if (ret) return 0; } @@ -777,7 +774,7 @@ static int drbg_hmac_update(struct drbg_state *drbg, struct list_head *seed, static int drbg_hmac_generate(struct drbg_state *drbg, unsigned char *buf, unsigned int buflen, - struct drbg_string *addtl) + struct list_head *addtl) { int len = 0; int ret = 0; @@ -785,11 +782,8 @@ static int drbg_hmac_generate(struct drbg_state *drbg, LIST_HEAD(datalist); /* 10.1.2.5 step 2 */ - if (addtl && 0 < addtl->len) { - LIST_HEAD(addtllist); - - list_add_tail(&addtl->list, &addtllist); - ret = drbg_hmac_update(drbg, &addtllist, 1); + if (addtl && !list_empty(addtl)) { + ret = drbg_hmac_update(drbg, addtl, 1); if (ret) return ret; } @@ -813,14 +807,10 @@ static int drbg_hmac_generate(struct drbg_state *drbg, } /* 10.1.2.5 step 6 */ - if (addtl && 0 < addtl->len) { - LIST_HEAD(addtllist); - - list_add_tail(&addtl->list, &addtllist); - ret = drbg_hmac_update(drbg, &addtllist, 1); - } else { + if (addtl && !list_empty(addtl)) + ret = drbg_hmac_update(drbg, addtl, 1); + else ret = drbg_hmac_update(drbg, NULL, 1); - } if (ret) return ret; @@ -944,7 +934,7 @@ out: /* processing of additional information string for Hash DRBG */ static int drbg_hash_process_addtl(struct drbg_state *drbg, - struct drbg_string *addtl) + struct list_head *addtl) { int ret = 0; struct drbg_string data1, data2; @@ -955,7 +945,7 @@ static int drbg_hash_process_addtl(struct drbg_state *drbg, memset(drbg->scratchpad, 0, drbg_blocklen(drbg)); /* 10.1.1.4 step 2 */ - if (!addtl || 0 == addtl->len) + if (!addtl || list_empty(addtl)) return 0; /* 10.1.1.4 step 2a */ @@ -963,7 +953,7 @@ static int drbg_hash_process_addtl(struct drbg_state *drbg, drbg_string_fill(&data2, drbg->V, drbg_statelen(drbg)); list_add_tail(&data1.list, &datalist); list_add_tail(&data2.list, &datalist); - list_add_tail(&addtl->list, &datalist); + list_splice_tail(addtl, &datalist); ret = drbg_kcapi_hash(drbg, NULL, drbg->scratchpad, &datalist); if (ret) goto out; @@ -1029,7 +1019,7 @@ out: /* generate function for Hash DRBG as defined in 10.1.1.4 */ static int drbg_hash_generate(struct drbg_state *drbg, unsigned char *buf, unsigned int buflen, - struct drbg_string *addtl) + struct list_head *addtl) { int len = 0; int ret = 0; @@ -1347,6 +1337,12 @@ static int drbg_generate(struct drbg_state *drbg, { int len = 0; struct drbg_state *shadow = NULL; + LIST_HEAD(addtllist); + struct drbg_string timestamp; + union { + cycles_t cycles; + unsigned char char_cycles[sizeof(cycles_t)]; + } now; if (0 == buflen || !buf) { pr_devel("DRBG: no output buffer provided\n"); @@ -1407,8 +1403,23 @@ static int drbg_generate(struct drbg_state *drbg, /* 9.3.1 step 7.4 */ addtl = NULL; } + + /* + * Mix the time stamp into the DRBG state if the DRBG is not in + * test mode. If there are two callers invoking the DRBG at the same + * time, i.e. before the first caller merges its shadow state back, + * both callers would obtain the same random number stream without + * changing the state here. + */ + if (!drbg->test_data) { + now.cycles = random_get_entropy(); + drbg_string_fill(×tamp, now.char_cycles, sizeof(cycles_t)); + list_add_tail(×tamp.list, &addtllist); + } + if (addtl && 0 < addtl->len) + list_add_tail(&addtl->list, &addtllist); /* 9.3.1 step 8 and 10 */ - len = shadow->d_ops->generate(shadow, buf, buflen, addtl); + len = shadow->d_ops->generate(shadow, buf, buflen, &addtllist); /* 10.1.1.4 step 6, 10.1.2.5 step 7, 10.2.1.5.2 step 7 */ shadow->reseed_ctr++; diff --git a/include/crypto/drbg.h b/include/crypto/drbg.h index 4065dfca146a..831d786976c5 100644 --- a/include/crypto/drbg.h +++ b/include/crypto/drbg.h @@ -102,7 +102,7 @@ struct drbg_state_ops { int reseed); int (*generate)(struct drbg_state *drbg, unsigned char *buf, unsigned int buflen, - struct drbg_string *addtl); + struct list_head *addtl); int (*crypto_init)(struct drbg_state *drbg); int (*crypto_fini)(struct drbg_state *drbg); -- cgit v1.2.3 From e941759c74a44d6ac2eed21bb0a38b21fe4559e2 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 1 Jul 2014 12:57:14 +0200 Subject: fence: dma-buf cross-device synchronization (v18) A fence can be attached to a buffer which is being filled or consumed by hw, to allow userspace to pass the buffer without waiting to another device. For example, userspace can call page_flip ioctl to display the next frame of graphics after kicking the GPU but while the GPU is still rendering. The display device sharing the buffer with the GPU would attach a callback to get notified when the GPU's rendering-complete IRQ fires, to update the scan-out address of the display, without having to wake up userspace. A driver must allocate a fence context for each execution ring that can run in parallel. The function for this takes an argument with how many contexts to allocate: + fence_context_alloc() A fence is transient, one-shot deal. It is allocated and attached to one or more dma-buf's. When the one that attached it is done, with the pending operation, it can signal the fence: + fence_signal() To have a rough approximation whether a fence is fired, call: + fence_is_signaled() The dma-buf-mgr handles tracking, and waiting on, the fences associated with a dma-buf. The one pending on the fence can add an async callback: + fence_add_callback() The callback can optionally be cancelled with: + fence_remove_callback() To wait synchronously, optionally with a timeout: + fence_wait() + fence_wait_timeout() When emitting a fence, call: + trace_fence_emit() To annotate that a fence is blocking on another fence, call: + trace_fence_annotate_wait_on(fence, on_fence) A default software-only implementation is provided, which can be used by drivers attaching a fence to a buffer when they have no other means for hw sync. But a memory backed fence is also envisioned, because it is common that GPU's can write to, or poll on some memory location for synchronization. For example: fence = custom_get_fence(...); if ((seqno_fence = to_seqno_fence(fence)) != NULL) { dma_buf *fence_buf = seqno_fence->sync_buf; get_dma_buf(fence_buf); ... tell the hw the memory location to wait ... custom_wait_on(fence_buf, seqno_fence->seqno_ofs, fence->seqno); } else { /* fall-back to sw sync * / fence_add_callback(fence, my_cb); } On SoC platforms, if some other hw mechanism is provided for synchronizing between IP blocks, it could be supported as an alternate implementation with it's own fence ops in a similar way. enable_signaling callback is used to provide sw signaling in case a cpu waiter is requested or no compatible hardware signaling could be used. The intention is to provide a userspace interface (presumably via eventfd) later, to be used in conjunction with dma-buf's mmap support for sw access to buffers (or for userspace apps that would prefer to do their own synchronization). v1: Original v2: After discussion w/ danvet and mlankhorst on #dri-devel, we decided that dma-fence didn't need to care about the sw->hw signaling path (it can be handled same as sw->sw case), and therefore the fence->ops can be simplified and more handled in the core. So remove the signal, add_callback, cancel_callback, and wait ops, and replace with a simple enable_signaling() op which can be used to inform a fence supporting hw->hw signaling that one or more devices which do not support hw signaling are waiting (and therefore it should enable an irq or do whatever is necessary in order that the CPU is notified when the fence is passed). v3: Fix locking fail in attach_fence() and get_fence() v4: Remove tie-in w/ dma-buf.. after discussion w/ danvet and mlankorst we decided that we need to be able to attach one fence to N dma-buf's, so using the list_head in dma-fence struct would be problematic. v5: [ Maarten Lankhorst ] Updated for dma-bikeshed-fence and dma-buf-manager. v6: [ Maarten Lankhorst ] I removed dma_fence_cancel_callback and some comments about checking if fence fired or not. This is broken by design. waitqueue_active during destruction is now fatal, since the signaller should be holding a reference in enable_signalling until it signalled the fence. Pass the original dma_fence_cb along, and call __remove_wait in the dma_fence_callback handler, so that no cleanup needs to be performed. v7: [ Maarten Lankhorst ] Set cb->func and only enable sw signaling if fence wasn't signaled yet, for example for hardware fences that may choose to signal blindly. v8: [ Maarten Lankhorst ] Tons of tiny fixes, moved __dma_fence_init to header and fixed include mess. dma-fence.h now includes dma-buf.h All members are now initialized, so kmalloc can be used for allocating a dma-fence. More documentation added. v9: Change compiler bitfields to flags, change return type of enable_signaling to bool. Rework dma_fence_wait. Added dma_fence_is_signaled and dma_fence_wait_timeout. s/dma// and change exports to non GPL. Added fence_is_signaled and fence_enable_sw_signaling calls, add ability to override default wait operation. v10: remove event_queue, use a custom list, export try_to_wake_up from scheduler. Remove fence lock and use a global spinlock instead, this should hopefully remove all the locking headaches I was having on trying to implement this. enable_signaling is called with this lock held. v11: Use atomic ops for flags, lifting the need for some spin_lock_irqsaves. However I kept the guarantee that after fence_signal returns, it is guaranteed that enable_signaling has either been called to completion, or will not be called any more. Add contexts and seqno to base fence implementation. This allows you to wait for less fences, by testing for seqno + signaled, and then only wait on the later fence. Add FENCE_TRACE, FENCE_WARN, and FENCE_ERR. This makes debugging easier. An CONFIG_DEBUG_FENCE will be added to turn off the FENCE_TRACE spam, and another runtime option can turn it off at runtime. v12: Add CONFIG_FENCE_TRACE. Add missing documentation for the fence->context and fence->seqno members. v13: Fixup CONFIG_FENCE_TRACE kconfig description. Move fence_context_alloc to fence. Simplify fence_later. Kill priv member to fence_cb. v14: Remove priv argument from fence_add_callback, oops! v15: Remove priv from documentation. Explicitly include linux/atomic.h. v16: Add trace events. Import changes required by android syncpoints. v17: Use wake_up_state instead of try_to_wake_up. (Colin Cross) Fix up commit description for seqno_fence. (Rob Clark) v18: Rename release_fence to fence_release. Move to drivers/dma-buf/. Rename __fence_is_signaled and __fence_signal to *_locked. Rename __fence_init to fence_init. Make fence_default_wait return a signed long, and fix wait ops too. Signed-off-by: Maarten Lankhorst Signed-off-by: Thierry Reding #use smp_mb__before_atomic() Acked-by: Sumit Semwal Acked-by: Daniel Vetter Reviewed-by: Rob Clark Signed-off-by: Greg Kroah-Hartman --- Documentation/DocBook/device-drivers.tmpl | 2 + MAINTAINERS | 2 +- drivers/base/Kconfig | 9 + drivers/dma-buf/Makefile | 2 +- drivers/dma-buf/fence.c | 431 ++++++++++++++++++++++++++++++ include/linux/fence.h | 343 ++++++++++++++++++++++++ include/trace/events/fence.h | 128 +++++++++ 7 files changed, 915 insertions(+), 2 deletions(-) create mode 100644 drivers/dma-buf/fence.c create mode 100644 include/linux/fence.h create mode 100644 include/trace/events/fence.h (limited to 'include') diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index ac61ebd92875..e634657efb52 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -129,6 +129,8 @@ X!Edrivers/base/interface.c Device Drivers DMA Management !Edrivers/dma-buf/dma-buf.c +!Edrivers/dma-buf/fence.c +!Iinclude/linux/fence.h !Iinclude/linux/reservation.h !Edrivers/base/dma-coherent.c !Edrivers/base/dma-mapping.c diff --git a/MAINTAINERS b/MAINTAINERS index 2eefee768d46..65c8f534b22f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2901,7 +2901,7 @@ L: linux-media@vger.kernel.org L: dri-devel@lists.freedesktop.org L: linaro-mm-sig@lists.linaro.org F: drivers/dma-buf/ -F: include/linux/dma-buf* include/linux/reservation.h +F: include/linux/dma-buf* include/linux/reservation.h include/linux/fence.h F: Documentation/dma-buf-sharing.txt T: git git://git.linaro.org/people/sumitsemwal/linux-dma-buf.git diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index 23b8726962af..00e13ce5cbbd 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -208,6 +208,15 @@ config DMA_SHARED_BUFFER APIs extension; the file's descriptor can then be passed on to other driver. +config FENCE_TRACE + bool "Enable verbose FENCE_TRACE messages" + depends on DMA_SHARED_BUFFER + help + Enable the FENCE_TRACE printks. This will add extra + spam to the console log, but will make it easier to diagnose + lockup related problems for dma-buffers shared across multiple + devices. + config DMA_CMA bool "DMA Contiguous Memory Allocator" depends on HAVE_DMA_CONTIGUOUS && CMA diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile index 4a4f4c9bacd0..d7825bfe630e 100644 --- a/drivers/dma-buf/Makefile +++ b/drivers/dma-buf/Makefile @@ -1 +1 @@ -obj-y := dma-buf.o reservation.o +obj-y := dma-buf.o fence.o reservation.o diff --git a/drivers/dma-buf/fence.c b/drivers/dma-buf/fence.c new file mode 100644 index 000000000000..948bf00d955e --- /dev/null +++ b/drivers/dma-buf/fence.c @@ -0,0 +1,431 @@ +/* + * Fence mechanism for dma-buf and to allow for asynchronous dma access + * + * Copyright (C) 2012 Canonical Ltd + * Copyright (C) 2012 Texas Instruments + * + * Authors: + * Rob Clark + * Maarten Lankhorst + * + * 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. + */ + +#include +#include +#include +#include + +#define CREATE_TRACE_POINTS +#include + +EXPORT_TRACEPOINT_SYMBOL(fence_annotate_wait_on); +EXPORT_TRACEPOINT_SYMBOL(fence_emit); + +/** + * fence context counter: each execution context should have its own + * fence context, this allows checking if fences belong to the same + * context or not. One device can have multiple separate contexts, + * and they're used if some engine can run independently of another. + */ +static atomic_t fence_context_counter = ATOMIC_INIT(0); + +/** + * fence_context_alloc - allocate an array of fence contexts + * @num: [in] amount of contexts to allocate + * + * This function will return the first index of the number of fences allocated. + * The fence context is used for setting fence->context to a unique number. + */ +unsigned fence_context_alloc(unsigned num) +{ + BUG_ON(!num); + return atomic_add_return(num, &fence_context_counter) - num; +} +EXPORT_SYMBOL(fence_context_alloc); + +/** + * fence_signal_locked - signal completion of a fence + * @fence: the fence to signal + * + * Signal completion for software callbacks on a fence, this will unblock + * fence_wait() calls and run all the callbacks added with + * fence_add_callback(). Can be called multiple times, but since a fence + * can only go from unsignaled to signaled state, it will only be effective + * the first time. + * + * Unlike fence_signal, this function must be called with fence->lock held. + */ +int fence_signal_locked(struct fence *fence) +{ + struct fence_cb *cur, *tmp; + int ret = 0; + + if (WARN_ON(!fence)) + return -EINVAL; + + if (!ktime_to_ns(fence->timestamp)) { + fence->timestamp = ktime_get(); + smp_mb__before_atomic(); + } + + if (test_and_set_bit(FENCE_FLAG_SIGNALED_BIT, &fence->flags)) { + ret = -EINVAL; + + /* + * we might have raced with the unlocked fence_signal, + * still run through all callbacks + */ + } else + trace_fence_signaled(fence); + + list_for_each_entry_safe(cur, tmp, &fence->cb_list, node) { + list_del_init(&cur->node); + cur->func(fence, cur); + } + return ret; +} +EXPORT_SYMBOL(fence_signal_locked); + +/** + * fence_signal - signal completion of a fence + * @fence: the fence to signal + * + * Signal completion for software callbacks on a fence, this will unblock + * fence_wait() calls and run all the callbacks added with + * fence_add_callback(). Can be called multiple times, but since a fence + * can only go from unsignaled to signaled state, it will only be effective + * the first time. + */ +int fence_signal(struct fence *fence) +{ + unsigned long flags; + + if (!fence) + return -EINVAL; + + if (!ktime_to_ns(fence->timestamp)) { + fence->timestamp = ktime_get(); + smp_mb__before_atomic(); + } + + if (test_and_set_bit(FENCE_FLAG_SIGNALED_BIT, &fence->flags)) + return -EINVAL; + + trace_fence_signaled(fence); + + if (test_bit(FENCE_FLAG_ENABLE_SIGNAL_BIT, &fence->flags)) { + struct fence_cb *cur, *tmp; + + spin_lock_irqsave(fence->lock, flags); + list_for_each_entry_safe(cur, tmp, &fence->cb_list, node) { + list_del_init(&cur->node); + cur->func(fence, cur); + } + spin_unlock_irqrestore(fence->lock, flags); + } + return 0; +} +EXPORT_SYMBOL(fence_signal); + +/** + * fence_wait_timeout - sleep until the fence gets signaled + * or until timeout elapses + * @fence: [in] the fence to wait on + * @intr: [in] if true, do an interruptible wait + * @timeout: [in] timeout value in jiffies, or MAX_SCHEDULE_TIMEOUT + * + * Returns -ERESTARTSYS if interrupted, 0 if the wait timed out, or the + * remaining timeout in jiffies on success. Other error values may be + * returned on custom implementations. + * + * Performs a synchronous wait on this fence. It is assumed the caller + * directly or indirectly (buf-mgr between reservation and committing) + * holds a reference to the fence, otherwise the fence might be + * freed before return, resulting in undefined behavior. + */ +signed long +fence_wait_timeout(struct fence *fence, bool intr, signed long timeout) +{ + signed long ret; + + if (WARN_ON(timeout < 0)) + return -EINVAL; + + trace_fence_wait_start(fence); + ret = fence->ops->wait(fence, intr, timeout); + trace_fence_wait_end(fence); + return ret; +} +EXPORT_SYMBOL(fence_wait_timeout); + +void fence_release(struct kref *kref) +{ + struct fence *fence = + container_of(kref, struct fence, refcount); + + trace_fence_destroy(fence); + + BUG_ON(!list_empty(&fence->cb_list)); + + if (fence->ops->release) + fence->ops->release(fence); + else + fence_free(fence); +} +EXPORT_SYMBOL(fence_release); + +void fence_free(struct fence *fence) +{ + kfree(fence); +} +EXPORT_SYMBOL(fence_free); + +/** + * fence_enable_sw_signaling - enable signaling on fence + * @fence: [in] the fence to enable + * + * this will request for sw signaling to be enabled, to make the fence + * complete as soon as possible + */ +void fence_enable_sw_signaling(struct fence *fence) +{ + unsigned long flags; + + if (!test_and_set_bit(FENCE_FLAG_ENABLE_SIGNAL_BIT, &fence->flags) && + !test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->flags)) { + trace_fence_enable_signal(fence); + + spin_lock_irqsave(fence->lock, flags); + + if (!fence->ops->enable_signaling(fence)) + fence_signal_locked(fence); + + spin_unlock_irqrestore(fence->lock, flags); + } +} +EXPORT_SYMBOL(fence_enable_sw_signaling); + +/** + * fence_add_callback - add a callback to be called when the fence + * is signaled + * @fence: [in] the fence to wait on + * @cb: [in] the callback to register + * @func: [in] the function to call + * + * cb will be initialized by fence_add_callback, no initialization + * by the caller is required. Any number of callbacks can be registered + * to a fence, but a callback can only be registered to one fence at a time. + * + * Note that the callback can be called from an atomic context. If + * fence is already signaled, this function will return -ENOENT (and + * *not* call the callback) + * + * Add a software callback to the fence. Same restrictions apply to + * refcount as it does to fence_wait, however the caller doesn't need to + * keep a refcount to fence afterwards: when software access is enabled, + * the creator of the fence is required to keep the fence alive until + * after it signals with fence_signal. The callback itself can be called + * from irq context. + * + */ +int fence_add_callback(struct fence *fence, struct fence_cb *cb, + fence_func_t func) +{ + unsigned long flags; + int ret = 0; + bool was_set; + + if (WARN_ON(!fence || !func)) + return -EINVAL; + + if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->flags)) { + INIT_LIST_HEAD(&cb->node); + return -ENOENT; + } + + spin_lock_irqsave(fence->lock, flags); + + was_set = test_and_set_bit(FENCE_FLAG_ENABLE_SIGNAL_BIT, &fence->flags); + + if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->flags)) + ret = -ENOENT; + else if (!was_set) { + trace_fence_enable_signal(fence); + + if (!fence->ops->enable_signaling(fence)) { + fence_signal_locked(fence); + ret = -ENOENT; + } + } + + if (!ret) { + cb->func = func; + list_add_tail(&cb->node, &fence->cb_list); + } else + INIT_LIST_HEAD(&cb->node); + spin_unlock_irqrestore(fence->lock, flags); + + return ret; +} +EXPORT_SYMBOL(fence_add_callback); + +/** + * fence_remove_callback - remove a callback from the signaling list + * @fence: [in] the fence to wait on + * @cb: [in] the callback to remove + * + * Remove a previously queued callback from the fence. This function returns + * true if the callback is succesfully removed, or false if the fence has + * already been signaled. + * + * *WARNING*: + * Cancelling a callback should only be done if you really know what you're + * doing, since deadlocks and race conditions could occur all too easily. For + * this reason, it should only ever be done on hardware lockup recovery, + * with a reference held to the fence. + */ +bool +fence_remove_callback(struct fence *fence, struct fence_cb *cb) +{ + unsigned long flags; + bool ret; + + spin_lock_irqsave(fence->lock, flags); + + ret = !list_empty(&cb->node); + if (ret) + list_del_init(&cb->node); + + spin_unlock_irqrestore(fence->lock, flags); + + return ret; +} +EXPORT_SYMBOL(fence_remove_callback); + +struct default_wait_cb { + struct fence_cb base; + struct task_struct *task; +}; + +static void +fence_default_wait_cb(struct fence *fence, struct fence_cb *cb) +{ + struct default_wait_cb *wait = + container_of(cb, struct default_wait_cb, base); + + wake_up_state(wait->task, TASK_NORMAL); +} + +/** + * fence_default_wait - default sleep until the fence gets signaled + * or until timeout elapses + * @fence: [in] the fence to wait on + * @intr: [in] if true, do an interruptible wait + * @timeout: [in] timeout value in jiffies, or MAX_SCHEDULE_TIMEOUT + * + * Returns -ERESTARTSYS if interrupted, 0 if the wait timed out, or the + * remaining timeout in jiffies on success. + */ +signed long +fence_default_wait(struct fence *fence, bool intr, signed long timeout) +{ + struct default_wait_cb cb; + unsigned long flags; + signed long ret = timeout; + bool was_set; + + if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->flags)) + return timeout; + + spin_lock_irqsave(fence->lock, flags); + + if (intr && signal_pending(current)) { + ret = -ERESTARTSYS; + goto out; + } + + was_set = test_and_set_bit(FENCE_FLAG_ENABLE_SIGNAL_BIT, &fence->flags); + + if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->flags)) + goto out; + + if (!was_set) { + trace_fence_enable_signal(fence); + + if (!fence->ops->enable_signaling(fence)) { + fence_signal_locked(fence); + goto out; + } + } + + cb.base.func = fence_default_wait_cb; + cb.task = current; + list_add(&cb.base.node, &fence->cb_list); + + while (!test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->flags) && ret > 0) { + if (intr) + __set_current_state(TASK_INTERRUPTIBLE); + else + __set_current_state(TASK_UNINTERRUPTIBLE); + spin_unlock_irqrestore(fence->lock, flags); + + ret = schedule_timeout(ret); + + spin_lock_irqsave(fence->lock, flags); + if (ret > 0 && intr && signal_pending(current)) + ret = -ERESTARTSYS; + } + + if (!list_empty(&cb.base.node)) + list_del(&cb.base.node); + __set_current_state(TASK_RUNNING); + +out: + spin_unlock_irqrestore(fence->lock, flags); + return ret; +} +EXPORT_SYMBOL(fence_default_wait); + +/** + * fence_init - Initialize a custom fence. + * @fence: [in] the fence to initialize + * @ops: [in] the fence_ops for operations on this fence + * @lock: [in] the irqsafe spinlock to use for locking this fence + * @context: [in] the execution context this fence is run on + * @seqno: [in] a linear increasing sequence number for this context + * + * Initializes an allocated fence, the caller doesn't have to keep its + * refcount after committing with this fence, but it will need to hold a + * refcount again if fence_ops.enable_signaling gets called. This can + * be used for other implementing other types of fence. + * + * context and seqno are used for easy comparison between fences, allowing + * to check which fence is later by simply using fence_later. + */ +void +fence_init(struct fence *fence, const struct fence_ops *ops, + spinlock_t *lock, unsigned context, unsigned seqno) +{ + BUG_ON(!lock); + BUG_ON(!ops || !ops->wait || !ops->enable_signaling || + !ops->get_driver_name || !ops->get_timeline_name); + + kref_init(&fence->refcount); + fence->ops = ops; + INIT_LIST_HEAD(&fence->cb_list); + fence->lock = lock; + fence->context = context; + fence->seqno = seqno; + fence->flags = 0UL; + + trace_fence_init(fence); +} +EXPORT_SYMBOL(fence_init); diff --git a/include/linux/fence.h b/include/linux/fence.h new file mode 100644 index 000000000000..b935cc650123 --- /dev/null +++ b/include/linux/fence.h @@ -0,0 +1,343 @@ +/* + * Fence mechanism for dma-buf to allow for asynchronous dma access + * + * Copyright (C) 2012 Canonical Ltd + * Copyright (C) 2012 Texas Instruments + * + * Authors: + * Rob Clark + * Maarten Lankhorst + * + * 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. + */ + +#ifndef __LINUX_FENCE_H +#define __LINUX_FENCE_H + +#include +#include +#include +#include +#include +#include +#include + +struct fence; +struct fence_ops; +struct fence_cb; + +/** + * struct fence - software synchronization primitive + * @refcount: refcount for this fence + * @ops: fence_ops associated with this fence + * @cb_list: list of all callbacks to call + * @lock: spin_lock_irqsave used for locking + * @context: execution context this fence belongs to, returned by + * fence_context_alloc() + * @seqno: the sequence number of this fence inside the execution context, + * can be compared to decide which fence would be signaled later. + * @flags: A mask of FENCE_FLAG_* defined below + * @timestamp: Timestamp when the fence was signaled. + * @status: Optional, only valid if < 0, must be set before calling + * fence_signal, indicates that the fence has completed with an error. + * + * the flags member must be manipulated and read using the appropriate + * atomic ops (bit_*), so taking the spinlock will not be needed most + * of the time. + * + * FENCE_FLAG_SIGNALED_BIT - fence is already signaled + * FENCE_FLAG_ENABLE_SIGNAL_BIT - enable_signaling might have been called* + * FENCE_FLAG_USER_BITS - start of the unused bits, can be used by the + * implementer of the fence for its own purposes. Can be used in different + * ways by different fence implementers, so do not rely on this. + * + * *) Since atomic bitops are used, this is not guaranteed to be the case. + * Particularly, if the bit was set, but fence_signal was called right + * before this bit was set, it would have been able to set the + * FENCE_FLAG_SIGNALED_BIT, before enable_signaling was called. + * Adding a check for FENCE_FLAG_SIGNALED_BIT after setting + * FENCE_FLAG_ENABLE_SIGNAL_BIT closes this race, and makes sure that + * after fence_signal was called, any enable_signaling call will have either + * been completed, or never called at all. + */ +struct fence { + struct kref refcount; + const struct fence_ops *ops; + struct list_head cb_list; + spinlock_t *lock; + unsigned context, seqno; + unsigned long flags; + ktime_t timestamp; + int status; +}; + +enum fence_flag_bits { + FENCE_FLAG_SIGNALED_BIT, + FENCE_FLAG_ENABLE_SIGNAL_BIT, + FENCE_FLAG_USER_BITS, /* must always be last member */ +}; + +typedef void (*fence_func_t)(struct fence *fence, struct fence_cb *cb); + +/** + * struct fence_cb - callback for fence_add_callback + * @node: used by fence_add_callback to append this struct to fence::cb_list + * @func: fence_func_t to call + * + * This struct will be initialized by fence_add_callback, additional + * data can be passed along by embedding fence_cb in another struct. + */ +struct fence_cb { + struct list_head node; + fence_func_t func; +}; + +/** + * struct fence_ops - operations implemented for fence + * @get_driver_name: returns the driver name. + * @get_timeline_name: return the name of the context this fence belongs to. + * @enable_signaling: enable software signaling of fence. + * @signaled: [optional] peek whether the fence is signaled, can be null. + * @wait: custom wait implementation, or fence_default_wait. + * @release: [optional] called on destruction of fence, can be null + * @fill_driver_data: [optional] callback to fill in free-form debug info + * Returns amount of bytes filled, or -errno. + * @fence_value_str: [optional] fills in the value of the fence as a string + * @timeline_value_str: [optional] fills in the current value of the timeline + * as a string + * + * Notes on enable_signaling: + * For fence implementations that have the capability for hw->hw + * signaling, they can implement this op to enable the necessary + * irqs, or insert commands into cmdstream, etc. This is called + * in the first wait() or add_callback() path to let the fence + * implementation know that there is another driver waiting on + * the signal (ie. hw->sw case). + * + * This function can be called called from atomic context, but not + * from irq context, so normal spinlocks can be used. + * + * A return value of false indicates the fence already passed, + * or some failure occured that made it impossible to enable + * signaling. True indicates succesful enabling. + * + * fence->status may be set in enable_signaling, but only when false is + * returned. + * + * Calling fence_signal before enable_signaling is called allows + * for a tiny race window in which enable_signaling is called during, + * before, or after fence_signal. To fight this, it is recommended + * that before enable_signaling returns true an extra reference is + * taken on the fence, to be released when the fence is signaled. + * This will mean fence_signal will still be called twice, but + * the second time will be a noop since it was already signaled. + * + * Notes on signaled: + * May set fence->status if returning true. + * + * Notes on wait: + * Must not be NULL, set to fence_default_wait for default implementation. + * the fence_default_wait implementation should work for any fence, as long + * as enable_signaling works correctly. + * + * Must return -ERESTARTSYS if the wait is intr = true and the wait was + * interrupted, and remaining jiffies if fence has signaled, or 0 if wait + * timed out. Can also return other error values on custom implementations, + * which should be treated as if the fence is signaled. For example a hardware + * lockup could be reported like that. + * + * Notes on release: + * Can be NULL, this function allows additional commands to run on + * destruction of the fence. Can be called from irq context. + * If pointer is set to NULL, kfree will get called instead. + */ + +struct fence_ops { + const char * (*get_driver_name)(struct fence *fence); + const char * (*get_timeline_name)(struct fence *fence); + bool (*enable_signaling)(struct fence *fence); + bool (*signaled)(struct fence *fence); + signed long (*wait)(struct fence *fence, bool intr, signed long timeout); + void (*release)(struct fence *fence); + + int (*fill_driver_data)(struct fence *fence, void *data, int size); + void (*fence_value_str)(struct fence *fence, char *str, int size); + void (*timeline_value_str)(struct fence *fence, char *str, int size); +}; + +void fence_init(struct fence *fence, const struct fence_ops *ops, + spinlock_t *lock, unsigned context, unsigned seqno); + +void fence_release(struct kref *kref); +void fence_free(struct fence *fence); + +/** + * fence_get - increases refcount of the fence + * @fence: [in] fence to increase refcount of + * + * Returns the same fence, with refcount increased by 1. + */ +static inline struct fence *fence_get(struct fence *fence) +{ + if (fence) + kref_get(&fence->refcount); + return fence; +} + +/** + * fence_put - decreases refcount of the fence + * @fence: [in] fence to reduce refcount of + */ +static inline void fence_put(struct fence *fence) +{ + if (fence) + kref_put(&fence->refcount, fence_release); +} + +int fence_signal(struct fence *fence); +int fence_signal_locked(struct fence *fence); +signed long fence_default_wait(struct fence *fence, bool intr, signed long timeout); +int fence_add_callback(struct fence *fence, struct fence_cb *cb, + fence_func_t func); +bool fence_remove_callback(struct fence *fence, struct fence_cb *cb); +void fence_enable_sw_signaling(struct fence *fence); + +/** + * fence_is_signaled_locked - Return an indication if the fence is signaled yet. + * @fence: [in] the fence to check + * + * Returns true if the fence was already signaled, false if not. Since this + * function doesn't enable signaling, it is not guaranteed to ever return + * true if fence_add_callback, fence_wait or fence_enable_sw_signaling + * haven't been called before. + * + * This function requires fence->lock to be held. + */ +static inline bool +fence_is_signaled_locked(struct fence *fence) +{ + if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->flags)) + return true; + + if (fence->ops->signaled && fence->ops->signaled(fence)) { + fence_signal_locked(fence); + return true; + } + + return false; +} + +/** + * fence_is_signaled - Return an indication if the fence is signaled yet. + * @fence: [in] the fence to check + * + * Returns true if the fence was already signaled, false if not. Since this + * function doesn't enable signaling, it is not guaranteed to ever return + * true if fence_add_callback, fence_wait or fence_enable_sw_signaling + * haven't been called before. + * + * It's recommended for seqno fences to call fence_signal when the + * operation is complete, it makes it possible to prevent issues from + * wraparound between time of issue and time of use by checking the return + * value of this function before calling hardware-specific wait instructions. + */ +static inline bool +fence_is_signaled(struct fence *fence) +{ + if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->flags)) + return true; + + if (fence->ops->signaled && fence->ops->signaled(fence)) { + fence_signal(fence); + return true; + } + + return false; +} + +/** + * fence_later - return the chronologically later fence + * @f1: [in] the first fence from the same context + * @f2: [in] the second fence from the same context + * + * Returns NULL if both fences are signaled, otherwise the fence that would be + * signaled last. Both fences must be from the same context, since a seqno is + * not re-used across contexts. + */ +static inline struct fence *fence_later(struct fence *f1, struct fence *f2) +{ + if (WARN_ON(f1->context != f2->context)) + return NULL; + + /* + * can't check just FENCE_FLAG_SIGNALED_BIT here, it may never have been + * set if enable_signaling wasn't called, and enabling that here is + * overkill. + */ + if (f2->seqno - f1->seqno <= INT_MAX) + return fence_is_signaled(f2) ? NULL : f2; + else + return fence_is_signaled(f1) ? NULL : f1; +} + +signed long fence_wait_timeout(struct fence *, bool intr, signed long timeout); + + +/** + * fence_wait - sleep until the fence gets signaled + * @fence: [in] the fence to wait on + * @intr: [in] if true, do an interruptible wait + * + * This function will return -ERESTARTSYS if interrupted by a signal, + * or 0 if the fence was signaled. Other error values may be + * returned on custom implementations. + * + * Performs a synchronous wait on this fence. It is assumed the caller + * directly or indirectly holds a reference to the fence, otherwise the + * fence might be freed before return, resulting in undefined behavior. + */ +static inline signed long fence_wait(struct fence *fence, bool intr) +{ + signed long ret; + + /* Since fence_wait_timeout cannot timeout with + * MAX_SCHEDULE_TIMEOUT, only valid return values are + * -ERESTARTSYS and MAX_SCHEDULE_TIMEOUT. + */ + ret = fence_wait_timeout(fence, intr, MAX_SCHEDULE_TIMEOUT); + + return ret < 0 ? ret : 0; +} + +unsigned fence_context_alloc(unsigned num); + +#define FENCE_TRACE(f, fmt, args...) \ + do { \ + struct fence *__ff = (f); \ + if (config_enabled(CONFIG_FENCE_TRACE)) \ + pr_info("f %u#%u: " fmt, \ + __ff->context, __ff->seqno, ##args); \ + } while (0) + +#define FENCE_WARN(f, fmt, args...) \ + do { \ + struct fence *__ff = (f); \ + pr_warn("f %u#%u: " fmt, __ff->context, __ff->seqno, \ + ##args); \ + } while (0) + +#define FENCE_ERR(f, fmt, args...) \ + do { \ + struct fence *__ff = (f); \ + pr_err("f %u#%u: " fmt, __ff->context, __ff->seqno, \ + ##args); \ + } while (0) + +#endif /* __LINUX_FENCE_H */ diff --git a/include/trace/events/fence.h b/include/trace/events/fence.h new file mode 100644 index 000000000000..98feb1b82896 --- /dev/null +++ b/include/trace/events/fence.h @@ -0,0 +1,128 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM fence + +#if !defined(_TRACE_FENCE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_FENCE_H + +#include + +struct fence; + +TRACE_EVENT(fence_annotate_wait_on, + + /* fence: the fence waiting on f1, f1: the fence to be waited on. */ + TP_PROTO(struct fence *fence, struct fence *f1), + + TP_ARGS(fence, f1), + + TP_STRUCT__entry( + __string(driver, fence->ops->get_driver_name(fence)) + __string(timeline, fence->ops->get_driver_name(fence)) + __field(unsigned int, context) + __field(unsigned int, seqno) + + __string(waiting_driver, f1->ops->get_driver_name(f1)) + __string(waiting_timeline, f1->ops->get_timeline_name(f1)) + __field(unsigned int, waiting_context) + __field(unsigned int, waiting_seqno) + ), + + TP_fast_assign( + __assign_str(driver, fence->ops->get_driver_name(fence)) + __assign_str(timeline, fence->ops->get_timeline_name(fence)) + __entry->context = fence->context; + __entry->seqno = fence->seqno; + + __assign_str(waiting_driver, f1->ops->get_driver_name(f1)) + __assign_str(waiting_timeline, f1->ops->get_timeline_name(f1)) + __entry->waiting_context = f1->context; + __entry->waiting_seqno = f1->seqno; + + ), + + TP_printk("driver=%s timeline=%s context=%u seqno=%u " \ + "waits on driver=%s timeline=%s context=%u seqno=%u", + __get_str(driver), __get_str(timeline), __entry->context, + __entry->seqno, + __get_str(waiting_driver), __get_str(waiting_timeline), + __entry->waiting_context, __entry->waiting_seqno) +); + +DECLARE_EVENT_CLASS(fence, + + TP_PROTO(struct fence *fence), + + TP_ARGS(fence), + + TP_STRUCT__entry( + __string(driver, fence->ops->get_driver_name(fence)) + __string(timeline, fence->ops->get_timeline_name(fence)) + __field(unsigned int, context) + __field(unsigned int, seqno) + ), + + TP_fast_assign( + __assign_str(driver, fence->ops->get_driver_name(fence)) + __assign_str(timeline, fence->ops->get_timeline_name(fence)) + __entry->context = fence->context; + __entry->seqno = fence->seqno; + ), + + TP_printk("driver=%s timeline=%s context=%u seqno=%u", + __get_str(driver), __get_str(timeline), __entry->context, + __entry->seqno) +); + +DEFINE_EVENT(fence, fence_emit, + + TP_PROTO(struct fence *fence), + + TP_ARGS(fence) +); + +DEFINE_EVENT(fence, fence_init, + + TP_PROTO(struct fence *fence), + + TP_ARGS(fence) +); + +DEFINE_EVENT(fence, fence_destroy, + + TP_PROTO(struct fence *fence), + + TP_ARGS(fence) +); + +DEFINE_EVENT(fence, fence_enable_signal, + + TP_PROTO(struct fence *fence), + + TP_ARGS(fence) +); + +DEFINE_EVENT(fence, fence_signaled, + + TP_PROTO(struct fence *fence), + + TP_ARGS(fence) +); + +DEFINE_EVENT(fence, fence_wait_start, + + TP_PROTO(struct fence *fence), + + TP_ARGS(fence) +); + +DEFINE_EVENT(fence, fence_wait_end, + + TP_PROTO(struct fence *fence), + + TP_ARGS(fence) +); + +#endif /* _TRACE_FENCE_H */ + +/* This part must be outside protection */ +#include -- cgit v1.2.3 From 606b23ad609c71cfb37eeb972ea4c901034edd3c Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 1 Jul 2014 12:57:20 +0200 Subject: seqno-fence: Hardware dma-buf implementation of fencing (v6) This type of fence can be used with hardware synchronization for simple hardware that can block execution until the condition (dma_buf[offset] - value) >= 0 has been met when WAIT_GEQUAL is used, or (dma_buf[offset] != 0) has been met when WAIT_NONZERO is set. A software fallback still has to be provided in case the fence is used with a device that doesn't support this mechanism. It is useful to expose this for graphics cards that have an op to support this. Some cards like i915 can export those, but don't have an option to wait, so they need the software fallback. I extended the original patch by Rob Clark. v1: Original v2: Renamed from bikeshed to seqno, moved into dma-fence.c since not much was left of the file. Lots of documentation added. v3: Use fence_ops instead of custom callbacks. Moved to own file to avoid circular dependency between dma-buf.h and fence.h v4: Add spinlock pointer to seqno_fence_init v5: Add condition member to allow wait for != 0. Fix small style errors pointed out by checkpatch. v6: Move to a separate file. Fix up api changes in fences. Signed-off-by: Maarten Lankhorst Acked-by: Sumit Semwal Acked-by: Daniel Vetter Reviewed-by: Rob Clark #v4 Signed-off-by: Greg Kroah-Hartman --- Documentation/DocBook/device-drivers.tmpl | 2 + MAINTAINERS | 2 +- drivers/dma-buf/Makefile | 2 +- drivers/dma-buf/seqno-fence.c | 73 +++++++++++++++++++ include/linux/seqno-fence.h | 116 ++++++++++++++++++++++++++++++ 5 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 drivers/dma-buf/seqno-fence.c create mode 100644 include/linux/seqno-fence.h (limited to 'include') diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index e634657efb52..ed0ef00cd7bc 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -130,7 +130,9 @@ X!Edrivers/base/interface.c Device Drivers DMA Management !Edrivers/dma-buf/dma-buf.c !Edrivers/dma-buf/fence.c +!Edrivers/dma-buf/seqno-fence.c !Iinclude/linux/fence.h +!Iinclude/linux/seqno-fence.h !Iinclude/linux/reservation.h !Edrivers/base/dma-coherent.c !Edrivers/base/dma-mapping.c diff --git a/MAINTAINERS b/MAINTAINERS index 65c8f534b22f..7c97777dd1b3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2901,7 +2901,7 @@ L: linux-media@vger.kernel.org L: dri-devel@lists.freedesktop.org L: linaro-mm-sig@lists.linaro.org F: drivers/dma-buf/ -F: include/linux/dma-buf* include/linux/reservation.h include/linux/fence.h +F: include/linux/dma-buf* include/linux/reservation.h include/linux/*fence.h F: Documentation/dma-buf-sharing.txt T: git git://git.linaro.org/people/sumitsemwal/linux-dma-buf.git diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile index d7825bfe630e..57a675f90cd0 100644 --- a/drivers/dma-buf/Makefile +++ b/drivers/dma-buf/Makefile @@ -1 +1 @@ -obj-y := dma-buf.o fence.o reservation.o +obj-y := dma-buf.o fence.o reservation.o seqno-fence.o diff --git a/drivers/dma-buf/seqno-fence.c b/drivers/dma-buf/seqno-fence.c new file mode 100644 index 000000000000..7d12a39a4b57 --- /dev/null +++ b/drivers/dma-buf/seqno-fence.c @@ -0,0 +1,73 @@ +/* + * seqno-fence, using a dma-buf to synchronize fencing + * + * Copyright (C) 2012 Texas Instruments + * Copyright (C) 2012-2014 Canonical Ltd + * Authors: + * Rob Clark + * Maarten Lankhorst + * + * 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. + */ + +#include +#include +#include + +static const char *seqno_fence_get_driver_name(struct fence *fence) +{ + struct seqno_fence *seqno_fence = to_seqno_fence(fence); + return seqno_fence->ops->get_driver_name(fence); +} + +static const char *seqno_fence_get_timeline_name(struct fence *fence) +{ + struct seqno_fence *seqno_fence = to_seqno_fence(fence); + return seqno_fence->ops->get_timeline_name(fence); +} + +static bool seqno_enable_signaling(struct fence *fence) +{ + struct seqno_fence *seqno_fence = to_seqno_fence(fence); + return seqno_fence->ops->enable_signaling(fence); +} + +static bool seqno_signaled(struct fence *fence) +{ + struct seqno_fence *seqno_fence = to_seqno_fence(fence); + return seqno_fence->ops->signaled && seqno_fence->ops->signaled(fence); +} + +static void seqno_release(struct fence *fence) +{ + struct seqno_fence *f = to_seqno_fence(fence); + + dma_buf_put(f->sync_buf); + if (f->ops->release) + f->ops->release(fence); + else + fence_free(&f->base); +} + +static signed long seqno_wait(struct fence *fence, bool intr, signed long timeout) +{ + struct seqno_fence *f = to_seqno_fence(fence); + return f->ops->wait(fence, intr, timeout); +} + +const struct fence_ops seqno_fence_ops = { + .get_driver_name = seqno_fence_get_driver_name, + .get_timeline_name = seqno_fence_get_timeline_name, + .enable_signaling = seqno_enable_signaling, + .signaled = seqno_signaled, + .wait = seqno_wait, + .release = seqno_release, +}; +EXPORT_SYMBOL(seqno_fence_ops); diff --git a/include/linux/seqno-fence.h b/include/linux/seqno-fence.h new file mode 100644 index 000000000000..3d6003de4b0d --- /dev/null +++ b/include/linux/seqno-fence.h @@ -0,0 +1,116 @@ +/* + * seqno-fence, using a dma-buf to synchronize fencing + * + * Copyright (C) 2012 Texas Instruments + * Copyright (C) 2012 Canonical Ltd + * Authors: + * Rob Clark + * Maarten Lankhorst + * + * 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. + */ + +#ifndef __LINUX_SEQNO_FENCE_H +#define __LINUX_SEQNO_FENCE_H + +#include +#include + +enum seqno_fence_condition { + SEQNO_FENCE_WAIT_GEQUAL, + SEQNO_FENCE_WAIT_NONZERO +}; + +struct seqno_fence { + struct fence base; + + const struct fence_ops *ops; + struct dma_buf *sync_buf; + uint32_t seqno_ofs; + enum seqno_fence_condition condition; +}; + +extern const struct fence_ops seqno_fence_ops; + +/** + * to_seqno_fence - cast a fence to a seqno_fence + * @fence: fence to cast to a seqno_fence + * + * Returns NULL if the fence is not a seqno_fence, + * or the seqno_fence otherwise. + */ +static inline struct seqno_fence * +to_seqno_fence(struct fence *fence) +{ + if (fence->ops != &seqno_fence_ops) + return NULL; + return container_of(fence, struct seqno_fence, base); +} + +/** + * seqno_fence_init - initialize a seqno fence + * @fence: seqno_fence to initialize + * @lock: pointer to spinlock to use for fence + * @sync_buf: buffer containing the memory location to signal on + * @context: the execution context this fence is a part of + * @seqno_ofs: the offset within @sync_buf + * @seqno: the sequence # to signal on + * @ops: the fence_ops for operations on this seqno fence + * + * This function initializes a struct seqno_fence with passed parameters, + * and takes a reference on sync_buf which is released on fence destruction. + * + * A seqno_fence is a dma_fence which can complete in software when + * enable_signaling is called, but it also completes when + * (s32)((sync_buf)[seqno_ofs] - seqno) >= 0 is true + * + * The seqno_fence will take a refcount on the sync_buf until it's + * destroyed, but actual lifetime of sync_buf may be longer if one of the + * callers take a reference to it. + * + * Certain hardware have instructions to insert this type of wait condition + * in the command stream, so no intervention from software would be needed. + * This type of fence can be destroyed before completed, however a reference + * on the sync_buf dma-buf can be taken. It is encouraged to re-use the same + * dma-buf for sync_buf, since mapping or unmapping the sync_buf to the + * device's vm can be expensive. + * + * It is recommended for creators of seqno_fence to call fence_signal + * before destruction. This will prevent possible issues from wraparound at + * time of issue vs time of check, since users can check fence_is_signaled + * before submitting instructions for the hardware to wait on the fence. + * However, when ops.enable_signaling is not called, it doesn't have to be + * done as soon as possible, just before there's any real danger of seqno + * wraparound. + */ +static inline void +seqno_fence_init(struct seqno_fence *fence, spinlock_t *lock, + struct dma_buf *sync_buf, uint32_t context, + uint32_t seqno_ofs, uint32_t seqno, + enum seqno_fence_condition cond, + const struct fence_ops *ops) +{ + BUG_ON(!fence || !sync_buf || !ops); + BUG_ON(!ops->wait || !ops->enable_signaling || + !ops->get_driver_name || !ops->get_timeline_name); + + /* + * ops is used in fence_init for get_driver_name, so needs to be + * initialized first + */ + fence->ops = ops; + fence_init(&fence->base, &seqno_fence_ops, lock, context, seqno); + get_dma_buf(sync_buf); + fence->sync_buf = sync_buf; + fence->seqno_ofs = seqno_ofs; + fence->condition = cond; +} + +#endif /* __LINUX_SEQNO_FENCE_H */ -- cgit v1.2.3 From 3aac4502fd3f80dcf7e65dbf6edd8676893c1f46 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 1 Jul 2014 12:57:26 +0200 Subject: dma-buf: use reservation objects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows reservation objects to be used in dma-buf. it's required for implementing polling support on the fences that belong to a dma-buf. Signed-off-by: Maarten Lankhorst Acked-by: Mauro Carvalho Chehab #drivers/media/v4l2-core/ Acked-by: Thomas Hellstrom #drivers/gpu/drm/ttm Acked-by: Sumit Semwal Acked-by: Daniel Vetter Signed-off-by: Vincent Stehlé #drivers/gpu/drm/armada/ Signed-off-by: Greg Kroah-Hartman --- drivers/dma-buf/dma-buf.c | 22 ++++++++++++++++++++-- drivers/gpu/drm/armada/armada_gem.c | 2 +- drivers/gpu/drm/drm_prime.c | 8 +++++++- drivers/gpu/drm/exynos/exynos_drm_dmabuf.c | 2 +- drivers/gpu/drm/i915/i915_gem_dmabuf.c | 3 ++- drivers/gpu/drm/nouveau/nouveau_drm.c | 1 + drivers/gpu/drm/nouveau/nouveau_gem.h | 1 + drivers/gpu/drm/nouveau/nouveau_prime.c | 7 +++++++ drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c | 2 +- drivers/gpu/drm/radeon/radeon_drv.c | 2 ++ drivers/gpu/drm/radeon/radeon_prime.c | 8 ++++++++ drivers/gpu/drm/tegra/gem.c | 2 +- drivers/gpu/drm/ttm/ttm_object.c | 2 +- drivers/media/v4l2-core/videobuf2-dma-contig.c | 2 +- drivers/staging/android/ion/ion.c | 3 ++- include/drm/drmP.h | 3 +++ include/linux/dma-buf.h | 9 ++++++--- 17 files changed, 65 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index 840c7fa80983..cd40ca22911f 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -25,10 +25,12 @@ #include #include #include +#include #include #include #include #include +#include static inline int is_dma_buf_file(struct file *); @@ -56,6 +58,9 @@ static int dma_buf_release(struct inode *inode, struct file *file) list_del(&dmabuf->list_node); mutex_unlock(&db_list.lock); + if (dmabuf->resv == (struct reservation_object *)&dmabuf[1]) + reservation_object_fini(dmabuf->resv); + kfree(dmabuf); return 0; } @@ -128,6 +133,7 @@ static inline int is_dma_buf_file(struct file *file) * @size: [in] Size of the buffer * @flags: [in] mode flags for the file. * @exp_name: [in] name of the exporting module - useful for debugging. + * @resv: [in] reservation-object, NULL to allocate default one. * * Returns, on success, a newly created dma_buf object, which wraps the * supplied private data and operations for dma_buf_ops. On either missing @@ -135,10 +141,17 @@ static inline int is_dma_buf_file(struct file *file) * */ struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops, - size_t size, int flags, const char *exp_name) + size_t size, int flags, const char *exp_name, + struct reservation_object *resv) { struct dma_buf *dmabuf; struct file *file; + size_t alloc_size = sizeof(struct dma_buf); + if (!resv) + alloc_size += sizeof(struct reservation_object); + else + /* prevent &dma_buf[1] == dma_buf->resv */ + alloc_size += 1; if (WARN_ON(!priv || !ops || !ops->map_dma_buf @@ -150,7 +163,7 @@ struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops, return ERR_PTR(-EINVAL); } - dmabuf = kzalloc(sizeof(struct dma_buf), GFP_KERNEL); + dmabuf = kzalloc(alloc_size, GFP_KERNEL); if (dmabuf == NULL) return ERR_PTR(-ENOMEM); @@ -158,6 +171,11 @@ struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops, dmabuf->ops = ops; dmabuf->size = size; dmabuf->exp_name = exp_name; + if (!resv) { + resv = (struct reservation_object *)&dmabuf[1]; + reservation_object_init(resv); + } + dmabuf->resv = resv; file = anon_inode_getfile("dmabuf", &dma_buf_fops, dmabuf, flags); if (IS_ERR(file)) { diff --git a/drivers/gpu/drm/armada/armada_gem.c b/drivers/gpu/drm/armada/armada_gem.c index bb9b642d8485..7496f55611a5 100644 --- a/drivers/gpu/drm/armada/armada_gem.c +++ b/drivers/gpu/drm/armada/armada_gem.c @@ -539,7 +539,7 @@ armada_gem_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags) { return dma_buf_export(obj, &armada_gem_prime_dmabuf_ops, obj->size, - O_RDWR); + O_RDWR, NULL); } struct drm_gem_object * diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 304ca8cacbc4..99d578bad17e 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -336,7 +336,13 @@ static const struct dma_buf_ops drm_gem_prime_dmabuf_ops = { struct dma_buf *drm_gem_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags) { - return dma_buf_export(obj, &drm_gem_prime_dmabuf_ops, obj->size, flags); + struct reservation_object *robj = NULL; + + if (dev->driver->gem_prime_res_obj) + robj = dev->driver->gem_prime_res_obj(obj); + + return dma_buf_export(obj, &drm_gem_prime_dmabuf_ops, obj->size, + flags, robj); } EXPORT_SYMBOL(drm_gem_prime_export); diff --git a/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c b/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c index 2a3ad24276f8..60192ed544f0 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c @@ -187,7 +187,7 @@ struct dma_buf *exynos_dmabuf_prime_export(struct drm_device *drm_dev, struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); return dma_buf_export(obj, &exynos_dmabuf_ops, - exynos_gem_obj->base.size, flags); + exynos_gem_obj->base.size, flags, NULL); } struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev, diff --git a/drivers/gpu/drm/i915/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/i915_gem_dmabuf.c index 580aa42443ed..82a1f4b57778 100644 --- a/drivers/gpu/drm/i915/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/i915_gem_dmabuf.c @@ -237,7 +237,8 @@ struct dma_buf *i915_gem_prime_export(struct drm_device *dev, return ERR_PTR(ret); } - return dma_buf_export(gem_obj, &i915_dmabuf_ops, gem_obj->size, flags); + return dma_buf_export(gem_obj, &i915_dmabuf_ops, gem_obj->size, flags, + NULL); } static int i915_gem_object_get_pages_dmabuf(struct drm_i915_gem_object *obj) diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index ddd83756b9a2..e8ae68a9aaf1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -844,6 +844,7 @@ driver = { .gem_prime_export = drm_gem_prime_export, .gem_prime_import = drm_gem_prime_import, .gem_prime_pin = nouveau_gem_prime_pin, + .gem_prime_res_obj = nouveau_gem_prime_res_obj, .gem_prime_unpin = nouveau_gem_prime_unpin, .gem_prime_get_sg_table = nouveau_gem_prime_get_sg_table, .gem_prime_import_sg_table = nouveau_gem_prime_import_sg_table, diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.h b/drivers/gpu/drm/nouveau/nouveau_gem.h index 7caca057bc38..ddab762d81fe 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.h +++ b/drivers/gpu/drm/nouveau/nouveau_gem.h @@ -35,6 +35,7 @@ extern int nouveau_gem_ioctl_info(struct drm_device *, void *, struct drm_file *); extern int nouveau_gem_prime_pin(struct drm_gem_object *); +struct reservation_object *nouveau_gem_prime_res_obj(struct drm_gem_object *); extern void nouveau_gem_prime_unpin(struct drm_gem_object *); extern struct sg_table *nouveau_gem_prime_get_sg_table(struct drm_gem_object *); extern struct drm_gem_object *nouveau_gem_prime_import_sg_table( diff --git a/drivers/gpu/drm/nouveau/nouveau_prime.c b/drivers/gpu/drm/nouveau/nouveau_prime.c index 51a2cb102b44..1f51008e4d26 100644 --- a/drivers/gpu/drm/nouveau/nouveau_prime.c +++ b/drivers/gpu/drm/nouveau/nouveau_prime.c @@ -102,3 +102,10 @@ void nouveau_gem_prime_unpin(struct drm_gem_object *obj) nouveau_bo_unpin(nvbo); } + +struct reservation_object *nouveau_gem_prime_res_obj(struct drm_gem_object *obj) +{ + struct nouveau_bo *nvbo = nouveau_gem_object(obj); + + return nvbo->bo.resv; +} diff --git a/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c index 4fcca8d42796..a2dbfb1737b4 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c +++ b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c @@ -171,7 +171,7 @@ static struct dma_buf_ops omap_dmabuf_ops = { struct dma_buf *omap_gem_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags) { - return dma_buf_export(obj, &omap_dmabuf_ops, obj->size, flags); + return dma_buf_export(obj, &omap_dmabuf_ops, obj->size, flags, NULL); } struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev, diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index cb1421369e3a..5fa62a113db6 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -132,6 +132,7 @@ struct drm_gem_object *radeon_gem_prime_import_sg_table(struct drm_device *dev, struct sg_table *sg); int radeon_gem_prime_pin(struct drm_gem_object *obj); void radeon_gem_prime_unpin(struct drm_gem_object *obj); +struct reservation_object *radeon_gem_prime_res_obj(struct drm_gem_object *); void *radeon_gem_prime_vmap(struct drm_gem_object *obj); void radeon_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr); extern long radeon_kms_compat_ioctl(struct file *filp, unsigned int cmd, @@ -566,6 +567,7 @@ static struct drm_driver kms_driver = { .gem_prime_import = drm_gem_prime_import, .gem_prime_pin = radeon_gem_prime_pin, .gem_prime_unpin = radeon_gem_prime_unpin, + .gem_prime_res_obj = radeon_gem_prime_res_obj, .gem_prime_get_sg_table = radeon_gem_prime_get_sg_table, .gem_prime_import_sg_table = radeon_gem_prime_import_sg_table, .gem_prime_vmap = radeon_gem_prime_vmap, diff --git a/drivers/gpu/drm/radeon/radeon_prime.c b/drivers/gpu/drm/radeon/radeon_prime.c index 20074560fc25..28d71070c389 100644 --- a/drivers/gpu/drm/radeon/radeon_prime.c +++ b/drivers/gpu/drm/radeon/radeon_prime.c @@ -103,3 +103,11 @@ void radeon_gem_prime_unpin(struct drm_gem_object *obj) radeon_bo_unpin(bo); radeon_bo_unreserve(bo); } + + +struct reservation_object *radeon_gem_prime_res_obj(struct drm_gem_object *obj) +{ + struct radeon_bo *bo = gem_to_radeon_bo(obj); + + return bo->tbo.resv; +} diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index aa85b7b26f10..78cc8143760a 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -420,7 +420,7 @@ struct dma_buf *tegra_gem_prime_export(struct drm_device *drm, int flags) { return dma_buf_export(gem, &tegra_gem_prime_dmabuf_ops, gem->size, - flags); + flags, NULL); } struct drm_gem_object *tegra_gem_prime_import(struct drm_device *drm, diff --git a/drivers/gpu/drm/ttm/ttm_object.c b/drivers/gpu/drm/ttm/ttm_object.c index d2a053352789..12c87110db3a 100644 --- a/drivers/gpu/drm/ttm/ttm_object.c +++ b/drivers/gpu/drm/ttm/ttm_object.c @@ -695,7 +695,7 @@ int ttm_prime_handle_to_fd(struct ttm_object_file *tfile, } dma_buf = dma_buf_export(prime, &tdev->ops, - prime->size, flags); + prime->size, flags, NULL); if (IS_ERR(dma_buf)) { ret = PTR_ERR(dma_buf); ttm_mem_global_free(tdev->mem_glob, diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c index 880be0782dd9..c4e4dfa8123a 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-contig.c +++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c @@ -404,7 +404,7 @@ static struct dma_buf *vb2_dc_get_dmabuf(void *buf_priv, unsigned long flags) if (WARN_ON(!buf->sgt_base)) return NULL; - dbuf = dma_buf_export(buf, &vb2_dc_dmabuf_ops, buf->size, flags); + dbuf = dma_buf_export(buf, &vb2_dc_dmabuf_ops, buf->size, flags, NULL); if (IS_ERR(dbuf)) return NULL; diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index 389b8f67a2ec..270360912b2c 100644 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -1120,7 +1120,8 @@ struct dma_buf *ion_share_dma_buf(struct ion_client *client, ion_buffer_get(buffer); mutex_unlock(&client->lock); - dmabuf = dma_buf_export(buffer, &dma_buf_ops, buffer->size, O_RDWR); + dmabuf = dma_buf_export(buffer, &dma_buf_ops, buffer->size, O_RDWR, + NULL); if (IS_ERR(dmabuf)) { ion_buffer_put(buffer); return dmabuf; diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 8af71a8e2c00..e41f17ea1f13 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -83,6 +83,7 @@ struct drm_device; struct device_node; struct videomode; +struct reservation_object; #include #include @@ -923,6 +924,8 @@ struct drm_driver { /* low-level interface used by drm_gem_prime_{import,export} */ int (*gem_prime_pin)(struct drm_gem_object *obj); void (*gem_prime_unpin)(struct drm_gem_object *obj); + struct reservation_object * (*gem_prime_res_obj)( + struct drm_gem_object *obj); struct sg_table *(*gem_prime_get_sg_table)(struct drm_gem_object *obj); struct drm_gem_object *(*gem_prime_import_sg_table)( struct drm_device *dev, size_t size, diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h index f886985a28b2..fd7def2e0ae2 100644 --- a/include/linux/dma-buf.h +++ b/include/linux/dma-buf.h @@ -115,6 +115,7 @@ struct dma_buf_ops { * @exp_name: name of the exporter; useful for debugging. * @list_node: node for dma_buf accounting and debugging. * @priv: exporter specific private data for this buffer object. + * @resv: reservation object linked to this dma-buf */ struct dma_buf { size_t size; @@ -128,6 +129,7 @@ struct dma_buf { const char *exp_name; struct list_head list_node; void *priv; + struct reservation_object *resv; }; /** @@ -168,10 +170,11 @@ void dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *dmabuf_attach); struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops, - size_t size, int flags, const char *); + size_t size, int flags, const char *, + struct reservation_object *); -#define dma_buf_export(priv, ops, size, flags) \ - dma_buf_export_named(priv, ops, size, flags, KBUILD_MODNAME) +#define dma_buf_export(priv, ops, size, flags, resv) \ + dma_buf_export_named(priv, ops, size, flags, KBUILD_MODNAME, resv) int dma_buf_fd(struct dma_buf *dmabuf, int flags); struct dma_buf *dma_buf_get(int fd); -- cgit v1.2.3 From 3b1cae7c2c2b801b8530db07a02587bc1a41b3fe Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 30 Jun 2014 09:50:00 +0100 Subject: staging:iio:ad7291: Move out of staging The ad7291 driver is in a reasonable shape. It does not use non-standard API/ABI and there are no major style issues with the driver. So this patch moves it out of staging. There is one small warning from checkpatch which is also fixed in this patch. The patch also sorts the #include directives in alphabetical order. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 10 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ad7291.c | 585 +++++++++++++++++++++++++++++++++++ drivers/staging/iio/adc/Kconfig | 7 - drivers/staging/iio/adc/Makefile | 1 - drivers/staging/iio/adc/ad7291.c | 584 ---------------------------------- drivers/staging/iio/adc/ad7291.h | 12 - include/linux/platform_data/ad7291.h | 12 + 8 files changed, 608 insertions(+), 604 deletions(-) create mode 100644 drivers/iio/adc/ad7291.c delete mode 100644 drivers/staging/iio/adc/ad7291.c delete mode 100644 drivers/staging/iio/adc/ad7291.h create mode 100644 include/linux/platform_data/ad7291.h (limited to 'include') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 20a7073f1dd6..11b048a59fde 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -20,6 +20,16 @@ config AD7266 Say yes here to build support for Analog Devices AD7265 and AD7266 ADCs. +config AD7291 + tristate "Analog Devices AD7291 ADC driver" + depends on I2C + help + Say yes here to build support for Analog Devices AD7291 + 8 Channel ADC with temperature sensor. + + To compile this driver as a module, choose M here: the + module will be called ad7291. + config AD7298 tristate "Analog Devices AD7298 ADC driver" depends on SPI diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 38cf5c3f5631..ad81b512aa3d 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -5,6 +5,7 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o obj-$(CONFIG_AD7266) += ad7266.o +obj-$(CONFIG_AD7291) += ad7291.o obj-$(CONFIG_AD7298) += ad7298.o obj-$(CONFIG_AD7923) += ad7923.o obj-$(CONFIG_AD7476) += ad7476.o diff --git a/drivers/iio/adc/ad7291.c b/drivers/iio/adc/ad7291.c new file mode 100644 index 000000000000..4ed78b94afd8 --- /dev/null +++ b/drivers/iio/adc/ad7291.c @@ -0,0 +1,585 @@ +/* + * AD7291 8-Channel, I2C, 12-Bit SAR ADC with Temperature Sensor + * + * Copyright 2010-2011 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/* + * Simplified handling + * + * If no events enabled - single polled channel read + * If event enabled direct reads disable unless channel + * is in the read mask. + * + * The noise-delayed bit as per datasheet suggestion is always enabled. + */ + +/* + * AD7291 registers definition + */ +#define AD7291_COMMAND 0x00 +#define AD7291_VOLTAGE 0x01 +#define AD7291_T_SENSE 0x02 +#define AD7291_T_AVERAGE 0x03 +#define AD7291_DATA_HIGH(x) ((x) * 3 + 0x4) +#define AD7291_DATA_LOW(x) ((x) * 3 + 0x5) +#define AD7291_HYST(x) ((x) * 3 + 0x6) +#define AD7291_VOLTAGE_ALERT_STATUS 0x1F +#define AD7291_T_ALERT_STATUS 0x20 + +#define AD7291_BITS 12 +#define AD7291_VOLTAGE_LIMIT_COUNT 8 + + +/* + * AD7291 command + */ +#define AD7291_AUTOCYCLE BIT(0) +#define AD7291_RESET BIT(1) +#define AD7291_ALERT_CLEAR BIT(2) +#define AD7291_ALERT_POLARITY BIT(3) +#define AD7291_EXT_REF BIT(4) +#define AD7291_NOISE_DELAY BIT(5) +#define AD7291_T_SENSE_MASK BIT(7) +#define AD7291_VOLTAGE_MASK GENMASK(15, 8) +#define AD7291_VOLTAGE_OFFSET 8 + +/* + * AD7291 value masks + */ +#define AD7291_VALUE_MASK GENMASK(11, 0) + +/* + * AD7291 alert register bits + */ +#define AD7291_T_LOW BIT(0) +#define AD7291_T_HIGH BIT(1) +#define AD7291_T_AVG_LOW BIT(2) +#define AD7291_T_AVG_HIGH BIT(3) +#define AD7291_V_LOW(x) BIT((x) * 2) +#define AD7291_V_HIGH(x) BIT((x) * 2 + 1) + + +struct ad7291_chip_info { + struct i2c_client *client; + struct regulator *reg; + u16 command; + u16 c_mask; /* Active voltage channels for events */ + struct mutex state_lock; +}; + +static int ad7291_i2c_read(struct ad7291_chip_info *chip, u8 reg, u16 *data) +{ + struct i2c_client *client = chip->client; + int ret = 0; + + ret = i2c_smbus_read_word_swapped(client, reg); + if (ret < 0) { + dev_err(&client->dev, "I2C read error\n"); + return ret; + } + + *data = ret; + + return 0; +} + +static int ad7291_i2c_write(struct ad7291_chip_info *chip, u8 reg, u16 data) +{ + return i2c_smbus_write_word_swapped(chip->client, reg, data); +} + +static irqreturn_t ad7291_event_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct ad7291_chip_info *chip = iio_priv(private); + u16 t_status, v_status; + u16 command; + int i; + s64 timestamp = iio_get_time_ns(); + + if (ad7291_i2c_read(chip, AD7291_T_ALERT_STATUS, &t_status)) + return IRQ_HANDLED; + + if (ad7291_i2c_read(chip, AD7291_VOLTAGE_ALERT_STATUS, &v_status)) + return IRQ_HANDLED; + + if (!(t_status || v_status)) + return IRQ_HANDLED; + + command = chip->command | AD7291_ALERT_CLEAR; + ad7291_i2c_write(chip, AD7291_COMMAND, command); + + command = chip->command & ~AD7291_ALERT_CLEAR; + ad7291_i2c_write(chip, AD7291_COMMAND, command); + + /* For now treat t_sense and t_sense_average the same */ + if ((t_status & AD7291_T_LOW) || (t_status & AD7291_T_AVG_LOW)) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_TEMP, + 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + timestamp); + if ((t_status & AD7291_T_HIGH) || (t_status & AD7291_T_AVG_HIGH)) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_TEMP, + 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + timestamp); + + for (i = 0; i < AD7291_VOLTAGE_LIMIT_COUNT; i++) { + if (v_status & AD7291_V_LOW(i)) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, + i, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + timestamp); + if (v_status & AD7291_V_HIGH(i)) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, + i, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + timestamp); + } + + return IRQ_HANDLED; +} + +static unsigned int ad7291_threshold_reg(const struct iio_chan_spec *chan, + enum iio_event_direction dir, + enum iio_event_info info) +{ + unsigned int offset; + + switch (chan->type) { + case IIO_VOLTAGE: + offset = chan->channel; + break; + case IIO_TEMP: + offset = AD7291_VOLTAGE_OFFSET; + break; + default: + return 0; + } + + switch (info) { + case IIO_EV_INFO_VALUE: + if (dir == IIO_EV_DIR_FALLING) + return AD7291_DATA_HIGH(offset); + else + return AD7291_DATA_LOW(offset); + case IIO_EV_INFO_HYSTERESIS: + return AD7291_HYST(offset); + default: + break; + } + return 0; +} + +static int ad7291_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct ad7291_chip_info *chip = iio_priv(indio_dev); + int ret; + u16 uval; + + ret = ad7291_i2c_read(chip, ad7291_threshold_reg(chan, dir, info), + &uval); + if (ret < 0) + return ret; + + if (info == IIO_EV_INFO_HYSTERESIS || chan->type == IIO_VOLTAGE) + *val = uval & AD7291_VALUE_MASK; + + else + *val = sign_extend32(uval, 11); + + return IIO_VAL_INT; +} + +static int ad7291_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct ad7291_chip_info *chip = iio_priv(indio_dev); + + if (info == IIO_EV_INFO_HYSTERESIS || chan->type == IIO_VOLTAGE) { + if (val > AD7291_VALUE_MASK || val < 0) + return -EINVAL; + } else { + if (val > 2047 || val < -2048) + return -EINVAL; + } + + return ad7291_i2c_write(chip, ad7291_threshold_reg(chan, dir, info), + val); +} + +static int ad7291_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct ad7291_chip_info *chip = iio_priv(indio_dev); + /* + * To be enabled the channel must simply be on. If any are enabled + * we are in continuous sampling mode + */ + + switch (chan->type) { + case IIO_VOLTAGE: + return !!(chip->c_mask & BIT(15 - chan->channel)); + case IIO_TEMP: + /* always on */ + return 1; + default: + return -EINVAL; + } + +} + +static int ad7291_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int state) +{ + int ret = 0; + struct ad7291_chip_info *chip = iio_priv(indio_dev); + unsigned int mask; + u16 regval; + + mutex_lock(&chip->state_lock); + regval = chip->command; + /* + * To be enabled the channel must simply be on. If any are enabled + * use continuous sampling mode. + * Possible to disable temp as well but that makes single read tricky. + */ + + mask = BIT(15 - chan->channel); + + switch (chan->type) { + case IIO_VOLTAGE: + if ((!state) && (chip->c_mask & mask)) + chip->c_mask &= ~mask; + else if (state && (!(chip->c_mask & mask))) + chip->c_mask |= mask; + else + break; + + regval &= ~AD7291_AUTOCYCLE; + regval |= chip->c_mask; + if (chip->c_mask) /* Enable autocycle? */ + regval |= AD7291_AUTOCYCLE; + + ret = ad7291_i2c_write(chip, AD7291_COMMAND, regval); + if (ret < 0) + goto error_ret; + + chip->command = regval; + break; + default: + ret = -EINVAL; + } + +error_ret: + mutex_unlock(&chip->state_lock); + return ret; +} + +static int ad7291_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + int ret; + struct ad7291_chip_info *chip = iio_priv(indio_dev); + u16 regval; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_VOLTAGE: + mutex_lock(&chip->state_lock); + /* If in autocycle mode drop through */ + if (chip->command & AD7291_AUTOCYCLE) { + mutex_unlock(&chip->state_lock); + return -EBUSY; + } + /* Enable this channel alone */ + regval = chip->command & (~AD7291_VOLTAGE_MASK); + regval |= BIT(15 - chan->channel); + ret = ad7291_i2c_write(chip, AD7291_COMMAND, regval); + if (ret < 0) { + mutex_unlock(&chip->state_lock); + return ret; + } + /* Read voltage */ + ret = i2c_smbus_read_word_swapped(chip->client, + AD7291_VOLTAGE); + if (ret < 0) { + mutex_unlock(&chip->state_lock); + return ret; + } + *val = ret & AD7291_VALUE_MASK; + mutex_unlock(&chip->state_lock); + return IIO_VAL_INT; + case IIO_TEMP: + /* Assumes tsense bit of command register always set */ + ret = i2c_smbus_read_word_swapped(chip->client, + AD7291_T_SENSE); + if (ret < 0) + return ret; + *val = sign_extend32(ret, 11); + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_AVERAGE_RAW: + ret = i2c_smbus_read_word_swapped(chip->client, + AD7291_T_AVERAGE); + if (ret < 0) + return ret; + *val = sign_extend32(ret, 11); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + if (chip->reg) { + int vref; + + vref = regulator_get_voltage(chip->reg); + if (vref < 0) + return vref; + *val = vref / 1000; + } else { + *val = 2500; + } + *val2 = AD7291_BITS; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_TEMP: + /* + * One LSB of the ADC corresponds to 0.25 deg C. + * The temperature reading is in 12-bit twos + * complement format + */ + *val = 250; + return IIO_VAL_INT; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static const struct iio_event_spec ad7291_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_HYSTERESIS), + }, +}; + +#define AD7291_VOLTAGE_CHAN(_chan) \ +{ \ + .type = IIO_VOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .indexed = 1, \ + .channel = _chan, \ + .event_spec = ad7291_events, \ + .num_event_specs = ARRAY_SIZE(ad7291_events), \ +} + +static const struct iio_chan_spec ad7291_channels[] = { + AD7291_VOLTAGE_CHAN(0), + AD7291_VOLTAGE_CHAN(1), + AD7291_VOLTAGE_CHAN(2), + AD7291_VOLTAGE_CHAN(3), + AD7291_VOLTAGE_CHAN(4), + AD7291_VOLTAGE_CHAN(5), + AD7291_VOLTAGE_CHAN(6), + AD7291_VOLTAGE_CHAN(7), + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_AVERAGE_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, + .channel = 0, + .event_spec = ad7291_events, + .num_event_specs = ARRAY_SIZE(ad7291_events), + } +}; + +static const struct iio_info ad7291_info = { + .read_raw = &ad7291_read_raw, + .read_event_config = &ad7291_read_event_config, + .write_event_config = &ad7291_write_event_config, + .read_event_value = &ad7291_read_event_value, + .write_event_value = &ad7291_write_event_value, + .driver_module = THIS_MODULE, +}; + +static int ad7291_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ad7291_platform_data *pdata = client->dev.platform_data; + struct ad7291_chip_info *chip; + struct iio_dev *indio_dev; + int ret = 0; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); + if (!indio_dev) + return -ENOMEM; + chip = iio_priv(indio_dev); + + if (pdata && pdata->use_external_ref) { + chip->reg = devm_regulator_get(&client->dev, "vref"); + if (IS_ERR(chip->reg)) + return ret; + + ret = regulator_enable(chip->reg); + if (ret) + return ret; + } + + mutex_init(&chip->state_lock); + /* this is only used for device removal purposes */ + i2c_set_clientdata(client, indio_dev); + + chip->client = client; + + chip->command = AD7291_NOISE_DELAY | + AD7291_T_SENSE_MASK | /* Tsense always enabled */ + AD7291_ALERT_POLARITY; /* set irq polarity low level */ + + if (pdata && pdata->use_external_ref) + chip->command |= AD7291_EXT_REF; + + indio_dev->name = id->name; + indio_dev->channels = ad7291_channels; + indio_dev->num_channels = ARRAY_SIZE(ad7291_channels); + + indio_dev->dev.parent = &client->dev; + indio_dev->info = &ad7291_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = ad7291_i2c_write(chip, AD7291_COMMAND, AD7291_RESET); + if (ret) { + ret = -EIO; + goto error_disable_reg; + } + + ret = ad7291_i2c_write(chip, AD7291_COMMAND, chip->command); + if (ret) { + ret = -EIO; + goto error_disable_reg; + } + + if (client->irq > 0) { + ret = request_threaded_irq(client->irq, + NULL, + &ad7291_event_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + id->name, + indio_dev); + if (ret) + goto error_disable_reg; + } + + ret = iio_device_register(indio_dev); + if (ret) + goto error_unreg_irq; + + return 0; + +error_unreg_irq: + if (client->irq) + free_irq(client->irq, indio_dev); +error_disable_reg: + if (chip->reg) + regulator_disable(chip->reg); + + return ret; +} + +static int ad7291_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct ad7291_chip_info *chip = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + if (client->irq) + free_irq(client->irq, indio_dev); + + if (chip->reg) + regulator_disable(chip->reg); + + return 0; +} + +static const struct i2c_device_id ad7291_id[] = { + { "ad7291", 0 }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, ad7291_id); + +static struct i2c_driver ad7291_driver = { + .driver = { + .name = KBUILD_MODNAME, + }, + .probe = ad7291_probe, + .remove = ad7291_remove, + .id_table = ad7291_id, +}; +module_i2c_driver(ad7291_driver); + +MODULE_AUTHOR("Sonic Zhang "); +MODULE_DESCRIPTION("Analog Devices AD7291 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig index b87e382ad768..75d2d1bf93d1 100644 --- a/drivers/staging/iio/adc/Kconfig +++ b/drivers/staging/iio/adc/Kconfig @@ -3,13 +3,6 @@ # menu "Analog to digital converters" -config AD7291 - tristate "Analog Devices AD7291 ADC driver" - depends on I2C - help - Say yes here to build support for Analog Devices AD7291 - 8 Channel ADC with temperature sensor. - config AD7606 tristate "Analog Devices AD7606 ADC driver" depends on GPIOLIB diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile index afdcd1ff08ff..1c4277dbd318 100644 --- a/drivers/staging/iio/adc/Makefile +++ b/drivers/staging/iio/adc/Makefile @@ -8,7 +8,6 @@ ad7606-$(CONFIG_AD7606_IFACE_PARALLEL) += ad7606_par.o ad7606-$(CONFIG_AD7606_IFACE_SPI) += ad7606_spi.o obj-$(CONFIG_AD7606) += ad7606.o -obj-$(CONFIG_AD7291) += ad7291.o obj-$(CONFIG_AD7780) += ad7780.o obj-$(CONFIG_AD7816) += ad7816.o obj-$(CONFIG_AD7192) += ad7192.o diff --git a/drivers/staging/iio/adc/ad7291.c b/drivers/staging/iio/adc/ad7291.c deleted file mode 100644 index 9139958da787..000000000000 --- a/drivers/staging/iio/adc/ad7291.c +++ /dev/null @@ -1,584 +0,0 @@ -/* - * AD7291 8-Channel, I2C, 12-Bit SAR ADC with Temperature Sensor - * - * Copyright 2010-2011 Analog Devices Inc. - * - * Licensed under the GPL-2 or later. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "ad7291.h" - -/* - * Simplified handling - * - * If no events enabled - single polled channel read - * If event enabled direct reads disable unless channel - * is in the read mask. - * - * The noise-delayed bit as per datasheet suggestion is always enabled. - */ - -/* - * AD7291 registers definition - */ -#define AD7291_COMMAND 0x00 -#define AD7291_VOLTAGE 0x01 -#define AD7291_T_SENSE 0x02 -#define AD7291_T_AVERAGE 0x03 -#define AD7291_DATA_HIGH(x) ((x) * 3 + 0x4) -#define AD7291_DATA_LOW(x) ((x) * 3 + 0x5) -#define AD7291_HYST(x) ((x) * 3 + 0x6) -#define AD7291_VOLTAGE_ALERT_STATUS 0x1F -#define AD7291_T_ALERT_STATUS 0x20 - -#define AD7291_BITS 12 -#define AD7291_VOLTAGE_LIMIT_COUNT 8 - - -/* - * AD7291 command - */ -#define AD7291_AUTOCYCLE BIT(0) -#define AD7291_RESET BIT(1) -#define AD7291_ALERT_CLEAR BIT(2) -#define AD7291_ALERT_POLARITY BIT(3) -#define AD7291_EXT_REF BIT(4) -#define AD7291_NOISE_DELAY BIT(5) -#define AD7291_T_SENSE_MASK BIT(7) -#define AD7291_VOLTAGE_MASK GENMASK(15, 8) -#define AD7291_VOLTAGE_OFFSET 8 - -/* - * AD7291 value masks - */ -#define AD7291_VALUE_MASK GENMASK(11, 0) - -/* - * AD7291 alert register bits - */ -#define AD7291_T_LOW BIT(0) -#define AD7291_T_HIGH BIT(1) -#define AD7291_T_AVG_LOW BIT(2) -#define AD7291_T_AVG_HIGH BIT(3) -#define AD7291_V_LOW(x) BIT((x) * 2) -#define AD7291_V_HIGH(x) BIT((x) * 2 + 1) - - -struct ad7291_chip_info { - struct i2c_client *client; - struct regulator *reg; - u16 command; - u16 c_mask; /* Active voltage channels for events */ - struct mutex state_lock; -}; - -static int ad7291_i2c_read(struct ad7291_chip_info *chip, u8 reg, u16 *data) -{ - struct i2c_client *client = chip->client; - int ret = 0; - - ret = i2c_smbus_read_word_swapped(client, reg); - if (ret < 0) { - dev_err(&client->dev, "I2C read error\n"); - return ret; - } - - *data = ret; - - return 0; -} - -static int ad7291_i2c_write(struct ad7291_chip_info *chip, u8 reg, u16 data) -{ - return i2c_smbus_write_word_swapped(chip->client, reg, data); -} - -static irqreturn_t ad7291_event_handler(int irq, void *private) -{ - struct iio_dev *indio_dev = private; - struct ad7291_chip_info *chip = iio_priv(private); - u16 t_status, v_status; - u16 command; - int i; - s64 timestamp = iio_get_time_ns(); - - if (ad7291_i2c_read(chip, AD7291_T_ALERT_STATUS, &t_status)) - return IRQ_HANDLED; - - if (ad7291_i2c_read(chip, AD7291_VOLTAGE_ALERT_STATUS, &v_status)) - return IRQ_HANDLED; - - if (!(t_status || v_status)) - return IRQ_HANDLED; - - command = chip->command | AD7291_ALERT_CLEAR; - ad7291_i2c_write(chip, AD7291_COMMAND, command); - - command = chip->command & ~AD7291_ALERT_CLEAR; - ad7291_i2c_write(chip, AD7291_COMMAND, command); - - /* For now treat t_sense and t_sense_average the same */ - if ((t_status & AD7291_T_LOW) || (t_status & AD7291_T_AVG_LOW)) - iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE(IIO_TEMP, - 0, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_FALLING), - timestamp); - if ((t_status & AD7291_T_HIGH) || (t_status & AD7291_T_AVG_HIGH)) - iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE(IIO_TEMP, - 0, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_RISING), - timestamp); - - for (i = 0; i < AD7291_VOLTAGE_LIMIT_COUNT; i++) { - if (v_status & AD7291_V_LOW(i)) - iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, - i, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_FALLING), - timestamp); - if (v_status & AD7291_V_HIGH(i)) - iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, - i, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_RISING), - timestamp); - } - - return IRQ_HANDLED; -} - -static unsigned int ad7291_threshold_reg(const struct iio_chan_spec *chan, - enum iio_event_direction dir, - enum iio_event_info info) -{ - unsigned int offset; - - switch (chan->type) { - case IIO_VOLTAGE: - offset = chan->channel; - break; - case IIO_TEMP: - offset = AD7291_VOLTAGE_OFFSET; - break; - default: - return 0; - } - - switch (info) { - case IIO_EV_INFO_VALUE: - if (dir == IIO_EV_DIR_FALLING) - return AD7291_DATA_HIGH(offset); - else - return AD7291_DATA_LOW(offset); - case IIO_EV_INFO_HYSTERESIS: - return AD7291_HYST(offset); - default: - break; - } - return 0; -} - -static int ad7291_read_event_value(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, - enum iio_event_info info, - int *val, int *val2) -{ - struct ad7291_chip_info *chip = iio_priv(indio_dev); - int ret; - u16 uval; - - ret = ad7291_i2c_read(chip, ad7291_threshold_reg(chan, dir, info), - &uval); - if (ret < 0) - return ret; - - if (info == IIO_EV_INFO_HYSTERESIS || chan->type == IIO_VOLTAGE) - *val = uval & AD7291_VALUE_MASK; - - else - *val = sign_extend32(uval, 11); - - return IIO_VAL_INT; -} - -static int ad7291_write_event_value(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, - enum iio_event_info info, - int val, int val2) -{ - struct ad7291_chip_info *chip = iio_priv(indio_dev); - - if (info == IIO_EV_INFO_HYSTERESIS || chan->type == IIO_VOLTAGE) { - if (val > AD7291_VALUE_MASK || val < 0) - return -EINVAL; - } else { - if (val > 2047 || val < -2048) - return -EINVAL; - } - - return ad7291_i2c_write(chip, ad7291_threshold_reg(chan, dir, info), - val); -} - -static int ad7291_read_event_config(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir) -{ - struct ad7291_chip_info *chip = iio_priv(indio_dev); - /* - * To be enabled the channel must simply be on. If any are enabled - * we are in continuous sampling mode - */ - - switch (chan->type) { - case IIO_VOLTAGE: - return !!(chip->c_mask & BIT(15 - chan->channel)); - case IIO_TEMP: - /* always on */ - return 1; - default: - return -EINVAL; - } - -} - -static int ad7291_write_event_config(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, - int state) -{ - int ret = 0; - struct ad7291_chip_info *chip = iio_priv(indio_dev); - unsigned int mask; - u16 regval; - - mutex_lock(&chip->state_lock); - regval = chip->command; - /* - * To be enabled the channel must simply be on. If any are enabled - * use continuous sampling mode. - * Possible to disable temp as well but that makes single read tricky. - */ - - mask = BIT(15 - chan->channel); - - switch (chan->type) { - case IIO_VOLTAGE: - if ((!state) && (chip->c_mask & mask)) - chip->c_mask &= ~mask; - else if (state && (!(chip->c_mask & mask))) - chip->c_mask |= mask; - else - break; - - regval &= ~AD7291_AUTOCYCLE; - regval |= chip->c_mask; - if (chip->c_mask) /* Enable autocycle? */ - regval |= AD7291_AUTOCYCLE; - - ret = ad7291_i2c_write(chip, AD7291_COMMAND, regval); - if (ret < 0) - goto error_ret; - - chip->command = regval; - break; - default: - ret = -EINVAL; - } - -error_ret: - mutex_unlock(&chip->state_lock); - return ret; -} - -static int ad7291_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long mask) -{ - int ret; - struct ad7291_chip_info *chip = iio_priv(indio_dev); - u16 regval; - - switch (mask) { - case IIO_CHAN_INFO_RAW: - switch (chan->type) { - case IIO_VOLTAGE: - mutex_lock(&chip->state_lock); - /* If in autocycle mode drop through */ - if (chip->command & AD7291_AUTOCYCLE) { - mutex_unlock(&chip->state_lock); - return -EBUSY; - } - /* Enable this channel alone */ - regval = chip->command & (~AD7291_VOLTAGE_MASK); - regval |= BIT(15 - chan->channel); - ret = ad7291_i2c_write(chip, AD7291_COMMAND, regval); - if (ret < 0) { - mutex_unlock(&chip->state_lock); - return ret; - } - /* Read voltage */ - ret = i2c_smbus_read_word_swapped(chip->client, - AD7291_VOLTAGE); - if (ret < 0) { - mutex_unlock(&chip->state_lock); - return ret; - } - *val = ret & AD7291_VALUE_MASK; - mutex_unlock(&chip->state_lock); - return IIO_VAL_INT; - case IIO_TEMP: - /* Assumes tsense bit of command register always set */ - ret = i2c_smbus_read_word_swapped(chip->client, - AD7291_T_SENSE); - if (ret < 0) - return ret; - *val = sign_extend32(ret, 11); - return IIO_VAL_INT; - default: - return -EINVAL; - } - case IIO_CHAN_INFO_AVERAGE_RAW: - ret = i2c_smbus_read_word_swapped(chip->client, - AD7291_T_AVERAGE); - if (ret < 0) - return ret; - *val = sign_extend32(ret, 11); - return IIO_VAL_INT; - case IIO_CHAN_INFO_SCALE: - switch (chan->type) { - case IIO_VOLTAGE: - if (chip->reg) { - int vref; - vref = regulator_get_voltage(chip->reg); - if (vref < 0) - return vref; - *val = vref / 1000; - } else { - *val = 2500; - } - *val2 = AD7291_BITS; - return IIO_VAL_FRACTIONAL_LOG2; - case IIO_TEMP: - /* - * One LSB of the ADC corresponds to 0.25 deg C. - * The temperature reading is in 12-bit twos - * complement format - */ - *val = 250; - return IIO_VAL_INT; - default: - return -EINVAL; - } - default: - return -EINVAL; - } -} - -static const struct iio_event_spec ad7291_events[] = { - { - .type = IIO_EV_TYPE_THRESH, - .dir = IIO_EV_DIR_RISING, - .mask_separate = BIT(IIO_EV_INFO_VALUE) | - BIT(IIO_EV_INFO_ENABLE), - }, { - .type = IIO_EV_TYPE_THRESH, - .dir = IIO_EV_DIR_FALLING, - .mask_separate = BIT(IIO_EV_INFO_VALUE) | - BIT(IIO_EV_INFO_ENABLE), - }, { - .type = IIO_EV_TYPE_THRESH, - .dir = IIO_EV_DIR_EITHER, - .mask_separate = BIT(IIO_EV_INFO_HYSTERESIS), - }, -}; - -#define AD7291_VOLTAGE_CHAN(_chan) \ -{ \ - .type = IIO_VOLTAGE, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ - .indexed = 1, \ - .channel = _chan, \ - .event_spec = ad7291_events, \ - .num_event_specs = ARRAY_SIZE(ad7291_events), \ -} - -static const struct iio_chan_spec ad7291_channels[] = { - AD7291_VOLTAGE_CHAN(0), - AD7291_VOLTAGE_CHAN(1), - AD7291_VOLTAGE_CHAN(2), - AD7291_VOLTAGE_CHAN(3), - AD7291_VOLTAGE_CHAN(4), - AD7291_VOLTAGE_CHAN(5), - AD7291_VOLTAGE_CHAN(6), - AD7291_VOLTAGE_CHAN(7), - { - .type = IIO_TEMP, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_AVERAGE_RAW) | - BIT(IIO_CHAN_INFO_SCALE), - .indexed = 1, - .channel = 0, - .event_spec = ad7291_events, - .num_event_specs = ARRAY_SIZE(ad7291_events), - } -}; - -static const struct iio_info ad7291_info = { - .read_raw = &ad7291_read_raw, - .read_event_config = &ad7291_read_event_config, - .write_event_config = &ad7291_write_event_config, - .read_event_value = &ad7291_read_event_value, - .write_event_value = &ad7291_write_event_value, - .driver_module = THIS_MODULE, -}; - -static int ad7291_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct ad7291_platform_data *pdata = client->dev.platform_data; - struct ad7291_chip_info *chip; - struct iio_dev *indio_dev; - int ret = 0; - - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); - if (!indio_dev) - return -ENOMEM; - chip = iio_priv(indio_dev); - - if (pdata && pdata->use_external_ref) { - chip->reg = devm_regulator_get(&client->dev, "vref"); - if (IS_ERR(chip->reg)) - return ret; - - ret = regulator_enable(chip->reg); - if (ret) - return ret; - } - - mutex_init(&chip->state_lock); - /* this is only used for device removal purposes */ - i2c_set_clientdata(client, indio_dev); - - chip->client = client; - - chip->command = AD7291_NOISE_DELAY | - AD7291_T_SENSE_MASK | /* Tsense always enabled */ - AD7291_ALERT_POLARITY; /* set irq polarity low level */ - - if (pdata && pdata->use_external_ref) - chip->command |= AD7291_EXT_REF; - - indio_dev->name = id->name; - indio_dev->channels = ad7291_channels; - indio_dev->num_channels = ARRAY_SIZE(ad7291_channels); - - indio_dev->dev.parent = &client->dev; - indio_dev->info = &ad7291_info; - indio_dev->modes = INDIO_DIRECT_MODE; - - ret = ad7291_i2c_write(chip, AD7291_COMMAND, AD7291_RESET); - if (ret) { - ret = -EIO; - goto error_disable_reg; - } - - ret = ad7291_i2c_write(chip, AD7291_COMMAND, chip->command); - if (ret) { - ret = -EIO; - goto error_disable_reg; - } - - if (client->irq > 0) { - ret = request_threaded_irq(client->irq, - NULL, - &ad7291_event_handler, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - id->name, - indio_dev); - if (ret) - goto error_disable_reg; - } - - ret = iio_device_register(indio_dev); - if (ret) - goto error_unreg_irq; - - return 0; - -error_unreg_irq: - if (client->irq) - free_irq(client->irq, indio_dev); -error_disable_reg: - if (chip->reg) - regulator_disable(chip->reg); - - return ret; -} - -static int ad7291_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - struct ad7291_chip_info *chip = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - if (client->irq) - free_irq(client->irq, indio_dev); - - if (chip->reg) - regulator_disable(chip->reg); - - return 0; -} - -static const struct i2c_device_id ad7291_id[] = { - { "ad7291", 0 }, - {} -}; - -MODULE_DEVICE_TABLE(i2c, ad7291_id); - -static struct i2c_driver ad7291_driver = { - .driver = { - .name = KBUILD_MODNAME, - }, - .probe = ad7291_probe, - .remove = ad7291_remove, - .id_table = ad7291_id, -}; -module_i2c_driver(ad7291_driver); - -MODULE_AUTHOR("Sonic Zhang "); -MODULE_DESCRIPTION("Analog Devices AD7291 ADC driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/adc/ad7291.h b/drivers/staging/iio/adc/ad7291.h deleted file mode 100644 index bbd89fa51188..000000000000 --- a/drivers/staging/iio/adc/ad7291.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef __IIO_AD7291_H__ -#define __IIO_AD7291_H__ - -/** - * struct ad7291_platform_data - AD7291 platform data - * @use_external_ref: Whether to use an external or internal reference voltage - */ -struct ad7291_platform_data { - bool use_external_ref; -}; - -#endif diff --git a/include/linux/platform_data/ad7291.h b/include/linux/platform_data/ad7291.h new file mode 100644 index 000000000000..bbd89fa51188 --- /dev/null +++ b/include/linux/platform_data/ad7291.h @@ -0,0 +1,12 @@ +#ifndef __IIO_AD7291_H__ +#define __IIO_AD7291_H__ + +/** + * struct ad7291_platform_data - AD7291 platform data + * @use_external_ref: Whether to use an external or internal reference voltage + */ +struct ad7291_platform_data { + bool use_external_ref; +}; + +#endif -- cgit v1.2.3 From 0ba6b8fb91fc051535c7612f6241c8197d92323b Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 1 Jul 2014 12:57:37 +0200 Subject: reservation: add support for fences to enable cross-device synchronisation Signed-off-by: Maarten Lankhorst Acked-by: Sumit Semwal Acked-by: Daniel Vetter Reviewed-by: Rob Clark Signed-off-by: Greg Kroah-Hartman --- include/linux/reservation.h | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/reservation.h b/include/linux/reservation.h index 813dae960ebd..f3f57460a205 100644 --- a/include/linux/reservation.h +++ b/include/linux/reservation.h @@ -6,7 +6,7 @@ * Copyright (C) 2012 Texas Instruments * * Authors: - * Rob Clark + * Rob Clark * Maarten Lankhorst * Thomas Hellstrom * @@ -40,22 +40,40 @@ #define _LINUX_RESERVATION_H #include +#include +#include extern struct ww_class reservation_ww_class; struct reservation_object { struct ww_mutex lock; + + struct fence *fence_excl; + struct fence **fence_shared; + u32 fence_shared_count, fence_shared_max; }; static inline void reservation_object_init(struct reservation_object *obj) { ww_mutex_init(&obj->lock, &reservation_ww_class); + + obj->fence_shared_count = obj->fence_shared_max = 0; + obj->fence_shared = NULL; + obj->fence_excl = NULL; } static inline void reservation_object_fini(struct reservation_object *obj) { + int i; + + if (obj->fence_excl) + fence_put(obj->fence_excl); + for (i = 0; i < obj->fence_shared_count; ++i) + fence_put(obj->fence_shared[i]); + kfree(obj->fence_shared); + ww_mutex_destroy(&obj->lock); } -- cgit v1.2.3 From 9b495a5887994a6d74d5c261d012083a92b94738 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 1 Jul 2014 12:57:43 +0200 Subject: dma-buf: add poll support, v3 Thanks to Fengguang Wu for spotting a missing static cast. v2: - Kill unused variable need_shared. v3: - Clarify the BUG() in dma_buf_release some more. (Rob Clark) Signed-off-by: Maarten Lankhorst Acked-by: Sumit Semwal Acked-by: Daniel Vetter Signed-off-by: Greg Kroah-Hartman --- drivers/dma-buf/dma-buf.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/dma-buf.h | 12 ++++++ 2 files changed, 120 insertions(+) (limited to 'include') diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index cd40ca22911f..25e8c4165936 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -30,6 +30,7 @@ #include #include #include +#include #include static inline int is_dma_buf_file(struct file *); @@ -52,6 +53,16 @@ static int dma_buf_release(struct inode *inode, struct file *file) BUG_ON(dmabuf->vmapping_counter); + /* + * Any fences that a dma-buf poll can wait on should be signaled + * before releasing dma-buf. This is the responsibility of each + * driver that uses the reservation objects. + * + * If you hit this BUG() it means someone dropped their ref to the + * dma-buf while still having pending operation to the buffer. + */ + BUG_ON(dmabuf->cb_shared.active || dmabuf->cb_excl.active); + dmabuf->ops->release(dmabuf); mutex_lock(&db_list.lock); @@ -108,10 +119,103 @@ static loff_t dma_buf_llseek(struct file *file, loff_t offset, int whence) return base + offset; } +static void dma_buf_poll_cb(struct fence *fence, struct fence_cb *cb) +{ + struct dma_buf_poll_cb_t *dcb = (struct dma_buf_poll_cb_t *)cb; + unsigned long flags; + + spin_lock_irqsave(&dcb->poll->lock, flags); + wake_up_locked_poll(dcb->poll, dcb->active); + dcb->active = 0; + spin_unlock_irqrestore(&dcb->poll->lock, flags); +} + +static unsigned int dma_buf_poll(struct file *file, poll_table *poll) +{ + struct dma_buf *dmabuf; + struct reservation_object *resv; + unsigned long events; + + dmabuf = file->private_data; + if (!dmabuf || !dmabuf->resv) + return POLLERR; + + resv = dmabuf->resv; + + poll_wait(file, &dmabuf->poll, poll); + + events = poll_requested_events(poll) & (POLLIN | POLLOUT); + if (!events) + return 0; + + ww_mutex_lock(&resv->lock, NULL); + + if (resv->fence_excl && (!(events & POLLOUT) || + resv->fence_shared_count == 0)) { + struct dma_buf_poll_cb_t *dcb = &dmabuf->cb_excl; + unsigned long pevents = POLLIN; + + if (resv->fence_shared_count == 0) + pevents |= POLLOUT; + + spin_lock_irq(&dmabuf->poll.lock); + if (dcb->active) { + dcb->active |= pevents; + events &= ~pevents; + } else + dcb->active = pevents; + spin_unlock_irq(&dmabuf->poll.lock); + + if (events & pevents) { + if (!fence_add_callback(resv->fence_excl, + &dcb->cb, dma_buf_poll_cb)) + events &= ~pevents; + else + /* + * No callback queued, wake up any additional + * waiters. + */ + dma_buf_poll_cb(NULL, &dcb->cb); + } + } + + if ((events & POLLOUT) && resv->fence_shared_count > 0) { + struct dma_buf_poll_cb_t *dcb = &dmabuf->cb_shared; + int i; + + /* Only queue a new callback if no event has fired yet */ + spin_lock_irq(&dmabuf->poll.lock); + if (dcb->active) + events &= ~POLLOUT; + else + dcb->active = POLLOUT; + spin_unlock_irq(&dmabuf->poll.lock); + + if (!(events & POLLOUT)) + goto out; + + for (i = 0; i < resv->fence_shared_count; ++i) + if (!fence_add_callback(resv->fence_shared[i], + &dcb->cb, dma_buf_poll_cb)) { + events &= ~POLLOUT; + break; + } + + /* No callback queued, wake up any additional waiters. */ + if (i == resv->fence_shared_count) + dma_buf_poll_cb(NULL, &dcb->cb); + } + +out: + ww_mutex_unlock(&resv->lock); + return events; +} + static const struct file_operations dma_buf_fops = { .release = dma_buf_release, .mmap = dma_buf_mmap_internal, .llseek = dma_buf_llseek, + .poll = dma_buf_poll, }; /* @@ -171,6 +275,10 @@ struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops, dmabuf->ops = ops; dmabuf->size = size; dmabuf->exp_name = exp_name; + init_waitqueue_head(&dmabuf->poll); + dmabuf->cb_excl.poll = dmabuf->cb_shared.poll = &dmabuf->poll; + dmabuf->cb_excl.active = dmabuf->cb_shared.active = 0; + if (!resv) { resv = (struct reservation_object *)&dmabuf[1]; reservation_object_init(resv); diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h index fd7def2e0ae2..694e1fe1c4b4 100644 --- a/include/linux/dma-buf.h +++ b/include/linux/dma-buf.h @@ -30,6 +30,8 @@ #include #include #include +#include +#include struct device; struct dma_buf; @@ -130,6 +132,16 @@ struct dma_buf { struct list_head list_node; void *priv; struct reservation_object *resv; + + /* poll support */ + wait_queue_head_t poll; + + struct dma_buf_poll_cb_t { + struct fence_cb cb; + wait_queue_head_t *poll; + + unsigned long active; + } cb_excl, cb_shared; }; /** -- cgit v1.2.3 From 04a5faa8cbe5a8eaf152cb88959ba6360c26e702 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 1 Jul 2014 12:57:54 +0200 Subject: reservation: update api and add some helpers Move the list of shared fences to a struct, and return it in reservation_object_get_list(). Add reservation_object_get_excl to get the exclusive fence. Add reservation_object_reserve_shared(), which reserves space in the reservation_object for 1 more shared fence. reservation_object_add_shared_fence() and reservation_object_add_excl_fence() are used to assign a new fence to a reservation_object pointer, to complete a reservation. Changes since v1: - Add reservation_object_get_excl, reorder code a bit. Signed-off-by: Maarten Lankhorst Acked-by: Sumit Semwal Acked-by: Daniel Vetter Signed-off-by: Greg Kroah-Hartman --- Documentation/DocBook/device-drivers.tmpl | 1 + drivers/dma-buf/dma-buf.c | 35 ++++--- drivers/dma-buf/reservation.c | 156 +++++++++++++++++++++++++++++- include/linux/reservation.h | 56 +++++++++-- 4 files changed, 229 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index ed0ef00cd7bc..dd3f278faa8a 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -133,6 +133,7 @@ X!Edrivers/base/interface.c !Edrivers/dma-buf/seqno-fence.c !Iinclude/linux/fence.h !Iinclude/linux/seqno-fence.h +!Edrivers/dma-buf/reservation.c !Iinclude/linux/reservation.h !Edrivers/base/dma-coherent.c !Edrivers/base/dma-mapping.c diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index 25e8c4165936..cb8379dfeed5 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -134,7 +134,10 @@ static unsigned int dma_buf_poll(struct file *file, poll_table *poll) { struct dma_buf *dmabuf; struct reservation_object *resv; + struct reservation_object_list *fobj; + struct fence *fence_excl; unsigned long events; + unsigned shared_count; dmabuf = file->private_data; if (!dmabuf || !dmabuf->resv) @@ -150,12 +153,18 @@ static unsigned int dma_buf_poll(struct file *file, poll_table *poll) ww_mutex_lock(&resv->lock, NULL); - if (resv->fence_excl && (!(events & POLLOUT) || - resv->fence_shared_count == 0)) { + fobj = resv->fence; + if (!fobj) + goto out; + + shared_count = fobj->shared_count; + fence_excl = resv->fence_excl; + + if (fence_excl && (!(events & POLLOUT) || shared_count == 0)) { struct dma_buf_poll_cb_t *dcb = &dmabuf->cb_excl; unsigned long pevents = POLLIN; - if (resv->fence_shared_count == 0) + if (shared_count == 0) pevents |= POLLOUT; spin_lock_irq(&dmabuf->poll.lock); @@ -167,19 +176,20 @@ static unsigned int dma_buf_poll(struct file *file, poll_table *poll) spin_unlock_irq(&dmabuf->poll.lock); if (events & pevents) { - if (!fence_add_callback(resv->fence_excl, - &dcb->cb, dma_buf_poll_cb)) + if (!fence_add_callback(fence_excl, &dcb->cb, + dma_buf_poll_cb)) { events &= ~pevents; - else + } else { /* * No callback queued, wake up any additional * waiters. */ dma_buf_poll_cb(NULL, &dcb->cb); + } } } - if ((events & POLLOUT) && resv->fence_shared_count > 0) { + if ((events & POLLOUT) && shared_count > 0) { struct dma_buf_poll_cb_t *dcb = &dmabuf->cb_shared; int i; @@ -194,15 +204,18 @@ static unsigned int dma_buf_poll(struct file *file, poll_table *poll) if (!(events & POLLOUT)) goto out; - for (i = 0; i < resv->fence_shared_count; ++i) - if (!fence_add_callback(resv->fence_shared[i], - &dcb->cb, dma_buf_poll_cb)) { + for (i = 0; i < shared_count; ++i) { + struct fence *fence = fobj->shared[i]; + + if (!fence_add_callback(fence, &dcb->cb, + dma_buf_poll_cb)) { events &= ~POLLOUT; break; } + } /* No callback queued, wake up any additional waiters. */ - if (i == resv->fence_shared_count) + if (i == shared_count) dma_buf_poll_cb(NULL, &dcb->cb); } diff --git a/drivers/dma-buf/reservation.c b/drivers/dma-buf/reservation.c index a73fbf3b8e56..e6166723a9ae 100644 --- a/drivers/dma-buf/reservation.c +++ b/drivers/dma-buf/reservation.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2013 Canonical Ltd + * Copyright (C) 2012-2014 Canonical Ltd (Maarten Lankhorst) * * Based on bo.c which bears the following copyright notice, * but is dual licensed: @@ -37,3 +37,157 @@ DEFINE_WW_CLASS(reservation_ww_class); EXPORT_SYMBOL(reservation_ww_class); + +/* + * Reserve space to add a shared fence to a reservation_object, + * must be called with obj->lock held. + */ +int reservation_object_reserve_shared(struct reservation_object *obj) +{ + struct reservation_object_list *fobj, *old; + u32 max; + + old = reservation_object_get_list(obj); + + if (old && old->shared_max) { + if (old->shared_count < old->shared_max) { + /* perform an in-place update */ + kfree(obj->staged); + obj->staged = NULL; + return 0; + } else + max = old->shared_max * 2; + } else + max = 4; + + /* + * resize obj->staged or allocate if it doesn't exist, + * noop if already correct size + */ + fobj = krealloc(obj->staged, offsetof(typeof(*fobj), shared[max]), + GFP_KERNEL); + if (!fobj) + return -ENOMEM; + + obj->staged = fobj; + fobj->shared_max = max; + return 0; +} +EXPORT_SYMBOL(reservation_object_reserve_shared); + +static void +reservation_object_add_shared_inplace(struct reservation_object *obj, + struct reservation_object_list *fobj, + struct fence *fence) +{ + u32 i; + + for (i = 0; i < fobj->shared_count; ++i) { + if (fobj->shared[i]->context == fence->context) { + struct fence *old_fence = fobj->shared[i]; + + fence_get(fence); + + fobj->shared[i] = fence; + + fence_put(old_fence); + return; + } + } + + fence_get(fence); + fobj->shared[fobj->shared_count] = fence; + /* + * make the new fence visible before incrementing + * fobj->shared_count + */ + smp_wmb(); + fobj->shared_count++; +} + +static void +reservation_object_add_shared_replace(struct reservation_object *obj, + struct reservation_object_list *old, + struct reservation_object_list *fobj, + struct fence *fence) +{ + unsigned i; + + fence_get(fence); + + if (!old) { + fobj->shared[0] = fence; + fobj->shared_count = 1; + goto done; + } + + /* + * no need to bump fence refcounts, rcu_read access + * requires the use of kref_get_unless_zero, and the + * references from the old struct are carried over to + * the new. + */ + fobj->shared_count = old->shared_count; + + for (i = 0; i < old->shared_count; ++i) { + if (fence && old->shared[i]->context == fence->context) { + fence_put(old->shared[i]); + fobj->shared[i] = fence; + fence = NULL; + } else + fobj->shared[i] = old->shared[i]; + } + if (fence) + fobj->shared[fobj->shared_count++] = fence; + +done: + obj->fence = fobj; + kfree(old); +} + +/* + * Add a fence to a shared slot, obj->lock must be held, and + * reservation_object_reserve_shared_fence has been called. + */ +void reservation_object_add_shared_fence(struct reservation_object *obj, + struct fence *fence) +{ + struct reservation_object_list *old, *fobj = obj->staged; + + old = reservation_object_get_list(obj); + obj->staged = NULL; + + if (!fobj) { + BUG_ON(old->shared_count == old->shared_max); + reservation_object_add_shared_inplace(obj, old, fence); + } else + reservation_object_add_shared_replace(obj, old, fobj, fence); +} +EXPORT_SYMBOL(reservation_object_add_shared_fence); + +void reservation_object_add_excl_fence(struct reservation_object *obj, + struct fence *fence) +{ + struct fence *old_fence = obj->fence_excl; + struct reservation_object_list *old; + u32 i = 0; + + old = reservation_object_get_list(obj); + if (old) { + i = old->shared_count; + old->shared_count = 0; + } + + if (fence) + fence_get(fence); + + obj->fence_excl = fence; + + /* inplace update, no shared fences */ + while (i--) + fence_put(old->shared[i]); + + if (old_fence) + fence_put(old_fence); +} +EXPORT_SYMBOL(reservation_object_add_excl_fence); diff --git a/include/linux/reservation.h b/include/linux/reservation.h index f3f57460a205..2affe67dea6e 100644 --- a/include/linux/reservation.h +++ b/include/linux/reservation.h @@ -45,36 +45,78 @@ extern struct ww_class reservation_ww_class; +struct reservation_object_list { + u32 shared_count, shared_max; + struct fence *shared[]; +}; + struct reservation_object { struct ww_mutex lock; struct fence *fence_excl; - struct fence **fence_shared; - u32 fence_shared_count, fence_shared_max; + struct reservation_object_list *fence; + struct reservation_object_list *staged; }; +#define reservation_object_assert_held(obj) \ + lockdep_assert_held(&(obj)->lock.base) + static inline void reservation_object_init(struct reservation_object *obj) { ww_mutex_init(&obj->lock, &reservation_ww_class); - obj->fence_shared_count = obj->fence_shared_max = 0; - obj->fence_shared = NULL; obj->fence_excl = NULL; + obj->fence = NULL; + obj->staged = NULL; } static inline void reservation_object_fini(struct reservation_object *obj) { int i; + struct reservation_object_list *fobj; + /* + * This object should be dead and all references must have + * been released to it. + */ if (obj->fence_excl) fence_put(obj->fence_excl); - for (i = 0; i < obj->fence_shared_count; ++i) - fence_put(obj->fence_shared[i]); - kfree(obj->fence_shared); + + fobj = obj->fence; + if (fobj) { + for (i = 0; i < fobj->shared_count; ++i) + fence_put(fobj->shared[i]); + + kfree(fobj); + } + kfree(obj->staged); ww_mutex_destroy(&obj->lock); } +static inline struct reservation_object_list * +reservation_object_get_list(struct reservation_object *obj) +{ + reservation_object_assert_held(obj); + + return obj->fence; +} + +static inline struct fence * +reservation_object_get_excl(struct reservation_object *obj) +{ + reservation_object_assert_held(obj); + + return obj->fence_excl; +} + +int reservation_object_reserve_shared(struct reservation_object *obj); +void reservation_object_add_shared_fence(struct reservation_object *obj, + struct fence *fence); + +void reservation_object_add_excl_fence(struct reservation_object *obj, + struct fence *fence); + #endif /* _LINUX_RESERVATION_H */ -- cgit v1.2.3 From 3c3b177a9369b26890ced004867fb32708e8ef5b Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 1 Jul 2014 12:58:00 +0200 Subject: reservation: add suppport for read-only access using rcu This adds some extra functions to deal with rcu. reservation_object_get_fences_rcu() will obtain the list of shared and exclusive fences without obtaining the ww_mutex. reservation_object_wait_timeout_rcu() will wait on all fences of the reservation_object, without obtaining the ww_mutex. reservation_object_test_signaled_rcu() will test if all fences of the reservation_object are signaled without using the ww_mutex. reservation_object_get_excl and reservation_object_get_list require the reservation object to be held, updating requires write_seqcount_begin/end. If only the exclusive fence is needed, rcu_dereference followed by fence_get_rcu can be used, if the shared fences are needed it's recommended to use the supplied functions. Signed-off-by: Maarten Lankhorst Acked-by: Sumit Semwal Acked-by: Daniel Vetter Reviewed-By: Thomas Hellstrom Signed-off-by: Greg Kroah-Hartman --- drivers/dma-buf/dma-buf.c | 47 ++++-- drivers/dma-buf/fence.c | 2 +- drivers/dma-buf/reservation.c | 336 ++++++++++++++++++++++++++++++++++++++---- include/linux/fence.h | 17 +++ include/linux/reservation.h | 52 +++++-- 5 files changed, 400 insertions(+), 54 deletions(-) (limited to 'include') diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index cb8379dfeed5..f3014c448e1e 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -137,7 +137,7 @@ static unsigned int dma_buf_poll(struct file *file, poll_table *poll) struct reservation_object_list *fobj; struct fence *fence_excl; unsigned long events; - unsigned shared_count; + unsigned shared_count, seq; dmabuf = file->private_data; if (!dmabuf || !dmabuf->resv) @@ -151,14 +151,20 @@ static unsigned int dma_buf_poll(struct file *file, poll_table *poll) if (!events) return 0; - ww_mutex_lock(&resv->lock, NULL); +retry: + seq = read_seqcount_begin(&resv->seq); + rcu_read_lock(); - fobj = resv->fence; - if (!fobj) - goto out; - - shared_count = fobj->shared_count; - fence_excl = resv->fence_excl; + fobj = rcu_dereference(resv->fence); + if (fobj) + shared_count = fobj->shared_count; + else + shared_count = 0; + fence_excl = rcu_dereference(resv->fence_excl); + if (read_seqcount_retry(&resv->seq, seq)) { + rcu_read_unlock(); + goto retry; + } if (fence_excl && (!(events & POLLOUT) || shared_count == 0)) { struct dma_buf_poll_cb_t *dcb = &dmabuf->cb_excl; @@ -176,14 +182,20 @@ static unsigned int dma_buf_poll(struct file *file, poll_table *poll) spin_unlock_irq(&dmabuf->poll.lock); if (events & pevents) { - if (!fence_add_callback(fence_excl, &dcb->cb, + if (!fence_get_rcu(fence_excl)) { + /* force a recheck */ + events &= ~pevents; + dma_buf_poll_cb(NULL, &dcb->cb); + } else if (!fence_add_callback(fence_excl, &dcb->cb, dma_buf_poll_cb)) { events &= ~pevents; + fence_put(fence_excl); } else { /* * No callback queued, wake up any additional * waiters. */ + fence_put(fence_excl); dma_buf_poll_cb(NULL, &dcb->cb); } } @@ -205,13 +217,26 @@ static unsigned int dma_buf_poll(struct file *file, poll_table *poll) goto out; for (i = 0; i < shared_count; ++i) { - struct fence *fence = fobj->shared[i]; + struct fence *fence = rcu_dereference(fobj->shared[i]); + if (!fence_get_rcu(fence)) { + /* + * fence refcount dropped to zero, this means + * that fobj has been freed + * + * call dma_buf_poll_cb and force a recheck! + */ + events &= ~POLLOUT; + dma_buf_poll_cb(NULL, &dcb->cb); + break; + } if (!fence_add_callback(fence, &dcb->cb, dma_buf_poll_cb)) { + fence_put(fence); events &= ~POLLOUT; break; } + fence_put(fence); } /* No callback queued, wake up any additional waiters. */ @@ -220,7 +245,7 @@ static unsigned int dma_buf_poll(struct file *file, poll_table *poll) } out: - ww_mutex_unlock(&resv->lock); + rcu_read_unlock(); return events; } diff --git a/drivers/dma-buf/fence.c b/drivers/dma-buf/fence.c index 948bf00d955e..4222cb2aa96a 100644 --- a/drivers/dma-buf/fence.c +++ b/drivers/dma-buf/fence.c @@ -184,7 +184,7 @@ EXPORT_SYMBOL(fence_release); void fence_free(struct fence *fence) { - kfree(fence); + kfree_rcu(fence, rcu); } EXPORT_SYMBOL(fence_free); diff --git a/drivers/dma-buf/reservation.c b/drivers/dma-buf/reservation.c index e6166723a9ae..3c97c8fa8d02 100644 --- a/drivers/dma-buf/reservation.c +++ b/drivers/dma-buf/reservation.c @@ -38,6 +38,11 @@ DEFINE_WW_CLASS(reservation_ww_class); EXPORT_SYMBOL(reservation_ww_class); +struct lock_class_key reservation_seqcount_class; +EXPORT_SYMBOL(reservation_seqcount_class); + +const char reservation_seqcount_string[] = "reservation_seqcount"; +EXPORT_SYMBOL(reservation_seqcount_string); /* * Reserve space to add a shared fence to a reservation_object, * must be called with obj->lock held. @@ -82,27 +87,37 @@ reservation_object_add_shared_inplace(struct reservation_object *obj, { u32 i; + fence_get(fence); + + preempt_disable(); + write_seqcount_begin(&obj->seq); + for (i = 0; i < fobj->shared_count; ++i) { - if (fobj->shared[i]->context == fence->context) { - struct fence *old_fence = fobj->shared[i]; + struct fence *old_fence; - fence_get(fence); + old_fence = rcu_dereference_protected(fobj->shared[i], + reservation_object_held(obj)); - fobj->shared[i] = fence; + if (old_fence->context == fence->context) { + /* memory barrier is added by write_seqcount_begin */ + RCU_INIT_POINTER(fobj->shared[i], fence); + write_seqcount_end(&obj->seq); + preempt_enable(); fence_put(old_fence); return; } } - fence_get(fence); - fobj->shared[fobj->shared_count] = fence; /* - * make the new fence visible before incrementing - * fobj->shared_count + * memory barrier is added by write_seqcount_begin, + * fobj->shared_count is protected by this lock too */ - smp_wmb(); + RCU_INIT_POINTER(fobj->shared[fobj->shared_count], fence); fobj->shared_count++; + + write_seqcount_end(&obj->seq); + preempt_enable(); } static void @@ -112,11 +127,12 @@ reservation_object_add_shared_replace(struct reservation_object *obj, struct fence *fence) { unsigned i; + struct fence *old_fence = NULL; fence_get(fence); if (!old) { - fobj->shared[0] = fence; + RCU_INIT_POINTER(fobj->shared[0], fence); fobj->shared_count = 1; goto done; } @@ -130,19 +146,38 @@ reservation_object_add_shared_replace(struct reservation_object *obj, fobj->shared_count = old->shared_count; for (i = 0; i < old->shared_count; ++i) { - if (fence && old->shared[i]->context == fence->context) { - fence_put(old->shared[i]); - fobj->shared[i] = fence; - fence = NULL; + struct fence *check; + + check = rcu_dereference_protected(old->shared[i], + reservation_object_held(obj)); + + if (!old_fence && check->context == fence->context) { + old_fence = check; + RCU_INIT_POINTER(fobj->shared[i], fence); } else - fobj->shared[i] = old->shared[i]; + RCU_INIT_POINTER(fobj->shared[i], check); + } + if (!old_fence) { + RCU_INIT_POINTER(fobj->shared[fobj->shared_count], fence); + fobj->shared_count++; } - if (fence) - fobj->shared[fobj->shared_count++] = fence; done: - obj->fence = fobj; - kfree(old); + preempt_disable(); + write_seqcount_begin(&obj->seq); + /* + * RCU_INIT_POINTER can be used here, + * seqcount provides the necessary barriers + */ + RCU_INIT_POINTER(obj->fence, fobj); + write_seqcount_end(&obj->seq); + preempt_enable(); + + if (old) + kfree_rcu(old, rcu); + + if (old_fence) + fence_put(old_fence); } /* @@ -158,7 +193,7 @@ void reservation_object_add_shared_fence(struct reservation_object *obj, obj->staged = NULL; if (!fobj) { - BUG_ON(old->shared_count == old->shared_max); + BUG_ON(old->shared_count >= old->shared_max); reservation_object_add_shared_inplace(obj, old, fence); } else reservation_object_add_shared_replace(obj, old, fobj, fence); @@ -168,26 +203,275 @@ EXPORT_SYMBOL(reservation_object_add_shared_fence); void reservation_object_add_excl_fence(struct reservation_object *obj, struct fence *fence) { - struct fence *old_fence = obj->fence_excl; + struct fence *old_fence = reservation_object_get_excl(obj); struct reservation_object_list *old; u32 i = 0; old = reservation_object_get_list(obj); - if (old) { + if (old) i = old->shared_count; - old->shared_count = 0; - } if (fence) fence_get(fence); - obj->fence_excl = fence; + preempt_disable(); + write_seqcount_begin(&obj->seq); + /* write_seqcount_begin provides the necessary memory barrier */ + RCU_INIT_POINTER(obj->fence_excl, fence); + if (old) + old->shared_count = 0; + write_seqcount_end(&obj->seq); + preempt_enable(); /* inplace update, no shared fences */ while (i--) - fence_put(old->shared[i]); + fence_put(rcu_dereference_protected(old->shared[i], + reservation_object_held(obj))); if (old_fence) fence_put(old_fence); } EXPORT_SYMBOL(reservation_object_add_excl_fence); + +int reservation_object_get_fences_rcu(struct reservation_object *obj, + struct fence **pfence_excl, + unsigned *pshared_count, + struct fence ***pshared) +{ + unsigned shared_count = 0; + unsigned retry = 1; + struct fence **shared = NULL, *fence_excl = NULL; + int ret = 0; + + while (retry) { + struct reservation_object_list *fobj; + unsigned seq; + + seq = read_seqcount_begin(&obj->seq); + + rcu_read_lock(); + + fobj = rcu_dereference(obj->fence); + if (fobj) { + struct fence **nshared; + size_t sz = sizeof(*shared) * fobj->shared_max; + + nshared = krealloc(shared, sz, + GFP_NOWAIT | __GFP_NOWARN); + if (!nshared) { + rcu_read_unlock(); + nshared = krealloc(shared, sz, GFP_KERNEL); + if (nshared) { + shared = nshared; + continue; + } + + ret = -ENOMEM; + shared_count = 0; + break; + } + shared = nshared; + memcpy(shared, fobj->shared, sz); + shared_count = fobj->shared_count; + } else + shared_count = 0; + fence_excl = rcu_dereference(obj->fence_excl); + + retry = read_seqcount_retry(&obj->seq, seq); + if (retry) + goto unlock; + + if (!fence_excl || fence_get_rcu(fence_excl)) { + unsigned i; + + for (i = 0; i < shared_count; ++i) { + if (fence_get_rcu(shared[i])) + continue; + + /* uh oh, refcount failed, abort and retry */ + while (i--) + fence_put(shared[i]); + + if (fence_excl) { + fence_put(fence_excl); + fence_excl = NULL; + } + + retry = 1; + break; + } + } else + retry = 1; + +unlock: + rcu_read_unlock(); + } + *pshared_count = shared_count; + if (shared_count) + *pshared = shared; + else { + *pshared = NULL; + kfree(shared); + } + *pfence_excl = fence_excl; + + return ret; +} +EXPORT_SYMBOL_GPL(reservation_object_get_fences_rcu); + +long reservation_object_wait_timeout_rcu(struct reservation_object *obj, + bool wait_all, bool intr, + unsigned long timeout) +{ + struct fence *fence; + unsigned seq, shared_count, i = 0; + long ret = timeout; + +retry: + fence = NULL; + shared_count = 0; + seq = read_seqcount_begin(&obj->seq); + rcu_read_lock(); + + if (wait_all) { + struct reservation_object_list *fobj = rcu_dereference(obj->fence); + + if (fobj) + shared_count = fobj->shared_count; + + if (read_seqcount_retry(&obj->seq, seq)) + goto unlock_retry; + + for (i = 0; i < shared_count; ++i) { + struct fence *lfence = rcu_dereference(fobj->shared[i]); + + if (test_bit(FENCE_FLAG_SIGNALED_BIT, &lfence->flags)) + continue; + + if (!fence_get_rcu(lfence)) + goto unlock_retry; + + if (fence_is_signaled(lfence)) { + fence_put(lfence); + continue; + } + + fence = lfence; + break; + } + } + + if (!shared_count) { + struct fence *fence_excl = rcu_dereference(obj->fence_excl); + + if (read_seqcount_retry(&obj->seq, seq)) + goto unlock_retry; + + if (fence_excl && + !test_bit(FENCE_FLAG_SIGNALED_BIT, &fence_excl->flags)) { + if (!fence_get_rcu(fence_excl)) + goto unlock_retry; + + if (fence_is_signaled(fence_excl)) + fence_put(fence_excl); + else + fence = fence_excl; + } + } + + rcu_read_unlock(); + if (fence) { + ret = fence_wait_timeout(fence, intr, ret); + fence_put(fence); + if (ret > 0 && wait_all && (i + 1 < shared_count)) + goto retry; + } + return ret; + +unlock_retry: + rcu_read_unlock(); + goto retry; +} +EXPORT_SYMBOL_GPL(reservation_object_wait_timeout_rcu); + + +static inline int +reservation_object_test_signaled_single(struct fence *passed_fence) +{ + struct fence *fence, *lfence = passed_fence; + int ret = 1; + + if (!test_bit(FENCE_FLAG_SIGNALED_BIT, &lfence->flags)) { + int ret; + + fence = fence_get_rcu(lfence); + if (!fence) + return -1; + + ret = !!fence_is_signaled(fence); + fence_put(fence); + } + return ret; +} + +bool reservation_object_test_signaled_rcu(struct reservation_object *obj, + bool test_all) +{ + unsigned seq, shared_count; + int ret = true; + +retry: + shared_count = 0; + seq = read_seqcount_begin(&obj->seq); + rcu_read_lock(); + + if (test_all) { + unsigned i; + + struct reservation_object_list *fobj = rcu_dereference(obj->fence); + + if (fobj) + shared_count = fobj->shared_count; + + if (read_seqcount_retry(&obj->seq, seq)) + goto unlock_retry; + + for (i = 0; i < shared_count; ++i) { + struct fence *fence = rcu_dereference(fobj->shared[i]); + + ret = reservation_object_test_signaled_single(fence); + if (ret < 0) + goto unlock_retry; + else if (!ret) + break; + } + + /* + * There could be a read_seqcount_retry here, but nothing cares + * about whether it's the old or newer fence pointers that are + * signaled. That race could still have happened after checking + * read_seqcount_retry. If you care, use ww_mutex_lock. + */ + } + + if (!shared_count) { + struct fence *fence_excl = rcu_dereference(obj->fence_excl); + + if (read_seqcount_retry(&obj->seq, seq)) + goto unlock_retry; + + if (fence_excl) { + ret = reservation_object_test_signaled_single(fence_excl); + if (ret < 0) + goto unlock_retry; + } + } + + rcu_read_unlock(); + return ret; + +unlock_retry: + rcu_read_unlock(); + goto retry; +} +EXPORT_SYMBOL_GPL(reservation_object_test_signaled_rcu); diff --git a/include/linux/fence.h b/include/linux/fence.h index b935cc650123..d174585b874b 100644 --- a/include/linux/fence.h +++ b/include/linux/fence.h @@ -28,6 +28,7 @@ #include #include #include +#include struct fence; struct fence_ops; @@ -37,6 +38,7 @@ struct fence_cb; * struct fence - software synchronization primitive * @refcount: refcount for this fence * @ops: fence_ops associated with this fence + * @rcu: used for releasing fence with kfree_rcu * @cb_list: list of all callbacks to call * @lock: spin_lock_irqsave used for locking * @context: execution context this fence belongs to, returned by @@ -70,6 +72,7 @@ struct fence_cb; struct fence { struct kref refcount; const struct fence_ops *ops; + struct rcu_head rcu; struct list_head cb_list; spinlock_t *lock; unsigned context, seqno; @@ -191,6 +194,20 @@ static inline struct fence *fence_get(struct fence *fence) return fence; } +/** + * fence_get_rcu - get a fence from a reservation_object_list with rcu read lock + * @fence: [in] fence to increase refcount of + * + * Function returns NULL if no refcount could be obtained, or the fence. + */ +static inline struct fence *fence_get_rcu(struct fence *fence) +{ + if (kref_get_unless_zero(&fence->refcount)) + return fence; + else + return NULL; +} + /** * fence_put - decreases refcount of the fence * @fence: [in] fence to reduce refcount of diff --git a/include/linux/reservation.h b/include/linux/reservation.h index 2affe67dea6e..5a0b64cf68b4 100644 --- a/include/linux/reservation.h +++ b/include/linux/reservation.h @@ -42,22 +42,29 @@ #include #include #include +#include +#include extern struct ww_class reservation_ww_class; +extern struct lock_class_key reservation_seqcount_class; +extern const char reservation_seqcount_string[]; struct reservation_object_list { + struct rcu_head rcu; u32 shared_count, shared_max; - struct fence *shared[]; + struct fence __rcu *shared[]; }; struct reservation_object { struct ww_mutex lock; + seqcount_t seq; - struct fence *fence_excl; - struct reservation_object_list *fence; + struct fence __rcu *fence_excl; + struct reservation_object_list __rcu *fence; struct reservation_object_list *staged; }; +#define reservation_object_held(obj) lockdep_is_held(&(obj)->lock.base) #define reservation_object_assert_held(obj) \ lockdep_assert_held(&(obj)->lock.base) @@ -66,8 +73,9 @@ reservation_object_init(struct reservation_object *obj) { ww_mutex_init(&obj->lock, &reservation_ww_class); - obj->fence_excl = NULL; - obj->fence = NULL; + __seqcount_init(&obj->seq, reservation_seqcount_string, &reservation_seqcount_class); + RCU_INIT_POINTER(obj->fence, NULL); + RCU_INIT_POINTER(obj->fence_excl, NULL); obj->staged = NULL; } @@ -76,18 +84,20 @@ reservation_object_fini(struct reservation_object *obj) { int i; struct reservation_object_list *fobj; + struct fence *excl; /* * This object should be dead and all references must have - * been released to it. + * been released to it, so no need to be protected with rcu. */ - if (obj->fence_excl) - fence_put(obj->fence_excl); + excl = rcu_dereference_protected(obj->fence_excl, 1); + if (excl) + fence_put(excl); - fobj = obj->fence; + fobj = rcu_dereference_protected(obj->fence, 1); if (fobj) { for (i = 0; i < fobj->shared_count; ++i) - fence_put(fobj->shared[i]); + fence_put(rcu_dereference_protected(fobj->shared[i], 1)); kfree(fobj); } @@ -99,17 +109,15 @@ reservation_object_fini(struct reservation_object *obj) static inline struct reservation_object_list * reservation_object_get_list(struct reservation_object *obj) { - reservation_object_assert_held(obj); - - return obj->fence; + return rcu_dereference_protected(obj->fence, + reservation_object_held(obj)); } static inline struct fence * reservation_object_get_excl(struct reservation_object *obj) { - reservation_object_assert_held(obj); - - return obj->fence_excl; + return rcu_dereference_protected(obj->fence_excl, + reservation_object_held(obj)); } int reservation_object_reserve_shared(struct reservation_object *obj); @@ -119,4 +127,16 @@ void reservation_object_add_shared_fence(struct reservation_object *obj, void reservation_object_add_excl_fence(struct reservation_object *obj, struct fence *fence); +int reservation_object_get_fences_rcu(struct reservation_object *obj, + struct fence **pfence_excl, + unsigned *pshared_count, + struct fence ***pshared); + +long reservation_object_wait_timeout_rcu(struct reservation_object *obj, + bool wait_all, bool intr, + unsigned long timeout); + +bool reservation_object_test_signaled_rcu(struct reservation_object *obj, + bool test_all); + #endif /* _LINUX_RESERVATION_H */ -- cgit v1.2.3 From 9f12fbe603f7ae346b2b46008e325f0c9a68e55d Mon Sep 17 00:00:00 2001 From: Zi Shen Lim Date: Thu, 3 Jul 2014 07:56:54 -0700 Subject: net: filter: move load_pointer() into filter.h load_pointer() is already a static inline function. Let's move it into filter.h so BPF JIT implementations can reuse this function. Since we're exporting this function, let's also rename it to bpf_load_pointer() for clarity. Signed-off-by: Zi Shen Lim Reviewed-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/filter.h | 13 +++++++++++++ net/core/filter.c | 15 +++------------ 2 files changed, 16 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/linux/filter.h b/include/linux/filter.h index a7e3c48d73a7..b885dcb7eaca 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -406,6 +407,18 @@ static inline u16 bpf_anc_helper(const struct sock_filter *ftest) } } +void *bpf_internal_load_pointer_neg_helper(const struct sk_buff *skb, + int k, unsigned int size); + +static inline void *bpf_load_pointer(const struct sk_buff *skb, int k, + unsigned int size, void *buffer) +{ + if (k >= 0) + return skb_header_pointer(skb, k, size, buffer); + + return bpf_internal_load_pointer_neg_helper(skb, k, size); +} + #ifdef CONFIG_BPF_JIT #include #include diff --git a/net/core/filter.c b/net/core/filter.c index 1dbf6462f766..87af1e3e56c0 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -84,15 +84,6 @@ void *bpf_internal_load_pointer_neg_helper(const struct sk_buff *skb, int k, uns return NULL; } -static inline void *load_pointer(const struct sk_buff *skb, int k, - unsigned int size, void *buffer) -{ - if (k >= 0) - return skb_header_pointer(skb, k, size, buffer); - - return bpf_internal_load_pointer_neg_helper(skb, k, size); -} - /** * sk_filter - run a packet through a socket filter * @sk: sock associated with &sk_buff @@ -537,7 +528,7 @@ load_word: * BPF_R0 - 8/16/32-bit skb data converted to cpu endianness */ - ptr = load_pointer((struct sk_buff *) (unsigned long) CTX, off, 4, &tmp); + ptr = bpf_load_pointer((struct sk_buff *) (unsigned long) CTX, off, 4, &tmp); if (likely(ptr != NULL)) { BPF_R0 = get_unaligned_be32(ptr); CONT; @@ -547,7 +538,7 @@ load_word: LD_ABS_H: /* BPF_R0 = ntohs(*(u16 *) (skb->data + imm32)) */ off = IMM; load_half: - ptr = load_pointer((struct sk_buff *) (unsigned long) CTX, off, 2, &tmp); + ptr = bpf_load_pointer((struct sk_buff *) (unsigned long) CTX, off, 2, &tmp); if (likely(ptr != NULL)) { BPF_R0 = get_unaligned_be16(ptr); CONT; @@ -557,7 +548,7 @@ load_half: LD_ABS_B: /* BPF_R0 = *(u8 *) (skb->data + imm32) */ off = IMM; load_byte: - ptr = load_pointer((struct sk_buff *) (unsigned long) CTX, off, 1, &tmp); + ptr = bpf_load_pointer((struct sk_buff *) (unsigned long) CTX, off, 1, &tmp); if (likely(ptr != NULL)) { BPF_R0 = *(u8 *)ptr; CONT; -- cgit v1.2.3 From be1f4f48cef17c6ea9350db04c774dc8eb252158 Mon Sep 17 00:00:00 2001 From: David Laight Date: Fri, 4 Jul 2014 14:35:51 +0000 Subject: net: sctp: Inline the functions from command.c sctp_init_cmd_seq() and sctp_next_cmd() are only called from one place. The call sequence for sctp_add_cmd_sf() is likely to be longer than the inlined code. With sctp_add_cmd_sf() inlined the compiler can optimise repeated calls. Signed-off-by: David Laight Signed-off-by: David S. Miller --- include/net/sctp/command.h | 27 +++++++++++++++--- net/sctp/Makefile | 2 +- net/sctp/command.c | 68 ---------------------------------------------- 3 files changed, 24 insertions(+), 73 deletions(-) delete mode 100644 net/sctp/command.c (limited to 'include') diff --git a/include/net/sctp/command.h b/include/net/sctp/command.h index 4b7cd695e431..0e91a42065db 100644 --- a/include/net/sctp/command.h +++ b/include/net/sctp/command.h @@ -210,19 +210,38 @@ typedef struct { /* Initialize a block of memory as a command sequence. * Return 0 if the initialization fails. */ -int sctp_init_cmd_seq(sctp_cmd_seq_t *seq); +static inline int sctp_init_cmd_seq(sctp_cmd_seq_t *seq) +{ + memset(seq, 0, sizeof(sctp_cmd_seq_t)); + return 1; /* We always succeed. */ +} + /* Add a command to an sctp_cmd_seq_t. * * Use the SCTP_* constructors defined by SCTP_ARG_CONSTRUCTOR() above * to wrap data which goes in the obj argument. */ -void sctp_add_cmd_sf(sctp_cmd_seq_t *seq, sctp_verb_t verb, sctp_arg_t obj); +static inline void sctp_add_cmd_sf(sctp_cmd_seq_t *seq, sctp_verb_t verb, + sctp_arg_t obj) +{ + BUG_ON(seq->next_free_slot >= SCTP_MAX_NUM_COMMANDS); + + seq->cmds[seq->next_free_slot].verb = verb; + seq->cmds[seq->next_free_slot++].obj = obj; +} /* Return the next command structure in an sctp_cmd_seq. * Return NULL at the end of the sequence. */ -sctp_cmd_t *sctp_next_cmd(sctp_cmd_seq_t *seq); +static inline sctp_cmd_t *sctp_next_cmd(sctp_cmd_seq_t *seq) +{ + sctp_cmd_t *retval = NULL; -#endif /* __net_sctp_command_h__ */ + if (seq->next_cmd < seq->next_free_slot) + retval = &seq->cmds[seq->next_cmd++]; + + return retval; +} +#endif /* __net_sctp_command_h__ */ diff --git a/net/sctp/Makefile b/net/sctp/Makefile index 5c30b7a873df..3b4ffb021cf1 100644 --- a/net/sctp/Makefile +++ b/net/sctp/Makefile @@ -8,7 +8,7 @@ obj-$(CONFIG_NET_SCTPPROBE) += sctp_probe.o sctp-y := sm_statetable.o sm_statefuns.o sm_sideeffect.o \ protocol.o endpointola.o associola.o \ transport.o chunk.o sm_make_chunk.o ulpevent.o \ - inqueue.o outqueue.o ulpqueue.o command.o \ + inqueue.o outqueue.o ulpqueue.o \ tsnmap.o bind_addr.o socket.o primitive.o \ output.o input.o debug.o ssnmap.o auth.o diff --git a/net/sctp/command.c b/net/sctp/command.c deleted file mode 100644 index dd7375851618..000000000000 --- a/net/sctp/command.c +++ /dev/null @@ -1,68 +0,0 @@ -/* SCTP kernel implementation Copyright (C) 1999-2001 - * Cisco, Motorola, and IBM - * Copyright 2001 La Monte H.P. Yarroll - * - * This file is part of the SCTP kernel implementation - * - * These functions manipulate sctp command sequences. - * - * This SCTP implementation 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 SCTP implementation is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * ************************ - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU CC; see the file COPYING. If not, see - * . - * - * Please send any bug reports or fixes you make to the - * email address(es): - * lksctp developers - * - * Written or modified by: - * La Monte H.P. Yarroll - * Karl Knutson - */ - -#include -#include -#include - -/* Initialize a block of memory as a command sequence. */ -int sctp_init_cmd_seq(sctp_cmd_seq_t *seq) -{ - memset(seq, 0, sizeof(sctp_cmd_seq_t)); - return 1; /* We always succeed. */ -} - -/* Add a command to a sctp_cmd_seq_t. - * Return 0 if the command sequence is full. - */ -void sctp_add_cmd_sf(sctp_cmd_seq_t *seq, sctp_verb_t verb, sctp_arg_t obj) -{ - BUG_ON(seq->next_free_slot >= SCTP_MAX_NUM_COMMANDS); - - seq->cmds[seq->next_free_slot].verb = verb; - seq->cmds[seq->next_free_slot++].obj = obj; -} - -/* Return the next command structure in a sctp_cmd_seq. - * Returns NULL at the end of the sequence. - */ -sctp_cmd_t *sctp_next_cmd(sctp_cmd_seq_t *seq) -{ - sctp_cmd_t *retval = NULL; - - if (seq->next_cmd < seq->next_free_slot) - retval = &seq->cmds[seq->next_cmd++]; - - return retval; -} - -- cgit v1.2.3 From b9420e1c87838bcb354ae3495852430413dd9e4b Mon Sep 17 00:00:00 2001 From: David Laight Date: Fri, 4 Jul 2014 14:35:51 +0000 Subject: net: sctp: Optimise the way 'sctp_arg_t' values are initialised. Even if memset() is inlined (as on x86) using it to zero the union generates a memory word write of zero, followed by a write of the smaller field, and then a read of the word. As well as being a lot of instructions the sequence is unlikely to be optimised by the store-load forward hardware so will be slow. Instead allocate a field of the union that is the same size as the entire union and write a zero value to it. The compiler will then generate the required value in a register. Zeroing the union shouldn't be necessary, but this patch series isn't intended to have a behavioural change. Signed-off-by: David Laight Signed-off-by: David S. Miller --- include/net/sctp/command.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/sctp/command.h b/include/net/sctp/command.h index 0e91a42065db..589a1918ad55 100644 --- a/include/net/sctp/command.h +++ b/include/net/sctp/command.h @@ -118,6 +118,7 @@ typedef enum { #define SCTP_MAX_NUM_COMMANDS 14 typedef union { + void *zero_all; /* Set to NULL to clear the entire union */ __s32 i32; __u32 u32; __be32 be32; @@ -154,7 +155,7 @@ typedef union { static inline sctp_arg_t \ SCTP_## name (type arg) \ { sctp_arg_t retval;\ - memset(&retval, 0, sizeof(sctp_arg_t));\ + retval.zero_all = NULL;\ retval.elt = arg;\ return retval;\ } @@ -191,7 +192,7 @@ static inline sctp_arg_t SCTP_NOFORCE(void) static inline sctp_arg_t SCTP_NULL(void) { sctp_arg_t retval; - memset(&retval, 0, sizeof(sctp_arg_t)); + retval.zero_all = NULL; return retval; } @@ -212,7 +213,8 @@ typedef struct { */ static inline int sctp_init_cmd_seq(sctp_cmd_seq_t *seq) { - memset(seq, 0, sizeof(sctp_cmd_seq_t)); + seq->next_free_slot = 0; + seq->next_cmd = 0; return 1; /* We always succeed. */ } -- cgit v1.2.3 From d1a3fe26e97c0f17579c39f8a446c50f7cc3154a Mon Sep 17 00:00:00 2001 From: David Laight Date: Fri, 4 Jul 2014 14:35:57 +0000 Subject: net: sctp: Use pointers (not array indexes) to access sctp_cmd_seq_t.cmds[]. Using pointers into sctp_cmd_seq_t.cmds[] lets the compiler generate much better code. Use the last entry first to optimise the overflow check. Signed-off-by: David Laight Signed-off-by: David S. Miller --- include/net/sctp/command.h | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/net/sctp/command.h b/include/net/sctp/command.h index 589a1918ad55..f22538e68245 100644 --- a/include/net/sctp/command.h +++ b/include/net/sctp/command.h @@ -203,8 +203,8 @@ typedef struct { typedef struct { sctp_cmd_t cmds[SCTP_MAX_NUM_COMMANDS]; - __u8 next_free_slot; - __u8 next_cmd; + sctp_cmd_t *last_used_slot; + sctp_cmd_t *next_cmd; } sctp_cmd_seq_t; @@ -213,8 +213,9 @@ typedef struct { */ static inline int sctp_init_cmd_seq(sctp_cmd_seq_t *seq) { - seq->next_free_slot = 0; - seq->next_cmd = 0; + /* cmds[] is filled backwards to simplify the overflow BUG() check */ + seq->last_used_slot = seq->cmds + SCTP_MAX_NUM_COMMANDS; + seq->next_cmd = seq->last_used_slot; return 1; /* We always succeed. */ } @@ -227,10 +228,13 @@ static inline int sctp_init_cmd_seq(sctp_cmd_seq_t *seq) static inline void sctp_add_cmd_sf(sctp_cmd_seq_t *seq, sctp_verb_t verb, sctp_arg_t obj) { - BUG_ON(seq->next_free_slot >= SCTP_MAX_NUM_COMMANDS); + sctp_cmd_t *cmd = seq->last_used_slot - 1; - seq->cmds[seq->next_free_slot].verb = verb; - seq->cmds[seq->next_free_slot++].obj = obj; + BUG_ON(cmd < seq->cmds); + + cmd->verb = verb; + cmd->obj = obj; + seq->last_used_slot = cmd; } /* Return the next command structure in an sctp_cmd_seq. @@ -238,12 +242,10 @@ static inline void sctp_add_cmd_sf(sctp_cmd_seq_t *seq, sctp_verb_t verb, */ static inline sctp_cmd_t *sctp_next_cmd(sctp_cmd_seq_t *seq) { - sctp_cmd_t *retval = NULL; - - if (seq->next_cmd < seq->next_free_slot) - retval = &seq->cmds[seq->next_cmd++]; + if (seq->next_cmd <= seq->last_used_slot) + return NULL; - return retval; + return --seq->next_cmd; } #endif /* __net_sctp_command_h__ */ -- cgit v1.2.3 From 667c24917144e34880f821486bf0a6e4d05a3a14 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 8 Jul 2014 18:02:56 -0400 Subject: cgroup: introduce cgroup->subtree_control cgroup is implementing support for subsystem dependency which would require a way to enable a subsystem even when it's not directly configured through "cgroup.subtree_control". Previously, cgroup->child_subsys_mask directly reflected "cgroup.subtree_control" and the enabled subsystems in the child cgroups. This patch adds cgroup->subtree_control which "cgroup.subtree_control" operates on. cgroup->child_subsys_mask is now calculated from cgroup->subtree_control by cgroup_refresh_child_subsys_mask(), which sets it identical to cgroup->subtree_control for now. This will allow using cgroup->child_subsys_mask for all the enabled subsystems including the implicit ones and ->subtree_control for tracking the explicitly requested ones. This patch keeps the two masks identical and doesn't introduce any behavior changes. Signed-off-by: Tejun Heo Acked-by: Li Zefan Acked-by: Johannes Weiner --- include/linux/cgroup.h | 8 +++++++- kernel/cgroup.c | 46 +++++++++++++++++++++++++++++----------------- 2 files changed, 36 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 8a111dd42d7a..8d52c8e5b510 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -203,7 +203,13 @@ struct cgroup { struct kernfs_node *kn; /* cgroup kernfs entry */ struct kernfs_node *populated_kn; /* kn for "cgroup.subtree_populated" */ - /* the bitmask of subsystems enabled on the child cgroups */ + /* + * The bitmask of subsystems enabled on the child cgroups. + * ->subtree_control is the one configured through + * "cgroup.subtree_control" while ->child_subsys_mask is the + * effective one which may have more subsystems enabled. + */ + unsigned int subtree_control; unsigned int child_subsys_mask; /* Private pointers for each registered subsystem */ diff --git a/kernel/cgroup.c b/kernel/cgroup.c index a46d7e2012b4..14a9d88eacf9 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -1036,6 +1036,11 @@ static void cgroup_put(struct cgroup *cgrp) css_put(&cgrp->self); } +static void cgroup_refresh_child_subsys_mask(struct cgroup *cgrp) +{ + cgrp->child_subsys_mask = cgrp->subtree_control; +} + /** * cgroup_kn_unlock - unlocking helper for cgroup kernfs methods * @kn: the kernfs_node being serviced @@ -1208,12 +1213,15 @@ static int rebind_subsystems(struct cgroup_root *dst_root, unsigned int ss_mask) up_write(&css_set_rwsem); src_root->subsys_mask &= ~(1 << ssid); - src_root->cgrp.child_subsys_mask &= ~(1 << ssid); + src_root->cgrp.subtree_control &= ~(1 << ssid); + cgroup_refresh_child_subsys_mask(&src_root->cgrp); /* default hierarchy doesn't enable controllers by default */ dst_root->subsys_mask |= 1 << ssid; - if (dst_root != &cgrp_dfl_root) - dst_root->cgrp.child_subsys_mask |= 1 << ssid; + if (dst_root != &cgrp_dfl_root) { + dst_root->cgrp.subtree_control |= 1 << ssid; + cgroup_refresh_child_subsys_mask(&dst_root->cgrp); + } if (ss->bind) ss->bind(css); @@ -2454,7 +2462,7 @@ static int cgroup_controllers_show(struct seq_file *seq, void *v) { struct cgroup *cgrp = seq_css(seq)->cgroup; - cgroup_print_ss_mask(seq, cgroup_parent(cgrp)->child_subsys_mask); + cgroup_print_ss_mask(seq, cgroup_parent(cgrp)->subtree_control); return 0; } @@ -2463,7 +2471,7 @@ static int cgroup_subtree_control_show(struct seq_file *seq, void *v) { struct cgroup *cgrp = seq_css(seq)->cgroup; - cgroup_print_ss_mask(seq, cgrp->child_subsys_mask); + cgroup_print_ss_mask(seq, cgrp->subtree_control); return 0; } @@ -2608,7 +2616,7 @@ static ssize_t cgroup_subtree_control_write(struct kernfs_open_file *of, for_each_subsys(ss, ssid) { if (enable & (1 << ssid)) { - if (cgrp->child_subsys_mask & (1 << ssid)) { + if (cgrp->subtree_control & (1 << ssid)) { enable &= ~(1 << ssid); continue; } @@ -2616,7 +2624,7 @@ static ssize_t cgroup_subtree_control_write(struct kernfs_open_file *of, /* unavailable or not enabled on the parent? */ if (!(cgrp_dfl_root.subsys_mask & (1 << ssid)) || (cgroup_parent(cgrp) && - !(cgroup_parent(cgrp)->child_subsys_mask & (1 << ssid)))) { + !(cgroup_parent(cgrp)->subtree_control & (1 << ssid)))) { ret = -ENOENT; goto out_unlock; } @@ -2644,14 +2652,14 @@ static ssize_t cgroup_subtree_control_write(struct kernfs_open_file *of, return restart_syscall(); } } else if (disable & (1 << ssid)) { - if (!(cgrp->child_subsys_mask & (1 << ssid))) { + if (!(cgrp->subtree_control & (1 << ssid))) { disable &= ~(1 << ssid); continue; } /* a child has it enabled? */ cgroup_for_each_live_child(child, cgrp) { - if (child->child_subsys_mask & (1 << ssid)) { + if (child->subtree_control & (1 << ssid)) { ret = -EBUSY; goto out_unlock; } @@ -2665,7 +2673,7 @@ static ssize_t cgroup_subtree_control_write(struct kernfs_open_file *of, } /* - * Except for the root, child_subsys_mask must be zero for a cgroup + * Except for the root, subtree_control must be zero for a cgroup * with tasks so that child cgroups don't compete against tasks. */ if (enable && cgroup_parent(cgrp) && !list_empty(&cgrp->cset_links)) { @@ -2673,8 +2681,9 @@ static ssize_t cgroup_subtree_control_write(struct kernfs_open_file *of, goto out_unlock; } - cgrp->child_subsys_mask |= enable; - cgrp->child_subsys_mask &= ~disable; + cgrp->subtree_control |= enable; + cgrp->subtree_control &= ~disable; + cgroup_refresh_child_subsys_mask(cgrp); /* create new csses */ for_each_subsys(ss, ssid) { @@ -2713,8 +2722,9 @@ out_unlock: return ret ?: nbytes; err_undo_css: - cgrp->child_subsys_mask &= ~enable; - cgrp->child_subsys_mask |= disable; + cgrp->subtree_control &= ~enable; + cgrp->subtree_control |= disable; + cgroup_refresh_child_subsys_mask(cgrp); for_each_subsys(ss, ssid) { if (!(enable & (1 << ssid))) @@ -4428,10 +4438,12 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, /* * On the default hierarchy, a child doesn't automatically inherit - * child_subsys_mask from the parent. Each is configured manually. + * subtree_control from the parent. Each is configured manually. */ - if (!cgroup_on_dfl(cgrp)) - cgrp->child_subsys_mask = parent->child_subsys_mask; + if (!cgroup_on_dfl(cgrp)) { + cgrp->subtree_control = parent->subtree_control; + cgroup_refresh_child_subsys_mask(cgrp); + } kernfs_activate(kn); -- cgit v1.2.3 From f63070d350e3562baa6196f1043e01cd8da2509a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 8 Jul 2014 18:02:57 -0400 Subject: cgroup: make interface files visible iff enabled on cgroup->subtree_control cgroup is implementing support for subsystem dependency which would require a way to enable a subsystem even when it's not directly configured through "cgroup.subtree_control". The preceding patch distinguished cgroup->subtree_control and ->child_subsys_mask where the former is the subsystems explicitly configured by the userland and the latter is all enabled subsystems currently is equal to the former but will include subsystems implicitly enabled through dependency. Subsystems which are enabled due to dependency shouldn't be visible to userland. This patch updates cgroup_subtree_control_write() and create_css() such that interface files are not created for implicitly enabled subsytems. * @visible paramter is added to create_css(). Interface files are created only when true. * If an already implicitly enabled subsystem is turned on through "cgroup.subtree_control", the existing css should be used. css draining is skipped. * cgroup_subtree_control_write() computes the new target cgroup->child_subsys_mask and create/kill or show/hide csses accordingly. As the two subsystem masks are still kept identical, this patch doesn't introduce any behavior changes. Signed-off-by: Tejun Heo Acked-by: Li Zefan Acked-by: Johannes Weiner --- include/linux/cgroup.h | 2 ++ kernel/cgroup.c | 78 +++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 66 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 8d52c8e5b510..5287f931680a 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -208,6 +208,8 @@ struct cgroup { * ->subtree_control is the one configured through * "cgroup.subtree_control" while ->child_subsys_mask is the * effective one which may have more subsystems enabled. + * Controller knobs are made available iff it's enabled in + * ->subtree_control. */ unsigned int subtree_control; unsigned int child_subsys_mask; diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 14a9d88eacf9..331fa296c7e0 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -186,7 +186,8 @@ static void cgroup_put(struct cgroup *cgrp); static int rebind_subsystems(struct cgroup_root *dst_root, unsigned int ss_mask); static int cgroup_destroy_locked(struct cgroup *cgrp); -static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss); +static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss, + bool visible); static void css_release(struct percpu_ref *ref); static void kill_css(struct cgroup_subsys_state *css); static int cgroup_addrm_files(struct cgroup *cgrp, struct cftype cfts[], @@ -2577,6 +2578,7 @@ static ssize_t cgroup_subtree_control_write(struct kernfs_open_file *of, loff_t off) { unsigned int enable = 0, disable = 0; + unsigned int css_enable, css_disable, old_ctrl, new_ctrl; struct cgroup *cgrp, *child; struct cgroup_subsys *ss; char *tok; @@ -2629,6 +2631,13 @@ static ssize_t cgroup_subtree_control_write(struct kernfs_open_file *of, goto out_unlock; } + /* + * @ss is already enabled through dependency and + * we'll just make it visible. Skip draining. + */ + if (cgrp->child_subsys_mask & (1 << ssid)) + continue; + /* * Because css offlining is asynchronous, userland * might try to re-enable the same controller while @@ -2681,17 +2690,39 @@ static ssize_t cgroup_subtree_control_write(struct kernfs_open_file *of, goto out_unlock; } + /* + * Update subsys masks and calculate what needs to be done. More + * subsystems than specified may need to be enabled or disabled + * depending on subsystem dependencies. + */ cgrp->subtree_control |= enable; cgrp->subtree_control &= ~disable; + + old_ctrl = cgrp->child_subsys_mask; cgroup_refresh_child_subsys_mask(cgrp); + new_ctrl = cgrp->child_subsys_mask; + + css_enable = ~old_ctrl & new_ctrl; + css_disable = old_ctrl & ~new_ctrl; + enable |= css_enable; + disable |= css_disable; - /* create new csses */ + /* + * Create new csses or make the existing ones visible. A css is + * created invisible if it's being implicitly enabled through + * dependency. An invisible css is made visible when the userland + * explicitly enables it. + */ for_each_subsys(ss, ssid) { if (!(enable & (1 << ssid))) continue; cgroup_for_each_live_child(child, cgrp) { - ret = create_css(child, ss); + if (css_enable & (1 << ssid)) + ret = create_css(child, ss, + cgrp->subtree_control & (1 << ssid)); + else + ret = cgroup_populate_dir(child, 1 << ssid); if (ret) goto err_undo_css; } @@ -2706,13 +2737,21 @@ static ssize_t cgroup_subtree_control_write(struct kernfs_open_file *of, if (ret) goto err_undo_css; - /* all tasks are now migrated away from the old csses, kill them */ + /* + * All tasks are migrated out of disabled csses. Kill or hide + * them. A css is hidden when the userland requests it to be + * disabled while other subsystems are still depending on it. + */ for_each_subsys(ss, ssid) { if (!(disable & (1 << ssid))) continue; - cgroup_for_each_live_child(child, cgrp) - kill_css(cgroup_css(child, ss)); + cgroup_for_each_live_child(child, cgrp) { + if (css_disable & (1 << ssid)) + kill_css(cgroup_css(child, ss)); + else + cgroup_clear_dir(child, 1 << ssid); + } } kernfs_activate(cgrp->kn); @@ -2732,8 +2771,14 @@ err_undo_css: cgroup_for_each_live_child(child, cgrp) { struct cgroup_subsys_state *css = cgroup_css(child, ss); - if (css) + + if (!css) + continue; + + if (css_enable & (1 << ssid)) kill_css(css); + else + cgroup_clear_dir(child, 1 << ssid); } } goto out_unlock; @@ -4282,12 +4327,14 @@ static void offline_css(struct cgroup_subsys_state *css) * create_css - create a cgroup_subsys_state * @cgrp: the cgroup new css will be associated with * @ss: the subsys of new css + * @visible: whether to create control knobs for the new css or not * * Create a new css associated with @cgrp - @ss pair. On success, the new - * css is online and installed in @cgrp with all interface files created. - * Returns 0 on success, -errno on failure. + * css is online and installed in @cgrp with all interface files created if + * @visible. Returns 0 on success, -errno on failure. */ -static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss) +static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss, + bool visible) { struct cgroup *parent = cgroup_parent(cgrp); struct cgroup_subsys_state *parent_css = cgroup_css(parent, ss); @@ -4311,9 +4358,11 @@ static int create_css(struct cgroup *cgrp, struct cgroup_subsys *ss) goto err_free_percpu_ref; css->id = err; - err = cgroup_populate_dir(cgrp, 1 << ss->id); - if (err) - goto err_free_id; + if (visible) { + err = cgroup_populate_dir(cgrp, 1 << ss->id); + if (err) + goto err_free_id; + } /* @css is ready to be brought online now, make it visible */ list_add_tail_rcu(&css->sibling, &parent_css->children); @@ -4430,7 +4479,8 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name, /* let's create and online css's */ for_each_subsys(ss, ssid) { if (parent->child_subsys_mask & (1 << ssid)) { - ret = create_css(cgrp, ss); + ret = create_css(cgrp, ss, + parent->subtree_control & (1 << ssid)); if (ret) goto out_destroy; } -- cgit v1.2.3 From b4536f0cab2b18414e26101a2b9d484c5cbea0c0 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 8 Jul 2014 18:02:57 -0400 Subject: cgroup: implement cgroup_subsys->css_reset() cgroup is implementing support for subsystem dependency which would require a way to enable a subsystem even when it's not directly configured through "cgroup.subtree_control". The previous patches added support for explicitly and implicitly enabled subsystems and showing/hiding their interface files. An explicitly enabled subsystem may become implicitly enabled if it's turned off through "cgroup.subtree_control" but there are subsystems depending on it. In such cases, the subsystem, as it's turned off when seen from userland, shouldn't enforce any resource control. Also, the subsystem may be explicitly turned on later again and its interface files should be as close to the intial state as possible. This patch adds cgroup_subsys->css_reset() which is invoked when a css is hidden. The callback should disable resource control and reset the state to the vanilla state. Signed-off-by: Tejun Heo Acked-by: Li Zefan Acked-by: Johannes Weiner --- Documentation/cgroups/cgroups.txt | 14 ++++++++++++++ include/linux/cgroup.h | 1 + kernel/cgroup.c | 16 ++++++++++++---- 3 files changed, 27 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/Documentation/cgroups/cgroups.txt b/Documentation/cgroups/cgroups.txt index 821de56d1580..10c949b293e4 100644 --- a/Documentation/cgroups/cgroups.txt +++ b/Documentation/cgroups/cgroups.txt @@ -599,6 +599,20 @@ fork. If this method returns 0 (success) then this should remain valid while the caller holds cgroup_mutex and it is ensured that either attach() or cancel_attach() will be called in future. +void css_reset(struct cgroup_subsys_state *css) +(cgroup_mutex held by caller) + +An optional operation which should restore @css's configuration to the +initial state. This is currently only used on the unified hierarchy +when a subsystem is disabled on a cgroup through +"cgroup.subtree_control" but should remain enabled because other +subsystems depend on it. cgroup core makes such a css invisible by +removing the associated interface files and invokes this callback so +that the hidden subsystem can return to the initial neutral state. +This prevents unexpected resource control from a hidden css and +ensures that the configuration is in the initial state when it is made +visible again later. + void cancel_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) (cgroup_mutex held by caller) diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 5287f931680a..db99e3b923b1 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -642,6 +642,7 @@ struct cgroup_subsys { int (*css_online)(struct cgroup_subsys_state *css); void (*css_offline)(struct cgroup_subsys_state *css); void (*css_free)(struct cgroup_subsys_state *css); + void (*css_reset)(struct cgroup_subsys_state *css); int (*can_attach)(struct cgroup_subsys_state *css, struct cgroup_taskset *tset); diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 331fa296c7e0..3a6b77d7ba4a 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -2740,17 +2740,25 @@ static ssize_t cgroup_subtree_control_write(struct kernfs_open_file *of, /* * All tasks are migrated out of disabled csses. Kill or hide * them. A css is hidden when the userland requests it to be - * disabled while other subsystems are still depending on it. + * disabled while other subsystems are still depending on it. The + * css must not actively control resources and be in the vanilla + * state if it's made visible again later. Controllers which may + * be depended upon should provide ->css_reset() for this purpose. */ for_each_subsys(ss, ssid) { if (!(disable & (1 << ssid))) continue; cgroup_for_each_live_child(child, cgrp) { - if (css_disable & (1 << ssid)) - kill_css(cgroup_css(child, ss)); - else + struct cgroup_subsys_state *css = cgroup_css(child, ss); + + if (css_disable & (1 << ssid)) { + kill_css(css); + } else { cgroup_clear_dir(child, 1 << ssid); + if (ss->css_reset) + ss->css_reset(css); + } } } -- cgit v1.2.3 From af0ba6789c8e43518635606d0af1ff475ba7471a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 8 Jul 2014 18:02:57 -0400 Subject: cgroup: implement cgroup_subsys->depends_on Currently, the blkio subsystem attributes all of writeback IOs to the root. One of the issues is that there's no way to tell who originated a writeback IO from block layer. Those IOs are usually issued asynchronously from a task which didn't have anything to do with actually generating the dirty pages. The memory subsystem, when enabled, already keeps track of the ownership of each dirty page and it's desirable for blkio to piggyback instead of adding its own per-page tag. blkio piggybacking on memory is an implementation detail which preferably should be handled automatically without requiring explicit userland action. To achieve that, this patch implements cgroup_subsys->depends_on which contains the mask of subsystems which should be enabled together when the subsystem is enabled. The previous patches already implemented the support for enabled but invisible subsystems and cgroup_subsys->depends_on can be easily implemented by updating cgroup_refresh_child_subsys_mask() so that it calculates cgroup->child_subsys_mask considering cgroup_subsys->depends_on of the explicitly enabled subsystems. Documentation/cgroups/unified-hierarchy.txt is updated to explain that subsystems may not become immediately available after being unused from userland and that dependency could be a factor in it. As subsystems may already keep residual references, this doesn't significantly change how subsystem rebinding can be used. Signed-off-by: Tejun Heo Acked-by: Li Zefan Acked-by: Johannes Weiner --- Documentation/cgroups/unified-hierarchy.txt | 23 ++++++++++++-- include/linux/cgroup.h | 9 ++++++ kernel/cgroup.c | 49 ++++++++++++++++++++++++++++- 3 files changed, 77 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/Documentation/cgroups/unified-hierarchy.txt b/Documentation/cgroups/unified-hierarchy.txt index 324b182e6000..a7a2205539a7 100644 --- a/Documentation/cgroups/unified-hierarchy.txt +++ b/Documentation/cgroups/unified-hierarchy.txt @@ -97,9 +97,26 @@ change soon. All controllers which are not bound to other hierarchies are automatically bound to unified hierarchy and show up at the root of it. Controllers which are enabled only in the root of unified -hierarchy can be bound to other hierarchies at any time. This allows -mixing unified hierarchy with the traditional multiple hierarchies in -a fully backward compatible way. +hierarchy can be bound to other hierarchies. This allows mixing +unified hierarchy with the traditional multiple hierarchies in a fully +backward compatible way. + +A controller can be moved across hierarchies only after the controller +is no longer referenced in its current hierarchy. Because per-cgroup +controller states are destroyed asynchronously and controllers may +have lingering references, a controller may not show up immediately on +the unified hierarchy after the final umount of the previous +hierarchy. Similarly, a controller should be fully disabled to be +moved out of the unified hierarchy and it may take some time for the +disabled controller to become available for other hierarchies; +furthermore, due to dependencies among controllers, other controllers +may need to be disabled too. + +While useful for development and manual configurations, dynamically +moving controllers between the unified and other hierarchies is +strongly discouraged for production use. It is recommended to decide +the hierarchies and controller associations before starting using the +controllers. 2-2. cgroup.subtree_control diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index db99e3b923b1..28853e771f3b 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -693,6 +693,15 @@ struct cgroup_subsys { /* base cftypes, automatically registered with subsys itself */ struct cftype *base_cftypes; + + /* + * A subsystem may depend on other subsystems. When such subsystem + * is enabled on a cgroup, the depended-upon subsystems are enabled + * together if available. Subsystems enabled due to dependency are + * not visible to userland until explicitly enabled. The following + * specifies the mask of subsystems that this one depends on. + */ + unsigned int depends_on; }; #define SUBSYS(_x) extern struct cgroup_subsys _x ## _cgrp_subsys; diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 3a6b77d7ba4a..cd02e99d5d3b 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -1037,9 +1037,56 @@ static void cgroup_put(struct cgroup *cgrp) css_put(&cgrp->self); } +/** + * cgroup_refresh_child_subsys_mask - update child_subsys_mask + * @cgrp: the target cgroup + * + * On the default hierarchy, a subsystem may request other subsystems to be + * enabled together through its ->depends_on mask. In such cases, more + * subsystems than specified in "cgroup.subtree_control" may be enabled. + * + * This function determines which subsystems need to be enabled given the + * current @cgrp->subtree_control and records it in + * @cgrp->child_subsys_mask. The resulting mask is always a superset of + * @cgrp->subtree_control and follows the usual hierarchy rules. + */ static void cgroup_refresh_child_subsys_mask(struct cgroup *cgrp) { - cgrp->child_subsys_mask = cgrp->subtree_control; + struct cgroup *parent = cgroup_parent(cgrp); + unsigned int cur_ss_mask = cgrp->subtree_control; + struct cgroup_subsys *ss; + int ssid; + + lockdep_assert_held(&cgroup_mutex); + + if (!cgroup_on_dfl(cgrp)) { + cgrp->child_subsys_mask = cur_ss_mask; + return; + } + + while (true) { + unsigned int new_ss_mask = cur_ss_mask; + + for_each_subsys(ss, ssid) + if (cur_ss_mask & (1 << ssid)) + new_ss_mask |= ss->depends_on; + + /* + * Mask out subsystems which aren't available. This can + * happen only if some depended-upon subsystems were bound + * to non-default hierarchies. + */ + if (parent) + new_ss_mask &= parent->child_subsys_mask; + else + new_ss_mask &= cgrp->root->subsys_mask; + + if (new_ss_mask == cur_ss_mask) + break; + cur_ss_mask = new_ss_mask; + } + + cgrp->child_subsys_mask = cur_ss_mask; } /** -- cgit v1.2.3 From 5a1379e8748a5cfa3eb068f812d61bde849ef76c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 4 Jun 2014 17:48:15 +0200 Subject: firmware loader: allow disabling of udev as firmware loader [The patch was originally proposed by Tom Gundersen, and rewritten afterwards by me; most of changelogs below borrowed from Tom's original patch -- tiwai] Currently (at least) the dell-rbu driver selects FW_LOADER_USER_HELPER, which means that distros can't really stop loading firmware through udev without breaking other users (though some have). Ideally we would remove/disable the udev firmware helper in both the kernel and in udev, but if we were to disable it in udev and not the kernel, the result would be (seemingly) hung kernels as no one would be around to cancel firmware requests. This patch allows udev firmware loading to be disabled while still allowing non-udev firmware loading, as done by the dell-rbu driver, to continue working. This is achieved by only using the fallback mechanism when the uevent is suppressed. The patch renames the user-selectable Kconfig from FW_LOADER_USER_HELPER to FW_LOADER_USER_HELPER_FALLBACK, and the former is reverse-selected by the latter or the drivers that need userhelper like dell-rbu. Also, the "default y" is removed together with this change, since it's been deprecated in udev upstream, thus rather better to disable it nowadays. Tested with FW_LOADER_USER_HELPER=n LATTICE_ECP3_CONFIG=y DELL_RBU=y and udev without the firmware loading support, but I don't have the hardware to test the lattice/dell drivers, so additional testing would be appreciated. Reviewed-by: Tom Gundersen Cc: Ming Lei Cc: Abhay Salunke Cc: Stefan Roese Cc: Arnd Bergmann Cc: Kay Sievers Tested-by: Balaji Singh Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- drivers/base/Kconfig | 10 ++++++++-- drivers/base/firmware_class.c | 15 ++++++++++----- include/linux/firmware.h | 2 +- 3 files changed, 19 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index 00e13ce5cbbd..88500fed3c7a 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -149,15 +149,21 @@ config EXTRA_FIRMWARE_DIR some other directory containing the firmware files. config FW_LOADER_USER_HELPER + bool + +config FW_LOADER_USER_HELPER_FALLBACK bool "Fallback user-helper invocation for firmware loading" depends on FW_LOADER - default y + select FW_LOADER_USER_HELPER help This option enables / disables the invocation of user-helper (e.g. udev) for loading firmware files as a fallback after the direct file loading in kernel fails. The user-mode helper is no longer required unless you have a special firmware file that - resides in a non-standard path. + resides in a non-standard path. Moreover, the udev support has + been deprecated upstream. + + If you are unsure about this, say N here. config DEBUG_DRIVER bool "Driver Core verbose debug messages" diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index d276e33880be..46ea5f4c3bb5 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -100,9 +100,14 @@ static inline long firmware_loading_timeout(void) #define FW_OPT_UEVENT (1U << 0) #define FW_OPT_NOWAIT (1U << 1) #ifdef CONFIG_FW_LOADER_USER_HELPER -#define FW_OPT_FALLBACK (1U << 2) +#define FW_OPT_USERHELPER (1U << 2) #else -#define FW_OPT_FALLBACK 0 +#define FW_OPT_USERHELPER 0 +#endif +#ifdef CONFIG_FW_LOADER_USER_HELPER_FALLBACK +#define FW_OPT_FALLBACK FW_OPT_USERHELPER +#else +#define FW_OPT_FALLBACK 0 #endif struct firmware_cache { @@ -1111,7 +1116,7 @@ _request_firmware(const struct firmware **firmware_p, const char *name, ret = fw_get_filesystem_firmware(device, fw->priv); if (ret) { - if (opt_flags & FW_OPT_FALLBACK) { + if (opt_flags & FW_OPT_USERHELPER) { dev_warn(device, "Direct firmware load failed with error %d\n", ret); @@ -1171,7 +1176,7 @@ request_firmware(const struct firmware **firmware_p, const char *name, } EXPORT_SYMBOL(request_firmware); -#ifdef CONFIG_FW_LOADER_USER_HELPER +#ifdef CONFIG_FW_LOADER_USER_HELPER_FALLBACK /** * request_firmware: - load firmware directly without usermode helper * @firmware_p: pointer to firmware image @@ -1277,7 +1282,7 @@ request_firmware_nowait( fw_work->context = context; fw_work->cont = cont; fw_work->opt_flags = FW_OPT_NOWAIT | FW_OPT_FALLBACK | - (uevent ? FW_OPT_UEVENT : 0); + (uevent ? FW_OPT_UEVENT : FW_OPT_USERHELPER); if (!try_module_get(module)) { kfree(fw_work); diff --git a/include/linux/firmware.h b/include/linux/firmware.h index 59529330efd6..67e5b801af0c 100644 --- a/include/linux/firmware.h +++ b/include/linux/firmware.h @@ -68,7 +68,7 @@ static inline void release_firmware(const struct firmware *fw) #endif -#ifdef CONFIG_FW_LOADER_USER_HELPER +#ifdef CONFIG_FW_LOADER_USER_HELPER_FALLBACK int request_firmware_direct(const struct firmware **fw, const char *name, struct device *device); #else -- cgit v1.2.3 From c868edf42b4db89907b467c92b7f035c8c1cb0e5 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 2 Jul 2014 09:55:05 -0700 Subject: firmware loader: inform direct failure when udev loader is disabled Now that the udev firmware loader is optional request_firmware() will not provide any information on the kernel ring buffer if direct firmware loading failed and udev firmware loading is disabled. If no information is needed request_firmware_direct() should be used for optional firmware, at which point drivers can take on the onus over informing of any failures, if udev firmware loading is disabled though we should at the very least provide some sort of information as when the udev loader was enabled by default back in the days. With this change with a simple firmware load test module [0]: Example output without FW_LOADER_USER_HELPER_FALLBACK platform fake-dev.0: Direct firmware load for fake.bin failed with error -2 Example with FW_LOADER_USER_HELPER_FALLBACK platform fake-dev.0: Direct firmware load for fake.bin failed with error -2 platform fake-dev.0: Falling back to user helper Without this change without FW_LOADER_USER_HELPER_FALLBACK we get no output logged upon failure. Cc: Tom Gundersen Cc: Ming Lei Cc: Abhay Salunke Cc: Stefan Roese Cc: Arnd Bergmann Cc: Kay Sievers Signed-off-by: Luis R. Rodriguez Reviewed-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_class.c | 13 +++++++------ include/linux/firmware.h | 15 ++++++++------- 2 files changed, 15 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 28bc6db9fbf4..124d50ceb116 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -109,6 +109,7 @@ static inline long firmware_loading_timeout(void) #else #define FW_OPT_FALLBACK 0 #endif +#define FW_OPT_NO_WARN (1U << 3) struct firmware_cache { /* firmware_buf instance will be added into the below list */ @@ -1105,10 +1106,11 @@ _request_firmware(const struct firmware **firmware_p, const char *name, ret = fw_get_filesystem_firmware(device, fw->priv); if (ret) { - if (opt_flags & FW_OPT_USERHELPER) { + if (!(opt_flags & FW_OPT_NO_WARN)) dev_warn(device, - "Direct firmware load failed with error %d\n", - ret); + "Direct firmware load for %s failed with error %d\n", + name, ret); + if (opt_flags & FW_OPT_USERHELPER) { dev_warn(device, "Falling back to user helper\n"); ret = fw_load_from_user_helper(fw, name, device, opt_flags, timeout); @@ -1165,7 +1167,6 @@ request_firmware(const struct firmware **firmware_p, const char *name, } EXPORT_SYMBOL(request_firmware); -#ifdef CONFIG_FW_LOADER_USER_HELPER_FALLBACK /** * request_firmware: - load firmware directly without usermode helper * @firmware_p: pointer to firmware image @@ -1182,12 +1183,12 @@ int request_firmware_direct(const struct firmware **firmware_p, { int ret; __module_get(THIS_MODULE); - ret = _request_firmware(firmware_p, name, device, FW_OPT_UEVENT); + ret = _request_firmware(firmware_p, name, device, + FW_OPT_UEVENT | FW_OPT_NO_WARN); module_put(THIS_MODULE); return ret; } EXPORT_SYMBOL_GPL(request_firmware_direct); -#endif /** * release_firmware: - release the resource associated with a firmware image diff --git a/include/linux/firmware.h b/include/linux/firmware.h index 67e5b801af0c..5c41c5e75b5c 100644 --- a/include/linux/firmware.h +++ b/include/linux/firmware.h @@ -45,6 +45,8 @@ int request_firmware_nowait( struct module *module, bool uevent, const char *name, struct device *device, gfp_t gfp, void *context, void (*cont)(const struct firmware *fw, void *context)); +int request_firmware_direct(const struct firmware **fw, const char *name, + struct device *device); void release_firmware(const struct firmware *fw); #else @@ -66,13 +68,12 @@ static inline void release_firmware(const struct firmware *fw) { } -#endif +static inline int request_firmware_direct(const struct firmware **fw, + const char *name, + struct device *device) +{ + return -EINVAL; +} -#ifdef CONFIG_FW_LOADER_USER_HELPER_FALLBACK -int request_firmware_direct(const struct firmware **fw, const char *name, - struct device *device); -#else -#define request_firmware_direct request_firmware #endif - #endif -- cgit v1.2.3 From 3d713e0e382e6fcfb4bba1501645b66c129ad60b Mon Sep 17 00:00:00 2001 From: Kim Phillips Date: Mon, 2 Jun 2014 19:42:58 -0500 Subject: driver core: platform: add device binding path 'driver_override' Needed by platform device drivers, such as the upcoming vfio-platform driver, in order to bypass the existing OF, ACPI, id_table and name string matches, and successfully be able to be bound to any device, like so: echo vfio-platform > /sys/bus/platform/devices/fff51000.ethernet/driver_override echo fff51000.ethernet > /sys/bus/platform/devices/fff51000.ethernet/driver/unbind echo fff51000.ethernet > /sys/bus/platform/drivers_probe This mimics "PCI: Introduce new device binding path using pci_dev.driver_override", which is an interface enhancement for more deterministic PCI device binding, e.g., when in the presence of hotplug. Reviewed-by: Alex Williamson Reviewed-by: Alexander Graf Reviewed-by: Stuart Yoder Signed-off-by: Kim Phillips Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-bus-platform | 20 ++++++++++++ drivers/base/platform.c | 47 ++++++++++++++++++++++++++++ include/linux/platform_device.h | 1 + 3 files changed, 68 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-platform (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-bus-platform b/Documentation/ABI/testing/sysfs-bus-platform new file mode 100644 index 000000000000..5172a6124b27 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-platform @@ -0,0 +1,20 @@ +What: /sys/bus/platform/devices/.../driver_override +Date: April 2014 +Contact: Kim Phillips +Description: + This file allows the driver for a device to be specified which + will override standard OF, ACPI, ID table, and name matching. + When specified, only a driver with a name matching the value + written to driver_override will have an opportunity to bind + to the device. The override is specified by writing a string + to the driver_override file (echo vfio-platform > \ + driver_override) and may be cleared with an empty string + (echo > driver_override). This returns the device to standard + matching rules binding. Writing to driver_override does not + automatically unbind the device from its current driver or make + any attempt to automatically load the specified driver. If no + driver with a matching name is currently loaded in the kernel, + the device will not bind to any driver. This also allows + devices to opt-out of driver binding using a driver_override + name such as "none". Only a single driver may be specified in + the override, there is no support for parsing delimiters. diff --git a/drivers/base/platform.c b/drivers/base/platform.c index c48c4acb9b87..148f66a1d49a 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "base.h" #include "power/power.h" @@ -191,6 +192,7 @@ static void platform_device_release(struct device *dev) kfree(pa->pdev.dev.platform_data); kfree(pa->pdev.mfd_cell); kfree(pa->pdev.resource); + kfree(pa->pdev.driver_override); kfree(pa); } @@ -698,8 +700,49 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *a, } static DEVICE_ATTR_RO(modalias); +static ssize_t driver_override_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + char *driver_override, *old = pdev->driver_override, *cp; + + if (count > PATH_MAX) + return -EINVAL; + + driver_override = kstrndup(buf, count, GFP_KERNEL); + if (!driver_override) + return -ENOMEM; + + cp = strchr(driver_override, '\n'); + if (cp) + *cp = '\0'; + + if (strlen(driver_override)) { + pdev->driver_override = driver_override; + } else { + kfree(driver_override); + pdev->driver_override = NULL; + } + + kfree(old); + + return count; +} + +static ssize_t driver_override_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + + return sprintf(buf, "%s\n", pdev->driver_override); +} +static DEVICE_ATTR_RW(driver_override); + + static struct attribute *platform_dev_attrs[] = { &dev_attr_modalias.attr, + &dev_attr_driver_override.attr, NULL, }; ATTRIBUTE_GROUPS(platform_dev); @@ -755,6 +798,10 @@ static int platform_match(struct device *dev, struct device_driver *drv) struct platform_device *pdev = to_platform_device(dev); struct platform_driver *pdrv = to_platform_driver(drv); + /* When driver_override is set, only bind to the matching driver */ + if (pdev->driver_override) + return !strcmp(pdev->driver_override, drv->name); + /* Attempt an OF style match first */ if (of_driver_match_device(dev, drv)) return 1; diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index 16f6654082dd..153d303af7eb 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -28,6 +28,7 @@ struct platform_device { struct resource *resource; const struct platform_device_id *id_entry; + char *driver_override; /* Driver name to force a match */ /* MFD cell pointer */ struct mfd_cell *mfd_cell; -- cgit v1.2.3 From f941a6d9a9e0612eb807af822b0d1ac004da8175 Mon Sep 17 00:00:00 2001 From: Linus Lüssing Date: Mon, 7 Jul 2014 05:41:16 +0200 Subject: bridge: adding stubs for multicast exports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To make users (e.g. batman-adv soon) load- and runnable even if the bridge was compiled without snooping capabilities - or even if the kernel was compiled without any bridge code at all. Signed-off-by: Linus Lüssing Signed-off-by: David S. Miller --- include/linux/if_bridge.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'include') diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h index fd22789d7b2e..e0c575ca89f7 100644 --- a/include/linux/if_bridge.h +++ b/include/linux/if_bridge.h @@ -36,8 +36,22 @@ extern void brioctl_set(int (*ioctl_hook)(struct net *, unsigned int, void __use typedef int br_should_route_hook_t(struct sk_buff *skb); extern br_should_route_hook_t __rcu *br_should_route_hook; + +#if IS_ENABLED(CONFIG_BRIDGE) && IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING) int br_multicast_list_adjacent(struct net_device *dev, struct list_head *br_ip_list); bool br_multicast_has_querier_adjacent(struct net_device *dev, int proto); +#else +static inline int br_multicast_list_adjacent(struct net_device *dev, + struct list_head *br_ip_list) +{ + return 0; +} +static inline bool br_multicast_has_querier_adjacent(struct net_device *dev, + int proto) +{ + return false; +} +#endif #endif -- cgit v1.2.3 From c34963e21685659eb513e1c4d847f81d8a8f13f3 Mon Sep 17 00:00:00 2001 From: Linus Lüssing Date: Mon, 7 Jul 2014 05:41:17 +0200 Subject: bridge: export knowledge about the presence of IGMP/MLD queriers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this patch other modules are able to ask the bridge whether an IGMP or MLD querier exists on the according, bridged link layer. Multicast snooping can only be performed if a valid, selected querier exists on a link. Just like the bridge only enables its multicast snooping if a querier exists, e.g. batman-adv too can only activate its multicast snooping in bridged scenarios if a querier is present. For instance this export avoids having to reimplement IGMP/MLD querier message snooping and parsing in e.g. batman-adv, when multicast optimizations for bridged scenarios are added in the future. Signed-off-by: Linus Lüssing Signed-off-by: David S. Miller --- include/linux/if_bridge.h | 6 ++++++ net/bridge/br_multicast.c | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) (limited to 'include') diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h index e0c575ca89f7..808dcb8cc04f 100644 --- a/include/linux/if_bridge.h +++ b/include/linux/if_bridge.h @@ -40,6 +40,7 @@ extern br_should_route_hook_t __rcu *br_should_route_hook; #if IS_ENABLED(CONFIG_BRIDGE) && IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING) int br_multicast_list_adjacent(struct net_device *dev, struct list_head *br_ip_list); +bool br_multicast_has_querier_anywhere(struct net_device *dev, int proto); bool br_multicast_has_querier_adjacent(struct net_device *dev, int proto); #else static inline int br_multicast_list_adjacent(struct net_device *dev, @@ -47,6 +48,11 @@ static inline int br_multicast_list_adjacent(struct net_device *dev, { return 0; } +static inline bool br_multicast_has_querier_anywhere(struct net_device *dev, + int proto) +{ + return false; +} static inline bool br_multicast_has_querier_adjacent(struct net_device *dev, int proto) { diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index abfa0b65a111..b4845f4b2bb4 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -2215,6 +2215,43 @@ unlock: } EXPORT_SYMBOL_GPL(br_multicast_list_adjacent); +/** + * br_multicast_has_querier_anywhere - Checks for a querier on a bridge + * @dev: The bridge port providing the bridge on which to check for a querier + * @proto: The protocol family to check for: IGMP -> ETH_P_IP, MLD -> ETH_P_IPV6 + * + * Checks whether the given interface has a bridge on top and if so returns + * true if a valid querier exists anywhere on the bridged link layer. + * Otherwise returns false. + */ +bool br_multicast_has_querier_anywhere(struct net_device *dev, int proto) +{ + struct net_bridge *br; + struct net_bridge_port *port; + struct ethhdr eth; + bool ret = false; + + rcu_read_lock(); + if (!br_port_exists(dev)) + goto unlock; + + port = br_port_get_rcu(dev); + if (!port || !port->br) + goto unlock; + + br = port->br; + + memset(ð, 0, sizeof(eth)); + eth.h_proto = htons(proto); + + ret = br_multicast_querier_exists(br, ð); + +unlock: + rcu_read_unlock(); + return ret; +} +EXPORT_SYMBOL_GPL(br_multicast_has_querier_anywhere); + /** * br_multicast_has_querier_adjacent - Checks for a querier behind a bridge port * @dev: The bridge port adjacent to which to check for a querier -- cgit v1.2.3 From 8471bb73ba10ed6788b4f1e9b8a0f9dc6bdb05b5 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 21 May 2014 19:06:12 -0300 Subject: mtd: Introduce mtd_block_isreserved() In addition to mtd_block_isbad(), which checks if a block is bad or reserved, it's needed to check if a block is reserved only (but not bad). This commit adds an MTD interface for it, in a similar fashion to mtd_block_isbad(). While here, fix mtd_block_isbad() so the out-of-bounds checking is done before the callback check. Signed-off-by: Ezequiel Garcia Tested-by: Pekon Gupta Signed-off-by: Brian Norris --- drivers/mtd/mtdcore.c | 14 ++++++++++++-- drivers/mtd/mtdpart.c | 9 +++++++++ drivers/mtd/nand/nand_base.c | 18 ++++++++++++++++++ drivers/mtd/nand/nand_bbt.c | 14 ++++++++++++++ include/linux/mtd/mtd.h | 2 ++ include/linux/mtd/nand.h | 1 + 6 files changed, 56 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 11857faa0d96..e4831b4159db 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1043,12 +1043,22 @@ int mtd_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) } EXPORT_SYMBOL_GPL(mtd_is_locked); -int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs) +int mtd_block_isreserved(struct mtd_info *mtd, loff_t ofs) { - if (!mtd->_block_isbad) + if (ofs < 0 || ofs > mtd->size) + return -EINVAL; + if (!mtd->_block_isreserved) return 0; + return mtd->_block_isreserved(mtd, ofs); +} +EXPORT_SYMBOL_GPL(mtd_block_isreserved); + +int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs) +{ if (ofs < 0 || ofs > mtd->size) return -EINVAL; + if (!mtd->_block_isbad) + return 0; return mtd->_block_isbad(mtd, ofs); } EXPORT_SYMBOL_GPL(mtd_block_isbad); diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 1ca9aec141ff..921e8c647884 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -290,6 +290,13 @@ static void part_resume(struct mtd_info *mtd) part->master->_resume(part->master); } +static int part_block_isreserved(struct mtd_info *mtd, loff_t ofs) +{ + struct mtd_part *part = PART(mtd); + ofs += part->offset; + return part->master->_block_isreserved(part->master, ofs); +} + static int part_block_isbad(struct mtd_info *mtd, loff_t ofs) { struct mtd_part *part = PART(mtd); @@ -422,6 +429,8 @@ static struct mtd_part *allocate_partition(struct mtd_info *master, slave->mtd._unlock = part_unlock; if (master->_is_locked) slave->mtd._is_locked = part_is_locked; + if (master->_block_isreserved) + slave->mtd._block_isreserved = part_block_isreserved; if (master->_block_isbad) slave->mtd._block_isbad = part_block_isbad; if (master->_block_markbad) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 41167e9e991e..0c505dd1f522 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -484,6 +484,23 @@ static int nand_check_wp(struct mtd_info *mtd) return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1; } +/** + * nand_block_checkbad - [GENERIC] Check if a block is marked bad + * @mtd: MTD device structure + * @ofs: offset from device start + * + * Check if the block is mark as reserved. + */ +static int nand_block_isreserved(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *chip = mtd->priv; + + if (!chip->bbt) + return 0; + /* Return info from the table */ + return nand_isreserved_bbt(mtd, ofs); +} + /** * nand_block_checkbad - [GENERIC] Check if a block is marked bad * @mtd: MTD device structure @@ -4111,6 +4128,7 @@ int nand_scan_tail(struct mtd_info *mtd) mtd->_unlock = NULL; mtd->_suspend = nand_suspend; mtd->_resume = nand_resume; + mtd->_block_isreserved = nand_block_isreserved; mtd->_block_isbad = nand_block_isbad; mtd->_block_markbad = nand_block_markbad; mtd->writebufsize = mtd->writesize; diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 7f0c3b4c2a4f..443fa82cde6a 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -1310,6 +1310,20 @@ int nand_default_bbt(struct mtd_info *mtd) return nand_scan_bbt(mtd, this->badblock_pattern); } +/** + * nand_isreserved_bbt - [NAND Interface] Check if a block is reserved + * @mtd: MTD device structure + * @offs: offset in the device + */ +int nand_isreserved_bbt(struct mtd_info *mtd, loff_t offs) +{ + struct nand_chip *this = mtd->priv; + int block; + + block = (int)(offs >> this->bbt_erase_shift); + return bbt_get_entry(this, block) == BBT_BLOCK_RESERVED; +} + /** * nand_isbad_bbt - [NAND Interface] Check if a block is bad * @mtd: MTD device structure diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index a1b0b4c8fd79..031ff3a9a0bd 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -222,6 +222,7 @@ struct mtd_info { int (*_lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len); int (*_unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len); int (*_is_locked) (struct mtd_info *mtd, loff_t ofs, uint64_t len); + int (*_block_isreserved) (struct mtd_info *mtd, loff_t ofs); int (*_block_isbad) (struct mtd_info *mtd, loff_t ofs); int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs); int (*_suspend) (struct mtd_info *mtd); @@ -302,6 +303,7 @@ static inline void mtd_sync(struct mtd_info *mtd) int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); int mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); int mtd_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len); +int mtd_block_isreserved(struct mtd_info *mtd, loff_t ofs); int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs); int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs); diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 2f0af2891f0f..1cff329ae13d 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -810,6 +810,7 @@ extern struct nand_manufacturers nand_manuf_ids[]; extern int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd); extern int nand_default_bbt(struct mtd_info *mtd); extern int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs); +extern int nand_isreserved_bbt(struct mtd_info *mtd, loff_t offs); extern int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt); extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, int allowbbt); -- cgit v1.2.3 From 523ece889eeee84a381e16086b81e07a76cff8b6 Mon Sep 17 00:00:00 2001 From: Eugenia Emantayev Date: Tue, 8 Jul 2014 11:25:19 +0300 Subject: net/mlx4_en: Fix set port ratelimit for 40GE In 40GE we can't use the default bw units for set ratelimit (100 Mbps) since the max is 255*100 Mbps = 25 Gbps (not suited for 40GE), thus we need 1 Gbps units. But for 10GE 1 Gbps units might be too bruit so we use the following solution. For user set ratelimit <= 25 Gbps: use 100 Mbps units * user_ratelimit (* 10). For user set ratelimit > 25 Gbps: use 1 Gbps units * user_ratelimit. For user set unlimited ratelimit (0 Gbps): use 1 Gbps units * MAX_RATELIMIT_DEFAULT (57) Note: any value > 58 will damage the FW ratelimit computation, so we allow a max and any higher value will be pulled down to 57. Signed-off-by: Sagi Grimberg Signed-off-by: Eugenia Emantayev Signed-off-by: Amir Vadai Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/mlx4.h | 5 ----- drivers/net/ethernet/mellanox/mlx4/port.c | 22 +++++++++++++++++----- include/linux/mlx4/device.h | 11 +++++++++++ 3 files changed, 28 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index 1d8af7336807..13fbcd03c3e4 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -62,11 +62,6 @@ #define INIT_HCA_TPT_MW_ENABLE (1 << 7) -#define MLX4_NUM_UP 8 -#define MLX4_NUM_TC 8 -#define MLX4_RATELIMIT_UNITS 3 /* 100 Mbps */ -#define MLX4_RATELIMIT_DEFAULT 0xffff - struct mlx4_set_port_prio2tc_context { u8 prio2tc[4]; }; diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c index 7ab97174886d..5d76a60ac053 100644 --- a/drivers/net/ethernet/mellanox/mlx4/port.c +++ b/drivers/net/ethernet/mellanox/mlx4/port.c @@ -1051,14 +1051,26 @@ int mlx4_SET_PORT_SCHEDULER(struct mlx4_dev *dev, u8 port, u8 *tc_tx_bw, for (i = 0; i < MLX4_NUM_TC; i++) { struct mlx4_port_scheduler_tc_cfg_be *tc = &context->tc[i]; - u16 r = ratelimit && ratelimit[i] ? ratelimit[i] : - MLX4_RATELIMIT_DEFAULT; + u16 r; + + if (ratelimit && ratelimit[i]) { + if (ratelimit[i] <= MLX4_MAX_100M_UNITS_VAL) { + r = ratelimit[i]; + tc->max_bw_units = + htons(MLX4_RATELIMIT_100M_UNITS); + } else { + r = ratelimit[i]/10; + tc->max_bw_units = + htons(MLX4_RATELIMIT_1G_UNITS); + } + tc->max_bw_value = htons(r); + } else { + tc->max_bw_value = htons(MLX4_RATELIMIT_DEFAULT); + tc->max_bw_units = htons(MLX4_RATELIMIT_1G_UNITS); + } tc->pg = htons(pg[i]); tc->bw_precentage = htons(tc_tx_bw[i]); - - tc->max_bw_units = htons(MLX4_RATELIMIT_UNITS); - tc->max_bw_value = htons(r); } in_mod = MLX4_SET_PORT_SCHEDULER << 8 | port; diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index b12f4bbd064c..db0aef37645f 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -48,6 +48,17 @@ #define MSIX_LEGACY_SZ 4 #define MIN_MSIX_P_PORT 5 +#define MLX4_NUM_UP 8 +#define MLX4_NUM_TC 8 +#define MLX4_MAX_100M_UNITS_VAL 255 /* + * work around: can't set values + * greater then this value when + * using 100 Mbps units. + */ +#define MLX4_RATELIMIT_100M_UNITS 3 /* 100 Mbps */ +#define MLX4_RATELIMIT_1G_UNITS 4 /* 1 Gbps */ +#define MLX4_RATELIMIT_DEFAULT 0x00ff + #define MLX4_ROCE_MAX_GIDS 128 #define MLX4_ROCE_PF_GIDS 16 -- cgit v1.2.3 From a37934fc0d0c087dd120dba229077048f1abfd37 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 8 Jul 2014 11:15:03 -0700 Subject: net: provide stubs for ip6_set_txhash and ip6_make_flowlabel Commit cb1ce2ef387b ("ipv6: Implement automatic flow label generation on transmit") introduced ip6_make_flowlabel, while commit b73c3d0e4f0e ("net: Save TX flow hash in sock and set in skbuf on xmit") introduced ip6_set_txhash. ip6_set_tx_hash() uses sk_v6_daddr which references __sk_common.skc_v6_daddr from struct sock_common, which is gated with IS_ENABLED(CONFIG_IPV6). ip6_make_flowlabel() uses the ipv6 member from struct net which is also gated with IS_ENABLED(CONFIG_IPV6). When CONFIG_IPV6 is disabled, we will hit a build failure that looks like this when the compiler attempts inlining these functions: CC [M] drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.o In file included from include/net/inet_sock.h:27:0, from include/net/ip.h:30, from drivers/net/ethernet/broadcom/cnic.c:37: include/net/ipv6.h: In function 'ip6_set_txhash': include/net/sock.h:327:33: error: 'struct sock_common' has no member named 'skc_v6_daddr' #define sk_v6_daddr __sk_common.skc_v6_daddr ^ include/net/ipv6.h:696:49: note: in expansion of macro 'sk_v6_daddr' keys.dst = (__force __be32)ipv6_addr_hash(&sk->sk_v6_daddr); ^ In file included from include/net/inetpeer.h:15:0, from include/net/route.h:28, from include/net/ip.h:31, from drivers/net/ethernet/broadcom/cnic.c:37: include/net/ipv6.h: In function 'ip6_make_flowlabel': include/net/ipv6.h:706:37: error: 'struct net' has no member named 'ipv6' if (!flowlabel && (autolabel || net->ipv6.sysctl.auto_flowlabels)) { ^ Fixes: cb1ce2ef387b ("ipv6: Implement automatic flow label generation on transmit") Fixes: b73c3d0e4f0e ("net: Save TX flow hash in sock and set in skbuf on xmit") Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- include/net/ipv6.h | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'include') diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 4308f2ada8b3..b9ac2357e7db 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -685,6 +685,7 @@ static inline int ip6_sk_dst_hoplimit(struct ipv6_pinfo *np, struct flowi6 *fl6, return hlimit; } +#if IS_ENABLED(CONFIG_IPV6) static inline void ip6_set_txhash(struct sock *sk) { struct inet_sock *inet = inet_sk(sk); @@ -718,6 +719,15 @@ static inline __be32 ip6_make_flowlabel(struct net *net, struct sk_buff *skb, return flowlabel; } +#else +static inline void ip6_set_txhash(struct sock *sk) { } +static inline __be32 ip6_make_flowlabel(struct net *net, struct sk_buff *skb, + __be32 flowlabel, bool autolabel) +{ + return flowlabel; +} +#endif + /* * Header manipulation -- cgit v1.2.3 From cd7ca0ec5e046c570497b387332560eb42908cc4 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 9 Jul 2014 09:49:05 +0200 Subject: Bluetooth: Fix enabling Authenticated Payload Timeout Expired event The Authenticated Payload Timeout Expired event is valid for controllers with BR/EDR Secure Connections support, but also for LE only controllers supporting LE Ping feature. When either of them is available enable this event. Previous it was not enabled when the controller was only supporting LE operation. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 1 + net/bluetooth/hci_core.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 6ed1f7288f13..a01236e2df13 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -357,6 +357,7 @@ enum { /* LE features */ #define HCI_LE_CONN_PARAM_REQ_PROC 0x02 +#define HCI_LE_PING 0x10 /* Connection modes */ #define HCI_CM_ACTIVE 0x0000 diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 8ffaca0290f8..421faf5fa1f5 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1569,7 +1569,7 @@ static void hci_set_event_mask_page_2(struct hci_request *req) } /* Enable Authenticated Payload Timeout Expired event if supported */ - if (lmp_ping_capable(hdev)) + if (lmp_ping_capable(hdev) || hdev->le_features[0] & HCI_LE_PING) events[2] |= 0x80; hci_req_add(req, HCI_OP_SET_EVENT_MASK_PAGE_2, sizeof(events), events); -- cgit v1.2.3 From 6b32fafee2bb5fcf0b3d3d04a9762d3a0212089e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 20 Jun 2014 14:37:38 +0200 Subject: dmaengine: shdma: Add more register documentation Also add a few definitions that were missing. Signed-off-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- arch/sh/include/asm/dma-register.h | 36 +++++++++++++++++++----------------- drivers/dma/sh/shdmac.c | 12 ++++++------ include/linux/sh_dma.h | 24 +++++++++++++----------- 3 files changed, 38 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/arch/sh/include/asm/dma-register.h b/arch/sh/include/asm/dma-register.h index 51cd78feacff..c757b47e6b64 100644 --- a/arch/sh/include/asm/dma-register.h +++ b/arch/sh/include/asm/dma-register.h @@ -13,17 +13,17 @@ #ifndef DMA_REGISTER_H #define DMA_REGISTER_H -/* DMA register */ -#define SAR 0x00 -#define DAR 0x04 -#define TCR 0x08 -#define CHCR 0x0C -#define DMAOR 0x40 +/* DMA registers */ +#define SAR 0x00 /* Source Address Register */ +#define DAR 0x04 /* Destination Address Register */ +#define TCR 0x08 /* Transfer Count Register */ +#define CHCR 0x0C /* Channel Control Register */ +#define DMAOR 0x40 /* DMA Operation Register */ /* DMAOR definitions */ -#define DMAOR_AE 0x00000004 +#define DMAOR_AE 0x00000004 /* Address Error Flag */ #define DMAOR_NMIF 0x00000002 -#define DMAOR_DME 0x00000001 +#define DMAOR_DME 0x00000001 /* DMA Master Enable */ /* Definitions for the SuperH DMAC */ #define REQ_L 0x00000000 @@ -34,18 +34,20 @@ #define ACK_W 0x00020000 #define ACK_H 0x00000000 #define ACK_L 0x00010000 -#define DM_INC 0x00004000 -#define DM_DEC 0x00008000 -#define DM_FIX 0x0000c000 -#define SM_INC 0x00001000 -#define SM_DEC 0x00002000 -#define SM_FIX 0x00003000 +#define DM_INC 0x00004000 /* Destination addresses are incremented */ +#define DM_DEC 0x00008000 /* Destination addresses are decremented */ +#define DM_FIX 0x0000c000 /* Destination address is fixed */ +#define SM_INC 0x00001000 /* Source addresses are incremented */ +#define SM_DEC 0x00002000 /* Source addresses are decremented */ +#define SM_FIX 0x00003000 /* Source address is fixed */ #define RS_IN 0x00000200 #define RS_OUT 0x00000300 +#define RS_AUTO 0x00000400 /* Auto Request */ +#define RS_ERS 0x00000800 /* DMA extended resource selector */ #define TS_BLK 0x00000040 #define TM_BUR 0x00000020 -#define CHCR_DE 0x00000001 -#define CHCR_TE 0x00000002 -#define CHCR_IE 0x00000004 +#define CHCR_DE 0x00000001 /* DMA Enable */ +#define CHCR_TE 0x00000002 /* Transfer End Flag */ +#define CHCR_IE 0x00000004 /* Interrupt Enable */ #endif diff --git a/drivers/dma/sh/shdmac.c b/drivers/dma/sh/shdmac.c index 146d5df926db..1a6f6595c6c1 100644 --- a/drivers/dma/sh/shdmac.c +++ b/drivers/dma/sh/shdmac.c @@ -38,12 +38,12 @@ #include "../dmaengine.h" #include "shdma.h" -/* DMA register */ -#define SAR 0x00 -#define DAR 0x04 -#define TCR 0x08 -#define CHCR 0x0C -#define DMAOR 0x40 +/* DMA registers */ +#define SAR 0x00 /* Source Address Register */ +#define DAR 0x04 /* Destination Address Register */ +#define TCR 0x08 /* Transfer Count Register */ +#define CHCR 0x0C /* Channel Control Register */ +#define DMAOR 0x40 /* DMA Operation Register */ #define TEND 0x18 /* USB-DMAC */ diff --git a/include/linux/sh_dma.h b/include/linux/sh_dma.h index b7b43b82231e..56b97eed28a4 100644 --- a/include/linux/sh_dma.h +++ b/include/linux/sh_dma.h @@ -95,19 +95,21 @@ struct sh_dmae_pdata { }; /* DMAOR definitions */ -#define DMAOR_AE 0x00000004 +#define DMAOR_AE 0x00000004 /* Address Error Flag */ #define DMAOR_NMIF 0x00000002 -#define DMAOR_DME 0x00000001 +#define DMAOR_DME 0x00000001 /* DMA Master Enable */ /* Definitions for the SuperH DMAC */ -#define DM_INC 0x00004000 -#define DM_DEC 0x00008000 -#define DM_FIX 0x0000c000 -#define SM_INC 0x00001000 -#define SM_DEC 0x00002000 -#define SM_FIX 0x00003000 -#define CHCR_DE 0x00000001 -#define CHCR_TE 0x00000002 -#define CHCR_IE 0x00000004 +#define DM_INC 0x00004000 /* Destination addresses are incremented */ +#define DM_DEC 0x00008000 /* Destination addresses are decremented */ +#define DM_FIX 0x0000c000 /* Destination address is fixed */ +#define SM_INC 0x00001000 /* Source addresses are incremented */ +#define SM_DEC 0x00002000 /* Source addresses are decremented */ +#define SM_FIX 0x00003000 /* Source address is fixed */ +#define RS_AUTO 0x00000400 /* Auto Request */ +#define RS_ERS 0x00000800 /* DMA extended resource selector */ +#define CHCR_DE 0x00000001 /* DMA Enable */ +#define CHCR_TE 0x00000002 /* Transfer End Flag */ +#define CHCR_IE 0x00000004 /* Interrupt Enable */ #endif -- cgit v1.2.3 From 3a48edc4bd68f841c07c7bc86358d2f02133f247 Mon Sep 17 00:00:00 2001 From: Tim Kryger Date: Fri, 13 Jun 2014 10:13:56 -0700 Subject: mmc: sdhci: Use mmc core regulator infrastucture Switch the common SDHCI code over to use mmc_host's regulator pointers and remove the ones in the sdhci_host structure. Additionally, use the common mmc_regulator_get_supply function to get the regulators and set the ocr_avail mask. This change sets the ocr_avail directly based upon the voltage ranges supported which ensures ocr_avail is set correctly while allowing the use of regulators that can't provide exactly 1.8v, 3.0v, or 3.3v. Signed-off-by: Tim Kryger Signed-off-by: Markus Mayer Reviewed-by: Matt Porter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 97 ++++++++++++++++++----------------------------- include/linux/mmc/sdhci.h | 3 -- 2 files changed, 36 insertions(+), 64 deletions(-) (limited to 'include') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 47055f3f01b8..ee524b06db14 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1223,6 +1223,7 @@ EXPORT_SYMBOL_GPL(sdhci_set_clock); static void sdhci_set_power(struct sdhci_host *host, unsigned char mode, unsigned short vdd) { + struct mmc_host *mmc = host->mmc; u8 pwr = 0; if (mode != MMC_POWER_OFF) { @@ -1284,9 +1285,9 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned char mode, mdelay(10); } - if (host->vmmc) { + if (!IS_ERR(mmc->supply.vmmc)) { spin_unlock_irq(&host->lock); - mmc_regulator_set_ocr(host->mmc, host->vmmc, vdd); + mmc_regulator_set_ocr(host->mmc, mmc->supply.vmmc, vdd); spin_lock_irq(&host->lock); } } @@ -1440,13 +1441,15 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) { unsigned long flags; u8 ctrl; + struct mmc_host *mmc = host->mmc; spin_lock_irqsave(&host->lock, flags); if (host->flags & SDHCI_DEVICE_DEAD) { spin_unlock_irqrestore(&host->lock, flags); - if (host->vmmc && ios->power_mode == MMC_POWER_OFF) - mmc_regulator_set_ocr(host->mmc, host->vmmc, 0); + if (!IS_ERR(mmc->supply.vmmc) && + ios->power_mode == MMC_POWER_OFF) + mmc_regulator_set_ocr(host->mmc, mmc->supply.vmmc, 0); return; } @@ -1707,6 +1710,7 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, struct mmc_ios *ios) { + struct mmc_host *mmc = host->mmc; u16 ctrl; int ret; @@ -1725,8 +1729,9 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, ctrl &= ~SDHCI_CTRL_VDD_180; sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); - if (host->vqmmc) { - ret = regulator_set_voltage(host->vqmmc, 2700000, 3600000); + if (!IS_ERR(mmc->supply.vqmmc)) { + ret = regulator_set_voltage(mmc->supply.vqmmc, 2700000, + 3600000); if (ret) { pr_warning("%s: Switching to 3.3V signalling voltage " " failed\n", mmc_hostname(host->mmc)); @@ -1746,8 +1751,8 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, return -EAGAIN; case MMC_SIGNAL_VOLTAGE_180: - if (host->vqmmc) { - ret = regulator_set_voltage(host->vqmmc, + if (!IS_ERR(mmc->supply.vqmmc)) { + ret = regulator_set_voltage(mmc->supply.vqmmc, 1700000, 1950000); if (ret) { pr_warning("%s: Switching to 1.8V signalling voltage " @@ -1776,8 +1781,9 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, return -EAGAIN; case MMC_SIGNAL_VOLTAGE_120: - if (host->vqmmc) { - ret = regulator_set_voltage(host->vqmmc, 1100000, 1300000); + if (!IS_ERR(mmc->supply.vqmmc)) { + ret = regulator_set_voltage(mmc->supply.vqmmc, 1100000, + 1300000); if (ret) { pr_warning("%s: Switching to 1.2V signalling voltage " " failed\n", mmc_hostname(host->mmc)); @@ -2962,25 +2968,22 @@ int sdhci_add_host(struct sdhci_host *host) !(host->mmc->caps & MMC_CAP_NONREMOVABLE)) mmc->caps |= MMC_CAP_NEEDS_POLL; + /* If there are external regulators, get them */ + if (mmc_regulator_get_supply(mmc) == -EPROBE_DEFER) + return -EPROBE_DEFER; + /* If vqmmc regulator and no 1.8V signalling, then there's no UHS */ - host->vqmmc = regulator_get_optional(mmc_dev(mmc), "vqmmc"); - if (IS_ERR_OR_NULL(host->vqmmc)) { - if (PTR_ERR(host->vqmmc) < 0) { - pr_info("%s: no vqmmc regulator found\n", - mmc_hostname(mmc)); - host->vqmmc = NULL; - } - } else { - ret = regulator_enable(host->vqmmc); - if (!regulator_is_supported_voltage(host->vqmmc, 1700000, - 1950000)) + if (!IS_ERR(mmc->supply.vqmmc)) { + ret = regulator_enable(mmc->supply.vqmmc); + if (!regulator_is_supported_voltage(mmc->supply.vqmmc, 1700000, + 1950000)) caps[1] &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50); if (ret) { pr_warn("%s: Failed to enable vqmmc regulator: %d\n", mmc_hostname(mmc), ret); - host->vqmmc = NULL; + mmc->supply.vqmmc = NULL; } } @@ -3041,34 +3044,6 @@ int sdhci_add_host(struct sdhci_host *host) ocr_avail = 0; - host->vmmc = regulator_get_optional(mmc_dev(mmc), "vmmc"); - if (IS_ERR_OR_NULL(host->vmmc)) { - if (PTR_ERR(host->vmmc) < 0) { - pr_info("%s: no vmmc regulator found\n", - mmc_hostname(mmc)); - host->vmmc = NULL; - } - } - -#ifdef CONFIG_REGULATOR - /* - * Voltage range check makes sense only if regulator reports - * any voltage value. - */ - if (host->vmmc && regulator_get_voltage(host->vmmc) > 0) { - ret = regulator_is_supported_voltage(host->vmmc, 2700000, - 3600000); - if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_330))) - caps[0] &= ~SDHCI_CAN_VDD_330; - if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_300))) - caps[0] &= ~SDHCI_CAN_VDD_300; - ret = regulator_is_supported_voltage(host->vmmc, 1700000, - 1950000); - if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_180))) - caps[0] &= ~SDHCI_CAN_VDD_180; - } -#endif /* CONFIG_REGULATOR */ - /* * According to SD Host Controller spec v3.00, if the Host System * can afford more than 150mA, Host Driver should set XPC to 1. Also @@ -3077,8 +3052,8 @@ int sdhci_add_host(struct sdhci_host *host) * value. */ max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT); - if (!max_current_caps && host->vmmc) { - u32 curr = regulator_get_current_limit(host->vmmc); + if (!max_current_caps && !IS_ERR(mmc->supply.vmmc)) { + u32 curr = regulator_get_current_limit(mmc->supply.vmmc); if (curr > 0) { /* convert to SDHCI_MAX_CURRENT format */ @@ -3118,8 +3093,11 @@ int sdhci_add_host(struct sdhci_host *host) SDHCI_MAX_CURRENT_MULTIPLIER; } + if (mmc->ocr_avail) + ocr_avail &= mmc->ocr_avail; + if (host->ocr_mask) - ocr_avail = host->ocr_mask; + ocr_avail &= host->ocr_mask; mmc->ocr_avail = ocr_avail; mmc->ocr_avail_sdio = ocr_avail; @@ -3273,6 +3251,7 @@ EXPORT_SYMBOL_GPL(sdhci_add_host); void sdhci_remove_host(struct sdhci_host *host, int dead) { + struct mmc_host *mmc = host->mmc; unsigned long flags; if (dead) { @@ -3310,15 +3289,11 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) tasklet_kill(&host->finish_tasklet); - if (host->vmmc) { - regulator_disable(host->vmmc); - regulator_put(host->vmmc); - } + if (!IS_ERR(mmc->supply.vmmc)) + regulator_disable(mmc->supply.vmmc); - if (host->vqmmc) { - regulator_disable(host->vqmmc); - regulator_put(host->vqmmc); - } + if (!IS_ERR(mmc->supply.vqmmc)) + regulator_disable(mmc->supply.vqmmc); if (host->adma_desc) dma_free_coherent(mmc_dev(host->mmc), ADMA_SIZE, diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index 08abe9941884..09ebe57d5ce9 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -104,9 +104,6 @@ struct sdhci_host { const struct sdhci_ops *ops; /* Low level hw interface */ - struct regulator *vmmc; /* Power regulator (vmmc) */ - struct regulator *vqmmc; /* Signaling regulator (vccq) */ - /* Internal data */ struct mmc_host *mmc; /* MMC structure */ u64 dma_mask; /* custom DMA mask */ -- cgit v1.2.3 From 2cd3a2a54656f9c480b1c7272fc07635d575841b Mon Sep 17 00:00:00 2001 From: Andreas Fenkart Date: Thu, 29 May 2014 10:28:00 +0200 Subject: mmc: omap_hsmmc: Enable SDIO interrupt There have been various patches floating around for enabling the SDIO IRQ for hsmmc, but none of them ever got merged. Probably the reason for not merging the SDIO interrupt patches has been the lack of wake-up path for SDIO on some omaps that has also needed remuxing the SDIO DAT1 line to a GPIO making the patches complex. This patch adds the minimal SDIO IRQ support to hsmmc for omaps that do have the wake-up path. For those omaps, the DAT1 line need to have the wake-up enable bit set, and the wake-up interrupt is the same as for the MMC controller. This patch has been tested on am3730 es1.2 with mwifiex connected to MMC3 with mwifiex waking to Ethernet traffic from off-idle mode. Note that for omaps that do not have the SDIO wake-up path, this patch will not work for idle modes and further patches for remuxing DAT1 to GPIO are needed. Based on earlier patches [1][2] by David Vrabel , Steve Sakoman For now, only support SDIO interrupt if we are booted with a separate wake-irq configued via device tree. This is because omaps need the wake-irq for idle states, and some omaps need special quirks. And we don't want to add new legacy mux platform init code callbacks any longer as we are moving to DT based booting anyways. To use it, you need to specify the wake-irq using the interrupts-extended property. [1] http://www.sakoman.com/cgi-bin/gitweb.cgi?p=linux.git;a=commitdiff_plain;h=010810d22f6f49ac03da4ba384969432e0320453 [2] http://comments.gmane.org/gmane.linux.kernel.mmc/20446 Acked-by: Balaji T K Signed-off-by: Andreas Fenkart Signed-off-by: Tony Lindgren Signed-off-by: Ulf Hansson --- .../devicetree/bindings/mmc/ti-omap-hsmmc.txt | 1 + drivers/mmc/host/omap_hsmmc.c | 201 +++++++++++++++++++-- include/linux/platform_data/mmc-omap.h | 1 + 3 files changed, 191 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt index ce8056116fb0..0233ba7951e5 100644 --- a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt +++ b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt @@ -12,6 +12,7 @@ Required properties: Should be "ti,omap3-hsmmc", for OMAP3 controllers Should be "ti,omap3-pre-es3-hsmmc" for OMAP3 controllers pre ES3.0 Should be "ti,omap4-hsmmc", for OMAP4 controllers + Should be "ti,am33xx-hsmmc", for AM335x controllers - ti,hwmods: Must be "mmc", n is controller instance starting 1 Optional properties: diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 6b7b75585926..9446010a5dd9 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -106,6 +108,7 @@ #define TC_EN (1 << 1) #define BWR_EN (1 << 4) #define BRR_EN (1 << 5) +#define CIRQ_EN (1 << 8) #define ERR_EN (1 << 15) #define CTO_EN (1 << 16) #define CCRC_EN (1 << 17) @@ -140,7 +143,6 @@ #define VDD_3V0 3000000 /* 300000 uV */ #define VDD_165_195 (ffs(MMC_VDD_165_195) - 1) -#define AUTO_CMD23 (1 << 1) /* Auto CMD23 support */ /* * One controller can have multiple slots, like on some omap boards using * omap.c controller driver. Luckily this is not currently done on any known @@ -194,6 +196,7 @@ struct omap_hsmmc_host { u32 sysctl; u32 capa; int irq; + int wake_irq; int use_dma, dma_ch; struct dma_chan *tx_chan; struct dma_chan *rx_chan; @@ -206,6 +209,9 @@ struct omap_hsmmc_host { int req_in_progress; unsigned long clk_rate; unsigned int flags; +#define AUTO_CMD23 (1 << 0) /* Auto CMD23 support */ +#define HSMMC_SDIO_IRQ_ENABLED (1 << 1) /* SDIO irq enabled */ +#define HSMMC_WAKE_IRQ_ENABLED (1 << 2) struct omap_hsmmc_next next_data; struct omap_mmc_platform_data *pdata; }; @@ -510,27 +516,40 @@ static void omap_hsmmc_stop_clock(struct omap_hsmmc_host *host) static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host, struct mmc_command *cmd) { - unsigned int irq_mask; + u32 irq_mask = INT_EN_MASK; + unsigned long flags; if (host->use_dma) - irq_mask = INT_EN_MASK & ~(BRR_EN | BWR_EN); - else - irq_mask = INT_EN_MASK; + irq_mask &= ~(BRR_EN | BWR_EN); /* Disable timeout for erases */ if (cmd->opcode == MMC_ERASE) irq_mask &= ~DTO_EN; + spin_lock_irqsave(&host->irq_lock, flags); OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); OMAP_HSMMC_WRITE(host->base, ISE, irq_mask); + + /* latch pending CIRQ, but don't signal MMC core */ + if (host->flags & HSMMC_SDIO_IRQ_ENABLED) + irq_mask |= CIRQ_EN; OMAP_HSMMC_WRITE(host->base, IE, irq_mask); + spin_unlock_irqrestore(&host->irq_lock, flags); } static void omap_hsmmc_disable_irq(struct omap_hsmmc_host *host) { - OMAP_HSMMC_WRITE(host->base, ISE, 0); - OMAP_HSMMC_WRITE(host->base, IE, 0); + u32 irq_mask = 0; + unsigned long flags; + + spin_lock_irqsave(&host->irq_lock, flags); + /* no transfer running but need to keep cirq if enabled */ + if (host->flags & HSMMC_SDIO_IRQ_ENABLED) + irq_mask |= CIRQ_EN; + OMAP_HSMMC_WRITE(host->base, ISE, irq_mask); + OMAP_HSMMC_WRITE(host->base, IE, irq_mask); OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); + spin_unlock_irqrestore(&host->irq_lock, flags); } /* Calculate divisor for the given clock frequency */ @@ -681,7 +700,9 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host) && time_before(jiffies, timeout)) ; - omap_hsmmc_disable_irq(host); + OMAP_HSMMC_WRITE(host->base, ISE, 0); + OMAP_HSMMC_WRITE(host->base, IE, 0); + OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); /* Do not initialize card-specific things if the power is off */ if (host->power_mode == MMC_POWER_OFF) @@ -1118,8 +1139,12 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id) int status; status = OMAP_HSMMC_READ(host->base, STAT); - while (status & INT_EN_MASK && host->req_in_progress) { - omap_hsmmc_do_irq(host, status); + while (status & (INT_EN_MASK | CIRQ_EN)) { + if (host->req_in_progress) + omap_hsmmc_do_irq(host, status); + + if (status & CIRQ_EN) + mmc_signal_sdio_irq(host->mmc); /* Flush posted write */ status = OMAP_HSMMC_READ(host->base, STAT); @@ -1128,6 +1153,22 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id) return IRQ_HANDLED; } +static irqreturn_t omap_hsmmc_wake_irq(int irq, void *dev_id) +{ + struct omap_hsmmc_host *host = dev_id; + + /* cirq is level triggered, disable to avoid infinite loop */ + spin_lock(&host->irq_lock); + if (host->flags & HSMMC_WAKE_IRQ_ENABLED) { + disable_irq_nosync(host->wake_irq); + host->flags &= ~HSMMC_WAKE_IRQ_ENABLED; + } + spin_unlock(&host->irq_lock); + pm_request_resume(host->dev); /* no use counter */ + + return IRQ_HANDLED; +} + static void set_sd_bus_power(struct omap_hsmmc_host *host) { unsigned long i; @@ -1639,6 +1680,79 @@ static void omap_hsmmc_init_card(struct mmc_host *mmc, struct mmc_card *card) mmc_slot(host).init_card(card); } +static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int enable) +{ + struct omap_hsmmc_host *host = mmc_priv(mmc); + u32 irq_mask; + unsigned long flags; + + spin_lock_irqsave(&host->irq_lock, flags); + + irq_mask = OMAP_HSMMC_READ(host->base, ISE); + if (enable) { + host->flags |= HSMMC_SDIO_IRQ_ENABLED; + irq_mask |= CIRQ_EN; + } else { + host->flags &= ~HSMMC_SDIO_IRQ_ENABLED; + irq_mask &= ~CIRQ_EN; + } + OMAP_HSMMC_WRITE(host->base, IE, irq_mask); + + /* + * if enable, piggy back detection on current request + * but always disable immediately + */ + if (!host->req_in_progress || !enable) + OMAP_HSMMC_WRITE(host->base, ISE, irq_mask); + + /* flush posted write */ + OMAP_HSMMC_READ(host->base, IE); + + spin_unlock_irqrestore(&host->irq_lock, flags); +} + +static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host) +{ + struct mmc_host *mmc = host->mmc; + int ret; + + /* + * For omaps with wake-up path, wakeirq will be irq from pinctrl and + * for other omaps, wakeirq will be from GPIO (dat line remuxed to + * gpio). wakeirq is needed to detect sdio irq in runtime suspend state + * with functional clock disabled. + */ + if (!host->dev->of_node || !host->wake_irq) + return -ENODEV; + + /* Prevent auto-enabling of IRQ */ + irq_set_status_flags(host->wake_irq, IRQ_NOAUTOEN); + ret = devm_request_irq(host->dev, host->wake_irq, omap_hsmmc_wake_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + mmc_hostname(mmc), host); + if (ret) { + dev_err(mmc_dev(host->mmc), "Unable to request wake IRQ\n"); + goto err; + } + + /* + * Some omaps don't have wake-up path from deeper idle states + * and need to remux SDIO DAT1 to GPIO for wake-up from idle. + */ + if (host->pdata->controller_flags & OMAP_HSMMC_SWAKEUP_MISSING) { + ret = -ENODEV; + devm_free_irq(host->dev, host->wake_irq, host); + goto err; + } + + return 0; + +err: + dev_warn(host->dev, "no SDIO IRQ support, falling back to polling\n"); + host->wake_irq = 0; + return ret; +} + static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host) { u32 hctl, capa, value; @@ -1691,7 +1805,7 @@ static const struct mmc_host_ops omap_hsmmc_ops = { .get_cd = omap_hsmmc_get_cd, .get_ro = omap_hsmmc_get_ro, .init_card = omap_hsmmc_init_card, - /* NYET -- enable_sdio_irq */ + .enable_sdio_irq = omap_hsmmc_enable_sdio_irq, }; #ifdef CONFIG_DEBUG_FS @@ -1761,6 +1875,10 @@ static const struct omap_mmc_of_data omap3_pre_es3_mmc_of_data = { static const struct omap_mmc_of_data omap4_mmc_of_data = { .reg_offset = 0x100, }; +static const struct omap_mmc_of_data am33xx_mmc_of_data = { + .reg_offset = 0x100, + .controller_flags = OMAP_HSMMC_SWAKEUP_MISSING, +}; static const struct of_device_id omap_mmc_of_match[] = { { @@ -1777,6 +1895,10 @@ static const struct of_device_id omap_mmc_of_match[] = { .compatible = "ti,omap4-hsmmc", .data = &omap4_mmc_of_data, }, + { + .compatible = "ti,am33xx-hsmmc", + .data = &am33xx_mmc_of_data, + }, {}, }; MODULE_DEVICE_TABLE(of, omap_mmc_of_match); @@ -1913,6 +2035,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); + if (pdev->dev.of_node) + host->wake_irq = irq_of_parse_and_map(pdev->dev.of_node, 1); + mmc->ops = &omap_hsmmc_ops; mmc->f_min = OMAP_MMC_MIN_CLOCK; @@ -2066,6 +2191,18 @@ static int omap_hsmmc_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "pins are not configured from the driver\n"); + /* + * For now, only support SDIO interrupt if we have a separate + * wake-up interrupt configured from device tree. This is because + * the wake-up interrupt is needed for idle state and some + * platforms need special quirks. And we don't want to add new + * legacy mux platform init code callbacks any longer as we + * are moving to DT based booting anyways. + */ + ret = omap_hsmmc_configure_wake_irq(host); + if (!ret) + mmc->caps |= MMC_CAP_SDIO_IRQ; + omap_hsmmc_protect_card(host); mmc_add_host(mmc); @@ -2170,11 +2307,18 @@ static int omap_hsmmc_suspend(struct device *dev) pm_runtime_get_sync(host->dev); if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) { - omap_hsmmc_disable_irq(host); + OMAP_HSMMC_WRITE(host->base, ISE, 0); + OMAP_HSMMC_WRITE(host->base, IE, 0); + OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP); } + /* do not wake up due to sdio irq */ + if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) && + !(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ)) + disable_irq(host->wake_irq); + if (host->dbclk) clk_disable_unprepare(host->dbclk); @@ -2200,6 +2344,10 @@ static int omap_hsmmc_resume(struct device *dev) omap_hsmmc_protect_card(host); + if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) && + !(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ)) + enable_irq(host->wake_irq); + pm_runtime_mark_last_busy(host->dev); pm_runtime_put_autosuspend(host->dev); return 0; @@ -2215,22 +2363,51 @@ static int omap_hsmmc_resume(struct device *dev) static int omap_hsmmc_runtime_suspend(struct device *dev) { struct omap_hsmmc_host *host; + unsigned long flags; host = platform_get_drvdata(to_platform_device(dev)); omap_hsmmc_context_save(host); dev_dbg(dev, "disabled\n"); + spin_lock_irqsave(&host->irq_lock, flags); + if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) && + (host->flags & HSMMC_SDIO_IRQ_ENABLED)) { + /* disable sdio irq handling to prevent race */ + OMAP_HSMMC_WRITE(host->base, ISE, 0); + OMAP_HSMMC_WRITE(host->base, IE, 0); + OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); + + WARN_ON(host->flags & HSMMC_WAKE_IRQ_ENABLED); + enable_irq(host->wake_irq); + host->flags |= HSMMC_WAKE_IRQ_ENABLED; + } + spin_unlock_irqrestore(&host->irq_lock, flags); return 0; } static int omap_hsmmc_runtime_resume(struct device *dev) { struct omap_hsmmc_host *host; + unsigned long flags; host = platform_get_drvdata(to_platform_device(dev)); omap_hsmmc_context_restore(host); dev_dbg(dev, "enabled\n"); + spin_lock_irqsave(&host->irq_lock, flags); + if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) && + (host->flags & HSMMC_SDIO_IRQ_ENABLED)) { + /* sdio irq flag can't change while in runtime suspend */ + if (host->flags & HSMMC_WAKE_IRQ_ENABLED) { + disable_irq_nosync(host->wake_irq); + host->flags &= ~HSMMC_WAKE_IRQ_ENABLED; + } + + OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); + OMAP_HSMMC_WRITE(host->base, ISE, CIRQ_EN); + OMAP_HSMMC_WRITE(host->base, IE, CIRQ_EN); + } + spin_unlock_irqrestore(&host->irq_lock, flags); return 0; } diff --git a/include/linux/platform_data/mmc-omap.h b/include/linux/platform_data/mmc-omap.h index 2bf1b30cb5dc..51e70cf25cbc 100644 --- a/include/linux/platform_data/mmc-omap.h +++ b/include/linux/platform_data/mmc-omap.h @@ -28,6 +28,7 @@ */ #define OMAP_HSMMC_SUPPORTS_DUAL_VOLT BIT(0) #define OMAP_HSMMC_BROKEN_MULTIBLOCK_READ BIT(1) +#define OMAP_HSMMC_SWAKEUP_MISSING BIT(2) struct mmc_card; -- cgit v1.2.3 From dcc36c16c2f1c9800146c8416ee5a4c3dc974623 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jul 2014 12:59:13 +0300 Subject: Bluetooth: Unify helpers for bdaddr_list manipulations We already have several lists with struct bdaddr_list entries, and there will be more in the future. Since the operations for adding, removing, looking up and clearing entries in these lists are exactly the same it doesn't make sense to define new functions for every single list. This patch unifies the functions by passing the list_head to them instead of a hci_dev pointer. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 15 +++---- net/bluetooth/hci_core.c | 85 ++++++---------------------------------- net/bluetooth/hci_event.c | 15 ++++--- net/bluetooth/hci_sock.c | 4 +- net/bluetooth/l2cap_core.c | 7 ++-- net/bluetooth/mgmt.c | 6 ++- 6 files changed, 36 insertions(+), 96 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index c98de309967e..3a1caf10cc8d 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -849,16 +849,11 @@ 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, u8 type); -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); - -struct bdaddr_list *hci_white_list_lookup(struct hci_dev *hdev, - bdaddr_t *bdaddr, u8 type); -void hci_white_list_clear(struct hci_dev *hdev); -int hci_white_list_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); -int hci_white_list_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); +struct bdaddr_list *hci_bdaddr_list_lookup(struct list_head *list, + bdaddr_t *bdaddr, u8 type); +int hci_bdaddr_list_add(struct list_head *list, bdaddr_t *bdaddr, u8 type); +int hci_bdaddr_list_del(struct list_head *list, bdaddr_t *bdaddr, u8 type); +void hci_bdaddr_list_clear(struct list_head *list); struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 421faf5fa1f5..705f8df7af96 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3351,12 +3351,12 @@ int hci_add_remote_oob_ext_data(struct hci_dev *hdev, bdaddr_t *bdaddr, return 0; } -struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, +struct bdaddr_list *hci_bdaddr_list_lookup(struct list_head *bdaddr_list, bdaddr_t *bdaddr, u8 type) { struct bdaddr_list *b; - list_for_each_entry(b, &hdev->blacklist, list) { + list_for_each_entry(b, bdaddr_list, list) { if (!bacmp(&b->bdaddr, bdaddr) && b->bdaddr_type == type) return b; } @@ -3364,11 +3364,11 @@ struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, return NULL; } -static void hci_blacklist_clear(struct hci_dev *hdev) +void hci_bdaddr_list_clear(struct list_head *bdaddr_list) { struct list_head *p, *n; - list_for_each_safe(p, n, &hdev->blacklist) { + list_for_each_safe(p, n, bdaddr_list) { struct bdaddr_list *b = list_entry(p, struct bdaddr_list, list); list_del(p); @@ -3376,14 +3376,14 @@ static void hci_blacklist_clear(struct hci_dev *hdev) } } -int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) +int hci_bdaddr_list_add(struct list_head *list, bdaddr_t *bdaddr, u8 type) { struct bdaddr_list *entry; if (!bacmp(bdaddr, BDADDR_ANY)) return -EBADF; - if (hci_blacklist_lookup(hdev, bdaddr, type)) + if (hci_bdaddr_list_lookup(list, bdaddr, type)) return -EEXIST; entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL); @@ -3393,82 +3393,21 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) bacpy(&entry->bdaddr, bdaddr); entry->bdaddr_type = type; - list_add(&entry->list, &hdev->blacklist); + list_add(&entry->list, list); return 0; } -int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) +int hci_bdaddr_list_del(struct list_head *list, bdaddr_t *bdaddr, u8 type) { struct bdaddr_list *entry; if (!bacmp(bdaddr, BDADDR_ANY)) { - hci_blacklist_clear(hdev); + hci_bdaddr_list_clear(list); return 0; } - entry = hci_blacklist_lookup(hdev, bdaddr, type); - if (!entry) - return -ENOENT; - - list_del(&entry->list); - kfree(entry); - - return 0; -} - -struct bdaddr_list *hci_white_list_lookup(struct hci_dev *hdev, - bdaddr_t *bdaddr, u8 type) -{ - struct bdaddr_list *b; - - list_for_each_entry(b, &hdev->le_white_list, list) { - if (!bacmp(&b->bdaddr, bdaddr) && b->bdaddr_type == type) - return b; - } - - return NULL; -} - -void hci_white_list_clear(struct hci_dev *hdev) -{ - struct list_head *p, *n; - - list_for_each_safe(p, n, &hdev->le_white_list) { - struct bdaddr_list *b = list_entry(p, struct bdaddr_list, list); - - list_del(p); - kfree(b); - } -} - -int hci_white_list_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) -{ - struct bdaddr_list *entry; - - if (!bacmp(bdaddr, BDADDR_ANY)) - return -EBADF; - - entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL); - if (!entry) - return -ENOMEM; - - bacpy(&entry->bdaddr, bdaddr); - entry->bdaddr_type = type; - - list_add(&entry->list, &hdev->le_white_list); - - return 0; -} - -int hci_white_list_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) -{ - struct bdaddr_list *entry; - - if (!bacmp(bdaddr, BDADDR_ANY)) - return -EBADF; - - entry = hci_white_list_lookup(hdev, bdaddr, type); + entry = hci_bdaddr_list_lookup(list, bdaddr, type); if (!entry) return -ENOENT; @@ -4096,13 +4035,13 @@ void hci_unregister_dev(struct hci_dev *hdev) destroy_workqueue(hdev->req_workqueue); hci_dev_lock(hdev); - hci_blacklist_clear(hdev); + hci_bdaddr_list_clear(&hdev->blacklist); hci_uuids_clear(hdev); hci_link_keys_clear(hdev); hci_smp_ltks_clear(hdev); hci_smp_irks_clear(hdev); hci_remote_oob_data_clear(hdev); - hci_white_list_clear(hdev); + hci_bdaddr_list_clear(&hdev->le_white_list); hci_conn_params_clear_all(hdev); hci_dev_unlock(hdev); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index f6997901bdd7..381c631423f1 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1222,7 +1222,7 @@ static void hci_cc_le_clear_white_list(struct hci_dev *hdev, if (status) return; - hci_white_list_clear(hdev); + hci_bdaddr_list_clear(&hdev->le_white_list); } static void hci_cc_le_add_to_white_list(struct hci_dev *hdev, @@ -1240,7 +1240,8 @@ static void hci_cc_le_add_to_white_list(struct hci_dev *hdev, if (!sent) return; - hci_white_list_add(hdev, &sent->bdaddr, sent->bdaddr_type); + hci_bdaddr_list_add(&hdev->le_white_list, &sent->bdaddr, + sent->bdaddr_type); } static void hci_cc_le_del_from_white_list(struct hci_dev *hdev, @@ -1258,7 +1259,8 @@ static void hci_cc_le_del_from_white_list(struct hci_dev *hdev, if (!sent) return; - hci_white_list_del(hdev, &sent->bdaddr, sent->bdaddr_type); + hci_bdaddr_list_del(&hdev->le_white_list, &sent->bdaddr, + sent->bdaddr_type); } static void hci_cc_le_read_supported_states(struct hci_dev *hdev, @@ -2132,7 +2134,8 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) &flags); if ((mask & HCI_LM_ACCEPT) && - !hci_blacklist_lookup(hdev, &ev->bdaddr, BDADDR_BREDR)) { + !hci_bdaddr_list_lookup(&hdev->blacklist, &ev->bdaddr, + BDADDR_BREDR)) { /* Connection accepted */ struct inquiry_entry *ie; struct hci_conn *conn; @@ -4184,7 +4187,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) addr_type = BDADDR_LE_RANDOM; /* Drop the connection if he device is blocked */ - if (hci_blacklist_lookup(hdev, &conn->dst, addr_type)) { + if (hci_bdaddr_list_lookup(&hdev->blacklist, &conn->dst, addr_type)) { hci_conn_drop(conn); goto unlock; } @@ -4253,7 +4256,7 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, return; /* Ignore if the device is blocked */ - if (hci_blacklist_lookup(hdev, addr, addr_type)) + if (hci_bdaddr_list_lookup(&hdev->blacklist, addr, addr_type)) return; /* If we're connectable, always connect any ADV_DIRECT_IND event */ diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 802665751cc4..c64728d571ae 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -481,7 +481,7 @@ static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg) hci_dev_lock(hdev); - err = hci_blacklist_add(hdev, &bdaddr, BDADDR_BREDR); + err = hci_bdaddr_list_add(&hdev->blacklist, &bdaddr, BDADDR_BREDR); hci_dev_unlock(hdev); @@ -498,7 +498,7 @@ static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg) hci_dev_lock(hdev); - err = hci_blacklist_del(hdev, &bdaddr, BDADDR_BREDR); + err = hci_bdaddr_list_del(&hdev->blacklist, &bdaddr, BDADDR_BREDR); hci_dev_unlock(hdev); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index bf379a379fa0..d006e6c0e3b4 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1460,6 +1460,7 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid, static void l2cap_le_conn_ready(struct l2cap_conn *conn) { struct hci_conn *hcon = conn->hcon; + struct hci_dev *hdev = hcon->hdev; struct l2cap_chan *chan, *pchan; u8 dst_type; @@ -1478,7 +1479,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) dst_type = bdaddr_type(hcon, hcon->dst_type); /* If device is blocked, do not create a channel for it */ - if (hci_blacklist_lookup(hcon->hdev, &hcon->dst, dst_type)) + if (hci_bdaddr_list_lookup(&hdev->blacklist, &hcon->dst, dst_type)) return; /* For LE slave connections, make sure the connection interval @@ -6918,8 +6919,8 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) * at least ensure that we ignore incoming data from them. */ if (hcon->type == LE_LINK && - hci_blacklist_lookup(hcon->hdev, &hcon->dst, - bdaddr_type(hcon, hcon->dst_type))) { + hci_bdaddr_list_lookup(&hcon->hdev->blacklist, &hcon->dst, + bdaddr_type(hcon, hcon->dst_type))) { kfree_skb(skb); return; } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 216aa93514b6..592e73eea76d 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3956,7 +3956,8 @@ static int block_device(struct sock *sk, struct hci_dev *hdev, void *data, hci_dev_lock(hdev); - err = hci_blacklist_add(hdev, &cp->addr.bdaddr, cp->addr.type); + err = hci_bdaddr_list_add(&hdev->blacklist, &cp->addr.bdaddr, + cp->addr.type); if (err < 0) { status = MGMT_STATUS_FAILED; goto done; @@ -3991,7 +3992,8 @@ static int unblock_device(struct sock *sk, struct hci_dev *hdev, void *data, hci_dev_lock(hdev); - err = hci_blacklist_del(hdev, &cp->addr.bdaddr, cp->addr.type); + err = hci_bdaddr_list_del(&hdev->blacklist, &cp->addr.bdaddr, + cp->addr.type); if (err < 0) { status = MGMT_STATUS_INVALID_PARAMS; goto done; -- cgit v1.2.3 From 6659358efe617bb46237e62f7303c76e10568d70 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jul 2014 12:59:14 +0300 Subject: Bluetooth: Introduce a whitelist for BR/EDR devices This patch extends the Add/Remove device commands by letting user space pass BR/EDR addresses to them. The resulting entries get stored in a new hdev->whitelist list. The idea is that we can now selectively accept connections from devices in the list even though HCI_CONNECTABLE is not set (the actual implementation of this is coming in a subsequent patch). Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 29 +++++++++++++++++++++++++ net/bluetooth/mgmt.c | 46 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 74 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 3a1caf10cc8d..cba4837dcaa5 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -305,6 +305,7 @@ struct hci_dev { struct list_head mgmt_pending; struct list_head blacklist; + struct list_head whitelist; struct list_head uuids; struct list_head link_keys; struct list_head long_term_keys; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 705f8df7af96..728a6ee471ea 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -191,6 +191,31 @@ static const struct file_operations blacklist_fops = { .release = single_release, }; +static int whitelist_show(struct seq_file *f, void *p) +{ + struct hci_dev *hdev = f->private; + struct bdaddr_list *b; + + hci_dev_lock(hdev); + list_for_each_entry(b, &hdev->whitelist, list) + seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type); + hci_dev_unlock(hdev); + + return 0; +} + +static int whitelist_open(struct inode *inode, struct file *file) +{ + return single_open(file, whitelist_show, inode->i_private); +} + +static const struct file_operations whitelist_fops = { + .open = whitelist_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static int uuids_show(struct seq_file *f, void *p) { struct hci_dev *hdev = f->private; @@ -1707,6 +1732,8 @@ static int __hci_init(struct hci_dev *hdev) debugfs_create_u16("hci_revision", 0444, hdev->debugfs, &hdev->hci_rev); debugfs_create_file("blacklist", 0444, hdev->debugfs, hdev, &blacklist_fops); + debugfs_create_file("whitelist", 0444, hdev->debugfs, hdev, + &whitelist_fops); debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops); debugfs_create_file("conn_info_min_age", 0644, hdev->debugfs, hdev, @@ -3825,6 +3852,7 @@ struct hci_dev *hci_alloc_dev(void) INIT_LIST_HEAD(&hdev->mgmt_pending); INIT_LIST_HEAD(&hdev->blacklist); + INIT_LIST_HEAD(&hdev->whitelist); INIT_LIST_HEAD(&hdev->uuids); INIT_LIST_HEAD(&hdev->link_keys); INIT_LIST_HEAD(&hdev->long_term_keys); @@ -4036,6 +4064,7 @@ void hci_unregister_dev(struct hci_dev *hdev) hci_dev_lock(hdev); hci_bdaddr_list_clear(&hdev->blacklist); + hci_bdaddr_list_clear(&hdev->whitelist); hci_uuids_clear(hdev); hci_link_keys_clear(hdev); hci_smp_ltks_clear(hdev); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 592e73eea76d..49581e99685c 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5219,7 +5219,7 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, BT_DBG("%s", hdev->name); - if (!bdaddr_type_is_le(cp->addr.type) || + if (!bdaddr_type_is_valid(cp->addr.type) || !bacmp(&cp->addr.bdaddr, BDADDR_ANY)) return cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, MGMT_STATUS_INVALID_PARAMS, @@ -5232,6 +5232,22 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, hci_dev_lock(hdev); + if (cp->addr.type == BDADDR_BREDR) { + /* Only "connect" action supported for now */ + if (cp->action != 0x01) { + err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, + MGMT_STATUS_INVALID_PARAMS, + &cp->addr, sizeof(cp->addr)); + goto unlock; + } + + err = hci_bdaddr_list_add(&hdev->whitelist, &cp->addr.bdaddr, + cp->addr.type); + if (err) + goto unlock; + goto added; + } + if (cp->addr.type == BDADDR_LE_PUBLIC) addr_type = ADDR_LE_DEV_PUBLIC; else @@ -5253,6 +5269,7 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, goto unlock; } +added: device_added(sk, hdev, &cp->addr.bdaddr, cp->addr.type, cp->action); err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, @@ -5288,13 +5305,30 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, struct hci_conn_params *params; u8 addr_type; - if (!bdaddr_type_is_le(cp->addr.type)) { + if (!bdaddr_type_is_valid(cp->addr.type)) { err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, MGMT_STATUS_INVALID_PARAMS, &cp->addr, sizeof(cp->addr)); goto unlock; } + if (cp->addr.type == BDADDR_BREDR) { + err = hci_bdaddr_list_del(&hdev->whitelist, + &cp->addr.bdaddr, + cp->addr.type); + if (err) { + err = cmd_complete(sk, hdev->id, + MGMT_OP_REMOVE_DEVICE, + MGMT_STATUS_INVALID_PARAMS, + &cp->addr, sizeof(cp->addr)); + goto unlock; + } + + device_removed(sk, hdev, &cp->addr.bdaddr, + cp->addr.type); + goto complete; + } + if (cp->addr.type == BDADDR_LE_PUBLIC) addr_type = ADDR_LE_DEV_PUBLIC; else @@ -5324,6 +5358,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, device_removed(sk, hdev, &cp->addr.bdaddr, cp->addr.type); } else { struct hci_conn_params *p, *tmp; + struct bdaddr_list *b, *btmp; if (cp->addr.type) { err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, @@ -5332,6 +5367,12 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, goto unlock; } + list_for_each_entry_safe(b, btmp, &hdev->whitelist, list) { + device_removed(sk, hdev, &b->bdaddr, b->bdaddr_type); + list_del(&b->list); + kfree(b); + } + list_for_each_entry_safe(p, tmp, &hdev->le_conn_params, list) { if (p->auto_connect == HCI_AUTO_CONN_DISABLED) continue; @@ -5346,6 +5387,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, hci_update_background_scan(hdev); } +complete: err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, MGMT_STATUS_SUCCESS, &cp->addr, sizeof(cp->addr)); -- cgit v1.2.3 From 91a668b0565dddc9f556f9bce65da58264c74623 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jul 2014 13:28:26 +0300 Subject: Bluetooth: Fix setting HCI_CONNECTABLE from ioctl code When the white list is in use the code would not update the HCI_CONNECTABLE flag if it gets changed through the ioctl code (e.g. hciconfig hci0 pscan). Since the flag is important for properly accepting incoming connections add code to fix it up if necessary and emit a New Settings mgmt event. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 17 +++++++++++++++++ net/bluetooth/mgmt.c | 5 +++++ 3 files changed, 23 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index cba4837dcaa5..e69c2b08c0c6 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1281,6 +1281,7 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event); #define DISCOV_BREDR_INQUIRY_LEN 0x08 int mgmt_control(struct sock *sk, struct msghdr *msg, size_t len); +int mgmt_new_settings(struct hci_dev *hdev); void mgmt_index_added(struct hci_dev *hdev); void mgmt_index_removed(struct hci_dev *hdev); void mgmt_set_powered_failed(struct hci_dev *hdev, int err); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 728a6ee471ea..84431b86af96 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2715,6 +2715,23 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg) case HCISETSCAN: err = hci_req_sync(hdev, hci_scan_req, dr.dev_opt, HCI_INIT_TIMEOUT); + + /* Ensure that the connectable state gets correctly + * notified if the whitelist is in use. + */ + if (!err && !list_empty(&hdev->whitelist)) { + bool changed; + + if ((dr.dev_opt & SCAN_PAGE)) + changed = !test_and_set_bit(HCI_CONNECTABLE, + &hdev->dev_flags); + else + changed = test_and_set_bit(HCI_CONNECTABLE, + &hdev->dev_flags); + + if (changed) + mgmt_new_settings(hdev); + } break; case HCISETLINKPOL: diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 98392d61b78f..91b1f92c681e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1427,6 +1427,11 @@ static int new_settings(struct hci_dev *hdev, struct sock *skip) return mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), skip); } +int mgmt_new_settings(struct hci_dev *hdev) +{ + return new_settings(hdev, NULL); +} + struct cmd_lookup { struct sock *sk; struct hci_dev *hdev; -- cgit v1.2.3 From ec38846ad59d7b780540afcec101b24139933195 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 29 May 2014 01:20:16 +0200 Subject: backlight: atmel-pwm-bl: remove obsolete driver The atmel-pwm-bl driver is now obsolete. It is not used by any mainlined boards and is replaced by the generic pwm_bl with the pawm-atmel driver using the generic PWM framework. Signed-off-by: Alexandre Belloni Acked-by: Hans-Christian Egtvedt Acked-by: Jingoo Han Signed-off-by: Nicolas Ferre --- drivers/video/backlight/Kconfig | 11 -- drivers/video/backlight/Makefile | 1 - drivers/video/backlight/atmel-pwm-bl.c | 223 --------------------------------- include/linux/atmel-pwm-bl.h | 43 ------- 4 files changed, 278 deletions(-) delete mode 100644 drivers/video/backlight/atmel-pwm-bl.c delete mode 100644 include/linux/atmel-pwm-bl.h (limited to 'include') diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 5d449059a556..c3c18339b8cb 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -178,17 +178,6 @@ config BACKLIGHT_ATMEL_LCDC If in doubt, it's safe to enable this option; it doesn't kick in unless the board's description says it's wired that way. -config BACKLIGHT_ATMEL_PWM - tristate "Atmel PWM backlight control" - depends on ATMEL_PWM - help - Say Y here if you want to use the PWM peripheral in Atmel AT91 and - AVR32 devices. This driver will need additional platform data to know - which PWM instance to use and how to configure it. - - To compile this driver as a module, choose M here: the module will be - called atmel-pwm-bl. - config BACKLIGHT_EP93XX tristate "Cirrus EP93xx Backlight Driver" depends on FB_EP93XX diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index bb820024f346..351451dbb607 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -25,7 +25,6 @@ obj-$(CONFIG_BACKLIGHT_ADP8860) += adp8860_bl.o obj-$(CONFIG_BACKLIGHT_ADP8870) += adp8870_bl.o obj-$(CONFIG_BACKLIGHT_APPLE) += apple_bl.o obj-$(CONFIG_BACKLIGHT_AS3711) += as3711_bl.o -obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o obj-$(CONFIG_BACKLIGHT_BD6107) += bd6107.o obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o diff --git a/drivers/video/backlight/atmel-pwm-bl.c b/drivers/video/backlight/atmel-pwm-bl.c deleted file mode 100644 index 261b1a4ec3d8..000000000000 --- a/drivers/video/backlight/atmel-pwm-bl.c +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright (C) 2008 Atmel Corporation - * - * Backlight driver using Atmel PWM peripheral. - * - * 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 - -struct atmel_pwm_bl { - const struct atmel_pwm_bl_platform_data *pdata; - struct backlight_device *bldev; - struct platform_device *pdev; - struct pwm_channel pwmc; - int gpio_on; -}; - -static void atmel_pwm_bl_set_gpio_on(struct atmel_pwm_bl *pwmbl, int on) -{ - if (!gpio_is_valid(pwmbl->gpio_on)) - return; - - gpio_set_value(pwmbl->gpio_on, on ^ pwmbl->pdata->on_active_low); -} - -static int atmel_pwm_bl_set_intensity(struct backlight_device *bd) -{ - struct atmel_pwm_bl *pwmbl = bl_get_data(bd); - int intensity = bd->props.brightness; - int pwm_duty; - - if (bd->props.power != FB_BLANK_UNBLANK) - intensity = 0; - if (bd->props.fb_blank != FB_BLANK_UNBLANK) - intensity = 0; - - if (pwmbl->pdata->pwm_active_low) - pwm_duty = pwmbl->pdata->pwm_duty_min + intensity; - else - pwm_duty = pwmbl->pdata->pwm_duty_max - intensity; - - if (pwm_duty > pwmbl->pdata->pwm_duty_max) - pwm_duty = pwmbl->pdata->pwm_duty_max; - if (pwm_duty < pwmbl->pdata->pwm_duty_min) - pwm_duty = pwmbl->pdata->pwm_duty_min; - - if (!intensity) { - atmel_pwm_bl_set_gpio_on(pwmbl, 0); - pwm_channel_writel(&pwmbl->pwmc, PWM_CUPD, pwm_duty); - pwm_channel_disable(&pwmbl->pwmc); - } else { - pwm_channel_enable(&pwmbl->pwmc); - pwm_channel_writel(&pwmbl->pwmc, PWM_CUPD, pwm_duty); - atmel_pwm_bl_set_gpio_on(pwmbl, 1); - } - - return 0; -} - -static int atmel_pwm_bl_get_intensity(struct backlight_device *bd) -{ - struct atmel_pwm_bl *pwmbl = bl_get_data(bd); - u32 cdty; - u32 intensity; - - cdty = pwm_channel_readl(&pwmbl->pwmc, PWM_CDTY); - if (pwmbl->pdata->pwm_active_low) - intensity = cdty - pwmbl->pdata->pwm_duty_min; - else - intensity = pwmbl->pdata->pwm_duty_max - cdty; - - return intensity & 0xffff; -} - -static int atmel_pwm_bl_init_pwm(struct atmel_pwm_bl *pwmbl) -{ - unsigned long pwm_rate = pwmbl->pwmc.mck; - unsigned long prescale = DIV_ROUND_UP(pwm_rate, - (pwmbl->pdata->pwm_frequency * - pwmbl->pdata->pwm_compare_max)) - 1; - - /* - * Prescale must be power of two and maximum 0xf in size because of - * hardware limit. PWM speed will be: - * PWM module clock speed / (2 ^ prescale). - */ - prescale = fls(prescale); - if (prescale > 0xf) - prescale = 0xf; - - pwm_channel_writel(&pwmbl->pwmc, PWM_CMR, prescale); - pwm_channel_writel(&pwmbl->pwmc, PWM_CDTY, - pwmbl->pdata->pwm_duty_min + - pwmbl->bldev->props.brightness); - pwm_channel_writel(&pwmbl->pwmc, PWM_CPRD, - pwmbl->pdata->pwm_compare_max); - - dev_info(&pwmbl->pdev->dev, "Atmel PWM backlight driver (%lu Hz)\n", - pwmbl->pwmc.mck / pwmbl->pdata->pwm_compare_max / - (1 << prescale)); - - return pwm_channel_enable(&pwmbl->pwmc); -} - -static const struct backlight_ops atmel_pwm_bl_ops = { - .get_brightness = atmel_pwm_bl_get_intensity, - .update_status = atmel_pwm_bl_set_intensity, -}; - -static int atmel_pwm_bl_probe(struct platform_device *pdev) -{ - struct backlight_properties props; - const struct atmel_pwm_bl_platform_data *pdata; - struct backlight_device *bldev; - struct atmel_pwm_bl *pwmbl; - unsigned long flags; - int retval; - - pdata = dev_get_platdata(&pdev->dev); - if (!pdata) - return -ENODEV; - - if (pdata->pwm_compare_max < pdata->pwm_duty_max || - pdata->pwm_duty_min > pdata->pwm_duty_max || - pdata->pwm_frequency == 0) - return -EINVAL; - - pwmbl = devm_kzalloc(&pdev->dev, sizeof(struct atmel_pwm_bl), - GFP_KERNEL); - if (!pwmbl) - return -ENOMEM; - - pwmbl->pdev = pdev; - pwmbl->pdata = pdata; - pwmbl->gpio_on = pdata->gpio_on; - - retval = pwm_channel_alloc(pdata->pwm_channel, &pwmbl->pwmc); - if (retval) - return retval; - - if (gpio_is_valid(pwmbl->gpio_on)) { - /* Turn display off by default. */ - if (pdata->on_active_low) - flags = GPIOF_OUT_INIT_HIGH; - else - flags = GPIOF_OUT_INIT_LOW; - - retval = devm_gpio_request_one(&pdev->dev, pwmbl->gpio_on, - flags, "gpio_atmel_pwm_bl"); - if (retval) - goto err_free_pwm; - } - - memset(&props, 0, sizeof(struct backlight_properties)); - props.type = BACKLIGHT_RAW; - props.max_brightness = pdata->pwm_duty_max - pdata->pwm_duty_min; - bldev = devm_backlight_device_register(&pdev->dev, "atmel-pwm-bl", - &pdev->dev, pwmbl, &atmel_pwm_bl_ops, - &props); - if (IS_ERR(bldev)) { - retval = PTR_ERR(bldev); - goto err_free_pwm; - } - - pwmbl->bldev = bldev; - - platform_set_drvdata(pdev, pwmbl); - - /* Power up the backlight by default at middle intesity. */ - bldev->props.power = FB_BLANK_UNBLANK; - bldev->props.brightness = bldev->props.max_brightness / 2; - - retval = atmel_pwm_bl_init_pwm(pwmbl); - if (retval) - goto err_free_pwm; - - atmel_pwm_bl_set_intensity(bldev); - - return 0; - -err_free_pwm: - pwm_channel_free(&pwmbl->pwmc); - - return retval; -} - -static int atmel_pwm_bl_remove(struct platform_device *pdev) -{ - struct atmel_pwm_bl *pwmbl = platform_get_drvdata(pdev); - - atmel_pwm_bl_set_gpio_on(pwmbl, 0); - pwm_channel_disable(&pwmbl->pwmc); - pwm_channel_free(&pwmbl->pwmc); - - return 0; -} - -static struct platform_driver atmel_pwm_bl_driver = { - .driver = { - .name = "atmel-pwm-bl", - }, - /* REVISIT add suspend() and resume() */ - .probe = atmel_pwm_bl_probe, - .remove = atmel_pwm_bl_remove, -}; - -module_platform_driver(atmel_pwm_bl_driver); - -MODULE_AUTHOR("Hans-Christian egtvedt "); -MODULE_DESCRIPTION("Atmel PWM backlight driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:atmel-pwm-bl"); diff --git a/include/linux/atmel-pwm-bl.h b/include/linux/atmel-pwm-bl.h deleted file mode 100644 index 0153a47806c2..000000000000 --- a/include/linux/atmel-pwm-bl.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2007 Atmel Corporation - * - * Driver for the AT32AP700X PS/2 controller (PSIF). - * - * 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 __INCLUDE_ATMEL_PWM_BL_H -#define __INCLUDE_ATMEL_PWM_BL_H - -/** - * struct atmel_pwm_bl_platform_data - * @pwm_channel: which PWM channel in the PWM module to use. - * @pwm_frequency: PWM frequency to generate, the driver will try to be as - * close as the prescaler allows. - * @pwm_compare_max: value to use in the PWM channel compare register. - * @pwm_duty_max: maximum duty cycle value, must be less than or equal to - * pwm_compare_max. - * @pwm_duty_min: minimum duty cycle value, must be less than pwm_duty_max. - * @pwm_active_low: set to one if the low part of the PWM signal increases the - * brightness of the backlight. - * @gpio_on: GPIO line to control the backlight on/off, set to -1 if not used. - * @on_active_low: set to one if the on/off signal is on when GPIO is low. - * - * This struct must be added to the platform device in the board code. It is - * used by the atmel-pwm-bl driver to setup the GPIO to control on/off and the - * PWM device. - */ -struct atmel_pwm_bl_platform_data { - unsigned int pwm_channel; - unsigned int pwm_frequency; - unsigned int pwm_compare_max; - unsigned int pwm_duty_max; - unsigned int pwm_duty_min; - unsigned int pwm_active_low; - int gpio_on; - unsigned int on_active_low; -}; - -#endif /* __INCLUDE_ATMEL_PWM_BL_H */ -- cgit v1.2.3 From f2a70e1fc1ccc0fcdf4ad12db7382134228fb552 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 29 May 2014 01:20:18 +0200 Subject: misc: atmel_pwm: remove obsolete driver The misc/atmel_pwm is not used by any mainlined boards and has been replaced by the pwm-driver using the generic PWM framework. Signed-off-by: Alexandre Belloni Acked-by: Greg Kroah-Hartman Signed-off-by: Nicolas Ferre --- drivers/misc/Kconfig | 10 -- drivers/misc/Makefile | 1 - drivers/misc/atmel_pwm.c | 402 ---------------------------------------------- include/linux/atmel_pwm.h | 70 -------- 4 files changed, 483 deletions(-) delete mode 100644 drivers/misc/atmel_pwm.c delete mode 100644 include/linux/atmel_pwm.h (limited to 'include') diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index ee9402324a23..b841180c7c74 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -51,16 +51,6 @@ config AD525X_DPOT_SPI To compile this driver as a module, choose M here: the module will be called ad525x_dpot-spi. -config ATMEL_PWM - tristate "Atmel AT32/AT91 PWM support" - depends on HAVE_CLK - depends on AVR32 || ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45 - help - This option enables device driver support for the PWM channels - on certain Atmel processors. Pulse Width Modulation is used for - purposes including software controlled power-efficient backlights - on LCD displays, motor control, and waveform generation. - config ATMEL_TCLIB bool "Atmel AT32/AT91 Timer/Counter Library" depends on (AVR32 || ARCH_AT91) diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index d59ce1261b38..5497d026e651 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -7,7 +7,6 @@ obj-$(CONFIG_AD525X_DPOT) += ad525x_dpot.o obj-$(CONFIG_AD525X_DPOT_I2C) += ad525x_dpot-i2c.o obj-$(CONFIG_AD525X_DPOT_SPI) += ad525x_dpot-spi.o obj-$(CONFIG_INTEL_MID_PTI) += pti.o -obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o obj-$(CONFIG_BMP085) += bmp085.o diff --git a/drivers/misc/atmel_pwm.c b/drivers/misc/atmel_pwm.c deleted file mode 100644 index a6dc56e1bc58..000000000000 --- a/drivers/misc/atmel_pwm.c +++ /dev/null @@ -1,402 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - - -/* - * This is a simple driver for the PWM controller found in various newer - * Atmel SOCs, including the AVR32 series and the AT91sam9263. - * - * Chips with current Linux ports have only 4 PWM channels, out of max 32. - * AT32UC3A and AT32UC3B chips have 7 channels (but currently no Linux). - * Docs are inconsistent about the width of the channel counter registers; - * it's at least 16 bits, but several places say 20 bits. - */ -#define PWM_NCHAN 4 /* max 32 */ - -struct pwm { - spinlock_t lock; - struct platform_device *pdev; - u32 mask; - int irq; - void __iomem *base; - struct clk *clk; - struct pwm_channel *channel[PWM_NCHAN]; - void (*handler[PWM_NCHAN])(struct pwm_channel *); -}; - - -/* global PWM controller registers */ -#define PWM_MR 0x00 -#define PWM_ENA 0x04 -#define PWM_DIS 0x08 -#define PWM_SR 0x0c -#define PWM_IER 0x10 -#define PWM_IDR 0x14 -#define PWM_IMR 0x18 -#define PWM_ISR 0x1c - -static inline void pwm_writel(const struct pwm *p, unsigned offset, u32 val) -{ - __raw_writel(val, p->base + offset); -} - -static inline u32 pwm_readl(const struct pwm *p, unsigned offset) -{ - return __raw_readl(p->base + offset); -} - -static inline void __iomem *pwmc_regs(const struct pwm *p, int index) -{ - return p->base + 0x200 + index * 0x20; -} - -static struct pwm *pwm; - -static void pwm_dumpregs(struct pwm_channel *ch, char *tag) -{ - struct device *dev = &pwm->pdev->dev; - - dev_dbg(dev, "%s: mr %08x, sr %08x, imr %08x\n", - tag, - pwm_readl(pwm, PWM_MR), - pwm_readl(pwm, PWM_SR), - pwm_readl(pwm, PWM_IMR)); - dev_dbg(dev, - "pwm ch%d - mr %08x, dty %u, prd %u, cnt %u\n", - ch->index, - pwm_channel_readl(ch, PWM_CMR), - pwm_channel_readl(ch, PWM_CDTY), - pwm_channel_readl(ch, PWM_CPRD), - pwm_channel_readl(ch, PWM_CCNT)); -} - - -/** - * pwm_channel_alloc - allocate an unused PWM channel - * @index: identifies the channel - * @ch: structure to be initialized - * - * Drivers allocate PWM channels according to the board's wiring, and - * matching board-specific setup code. Returns zero or negative errno. - */ -int pwm_channel_alloc(int index, struct pwm_channel *ch) -{ - unsigned long flags; - int status = 0; - - if (!pwm) - return -EPROBE_DEFER; - - if (!(pwm->mask & 1 << index)) - return -ENODEV; - - if (index < 0 || index >= PWM_NCHAN || !ch) - return -EINVAL; - memset(ch, 0, sizeof *ch); - - spin_lock_irqsave(&pwm->lock, flags); - if (pwm->channel[index]) - status = -EBUSY; - else { - clk_enable(pwm->clk); - - ch->regs = pwmc_regs(pwm, index); - ch->index = index; - - /* REVISIT: ap7000 seems to go 2x as fast as we expect!! */ - ch->mck = clk_get_rate(pwm->clk); - - pwm->channel[index] = ch; - pwm->handler[index] = NULL; - - /* channel and irq are always disabled when we return */ - pwm_writel(pwm, PWM_DIS, 1 << index); - pwm_writel(pwm, PWM_IDR, 1 << index); - } - spin_unlock_irqrestore(&pwm->lock, flags); - return status; -} -EXPORT_SYMBOL(pwm_channel_alloc); - -static int pwmcheck(struct pwm_channel *ch) -{ - int index; - - if (!pwm) - return -ENODEV; - if (!ch) - return -EINVAL; - index = ch->index; - if (index < 0 || index >= PWM_NCHAN || pwm->channel[index] != ch) - return -EINVAL; - - return index; -} - -/** - * pwm_channel_free - release a previously allocated channel - * @ch: the channel being released - * - * The channel is completely shut down (counter and IRQ disabled), - * and made available for re-use. Returns zero, or negative errno. - */ -int pwm_channel_free(struct pwm_channel *ch) -{ - unsigned long flags; - int t; - - spin_lock_irqsave(&pwm->lock, flags); - t = pwmcheck(ch); - if (t >= 0) { - pwm->channel[t] = NULL; - pwm->handler[t] = NULL; - - /* channel and irq are always disabled when we return */ - pwm_writel(pwm, PWM_DIS, 1 << t); - pwm_writel(pwm, PWM_IDR, 1 << t); - - clk_disable(pwm->clk); - t = 0; - } - spin_unlock_irqrestore(&pwm->lock, flags); - return t; -} -EXPORT_SYMBOL(pwm_channel_free); - -int __pwm_channel_onoff(struct pwm_channel *ch, int enabled) -{ - unsigned long flags; - int t; - - /* OMITTED FUNCTIONALITY: starting several channels in synch */ - - spin_lock_irqsave(&pwm->lock, flags); - t = pwmcheck(ch); - if (t >= 0) { - pwm_writel(pwm, enabled ? PWM_ENA : PWM_DIS, 1 << t); - t = 0; - pwm_dumpregs(ch, enabled ? "enable" : "disable"); - } - spin_unlock_irqrestore(&pwm->lock, flags); - - return t; -} -EXPORT_SYMBOL(__pwm_channel_onoff); - -/** - * pwm_clk_alloc - allocate and configure CLKA or CLKB - * @prescale: from 0..10, the power of two used to divide MCK - * @div: from 1..255, the linear divisor to use - * - * Returns PWM_CPR_CLKA, PWM_CPR_CLKB, or negative errno. The allocated - * clock will run with a period of (2^prescale * div) / MCK, or twice as - * long if center aligned PWM output is used. The clock must later be - * deconfigured using pwm_clk_free(). - */ -int pwm_clk_alloc(unsigned prescale, unsigned div) -{ - unsigned long flags; - u32 mr; - u32 val = (prescale << 8) | div; - int ret = -EBUSY; - - if (prescale >= 10 || div == 0 || div > 255) - return -EINVAL; - - spin_lock_irqsave(&pwm->lock, flags); - mr = pwm_readl(pwm, PWM_MR); - if ((mr & 0xffff) == 0) { - mr |= val; - ret = PWM_CPR_CLKA; - } else if ((mr & (0xffff << 16)) == 0) { - mr |= val << 16; - ret = PWM_CPR_CLKB; - } - if (ret > 0) - pwm_writel(pwm, PWM_MR, mr); - spin_unlock_irqrestore(&pwm->lock, flags); - return ret; -} -EXPORT_SYMBOL(pwm_clk_alloc); - -/** - * pwm_clk_free - deconfigure and release CLKA or CLKB - * - * Reverses the effect of pwm_clk_alloc(). - */ -void pwm_clk_free(unsigned clk) -{ - unsigned long flags; - u32 mr; - - spin_lock_irqsave(&pwm->lock, flags); - mr = pwm_readl(pwm, PWM_MR); - if (clk == PWM_CPR_CLKA) - pwm_writel(pwm, PWM_MR, mr & ~(0xffff << 0)); - if (clk == PWM_CPR_CLKB) - pwm_writel(pwm, PWM_MR, mr & ~(0xffff << 16)); - spin_unlock_irqrestore(&pwm->lock, flags); -} -EXPORT_SYMBOL(pwm_clk_free); - -/** - * pwm_channel_handler - manage channel's IRQ handler - * @ch: the channel - * @handler: the handler to use, possibly NULL - * - * If the handler is non-null, the handler will be called after every - * period of this PWM channel. If the handler is null, this channel - * won't generate an IRQ. - */ -int pwm_channel_handler(struct pwm_channel *ch, - void (*handler)(struct pwm_channel *ch)) -{ - unsigned long flags; - int t; - - spin_lock_irqsave(&pwm->lock, flags); - t = pwmcheck(ch); - if (t >= 0) { - pwm->handler[t] = handler; - pwm_writel(pwm, handler ? PWM_IER : PWM_IDR, 1 << t); - t = 0; - } - spin_unlock_irqrestore(&pwm->lock, flags); - - return t; -} -EXPORT_SYMBOL(pwm_channel_handler); - -static irqreturn_t pwm_irq(int id, void *_pwm) -{ - struct pwm *p = _pwm; - irqreturn_t handled = IRQ_NONE; - u32 irqstat; - int index; - - spin_lock(&p->lock); - - /* ack irqs, then handle them */ - irqstat = pwm_readl(pwm, PWM_ISR); - - while (irqstat) { - struct pwm_channel *ch; - void (*handler)(struct pwm_channel *ch); - - index = ffs(irqstat) - 1; - irqstat &= ~(1 << index); - ch = pwm->channel[index]; - handler = pwm->handler[index]; - if (handler && ch) { - spin_unlock(&p->lock); - handler(ch); - spin_lock(&p->lock); - handled = IRQ_HANDLED; - } - } - - spin_unlock(&p->lock); - return handled; -} - -static int __init pwm_probe(struct platform_device *pdev) -{ - struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - int irq = platform_get_irq(pdev, 0); - u32 *mp = pdev->dev.platform_data; - struct pwm *p; - int status = -EIO; - - if (pwm) - return -EBUSY; - if (!r || irq < 0 || !mp || !*mp) - return -ENODEV; - if (*mp & ~((1<dev, "mask 0x%x ... more than %d channels\n", - *mp, PWM_NCHAN); - return -EINVAL; - } - - p = kzalloc(sizeof(*p), GFP_KERNEL); - if (!p) - return -ENOMEM; - - spin_lock_init(&p->lock); - p->pdev = pdev; - p->mask = *mp; - p->irq = irq; - p->base = ioremap(r->start, resource_size(r)); - if (!p->base) - goto fail; - p->clk = clk_get(&pdev->dev, "pwm_clk"); - if (IS_ERR(p->clk)) { - status = PTR_ERR(p->clk); - p->clk = NULL; - goto fail; - } - - status = request_irq(irq, pwm_irq, 0, pdev->name, p); - if (status < 0) - goto fail; - - pwm = p; - platform_set_drvdata(pdev, p); - - return 0; - -fail: - if (p->clk) - clk_put(p->clk); - if (p->base) - iounmap(p->base); - - kfree(p); - return status; -} - -static int __exit pwm_remove(struct platform_device *pdev) -{ - struct pwm *p = platform_get_drvdata(pdev); - - if (p != pwm) - return -EINVAL; - - clk_enable(pwm->clk); - pwm_writel(pwm, PWM_DIS, (1 << PWM_NCHAN) - 1); - pwm_writel(pwm, PWM_IDR, (1 << PWM_NCHAN) - 1); - clk_disable(pwm->clk); - - pwm = NULL; - - free_irq(p->irq, p); - clk_put(p->clk); - iounmap(p->base); - kfree(p); - - return 0; -} - -static struct platform_driver atmel_pwm_driver = { - .driver = { - .name = "atmel_pwm", - .owner = THIS_MODULE, - }, - .remove = __exit_p(pwm_remove), - - /* NOTE: PWM can keep running in AVR32 "idle" and "frozen" states; - * and all AT91sam9263 states, albeit at reduced clock rate if - * MCK becomes the slow clock (i.e. what Linux labels STR). - */ -}; - -module_platform_driver_probe(atmel_pwm_driver, pwm_probe); - -MODULE_DESCRIPTION("Driver for AT32/AT91 PWM module"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:atmel_pwm"); diff --git a/include/linux/atmel_pwm.h b/include/linux/atmel_pwm.h deleted file mode 100644 index ea04abb3db8e..000000000000 --- a/include/linux/atmel_pwm.h +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef __LINUX_ATMEL_PWM_H -#define __LINUX_ATMEL_PWM_H - -/** - * struct pwm_channel - driver handle to a PWM channel - * @regs: base of this channel's registers - * @index: number of this channel (0..31) - * @mck: base clock rate, which can be prescaled and maybe subdivided - * - * Drivers initialize a pwm_channel structure using pwm_channel_alloc(). - * Then they configure its clock rate (derived from MCK), alignment, - * polarity, and duty cycle by writing directly to the channel registers, - * before enabling the channel by calling pwm_channel_enable(). - * - * After emitting a PWM signal for the desired length of time, drivers - * may then pwm_channel_disable() or pwm_channel_free(). Both of these - * disable the channel, but when it's freed the IRQ is deconfigured and - * the channel must later be re-allocated and reconfigured. - * - * Note that if the period or duty cycle need to be changed while the - * PWM channel is operating, drivers must use the PWM_CUPD double buffer - * mechanism, either polling until they change or getting implicitly - * notified through a once-per-period interrupt handler. - */ -struct pwm_channel { - void __iomem *regs; - unsigned index; - unsigned long mck; -}; - -extern int pwm_channel_alloc(int index, struct pwm_channel *ch); -extern int pwm_channel_free(struct pwm_channel *ch); - -extern int pwm_clk_alloc(unsigned prescale, unsigned div); -extern void pwm_clk_free(unsigned clk); - -extern int __pwm_channel_onoff(struct pwm_channel *ch, int enabled); - -#define pwm_channel_enable(ch) __pwm_channel_onoff((ch), 1) -#define pwm_channel_disable(ch) __pwm_channel_onoff((ch), 0) - -/* periodic interrupts, mostly for CUPD changes to period or cycle */ -extern int pwm_channel_handler(struct pwm_channel *ch, - void (*handler)(struct pwm_channel *ch)); - -/* per-channel registers (banked at pwm_channel->regs) */ -#define PWM_CMR 0x00 /* mode register */ -#define PWM_CPR_CPD (1 << 10) /* set: CUPD modifies period */ -#define PWM_CPR_CPOL (1 << 9) /* set: idle high */ -#define PWM_CPR_CALG (1 << 8) /* set: center align */ -#define PWM_CPR_CPRE (0xf << 0) /* mask: rate is mck/(2^pre) */ -#define PWM_CPR_CLKA (0xb << 0) /* rate CLKA */ -#define PWM_CPR_CLKB (0xc << 0) /* rate CLKB */ -#define PWM_CDTY 0x04 /* duty cycle (max of CPRD) */ -#define PWM_CPRD 0x08 /* period (count up from zero) */ -#define PWM_CCNT 0x0c /* counter (20 bits?) */ -#define PWM_CUPD 0x10 /* update CPRD (or CDTY) next period */ - -static inline void -pwm_channel_writel(struct pwm_channel *pwmc, unsigned offset, u32 val) -{ - __raw_writel(val, pwmc->regs + offset); -} - -static inline u32 pwm_channel_readl(struct pwm_channel *pwmc, unsigned offset) -{ - return __raw_readl(pwmc->regs + offset); -} - -#endif /* __LINUX_ATMEL_PWM_H */ -- cgit v1.2.3 From 8cd118308a8649c649533a0133af0ce731d223bb Mon Sep 17 00:00:00 2001 From: Micky Ching Date: Fri, 6 Jun 2014 15:05:44 +0800 Subject: mfd: rtsx: Add dma transfer function rtsx driver using a single function for transfer data, dma map/unmap are placed in one fix function. We need map/unmap dma in different place(for mmc async driver), so add three function for dma map, dma transfer and dma unmap. Signed-off-by: Micky Ching Signed-off-by: Lee Jones --- drivers/mfd/rtsx_pcr.c | 76 ++++++++++++++++++++++++++++---------------- include/linux/mfd/rtsx_pci.h | 6 ++++ 2 files changed, 54 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index 1d15735f9ef9..d01b8c249231 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -337,40 +337,64 @@ static void rtsx_pci_add_sg_tbl(struct rtsx_pcr *pcr, int rtsx_pci_transfer_data(struct rtsx_pcr *pcr, struct scatterlist *sglist, int num_sg, bool read, int timeout) { - struct completion trans_done; - u8 dir; - int err = 0, i, count; - long timeleft; - unsigned long flags; - struct scatterlist *sg; - enum dma_data_direction dma_dir; - u32 val; - dma_addr_t addr; - unsigned int len; + int err = 0, count; dev_dbg(&(pcr->pci->dev), "--> %s: num_sg = %d\n", __func__, num_sg); + count = rtsx_pci_dma_map_sg(pcr, sglist, num_sg, read); + if (count < 1) + return -EINVAL; + dev_dbg(&(pcr->pci->dev), "DMA mapping count: %d\n", count); + + err = rtsx_pci_dma_transfer(pcr, sglist, count, read, timeout); + + rtsx_pci_dma_unmap_sg(pcr, sglist, num_sg, read); + + return err; +} +EXPORT_SYMBOL_GPL(rtsx_pci_transfer_data); + +int rtsx_pci_dma_map_sg(struct rtsx_pcr *pcr, struct scatterlist *sglist, + int num_sg, bool read) +{ + enum dma_data_direction dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE; - /* don't transfer data during abort processing */ if (pcr->remove_pci) return -EINVAL; if ((sglist == NULL) || (num_sg <= 0)) return -EINVAL; - if (read) { - dir = DEVICE_TO_HOST; - dma_dir = DMA_FROM_DEVICE; - } else { - dir = HOST_TO_DEVICE; - dma_dir = DMA_TO_DEVICE; - } + return dma_map_sg(&(pcr->pci->dev), sglist, num_sg, dir); +} +EXPORT_SYMBOL_GPL(rtsx_pci_dma_map_sg); - count = dma_map_sg(&(pcr->pci->dev), sglist, num_sg, dma_dir); - if (count < 1) { - dev_err(&(pcr->pci->dev), "scatterlist map failed\n"); +void rtsx_pci_dma_unmap_sg(struct rtsx_pcr *pcr, struct scatterlist *sglist, + int num_sg, bool read) +{ + enum dma_data_direction dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + + dma_unmap_sg(&(pcr->pci->dev), sglist, num_sg, dir); +} +EXPORT_SYMBOL_GPL(rtsx_pci_dma_unmap_sg); + +int rtsx_pci_dma_transfer(struct rtsx_pcr *pcr, struct scatterlist *sglist, + int count, bool read, int timeout) +{ + struct completion trans_done; + struct scatterlist *sg; + dma_addr_t addr; + long timeleft; + unsigned long flags; + unsigned int len; + int i, err = 0; + u32 val; + u8 dir = read ? DEVICE_TO_HOST : HOST_TO_DEVICE; + + if (pcr->remove_pci) + return -ENODEV; + + if ((sglist == NULL) || (count < 1)) return -EINVAL; - } - dev_dbg(&(pcr->pci->dev), "DMA mapping count: %d\n", count); val = ((u32)(dir & 0x01) << 29) | TRIG_DMA | ADMA_MODE; pcr->sgi = 0; @@ -400,12 +424,10 @@ int rtsx_pci_transfer_data(struct rtsx_pcr *pcr, struct scatterlist *sglist, } spin_lock_irqsave(&pcr->lock, flags); - if (pcr->trans_result == TRANS_RESULT_FAIL) err = -EINVAL; else if (pcr->trans_result == TRANS_NO_DEVICE) err = -ENODEV; - spin_unlock_irqrestore(&pcr->lock, flags); out: @@ -413,8 +435,6 @@ out: pcr->done = NULL; spin_unlock_irqrestore(&pcr->lock, flags); - dma_unmap_sg(&(pcr->pci->dev), sglist, num_sg, dma_dir); - if ((err < 0) && (err != -ENODEV)) rtsx_pci_stop_cmd(pcr); @@ -423,7 +443,7 @@ out: return err; } -EXPORT_SYMBOL_GPL(rtsx_pci_transfer_data); +EXPORT_SYMBOL_GPL(rtsx_pci_dma_transfer); int rtsx_pci_read_ppbuf(struct rtsx_pcr *pcr, u8 *buf, int buf_len) { diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h index a3835976f7c6..74346d5e7899 100644 --- a/include/linux/mfd/rtsx_pci.h +++ b/include/linux/mfd/rtsx_pci.h @@ -943,6 +943,12 @@ void rtsx_pci_send_cmd_no_wait(struct rtsx_pcr *pcr); int rtsx_pci_send_cmd(struct rtsx_pcr *pcr, int timeout); int rtsx_pci_transfer_data(struct rtsx_pcr *pcr, struct scatterlist *sglist, int num_sg, bool read, int timeout); +int rtsx_pci_dma_map_sg(struct rtsx_pcr *pcr, struct scatterlist *sglist, + int num_sg, bool read); +void rtsx_pci_dma_unmap_sg(struct rtsx_pcr *pcr, struct scatterlist *sglist, + int num_sg, bool read); +int rtsx_pci_dma_transfer(struct rtsx_pcr *pcr, struct scatterlist *sglist, + int count, bool read, int timeout); int rtsx_pci_read_ppbuf(struct rtsx_pcr *pcr, u8 *buf, int buf_len); int rtsx_pci_write_ppbuf(struct rtsx_pcr *pcr, u8 *buf, int buf_len); int rtsx_pci_card_pull_ctl_enable(struct rtsx_pcr *pcr, int card); -- cgit v1.2.3 From bdbc736da636956a40435f5f41d2be6af544c3fb Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 2 Jun 2014 09:50:43 +0100 Subject: mfd: arizona: Lower ARIZONA_MAX_CORE_SUPPLIES to 2 There are no Arizona devices with 3 core supplies but we define a fix array with space for 3 core supplies. Lower the ARIZONA_MAX_CORE_SUPPLIES define to 2, to save a few bytes. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- include/linux/mfd/arizona/core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h index 6d9371f88875..70854d892760 100644 --- a/include/linux/mfd/arizona/core.h +++ b/include/linux/mfd/arizona/core.h @@ -18,7 +18,7 @@ #include #include -#define ARIZONA_MAX_CORE_SUPPLIES 3 +#define ARIZONA_MAX_CORE_SUPPLIES 2 enum arizona_type { WM5102 = 1, -- cgit v1.2.3 From 7811a989fca2de59fd6ba4afe63b6f670d0bd087 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Mon, 9 Jun 2014 14:01:59 +0530 Subject: dt-binding: mfd: as3722: Correct macro name The macro name for enable3 pin is named as AS3722_EXT_CONTROL_PIN_ENABLE2 which is conflict with the enable2 pin. Correct this macro name to correctly reflect the enable pin i.e. AS3722_EXT_CONTROL_PIN_ENABLE3. Signed-off-by: Laxman Dewangan Reported-by: Dan Willemsen Signed-off-by: Lee Jones --- include/dt-bindings/mfd/as3722.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/dt-bindings/mfd/as3722.h b/include/dt-bindings/mfd/as3722.h index 0e692562d77b..e66c0898c58e 100644 --- a/include/dt-bindings/mfd/as3722.h +++ b/include/dt-bindings/mfd/as3722.h @@ -13,7 +13,7 @@ /* External control pins */ #define AS3722_EXT_CONTROL_PIN_ENABLE1 1 #define AS3722_EXT_CONTROL_PIN_ENABLE2 2 -#define AS3722_EXT_CONTROL_PIN_ENABLE2 3 +#define AS3722_EXT_CONTROL_PIN_ENABLE3 3 /* Interrupt numbers for AS3722 */ #define AS3722_IRQ_LID 0 -- cgit v1.2.3 From 10f9edaeaa30468194e1dcd0e47e59b012f4cf8b Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Wed, 18 Jun 2014 21:05:40 +0400 Subject: mfd: mc13xxx: Use regmap irq framework for interrupts This patch convert mc13xxx MFD driver to use regmap irq framework for interrupt registration. Signed-off-by: Alexander Shiyan Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 1 + drivers/mfd/mc13xxx-core.c | 310 ++++++-------------------------------------- drivers/mfd/mc13xxx.h | 11 +- include/linux/mfd/mc13783.h | 1 - include/linux/mfd/mc13xxx.h | 23 +++- 5 files changed, 65 insertions(+), 281 deletions(-) (limited to 'include') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 207c433074af..defe58d65940 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -187,6 +187,7 @@ config MFD_MC13XXX tristate depends on (SPI_MASTER || I2C) select MFD_CORE + select REGMAP_IRQ help Enable support for the Freescale MC13783 and MC13892 PMICs. This driver provides common support for accessing the device, diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index acf5dd712eb2..2b6bc868cd3d 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -10,106 +10,18 @@ * Free Software Foundation. */ -#include #include -#include -#include -#include -#include -#include #include #include -#include +#include +#include #include "mc13xxx.h" #define MC13XXX_IRQSTAT0 0 -#define MC13XXX_IRQSTAT0_ADCDONEI (1 << 0) -#define MC13XXX_IRQSTAT0_ADCBISDONEI (1 << 1) -#define MC13XXX_IRQSTAT0_TSI (1 << 2) -#define MC13783_IRQSTAT0_WHIGHI (1 << 3) -#define MC13783_IRQSTAT0_WLOWI (1 << 4) -#define MC13XXX_IRQSTAT0_CHGDETI (1 << 6) -#define MC13783_IRQSTAT0_CHGOVI (1 << 7) -#define MC13XXX_IRQSTAT0_CHGREVI (1 << 8) -#define MC13XXX_IRQSTAT0_CHGSHORTI (1 << 9) -#define MC13XXX_IRQSTAT0_CCCVI (1 << 10) -#define MC13XXX_IRQSTAT0_CHGCURRI (1 << 11) -#define MC13XXX_IRQSTAT0_BPONI (1 << 12) -#define MC13XXX_IRQSTAT0_LOBATLI (1 << 13) -#define MC13XXX_IRQSTAT0_LOBATHI (1 << 14) -#define MC13783_IRQSTAT0_UDPI (1 << 15) -#define MC13783_IRQSTAT0_USBI (1 << 16) -#define MC13783_IRQSTAT0_IDI (1 << 19) -#define MC13783_IRQSTAT0_SE1I (1 << 21) -#define MC13783_IRQSTAT0_CKDETI (1 << 22) -#define MC13783_IRQSTAT0_UDMI (1 << 23) - #define MC13XXX_IRQMASK0 1 -#define MC13XXX_IRQMASK0_ADCDONEM MC13XXX_IRQSTAT0_ADCDONEI -#define MC13XXX_IRQMASK0_ADCBISDONEM MC13XXX_IRQSTAT0_ADCBISDONEI -#define MC13XXX_IRQMASK0_TSM MC13XXX_IRQSTAT0_TSI -#define MC13783_IRQMASK0_WHIGHM MC13783_IRQSTAT0_WHIGHI -#define MC13783_IRQMASK0_WLOWM MC13783_IRQSTAT0_WLOWI -#define MC13XXX_IRQMASK0_CHGDETM MC13XXX_IRQSTAT0_CHGDETI -#define MC13783_IRQMASK0_CHGOVM MC13783_IRQSTAT0_CHGOVI -#define MC13XXX_IRQMASK0_CHGREVM MC13XXX_IRQSTAT0_CHGREVI -#define MC13XXX_IRQMASK0_CHGSHORTM MC13XXX_IRQSTAT0_CHGSHORTI -#define MC13XXX_IRQMASK0_CCCVM MC13XXX_IRQSTAT0_CCCVI -#define MC13XXX_IRQMASK0_CHGCURRM MC13XXX_IRQSTAT0_CHGCURRI -#define MC13XXX_IRQMASK0_BPONM MC13XXX_IRQSTAT0_BPONI -#define MC13XXX_IRQMASK0_LOBATLM MC13XXX_IRQSTAT0_LOBATLI -#define MC13XXX_IRQMASK0_LOBATHM MC13XXX_IRQSTAT0_LOBATHI -#define MC13783_IRQMASK0_UDPM MC13783_IRQSTAT0_UDPI -#define MC13783_IRQMASK0_USBM MC13783_IRQSTAT0_USBI -#define MC13783_IRQMASK0_IDM MC13783_IRQSTAT0_IDI -#define MC13783_IRQMASK0_SE1M MC13783_IRQSTAT0_SE1I -#define MC13783_IRQMASK0_CKDETM MC13783_IRQSTAT0_CKDETI -#define MC13783_IRQMASK0_UDMM MC13783_IRQSTAT0_UDMI - #define MC13XXX_IRQSTAT1 3 -#define MC13XXX_IRQSTAT1_1HZI (1 << 0) -#define MC13XXX_IRQSTAT1_TODAI (1 << 1) -#define MC13783_IRQSTAT1_ONOFD1I (1 << 3) -#define MC13783_IRQSTAT1_ONOFD2I (1 << 4) -#define MC13783_IRQSTAT1_ONOFD3I (1 << 5) -#define MC13XXX_IRQSTAT1_SYSRSTI (1 << 6) -#define MC13XXX_IRQSTAT1_RTCRSTI (1 << 7) -#define MC13XXX_IRQSTAT1_PCI (1 << 8) -#define MC13XXX_IRQSTAT1_WARMI (1 << 9) -#define MC13XXX_IRQSTAT1_MEMHLDI (1 << 10) -#define MC13783_IRQSTAT1_PWRRDYI (1 << 11) -#define MC13XXX_IRQSTAT1_THWARNLI (1 << 12) -#define MC13XXX_IRQSTAT1_THWARNHI (1 << 13) -#define MC13XXX_IRQSTAT1_CLKI (1 << 14) -#define MC13783_IRQSTAT1_SEMAFI (1 << 15) -#define MC13783_IRQSTAT1_MC2BI (1 << 17) -#define MC13783_IRQSTAT1_HSDETI (1 << 18) -#define MC13783_IRQSTAT1_HSLI (1 << 19) -#define MC13783_IRQSTAT1_ALSPTHI (1 << 20) -#define MC13783_IRQSTAT1_AHSSHORTI (1 << 21) - #define MC13XXX_IRQMASK1 4 -#define MC13XXX_IRQMASK1_1HZM MC13XXX_IRQSTAT1_1HZI -#define MC13XXX_IRQMASK1_TODAM MC13XXX_IRQSTAT1_TODAI -#define MC13783_IRQMASK1_ONOFD1M MC13783_IRQSTAT1_ONOFD1I -#define MC13783_IRQMASK1_ONOFD2M MC13783_IRQSTAT1_ONOFD2I -#define MC13783_IRQMASK1_ONOFD3M MC13783_IRQSTAT1_ONOFD3I -#define MC13XXX_IRQMASK1_SYSRSTM MC13XXX_IRQSTAT1_SYSRSTI -#define MC13XXX_IRQMASK1_RTCRSTM MC13XXX_IRQSTAT1_RTCRSTI -#define MC13XXX_IRQMASK1_PCM MC13XXX_IRQSTAT1_PCI -#define MC13XXX_IRQMASK1_WARMM MC13XXX_IRQSTAT1_WARMI -#define MC13XXX_IRQMASK1_MEMHLDM MC13XXX_IRQSTAT1_MEMHLDI -#define MC13783_IRQMASK1_PWRRDYM MC13783_IRQSTAT1_PWRRDYI -#define MC13XXX_IRQMASK1_THWARNLM MC13XXX_IRQSTAT1_THWARNLI -#define MC13XXX_IRQMASK1_THWARNHM MC13XXX_IRQSTAT1_THWARNHI -#define MC13XXX_IRQMASK1_CLKM MC13XXX_IRQSTAT1_CLKI -#define MC13783_IRQMASK1_SEMAFM MC13783_IRQSTAT1_SEMAFI -#define MC13783_IRQMASK1_MC2BM MC13783_IRQSTAT1_MC2BI -#define MC13783_IRQMASK1_HSDETM MC13783_IRQSTAT1_HSDETI -#define MC13783_IRQMASK1_HSLM MC13783_IRQSTAT1_HSLI -#define MC13783_IRQMASK1_ALSPTHM MC13783_IRQSTAT1_ALSPTHI -#define MC13783_IRQMASK1_AHSSHORTM MC13783_IRQSTAT1_AHSSHORTI #define MC13XXX_REVISION 7 #define MC13XXX_REVISION_REVMETAL (0x07 << 0) @@ -189,45 +101,21 @@ EXPORT_SYMBOL(mc13xxx_reg_rmw); int mc13xxx_irq_mask(struct mc13xxx *mc13xxx, int irq) { - int ret; - unsigned int offmask = irq < 24 ? MC13XXX_IRQMASK0 : MC13XXX_IRQMASK1; - u32 irqbit = 1 << (irq < 24 ? irq : irq - 24); - u32 mask; - - if (irq < 0 || irq >= MC13XXX_NUM_IRQ) - return -EINVAL; - - ret = mc13xxx_reg_read(mc13xxx, offmask, &mask); - if (ret) - return ret; + int virq = regmap_irq_get_virq(mc13xxx->irq_data, irq); - if (mask & irqbit) - /* already masked */ - return 0; + disable_irq_nosync(virq); - return mc13xxx_reg_write(mc13xxx, offmask, mask | irqbit); + return 0; } EXPORT_SYMBOL(mc13xxx_irq_mask); int mc13xxx_irq_unmask(struct mc13xxx *mc13xxx, int irq) { - int ret; - unsigned int offmask = irq < 24 ? MC13XXX_IRQMASK0 : MC13XXX_IRQMASK1; - u32 irqbit = 1 << (irq < 24 ? irq : irq - 24); - u32 mask; - - if (irq < 0 || irq >= MC13XXX_NUM_IRQ) - return -EINVAL; + int virq = regmap_irq_get_virq(mc13xxx->irq_data, irq); - ret = mc13xxx_reg_read(mc13xxx, offmask, &mask); - if (ret) - return ret; + enable_irq(virq); - if (!(mask & irqbit)) - /* already unmasked */ - return 0; - - return mc13xxx_reg_write(mc13xxx, offmask, mask & ~irqbit); + return 0; } EXPORT_SYMBOL(mc13xxx_irq_unmask); @@ -239,7 +127,7 @@ int mc13xxx_irq_status(struct mc13xxx *mc13xxx, int irq, unsigned int offstat = irq < 24 ? MC13XXX_IRQSTAT0 : MC13XXX_IRQSTAT1; u32 irqbit = 1 << (irq < 24 ? irq : irq - 24); - if (irq < 0 || irq >= MC13XXX_NUM_IRQ) + if (irq < 0 || irq >= ARRAY_SIZE(mc13xxx->irqs)) return -EINVAL; if (enabled) { @@ -266,147 +154,26 @@ int mc13xxx_irq_status(struct mc13xxx *mc13xxx, int irq, } EXPORT_SYMBOL(mc13xxx_irq_status); -int mc13xxx_irq_ack(struct mc13xxx *mc13xxx, int irq) -{ - unsigned int offstat = irq < 24 ? MC13XXX_IRQSTAT0 : MC13XXX_IRQSTAT1; - unsigned int val = 1 << (irq < 24 ? irq : irq - 24); - - BUG_ON(irq < 0 || irq >= MC13XXX_NUM_IRQ); - - return mc13xxx_reg_write(mc13xxx, offstat, val); -} -EXPORT_SYMBOL(mc13xxx_irq_ack); - -int mc13xxx_irq_request_nounmask(struct mc13xxx *mc13xxx, int irq, - irq_handler_t handler, const char *name, void *dev) -{ - BUG_ON(!mutex_is_locked(&mc13xxx->lock)); - BUG_ON(!handler); - - if (irq < 0 || irq >= MC13XXX_NUM_IRQ) - return -EINVAL; - - if (mc13xxx->irqhandler[irq]) - return -EBUSY; - - mc13xxx->irqhandler[irq] = handler; - mc13xxx->irqdata[irq] = dev; - - return 0; -} -EXPORT_SYMBOL(mc13xxx_irq_request_nounmask); - int mc13xxx_irq_request(struct mc13xxx *mc13xxx, int irq, irq_handler_t handler, const char *name, void *dev) { - int ret; + int virq = regmap_irq_get_virq(mc13xxx->irq_data, irq); - ret = mc13xxx_irq_request_nounmask(mc13xxx, irq, handler, name, dev); - if (ret) - return ret; - - ret = mc13xxx_irq_unmask(mc13xxx, irq); - if (ret) { - mc13xxx->irqhandler[irq] = NULL; - mc13xxx->irqdata[irq] = NULL; - return ret; - } - - return 0; + return devm_request_threaded_irq(mc13xxx->dev, virq, NULL, handler, + 0, name, dev); } EXPORT_SYMBOL(mc13xxx_irq_request); int mc13xxx_irq_free(struct mc13xxx *mc13xxx, int irq, void *dev) { - int ret; - BUG_ON(!mutex_is_locked(&mc13xxx->lock)); + int virq = regmap_irq_get_virq(mc13xxx->irq_data, irq); - if (irq < 0 || irq >= MC13XXX_NUM_IRQ || !mc13xxx->irqhandler[irq] || - mc13xxx->irqdata[irq] != dev) - return -EINVAL; - - ret = mc13xxx_irq_mask(mc13xxx, irq); - if (ret) - return ret; - - mc13xxx->irqhandler[irq] = NULL; - mc13xxx->irqdata[irq] = NULL; + devm_free_irq(mc13xxx->dev, virq, dev); return 0; } EXPORT_SYMBOL(mc13xxx_irq_free); -static inline irqreturn_t mc13xxx_irqhandler(struct mc13xxx *mc13xxx, int irq) -{ - return mc13xxx->irqhandler[irq](irq, mc13xxx->irqdata[irq]); -} - -/* - * returns: number of handled irqs or negative error - * locking: holds mc13xxx->lock - */ -static int mc13xxx_irq_handle(struct mc13xxx *mc13xxx, - unsigned int offstat, unsigned int offmask, int baseirq) -{ - u32 stat, mask; - int ret = mc13xxx_reg_read(mc13xxx, offstat, &stat); - int num_handled = 0; - - if (ret) - return ret; - - ret = mc13xxx_reg_read(mc13xxx, offmask, &mask); - if (ret) - return ret; - - while (stat & ~mask) { - int irq = __ffs(stat & ~mask); - - stat &= ~(1 << irq); - - if (likely(mc13xxx->irqhandler[baseirq + irq])) { - irqreturn_t handled; - - handled = mc13xxx_irqhandler(mc13xxx, baseirq + irq); - if (handled == IRQ_HANDLED) - num_handled++; - } else { - dev_err(mc13xxx->dev, - "BUG: irq %u but no handler\n", - baseirq + irq); - - mask |= 1 << irq; - - ret = mc13xxx_reg_write(mc13xxx, offmask, mask); - } - } - - return num_handled; -} - -static irqreturn_t mc13xxx_irq_thread(int irq, void *data) -{ - struct mc13xxx *mc13xxx = data; - irqreturn_t ret; - int handled = 0; - - mc13xxx_lock(mc13xxx); - - ret = mc13xxx_irq_handle(mc13xxx, MC13XXX_IRQSTAT0, - MC13XXX_IRQMASK0, 0); - if (ret > 0) - handled = 1; - - ret = mc13xxx_irq_handle(mc13xxx, MC13XXX_IRQSTAT1, - MC13XXX_IRQMASK1, 24); - if (ret > 0) - handled = 1; - - mc13xxx_unlock(mc13xxx); - - return IRQ_RETVAL(handled); -} - #define maskval(reg, mask) (((reg) & (mask)) >> __ffs(mask)) static void mc13xxx_print_revision(struct mc13xxx *mc13xxx, u32 revision) { @@ -475,8 +242,6 @@ static irqreturn_t mc13xxx_handler_adcdone(int irq, void *data) { struct mc13xxx_adcdone_data *adcdone_data = data; - mc13xxx_irq_ack(adcdone_data->mc13xxx, irq); - complete_all(&adcdone_data->done); return IRQ_HANDLED; @@ -544,7 +309,6 @@ int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, unsigned int mode, dev_dbg(mc13xxx->dev, "%s: request irq\n", __func__); mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_ADCDONE, mc13xxx_handler_adcdone, __func__, &adcdone_data); - mc13xxx_irq_ack(mc13xxx, MC13XXX_IRQ_ADCDONE); mc13xxx_reg_write(mc13xxx, MC13XXX_ADC0, adc0); mc13xxx_reg_write(mc13xxx, MC13XXX_ADC1, adc1); @@ -599,7 +363,8 @@ static int mc13xxx_add_subdevice_pdata(struct mc13xxx *mc13xxx, if (!cell.name) return -ENOMEM; - return mfd_add_devices(mc13xxx->dev, -1, &cell, 1, NULL, 0, NULL); + return mfd_add_devices(mc13xxx->dev, -1, &cell, 1, NULL, 0, + regmap_irq_get_domain(mc13xxx->irq_data)); } static int mc13xxx_add_subdevice(struct mc13xxx *mc13xxx, const char *format) @@ -640,8 +405,8 @@ int mc13xxx_common_init(struct device *dev) { struct mc13xxx_platform_data *pdata = dev_get_platdata(dev); struct mc13xxx *mc13xxx = dev_get_drvdata(dev); - int ret; u32 revision; + int i, ret; mc13xxx->dev = dev; @@ -651,31 +416,32 @@ int mc13xxx_common_init(struct device *dev) mc13xxx->variant->print_revision(mc13xxx, revision); - /* mask all irqs */ - ret = mc13xxx_reg_write(mc13xxx, MC13XXX_IRQMASK0, 0x00ffffff); - if (ret) - return ret; + for (i = 0; i < ARRAY_SIZE(mc13xxx->irqs); i++) { + mc13xxx->irqs[i].reg_offset = i / MC13XXX_IRQ_PER_REG; + mc13xxx->irqs[i].mask = BIT(i % MC13XXX_IRQ_PER_REG); + } - ret = mc13xxx_reg_write(mc13xxx, MC13XXX_IRQMASK1, 0x00ffffff); + mc13xxx->irq_chip.name = dev_name(dev); + mc13xxx->irq_chip.status_base = MC13XXX_IRQSTAT0; + mc13xxx->irq_chip.mask_base = MC13XXX_IRQMASK0; + mc13xxx->irq_chip.ack_base = MC13XXX_IRQSTAT0; + mc13xxx->irq_chip.irq_reg_stride = MC13XXX_IRQSTAT1 - MC13XXX_IRQSTAT0; + mc13xxx->irq_chip.init_ack_masked = true; + mc13xxx->irq_chip.use_ack = true; + mc13xxx->irq_chip.num_regs = MC13XXX_IRQ_REG_CNT; + mc13xxx->irq_chip.irqs = mc13xxx->irqs; + mc13xxx->irq_chip.num_irqs = ARRAY_SIZE(mc13xxx->irqs); + + ret = regmap_add_irq_chip(mc13xxx->regmap, mc13xxx->irq, IRQF_ONESHOT, + 0, &mc13xxx->irq_chip, &mc13xxx->irq_data); if (ret) return ret; mutex_init(&mc13xxx->lock); - ret = request_threaded_irq(mc13xxx->irq, NULL, mc13xxx_irq_thread, - IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc13xxx", mc13xxx); - if (ret) - return ret; - if (mc13xxx_probe_flags_dt(mc13xxx) < 0 && pdata) mc13xxx->flags = pdata->flags; - if (mc13xxx->flags & MC13XXX_USE_ADC) - mc13xxx_add_subdevice(mc13xxx, "%s-adc"); - - if (mc13xxx->flags & MC13XXX_USE_RTC) - mc13xxx_add_subdevice(mc13xxx, "%s-rtc"); - if (pdata) { mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator", &pdata->regulators, sizeof(pdata->regulators)); @@ -699,6 +465,12 @@ int mc13xxx_common_init(struct device *dev) mc13xxx_add_subdevice(mc13xxx, "%s-ts"); } + if (mc13xxx->flags & MC13XXX_USE_ADC) + mc13xxx_add_subdevice(mc13xxx, "%s-adc"); + + if (mc13xxx->flags & MC13XXX_USE_RTC) + mc13xxx_add_subdevice(mc13xxx, "%s-rtc"); + return 0; } EXPORT_SYMBOL_GPL(mc13xxx_common_init); @@ -707,8 +479,8 @@ int mc13xxx_common_exit(struct device *dev) { struct mc13xxx *mc13xxx = dev_get_drvdata(dev); - free_irq(mc13xxx->irq, mc13xxx); mfd_remove_devices(dev); + regmap_del_irq_chip(mc13xxx->irq, mc13xxx->irq_data); mutex_destroy(&mc13xxx->lock); return 0; diff --git a/drivers/mfd/mc13xxx.h b/drivers/mfd/mc13xxx.h index ae7f1659f5d1..33677d1dcf66 100644 --- a/drivers/mfd/mc13xxx.h +++ b/drivers/mfd/mc13xxx.h @@ -13,7 +13,9 @@ #include #include -#define MC13XXX_NUMREGS 0x3f +#define MC13XXX_NUMREGS 0x3f +#define MC13XXX_IRQ_REG_CNT 2 +#define MC13XXX_IRQ_PER_REG 24 struct mc13xxx; @@ -33,13 +35,14 @@ struct mc13xxx { struct device *dev; const struct mc13xxx_variant *variant; + struct regmap_irq irqs[MC13XXX_IRQ_PER_REG * MC13XXX_IRQ_REG_CNT]; + struct regmap_irq_chip irq_chip; + struct regmap_irq_chip_data *irq_data; + struct mutex lock; int irq; int flags; - irq_handler_t irqhandler[MC13XXX_NUM_IRQ]; - void *irqdata[MC13XXX_NUM_IRQ]; - int adcflags; }; diff --git a/include/linux/mfd/mc13783.h b/include/linux/mfd/mc13783.h index a8eeda773a7b..4ff6137d8d67 100644 --- a/include/linux/mfd/mc13783.h +++ b/include/linux/mfd/mc13783.h @@ -86,6 +86,5 @@ #define MC13783_IRQ_HSL 43 #define MC13783_IRQ_ALSPTH 44 #define MC13783_IRQ_AHSSHORT 45 -#define MC13783_NUM_IRQ MC13XXX_NUM_IRQ #endif /* ifndef __LINUX_MFD_MC13783_H */ diff --git a/include/linux/mfd/mc13xxx.h b/include/linux/mfd/mc13xxx.h index d63b1d309106..638222e43e48 100644 --- a/include/linux/mfd/mc13xxx.h +++ b/include/linux/mfd/mc13xxx.h @@ -23,15 +23,10 @@ int mc13xxx_reg_rmw(struct mc13xxx *mc13xxx, unsigned int offset, int mc13xxx_irq_request(struct mc13xxx *mc13xxx, int irq, irq_handler_t handler, const char *name, void *dev); -int mc13xxx_irq_request_nounmask(struct mc13xxx *mc13xxx, int irq, - irq_handler_t handler, const char *name, void *dev); int mc13xxx_irq_free(struct mc13xxx *mc13xxx, int irq, void *dev); -int mc13xxx_irq_mask(struct mc13xxx *mc13xxx, int irq); -int mc13xxx_irq_unmask(struct mc13xxx *mc13xxx, int irq); int mc13xxx_irq_status(struct mc13xxx *mc13xxx, int irq, int *enabled, int *pending); -int mc13xxx_irq_ack(struct mc13xxx *mc13xxx, int irq); int mc13xxx_get_flags(struct mc13xxx *mc13xxx); @@ -39,6 +34,22 @@ int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, unsigned int mode, unsigned int channel, u8 ato, bool atox, unsigned int *sample); +/* Deprecated calls */ +static inline int mc13xxx_irq_ack(struct mc13xxx *mc13xxx, int irq) +{ + return 0; +} + +static inline int mc13xxx_irq_request_nounmask(struct mc13xxx *mc13xxx, int irq, + irq_handler_t handler, + const char *name, void *dev) +{ + return mc13xxx_irq_request(mc13xxx, irq, handler, name, dev); +} + +int mc13xxx_irq_mask(struct mc13xxx *mc13xxx, int irq); +int mc13xxx_irq_unmask(struct mc13xxx *mc13xxx, int irq); + #define MC13783_AUDIO_RX0 36 #define MC13783_AUDIO_RX1 37 #define MC13783_AUDIO_TX 38 @@ -68,8 +79,6 @@ int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, #define MC13XXX_IRQ_THWARNH 37 #define MC13XXX_IRQ_CLK 38 -#define MC13XXX_NUM_IRQ 46 - struct regulator_init_data; struct mc13xxx_regulator_init_data { -- cgit v1.2.3 From 54e8827d5f0e66d152ef63e7958030ef4880cd85 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Wed, 25 Jun 2014 16:14:44 +0900 Subject: mfd: sec-core: Add support for S2MPU02 device Add support for Samsung S2MPU02 PMIC device to the MFD sec-core driver. The S2MPU02 device includes PMIC/RTC/Clock devices. Signed-off-by: Chanwoo Choi Reviewed-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/sec-core.c | 46 ++++++--- drivers/mfd/sec-irq.c | 110 +++++++++++++++++--- include/linux/mfd/samsung/core.h | 1 + include/linux/mfd/samsung/irq.h | 24 +++++ include/linux/mfd/samsung/s2mpu02.h | 201 ++++++++++++++++++++++++++++++++++++ 5 files changed, 352 insertions(+), 30 deletions(-) create mode 100644 include/linux/mfd/samsung/s2mpu02.h (limited to 'include') diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c index be06d0abbf19..15ba847b3d23 100644 --- a/drivers/mfd/sec-core.c +++ b/drivers/mfd/sec-core.c @@ -89,6 +89,15 @@ static const struct mfd_cell s2mpa01_devs[] = { }, }; +static const struct mfd_cell s2mpu02_devs[] = { + { .name = "s2mpu02-pmic", }, + { .name = "s2mpu02-rtc", }, + { + .name = "s2mpu02-clk", + .of_compatible = "samsung,s2mpu02-clk", + } +}; + #ifdef CONFIG_OF static const struct of_device_id sec_dt_match[] = { { .compatible = "samsung,s5m8767-pmic", @@ -102,6 +111,9 @@ static const struct of_device_id sec_dt_match[] = { }, { .compatible = "samsung,s2mpa01-pmic", .data = (void *)S2MPA01, + }, { + .compatible = "samsung,s2mpu02-pmic", + .data = (void *)S2MPU02, }, { /* Sentinel */ }, @@ -250,9 +262,10 @@ static int sec_pmic_probe(struct i2c_client *i2c, { struct sec_platform_data *pdata = dev_get_platdata(&i2c->dev); const struct regmap_config *regmap; + const struct mfd_cell *sec_devs; struct sec_pmic_dev *sec_pmic; unsigned long device_type; - int ret; + int ret, num_sec_devs; sec_pmic = devm_kzalloc(&i2c->dev, sizeof(struct sec_pmic_dev), GFP_KERNEL); @@ -319,34 +332,39 @@ static int sec_pmic_probe(struct i2c_client *i2c, switch (sec_pmic->device_type) { case S5M8751X: - ret = mfd_add_devices(sec_pmic->dev, -1, s5m8751_devs, - ARRAY_SIZE(s5m8751_devs), NULL, 0, NULL); + sec_devs = s5m8751_devs; + num_sec_devs = ARRAY_SIZE(s5m8751_devs); break; case S5M8763X: - ret = mfd_add_devices(sec_pmic->dev, -1, s5m8763_devs, - ARRAY_SIZE(s5m8763_devs), NULL, 0, NULL); + sec_devs = s5m8763_devs; + num_sec_devs = ARRAY_SIZE(s5m8763_devs); break; case S5M8767X: - ret = mfd_add_devices(sec_pmic->dev, -1, s5m8767_devs, - ARRAY_SIZE(s5m8767_devs), NULL, 0, NULL); + sec_devs = s5m8767_devs; + num_sec_devs = ARRAY_SIZE(s5m8767_devs); break; case S2MPA01: - ret = mfd_add_devices(sec_pmic->dev, -1, s2mpa01_devs, - ARRAY_SIZE(s2mpa01_devs), NULL, 0, NULL); + sec_devs = s2mpa01_devs; + num_sec_devs = ARRAY_SIZE(s2mpa01_devs); break; case S2MPS11X: - ret = mfd_add_devices(sec_pmic->dev, -1, s2mps11_devs, - ARRAY_SIZE(s2mps11_devs), NULL, 0, NULL); + sec_devs = s2mps11_devs; + num_sec_devs = ARRAY_SIZE(s2mps11_devs); break; case S2MPS14X: - ret = mfd_add_devices(sec_pmic->dev, -1, s2mps14_devs, - ARRAY_SIZE(s2mps14_devs), NULL, 0, NULL); + sec_devs = s2mps14_devs; + num_sec_devs = ARRAY_SIZE(s2mps14_devs); + break; + case S2MPU02: + sec_devs = s2mpu02_devs; + num_sec_devs = ARRAY_SIZE(s2mpu02_devs); break; default: /* If this happens the probe function is problem */ BUG(); } - + ret = mfd_add_devices(sec_pmic->dev, -1, sec_devs, num_sec_devs, NULL, + 0, NULL); if (ret) goto err_mfd; diff --git a/drivers/mfd/sec-irq.c b/drivers/mfd/sec-irq.c index 654e2c1dbf7a..f9a57869e3ec 100644 --- a/drivers/mfd/sec-irq.c +++ b/drivers/mfd/sec-irq.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -161,6 +162,77 @@ static const struct regmap_irq s2mps14_irqs[] = { }, }; +static const struct regmap_irq s2mpu02_irqs[] = { + [S2MPU02_IRQ_PWRONF] = { + .reg_offset = 0, + .mask = S2MPS11_IRQ_PWRONF_MASK, + }, + [S2MPU02_IRQ_PWRONR] = { + .reg_offset = 0, + .mask = S2MPS11_IRQ_PWRONR_MASK, + }, + [S2MPU02_IRQ_JIGONBF] = { + .reg_offset = 0, + .mask = S2MPS11_IRQ_JIGONBF_MASK, + }, + [S2MPU02_IRQ_JIGONBR] = { + .reg_offset = 0, + .mask = S2MPS11_IRQ_JIGONBR_MASK, + }, + [S2MPU02_IRQ_ACOKBF] = { + .reg_offset = 0, + .mask = S2MPS11_IRQ_ACOKBF_MASK, + }, + [S2MPU02_IRQ_ACOKBR] = { + .reg_offset = 0, + .mask = S2MPS11_IRQ_ACOKBR_MASK, + }, + [S2MPU02_IRQ_PWRON1S] = { + .reg_offset = 0, + .mask = S2MPS11_IRQ_PWRON1S_MASK, + }, + [S2MPU02_IRQ_MRB] = { + .reg_offset = 0, + .mask = S2MPS11_IRQ_MRB_MASK, + }, + [S2MPU02_IRQ_RTC60S] = { + .reg_offset = 1, + .mask = S2MPS11_IRQ_RTC60S_MASK, + }, + [S2MPU02_IRQ_RTCA1] = { + .reg_offset = 1, + .mask = S2MPS11_IRQ_RTCA1_MASK, + }, + [S2MPU02_IRQ_RTCA0] = { + .reg_offset = 1, + .mask = S2MPS11_IRQ_RTCA0_MASK, + }, + [S2MPU02_IRQ_SMPL] = { + .reg_offset = 1, + .mask = S2MPS11_IRQ_SMPL_MASK, + }, + [S2MPU02_IRQ_RTC1S] = { + .reg_offset = 1, + .mask = S2MPS11_IRQ_RTC1S_MASK, + }, + [S2MPU02_IRQ_WTSR] = { + .reg_offset = 1, + .mask = S2MPS11_IRQ_WTSR_MASK, + }, + [S2MPU02_IRQ_INT120C] = { + .reg_offset = 2, + .mask = S2MPS11_IRQ_INT120C_MASK, + }, + [S2MPU02_IRQ_INT140C] = { + .reg_offset = 2, + .mask = S2MPS11_IRQ_INT140C_MASK, + }, + [S2MPU02_IRQ_TSD] = { + .reg_offset = 2, + .mask = S2MPS14_IRQ_TSD_MASK, + }, +}; + static const struct regmap_irq s5m8767_irqs[] = { [S5M8767_IRQ_PWRR] = { .reg_offset = 0, @@ -327,6 +399,16 @@ static const struct regmap_irq_chip s2mps14_irq_chip = { .ack_base = S2MPS14_REG_INT1, }; +static const struct regmap_irq_chip s2mpu02_irq_chip = { + .name = "s2mpu02", + .irqs = s2mpu02_irqs, + .num_irqs = ARRAY_SIZE(s2mpu02_irqs), + .num_regs = 3, + .status_base = S2MPU02_REG_INT1, + .mask_base = S2MPU02_REG_INT1M, + .ack_base = S2MPU02_REG_INT1, +}; + static const struct regmap_irq_chip s5m8767_irq_chip = { .name = "s5m8767", .irqs = s5m8767_irqs, @@ -351,6 +433,7 @@ int sec_irq_init(struct sec_pmic_dev *sec_pmic) { int ret = 0; int type = sec_pmic->device_type; + const struct regmap_irq_chip *sec_irq_chip; if (!sec_pmic->irq) { dev_warn(sec_pmic->dev, @@ -361,28 +444,19 @@ int sec_irq_init(struct sec_pmic_dev *sec_pmic) switch (type) { case S5M8763X: - ret = regmap_add_irq_chip(sec_pmic->regmap_pmic, sec_pmic->irq, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - sec_pmic->irq_base, &s5m8763_irq_chip, - &sec_pmic->irq_data); + sec_irq_chip = &s5m8763_irq_chip; break; case S5M8767X: - ret = regmap_add_irq_chip(sec_pmic->regmap_pmic, sec_pmic->irq, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - sec_pmic->irq_base, &s5m8767_irq_chip, - &sec_pmic->irq_data); + sec_irq_chip = &s5m8767_irq_chip; break; case S2MPS11X: - ret = regmap_add_irq_chip(sec_pmic->regmap_pmic, sec_pmic->irq, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - sec_pmic->irq_base, &s2mps11_irq_chip, - &sec_pmic->irq_data); + sec_irq_chip = &s2mps11_irq_chip; break; case S2MPS14X: - ret = regmap_add_irq_chip(sec_pmic->regmap_pmic, sec_pmic->irq, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - sec_pmic->irq_base, &s2mps14_irq_chip, - &sec_pmic->irq_data); + sec_irq_chip = &s2mps14_irq_chip; + break; + case S2MPU02: + sec_irq_chip = &s2mpu02_irq_chip; break; default: dev_err(sec_pmic->dev, "Unknown device type %lu\n", @@ -390,6 +464,10 @@ int sec_irq_init(struct sec_pmic_dev *sec_pmic) return -EINVAL; } + ret = regmap_add_irq_chip(sec_pmic->regmap_pmic, sec_pmic->irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + sec_pmic->irq_base, sec_irq_chip, + &sec_pmic->irq_data); if (ret != 0) { dev_err(sec_pmic->dev, "Failed to register IRQ chip: %d\n", ret); return ret; diff --git a/include/linux/mfd/samsung/core.h b/include/linux/mfd/samsung/core.h index 47d84242940b..b5f73de81aad 100644 --- a/include/linux/mfd/samsung/core.h +++ b/include/linux/mfd/samsung/core.h @@ -21,6 +21,7 @@ enum sec_device_type { S2MPA01, S2MPS11X, S2MPS14X, + S2MPU02, }; /** diff --git a/include/linux/mfd/samsung/irq.h b/include/linux/mfd/samsung/irq.h index 1224f447356b..f35af7361b60 100644 --- a/include/linux/mfd/samsung/irq.h +++ b/include/linux/mfd/samsung/irq.h @@ -129,6 +129,30 @@ enum s2mps14_irq { S2MPS14_IRQ_NR, }; +enum s2mpu02_irq { + S2MPU02_IRQ_PWRONF, + S2MPU02_IRQ_PWRONR, + S2MPU02_IRQ_JIGONBF, + S2MPU02_IRQ_JIGONBR, + S2MPU02_IRQ_ACOKBF, + S2MPU02_IRQ_ACOKBR, + S2MPU02_IRQ_PWRON1S, + S2MPU02_IRQ_MRB, + + S2MPU02_IRQ_RTC60S, + S2MPU02_IRQ_RTCA1, + S2MPU02_IRQ_RTCA0, + S2MPU02_IRQ_SMPL, + S2MPU02_IRQ_RTC1S, + S2MPU02_IRQ_WTSR, + + S2MPU02_IRQ_INT120C, + S2MPU02_IRQ_INT140C, + S2MPU02_IRQ_TSD, + + S2MPU02_IRQ_NR, +}; + /* Masks for interrupts are the same as in s2mps11 */ #define S2MPS14_IRQ_TSD_MASK (1 << 2) diff --git a/include/linux/mfd/samsung/s2mpu02.h b/include/linux/mfd/samsung/s2mpu02.h new file mode 100644 index 000000000000..47ae9bc583a7 --- /dev/null +++ b/include/linux/mfd/samsung/s2mpu02.h @@ -0,0 +1,201 @@ +/* + * s2mpu02.h + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd + * http://www.samsung.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License 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. + * + */ + +#ifndef __LINUX_MFD_S2MPU02_H +#define __LINUX_MFD_S2MPU02_H + +/* S2MPU02 registers */ +enum S2MPU02_reg { + S2MPU02_REG_ID, + S2MPU02_REG_INT1, + S2MPU02_REG_INT2, + S2MPU02_REG_INT3, + S2MPU02_REG_INT1M, + S2MPU02_REG_INT2M, + S2MPU02_REG_INT3M, + S2MPU02_REG_ST1, + S2MPU02_REG_ST2, + S2MPU02_REG_PWRONSRC, + S2MPU02_REG_OFFSRC, + S2MPU02_REG_BU_CHG, + S2MPU02_REG_RTCCTRL, + S2MPU02_REG_PMCTRL1, + S2MPU02_REG_RSVD1, + S2MPU02_REG_RSVD2, + S2MPU02_REG_RSVD3, + S2MPU02_REG_RSVD4, + S2MPU02_REG_RSVD5, + S2MPU02_REG_RSVD6, + S2MPU02_REG_RSVD7, + S2MPU02_REG_WRSTEN, + S2MPU02_REG_RSVD8, + S2MPU02_REG_RSVD9, + S2MPU02_REG_RSVD10, + S2MPU02_REG_B1CTRL1, + S2MPU02_REG_B1CTRL2, + S2MPU02_REG_B2CTRL1, + S2MPU02_REG_B2CTRL2, + S2MPU02_REG_B3CTRL1, + S2MPU02_REG_B3CTRL2, + S2MPU02_REG_B4CTRL1, + S2MPU02_REG_B4CTRL2, + S2MPU02_REG_B5CTRL1, + S2MPU02_REG_B5CTRL2, + S2MPU02_REG_B5CTRL3, + S2MPU02_REG_B5CTRL4, + S2MPU02_REG_B5CTRL5, + S2MPU02_REG_B6CTRL1, + S2MPU02_REG_B6CTRL2, + S2MPU02_REG_B7CTRL1, + S2MPU02_REG_B7CTRL2, + S2MPU02_REG_RAMP1, + S2MPU02_REG_RAMP2, + S2MPU02_REG_L1CTRL, + S2MPU02_REG_L2CTRL1, + S2MPU02_REG_L2CTRL2, + S2MPU02_REG_L2CTRL3, + S2MPU02_REG_L2CTRL4, + S2MPU02_REG_L3CTRL, + S2MPU02_REG_L4CTRL, + S2MPU02_REG_L5CTRL, + S2MPU02_REG_L6CTRL, + S2MPU02_REG_L7CTRL, + S2MPU02_REG_L8CTRL, + S2MPU02_REG_L9CTRL, + S2MPU02_REG_L10CTRL, + S2MPU02_REG_L11CTRL, + S2MPU02_REG_L12CTRL, + S2MPU02_REG_L13CTRL, + S2MPU02_REG_L14CTRL, + S2MPU02_REG_L15CTRL, + S2MPU02_REG_L16CTRL, + S2MPU02_REG_L17CTRL, + S2MPU02_REG_L18CTRL, + S2MPU02_REG_L19CTRL, + S2MPU02_REG_L20CTRL, + S2MPU02_REG_L21CTRL, + S2MPU02_REG_L22CTRL, + S2MPU02_REG_L23CTRL, + S2MPU02_REG_L24CTRL, + S2MPU02_REG_L25CTRL, + S2MPU02_REG_L26CTRL, + S2MPU02_REG_L27CTRL, + S2MPU02_REG_L28CTRL, + S2MPU02_REG_LDODSCH1, + S2MPU02_REG_LDODSCH2, + S2MPU02_REG_LDODSCH3, + S2MPU02_REG_LDODSCH4, + S2MPU02_REG_SELMIF, + S2MPU02_REG_RSVD11, + S2MPU02_REG_RSVD12, + S2MPU02_REG_RSVD13, + S2MPU02_REG_DVSSEL, + S2MPU02_REG_DVSPTR, + S2MPU02_REG_DVSDATA, +}; + +/* S2MPU02 regulator ids */ +enum S2MPU02_regulators { + S2MPU02_LDO1, + S2MPU02_LDO2, + S2MPU02_LDO3, + S2MPU02_LDO4, + S2MPU02_LDO5, + S2MPU02_LDO6, + S2MPU02_LDO7, + S2MPU02_LDO8, + S2MPU02_LDO9, + S2MPU02_LDO10, + S2MPU02_LDO11, + S2MPU02_LDO12, + S2MPU02_LDO13, + S2MPU02_LDO14, + S2MPU02_LDO15, + S2MPU02_LDO16, + S2MPU02_LDO17, + S2MPU02_LDO18, + S2MPU02_LDO19, + S2MPU02_LDO20, + S2MPU02_LDO21, + S2MPU02_LDO22, + S2MPU02_LDO23, + S2MPU02_LDO24, + S2MPU02_LDO25, + S2MPU02_LDO26, + S2MPU02_LDO27, + S2MPU02_LDO28, + S2MPU02_BUCK1, + S2MPU02_BUCK2, + S2MPU02_BUCK3, + S2MPU02_BUCK4, + S2MPU02_BUCK5, + S2MPU02_BUCK6, + S2MPU02_BUCK7, + + S2MPU02_REGULATOR_MAX, +}; + +/* Regulator constraints for BUCKx */ +#define S2MPU02_BUCK1234_MIN_600MV 600000 +#define S2MPU02_BUCK5_MIN_1081_25MV 1081250 +#define S2MPU02_BUCK6_MIN_1700MV 1700000 +#define S2MPU02_BUCK7_MIN_900MV 900000 + +#define S2MPU02_BUCK1234_STEP_6_25MV 6250 +#define S2MPU02_BUCK5_STEP_6_25MV 6250 +#define S2MPU02_BUCK6_STEP_2_50MV 2500 +#define S2MPU02_BUCK7_STEP_6_25MV 6250 + +#define S2MPU02_BUCK1234_START_SEL 0x00 +#define S2MPU02_BUCK5_START_SEL 0x4D +#define S2MPU02_BUCK6_START_SEL 0x28 +#define S2MPU02_BUCK7_START_SEL 0x30 + +#define S2MPU02_BUCK_RAMP_DELAY 12500 + +/* Regulator constraints for different types of LDOx */ +#define S2MPU02_LDO_MIN_900MV 900000 +#define S2MPU02_LDO_MIN_1050MV 1050000 +#define S2MPU02_LDO_MIN_1600MV 1600000 +#define S2MPU02_LDO_STEP_12_5MV 12500 +#define S2MPU02_LDO_STEP_25MV 25000 +#define S2MPU02_LDO_STEP_50MV 50000 + +#define S2MPU02_LDO_GROUP1_START_SEL 0x8 +#define S2MPU02_LDO_GROUP2_START_SEL 0xA +#define S2MPU02_LDO_GROUP3_START_SEL 0x10 + +#define S2MPU02_LDO_VSEL_MASK 0x3F +#define S2MPU02_BUCK_VSEL_MASK 0xFF +#define S2MPU02_ENABLE_MASK (0x03 << S2MPU02_ENABLE_SHIFT) +#define S2MPU02_ENABLE_SHIFT 6 + +/* On/Off controlled by PWREN */ +#define S2MPU02_ENABLE_SUSPEND (0x01 << S2MPU02_ENABLE_SHIFT) +#define S2MPU02_DISABLE_SUSPEND (0x11 << S2MPU02_ENABLE_SHIFT) +#define S2MPU02_LDO_N_VOLTAGES (S2MPU02_LDO_VSEL_MASK + 1) +#define S2MPU02_BUCK_N_VOLTAGES (S2MPU02_BUCK_VSEL_MASK + 1) + +/* RAMP delay for BUCK1234*/ +#define S2MPU02_BUCK1_RAMP_SHIFT 6 +#define S2MPU02_BUCK2_RAMP_SHIFT 4 +#define S2MPU02_BUCK3_RAMP_SHIFT 2 +#define S2MPU02_BUCK4_RAMP_SHIFT 0 +#define S2MPU02_BUCK1234_RAMP_MASK 0x3 + +#endif /* __LINUX_MFD_S2MPU02_H */ -- cgit v1.2.3 From ee98662ec914a23ab826b0c83797aa9414f737bc Mon Sep 17 00:00:00 2001 From: Bill Richardson Date: Wed, 18 Jun 2014 11:13:58 -0700 Subject: mfd: cros_ec: Fix the comment on cros_ec_remove() This comment was incorrect, so update it. Signed-off-by: Bill Richardson Signed-off-by: Simon Glass Signed-off-by: Doug Anderson Signed-off-by: Lee Jones --- include/linux/mfd/cros_ec.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h index 887ef4f7bef7..7e9fe6e98d2f 100644 --- a/include/linux/mfd/cros_ec.h +++ b/include/linux/mfd/cros_ec.h @@ -148,8 +148,7 @@ int cros_ec_prepare_tx(struct cros_ec_device *ec_dev, /** * cros_ec_remove - Remove a ChromeOS EC * - * Call this to deregister a ChromeOS EC. After this you should call - * cros_ec_free(). + * Call this to deregister a ChromeOS EC, then clean up any private data. * * @ec_dev: Device to register * @return 0 if ok, -ve on error -- cgit v1.2.3 From 2ce701ae4e351d9407ec0b30f5f9dd56b6de4292 Mon Sep 17 00:00:00 2001 From: Bill Richardson Date: Wed, 18 Jun 2014 11:13:59 -0700 Subject: mfd: cros_ec: Allow static din/dout buffers with cros_ec_register() The lower-level driver may want to provide its own buffers. If so, there's no need to allocate new ones. This already happens to work just fine (since we check for size of 0 and use devm allocation), but it's good to document it. [dianders: Resolved conflicts; documented that no code changes needed on mainline] Signed-off-by: Bill Richardson Signed-off-by: Doug Anderson Reviewed-by: Simon Glass Signed-off-by: Lee Jones --- include/linux/mfd/cros_ec.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h index 7e9fe6e98d2f..2ee3190b691c 100644 --- a/include/linux/mfd/cros_ec.h +++ b/include/linux/mfd/cros_ec.h @@ -68,8 +68,8 @@ struct cros_ec_msg { * We use this alignment to keep ARM and x86 happy. Probably word * alignment would be OK, there might be a small performance advantage * to using dword. - * @din_size: size of din buffer - * @dout_size: size of dout buffer + * @din_size: size of din buffer to allocate (zero to use static din) + * @dout_size: size of dout buffer to allocate (zero to use static dout) * @command_send: send a command * @command_recv: receive a command * @ec_name: name of EC device (e.g. 'chromeos-ec') -- cgit v1.2.3 From 7e6cb5b4dbbc4b1d98289c88d0bc4092cac328be Mon Sep 17 00:00:00 2001 From: Bill Richardson Date: Wed, 18 Jun 2014 11:14:00 -0700 Subject: mfd: cros_ec: Tweak struct cros_ec_device for clarity The members of struct cros_ec_device were improperly commented, and intermixed the private and public sections. This is just cleanup to make it more obvious what goes with what. [dianders: left lock in the structure but gave it the name that will eventually be used.] Signed-off-by: Bill Richardson Signed-off-by: Doug Anderson Signed-off-by: Lee Jones --- drivers/mfd/cros_ec.c | 2 +- drivers/mfd/cros_ec_i2c.c | 4 +-- drivers/mfd/cros_ec_spi.c | 10 +++---- include/linux/mfd/cros_ec.h | 65 ++++++++++++++++++++++++--------------------- 4 files changed, 43 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index 38fe9bf0d169..04e053c71cc6 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -57,7 +57,7 @@ static int cros_ec_command_sendrecv(struct cros_ec_device *ec_dev, msg.in_buf = in_buf; msg.in_len = in_len; - return ec_dev->command_xfer(ec_dev, &msg); + return ec_dev->cmd_xfer(ec_dev, &msg); } static int cros_ec_command_recv(struct cros_ec_device *ec_dev, diff --git a/drivers/mfd/cros_ec_i2c.c b/drivers/mfd/cros_ec_i2c.c index 4f71be99a183..777e529abb16 100644 --- a/drivers/mfd/cros_ec_i2c.c +++ b/drivers/mfd/cros_ec_i2c.c @@ -29,7 +29,7 @@ static inline struct cros_ec_device *to_ec_dev(struct device *dev) return i2c_get_clientdata(client); } -static int cros_ec_command_xfer(struct cros_ec_device *ec_dev, +static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev, struct cros_ec_msg *msg) { struct i2c_client *client = ec_dev->priv; @@ -136,7 +136,7 @@ static int cros_ec_i2c_probe(struct i2c_client *client, ec_dev->dev = dev; ec_dev->priv = client; ec_dev->irq = client->irq; - ec_dev->command_xfer = cros_ec_command_xfer; + ec_dev->cmd_xfer = cros_ec_cmd_xfer_i2c; ec_dev->ec_name = client->name; ec_dev->phys_name = client->adapter->name; ec_dev->parent = &client->dev; diff --git a/drivers/mfd/cros_ec_spi.c b/drivers/mfd/cros_ec_spi.c index 1fcc65ecad0e..6c3075fb5dc7 100644 --- a/drivers/mfd/cros_ec_spi.c +++ b/drivers/mfd/cros_ec_spi.c @@ -73,7 +73,7 @@ * if no record * @end_of_msg_delay: used to set the delay_usecs on the spi_transfer that * is sent when we want to turn off CS at the end of a transaction. - * @lock: mutex to ensure only one user of cros_ec_command_spi_xfer at a time + * @lock: mutex to ensure only one user of cros_ec_cmd_xfer_spi at a time */ struct cros_ec_spi { struct spi_device *spi; @@ -210,13 +210,13 @@ static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev, } /** - * cros_ec_command_spi_xfer - Transfer a message over SPI and receive the reply + * cros_ec_cmd_xfer_spi - Transfer a message over SPI and receive the reply * * @ec_dev: ChromeOS EC device * @ec_msg: Message to transfer */ -static int cros_ec_command_spi_xfer(struct cros_ec_device *ec_dev, - struct cros_ec_msg *ec_msg) +static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev, + struct cros_ec_msg *ec_msg) { struct cros_ec_spi *ec_spi = ec_dev->priv; struct spi_transfer trans; @@ -368,7 +368,7 @@ static int cros_ec_spi_probe(struct spi_device *spi) ec_dev->dev = dev; ec_dev->priv = ec_spi; ec_dev->irq = spi->irq; - ec_dev->command_xfer = cros_ec_command_spi_xfer; + ec_dev->cmd_xfer = cros_ec_cmd_xfer_spi; ec_dev->ec_name = ec_spi->spi->modalias; ec_dev->phys_name = dev_name(&ec_spi->spi->dev); ec_dev->parent = &ec_spi->spi->dev; diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h index 2ee3190b691c..79a35857cc9e 100644 --- a/include/linux/mfd/cros_ec.h +++ b/include/linux/mfd/cros_ec.h @@ -16,7 +16,9 @@ #ifndef __LINUX_MFD_CROS_EC_H #define __LINUX_MFD_CROS_EC_H +#include #include +#include /* * Command interface between EC and AP, for LPC, I2C and SPI interfaces. @@ -55,34 +57,53 @@ struct cros_ec_msg { /** * struct cros_ec_device - Information about a ChromeOS EC device * + * @ec_name: name of EC device (e.g. 'chromeos-ec') + * @phys_name: name of physical comms layer (e.g. 'i2c-4') + * @dev: Device pointer + * @was_wake_device: true if this device was set to wake the system from + * sleep at the last suspend + * @event_notifier: interrupt event notifier for transport devices + * @command_send: send a command + * @command_recv: receive a response + * @command_sendrecv: send a command and receive a response + * * @name: Name of this EC interface * @priv: Private data * @irq: Interrupt to use - * @din: input buffer (from EC) - * @dout: output buffer (to EC) + * @din: input buffer (for data from EC) + * @dout: output buffer (for data to EC) * \note * These two buffers will always be dword-aligned and include enough * space for up to 7 word-alignment bytes also, so we can ensure that * the body of the message is always dword-aligned (64-bit). - * * We use this alignment to keep ARM and x86 happy. Probably word * alignment would be OK, there might be a small performance advantage * to using dword. * @din_size: size of din buffer to allocate (zero to use static din) * @dout_size: size of dout buffer to allocate (zero to use static dout) - * @command_send: send a command - * @command_recv: receive a command - * @ec_name: name of EC device (e.g. 'chromeos-ec') - * @phys_name: name of physical comms layer (e.g. 'i2c-4') * @parent: pointer to parent device (e.g. i2c or spi device) - * @dev: Device pointer - * dev_lock: Lock to prevent concurrent access * @wake_enabled: true if this device can wake the system from sleep - * @was_wake_device: true if this device was set to wake the system from - * sleep at the last suspend - * @event_notifier: interrupt event notifier for transport devices + * @lock: one transaction at a time + * @cmd_xfer: low-level channel to the EC */ struct cros_ec_device { + + /* These are used by other drivers that want to talk to the EC */ + const char *ec_name; + const char *phys_name; + struct device *dev; + bool was_wake_device; + struct class *cros_class; + struct blocking_notifier_head event_notifier; + int (*command_send)(struct cros_ec_device *ec, + uint16_t cmd, void *out_buf, int out_len); + int (*command_recv)(struct cros_ec_device *ec, + uint16_t cmd, void *in_buf, int in_len); + int (*command_sendrecv)(struct cros_ec_device *ec, + uint16_t cmd, void *out_buf, int out_len, + void *in_buf, int in_len); + + /* These are used to implement the platform-specific interface */ const char *name; void *priv; int irq; @@ -90,26 +111,10 @@ struct cros_ec_device { uint8_t *dout; int din_size; int dout_size; - int (*command_send)(struct cros_ec_device *ec, - uint16_t cmd, void *out_buf, int out_len); - int (*command_recv)(struct cros_ec_device *ec, - uint16_t cmd, void *in_buf, int in_len); - int (*command_sendrecv)(struct cros_ec_device *ec, - uint16_t cmd, void *out_buf, int out_len, - void *in_buf, int in_len); - int (*command_xfer)(struct cros_ec_device *ec, - struct cros_ec_msg *msg); - - const char *ec_name; - const char *phys_name; struct device *parent; - - /* These are --private-- fields - do not assign */ - struct device *dev; - struct mutex dev_lock; bool wake_enabled; - bool was_wake_device; - struct blocking_notifier_head event_notifier; + struct mutex lock; + int (*cmd_xfer)(struct cros_ec_device *ec, struct cros_ec_msg *msg); }; /** -- cgit v1.2.3 From 5d4773e27e8ab37491767a6ef99ffd7100fe6341 Mon Sep 17 00:00:00 2001 From: Bill Richardson Date: Wed, 18 Jun 2014 11:14:02 -0700 Subject: mfd: cros_ec: Use struct cros_ec_command to communicate with the EC This is some internal structure reorganization / renaming to prepare for future patches that will add a userspace API to cros_ec. There should be no visible changes. Signed-off-by: Bill Richardson Signed-off-by: Doug Anderson Signed-off-by: Lee Jones --- drivers/mfd/cros_ec.c | 28 ++++++++++++++-------------- drivers/mfd/cros_ec_i2c.c | 24 ++++++++++++------------ drivers/mfd/cros_ec_spi.c | 16 ++++++++-------- include/linux/mfd/cros_ec.h | 35 ++++++++++++++++++----------------- 4 files changed, 52 insertions(+), 51 deletions(-) (limited to 'include') diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index 04e053c71cc6..2e86c282f0b4 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -25,22 +25,22 @@ #include int cros_ec_prepare_tx(struct cros_ec_device *ec_dev, - struct cros_ec_msg *msg) + struct cros_ec_command *msg) { uint8_t *out; int csum, i; - BUG_ON(msg->out_len > EC_PROTO2_MAX_PARAM_SIZE); + BUG_ON(msg->outsize > EC_PROTO2_MAX_PARAM_SIZE); out = ec_dev->dout; out[0] = EC_CMD_VERSION0 + msg->version; - out[1] = msg->cmd; - out[2] = msg->out_len; + out[1] = msg->command; + out[2] = msg->outsize; csum = out[0] + out[1] + out[2]; - for (i = 0; i < msg->out_len; i++) - csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->out_buf[i]; - out[EC_MSG_TX_HEADER_BYTES + msg->out_len] = (uint8_t)(csum & 0xff); + for (i = 0; i < msg->outsize; i++) + csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->outdata[i]; + out[EC_MSG_TX_HEADER_BYTES + msg->outsize] = (uint8_t)(csum & 0xff); - return EC_MSG_TX_PROTO_BYTES + msg->out_len; + return EC_MSG_TX_PROTO_BYTES + msg->outsize; } EXPORT_SYMBOL(cros_ec_prepare_tx); @@ -48,14 +48,14 @@ static int cros_ec_command_sendrecv(struct cros_ec_device *ec_dev, uint16_t cmd, void *out_buf, int out_len, void *in_buf, int in_len) { - struct cros_ec_msg msg; + struct cros_ec_command msg; msg.version = cmd >> 8; - msg.cmd = cmd & 0xff; - msg.out_buf = out_buf; - msg.out_len = out_len; - msg.in_buf = in_buf; - msg.in_len = in_len; + msg.command = cmd & 0xff; + msg.outdata = out_buf; + msg.outsize = out_len; + msg.indata = in_buf; + msg.insize = in_len; return ec_dev->cmd_xfer(ec_dev, &msg); } diff --git a/drivers/mfd/cros_ec_i2c.c b/drivers/mfd/cros_ec_i2c.c index 777e529abb16..37ed12f99560 100644 --- a/drivers/mfd/cros_ec_i2c.c +++ b/drivers/mfd/cros_ec_i2c.c @@ -30,7 +30,7 @@ static inline struct cros_ec_device *to_ec_dev(struct device *dev) } static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev, - struct cros_ec_msg *msg) + struct cros_ec_command *msg) { struct i2c_client *client = ec_dev->priv; int ret = -ENOMEM; @@ -50,7 +50,7 @@ static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev, * allocate larger packet (one byte for checksum, one byte for * length, and one for result code) */ - packet_len = msg->in_len + 3; + packet_len = msg->insize + 3; in_buf = kzalloc(packet_len, GFP_KERNEL); if (!in_buf) goto done; @@ -61,7 +61,7 @@ static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev, * allocate larger packet (one byte for checksum, one for * command code, one for length, and one for command version) */ - packet_len = msg->out_len + 4; + packet_len = msg->outsize + 4; out_buf = kzalloc(packet_len, GFP_KERNEL); if (!out_buf) goto done; @@ -69,16 +69,16 @@ static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev, i2c_msg[0].buf = (char *)out_buf; out_buf[0] = EC_CMD_VERSION0 + msg->version; - out_buf[1] = msg->cmd; - out_buf[2] = msg->out_len; + out_buf[1] = msg->command; + out_buf[2] = msg->outsize; /* copy message payload and compute checksum */ sum = out_buf[0] + out_buf[1] + out_buf[2]; - for (i = 0; i < msg->out_len; i++) { - out_buf[3 + i] = msg->out_buf[i]; + for (i = 0; i < msg->outsize; i++) { + out_buf[3 + i] = msg->outdata[i]; sum += out_buf[3 + i]; } - out_buf[3 + msg->out_len] = sum; + out_buf[3 + msg->outsize] = sum; /* send command to EC and read answer */ ret = i2c_transfer(client->adapter, i2c_msg, 2); @@ -94,20 +94,20 @@ static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev, /* check response error code */ if (i2c_msg[1].buf[0]) { dev_warn(ec_dev->dev, "command 0x%02x returned an error %d\n", - msg->cmd, i2c_msg[1].buf[0]); + msg->command, i2c_msg[1].buf[0]); ret = -EINVAL; goto done; } /* copy response packet payload and compute checksum */ sum = in_buf[0] + in_buf[1]; - for (i = 0; i < msg->in_len; i++) { - msg->in_buf[i] = in_buf[2 + i]; + for (i = 0; i < msg->insize; i++) { + msg->indata[i] = in_buf[2 + i]; sum += in_buf[2 + i]; } dev_dbg(ec_dev->dev, "packet: %*ph, sum = %02x\n", i2c_msg[1].len, in_buf, sum); - if (sum != in_buf[2 + msg->in_len]) { + if (sum != in_buf[2 + msg->insize]) { dev_err(ec_dev->dev, "bad packet checksum\n"); ret = -EBADMSG; goto done; diff --git a/drivers/mfd/cros_ec_spi.c b/drivers/mfd/cros_ec_spi.c index 9d45d88813b8..bef7735ecfde 100644 --- a/drivers/mfd/cros_ec_spi.c +++ b/drivers/mfd/cros_ec_spi.c @@ -216,7 +216,7 @@ static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev, * @ec_msg: Message to transfer */ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev, - struct cros_ec_msg *ec_msg) + struct cros_ec_command *ec_msg) { struct cros_ec_spi *ec_spi = ec_dev->priv; struct spi_transfer trans; @@ -261,7 +261,7 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev, /* Get the response */ if (!ret) { ret = cros_ec_spi_receive_response(ec_dev, - ec_msg->in_len + EC_MSG_TX_PROTO_BYTES); + ec_msg->insize + EC_MSG_TX_PROTO_BYTES); } else { dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); } @@ -290,21 +290,21 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev, if (ptr[0]) { if (ptr[0] == EC_RES_IN_PROGRESS) { dev_dbg(ec_dev->dev, "command 0x%02x in progress\n", - ec_msg->cmd); + ec_msg->command); ret = -EAGAIN; goto exit; } dev_warn(ec_dev->dev, "command 0x%02x returned an error %d\n", - ec_msg->cmd, ptr[0]); + ec_msg->command, ptr[0]); debug_packet(ec_dev->dev, "in_err", ptr, len); ret = -EINVAL; goto exit; } len = ptr[1]; sum = ptr[0] + ptr[1]; - if (len > ec_msg->in_len) { + if (len > ec_msg->insize) { dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)", - len, ec_msg->in_len); + len, ec_msg->insize); ret = -ENOSPC; goto exit; } @@ -312,8 +312,8 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev, /* copy response packet payload and compute checksum */ for (i = 0; i < len; i++) { sum += ptr[i + 2]; - if (ec_msg->in_len) - ec_msg->in_buf[i] = ptr[i + 2]; + if (ec_msg->insize) + ec_msg->indata[i] = ptr[i + 2]; } sum &= 0xff; diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h index 79a35857cc9e..f27c03766069 100644 --- a/include/linux/mfd/cros_ec.h +++ b/include/linux/mfd/cros_ec.h @@ -35,23 +35,23 @@ enum { EC_MSG_TX_PROTO_BYTES, }; -/** - * struct cros_ec_msg - A message sent to the EC, and its reply - * +/* * @version: Command version number (often 0) - * @cmd: Command to send (EC_CMD_...) - * @out_buf: Outgoing payload (to EC) - * @outlen: Outgoing length - * @in_buf: Incoming payload (from EC) - * @in_len: Incoming length + * @command: Command to send (EC_CMD_...) + * @outdata: Outgoing data to EC + * @outsize: Outgoing length in bytes + * @indata: Where to put the incoming data from EC + * @insize: Incoming length in bytes (filled in by EC) + * @result: EC's response to the command (separate from communication failure) */ -struct cros_ec_msg { - u8 version; - u8 cmd; - uint8_t *out_buf; - int out_len; - uint8_t *in_buf; - int in_len; +struct cros_ec_command { + uint32_t version; + uint32_t command; + uint8_t *outdata; + uint32_t outsize; + uint8_t *indata; + uint32_t insize; + uint32_t result; }; /** @@ -114,7 +114,8 @@ struct cros_ec_device { struct device *parent; bool wake_enabled; struct mutex lock; - int (*cmd_xfer)(struct cros_ec_device *ec, struct cros_ec_msg *msg); + int (*cmd_xfer)(struct cros_ec_device *ec, + struct cros_ec_command *msg); }; /** @@ -148,7 +149,7 @@ int cros_ec_resume(struct cros_ec_device *ec_dev); * @msg: Message to write */ int cros_ec_prepare_tx(struct cros_ec_device *ec_dev, - struct cros_ec_msg *msg); + struct cros_ec_command *msg); /** * cros_ec_remove - Remove a ChromeOS EC -- cgit v1.2.3 From 533cec8f34778de10412dfabac991cf458ebf3c9 Mon Sep 17 00:00:00 2001 From: Bill Richardson Date: Wed, 18 Jun 2014 11:14:03 -0700 Subject: mfd: cros_ec: cleanup: remove unused fields from struct cros_ec_device struct cros_ec_device has a superfluous "name" field. We can get all the debugging info we need from the existing ec_name and phys_name fields, so let's take out the extra field. The printout also has sufficient info in it without explicitly adding the transport. Before this change: cros-ec-spi spi2.0: Chrome EC (SPI) After this change: cros-ec-spi spi2.0: Chrome EC device registered Signed-off-by: Bill Richardson Signed-off-by: Doug Anderson Reviewed-by: Simon Glass Signed-off-by: Lee Jones --- drivers/mfd/cros_ec.c | 2 +- drivers/mfd/cros_ec_i2c.c | 1 - drivers/mfd/cros_ec_spi.c | 1 - include/linux/mfd/cros_ec.h | 2 -- 4 files changed, 1 insertion(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index 2e86c282f0b4..49ed8c340868 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -140,7 +140,7 @@ int cros_ec_register(struct cros_ec_device *ec_dev) goto fail_mfd; } - dev_info(dev, "Chrome EC (%s)\n", ec_dev->name); + dev_info(dev, "Chrome EC device registered\n"); return 0; diff --git a/drivers/mfd/cros_ec_i2c.c b/drivers/mfd/cros_ec_i2c.c index 37ed12f99560..5bb32f5550b3 100644 --- a/drivers/mfd/cros_ec_i2c.c +++ b/drivers/mfd/cros_ec_i2c.c @@ -132,7 +132,6 @@ static int cros_ec_i2c_probe(struct i2c_client *client, return -ENOMEM; i2c_set_clientdata(client, ec_dev); - ec_dev->name = "I2C"; ec_dev->dev = dev; ec_dev->priv = client; ec_dev->irq = client->irq; diff --git a/drivers/mfd/cros_ec_spi.c b/drivers/mfd/cros_ec_spi.c index bef7735ecfde..6e929b5f3bd3 100644 --- a/drivers/mfd/cros_ec_spi.c +++ b/drivers/mfd/cros_ec_spi.c @@ -370,7 +370,6 @@ static int cros_ec_spi_probe(struct spi_device *spi) cros_ec_spi_dt_probe(ec_spi, dev); spi_set_drvdata(spi, ec_dev); - ec_dev->name = "SPI"; ec_dev->dev = dev; ec_dev->priv = ec_spi; ec_dev->irq = spi->irq; diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h index f27c03766069..2b0c5982dbc1 100644 --- a/include/linux/mfd/cros_ec.h +++ b/include/linux/mfd/cros_ec.h @@ -67,7 +67,6 @@ struct cros_ec_command { * @command_recv: receive a response * @command_sendrecv: send a command and receive a response * - * @name: Name of this EC interface * @priv: Private data * @irq: Interrupt to use * @din: input buffer (for data from EC) @@ -104,7 +103,6 @@ struct cros_ec_device { void *in_buf, int in_len); /* These are used to implement the platform-specific interface */ - const char *name; void *priv; int irq; uint8_t *din; -- cgit v1.2.3 From 5799f95a373a2752e5c732f531a6f40fe458b818 Mon Sep 17 00:00:00 2001 From: Bill Richardson Date: Wed, 18 Jun 2014 11:14:04 -0700 Subject: mfd: cros_ec: cleanup: Remove EC wrapper functions Remove the three wrapper functions that talk to the EC without passing all the desired arguments and just use the underlying communication function that passes everything in a struct intead. This is internal code refactoring only. Nothing should change. Signed-off-by: Bill Richardson Signed-off-by: Doug Anderson Reviewed-by: Simon Glass Acked-by: Wolfram Sang Acked-by: Dmitry Torokhov Signed-off-by: Lee Jones --- drivers/i2c/busses/i2c-cros-ec-tunnel.c | 15 +++++++++++---- drivers/input/keyboard/cros_ec_keyb.c | 12 ++++++++++-- drivers/mfd/cros_ec.c | 32 -------------------------------- include/linux/mfd/cros_ec.h | 19 ++++++------------- 4 files changed, 27 insertions(+), 51 deletions(-) (limited to 'include') diff --git a/drivers/i2c/busses/i2c-cros-ec-tunnel.c b/drivers/i2c/busses/i2c-cros-ec-tunnel.c index 8e7a71487bb1..dd07818d03d0 100644 --- a/drivers/i2c/busses/i2c-cros-ec-tunnel.c +++ b/drivers/i2c/busses/i2c-cros-ec-tunnel.c @@ -183,6 +183,7 @@ static int ec_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg i2c_msgs[], u8 *request = NULL; u8 *response = NULL; int result; + struct cros_ec_command msg; request_len = ec_i2c_count_message(i2c_msgs, num); if (request_len < 0) { @@ -218,9 +219,15 @@ static int ec_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg i2c_msgs[], } ec_i2c_construct_message(request, i2c_msgs, num, bus_num); - result = bus->ec->command_sendrecv(bus->ec, EC_CMD_I2C_PASSTHRU, - request, request_len, - response, response_len); + + msg.version = 0; + msg.command = EC_CMD_I2C_PASSTHRU; + msg.outdata = request; + msg.outsize = request_len; + msg.indata = response; + msg.insize = response_len; + + result = bus->ec->cmd_xfer(bus->ec, &msg); if (result) goto exit; @@ -258,7 +265,7 @@ static int ec_i2c_probe(struct platform_device *pdev) u32 remote_bus; int err; - if (!ec->command_sendrecv) { + if (!ec->cmd_xfer) { dev_err(dev, "Missing sendrecv\n"); return -EINVAL; } diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index 408379669d3c..b8341ab99f55 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -191,8 +191,16 @@ static void cros_ec_keyb_close(struct input_dev *dev) static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state) { - return ckdev->ec->command_recv(ckdev->ec, EC_CMD_MKBP_STATE, - kb_state, ckdev->cols); + struct cros_ec_command msg = { + .version = 0, + .command = EC_CMD_MKBP_STATE, + .outdata = NULL, + .outsize = 0, + .indata = kb_state, + .insize = ckdev->cols, + }; + + return ckdev->ec->cmd_xfer(ckdev->ec, &msg); } static int cros_ec_keyb_work(struct notifier_block *nb, diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index 49ed8c340868..4851ed2fbe31 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -44,34 +44,6 @@ int cros_ec_prepare_tx(struct cros_ec_device *ec_dev, } EXPORT_SYMBOL(cros_ec_prepare_tx); -static int cros_ec_command_sendrecv(struct cros_ec_device *ec_dev, - uint16_t cmd, void *out_buf, int out_len, - void *in_buf, int in_len) -{ - struct cros_ec_command msg; - - msg.version = cmd >> 8; - msg.command = cmd & 0xff; - msg.outdata = out_buf; - msg.outsize = out_len; - msg.indata = in_buf; - msg.insize = in_len; - - return ec_dev->cmd_xfer(ec_dev, &msg); -} - -static int cros_ec_command_recv(struct cros_ec_device *ec_dev, - uint16_t cmd, void *buf, int buf_len) -{ - return cros_ec_command_sendrecv(ec_dev, cmd, NULL, 0, buf, buf_len); -} - -static int cros_ec_command_send(struct cros_ec_device *ec_dev, - uint16_t cmd, void *buf, int buf_len) -{ - return cros_ec_command_sendrecv(ec_dev, cmd, buf, buf_len, NULL, 0); -} - static irqreturn_t ec_irq_thread(int irq, void *data) { struct cros_ec_device *ec_dev = data; @@ -104,10 +76,6 @@ int cros_ec_register(struct cros_ec_device *ec_dev) BLOCKING_INIT_NOTIFIER_HEAD(&ec_dev->event_notifier); - ec_dev->command_send = cros_ec_command_send; - ec_dev->command_recv = cros_ec_command_recv; - ec_dev->command_sendrecv = cros_ec_command_sendrecv; - if (ec_dev->din_size) { ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL); if (!ec_dev->din) diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h index 2b0c5982dbc1..60c088055f3a 100644 --- a/include/linux/mfd/cros_ec.h +++ b/include/linux/mfd/cros_ec.h @@ -63,9 +63,10 @@ struct cros_ec_command { * @was_wake_device: true if this device was set to wake the system from * sleep at the last suspend * @event_notifier: interrupt event notifier for transport devices - * @command_send: send a command - * @command_recv: receive a response - * @command_sendrecv: send a command and receive a response + * @cmd_xfer: send command to EC and get response + * Returns 0 if the communication succeeded, but that doesn't mean the EC + * was happy with the command it got. Caller should check msg.result for + * the EC's result code. * * @priv: Private data * @irq: Interrupt to use @@ -83,7 +84,6 @@ struct cros_ec_command { * @parent: pointer to parent device (e.g. i2c or spi device) * @wake_enabled: true if this device can wake the system from sleep * @lock: one transaction at a time - * @cmd_xfer: low-level channel to the EC */ struct cros_ec_device { @@ -94,13 +94,8 @@ struct cros_ec_device { bool was_wake_device; struct class *cros_class; struct blocking_notifier_head event_notifier; - int (*command_send)(struct cros_ec_device *ec, - uint16_t cmd, void *out_buf, int out_len); - int (*command_recv)(struct cros_ec_device *ec, - uint16_t cmd, void *in_buf, int in_len); - int (*command_sendrecv)(struct cros_ec_device *ec, - uint16_t cmd, void *out_buf, int out_len, - void *in_buf, int in_len); + int (*cmd_xfer)(struct cros_ec_device *ec, + struct cros_ec_command *msg); /* These are used to implement the platform-specific interface */ void *priv; @@ -112,8 +107,6 @@ struct cros_ec_device { struct device *parent; bool wake_enabled; struct mutex lock; - int (*cmd_xfer)(struct cros_ec_device *ec, - struct cros_ec_command *msg); }; /** -- cgit v1.2.3 From 6db07b6336589ff480528173e41f8f6af3f0097f Mon Sep 17 00:00:00 2001 From: Bill Richardson Date: Wed, 18 Jun 2014 11:14:05 -0700 Subject: mfd: cros_ec: Check result code from EC messages Just because the host was able to talk to the EC doesn't mean that the EC was happy with what it was told. Errors in communincation are not the same as error messages from the EC itself. This change lets the EC report its errors separately. [dianders: Added common function to cros_ec.c] Signed-off-by: Bill Richardson Signed-off-by: Doug Anderson Signed-off-by: Lee Jones --- drivers/mfd/cros_ec.c | 18 ++++++++++++++++++ drivers/mfd/cros_ec_i2c.c | 8 +++----- drivers/mfd/cros_ec_spi.c | 19 ++++++------------- include/linux/mfd/cros_ec.h | 12 ++++++++++++ 4 files changed, 39 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index 4851ed2fbe31..83e30c663578 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -44,6 +44,24 @@ int cros_ec_prepare_tx(struct cros_ec_device *ec_dev, } EXPORT_SYMBOL(cros_ec_prepare_tx); +int cros_ec_check_result(struct cros_ec_device *ec_dev, + struct cros_ec_command *msg) +{ + switch (msg->result) { + case EC_RES_SUCCESS: + return 0; + case EC_RES_IN_PROGRESS: + dev_dbg(ec_dev->dev, "command 0x%02x in progress\n", + msg->command); + return -EAGAIN; + default: + dev_dbg(ec_dev->dev, "command 0x%02x returned %d\n", + msg->command, msg->result); + return 0; + } +} +EXPORT_SYMBOL(cros_ec_check_result); + static irqreturn_t ec_irq_thread(int irq, void *data) { struct cros_ec_device *ec_dev = data; diff --git a/drivers/mfd/cros_ec_i2c.c b/drivers/mfd/cros_ec_i2c.c index 5bb32f5550b3..189e7d1d7742 100644 --- a/drivers/mfd/cros_ec_i2c.c +++ b/drivers/mfd/cros_ec_i2c.c @@ -92,12 +92,10 @@ static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev, } /* check response error code */ - if (i2c_msg[1].buf[0]) { - dev_warn(ec_dev->dev, "command 0x%02x returned an error %d\n", - msg->command, i2c_msg[1].buf[0]); - ret = -EINVAL; + msg->result = i2c_msg[1].buf[0]; + ret = cros_ec_check_result(ec_dev, msg); + if (ret) goto done; - } /* copy response packet payload and compute checksum */ sum = in_buf[0] + in_buf[1]; diff --git a/drivers/mfd/cros_ec_spi.c b/drivers/mfd/cros_ec_spi.c index 6e929b5f3bd3..da1da05cd546 100644 --- a/drivers/mfd/cros_ec_spi.c +++ b/drivers/mfd/cros_ec_spi.c @@ -285,21 +285,14 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev, goto exit; } - /* check response error code */ ptr = ec_dev->din; - if (ptr[0]) { - if (ptr[0] == EC_RES_IN_PROGRESS) { - dev_dbg(ec_dev->dev, "command 0x%02x in progress\n", - ec_msg->command); - ret = -EAGAIN; - goto exit; - } - dev_warn(ec_dev->dev, "command 0x%02x returned an error %d\n", - ec_msg->command, ptr[0]); - debug_packet(ec_dev->dev, "in_err", ptr, len); - ret = -EINVAL; + + /* check response error code */ + ec_msg->result = ptr[0]; + ret = cros_ec_check_result(ec_dev, ec_msg); + if (ret) goto exit; - } + len = ptr[1]; sum = ptr[0] + ptr[1]; if (len > ec_msg->insize) { diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h index 60c088055f3a..1f79f162abe4 100644 --- a/include/linux/mfd/cros_ec.h +++ b/include/linux/mfd/cros_ec.h @@ -142,6 +142,18 @@ int cros_ec_resume(struct cros_ec_device *ec_dev); int cros_ec_prepare_tx(struct cros_ec_device *ec_dev, struct cros_ec_command *msg); +/** + * cros_ec_check_result - Check ec_msg->result + * + * This is used by ChromeOS EC drivers to check the ec_msg->result for + * errors and to warn about them. + * + * @ec_dev: EC device + * @msg: Message to check + */ +int cros_ec_check_result(struct cros_ec_device *ec_dev, + struct cros_ec_command *msg); + /** * cros_ec_remove - Remove a ChromeOS EC * -- cgit v1.2.3 From 12ebc8a50bc54e3a6fe207861fc6793181f9c2dc Mon Sep 17 00:00:00 2001 From: Bill Richardson Date: Wed, 18 Jun 2014 11:14:06 -0700 Subject: mfd: cros_ec: ec_dev->cmd_xfer() returns number of bytes received from EC When communicating with the EC, the cmd_xfer() function should return the number of bytes it received from the EC, or negative on error. Signed-off-by: Bill Richardson Signed-off-by: Doug Anderson Reviewed-by: Simon Glass Acked-by: Wolfram Sang Signed-off-by: Lee Jones --- drivers/i2c/busses/i2c-cros-ec-tunnel.c | 2 +- drivers/mfd/cros_ec_i2c.c | 2 +- drivers/mfd/cros_ec_spi.c | 2 +- include/linux/mfd/cros_ec.h | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/i2c/busses/i2c-cros-ec-tunnel.c b/drivers/i2c/busses/i2c-cros-ec-tunnel.c index dd07818d03d0..05e033c98115 100644 --- a/drivers/i2c/busses/i2c-cros-ec-tunnel.c +++ b/drivers/i2c/busses/i2c-cros-ec-tunnel.c @@ -228,7 +228,7 @@ static int ec_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg i2c_msgs[], msg.insize = response_len; result = bus->ec->cmd_xfer(bus->ec, &msg); - if (result) + if (result < 0) goto exit; result = ec_i2c_parse_response(response, i2c_msgs, &num); diff --git a/drivers/mfd/cros_ec_i2c.c b/drivers/mfd/cros_ec_i2c.c index 189e7d1d7742..fd7a546d3478 100644 --- a/drivers/mfd/cros_ec_i2c.c +++ b/drivers/mfd/cros_ec_i2c.c @@ -111,7 +111,7 @@ static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev, goto done; } - ret = 0; + ret = i2c_msg[1].buf[1]; done: kfree(in_buf); kfree(out_buf); diff --git a/drivers/mfd/cros_ec_spi.c b/drivers/mfd/cros_ec_spi.c index da1da05cd546..ac52e3653e90 100644 --- a/drivers/mfd/cros_ec_spi.c +++ b/drivers/mfd/cros_ec_spi.c @@ -320,7 +320,7 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev, goto exit; } - ret = 0; + ret = len; exit: mutex_unlock(&ec_spi->lock); return ret; diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h index 1f79f162abe4..0ebf26fddbbb 100644 --- a/include/linux/mfd/cros_ec.h +++ b/include/linux/mfd/cros_ec.h @@ -41,7 +41,7 @@ enum { * @outdata: Outgoing data to EC * @outsize: Outgoing length in bytes * @indata: Where to put the incoming data from EC - * @insize: Incoming length in bytes (filled in by EC) + * @insize: Max number of bytes to accept from EC * @result: EC's response to the command (separate from communication failure) */ struct cros_ec_command { @@ -64,9 +64,9 @@ struct cros_ec_command { * sleep at the last suspend * @event_notifier: interrupt event notifier for transport devices * @cmd_xfer: send command to EC and get response - * Returns 0 if the communication succeeded, but that doesn't mean the EC - * was happy with the command it got. Caller should check msg.result for - * the EC's result code. + * Returns the number of bytes received if the communication succeeded, but + * that doesn't mean the EC was happy with the command. The caller + * should check msg.result for the EC's result code. * * @priv: Private data * @irq: Interrupt to use -- cgit v1.2.3 From d1fd345e2087f0362c92bd3b0a1cea7fe636ac3a Mon Sep 17 00:00:00 2001 From: Andrew Bresticker Date: Wed, 18 Jun 2014 11:14:07 -0700 Subject: mfd: cros_ec: Move EC interrupt to cros_ec_keyb If we receive EC interrupts after the cros_ec driver has probed, but before the cros_ec_keyb driver has probed, the cros_ec IRQ handler will not run the cros_ec_keyb notifier and the EC will leave the IRQ line asserted. The cros_ec IRQ handler then returns IRQ_HANDLED and the resulting flood of interrupts causes the machine to hang. Since the EC interrupt is currently only used for the keyboard, move the setup and handling of the EC interrupt to the cros_ec_keyb driver. Signed-off-by: Andrew Bresticker Signed-off-by: Doug Anderson Acked-by: Dmitry Torokhov Signed-off-by: Lee Jones --- drivers/input/keyboard/cros_ec_keyb.c | 58 ++++++++++++++++++++--------------- drivers/mfd/cros_ec.c | 35 +-------------------- include/linux/mfd/cros_ec.h | 2 -- 3 files changed, 34 insertions(+), 61 deletions(-) (limited to 'include') diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index b8341ab99f55..791781ade4e7 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -24,8 +24,8 @@ #include #include #include +#include #include -#include #include #include #include @@ -42,7 +42,6 @@ * @dev: Device pointer * @idev: Input device * @ec: Top level ChromeOS device to use to talk to EC - * @event_notifier: interrupt event notifier for transport devices */ struct cros_ec_keyb { unsigned int rows; @@ -55,7 +54,6 @@ struct cros_ec_keyb { struct device *dev; struct input_dev *idev; struct cros_ec_device *ec; - struct notifier_block notifier; }; @@ -173,22 +171,6 @@ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, input_sync(ckdev->idev); } -static int cros_ec_keyb_open(struct input_dev *dev) -{ - struct cros_ec_keyb *ckdev = input_get_drvdata(dev); - - return blocking_notifier_chain_register(&ckdev->ec->event_notifier, - &ckdev->notifier); -} - -static void cros_ec_keyb_close(struct input_dev *dev) -{ - struct cros_ec_keyb *ckdev = input_get_drvdata(dev); - - blocking_notifier_chain_unregister(&ckdev->ec->event_notifier, - &ckdev->notifier); -} - static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state) { struct cros_ec_command msg = { @@ -203,19 +185,41 @@ static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state) return ckdev->ec->cmd_xfer(ckdev->ec, &msg); } -static int cros_ec_keyb_work(struct notifier_block *nb, - unsigned long state, void *_notify) +static irqreturn_t cros_ec_keyb_irq(int irq, void *data) { + struct cros_ec_keyb *ckdev = data; + struct cros_ec_device *ec = ckdev->ec; int ret; - struct cros_ec_keyb *ckdev = container_of(nb, struct cros_ec_keyb, - notifier); uint8_t kb_state[ckdev->cols]; + if (device_may_wakeup(ec->dev)) + pm_wakeup_event(ec->dev, 0); + ret = cros_ec_keyb_get_state(ckdev, kb_state); if (ret >= 0) cros_ec_keyb_process(ckdev, kb_state, ret); + else + dev_err(ec->dev, "failed to get keyboard state: %d\n", ret); - return NOTIFY_DONE; + return IRQ_HANDLED; +} + +static int cros_ec_keyb_open(struct input_dev *dev) +{ + struct cros_ec_keyb *ckdev = input_get_drvdata(dev); + struct cros_ec_device *ec = ckdev->ec; + + return request_threaded_irq(ec->irq, NULL, cros_ec_keyb_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "cros_ec_keyb", ckdev); +} + +static void cros_ec_keyb_close(struct input_dev *dev) +{ + struct cros_ec_keyb *ckdev = input_get_drvdata(dev); + struct cros_ec_device *ec = ckdev->ec; + + free_irq(ec->irq, ckdev); } static int cros_ec_keyb_probe(struct platform_device *pdev) @@ -246,8 +250,12 @@ static int cros_ec_keyb_probe(struct platform_device *pdev) if (!idev) return -ENOMEM; + if (!ec->irq) { + dev_err(dev, "no EC IRQ specified\n"); + return -EINVAL; + } + ckdev->ec = ec; - ckdev->notifier.notifier_call = cros_ec_keyb_work; ckdev->dev = dev; dev_set_drvdata(&pdev->dev, ckdev); diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index 83e30c663578..4873f9c50452 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -62,18 +62,6 @@ int cros_ec_check_result(struct cros_ec_device *ec_dev, } EXPORT_SYMBOL(cros_ec_check_result); -static irqreturn_t ec_irq_thread(int irq, void *data) -{ - struct cros_ec_device *ec_dev = data; - - if (device_may_wakeup(ec_dev->dev)) - pm_wakeup_event(ec_dev->dev, 0); - - blocking_notifier_call_chain(&ec_dev->event_notifier, 1, ec_dev); - - return IRQ_HANDLED; -} - static const struct mfd_cell cros_devs[] = { { .name = "cros-ec-keyb", @@ -92,8 +80,6 @@ int cros_ec_register(struct cros_ec_device *ec_dev) struct device *dev = ec_dev->dev; int err = 0; - BLOCKING_INIT_NOTIFIER_HEAD(&ec_dev->event_notifier); - if (ec_dev->din_size) { ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL); if (!ec_dev->din) @@ -105,42 +91,23 @@ int cros_ec_register(struct cros_ec_device *ec_dev) return -ENOMEM; } - if (!ec_dev->irq) { - dev_dbg(dev, "no valid IRQ: %d\n", ec_dev->irq); - return err; - } - - err = request_threaded_irq(ec_dev->irq, NULL, ec_irq_thread, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - "chromeos-ec", ec_dev); - if (err) { - dev_err(dev, "request irq %d: error %d\n", ec_dev->irq, err); - return err; - } - err = mfd_add_devices(dev, 0, cros_devs, ARRAY_SIZE(cros_devs), NULL, ec_dev->irq, NULL); if (err) { dev_err(dev, "failed to add mfd devices\n"); - goto fail_mfd; + return err; } dev_info(dev, "Chrome EC device registered\n"); return 0; - -fail_mfd: - free_irq(ec_dev->irq, ec_dev); - - return err; } EXPORT_SYMBOL(cros_ec_register); int cros_ec_remove(struct cros_ec_device *ec_dev) { mfd_remove_devices(ec_dev->dev); - free_irq(ec_dev->irq, ec_dev); return 0; } diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h index 0ebf26fddbbb..fcbe9d129a9d 100644 --- a/include/linux/mfd/cros_ec.h +++ b/include/linux/mfd/cros_ec.h @@ -62,7 +62,6 @@ struct cros_ec_command { * @dev: Device pointer * @was_wake_device: true if this device was set to wake the system from * sleep at the last suspend - * @event_notifier: interrupt event notifier for transport devices * @cmd_xfer: send command to EC and get response * Returns the number of bytes received if the communication succeeded, but * that doesn't mean the EC was happy with the command. The caller @@ -93,7 +92,6 @@ struct cros_ec_device { struct device *dev; bool was_wake_device; struct class *cros_class; - struct blocking_notifier_head event_notifier; int (*cmd_xfer)(struct cros_ec_device *ec, struct cros_ec_command *msg); -- cgit v1.2.3 From 9c87e0f10e281f782312e7b6aa202f2d434c84bf Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 1 Jul 2014 16:02:51 +0100 Subject: Provide PE binary definitions Provide some PE binary structural and constant definitions as taken from the pesign package sources. Signed-off-by: David Howells Acked-by: Vivek Goyal Reviewed-by: Kees Cook --- include/linux/pe.h | 448 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 448 insertions(+) create mode 100644 include/linux/pe.h (limited to 'include') diff --git a/include/linux/pe.h b/include/linux/pe.h new file mode 100644 index 000000000000..e170b95e763b --- /dev/null +++ b/include/linux/pe.h @@ -0,0 +1,448 @@ +/* + * Copyright 2011 Red Hat, Inc. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This 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 . + * + * Author(s): Peter Jones + */ +#ifndef __LINUX_PE_H +#define __LINUX_PE_H + +#include + +#define MZ_MAGIC 0x5a4d /* "MZ" */ + +struct mz_hdr { + uint16_t magic; /* MZ_MAGIC */ + uint16_t lbsize; /* size of last used block */ + uint16_t blocks; /* pages in file, 0x3 */ + uint16_t relocs; /* relocations */ + uint16_t hdrsize; /* header size in "paragraphs" */ + uint16_t min_extra_pps; /* .bss */ + uint16_t max_extra_pps; /* runtime limit for the arena size */ + uint16_t ss; /* relative stack segment */ + uint16_t sp; /* initial %sp register */ + uint16_t checksum; /* word checksum */ + uint16_t ip; /* initial %ip register */ + uint16_t cs; /* initial %cs relative to load segment */ + uint16_t reloc_table_offset; /* offset of the first relocation */ + uint16_t overlay_num; /* overlay number. set to 0. */ + uint16_t reserved0[4]; /* reserved */ + uint16_t oem_id; /* oem identifier */ + uint16_t oem_info; /* oem specific */ + uint16_t reserved1[10]; /* reserved */ + uint32_t peaddr; /* address of pe header */ + char message[64]; /* message to print */ +}; + +struct mz_reloc { + uint16_t offset; + uint16_t segment; +}; + +#define PE_MAGIC 0x00004550 /* "PE\0\0" */ +#define PE_OPT_MAGIC_PE32 0x010b +#define PE_OPT_MAGIC_PE32_ROM 0x0107 +#define PE_OPT_MAGIC_PE32PLUS 0x020b + +/* machine type */ +#define IMAGE_FILE_MACHINE_UNKNOWN 0x0000 +#define IMAGE_FILE_MACHINE_AM33 0x01d3 +#define IMAGE_FILE_MACHINE_AMD64 0x8664 +#define IMAGE_FILE_MACHINE_ARM 0x01c0 +#define IMAGE_FILE_MACHINE_ARMV7 0x01c4 +#define IMAGE_FILE_MACHINE_EBC 0x0ebc +#define IMAGE_FILE_MACHINE_I386 0x014c +#define IMAGE_FILE_MACHINE_IA64 0x0200 +#define IMAGE_FILE_MACHINE_M32R 0x9041 +#define IMAGE_FILE_MACHINE_MIPS16 0x0266 +#define IMAGE_FILE_MACHINE_MIPSFPU 0x0366 +#define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466 +#define IMAGE_FILE_MACHINE_POWERPC 0x01f0 +#define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1 +#define IMAGE_FILE_MACHINE_R4000 0x0166 +#define IMAGE_FILE_MACHINE_SH3 0x01a2 +#define IMAGE_FILE_MACHINE_SH3DSP 0x01a3 +#define IMAGE_FILE_MACHINE_SH3E 0x01a4 +#define IMAGE_FILE_MACHINE_SH4 0x01a6 +#define IMAGE_FILE_MACHINE_SH5 0x01a8 +#define IMAGE_FILE_MACHINE_THUMB 0x01c2 +#define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169 + +/* flags */ +#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 +#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 +#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 +#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 +#define IMAGE_FILE_AGGRESSIVE_WS_TRIM 0x0010 +#define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 +#define IMAGE_FILE_16BIT_MACHINE 0x0040 +#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 +#define IMAGE_FILE_32BIT_MACHINE 0x0100 +#define IMAGE_FILE_DEBUG_STRIPPED 0x0200 +#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 +#define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 +#define IMAGE_FILE_SYSTEM 0x1000 +#define IMAGE_FILE_DLL 0x2000 +#define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 +#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 + +struct pe_hdr { + uint32_t magic; /* PE magic */ + uint16_t machine; /* machine type */ + uint16_t sections; /* number of sections */ + uint32_t timestamp; /* time_t */ + uint32_t symbol_table; /* symbol table offset */ + uint32_t symbols; /* number of symbols */ + uint16_t opt_hdr_size; /* size of optional header */ + uint16_t flags; /* flags */ +}; + +#define IMAGE_FILE_OPT_ROM_MAGIC 0x107 +#define IMAGE_FILE_OPT_PE32_MAGIC 0x10b +#define IMAGE_FILE_OPT_PE32_PLUS_MAGIC 0x20b + +#define IMAGE_SUBSYSTEM_UNKNOWN 0 +#define IMAGE_SUBSYSTEM_NATIVE 1 +#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2 +#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3 +#define IMAGE_SUBSYSTEM_POSIX_CUI 7 +#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI 9 +#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10 +#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11 +#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12 +#define IMAGE_SUBSYSTEM_EFI_ROM_IMAGE 13 +#define IMAGE_SUBSYSTEM_XBOX 14 + +#define IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE 0x0040 +#define IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY 0x0080 +#define IMAGE_DLL_CHARACTERISTICS_NX_COMPAT 0x0100 +#define IMAGE_DLLCHARACTERISTICS_NO_ISOLATION 0x0200 +#define IMAGE_DLLCHARACTERISTICS_NO_SEH 0x0400 +#define IMAGE_DLLCHARACTERISTICS_NO_BIND 0x0800 +#define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER 0x2000 +#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000 + +/* the fact that pe32 isn't padded where pe32+ is 64-bit means union won't + * work right. vomit. */ +struct pe32_opt_hdr { + /* "standard" header */ + uint16_t magic; /* file type */ + uint8_t ld_major; /* linker major version */ + uint8_t ld_minor; /* linker minor version */ + uint32_t text_size; /* size of text section(s) */ + uint32_t data_size; /* size of data section(s) */ + uint32_t bss_size; /* size of bss section(s) */ + uint32_t entry_point; /* file offset of entry point */ + uint32_t code_base; /* relative code addr in ram */ + uint32_t data_base; /* relative data addr in ram */ + /* "windows" header */ + uint32_t image_base; /* preferred load address */ + uint32_t section_align; /* alignment in bytes */ + uint32_t file_align; /* file alignment in bytes */ + uint16_t os_major; /* major OS version */ + uint16_t os_minor; /* minor OS version */ + uint16_t image_major; /* major image version */ + uint16_t image_minor; /* minor image version */ + uint16_t subsys_major; /* major subsystem version */ + uint16_t subsys_minor; /* minor subsystem version */ + uint32_t win32_version; /* reserved, must be 0 */ + uint32_t image_size; /* image size */ + uint32_t header_size; /* header size rounded up to + file_align */ + uint32_t csum; /* checksum */ + uint16_t subsys; /* subsystem */ + uint16_t dll_flags; /* more flags! */ + uint32_t stack_size_req;/* amt of stack requested */ + uint32_t stack_size; /* amt of stack required */ + uint32_t heap_size_req; /* amt of heap requested */ + uint32_t heap_size; /* amt of heap required */ + uint32_t loader_flags; /* reserved, must be 0 */ + uint32_t data_dirs; /* number of data dir entries */ +}; + +struct pe32plus_opt_hdr { + uint16_t magic; /* file type */ + uint8_t ld_major; /* linker major version */ + uint8_t ld_minor; /* linker minor version */ + uint32_t text_size; /* size of text section(s) */ + uint32_t data_size; /* size of data section(s) */ + uint32_t bss_size; /* size of bss section(s) */ + uint32_t entry_point; /* file offset of entry point */ + uint32_t code_base; /* relative code addr in ram */ + /* "windows" header */ + uint64_t image_base; /* preferred load address */ + uint32_t section_align; /* alignment in bytes */ + uint32_t file_align; /* file alignment in bytes */ + uint16_t os_major; /* major OS version */ + uint16_t os_minor; /* minor OS version */ + uint16_t image_major; /* major image version */ + uint16_t image_minor; /* minor image version */ + uint16_t subsys_major; /* major subsystem version */ + uint16_t subsys_minor; /* minor subsystem version */ + uint32_t win32_version; /* reserved, must be 0 */ + uint32_t image_size; /* image size */ + uint32_t header_size; /* header size rounded up to + file_align */ + uint32_t csum; /* checksum */ + uint16_t subsys; /* subsystem */ + uint16_t dll_flags; /* more flags! */ + uint64_t stack_size_req;/* amt of stack requested */ + uint64_t stack_size; /* amt of stack required */ + uint64_t heap_size_req; /* amt of heap requested */ + uint64_t heap_size; /* amt of heap required */ + uint32_t loader_flags; /* reserved, must be 0 */ + uint32_t data_dirs; /* number of data dir entries */ +}; + +struct data_dirent { + uint32_t virtual_address; /* relative to load address */ + uint32_t size; +}; + +struct data_directory { + struct data_dirent exports; /* .edata */ + struct data_dirent imports; /* .idata */ + struct data_dirent resources; /* .rsrc */ + struct data_dirent exceptions; /* .pdata */ + struct data_dirent certs; /* certs */ + struct data_dirent base_relocations; /* .reloc */ + struct data_dirent debug; /* .debug */ + struct data_dirent arch; /* reservered */ + struct data_dirent global_ptr; /* global pointer reg. Size=0 */ + struct data_dirent tls; /* .tls */ + struct data_dirent load_config; /* load configuration structure */ + struct data_dirent bound_imports; /* no idea */ + struct data_dirent import_addrs; /* import address table */ + struct data_dirent delay_imports; /* delay-load import table */ + struct data_dirent clr_runtime_hdr; /* .cor (object only) */ + struct data_dirent reserved; +}; + +struct section_header { + char name[8]; /* name or "/12\0" string tbl offset */ + uint32_t virtual_size; /* size of loaded section in ram */ + uint32_t virtual_address; /* relative virtual address */ + uint32_t raw_data_size; /* size of the section */ + uint32_t data_addr; /* file pointer to first page of sec */ + uint32_t relocs; /* file pointer to relocation entries */ + uint32_t line_numbers; /* line numbers! */ + uint16_t num_relocs; /* number of relocations */ + uint16_t num_lin_numbers; /* srsly. */ + uint32_t flags; +}; + +/* they actually defined 0x00000000 as well, but I think we'll skip that one. */ +#define IMAGE_SCN_RESERVED_0 0x00000001 +#define IMAGE_SCN_RESERVED_1 0x00000002 +#define IMAGE_SCN_RESERVED_2 0x00000004 +#define IMAGE_SCN_TYPE_NO_PAD 0x00000008 /* don't pad - obsolete */ +#define IMAGE_SCN_RESERVED_3 0x00000010 +#define IMAGE_SCN_CNT_CODE 0x00000020 /* .text */ +#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 /* .data */ +#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 /* .bss */ +#define IMAGE_SCN_LNK_OTHER 0x00000100 /* reserved */ +#define IMAGE_SCN_LNK_INFO 0x00000200 /* .drectve comments */ +#define IMAGE_SCN_RESERVED_4 0x00000400 +#define IMAGE_SCN_LNK_REMOVE 0x00000800 /* .o only - scn to be rm'd*/ +#define IMAGE_SCN_LNK_COMDAT 0x00001000 /* .o only - COMDAT data */ +#define IMAGE_SCN_RESERVED_5 0x00002000 /* spec omits this */ +#define IMAGE_SCN_RESERVED_6 0x00004000 /* spec omits this */ +#define IMAGE_SCN_GPREL 0x00008000 /* global pointer referenced data */ +/* spec lists 0x20000 twice, I suspect they meant 0x10000 for one of them */ +#define IMAGE_SCN_MEM_PURGEABLE 0x00010000 /* reserved for "future" use */ +#define IMAGE_SCN_16BIT 0x00020000 /* reserved for "future" use */ +#define IMAGE_SCN_LOCKED 0x00040000 /* reserved for "future" use */ +#define IMAGE_SCN_PRELOAD 0x00080000 /* reserved for "future" use */ +/* and here they just stuck a 1-byte integer in the middle of a bitfield */ +#define IMAGE_SCN_ALIGN_1BYTES 0x00100000 /* it does what it says on the box */ +#define IMAGE_SCN_ALIGN_2BYTES 0x00200000 +#define IMAGE_SCN_ALIGN_4BYTES 0x00300000 +#define IMAGE_SCN_ALIGN_8BYTES 0x00400000 +#define IMAGE_SCN_ALIGN_16BYTES 0x00500000 +#define IMAGE_SCN_ALIGN_32BYTES 0x00600000 +#define IMAGE_SCN_ALIGN_64BYTES 0x00700000 +#define IMAGE_SCN_ALIGN_128BYTES 0x00800000 +#define IMAGE_SCN_ALIGN_256BYTES 0x00900000 +#define IMAGE_SCN_ALIGN_512BYTES 0x00a00000 +#define IMAGE_SCN_ALIGN_1024BYTES 0x00b00000 +#define IMAGE_SCN_ALIGN_2048BYTES 0x00c00000 +#define IMAGE_SCN_ALIGN_4096BYTES 0x00d00000 +#define IMAGE_SCN_ALIGN_8192BYTES 0x00e00000 +#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 /* extended relocations */ +#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 /* scn can be discarded */ +#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 /* cannot be cached */ +#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 /* not pageable */ +#define IMAGE_SCN_MEM_SHARED 0x10000000 /* can be shared */ +#define IMAGE_SCN_MEM_EXECUTE 0x20000000 /* can be executed as code */ +#define IMAGE_SCN_MEM_READ 0x40000000 /* readable */ +#define IMAGE_SCN_MEM_WRITE 0x80000000 /* writeable */ + +enum x64_coff_reloc_type { + IMAGE_REL_AMD64_ABSOLUTE = 0, + IMAGE_REL_AMD64_ADDR64, + IMAGE_REL_AMD64_ADDR32, + IMAGE_REL_AMD64_ADDR32N, + IMAGE_REL_AMD64_REL32, + IMAGE_REL_AMD64_REL32_1, + IMAGE_REL_AMD64_REL32_2, + IMAGE_REL_AMD64_REL32_3, + IMAGE_REL_AMD64_REL32_4, + IMAGE_REL_AMD64_REL32_5, + IMAGE_REL_AMD64_SECTION, + IMAGE_REL_AMD64_SECREL, + IMAGE_REL_AMD64_SECREL7, + IMAGE_REL_AMD64_TOKEN, + IMAGE_REL_AMD64_SREL32, + IMAGE_REL_AMD64_PAIR, + IMAGE_REL_AMD64_SSPAN32, +}; + +enum arm_coff_reloc_type { + IMAGE_REL_ARM_ABSOLUTE, + IMAGE_REL_ARM_ADDR32, + IMAGE_REL_ARM_ADDR32N, + IMAGE_REL_ARM_BRANCH2, + IMAGE_REL_ARM_BRANCH1, + IMAGE_REL_ARM_SECTION, + IMAGE_REL_ARM_SECREL, +}; + +enum sh_coff_reloc_type { + IMAGE_REL_SH3_ABSOLUTE, + IMAGE_REL_SH3_DIRECT16, + IMAGE_REL_SH3_DIRECT32, + IMAGE_REL_SH3_DIRECT8, + IMAGE_REL_SH3_DIRECT8_WORD, + IMAGE_REL_SH3_DIRECT8_LONG, + IMAGE_REL_SH3_DIRECT4, + IMAGE_REL_SH3_DIRECT4_WORD, + IMAGE_REL_SH3_DIRECT4_LONG, + IMAGE_REL_SH3_PCREL8_WORD, + IMAGE_REL_SH3_PCREL8_LONG, + IMAGE_REL_SH3_PCREL12_WORD, + IMAGE_REL_SH3_STARTOF_SECTION, + IMAGE_REL_SH3_SIZEOF_SECTION, + IMAGE_REL_SH3_SECTION, + IMAGE_REL_SH3_SECREL, + IMAGE_REL_SH3_DIRECT32_NB, + IMAGE_REL_SH3_GPREL4_LONG, + IMAGE_REL_SH3_TOKEN, + IMAGE_REL_SHM_PCRELPT, + IMAGE_REL_SHM_REFLO, + IMAGE_REL_SHM_REFHALF, + IMAGE_REL_SHM_RELLO, + IMAGE_REL_SHM_RELHALF, + IMAGE_REL_SHM_PAIR, + IMAGE_REL_SHM_NOMODE, +}; + +enum ppc_coff_reloc_type { + IMAGE_REL_PPC_ABSOLUTE, + IMAGE_REL_PPC_ADDR64, + IMAGE_REL_PPC_ADDR32, + IMAGE_REL_PPC_ADDR24, + IMAGE_REL_PPC_ADDR16, + IMAGE_REL_PPC_ADDR14, + IMAGE_REL_PPC_REL24, + IMAGE_REL_PPC_REL14, + IMAGE_REL_PPC_ADDR32N, + IMAGE_REL_PPC_SECREL, + IMAGE_REL_PPC_SECTION, + IMAGE_REL_PPC_SECREL16, + IMAGE_REL_PPC_REFHI, + IMAGE_REL_PPC_REFLO, + IMAGE_REL_PPC_PAIR, + IMAGE_REL_PPC_SECRELLO, + IMAGE_REL_PPC_GPREL, + IMAGE_REL_PPC_TOKEN, +}; + +enum x86_coff_reloc_type { + IMAGE_REL_I386_ABSOLUTE, + IMAGE_REL_I386_DIR16, + IMAGE_REL_I386_REL16, + IMAGE_REL_I386_DIR32, + IMAGE_REL_I386_DIR32NB, + IMAGE_REL_I386_SEG12, + IMAGE_REL_I386_SECTION, + IMAGE_REL_I386_SECREL, + IMAGE_REL_I386_TOKEN, + IMAGE_REL_I386_SECREL7, + IMAGE_REL_I386_REL32, +}; + +enum ia64_coff_reloc_type { + IMAGE_REL_IA64_ABSOLUTE, + IMAGE_REL_IA64_IMM14, + IMAGE_REL_IA64_IMM22, + IMAGE_REL_IA64_IMM64, + IMAGE_REL_IA64_DIR32, + IMAGE_REL_IA64_DIR64, + IMAGE_REL_IA64_PCREL21B, + IMAGE_REL_IA64_PCREL21M, + IMAGE_REL_IA64_PCREL21F, + IMAGE_REL_IA64_GPREL22, + IMAGE_REL_IA64_LTOFF22, + IMAGE_REL_IA64_SECTION, + IMAGE_REL_IA64_SECREL22, + IMAGE_REL_IA64_SECREL64I, + IMAGE_REL_IA64_SECREL32, + IMAGE_REL_IA64_DIR32NB, + IMAGE_REL_IA64_SREL14, + IMAGE_REL_IA64_SREL22, + IMAGE_REL_IA64_SREL32, + IMAGE_REL_IA64_UREL32, + IMAGE_REL_IA64_PCREL60X, + IMAGE_REL_IA64_PCREL60B, + IMAGE_REL_IA64_PCREL60F, + IMAGE_REL_IA64_PCREL60I, + IMAGE_REL_IA64_PCREL60M, + IMAGE_REL_IA64_IMMGPREL6, + IMAGE_REL_IA64_TOKEN, + IMAGE_REL_IA64_GPREL32, + IMAGE_REL_IA64_ADDEND, +}; + +struct coff_reloc { + uint32_t virtual_address; + uint32_t symbol_table_index; + union { + enum x64_coff_reloc_type x64_type; + enum arm_coff_reloc_type arm_type; + enum sh_coff_reloc_type sh_type; + enum ppc_coff_reloc_type ppc_type; + enum x86_coff_reloc_type x86_type; + enum ia64_coff_reloc_type ia64_type; + uint16_t data; + }; +}; + +/* + * Definitions for the contents of the certs data block + */ +#define WIN_CERT_TYPE_PKCS_SIGNED_DATA 0x0002 +#define WIN_CERT_TYPE_EFI_OKCS115 0x0EF0 +#define WIN_CERT_TYPE_EFI_GUID 0x0EF1 + +#define WIN_CERT_REVISION_1_0 0x0100 +#define WIN_CERT_REVISION_2_0 0x0200 + +struct win_certificate { + uint32_t length; + uint16_t revision; + uint16_t cert_type; +}; + +#endif /* __LINUX_PE_H */ -- cgit v1.2.3 From 26d1164be37f1145a96af15f294122876d8e5c77 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 1 Jul 2014 16:02:51 +0100 Subject: pefile: Parse a PE binary to find a key and a signature contained therein Parse a PE binary to find a key and a signature contained therein. Later patches will check the signature and add the key if the signature checks out. Signed-off-by: David Howells Acked-by: Vivek Goyal Reviewed-by: Kees Cook --- crypto/asymmetric_keys/Kconfig | 9 ++ crypto/asymmetric_keys/Makefile | 8 ++ crypto/asymmetric_keys/verify_pefile.c | 163 +++++++++++++++++++++++++++++++++ crypto/asymmetric_keys/verify_pefile.h | 37 ++++++++ include/linux/verify_pefile.h | 18 ++++ 5 files changed, 235 insertions(+) create mode 100644 crypto/asymmetric_keys/verify_pefile.c create mode 100644 crypto/asymmetric_keys/verify_pefile.h create mode 100644 include/linux/verify_pefile.h (limited to 'include') diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 14cac2860afa..ca41be5631c7 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -59,4 +59,13 @@ config PKCS7_TEST_KEY This is intended for testing the PKCS#7 parser. +config SIGNED_PE_FILE_VERIFICATION + bool "Support for PE file signature verification" + depends on PKCS7_MESSAGE_PARSER=y + select ASN1 + select OID_REGISTRY + help + This option provides support for verifying the signature(s) on a + signed PE binary. + endif # ASYMMETRIC_KEY_TYPE diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index 92d0e9af24d5..3e4de5297015 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -47,3 +47,11 @@ clean-files += pkcs7-asn1.c pkcs7-asn1.h obj-$(CONFIG_PKCS7_TEST_KEY) += pkcs7_test_key.o pkcs7_test_key-y := \ pkcs7_key_type.o + +# +# Signed PE binary-wrapped key handling +# +obj-$(CONFIG_SIGNED_PE_FILE_VERIFICATION) += verify_signed_pefile.o + +verify_signed_pefile-y := \ + verify_pefile.o diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c new file mode 100644 index 000000000000..aec7c509404e --- /dev/null +++ b/crypto/asymmetric_keys/verify_pefile.c @@ -0,0 +1,163 @@ +/* Parse a signed PE binary + * + * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "PEFILE: "fmt +#include +#include +#include +#include +#include +#include +#include +#include "verify_pefile.h" + +/* + * Parse a PE binary. + */ +static int pefile_parse_binary(const void *pebuf, unsigned int pelen, + struct pefile_context *ctx) +{ + const struct mz_hdr *mz = pebuf; + const struct pe_hdr *pe; + const struct pe32_opt_hdr *pe32; + const struct pe32plus_opt_hdr *pe64; + const struct data_directory *ddir; + const struct data_dirent *dde; + const struct section_header *secs, *sec; + size_t cursor, datalen = pelen; + + kenter(""); + +#define chkaddr(base, x, s) \ + do { \ + if ((x) < base || (s) >= datalen || (x) > datalen - (s)) \ + return -ELIBBAD; \ + } while (0) + + chkaddr(0, 0, sizeof(*mz)); + if (mz->magic != MZ_MAGIC) + return -ELIBBAD; + cursor = sizeof(*mz); + + chkaddr(cursor, mz->peaddr, sizeof(*pe)); + pe = pebuf + mz->peaddr; + if (pe->magic != PE_MAGIC) + return -ELIBBAD; + cursor = mz->peaddr + sizeof(*pe); + + chkaddr(0, cursor, sizeof(pe32->magic)); + pe32 = pebuf + cursor; + pe64 = pebuf + cursor; + + switch (pe32->magic) { + case PE_OPT_MAGIC_PE32: + chkaddr(0, cursor, sizeof(*pe32)); + ctx->image_checksum_offset = + (unsigned long)&pe32->csum - (unsigned long)pebuf; + ctx->header_size = pe32->header_size; + cursor += sizeof(*pe32); + ctx->n_data_dirents = pe32->data_dirs; + break; + + case PE_OPT_MAGIC_PE32PLUS: + chkaddr(0, cursor, sizeof(*pe64)); + ctx->image_checksum_offset = + (unsigned long)&pe64->csum - (unsigned long)pebuf; + ctx->header_size = pe64->header_size; + cursor += sizeof(*pe64); + ctx->n_data_dirents = pe64->data_dirs; + break; + + default: + pr_debug("Unknown PEOPT magic = %04hx\n", pe32->magic); + return -ELIBBAD; + } + + pr_debug("checksum @ %x\n", ctx->image_checksum_offset); + pr_debug("header size = %x\n", ctx->header_size); + + if (cursor >= ctx->header_size || ctx->header_size >= datalen) + return -ELIBBAD; + + if (ctx->n_data_dirents > (ctx->header_size - cursor) / sizeof(*dde)) + return -ELIBBAD; + + ddir = pebuf + cursor; + cursor += sizeof(*dde) * ctx->n_data_dirents; + + ctx->cert_dirent_offset = + (unsigned long)&ddir->certs - (unsigned long)pebuf; + ctx->certs_size = ddir->certs.size; + + if (!ddir->certs.virtual_address || !ddir->certs.size) { + pr_debug("Unsigned PE binary\n"); + return -EKEYREJECTED; + } + + chkaddr(ctx->header_size, ddir->certs.virtual_address, + ddir->certs.size); + ctx->sig_offset = ddir->certs.virtual_address; + ctx->sig_len = ddir->certs.size; + pr_debug("cert = %x @%x [%*ph]\n", + ctx->sig_len, ctx->sig_offset, + ctx->sig_len, pebuf + ctx->sig_offset); + + ctx->n_sections = pe->sections; + if (ctx->n_sections > (ctx->header_size - cursor) / sizeof(*sec)) + return -ELIBBAD; + ctx->secs = secs = pebuf + cursor; + + return 0; +} + +/** + * verify_pefile_signature - Verify the signature on a PE binary image + * @pebuf: Buffer containing the PE binary image + * @pelen: Length of the binary image + * @trust_keyring: Signing certificates to use as starting points + * @_trusted: Set to true if trustworth, false otherwise + * + * Validate that the certificate chain inside the PKCS#7 message inside the PE + * binary image intersects keys we already know and trust. + * + * Returns, in order of descending priority: + * + * (*) -ELIBBAD if the image cannot be parsed, or: + * + * (*) -EKEYREJECTED if a signature failed to match for which we have a valid + * key, or: + * + * (*) 0 if at least one signature chain intersects with the keys in the trust + * keyring, or: + * + * (*) -ENOPKG if a suitable crypto module couldn't be found for a check on a + * chain. + * + * (*) -ENOKEY if we couldn't find a match for any of the signature chains in + * the message. + * + * May also return -ENOMEM. + */ +int verify_pefile_signature(const void *pebuf, unsigned pelen, + struct key *trusted_keyring, bool *_trusted) +{ + struct pefile_context ctx; + int ret; + + kenter(""); + + memset(&ctx, 0, sizeof(ctx)); + ret = pefile_parse_binary(pebuf, pelen, &ctx); + if (ret < 0) + return ret; + + return -ENOANO; // Not yet complete +} diff --git a/crypto/asymmetric_keys/verify_pefile.h b/crypto/asymmetric_keys/verify_pefile.h new file mode 100644 index 000000000000..e165d23458d4 --- /dev/null +++ b/crypto/asymmetric_keys/verify_pefile.h @@ -0,0 +1,37 @@ +/* PE Binary parser bits + * + * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include +#include +#include + +struct pefile_context { + unsigned header_size; + unsigned image_checksum_offset; + unsigned cert_dirent_offset; + unsigned n_data_dirents; + unsigned n_sections; + unsigned certs_size; + unsigned sig_offset; + unsigned sig_len; + const struct section_header *secs; + struct pkcs7_message *pkcs7; + + /* PKCS#7 MS Individual Code Signing content */ + const void *digest; /* Digest */ + unsigned digest_len; /* Digest length */ + enum hash_algo digest_algo; /* Digest algorithm */ +}; + +#define kenter(FMT, ...) \ + pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__) +#define kleave(FMT, ...) \ + pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__) diff --git a/include/linux/verify_pefile.h b/include/linux/verify_pefile.h new file mode 100644 index 000000000000..ac34819214f9 --- /dev/null +++ b/include/linux/verify_pefile.h @@ -0,0 +1,18 @@ +/* Signed PE file verification + * + * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _LINUX_VERIFY_PEFILE_H +#define _LINUX_VERIFY_PEFILE_H + +extern int verify_pefile_signature(const void *pebuf, unsigned pelen, + struct key *trusted_keyring, bool *_trusted); + +#endif /* _LINUX_VERIFY_PEFILE_H */ -- cgit v1.2.3 From 4c0b4b1d1ae0cbc86f150e2905a1c3d2a17b7c1e Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 1 Jul 2014 16:02:52 +0100 Subject: pefile: Parse the "Microsoft individual code signing" data blob The PKCS#7 certificate should contain a "Microsoft individual code signing" data blob as its signed content. This blob contains a digest of the signed content of the PE binary and the OID of the digest algorithm used (typically SHA256). Signed-off-by: David Howells Acked-by: Vivek Goyal Reviewed-by: Kees Cook --- crypto/asymmetric_keys/Makefile | 9 ++- crypto/asymmetric_keys/mscode.asn1 | 28 ++++++++ crypto/asymmetric_keys/mscode_parser.c | 120 +++++++++++++++++++++++++++++++++ crypto/asymmetric_keys/verify_pefile.c | 7 ++ crypto/asymmetric_keys/verify_pefile.h | 5 ++ include/linux/oid_registry.h | 7 +- 6 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 crypto/asymmetric_keys/mscode.asn1 create mode 100644 crypto/asymmetric_keys/mscode_parser.c (limited to 'include') diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index 3e4de5297015..e47fcd9ac5e8 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -54,4 +54,11 @@ pkcs7_test_key-y := \ obj-$(CONFIG_SIGNED_PE_FILE_VERIFICATION) += verify_signed_pefile.o verify_signed_pefile-y := \ - verify_pefile.o + verify_pefile.o \ + mscode_parser.o \ + mscode-asn1.o + +$(obj)/mscode_parser.o: $(obj)/mscode-asn1.h $(obj)/mscode-asn1.h +$(obj)/mscode-asn1.o: $(obj)/mscode-asn1.c $(obj)/mscode-asn1.h + +clean-files += mscode-asn1.c mscode-asn1.h diff --git a/crypto/asymmetric_keys/mscode.asn1 b/crypto/asymmetric_keys/mscode.asn1 new file mode 100644 index 000000000000..6d09ba48c41c --- /dev/null +++ b/crypto/asymmetric_keys/mscode.asn1 @@ -0,0 +1,28 @@ +--- Microsoft individual code signing data blob parser +--- +--- Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. +--- Written by David Howells (dhowells@redhat.com) +--- +--- This program is free software; you can redistribute it and/or +--- modify it under the terms of the GNU General Public Licence +--- as published by the Free Software Foundation; either version +--- 2 of the Licence, or (at your option) any later version. +--- + +MSCode ::= SEQUENCE { + type SEQUENCE { + contentType ContentType, + parameters ANY + }, + content SEQUENCE { + digestAlgorithm DigestAlgorithmIdentifier, + digest OCTET STRING ({ mscode_note_digest }) + } +} + +ContentType ::= OBJECT IDENTIFIER ({ mscode_note_content_type }) + +DigestAlgorithmIdentifier ::= SEQUENCE { + algorithm OBJECT IDENTIFIER ({ mscode_note_digest_algo }), + parameters ANY OPTIONAL +} diff --git a/crypto/asymmetric_keys/mscode_parser.c b/crypto/asymmetric_keys/mscode_parser.c new file mode 100644 index 000000000000..09336c32b3d4 --- /dev/null +++ b/crypto/asymmetric_keys/mscode_parser.c @@ -0,0 +1,120 @@ +/* Parse a Microsoft Individual Code Signing blob + * + * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "MSCODE: "fmt +#include +#include +#include +#include +#include +#include "verify_pefile.h" +#include "mscode-asn1.h" + +/* + * Parse a Microsoft Individual Code Signing blob + */ +int mscode_parse(struct pefile_context *ctx) +{ + const void *content_data; + size_t data_len; + int ret; + + ret = pkcs7_get_content_data(ctx->pkcs7, &content_data, &data_len, 1); + + if (ret) { + pr_debug("PKCS#7 message does not contain data\n"); + return ret; + } + + pr_devel("Data: %zu [%*ph]\n", data_len, (unsigned)(data_len), + content_data); + + return asn1_ber_decoder(&mscode_decoder, ctx, content_data, data_len); +} + +/* + * Check the content type OID + */ +int mscode_note_content_type(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + enum OID oid; + + oid = look_up_OID(value, vlen); + if (oid == OID__NR) { + char buffer[50]; + + sprint_oid(value, vlen, buffer, sizeof(buffer)); + pr_err("Unknown OID: %s\n", buffer); + return -EBADMSG; + } + + if (oid != OID_msIndividualSPKeyPurpose) { + pr_err("Unexpected content type OID %u\n", oid); + return -EBADMSG; + } + + return 0; +} + +/* + * Note the digest algorithm OID + */ +int mscode_note_digest_algo(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pefile_context *ctx = context; + char buffer[50]; + enum OID oid; + + oid = look_up_OID(value, vlen); + switch (oid) { + case OID_md4: + ctx->digest_algo = HASH_ALGO_MD4; + break; + case OID_md5: + ctx->digest_algo = HASH_ALGO_MD5; + break; + case OID_sha1: + ctx->digest_algo = HASH_ALGO_SHA1; + break; + case OID_sha256: + ctx->digest_algo = HASH_ALGO_SHA256; + break; + + case OID__NR: + sprint_oid(value, vlen, buffer, sizeof(buffer)); + pr_err("Unknown OID: %s\n", buffer); + return -EBADMSG; + + default: + pr_err("Unsupported content type: %u\n", oid); + return -ENOPKG; + } + + return 0; +} + +/* + * Note the digest we're guaranteeing with this certificate + */ +int mscode_note_digest(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pefile_context *ctx = context; + + ctx->digest = value; + ctx->digest_len = vlen; + return 0; +} diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c index 13f3b44b5046..b975918e82d2 100644 --- a/crypto/asymmetric_keys/verify_pefile.c +++ b/crypto/asymmetric_keys/verify_pefile.c @@ -245,6 +245,13 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen, goto error; } + ret = mscode_parse(&ctx); + if (ret < 0) + goto error; + + pr_debug("Digest: %u [%*ph]\n", + ctx.digest_len, ctx.digest_len, ctx.digest); + ret = -ENOANO; // Not yet complete error: diff --git a/crypto/asymmetric_keys/verify_pefile.h b/crypto/asymmetric_keys/verify_pefile.h index e165d23458d4..55d5f7ebc45a 100644 --- a/crypto/asymmetric_keys/verify_pefile.h +++ b/crypto/asymmetric_keys/verify_pefile.h @@ -35,3 +35,8 @@ struct pefile_context { pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__) #define kleave(FMT, ...) \ pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__) + +/* + * mscode_parser.c + */ +extern int mscode_parse(struct pefile_context *ctx); diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h index edeff85cb1e8..c2bbf672b84e 100644 --- a/include/linux/oid_registry.h +++ b/include/linux/oid_registry.h @@ -52,8 +52,13 @@ enum OID { OID_md4, /* 1.2.840.113549.2.4 */ OID_md5, /* 1.2.840.113549.2.5 */ - OID_certAuthInfoAccess, /* 1.3.6.1.5.5.7.1.1 */ + /* Microsoft Authenticode & Software Publishing */ + OID_msIndirectData, /* 1.3.6.1.4.1.311.2.1.4 */ + OID_msPeImageDataObjId, /* 1.3.6.1.4.1.311.2.1.15 */ + OID_msIndividualSPKeyPurpose, /* 1.3.6.1.4.1.311.2.1.21 */ OID_msOutlookExpress, /* 1.3.6.1.4.1.311.16.4 */ + + OID_certAuthInfoAccess, /* 1.3.6.1.5.5.7.1.1 */ OID_sha1, /* 1.3.14.3.2.26 */ OID_sha256, /* 2.16.840.1.101.3.4.2.1 */ -- cgit v1.2.3 From 51499aa912e0d6f5b3497e2b1121309f579e78ed Mon Sep 17 00:00:00 2001 From: Alexey Skidanov Date: Tue, 8 Jul 2014 17:30:17 +0300 Subject: iommu/amd: Moving PPR fault flags macros definitions Any kernel source registering the invalid PPR calback may include the header file with PPR fault flags macros definitions. Thus we move them to include/linux/amd-iommu.h Signed-off-by: Alexey Skidanov Signed-off-by: Oded Gabbay Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu_types.h | 6 ------ include/linux/amd-iommu.h | 7 +++++++ 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h index f1a5abf11acf..e5f1cd4728ed 100644 --- a/drivers/iommu/amd_iommu_types.h +++ b/drivers/iommu/amd_iommu_types.h @@ -390,12 +390,6 @@ struct amd_iommu_fault { }; -#define PPR_FAULT_EXEC (1 << 1) -#define PPR_FAULT_READ (1 << 2) -#define PPR_FAULT_WRITE (1 << 5) -#define PPR_FAULT_USER (1 << 6) -#define PPR_FAULT_RSVD (1 << 7) -#define PPR_FAULT_GN (1 << 8) struct iommu_domain; diff --git a/include/linux/amd-iommu.h b/include/linux/amd-iommu.h index 15f6b9edd0b1..2b08e79f5100 100644 --- a/include/linux/amd-iommu.h +++ b/include/linux/amd-iommu.h @@ -119,6 +119,13 @@ typedef int (*amd_iommu_invalid_ppr_cb)(struct pci_dev *pdev, extern int amd_iommu_set_invalid_ppr_cb(struct pci_dev *pdev, amd_iommu_invalid_ppr_cb cb); +#define PPR_FAULT_EXEC (1 << 1) +#define PPR_FAULT_READ (1 << 2) +#define PPR_FAULT_WRITE (1 << 5) +#define PPR_FAULT_USER (1 << 6) +#define PPR_FAULT_RSVD (1 << 7) +#define PPR_FAULT_GN (1 << 8) + /** * amd_iommu_device_info() - Get information about IOMMUv2 support of a * PCI device -- cgit v1.2.3 From 7450e90bbb8d834c190cc8100d1cc41888358c7c Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 9 Jul 2014 10:08:07 -0400 Subject: cgroup: remove CGRP_ROOT_OPTION_MASK cgroup_root->flags only contains CGRP_ROOT_* flags and there's no reason to mask the flags. Remove CGRP_ROOT_OPTION_MASK. This doesn't cause any behavior differences. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 3 --- kernel/cgroup.c | 7 +++---- 2 files changed, 3 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 28853e771f3b..c4901c19668b 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -320,9 +320,6 @@ enum { CGRP_ROOT_NOPREFIX = (1 << 1), /* mounted subsystems have no named prefix */ CGRP_ROOT_XATTR = (1 << 2), /* supports extended attributes */ - - /* mount options live below bit 16 */ - CGRP_ROOT_OPTION_MASK = (1 << 16) - 1, }; /* diff --git a/kernel/cgroup.c b/kernel/cgroup.c index cd02e99d5d3b..5411fffa4b70 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -1490,11 +1490,10 @@ static int cgroup_remount(struct kernfs_root *kf_root, int *flags, char *data) removed_mask = root->subsys_mask & ~opts.subsys_mask; /* Don't allow flags or name to change at remount */ - if (((opts.flags ^ root->flags) & CGRP_ROOT_OPTION_MASK) || + if ((opts.flags ^ root->flags) || (opts.name && strcmp(opts.name, root->name))) { pr_err("option or name mismatch, new: 0x%x \"%s\", old: 0x%x \"%s\"\n", - opts.flags & CGRP_ROOT_OPTION_MASK, opts.name ?: "", - root->flags & CGRP_ROOT_OPTION_MASK, root->name); + opts.flags, opts.name ?: "", root->flags, root->name); ret = -EINVAL; goto out_unlock; } @@ -1762,7 +1761,7 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type, goto out_unlock; } - if ((root->flags ^ opts.flags) & CGRP_ROOT_OPTION_MASK) { + if (root->flags ^ opts.flags) { if ((root->flags | opts.flags) & CGRP_ROOT_SANE_BEHAVIOR) { pr_err("sane_behavior: new mount options should match the existing superblock\n"); ret = -EINVAL; -- cgit v1.2.3 From aa6ec29bee8692ce232132f1a1ea2a1f9196610e Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 9 Jul 2014 10:08:08 -0400 Subject: cgroup: remove sane_behavior support on non-default hierarchies sane_behavior has been used as a development vehicle for the default unified hierarchy. Now that the default hierarchy is in place, the flag became redundant and confusing as its usage is allowed on all hierarchies. There are gonna be either the default hierarchy or legacy ones. Let's make that clear by removing sane_behavior support on non-default hierarchies. This patch replaces cgroup_sane_behavior() with cgroup_on_dfl(). The comment on top of CGRP_ROOT_SANE_BEHAVIOR is moved to on top of cgroup_on_dfl() with sane_behavior specific part dropped. On the default and legacy hierarchies w/o sane_behavior, this shouldn't cause any behavior differences. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal Acked-by: Li Zefan Cc: Johannes Weiner Cc: Michal Hocko --- block/blk-throttle.c | 6 +-- include/linux/cgroup.h | 125 +++++++++++++++++++++---------------------------- kernel/cgroup.c | 19 ++++---- kernel/cpuset.c | 33 ++++++------- mm/memcontrol.c | 7 +-- 5 files changed, 86 insertions(+), 104 deletions(-) (limited to 'include') diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 3fdb21a390c1..9273d0969ebd 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -412,13 +412,13 @@ static void throtl_pd_init(struct blkcg_gq *blkg) int rw; /* - * If sane_hierarchy is enabled, we switch to properly hierarchical + * If on the default hierarchy, we switch to properly hierarchical * behavior where limits on a given throtl_grp are applied to the * whole subtree rather than just the group itself. e.g. If 16M * read_bps limit is set on the root group, the whole system can't * exceed 16M for the device. * - * If sane_hierarchy is not enabled, the broken flat hierarchy + * If not on the default hierarchy, the broken flat hierarchy * behavior is retained where all throtl_grps are treated as if * they're all separate root groups right below throtl_data. * Limits of a group don't interact with limits of other groups @@ -426,7 +426,7 @@ static void throtl_pd_init(struct blkcg_gq *blkg) */ parent_sq = &td->service_queue; - if (cgroup_sane_behavior(blkg->blkcg->css.cgroup) && blkg->parent) + if (cgroup_on_dfl(blkg->blkcg->css.cgroup) && blkg->parent) parent_sq = &blkg_to_tg(blkg->parent)->service_queue; throtl_service_queue_init(&tg->service_queue, parent_sq); diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index c4901c19668b..7bb274487c89 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -256,68 +256,7 @@ struct cgroup { /* cgroup_root->flags */ enum { - /* - * Unfortunately, cgroup core and various controllers are riddled - * with idiosyncrasies and pointless options. The following flag, - * when set, will force sane behavior - some options are forced on, - * others are disallowed, and some controllers will change their - * hierarchical or other behaviors. - * - * The set of behaviors affected by this flag are still being - * determined and developed and the mount option for this flag is - * prefixed with __DEVEL__. The prefix will be dropped once we - * reach the point where all behaviors are compatible with the - * planned unified hierarchy, which will automatically turn on this - * flag. - * - * The followings are the behaviors currently affected this flag. - * - * - Mount options "noprefix", "xattr", "clone_children", - * "release_agent" and "name" are disallowed. - * - * - When mounting an existing superblock, mount options should - * match. - * - * - Remount is disallowed. - * - * - rename(2) is disallowed. - * - * - "tasks" is removed. Everything should be at process - * granularity. Use "cgroup.procs" instead. - * - * - "cgroup.procs" is not sorted. pids will be unique unless they - * got recycled inbetween reads. - * - * - "release_agent" and "notify_on_release" are removed. - * Replacement notification mechanism will be implemented. - * - * - "cgroup.clone_children" is removed. - * - * - "cgroup.subtree_populated" is available. Its value is 0 if - * the cgroup and its descendants contain no task; otherwise, 1. - * The file also generates kernfs notification which can be - * monitored through poll and [di]notify when the value of the - * file changes. - * - * - If mount is requested with sane_behavior but without any - * subsystem, the default unified hierarchy is mounted. - * - * - cpuset: tasks will be kept in empty cpusets when hotplug happens - * and take masks of ancestors with non-empty cpus/mems, instead of - * being moved to an ancestor. - * - * - cpuset: a task can be moved into an empty cpuset, and again it - * takes masks of ancestors. - * - * - memcg: use_hierarchy is on by default and the cgroup file for - * the flag is not created. - * - * - blkcg: blk-throttle becomes properly hierarchical. - * - * - debug: disallowed on the default hierarchy. - */ - CGRP_ROOT_SANE_BEHAVIOR = (1 << 0), - + CGRP_ROOT_SANE_BEHAVIOR = (1 << 0), /* __DEVEL__sane_behavior specified */ CGRP_ROOT_NOPREFIX = (1 << 1), /* mounted subsystems have no named prefix */ CGRP_ROOT_XATTR = (1 << 2), /* supports extended attributes */ }; @@ -531,20 +470,64 @@ struct cftype { extern struct cgroup_root cgrp_dfl_root; extern struct css_set init_css_set; +/** + * cgroup_on_dfl - test whether a cgroup is on the default hierarchy + * @cgrp: the cgroup of interest + * + * The default hierarchy is the v2 interface of cgroup and this function + * can be used to test whether a cgroup is on the default hierarchy for + * cases where a subsystem should behave differnetly depending on the + * interface version. + * + * The set of behaviors which change on the default hierarchy are still + * being determined and the mount option is prefixed with __DEVEL__. + * + * List of changed behaviors: + * + * - Mount options "noprefix", "xattr", "clone_children", "release_agent" + * and "name" are disallowed. + * + * - When mounting an existing superblock, mount options should match. + * + * - Remount is disallowed. + * + * - rename(2) is disallowed. + * + * - "tasks" is removed. Everything should be at process granularity. Use + * "cgroup.procs" instead. + * + * - "cgroup.procs" is not sorted. pids will be unique unless they got + * recycled inbetween reads. + * + * - "release_agent" and "notify_on_release" are removed. Replacement + * notification mechanism will be implemented. + * + * - "cgroup.clone_children" is removed. + * + * - "cgroup.subtree_populated" is available. Its value is 0 if the cgroup + * and its descendants contain no task; otherwise, 1. The file also + * generates kernfs notification which can be monitored through poll and + * [di]notify when the value of the file changes. + * + * - cpuset: tasks will be kept in empty cpusets when hotplug happens and + * take masks of ancestors with non-empty cpus/mems, instead of being + * moved to an ancestor. + * + * - cpuset: a task can be moved into an empty cpuset, and again it takes + * masks of ancestors. + * + * - memcg: use_hierarchy is on by default and the cgroup file for the flag + * is not created. + * + * - blkcg: blk-throttle becomes properly hierarchical. + * + * - debug: disallowed on the default hierarchy. + */ static inline bool cgroup_on_dfl(const struct cgroup *cgrp) { return cgrp->root == &cgrp_dfl_root; } -/* - * See the comment above CGRP_ROOT_SANE_BEHAVIOR for details. This - * function can be called as long as @cgrp is accessible. - */ -static inline bool cgroup_sane_behavior(const struct cgroup *cgrp) -{ - return cgrp->root->flags & CGRP_ROOT_SANE_BEHAVIOR; -} - /* no synchronization, the result can only be used as a hint */ static inline bool cgroup_has_tasks(struct cgroup *cgrp) { diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 0ea54af6b133..fb07c6d43aff 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -1470,8 +1470,8 @@ static int cgroup_remount(struct kernfs_root *kf_root, int *flags, char *data) struct cgroup_sb_opts opts; unsigned int added_mask, removed_mask; - if (root->flags & CGRP_ROOT_SANE_BEHAVIOR) { - pr_err("sane_behavior: remount is not allowed\n"); + if (root == &cgrp_dfl_root) { + pr_err("remount is not allowed\n"); return -EINVAL; } @@ -2943,9 +2943,9 @@ static int cgroup_rename(struct kernfs_node *kn, struct kernfs_node *new_parent, /* * This isn't a proper migration and its usefulness is very - * limited. Disallow if sane_behavior. + * limited. Disallow on the default hierarchy. */ - if (cgroup_sane_behavior(cgrp)) + if (cgroup_on_dfl(cgrp)) return -EPERM; /* @@ -3031,7 +3031,7 @@ static int cgroup_addrm_files(struct cgroup *cgrp, struct cftype cfts[], /* does cft->flags tell us to skip this file on @cgrp? */ if ((cft->flags & CFTYPE_ONLY_ON_DFL) && !cgroup_on_dfl(cgrp)) continue; - if ((cft->flags & CFTYPE_INSANE) && cgroup_sane_behavior(cgrp)) + if ((cft->flags & CFTYPE_INSANE) && cgroup_on_dfl(cgrp)) continue; if ((cft->flags & CFTYPE_NOT_ON_ROOT) && !cgroup_parent(cgrp)) continue; @@ -3764,8 +3764,9 @@ after: * * All this extra complexity was caused by the original implementation * committing to an entirely unnecessary property. In the long term, we - * want to do away with it. Explicitly scramble sort order if - * sane_behavior so that no such expectation exists in the new interface. + * want to do away with it. Explicitly scramble sort order if on the + * default hierarchy so that no such expectation exists in the new + * interface. * * Scrambling is done by swapping every two consecutive bits, which is * non-identity one-to-one mapping which disturbs sort order sufficiently. @@ -3780,7 +3781,7 @@ static pid_t pid_fry(pid_t pid) static pid_t cgroup_pid_fry(struct cgroup *cgrp, pid_t pid) { - if (cgroup_sane_behavior(cgrp)) + if (cgroup_on_dfl(cgrp)) return pid_fry(pid); else return pid; @@ -3883,7 +3884,7 @@ static int pidlist_array_load(struct cgroup *cgrp, enum cgroup_filetype type, css_task_iter_end(&it); length = n; /* now sort & (if procs) strip out duplicates */ - if (cgroup_sane_behavior(cgrp)) + if (cgroup_on_dfl(cgrp)) sort(array, length, sizeof(pid_t), fried_cmppid, NULL); else sort(array, length, sizeof(pid_t), cmppid, NULL); diff --git a/kernel/cpuset.c b/kernel/cpuset.c index f6b33c696224..f9d4807c869f 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -1383,12 +1383,9 @@ static int cpuset_can_attach(struct cgroup_subsys_state *css, mutex_lock(&cpuset_mutex); - /* - * We allow to move tasks into an empty cpuset if sane_behavior - * flag is set. - */ + /* allow moving tasks into an empty cpuset if on default hierarchy */ ret = -ENOSPC; - if (!cgroup_sane_behavior(css->cgroup) && + if (!cgroup_on_dfl(css->cgroup) && (cpumask_empty(cs->cpus_allowed) || nodes_empty(cs->mems_allowed))) goto out_unlock; @@ -2030,7 +2027,7 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs) static cpumask_t off_cpus; static nodemask_t off_mems; bool is_empty; - bool sane = cgroup_sane_behavior(cs->css.cgroup); + bool on_dfl = cgroup_on_dfl(cs->css.cgroup); retry: wait_event(cpuset_attach_wq, cs->attach_in_progress == 0); @@ -2054,12 +2051,12 @@ retry: mutex_unlock(&callback_mutex); /* - * If sane_behavior flag is set, we need to update tasks' cpumask - * for empty cpuset to take on ancestor's cpumask. Otherwise, don't - * call update_tasks_cpumask() if the cpuset becomes empty, as - * the tasks in it will be migrated to an ancestor. + * If on_dfl, we need to update tasks' cpumask for empty cpuset to + * take on ancestor's cpumask. Otherwise, don't call + * update_tasks_cpumask() if the cpuset becomes empty, as the tasks + * in it will be migrated to an ancestor. */ - if ((sane && cpumask_empty(cs->cpus_allowed)) || + if ((on_dfl && cpumask_empty(cs->cpus_allowed)) || (!cpumask_empty(&off_cpus) && !cpumask_empty(cs->cpus_allowed))) update_tasks_cpumask(cs); @@ -2068,12 +2065,12 @@ retry: mutex_unlock(&callback_mutex); /* - * If sane_behavior flag is set, we need to update tasks' nodemask - * for empty cpuset to take on ancestor's nodemask. Otherwise, don't - * call update_tasks_nodemask() if the cpuset becomes empty, as - * the tasks in it will be migratd to an ancestor. + * If on_dfl, we need to update tasks' nodemask for empty cpuset to + * take on ancestor's nodemask. Otherwise, don't call + * update_tasks_nodemask() if the cpuset becomes empty, as the + * tasks in it will be migratd to an ancestor. */ - if ((sane && nodes_empty(cs->mems_allowed)) || + if ((on_dfl && nodes_empty(cs->mems_allowed)) || (!nodes_empty(off_mems) && !nodes_empty(cs->mems_allowed))) update_tasks_nodemask(cs); @@ -2083,13 +2080,13 @@ retry: mutex_unlock(&cpuset_mutex); /* - * If sane_behavior flag is set, we'll keep tasks in empty cpusets. + * If on_dfl, we'll keep tasks in empty cpusets. * * Otherwise move tasks to the nearest ancestor with execution * resources. This is full cgroup operation which will * also call back into cpuset. Should be done outside any lock. */ - if (!sane && is_empty) + if (!on_dfl && is_empty) remove_tasks_in_empty_cpuset(cs); } diff --git a/mm/memcontrol.c b/mm/memcontrol.c index db536e90c8ee..a2a4bd69a7ae 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -7024,16 +7024,17 @@ static void mem_cgroup_move_task(struct cgroup_subsys_state *css, /* * Cgroup retains root cgroups across [un]mount cycles making it necessary - * to verify sane_behavior flag on each mount attempt. + * to verify whether we're attached to the default hierarchy on each mount + * attempt. */ static void mem_cgroup_bind(struct cgroup_subsys_state *root_css) { /* - * use_hierarchy is forced with sane_behavior. cgroup core + * use_hierarchy is forced on the default hierarchy. cgroup core * guarantees that @root doesn't have any children, so turning it * on for the root memcg is enough. */ - if (cgroup_sane_behavior(root_css->cgroup)) + if (cgroup_on_dfl(root_css->cgroup)) mem_cgroup_from_css(root_css)->use_hierarchy = true; } -- cgit v1.2.3 From 01a0f4aaaefff9f57bb17e6cc514c84ba43a7335 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 2 Jul 2014 14:34:13 +0100 Subject: mfd: tps65910: Rid data size incompatibility warn when building for 64bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extinguishes: ../drivers/mfd/tps65910.c: In function ‘tps65910_parse_dt’: ../drivers/mfd/tps65910.c:404:14: warning: cast from pointer to integer of different size Signed-off-by: Lee Jones --- drivers/mfd/tps65910.c | 10 +++++----- include/linux/mfd/tps65910.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c index f9e42ea1cb1a..f243e75d28f3 100644 --- a/drivers/mfd/tps65910.c +++ b/drivers/mfd/tps65910.c @@ -387,7 +387,7 @@ static const struct of_device_id tps65910_of_match[] = { MODULE_DEVICE_TABLE(of, tps65910_of_match); static struct tps65910_board *tps65910_parse_dt(struct i2c_client *client, - int *chip_id) + unsigned long *chip_id) { struct device_node *np = client->dev.of_node; struct tps65910_board *board_info; @@ -401,7 +401,7 @@ static struct tps65910_board *tps65910_parse_dt(struct i2c_client *client, return NULL; } - *chip_id = (int)match->data; + *chip_id = (unsigned long)match->data; board_info = devm_kzalloc(&client->dev, sizeof(*board_info), GFP_KERNEL); @@ -431,7 +431,7 @@ static struct tps65910_board *tps65910_parse_dt(struct i2c_client *client, #else static inline struct tps65910_board *tps65910_parse_dt(struct i2c_client *client, - int *chip_id) + unsigned long *chip_id) { return NULL; } @@ -453,14 +453,14 @@ static void tps65910_power_off(void) } static int tps65910_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { struct tps65910 *tps65910; struct tps65910_board *pmic_plat_data; struct tps65910_board *of_pmic_plat_data = NULL; struct tps65910_platform_data *init_data; + unsigned long chip_id = id->driver_data; int ret = 0; - int chip_id = id->driver_data; pmic_plat_data = dev_get_platdata(&i2c->dev); diff --git a/include/linux/mfd/tps65910.h b/include/linux/mfd/tps65910.h index 16c2335c2856..6483a6fdce59 100644 --- a/include/linux/mfd/tps65910.h +++ b/include/linux/mfd/tps65910.h @@ -892,7 +892,7 @@ struct tps65910 { struct device *dev; struct i2c_client *i2c_client; struct regmap *regmap; - unsigned int id; + unsigned long id; /* Client devices */ struct tps65910_pmic *pmic; -- cgit v1.2.3 From f27bc4873fa8b75cc1eba7b641eda7375dc72ccf Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sun, 4 May 2014 15:38:38 -0700 Subject: rcu: Document deadlock-avoidance information for rcu_read_unlock() Reported-by: Oleg Nesterov Signed-off-by: Paul E. McKenney Reviewed-by: Lai Jiangshan --- include/linux/rcupdate.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'include') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 6a94cc8b1ca0..c56ad15204ec 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -858,6 +858,34 @@ static inline void rcu_read_lock(void) /** * rcu_read_unlock() - marks the end of an RCU read-side critical section. * + * In most situations, rcu_read_unlock() is immune from deadlock. + * However, in kernels built with CONFIG_RCU_BOOST, rcu_read_unlock() + * is responsible for deboosting, which it does via rt_mutex_unlock(). + * Unfortunately, this function acquires the scheduler's runqueue and + * priority-inheritance spinlocks. This means that deadlock could result + * if the caller of rcu_read_unlock() already holds one of these locks or + * any lock that is ever acquired while holding them. + * + * That said, RCU readers are never priority boosted unless they were + * preempted. Therefore, one way to avoid deadlock is to make sure + * that preemption never happens within any RCU read-side critical + * section whose outermost rcu_read_unlock() is called with one of + * rt_mutex_unlock()'s locks held. Such preemption can be avoided in + * a number of ways, for example, by invoking preempt_disable() before + * critical section's outermost rcu_read_lock(). + * + * Given that the set of locks acquired by rt_mutex_unlock() might change + * at any time, a somewhat more future-proofed approach is to make sure + * that that preemption never happens within any RCU read-side critical + * section whose outermost rcu_read_unlock() is called with irqs disabled. + * This approach relies on the fact that rt_mutex_unlock() currently only + * acquires irq-disabled locks. + * + * The second of these two approaches is best in most situations, + * however, the first approach can also be useful, at least to those + * developers willing to keep abreast of the set of locks acquired by + * rt_mutex_unlock(). + * * See rcu_read_lock() for more information. */ static inline void rcu_read_unlock(void) -- cgit v1.2.3 From ab74fdfd4e11ec040f21cf87edc14fc9f62cc934 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sun, 4 May 2014 15:41:21 -0700 Subject: rcu: Handle obsolete references to TINY_PREEMPT_RCU Signed-off-by: Paul E. McKenney Reviewed-by: Lai Jiangshan --- include/linux/rcupdate.h | 17 ++++++++--------- init/Kconfig | 2 +- 2 files changed, 9 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index c56ad15204ec..d231aa17b1d7 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -826,15 +826,14 @@ static inline void rcu_preempt_sleep_check(void) * read-side critical section that would block in a !PREEMPT kernel. * But if you want the full story, read on! * - * In non-preemptible RCU implementations (TREE_RCU and TINY_RCU), it - * is illegal to block while in an RCU read-side critical section. In - * preemptible RCU implementations (TREE_PREEMPT_RCU and TINY_PREEMPT_RCU) - * in CONFIG_PREEMPT kernel builds, RCU read-side critical sections may - * be preempted, but explicit blocking is illegal. Finally, in preemptible - * RCU implementations in real-time (with -rt patchset) kernel builds, - * RCU read-side critical sections may be preempted and they may also - * block, but only when acquiring spinlocks that are subject to priority - * inheritance. + * In non-preemptible RCU implementations (TREE_RCU and TINY_RCU), + * it is illegal to block while in an RCU read-side critical section. + * In preemptible RCU implementations (TREE_PREEMPT_RCU) in CONFIG_PREEMPT + * kernel builds, RCU read-side critical sections may be preempted, + * but explicit blocking is illegal. Finally, in preemptible RCU + * implementations in real-time (with -rt patchset) kernel builds, RCU + * read-side critical sections may be preempted and they may also block, but + * only when acquiring spinlocks that are subject to priority inheritance. */ static inline void rcu_read_lock(void) { diff --git a/init/Kconfig b/init/Kconfig index 9d76b99af1b9..977b37806e95 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -505,7 +505,7 @@ config PREEMPT_RCU def_bool TREE_PREEMPT_RCU help This option enables preemptible-RCU code that is common between - the TREE_PREEMPT_RCU and TINY_PREEMPT_RCU implementations. + TREE_PREEMPT_RCU and, in the old days, TINY_PREEMPT_RCU. config RCU_STALL_COMMON def_bool ( TREE_RCU || TREE_PREEMPT_RCU || RCU_TRACE ) -- cgit v1.2.3 From abaa93d9e1de2c29297e69ddba8ddd38f15064cf Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 12 Jun 2014 13:30:25 -0700 Subject: rcu: Simplify priority boosting by putting rt_mutex in rcu_node RCU priority boosting currently checks for boosting via a pointer in task_struct. However, this is not needed: As Oleg noted, if the rt_mutex is placed in the rcu_node instead of on the booster's stack, the boostee can simply check it see if it owns the lock. This commit makes this change, shrinking task_struct by one pointer and the kernel by thirteen lines. Suggested-by: Oleg Nesterov Signed-off-by: Paul E. McKenney --- include/linux/init_task.h | 9 +-------- include/linux/sched.h | 6 ------ kernel/rcu/tree.h | 3 +++ kernel/rcu/tree_plugin.h | 25 +++++++++++-------------- 4 files changed, 15 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/include/linux/init_task.h b/include/linux/init_task.h index 6df7f9fe0d01..2bb4c4f3531a 100644 --- a/include/linux/init_task.h +++ b/include/linux/init_task.h @@ -102,12 +102,6 @@ extern struct group_info init_groups; #define INIT_IDS #endif -#ifdef CONFIG_RCU_BOOST -#define INIT_TASK_RCU_BOOST() \ - .rcu_boost_mutex = NULL, -#else -#define INIT_TASK_RCU_BOOST() -#endif #ifdef CONFIG_TREE_PREEMPT_RCU #define INIT_TASK_RCU_TREE_PREEMPT() \ .rcu_blocked_node = NULL, @@ -119,8 +113,7 @@ extern struct group_info init_groups; .rcu_read_lock_nesting = 0, \ .rcu_read_unlock_special = 0, \ .rcu_node_entry = LIST_HEAD_INIT(tsk.rcu_node_entry), \ - INIT_TASK_RCU_TREE_PREEMPT() \ - INIT_TASK_RCU_BOOST() + INIT_TASK_RCU_TREE_PREEMPT() #else #define INIT_TASK_RCU_PREEMPT(tsk) #endif diff --git a/include/linux/sched.h b/include/linux/sched.h index 306f4f0c987a..3cfbc05e66e6 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1270,9 +1270,6 @@ struct task_struct { #ifdef CONFIG_TREE_PREEMPT_RCU struct rcu_node *rcu_blocked_node; #endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */ -#ifdef CONFIG_RCU_BOOST - struct rt_mutex *rcu_boost_mutex; -#endif /* #ifdef CONFIG_RCU_BOOST */ #if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT) struct sched_info sched_info; @@ -2009,9 +2006,6 @@ static inline void rcu_copy_process(struct task_struct *p) #ifdef CONFIG_TREE_PREEMPT_RCU p->rcu_blocked_node = NULL; #endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */ -#ifdef CONFIG_RCU_BOOST - p->rcu_boost_mutex = NULL; -#endif /* #ifdef CONFIG_RCU_BOOST */ INIT_LIST_HEAD(&p->rcu_node_entry); } diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h index 3eeb919e26a2..60fb0eaa2d16 100644 --- a/kernel/rcu/tree.h +++ b/kernel/rcu/tree.h @@ -177,6 +177,9 @@ struct rcu_node { /* to carry out the boosting is fully */ /* released with no future boostee accesses */ /* before that rt_mutex is re-initialized. */ + struct rt_mutex boost_mtx; + /* Used only for the priority-boosting */ + /* side effect, not as a lock. */ unsigned long boost_time; /* When to start boosting (jiffies). */ struct task_struct *boost_kthread_task; diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index 9c811879d31e..719587af7b10 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -33,6 +33,7 @@ #define RCU_KTHREAD_PRIO 1 #ifdef CONFIG_RCU_BOOST +#include "../locking/rtmutex_common.h" #define RCU_BOOST_PRIO CONFIG_RCU_BOOST_PRIO #else #define RCU_BOOST_PRIO RCU_KTHREAD_PRIO @@ -336,7 +337,7 @@ void rcu_read_unlock_special(struct task_struct *t) unsigned long flags; struct list_head *np; #ifdef CONFIG_RCU_BOOST - struct rt_mutex *rbmp = NULL; + bool drop_boost_mutex = false; #endif /* #ifdef CONFIG_RCU_BOOST */ struct rcu_node *rnp; int special; @@ -398,11 +399,8 @@ void rcu_read_unlock_special(struct task_struct *t) #ifdef CONFIG_RCU_BOOST if (&t->rcu_node_entry == rnp->boost_tasks) rnp->boost_tasks = np; - /* Snapshot/clear ->rcu_boost_mutex with rcu_node lock held. */ - if (t->rcu_boost_mutex) { - rbmp = t->rcu_boost_mutex; - t->rcu_boost_mutex = NULL; - } + /* Snapshot ->boost_mtx ownership with rcu_node lock held. */ + drop_boost_mutex = rt_mutex_owner(&rnp->boost_mtx) == t; #endif /* #ifdef CONFIG_RCU_BOOST */ /* @@ -427,8 +425,8 @@ void rcu_read_unlock_special(struct task_struct *t) #ifdef CONFIG_RCU_BOOST /* Unboost if we were boosted. */ - if (rbmp) { - rt_mutex_unlock(rbmp); + if (drop_boost_mutex) { + rt_mutex_unlock(&rnp->boost_mtx); complete(&rnp->boost_completion); } #endif /* #ifdef CONFIG_RCU_BOOST */ @@ -1151,7 +1149,6 @@ static void rcu_wake_cond(struct task_struct *t, int status) static int rcu_boost(struct rcu_node *rnp) { unsigned long flags; - struct rt_mutex mtx; struct task_struct *t; struct list_head *tb; @@ -1202,14 +1199,14 @@ static int rcu_boost(struct rcu_node *rnp) * section. */ t = container_of(tb, struct task_struct, rcu_node_entry); - rt_mutex_init_proxy_locked(&mtx, t); - t->rcu_boost_mutex = &mtx; + rt_mutex_init_proxy_locked(&rnp->boost_mtx, t); init_completion(&rnp->boost_completion); raw_spin_unlock_irqrestore(&rnp->lock, flags); - rt_mutex_lock(&mtx); /* Side effect: boosts task t's priority. */ - rt_mutex_unlock(&mtx); /* Keep lockdep happy. */ + /* Lock only for side effect: boosts task t's priority. */ + rt_mutex_lock(&rnp->boost_mtx); + rt_mutex_unlock(&rnp->boost_mtx); /* Then keep lockdep happy. */ - /* Wait until boostee is done accessing mtx before reinitializing. */ + /* Wait for boostee to be done w/boost_mtx before reinitializing. */ wait_for_completion(&rnp->boost_completion); return ACCESS_ONCE(rnp->exp_tasks) != NULL || -- cgit v1.2.3 From c0f489d2c6fec8994c642c2ec925eb858727dc7b Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 4 Jun 2014 13:46:03 -0700 Subject: rcu: Bind grace-period kthreads to non-NO_HZ_FULL CPUs Binding the grace-period kthreads to the timekeeping CPU resulted in significant performance decreases for some workloads. For more detail, see: https://lkml.org/lkml/2014/6/3/395 for benchmark numbers https://lkml.org/lkml/2014/6/4/218 for CPU statistics It turns out that it is necessary to bind the grace-period kthreads to the timekeeping CPU only when all but CPU 0 is a nohz_full CPU on the one hand or if CONFIG_NO_HZ_FULL_SYSIDLE=y on the other. In other cases, it suffices to bind the grace-period kthreads to the set of non-nohz_full CPUs. This commit therefore creates a tick_nohz_not_full_mask that is the complement of tick_nohz_full_mask, and then binds the grace-period kthread to the set of CPUs indicated by this new mask, which covers the CONFIG_NO_HZ_FULL_SYSIDLE=n case. The CONFIG_NO_HZ_FULL_SYSIDLE=y case still binds the grace-period kthreads to the timekeeping CPU. This commit also includes the tick_nohz_full_enabled() check suggested by Frederic Weisbecker. Reported-by: Jet Chen Signed-off-by: Paul E. McKenney [ paulmck: Created housekeeping_affine() and housekeeping_mask per fweisbec feedback. ] --- include/linux/tick.h | 20 ++++++++++++++++++++ kernel/rcu/tree_plugin.h | 14 +++++++++----- kernel/time/tick-sched.c | 10 ++++++++++ 3 files changed, 39 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/tick.h b/include/linux/tick.h index b84773cb9f4c..06cc093ab7ad 100644 --- a/include/linux/tick.h +++ b/include/linux/tick.h @@ -12,6 +12,7 @@ #include #include #include +#include #ifdef CONFIG_GENERIC_CLOCKEVENTS @@ -162,6 +163,7 @@ static inline u64 get_cpu_iowait_time_us(int cpu, u64 *unused) { return -1; } #ifdef CONFIG_NO_HZ_FULL extern bool tick_nohz_full_running; extern cpumask_var_t tick_nohz_full_mask; +extern cpumask_var_t housekeeping_mask; static inline bool tick_nohz_full_enabled(void) { @@ -194,6 +196,24 @@ static inline void tick_nohz_full_kick_all(void) { } static inline void __tick_nohz_task_switch(struct task_struct *tsk) { } #endif +static inline bool is_housekeeping_cpu(int cpu) +{ +#ifdef CONFIG_NO_HZ_FULL + if (tick_nohz_full_enabled()) + return cpumask_test_cpu(cpu, housekeeping_mask); +#endif + return true; +} + +static inline void housekeeping_affine(struct task_struct *t) +{ +#ifdef CONFIG_NO_HZ_FULL + if (tick_nohz_full_enabled()) + set_cpus_allowed_ptr(t, housekeeping_mask); + +#endif +} + static inline void tick_nohz_full_check(void) { if (tick_nohz_full_enabled()) diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index 719587af7b10..b39ba7239bd6 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -2846,12 +2846,16 @@ static bool rcu_nohz_full_cpu(struct rcu_state *rsp) */ static void rcu_bind_gp_kthread(void) { -#ifdef CONFIG_NO_HZ_FULL - int cpu = tick_do_timer_cpu; + int __maybe_unused cpu; - if (cpu < 0 || cpu >= nr_cpu_ids) + if (!tick_nohz_full_enabled()) return; - if (raw_smp_processor_id() != cpu) +#ifdef CONFIG_NO_HZ_FULL_SYSIDLE + cpu = tick_do_timer_cpu; + if (cpu >= 0 && cpu < nr_cpu_ids && raw_smp_processor_id() != cpu) set_cpus_allowed_ptr(current, cpumask_of(cpu)); -#endif /* #ifdef CONFIG_NO_HZ_FULL */ +#else /* #ifdef CONFIG_NO_HZ_FULL_SYSIDLE */ + if (!is_housekeeping_cpu(raw_smp_processor_id())) + housekeeping_affine(current); +#endif /* #else #ifdef CONFIG_NO_HZ_FULL_SYSIDLE */ } diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 6558b7ac112d..f784d83e29f1 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -154,6 +154,7 @@ static void tick_sched_handle(struct tick_sched *ts, struct pt_regs *regs) #ifdef CONFIG_NO_HZ_FULL cpumask_var_t tick_nohz_full_mask; +cpumask_var_t housekeeping_mask; bool tick_nohz_full_running; static bool can_stop_full_tick(void) @@ -281,6 +282,7 @@ static int __init tick_nohz_full_setup(char *str) int cpu; alloc_bootmem_cpumask_var(&tick_nohz_full_mask); + alloc_bootmem_cpumask_var(&housekeeping_mask); if (cpulist_parse(str, tick_nohz_full_mask) < 0) { pr_warning("NOHZ: Incorrect nohz_full cpumask\n"); return 1; @@ -291,6 +293,8 @@ static int __init tick_nohz_full_setup(char *str) pr_warning("NO_HZ: Clearing %d from nohz_full range for timekeeping\n", cpu); cpumask_clear_cpu(cpu, tick_nohz_full_mask); } + cpumask_andnot(housekeeping_mask, + cpu_possible_mask, tick_nohz_full_mask); tick_nohz_full_running = true; return 1; @@ -332,9 +336,15 @@ static int tick_nohz_init_all(void) pr_err("NO_HZ: Can't allocate full dynticks cpumask\n"); return err; } + if (!alloc_cpumask_var(&housekeeping_mask, GFP_KERNEL)) { + pr_err("NO_HZ: Can't allocate not-full dynticks cpumask\n"); + return err; + } err = 0; cpumask_setall(tick_nohz_full_mask); cpumask_clear_cpu(smp_processor_id(), tick_nohz_full_mask); + cpumask_clear(housekeeping_mask); + cpumask_set_cpu(smp_processor_id(), housekeeping_mask); tick_nohz_full_running = true; #endif return err; -- cgit v1.2.3 From c1f732ad767e37bd1d41043cbdefc0874b4d05e5 Mon Sep 17 00:00:00 2001 From: Kleber Sacilotto de Souza Date: Wed, 4 Jun 2014 10:57:50 -0300 Subject: GenWQE: Add sysfs interface for bitstream reload This patch adds an interface on sysfs for userspace to request a card bitstream reload. It sets the appropriate register and try to perform a fundamental reset on the PCIe slot for the card to reload the bitstream from the chosen partition. Signed-off-by: Kleber Sacilotto de Souza Acked-by: Frank Haverkamp Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-driver-genwqe | 9 +++ drivers/misc/genwqe/card_base.c | 90 +++++++++++++++++++++++++++ drivers/misc/genwqe/card_sysfs.c | 25 ++++++++ include/uapi/linux/genwqe/genwqe_card.h | 1 + 4 files changed, 125 insertions(+) (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-driver-genwqe b/Documentation/ABI/testing/sysfs-driver-genwqe index 1870737a1f5e..64ac6d567c4b 100644 --- a/Documentation/ABI/testing/sysfs-driver-genwqe +++ b/Documentation/ABI/testing/sysfs-driver-genwqe @@ -25,6 +25,15 @@ Date: Oct 2013 Contact: haver@linux.vnet.ibm.com Description: Interface to set the next bitstream to be used. +What: /sys/class/genwqe/genwqe_card/reload_bitstream +Date: May 2014 +Contact: klebers@linux.vnet.ibm.com +Description: Interface to trigger a PCIe card reset to reload the bitstream. + sudo sh -c 'echo 1 > \ + /sys/class/genwqe/genwqe0_card/reload_bitstream' + If successfully, the card will come back with the bitstream set + on 'next_bitstream'. + What: /sys/class/genwqe/genwqe_card/tempsens Date: Oct 2013 Contact: haver@linux.vnet.ibm.com diff --git a/drivers/misc/genwqe/card_base.c b/drivers/misc/genwqe/card_base.c index 74d51c9bb858..e6cc3e1e7326 100644 --- a/drivers/misc/genwqe/card_base.c +++ b/drivers/misc/genwqe/card_base.c @@ -760,6 +760,89 @@ static u64 genwqe_fir_checking(struct genwqe_dev *cd) return IO_ILLEGAL_VALUE; } +/** + * genwqe_pci_fundamental_reset() - trigger a PCIe fundamental reset on the slot + * + * Note: pci_set_pcie_reset_state() is not implemented on all archs, so this + * reset method will not work in all cases. + * + * Return: 0 on success or error code from pci_set_pcie_reset_state() + */ +static int genwqe_pci_fundamental_reset(struct pci_dev *pci_dev) +{ + int rc; + + /* + * lock pci config space access from userspace, + * save state and issue PCIe fundamental reset + */ + pci_cfg_access_lock(pci_dev); + pci_save_state(pci_dev); + rc = pci_set_pcie_reset_state(pci_dev, pcie_warm_reset); + if (!rc) { + /* keep PCIe reset asserted for 250ms */ + msleep(250); + pci_set_pcie_reset_state(pci_dev, pcie_deassert_reset); + /* Wait for 2s to reload flash and train the link */ + msleep(2000); + } + pci_restore_state(pci_dev); + pci_cfg_access_unlock(pci_dev); + return rc; +} + +/* + * genwqe_reload_bistream() - reload card bitstream + * + * Set the appropriate register and call fundamental reset to reaload the card + * bitstream. + * + * Return: 0 on success, error code otherwise + */ +static int genwqe_reload_bistream(struct genwqe_dev *cd) +{ + struct pci_dev *pci_dev = cd->pci_dev; + int rc; + + dev_info(&pci_dev->dev, + "[%s] resetting card for bitstream reload\n", + __func__); + + genwqe_stop(cd); + + /* + * Cause a CPLD reprogram with the 'next_bitstream' + * partition on PCIe hot or fundamental reset + */ + __genwqe_writeq(cd, IO_SLC_CFGREG_SOFTRESET, + (cd->softreset & 0xcull) | 0x70ull); + + rc = genwqe_pci_fundamental_reset(pci_dev); + if (rc) { + /* + * A fundamental reset failure can be caused + * by lack of support on the arch, so we just + * log the error and try to start the card + * again. + */ + dev_err(&pci_dev->dev, + "[%s] err: failed to reset card for bitstream reload\n", + __func__); + } + + rc = genwqe_start(cd); + if (rc) { + dev_err(&pci_dev->dev, + "[%s] err: cannot start card services! (err=%d)\n", + __func__, rc); + return rc; + } + dev_info(&pci_dev->dev, + "[%s] card reloaded\n", __func__); + return 0; +} + + /** * genwqe_health_thread() - Health checking thread * @@ -846,6 +929,13 @@ static int genwqe_health_thread(void *data) } } + if (cd->card_state == GENWQE_CARD_RELOAD_BITSTREAM) { + /* Userspace requested card bitstream reload */ + rc = genwqe_reload_bistream(cd); + if (rc) + goto fatal_error; + } + cd->last_gfir = gfir; cond_resched(); } diff --git a/drivers/misc/genwqe/card_sysfs.c b/drivers/misc/genwqe/card_sysfs.c index a72a99266c3c..7232e40a3ad9 100644 --- a/drivers/misc/genwqe/card_sysfs.c +++ b/drivers/misc/genwqe/card_sysfs.c @@ -223,6 +223,30 @@ static ssize_t next_bitstream_store(struct device *dev, } static DEVICE_ATTR_RW(next_bitstream); +static ssize_t reload_bitstream_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int reload; + struct genwqe_dev *cd = dev_get_drvdata(dev); + + if (kstrtoint(buf, 0, &reload) < 0) + return -EINVAL; + + if (reload == 0x1) { + if (cd->card_state == GENWQE_CARD_UNUSED || + cd->card_state == GENWQE_CARD_USED) + cd->card_state = GENWQE_CARD_RELOAD_BITSTREAM; + else + return -EIO; + } else { + return -EINVAL; + } + + return count; +} +static DEVICE_ATTR_WO(reload_bitstream); + /* * Create device_attribute structures / params: name, mode, show, store * additional flag if valid in VF @@ -239,6 +263,7 @@ static struct attribute *genwqe_attributes[] = { &dev_attr_status.attr, &dev_attr_freerunning_timer.attr, &dev_attr_queue_working_time.attr, + &dev_attr_reload_bitstream.attr, NULL, }; diff --git a/include/uapi/linux/genwqe/genwqe_card.h b/include/uapi/linux/genwqe/genwqe_card.h index 795e957bb840..4fc065f29255 100644 --- a/include/uapi/linux/genwqe/genwqe_card.h +++ b/include/uapi/linux/genwqe/genwqe_card.h @@ -328,6 +328,7 @@ enum genwqe_card_state { GENWQE_CARD_UNUSED = 0, GENWQE_CARD_USED = 1, GENWQE_CARD_FATAL_ERROR = 2, + GENWQE_CARD_RELOAD_BITSTREAM = 3, GENWQE_CARD_STATE_MAX, }; -- cgit v1.2.3 From 20f6fdd01c2c0de9cc1109083222edded24c5350 Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Fri, 4 Jul 2014 17:01:25 +0300 Subject: xhci: Platform: Set xhci lpm support quirk based on platform data If an xhci platform supports USB3 LPM capability then enable XHCI_LPM_SUPPORT quirk flag. Signed-off-by: Pratyush Anand Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/usb-xhci.txt | 3 ++- drivers/usb/host/xhci-plat.c | 6 +++++ include/linux/usb/xhci_pdriver.h | 27 ++++++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 include/linux/usb/xhci_pdriver.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/usb/usb-xhci.txt b/Documentation/devicetree/bindings/usb/usb-xhci.txt index 5a79377c6a96..86f67f0886bc 100644 --- a/Documentation/devicetree/bindings/usb/usb-xhci.txt +++ b/Documentation/devicetree/bindings/usb/usb-xhci.txt @@ -9,8 +9,9 @@ Required properties: register set for the device. - interrupts: one XHCI interrupt should be described here. -Optional property: +Optional properties: - clocks: reference to a clock + - usb3-lpm-capable: determines if platform is USB3 LPM capable Example: usb@f0931000 { diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index a4ccd0eb793e..b17459d3fcc8 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "xhci.h" #include "xhci-mvebu.h" @@ -97,6 +98,8 @@ static const struct hc_driver xhci_plat_xhci_driver = { static int xhci_plat_probe(struct platform_device *pdev) { + struct device_node *node = pdev->dev.of_node; + struct usb_xhci_pdata *pdata = dev_get_platdata(&pdev->dev); const struct hc_driver *driver; struct xhci_hcd *xhci; struct resource *res; @@ -185,6 +188,9 @@ static int xhci_plat_probe(struct platform_device *pdev) goto dealloc_usb2_hcd; } + if ((node && of_property_read_bool(node, "usb3-lpm-capable")) || + (pdata && pdata->usb3_lpm_capable)) + xhci->quirks |= XHCI_LPM_SUPPORT; /* * Set the xHCI pointer before xhci_plat_setup() (aka hcd_driver.reset) * is called by usb_add_hcd(). diff --git a/include/linux/usb/xhci_pdriver.h b/include/linux/usb/xhci_pdriver.h new file mode 100644 index 000000000000..376654b5b0f7 --- /dev/null +++ b/include/linux/usb/xhci_pdriver.h @@ -0,0 +1,27 @@ +/* + * 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. + * + */ + +#ifndef __USB_CORE_XHCI_PDRIVER_H +#define __USB_CORE_XHCI_PDRIVER_H + +/** + * struct usb_xhci_pdata - platform_data for generic xhci platform driver + * + * @usb3_lpm_capable: determines if this xhci platform supports USB3 + * LPM capability + * + */ +struct usb_xhci_pdata { + unsigned usb3_lpm_capable:1; +}; + +#endif /* __USB_CORE_XHCI_PDRIVER_H */ -- cgit v1.2.3 From d5d222605503dd39513b3baeb9475ddf316511d7 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 21 Jun 2014 08:08:11 -0700 Subject: i8k: uapi: Introduce define for new highest fan speed Some Dell laptops support fan speeds of {0, 1, 2, 3} instead of {0, 1, 2}. Add a define for it. Signed-off-by: Guenter Roeck Cc: Andreas Mohr Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/i8k.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/i8k.h b/include/uapi/linux/i8k.h index 1c45ba505115..133d02f03c25 100644 --- a/include/uapi/linux/i8k.h +++ b/include/uapi/linux/i8k.h @@ -34,7 +34,8 @@ #define I8K_FAN_OFF 0 #define I8K_FAN_LOW 1 #define I8K_FAN_HIGH 2 -#define I8K_FAN_MAX I8K_FAN_HIGH +#define I8K_FAN_TURBO 3 +#define I8K_FAN_MAX I8K_FAN_TURBO #define I8K_VOL_UP 1 #define I8K_VOL_DOWN 2 -- cgit v1.2.3 From 031547d8688a0fc5da875b504bf11e6c2e18390e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 10 Jul 2014 12:09:06 +0300 Subject: Bluetooth: Remove unneeded mgmt_connectable function The mgmt_connectable function has been used to ensure that the right actions to HCI_CONNECTABLE are taken when the HCI_Write_Scan_Enable command is triggered by something else than mgmt. The only other user that we really care about is the HCISETSCAN ioctl code, so we can actually more simply perform the needed changes there instead. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 - net/bluetooth/hci_core.c | 6 +++--- net/bluetooth/hci_event.c | 6 +----- net/bluetooth/mgmt.c | 32 -------------------------------- 4 files changed, 4 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index e69c2b08c0c6..76675c55be15 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1288,7 +1288,6 @@ void mgmt_set_powered_failed(struct hci_dev *hdev, int err); int mgmt_powered(struct hci_dev *hdev, u8 powered); void mgmt_discoverable_timeout(struct hci_dev *hdev); void mgmt_discoverable(struct hci_dev *hdev, u8 discoverable); -void mgmt_connectable(struct hci_dev *hdev, u8 connectable); void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status); void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, bool persistent); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 8f9df768f250..3844eeb85453 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2717,9 +2717,9 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg) HCI_INIT_TIMEOUT); /* Ensure that the connectable state gets correctly - * notified if the whitelist is in use. + * modified as this was a non-mgmt change. */ - if (!err && !list_empty(&hdev->whitelist)) { + if (!err) { bool changed; if ((dr.dev_opt & SCAN_PAGE)) @@ -2729,7 +2729,7 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg) changed = test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags); - if (changed) + if (changed && test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_new_settings(hdev); } break; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index c8ae9ee3cb12..38a0e457eaf9 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -330,12 +330,8 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) } else if (old_iscan) mgmt_discoverable(hdev, 0); - if (param & SCAN_PAGE) { + if (param & SCAN_PAGE) set_bit(HCI_PSCAN, &hdev->flags); - if (!old_pscan) - mgmt_connectable(hdev, 1); - } else if (old_pscan) - mgmt_connectable(hdev, 0); done: hci_dev_unlock(hdev); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 38f05386bc0c..9f9f11c8488b 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6068,38 +6068,6 @@ void mgmt_discoverable(struct hci_dev *hdev, u8 discoverable) } } -void mgmt_connectable(struct hci_dev *hdev, u8 connectable) -{ - bool changed; - - /* Nothing needed here if there's a pending command since that - * commands request completion callback takes care of everything - * necessary. - */ - if (mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) - return; - - /* Powering off may clear the scan mode - don't let that interfere */ - if (!connectable && mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) - return; - - /* If something else than mgmt changed the page scan state we - * can't differentiate this from a change triggered by adding - * the first element to the whitelist. Therefore, avoid - * incorrectly setting HCI_CONNECTABLE. - */ - if (connectable && !list_empty(&hdev->whitelist)) - return; - - if (connectable) - changed = !test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags); - else - changed = test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags); - - if (changed) - new_settings(hdev, NULL); -} - void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status) { u8 mgmt_err = mgmt_status(status); -- cgit v1.2.3 From bc6d2d04182877b198c1a945b7c401decbbb8c02 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 10 Jul 2014 12:09:08 +0300 Subject: Bluetooth: Remove unneeded mgmt_discoverable function Since the HCISETSCAN ioctl is the only non-mgmt user we care about for setting the right discoverable state we can simply do the necessary updates in the ioctl handler function instead. This then allows the removal of the mgmt_discoverable function and should simplify that state handling considerably. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_core.c | 24 +++++++++++--- net/bluetooth/hci_event.c | 19 +++--------- net/bluetooth/mgmt.c | 67 ++++++++++++++-------------------------- 4 files changed, 50 insertions(+), 62 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 76675c55be15..b65efb22be54 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1286,8 +1286,8 @@ void mgmt_index_added(struct hci_dev *hdev); void mgmt_index_removed(struct hci_dev *hdev); void mgmt_set_powered_failed(struct hci_dev *hdev, int err); int mgmt_powered(struct hci_dev *hdev, u8 powered); +int mgmt_update_adv_data(struct hci_dev *hdev); void mgmt_discoverable_timeout(struct hci_dev *hdev); -void mgmt_discoverable(struct hci_dev *hdev, u8 discoverable); void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status); void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, bool persistent); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 27c40e4901a3..3321c65c73ac 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2657,7 +2657,7 @@ done: static void hci_update_scan_state(struct hci_dev *hdev, u8 scan) { - bool conn_changed; + bool conn_changed, discov_changed; BT_DBG("%s scan 0x%02x", hdev->name, scan); @@ -2668,11 +2668,27 @@ static void hci_update_scan_state(struct hci_dev *hdev, u8 scan) conn_changed = test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags); + if ((scan & SCAN_INQUIRY)) { + discov_changed = !test_and_set_bit(HCI_DISCOVERABLE, + &hdev->dev_flags); + } else { + clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags); + discov_changed = test_and_clear_bit(HCI_DISCOVERABLE, + &hdev->dev_flags); + } + if (!test_bit(HCI_MGMT, &hdev->dev_flags)) return; - if (conn_changed) + if (conn_changed || discov_changed) { + /* In case this was disabled through mgmt */ + set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags); + + if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) + mgmt_update_adv_data(hdev); + mgmt_new_settings(hdev); + } } int hci_dev_cmd(unsigned int cmd, void __user *arg) @@ -2736,8 +2752,8 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg) err = hci_req_sync(hdev, hci_scan_req, dr.dev_opt, HCI_INIT_TIMEOUT); - /* Ensure that the connectable state gets correctly - * modified as this was a non-mgmt change. + /* Ensure that the connectable and discoverable states + * get correctly modified as this was a non-mgmt change. */ if (!err) hci_update_scan_state(hdev, dr.dev_opt); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 38a0e457eaf9..760bbd88f3c5 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -296,7 +296,6 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); __u8 param; - int old_pscan, old_iscan; void *sent; BT_DBG("%s status 0x%2.2x", hdev->name, status); @@ -315,23 +314,15 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) goto done; } - /* We need to ensure that we set this back on if someone changed - * the scan mode through a raw HCI socket. - */ - set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags); - - old_pscan = test_and_clear_bit(HCI_PSCAN, &hdev->flags); - old_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags); - - if (param & SCAN_INQUIRY) { + if (param & SCAN_INQUIRY) set_bit(HCI_ISCAN, &hdev->flags); - if (!old_iscan) - mgmt_discoverable(hdev, 1); - } else if (old_iscan) - mgmt_discoverable(hdev, 0); + else + clear_bit(HCI_ISCAN, &hdev->flags); if (param & SCAN_PAGE) set_bit(HCI_PSCAN, &hdev->flags); + else + clear_bit(HCI_ISCAN, &hdev->flags); done: hci_dev_unlock(hdev); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 9f9f11c8488b..1dad7bffc6af 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -906,6 +906,16 @@ static void update_adv_data(struct hci_request *req) hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp); } +int mgmt_update_adv_data(struct hci_dev *hdev) +{ + struct hci_request req; + + hci_req_init(&req, hdev); + update_adv_data(&req); + + return hci_req_run(&req, NULL); +} + static void create_eir(struct hci_dev *hdev, u8 *data) { u8 *ptr = data; @@ -1743,7 +1753,7 @@ static void set_connectable_complete(struct hci_dev *hdev, u8 status) { struct pending_cmd *cmd; struct mgmt_mode *cp; - bool changed; + bool conn_changed, discov_changed; BT_DBG("status 0x%02x", status); @@ -1760,15 +1770,23 @@ static void set_connectable_complete(struct hci_dev *hdev, u8 status) } cp = cmd->param; - if (cp->val) - changed = !test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags); - else - changed = test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags); + if (cp->val) { + conn_changed = !test_and_set_bit(HCI_CONNECTABLE, + &hdev->dev_flags); + discov_changed = false; + } else { + conn_changed = test_and_clear_bit(HCI_CONNECTABLE, + &hdev->dev_flags); + discov_changed = test_and_clear_bit(HCI_DISCOVERABLE, + &hdev->dev_flags); + } send_settings_rsp(cmd->sk, MGMT_OP_SET_CONNECTABLE, hdev); - if (changed) { + if (conn_changed || discov_changed) { new_settings(hdev, cmd->sk); + if (discov_changed) + mgmt_update_adv_data(hdev); hci_update_background_scan(hdev); } @@ -6031,43 +6049,6 @@ void mgmt_discoverable_timeout(struct hci_dev *hdev) hci_dev_unlock(hdev); } -void mgmt_discoverable(struct hci_dev *hdev, u8 discoverable) -{ - bool changed; - - /* Nothing needed here if there's a pending command since that - * commands request completion callback takes care of everything - * necessary. - */ - if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev)) - return; - - /* Powering off may clear the scan mode - don't let that interfere */ - if (!discoverable && mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) - return; - - if (discoverable) { - changed = !test_and_set_bit(HCI_DISCOVERABLE, &hdev->dev_flags); - } else { - clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags); - changed = test_and_clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); - } - - if (changed) { - struct hci_request req; - - /* In case this change in discoverable was triggered by - * a disabling of connectable there could be a need to - * update the advertising flags. - */ - hci_req_init(&req, hdev); - update_adv_data(&req); - hci_req_run(&req, NULL); - - new_settings(hdev, NULL); - } -} - void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status) { u8 mgmt_err = mgmt_status(status); -- cgit v1.2.3 From 13a779e42251184d0f53a8f8299ced614faa028f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 10 Jul 2014 12:09:09 +0300 Subject: Bluetooth: Remove unneeded mgmt_write_scan_failed function The Set Connectable/Discoverable mgmt handlers use a hci_request with a proper callback to handle the HCI command sending. It makes therefore little sense to have this extra function to be called from hci_event.c for command failures. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 - net/bluetooth/hci_event.c | 1 - net/bluetooth/mgmt.c | 13 ------------- 3 files changed, 15 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b65efb22be54..7e9e95633a85 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1288,7 +1288,6 @@ void mgmt_set_powered_failed(struct hci_dev *hdev, int err); int mgmt_powered(struct hci_dev *hdev, u8 powered); int mgmt_update_adv_data(struct hci_dev *hdev); void mgmt_discoverable_timeout(struct hci_dev *hdev); -void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status); void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, bool persistent); void mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 760bbd88f3c5..a62e918d2641 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -309,7 +309,6 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_lock(hdev); if (status) { - mgmt_write_scan_failed(hdev, param, status); hdev->discov_timeout = 0; goto done; } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 1dad7bffc6af..7703b72653ff 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6049,19 +6049,6 @@ void mgmt_discoverable_timeout(struct hci_dev *hdev) hci_dev_unlock(hdev); } -void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status) -{ - u8 mgmt_err = mgmt_status(status); - - if (scan & SCAN_PAGE) - mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, - cmd_status_rsp, &mgmt_err); - - if (scan & SCAN_INQUIRY) - mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, - cmd_status_rsp, &mgmt_err); -} - void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, bool persistent) { -- cgit v1.2.3 From 875cbf3e4614cfdcc7f65033e25292aec80f09c0 Mon Sep 17 00:00:00 2001 From: AKASHI Takahiro Date: Fri, 4 Jul 2014 08:28:30 +0100 Subject: arm64: Add audit support On AArch64, audit is supported through generic lib/audit.c and compat_audit.c, and so this patch adds arch specific definitions required. Acked-by Will Deacon Acked-by: Richard Guy Briggs Signed-off-by: AKASHI Takahiro Signed-off-by: Catalin Marinas --- arch/arm64/Kconfig | 2 ++ arch/arm64/include/asm/syscall.h | 14 ++++++++++++++ include/uapi/linux/audit.h | 1 + 3 files changed, 17 insertions(+) (limited to 'include') diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 1cb806529393..ce6e733e0c05 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -10,6 +10,7 @@ config ARM64 select ARM_AMBA select ARM_ARCH_TIMER select ARM_GIC + select AUDIT_ARCH_COMPAT_GENERIC select BUILDTIME_EXTABLE_SORT select CLONE_BACKWARDS select COMMON_CLK @@ -28,6 +29,7 @@ config ARM64 select GENERIC_STRNLEN_USER select GENERIC_TIME_VSYSCALL select HARDIRQS_SW_RESEND + select HAVE_ARCH_AUDITSYSCALL select HAVE_ARCH_JUMP_LABEL select HAVE_ARCH_KGDB select HAVE_ARCH_TRACEHOOK diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h index 383771eb0b87..709a574468f0 100644 --- a/arch/arm64/include/asm/syscall.h +++ b/arch/arm64/include/asm/syscall.h @@ -16,6 +16,8 @@ #ifndef __ASM_SYSCALL_H #define __ASM_SYSCALL_H +#include +#include #include extern const void *sys_call_table[]; @@ -105,4 +107,16 @@ static inline void syscall_set_arguments(struct task_struct *task, memcpy(®s->regs[i], args, n * sizeof(args[0])); } +/* + * We don't care about endianness (__AUDIT_ARCH_LE bit) here because + * AArch64 has the same system calls both on little- and big- endian. + */ +static inline int syscall_get_arch(void) +{ + if (is_compat_task()) + return AUDIT_ARCH_ARM; + + return AUDIT_ARCH_AARCH64; +} + #endif /* __ASM_SYSCALL_H */ diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h index cf6714752b69..3b9ff33e1768 100644 --- a/include/uapi/linux/audit.h +++ b/include/uapi/linux/audit.h @@ -342,6 +342,7 @@ enum { #define __AUDIT_ARCH_64BIT 0x80000000 #define __AUDIT_ARCH_LE 0x40000000 +#define AUDIT_ARCH_AARCH64 (EM_AARCH64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE) #define AUDIT_ARCH_ALPHA (EM_ALPHA|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE) #define AUDIT_ARCH_ARM (EM_ARM|__AUDIT_ARCH_LE) #define AUDIT_ARCH_ARMEB (EM_ARM) -- cgit v1.2.3 From 0b4820d6d8b6448bc9f7fac1bb1a801a53b425e1 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 12 May 2014 16:05:13 +0200 Subject: KVM: prepare for KVM_(S|G)ET_MP_STATE on other architectures Highlight the aspects of the ioctls that are actually specific to x86 and ia64. As defined restrictions (irqchip) and mp states may not apply to other architectures, these parts are flagged to belong to x86 and ia64. In preparation for the use of KVM_(S|G)ET_MP_STATE by s390. Fix a spelling error (KVM_SET_MP_STATE vs. KVM_SET_MPSTATE) on the way. Signed-off-by: David Hildenbrand Reviewed-by: Cornelia Huck Acked-by: Christian Borntraeger Signed-off-by: Christian Borntraeger --- Documentation/virtual/kvm/api.txt | 21 ++++++++++++--------- include/uapi/linux/kvm.h | 3 ++- 2 files changed, 14 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 0fe36497642c..904c61cdd311 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -988,18 +988,20 @@ uniprocessor guests). Possible values are: - - KVM_MP_STATE_RUNNABLE: the vcpu is currently running + - KVM_MP_STATE_RUNNABLE: the vcpu is currently running [x86, ia64] - KVM_MP_STATE_UNINITIALIZED: the vcpu is an application processor (AP) - which has not yet received an INIT signal + which has not yet received an INIT signal [x86, + ia64] - KVM_MP_STATE_INIT_RECEIVED: the vcpu has received an INIT signal, and is - now ready for a SIPI + now ready for a SIPI [x86, ia64] - KVM_MP_STATE_HALTED: the vcpu has executed a HLT instruction and - is waiting for an interrupt + is waiting for an interrupt [x86, ia64] - KVM_MP_STATE_SIPI_RECEIVED: the vcpu has just received a SIPI (vector - accessible via KVM_GET_VCPU_EVENTS) + accessible via KVM_GET_VCPU_EVENTS) [x86, ia64] -This ioctl is only useful after KVM_CREATE_IRQCHIP. Without an in-kernel -irqchip, the multiprocessing state must be maintained by userspace. +On x86 and ia64, this ioctl is only useful after KVM_CREATE_IRQCHIP. Without an +in-kernel irqchip, the multiprocessing state must be maintained by userspace on +these architectures. 4.39 KVM_SET_MP_STATE @@ -1013,8 +1015,9 @@ Returns: 0 on success; -1 on error Sets the vcpu's current "multiprocessing state"; see KVM_GET_MP_STATE for arguments. -This ioctl is only useful after KVM_CREATE_IRQCHIP. Without an in-kernel -irqchip, the multiprocessing state must be maintained by userspace. +On x86 and ia64, this ioctl is only useful after KVM_CREATE_IRQCHIP. Without an +in-kernel irqchip, the multiprocessing state must be maintained by userspace on +these architectures. 4.40 KVM_SET_IDENTITY_MAP_ADDR diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index e11d8f170a62..37d4ec6f14d8 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -399,8 +399,9 @@ struct kvm_vapic_addr { __u64 vapic_addr; }; -/* for KVM_SET_MPSTATE */ +/* for KVM_SET_MP_STATE */ +/* not all states are valid on all architectures */ #define KVM_MP_STATE_RUNNABLE 0 #define KVM_MP_STATE_UNINITIALIZED 1 #define KVM_MP_STATE_INIT_RECEIVED 2 -- cgit v1.2.3 From 6352e4d2dd9a349024a41356148eced553e1dce4 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Thu, 10 Apr 2014 17:35:00 +0200 Subject: KVM: s390: implement KVM_(S|G)ET_MP_STATE for user space state control This patch - adds s390 specific MP states to linux headers and documents them - implements the KVM_{SET,GET}_MP_STATE ioctls - enables KVM_CAP_MP_STATE - allows user space to control the VCPU state on s390. If user space sets the VCPU state using the ioctl KVM_SET_MP_STATE, we can disable manual changing of the VCPU state and trust user space to do the right thing. Signed-off-by: David Hildenbrand Reviewed-by: Cornelia Huck Acked-by: Christian Borntraeger Signed-off-by: Christian Borntraeger --- Documentation/virtual/kvm/api.txt | 10 ++++++++-- arch/s390/include/asm/kvm_host.h | 1 + arch/s390/kvm/diag.c | 3 ++- arch/s390/kvm/intercept.c | 3 ++- arch/s390/kvm/kvm-s390.c | 37 +++++++++++++++++++++++++++++++++---- arch/s390/kvm/kvm-s390.h | 6 ++++++ include/uapi/linux/kvm.h | 4 ++++ 7 files changed, 56 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 904c61cdd311..a41465bd6a5c 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -974,7 +974,7 @@ for vm-wide capabilities. 4.38 KVM_GET_MP_STATE Capability: KVM_CAP_MP_STATE -Architectures: x86, ia64 +Architectures: x86, ia64, s390 Type: vcpu ioctl Parameters: struct kvm_mp_state (out) Returns: 0 on success; -1 on error @@ -998,6 +998,12 @@ Possible values are: is waiting for an interrupt [x86, ia64] - KVM_MP_STATE_SIPI_RECEIVED: the vcpu has just received a SIPI (vector accessible via KVM_GET_VCPU_EVENTS) [x86, ia64] + - KVM_MP_STATE_STOPPED: the vcpu is stopped [s390] + - KVM_MP_STATE_CHECK_STOP: the vcpu is in a special error state [s390] + - KVM_MP_STATE_OPERATING: the vcpu is operating (running or halted) + [s390] + - KVM_MP_STATE_LOAD: the vcpu is in a special load/startup state + [s390] On x86 and ia64, this ioctl is only useful after KVM_CREATE_IRQCHIP. Without an in-kernel irqchip, the multiprocessing state must be maintained by userspace on @@ -1007,7 +1013,7 @@ these architectures. 4.39 KVM_SET_MP_STATE Capability: KVM_CAP_MP_STATE -Architectures: x86, ia64 +Architectures: x86, ia64, s390 Type: vcpu ioctl Parameters: struct kvm_mp_state (in) Returns: 0 on success; -1 on error diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index 4181d7baabba..c2ba0208a0e1 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -418,6 +418,7 @@ struct kvm_arch{ int css_support; int use_irqchip; int use_cmma; + int user_cpu_state_ctrl; struct s390_io_adapter *adapters[MAX_S390_IO_ADAPTERS]; wait_queue_head_t ipte_wq; spinlock_t start_stop_lock; diff --git a/arch/s390/kvm/diag.c b/arch/s390/kvm/diag.c index 0161675878a2..59bd8f991b98 100644 --- a/arch/s390/kvm/diag.c +++ b/arch/s390/kvm/diag.c @@ -176,7 +176,8 @@ static int __diag_ipl_functions(struct kvm_vcpu *vcpu) return -EOPNOTSUPP; } - kvm_s390_vcpu_stop(vcpu); + if (!kvm_s390_user_cpu_state_ctrl(vcpu->kvm)) + kvm_s390_vcpu_stop(vcpu); vcpu->run->s390_reset_flags |= KVM_S390_RESET_SUBSYSTEM; vcpu->run->s390_reset_flags |= KVM_S390_RESET_IPL; vcpu->run->s390_reset_flags |= KVM_S390_RESET_CPU_INIT; diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c index ac6b32585a36..eaf46291d361 100644 --- a/arch/s390/kvm/intercept.c +++ b/arch/s390/kvm/intercept.c @@ -73,7 +73,8 @@ static int handle_stop(struct kvm_vcpu *vcpu) return rc; } - kvm_s390_vcpu_stop(vcpu); + if (!kvm_s390_user_cpu_state_ctrl(vcpu->kvm)) + kvm_s390_vcpu_stop(vcpu); return -EOPNOTSUPP; } diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 342895350825..fdf88f7a539c 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -167,6 +167,7 @@ int kvm_dev_ioctl_check_extension(long ext) case KVM_CAP_DEVICE_CTRL: case KVM_CAP_ENABLE_CAP_VM: case KVM_CAP_VM_ATTRIBUTES: + case KVM_CAP_MP_STATE: r = 1; break; case KVM_CAP_NR_VCPUS: @@ -595,7 +596,8 @@ static void kvm_s390_vcpu_initial_reset(struct kvm_vcpu *vcpu) vcpu->arch.sie_block->pp = 0; vcpu->arch.pfault_token = KVM_S390_PFAULT_TOKEN_INVALID; kvm_clear_async_pf_completion_queue(vcpu); - kvm_s390_vcpu_stop(vcpu); + if (!kvm_s390_user_cpu_state_ctrl(vcpu->kvm)) + kvm_s390_vcpu_stop(vcpu); kvm_s390_clear_local_irqs(vcpu); } @@ -980,13 +982,34 @@ int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu, int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu, struct kvm_mp_state *mp_state) { - return -EINVAL; /* not implemented yet */ + /* CHECK_STOP and LOAD are not supported yet */ + return is_vcpu_stopped(vcpu) ? KVM_MP_STATE_STOPPED : + KVM_MP_STATE_OPERATING; } int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, struct kvm_mp_state *mp_state) { - return -EINVAL; /* not implemented yet */ + int rc = 0; + + /* user space knows about this interface - let it control the state */ + vcpu->kvm->arch.user_cpu_state_ctrl = 1; + + switch (mp_state->mp_state) { + case KVM_MP_STATE_STOPPED: + kvm_s390_vcpu_stop(vcpu); + break; + case KVM_MP_STATE_OPERATING: + kvm_s390_vcpu_start(vcpu); + break; + case KVM_MP_STATE_LOAD: + case KVM_MP_STATE_CHECK_STOP: + /* fall through - CHECK_STOP and LOAD are not supported yet */ + default: + rc = -ENXIO; + } + + return rc; } bool kvm_s390_cmma_enabled(struct kvm *kvm) @@ -1284,7 +1307,13 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) if (vcpu->sigset_active) sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved); - kvm_s390_vcpu_start(vcpu); + if (!kvm_s390_user_cpu_state_ctrl(vcpu->kvm)) { + kvm_s390_vcpu_start(vcpu); + } else if (is_vcpu_stopped(vcpu)) { + pr_err_ratelimited("kvm-s390: can't run stopped vcpu %d\n", + vcpu->vcpu_id); + return -EINVAL; + } switch (kvm_run->exit_reason) { case KVM_EXIT_S390_SIEIC: diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h index 77ed846342d4..33a0e4bed2a5 100644 --- a/arch/s390/kvm/kvm-s390.h +++ b/arch/s390/kvm/kvm-s390.h @@ -129,6 +129,12 @@ static inline void kvm_s390_set_psw_cc(struct kvm_vcpu *vcpu, unsigned long cc) vcpu->arch.sie_block->gpsw.mask |= cc << 44; } +/* are cpu states controlled by user space */ +static inline int kvm_s390_user_cpu_state_ctrl(struct kvm *kvm) +{ + return kvm->arch.user_cpu_state_ctrl != 0; +} + int kvm_s390_handle_wait(struct kvm_vcpu *vcpu); enum hrtimer_restart kvm_s390_idle_wakeup(struct hrtimer *timer); void kvm_s390_tasklet(unsigned long parm); diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 37d4ec6f14d8..9b744af871d7 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -407,6 +407,10 @@ struct kvm_vapic_addr { #define KVM_MP_STATE_INIT_RECEIVED 2 #define KVM_MP_STATE_HALTED 3 #define KVM_MP_STATE_SIPI_RECEIVED 4 +#define KVM_MP_STATE_STOPPED 5 +#define KVM_MP_STATE_CHECK_STOP 6 +#define KVM_MP_STATE_OPERATING 7 +#define KVM_MP_STATE_LOAD 8 struct kvm_mp_state { __u32 mp_state; -- cgit v1.2.3 From 8fe39aac0578cbb0abf27e1be70ff581e0c1d836 Mon Sep 17 00:00:00 2001 From: Philipp Reisner Date: Fri, 22 Nov 2013 13:22:13 +0100 Subject: drbd: device->ldev is not guaranteed on an D_ATTACHING disk Some parts of the code assumed that get_ldev_if_state(device, D_ATTACHING) is sufficient to access the ldev member of the device object. That was wrong. ldev may not be there or might be freed at any time if the device has a disk state of D_ATTACHING. bm_rw() Documented that drbd_bm_read() is only called from drbd_adm_attach. drbd_bm_write() is only called when a reference is held, and it is documented that a caller has to hold a reference before calling drbd_bm_write() drbd_bm_write_page() Use get_ldev() instead of get_ldev_if_state(device, D_ATTACHING) drbd_bmio_set_n_write() No longer use get_ldev_if_state(device, D_ATTACHING). All callers hold a reference to ldev now. drbd_bmio_clear_n_write() All callers where holding a reference of ldev anyways. Remove the misleading get_ldev_if_state(device, D_ATTACHING) drbd_reconsider_max_bio_size() Removed the get_ldev_if_state(device, D_ATTACHING). All callers now pass a struct drbd_backing_dev* when they have a proper reference, or a NULL pointer. Before this fix, the receiver could trigger a NULL pointer deref when in drbd_reconsider_max_bio_size() drbd_bump_write_ordering() Used get_ldev_if_state(device, D_ATTACHING) with the wrong assumption. Remove it, and allow the caller to pass in a struct drbd_backing_dev* when the caller knows that accessing this bdev is safe. Signed-off-by: Philipp Reisner Signed-off-by: Lars Ellenberg --- drivers/block/drbd/drbd_bitmap.c | 4 +++- drivers/block/drbd/drbd_int.h | 9 ++++---- drivers/block/drbd/drbd_main.c | 36 +++++++++++++------------------ drivers/block/drbd/drbd_nl.c | 41 +++++++++++++++++++++++------------- drivers/block/drbd/drbd_receiver.c | 43 ++++++++++++++++++++++++++------------ include/linux/drbd.h | 2 +- 6 files changed, 79 insertions(+), 56 deletions(-) (limited to 'include') diff --git a/drivers/block/drbd/drbd_bitmap.c b/drivers/block/drbd/drbd_bitmap.c index 1aa29f8fdfe1..ed310415020b 100644 --- a/drivers/block/drbd/drbd_bitmap.c +++ b/drivers/block/drbd/drbd_bitmap.c @@ -1085,6 +1085,8 @@ static int bm_rw(struct drbd_device *device, int rw, unsigned flags, unsigned la kfree(ctx); return -ENODEV; } + /* Here D_ATTACHING is sufficient since drbd_bm_read() is called only from + drbd_adm_attach(), after device->ldev was assigned. */ if (!ctx->flags) WARN_ON(!(BM_LOCKED_MASK & b->bm_flags)); @@ -1260,7 +1262,7 @@ int drbd_bm_write_page(struct drbd_device *device, unsigned int idx) __must_hold .kref = { ATOMIC_INIT(2) }, }; - if (!get_ldev_if_state(device, D_ATTACHING)) { /* put is in bm_aio_ctx_destroy() */ + if (!get_ldev(device)) { /* put is in bm_aio_ctx_destroy() */ drbd_err(device, "ASSERT FAILED: get_ldev_if_state() == 1 in drbd_bm_write_page()\n"); kfree(ctx); return -ENODEV; diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h index 1ef2474e8f11..c87bc8e8fd82 100644 --- a/drivers/block/drbd/drbd_int.h +++ b/drivers/block/drbd/drbd_int.h @@ -984,8 +984,8 @@ extern int drbd_bitmap_io(struct drbd_device *device, extern int drbd_bitmap_io_from_worker(struct drbd_device *device, int (*io_fn)(struct drbd_device *), char *why, enum bm_flag flags); -extern int drbd_bmio_set_n_write(struct drbd_device *device); -extern int drbd_bmio_clear_n_write(struct drbd_device *device); +extern int drbd_bmio_set_n_write(struct drbd_device *device) __must_hold(local); +extern int drbd_bmio_clear_n_write(struct drbd_device *device) __must_hold(local); extern void drbd_ldev_destroy(struct drbd_device *device); /* Meta data layout @@ -1313,7 +1313,7 @@ enum determine_dev_size { extern enum determine_dev_size drbd_determine_dev_size(struct drbd_device *, enum dds_flags, struct resize_parms *) __must_hold(local); extern void resync_after_online_grow(struct drbd_device *); -extern void drbd_reconsider_max_bio_size(struct drbd_device *device); +extern void drbd_reconsider_max_bio_size(struct drbd_device *device, struct drbd_backing_dev *bdev); extern enum drbd_state_rv drbd_set_role(struct drbd_device *device, enum drbd_role new_role, int force); @@ -1479,7 +1479,8 @@ static inline void drbd_generic_make_request(struct drbd_device *device, generic_make_request(bio); } -void drbd_bump_write_ordering(struct drbd_resource *resource, enum write_ordering_e wo); +void drbd_bump_write_ordering(struct drbd_resource *resource, struct drbd_backing_dev *bdev, + enum write_ordering_e wo); /* drbd_proc.c */ extern struct proc_dir_entry *drbd_proc; diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index 17b9a237f2e6..a6af93528d57 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c @@ -3466,23 +3466,19 @@ void drbd_uuid_set_bm(struct drbd_device *device, u64 val) __must_hold(local) * * Sets all bits in the bitmap and writes the whole bitmap to stable storage. */ -int drbd_bmio_set_n_write(struct drbd_device *device) +int drbd_bmio_set_n_write(struct drbd_device *device) __must_hold(local) { int rv = -EIO; - if (get_ldev_if_state(device, D_ATTACHING)) { - drbd_md_set_flag(device, MDF_FULL_SYNC); - drbd_md_sync(device); - drbd_bm_set_all(device); - - rv = drbd_bm_write(device); + drbd_md_set_flag(device, MDF_FULL_SYNC); + drbd_md_sync(device); + drbd_bm_set_all(device); - if (!rv) { - drbd_md_clear_flag(device, MDF_FULL_SYNC); - drbd_md_sync(device); - } + rv = drbd_bm_write(device); - put_ldev(device); + if (!rv) { + drbd_md_clear_flag(device, MDF_FULL_SYNC); + drbd_md_sync(device); } return rv; @@ -3494,18 +3490,11 @@ int drbd_bmio_set_n_write(struct drbd_device *device) * * Clears all bits in the bitmap and writes the whole bitmap to stable storage. */ -int drbd_bmio_clear_n_write(struct drbd_device *device) +int drbd_bmio_clear_n_write(struct drbd_device *device) __must_hold(local) { - int rv = -EIO; - drbd_resume_al(device); - if (get_ldev_if_state(device, D_ATTACHING)) { - drbd_bm_clear_all(device); - rv = drbd_bm_write(device); - put_ldev(device); - } - - return rv; + drbd_bm_clear_all(device); + return drbd_bm_write(device); } static int w_bitmap_io(struct drbd_work *w, int unused) @@ -3603,6 +3592,9 @@ static int w_go_diskless(struct drbd_work *w, int unused) * that drbd_set_out_of_sync() can not be called. This function MAY ONLY be * called from worker context. It MUST NOT be used while a previous such * work is still pending! + * + * Its worker function encloses the call of io_fn() by get_ldev() and + * put_ldev(). */ void drbd_queue_bitmap_io(struct drbd_device *device, int (*io_fn)(struct drbd_device *), diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c index 43fad2c1ba01..25f4b6f67c21 100644 --- a/drivers/block/drbd/drbd_nl.c +++ b/drivers/block/drbd/drbd_nl.c @@ -1110,15 +1110,16 @@ static int drbd_check_al_size(struct drbd_device *device, struct disk_conf *dc) return 0; } -static void drbd_setup_queue_param(struct drbd_device *device, unsigned int max_bio_size) +static void drbd_setup_queue_param(struct drbd_device *device, struct drbd_backing_dev *bdev, + unsigned int max_bio_size) { struct request_queue * const q = device->rq_queue; unsigned int max_hw_sectors = max_bio_size >> 9; unsigned int max_segments = 0; struct request_queue *b = NULL; - if (get_ldev_if_state(device, D_ATTACHING)) { - b = device->ldev->backing_bdev->bd_disk->queue; + if (bdev) { + b = bdev->backing_bdev->bd_disk->queue; max_hw_sectors = min(queue_max_hw_sectors(b), max_bio_size >> 9); rcu_read_lock(); @@ -1163,11 +1164,10 @@ static void drbd_setup_queue_param(struct drbd_device *device, unsigned int max_ b->backing_dev_info.ra_pages); q->backing_dev_info.ra_pages = b->backing_dev_info.ra_pages; } - put_ldev(device); } } -void drbd_reconsider_max_bio_size(struct drbd_device *device) +void drbd_reconsider_max_bio_size(struct drbd_device *device, struct drbd_backing_dev *bdev) { unsigned int now, new, local, peer; @@ -1175,10 +1175,9 @@ void drbd_reconsider_max_bio_size(struct drbd_device *device) local = device->local_max_bio_size; /* Eventually last known value, from volatile memory */ peer = device->peer_max_bio_size; /* Eventually last known value, from meta data */ - if (get_ldev_if_state(device, D_ATTACHING)) { - local = queue_max_hw_sectors(device->ldev->backing_bdev->bd_disk->queue) << 9; + if (bdev) { + local = queue_max_hw_sectors(bdev->backing_bdev->bd_disk->queue) << 9; device->local_max_bio_size = local; - put_ldev(device); } local = min(local, DRBD_MAX_BIO_SIZE); @@ -1211,7 +1210,7 @@ void drbd_reconsider_max_bio_size(struct drbd_device *device) if (new != now) drbd_info(device, "max BIO size = %u\n", new); - drbd_setup_queue_param(device, new); + drbd_setup_queue_param(device, bdev, new); } /* Starts the worker thread */ @@ -1399,7 +1398,7 @@ int drbd_adm_disk_opts(struct sk_buff *skb, struct genl_info *info) else set_bit(MD_NO_FUA, &device->flags); - drbd_bump_write_ordering(device->resource, WO_bdev_flush); + drbd_bump_write_ordering(device->resource, NULL, WO_bdev_flush); drbd_md_sync(device); @@ -1704,7 +1703,7 @@ int drbd_adm_attach(struct sk_buff *skb, struct genl_info *info) new_disk_conf = NULL; new_plan = NULL; - drbd_bump_write_ordering(device->resource, WO_bdev_flush); + drbd_bump_write_ordering(device->resource, device->ldev, WO_bdev_flush); if (drbd_md_test_flag(device->ldev, MDF_CRASHED_PRIMARY)) set_bit(CRASHED_PRIMARY, &device->flags); @@ -1720,7 +1719,7 @@ int drbd_adm_attach(struct sk_buff *skb, struct genl_info *info) device->read_cnt = 0; device->writ_cnt = 0; - drbd_reconsider_max_bio_size(device); + drbd_reconsider_max_bio_size(device, device->ldev); /* If I am currently not R_PRIMARY, * but meta data primary indicator is set, @@ -2648,8 +2647,13 @@ int drbd_adm_invalidate(struct sk_buff *skb, struct genl_info *info) if (retcode != NO_ERROR) goto out; - mutex_lock(&adm_ctx.resource->adm_mutex); device = adm_ctx.device; + if (!get_ldev(device)) { + retcode = ERR_NO_DISK; + goto out; + } + + mutex_lock(&adm_ctx.resource->adm_mutex); /* If there is still bitmap IO pending, probably because of a previous * resync just being finished, wait for it before requesting a new resync. @@ -2673,6 +2677,7 @@ int drbd_adm_invalidate(struct sk_buff *skb, struct genl_info *info) retcode = drbd_request_state(device, NS(conn, C_STARTING_SYNC_T)); drbd_resume_io(device); mutex_unlock(&adm_ctx.resource->adm_mutex); + put_ldev(device); out: drbd_adm_finish(&adm_ctx, info, retcode); return 0; @@ -2698,7 +2703,7 @@ out: return 0; } -static int drbd_bmio_set_susp_al(struct drbd_device *device) +static int drbd_bmio_set_susp_al(struct drbd_device *device) __must_hold(local) { int rv; @@ -2719,8 +2724,13 @@ int drbd_adm_invalidate_peer(struct sk_buff *skb, struct genl_info *info) if (retcode != NO_ERROR) goto out; - mutex_lock(&adm_ctx.resource->adm_mutex); device = adm_ctx.device; + if (!get_ldev(device)) { + retcode = ERR_NO_DISK; + goto out; + } + + mutex_lock(&adm_ctx.resource->adm_mutex); /* If there is still bitmap IO pending, probably because of a previous * resync just being finished, wait for it before requesting a new resync. @@ -2747,6 +2757,7 @@ int drbd_adm_invalidate_peer(struct sk_buff *skb, struct genl_info *info) retcode = drbd_request_state(device, NS(conn, C_STARTING_SYNC_S)); drbd_resume_io(device); mutex_unlock(&adm_ctx.resource->adm_mutex); + put_ldev(device); out: drbd_adm_finish(&adm_ctx, info, retcode); return 0; diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c index c7084188c2ae..be0c3761cdc6 100644 --- a/drivers/block/drbd/drbd_receiver.c +++ b/drivers/block/drbd/drbd_receiver.c @@ -1168,7 +1168,7 @@ static void drbd_flush(struct drbd_connection *connection) /* would rather check on EOPNOTSUPP, but that is not reliable. * don't try again for ANY return value != 0 * if (rv == -EOPNOTSUPP) */ - drbd_bump_write_ordering(connection->resource, WO_drain_io); + drbd_bump_write_ordering(connection->resource, NULL, WO_drain_io); } put_ldev(device); kref_put(&device->kref, drbd_destroy_device); @@ -1257,14 +1257,29 @@ static enum finish_epoch drbd_may_finish_epoch(struct drbd_connection *connectio return rv; } +static enum write_ordering_e +max_allowed_wo(struct drbd_backing_dev *bdev, enum write_ordering_e wo) +{ + struct disk_conf *dc; + + dc = rcu_dereference(bdev->disk_conf); + + if (wo == WO_bdev_flush && !dc->disk_flushes) + wo = WO_drain_io; + if (wo == WO_drain_io && !dc->disk_drain) + wo = WO_none; + + return wo; +} + /** * drbd_bump_write_ordering() - Fall back to an other write ordering method * @connection: DRBD connection. * @wo: Write ordering method to try. */ -void drbd_bump_write_ordering(struct drbd_resource *resource, enum write_ordering_e wo) +void drbd_bump_write_ordering(struct drbd_resource *resource, struct drbd_backing_dev *bdev, + enum write_ordering_e wo) { - struct disk_conf *dc; struct drbd_device *device; enum write_ordering_e pwo; int vnr; @@ -1278,17 +1293,18 @@ void drbd_bump_write_ordering(struct drbd_resource *resource, enum write_orderin wo = min(pwo, wo); rcu_read_lock(); idr_for_each_entry(&resource->devices, device, vnr) { - if (!get_ldev_if_state(device, D_ATTACHING)) - continue; - dc = rcu_dereference(device->ldev->disk_conf); - - if (wo == WO_bdev_flush && !dc->disk_flushes) - wo = WO_drain_io; - if (wo == WO_drain_io && !dc->disk_drain) - wo = WO_none; - put_ldev(device); + if (get_ldev(device)) { + wo = max_allowed_wo(device->ldev, wo); + if (device->ldev == bdev) + bdev = NULL; + put_ldev(device); + } } rcu_read_unlock(); + + if (bdev) + wo = max_allowed_wo(bdev, wo); + resource->write_ordering = wo; if (pwo != resource->write_ordering || wo == WO_bdev_flush) drbd_info(resource, "Method to ensure write ordering: %s\n", write_ordering_str[resource->write_ordering]); @@ -3709,7 +3725,6 @@ static int receive_sizes(struct drbd_connection *connection, struct packet_info } device->peer_max_bio_size = be32_to_cpu(p->max_bio_size); - drbd_reconsider_max_bio_size(device); /* Leave drbd_reconsider_max_bio_size() before drbd_determine_dev_size(). In case we cleared the QUEUE_FLAG_DISCARD from our queue in drbd_reconsider_max_bio_size(), we can be sure that after @@ -3717,6 +3732,7 @@ static int receive_sizes(struct drbd_connection *connection, struct packet_info ddsf = be16_to_cpu(p->dds_flags); if (get_ldev(device)) { + drbd_reconsider_max_bio_size(device, device->ldev); dd = drbd_determine_dev_size(device, ddsf, NULL); put_ldev(device); if (dd == DS_ERROR) @@ -3724,6 +3740,7 @@ static int receive_sizes(struct drbd_connection *connection, struct packet_info drbd_md_sync(device); } else { /* I am diskless, need to accept the peer's size. */ + drbd_reconsider_max_bio_size(device, NULL); drbd_set_my_capacity(device, p_size); } diff --git a/include/linux/drbd.h b/include/linux/drbd.h index 3dbe9bd57a09..20ec8903b1e4 100644 --- a/include/linux/drbd.h +++ b/include/linux/drbd.h @@ -245,7 +245,7 @@ enum drbd_disk_state { D_DISKLESS, D_ATTACHING, /* In the process of reading the meta-data */ D_FAILED, /* Becomes D_DISKLESS as soon as we told it the peer */ - /* when >= D_FAILED it is legal to access mdev->bc */ + /* when >= D_FAILED it is legal to access mdev->ldev */ D_NEGOTIATING, /* Late attaching state, we need to talk to the peer */ D_INCONSISTENT, D_OUTDATED, -- cgit v1.2.3 From f0175ab51993d2dc2728e7b22a16ffb0c8f4cfa0 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Wed, 9 Jul 2014 12:20:08 +0200 Subject: usb: gadget: f_fs: OS descriptors support Add support for OS descriptors. The new format of descriptors is used, because the "flags" field is required for extensions. os_count gives the number of OSDesc[] elements. The format of descriptors is given in include/uapi/linux/usb/functionfs.h. For extended properties descriptor the usb_ext_prop_desc structure covers only a part of a descriptor, because the wPropertyNameLength is unknown up front. Signed-off-by: Andrzej Pietrasiewicz Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_fs.c | 341 +++++++++++++++++++++++++++++++++++- drivers/usb/gadget/u_fs.h | 7 + include/uapi/linux/usb/functionfs.h | 81 ++++++++- 3 files changed, 419 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index e1b2ddd7964a..fe45060e0a7a 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -34,6 +34,7 @@ #include "u_fs.h" #include "u_f.h" +#include "u_os_desc.h" #include "configfs.h" #define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */ @@ -1644,11 +1645,19 @@ enum ffs_entity_type { FFS_DESCRIPTOR, FFS_INTERFACE, FFS_STRING, FFS_ENDPOINT }; +enum ffs_os_desc_type { + FFS_OS_DESC, FFS_OS_DESC_EXT_COMPAT, FFS_OS_DESC_EXT_PROP +}; + typedef int (*ffs_entity_callback)(enum ffs_entity_type entity, u8 *valuep, struct usb_descriptor_header *desc, void *priv); +typedef int (*ffs_os_desc_callback)(enum ffs_os_desc_type entity, + struct usb_os_desc_header *h, void *data, + unsigned len, void *priv); + static int __must_check ffs_do_single_desc(char *data, unsigned len, ffs_entity_callback entity, void *priv) @@ -1856,11 +1865,191 @@ static int __ffs_data_do_entity(enum ffs_entity_type type, return 0; } +static int __ffs_do_os_desc_header(enum ffs_os_desc_type *next_type, + struct usb_os_desc_header *desc) +{ + u16 bcd_version = le16_to_cpu(desc->bcdVersion); + u16 w_index = le16_to_cpu(desc->wIndex); + + if (bcd_version != 1) { + pr_vdebug("unsupported os descriptors version: %d", + bcd_version); + return -EINVAL; + } + switch (w_index) { + case 0x4: + *next_type = FFS_OS_DESC_EXT_COMPAT; + break; + case 0x5: + *next_type = FFS_OS_DESC_EXT_PROP; + break; + default: + pr_vdebug("unsupported os descriptor type: %d", w_index); + return -EINVAL; + } + + return sizeof(*desc); +} + +/* + * Process all extended compatibility/extended property descriptors + * of a feature descriptor + */ +static int __must_check ffs_do_single_os_desc(char *data, unsigned len, + enum ffs_os_desc_type type, + u16 feature_count, + ffs_os_desc_callback entity, + void *priv, + struct usb_os_desc_header *h) +{ + int ret; + const unsigned _len = len; + + ENTER(); + + /* loop over all ext compat/ext prop descriptors */ + while (feature_count--) { + ret = entity(type, h, data, len, priv); + if (unlikely(ret < 0)) { + pr_debug("bad OS descriptor, type: %d\n", type); + return ret; + } + data += ret; + len -= ret; + } + return _len - len; +} + +/* Process a number of complete Feature Descriptors (Ext Compat or Ext Prop) */ +static int __must_check ffs_do_os_descs(unsigned count, + char *data, unsigned len, + ffs_os_desc_callback entity, void *priv) +{ + const unsigned _len = len; + unsigned long num = 0; + + ENTER(); + + for (num = 0; num < count; ++num) { + int ret; + enum ffs_os_desc_type type; + u16 feature_count; + struct usb_os_desc_header *desc = (void *)data; + + if (len < sizeof(*desc)) + return -EINVAL; + + /* + * Record "descriptor" entity. + * Process dwLength, bcdVersion, wIndex, get b/wCount. + * Move the data pointer to the beginning of extended + * compatibilities proper or extended properties proper + * portions of the data + */ + if (le32_to_cpu(desc->dwLength) > len) + return -EINVAL; + + ret = __ffs_do_os_desc_header(&type, desc); + if (unlikely(ret < 0)) { + pr_debug("entity OS_DESCRIPTOR(%02lx); ret = %d\n", + num, ret); + return ret; + } + /* + * 16-bit hex "?? 00" Little Endian looks like 8-bit hex "??" + */ + feature_count = le16_to_cpu(desc->wCount); + if (type == FFS_OS_DESC_EXT_COMPAT && + (feature_count > 255 || desc->Reserved)) + return -EINVAL; + len -= ret; + data += ret; + + /* + * Process all function/property descriptors + * of this Feature Descriptor + */ + ret = ffs_do_single_os_desc(data, len, type, + feature_count, entity, priv, desc); + if (unlikely(ret < 0)) { + pr_debug("%s returns %d\n", __func__, ret); + return ret; + } + + len -= ret; + data += ret; + } + return _len - len; +} + +/** + * Validate contents of the buffer from userspace related to OS descriptors. + */ +static int __ffs_data_do_os_desc(enum ffs_os_desc_type type, + struct usb_os_desc_header *h, void *data, + unsigned len, void *priv) +{ + struct ffs_data *ffs = priv; + u8 length; + + ENTER(); + + switch (type) { + case FFS_OS_DESC_EXT_COMPAT: { + struct usb_ext_compat_desc *d = data; + int i; + + if (len < sizeof(*d) || + d->bFirstInterfaceNumber >= ffs->interfaces_count || + d->Reserved1) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(d->Reserved2); ++i) + if (d->Reserved2[i]) + return -EINVAL; + + length = sizeof(struct usb_ext_compat_desc); + } + break; + case FFS_OS_DESC_EXT_PROP: { + struct usb_ext_prop_desc *d = data; + u32 type, pdl; + u16 pnl; + + if (len < sizeof(*d) || h->interface >= ffs->interfaces_count) + return -EINVAL; + length = le32_to_cpu(d->dwSize); + type = le32_to_cpu(d->dwPropertyDataType); + if (type < USB_EXT_PROP_UNICODE || + type > USB_EXT_PROP_UNICODE_MULTI) { + pr_vdebug("unsupported os descriptor property type: %d", + type); + return -EINVAL; + } + pnl = le16_to_cpu(d->wPropertyNameLength); + pdl = le32_to_cpu(*(u32 *)((u8 *)data + 10 + pnl)); + if (length != 14 + pnl + pdl) { + pr_vdebug("invalid os descriptor length: %d pnl:%d pdl:%d (descriptor %d)\n", + length, pnl, pdl, type); + return -EINVAL; + } + ++ffs->ms_os_descs_ext_prop_count; + /* property name reported to the host as "WCHAR"s */ + ffs->ms_os_descs_ext_prop_name_len += pnl * 2; + ffs->ms_os_descs_ext_prop_data_len += pdl; + } + break; + default: + pr_vdebug("unknown descriptor: %d\n", type); + return -EINVAL; + } + return length; +} + static int __ffs_data_got_descs(struct ffs_data *ffs, char *const _data, size_t len) { char *data = _data, *raw_descs; - unsigned counts[3], flags; + unsigned os_descs_count = 0, counts[3], flags; int ret = -EINVAL, i; ENTER(); @@ -1878,7 +2067,8 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, flags = get_unaligned_le32(data + 8); if (flags & ~(FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC | - FUNCTIONFS_HAS_SS_DESC)) { + FUNCTIONFS_HAS_SS_DESC | + FUNCTIONFS_HAS_MS_OS_DESC)) { ret = -ENOSYS; goto error; } @@ -1901,6 +2091,11 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, len -= 4; } } + if (flags & (1 << i)) { + os_descs_count = get_unaligned_le32(data); + data += 4; + len -= 4; + }; /* Read descriptors */ raw_descs = data; @@ -1914,6 +2109,14 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, data += ret; len -= ret; } + if (os_descs_count) { + ret = ffs_do_os_descs(os_descs_count, data, len, + __ffs_data_do_os_desc, ffs); + if (ret < 0) + goto error; + data += ret; + len -= ret; + } if (raw_descs == data || len) { ret = -EINVAL; @@ -1926,6 +2129,7 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, ffs->fs_descs_count = counts[0]; ffs->hs_descs_count = counts[1]; ffs->ss_descs_count = counts[2]; + ffs->ms_os_descs_count = os_descs_count; return 0; @@ -2267,6 +2471,85 @@ static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep, return 0; } +static int __ffs_func_bind_do_os_desc(enum ffs_os_desc_type type, + struct usb_os_desc_header *h, void *data, + unsigned len, void *priv) +{ + struct ffs_function *func = priv; + u8 length = 0; + + switch (type) { + case FFS_OS_DESC_EXT_COMPAT: { + struct usb_ext_compat_desc *desc = data; + struct usb_os_desc_table *t; + + t = &func->function.os_desc_table[desc->bFirstInterfaceNumber]; + t->if_id = func->interfaces_nums[desc->bFirstInterfaceNumber]; + memcpy(t->os_desc->ext_compat_id, &desc->CompatibleID, + ARRAY_SIZE(desc->CompatibleID) + + ARRAY_SIZE(desc->SubCompatibleID)); + length = sizeof(*desc); + } + break; + case FFS_OS_DESC_EXT_PROP: { + struct usb_ext_prop_desc *desc = data; + struct usb_os_desc_table *t; + struct usb_os_desc_ext_prop *ext_prop; + char *ext_prop_name; + char *ext_prop_data; + + t = &func->function.os_desc_table[h->interface]; + t->if_id = func->interfaces_nums[h->interface]; + + ext_prop = func->ffs->ms_os_descs_ext_prop_avail; + func->ffs->ms_os_descs_ext_prop_avail += sizeof(*ext_prop); + + ext_prop->type = le32_to_cpu(desc->dwPropertyDataType); + ext_prop->name_len = le16_to_cpu(desc->wPropertyNameLength); + ext_prop->data_len = le32_to_cpu(*(u32 *) + usb_ext_prop_data_len_ptr(data, ext_prop->name_len)); + length = ext_prop->name_len + ext_prop->data_len + 14; + + ext_prop_name = func->ffs->ms_os_descs_ext_prop_name_avail; + func->ffs->ms_os_descs_ext_prop_name_avail += + ext_prop->name_len; + + ext_prop_data = func->ffs->ms_os_descs_ext_prop_data_avail; + func->ffs->ms_os_descs_ext_prop_data_avail += + ext_prop->data_len; + memcpy(ext_prop_data, + usb_ext_prop_data_ptr(data, ext_prop->name_len), + ext_prop->data_len); + /* unicode data reported to the host as "WCHAR"s */ + switch (ext_prop->type) { + case USB_EXT_PROP_UNICODE: + case USB_EXT_PROP_UNICODE_ENV: + case USB_EXT_PROP_UNICODE_LINK: + case USB_EXT_PROP_UNICODE_MULTI: + ext_prop->data_len *= 2; + break; + } + ext_prop->data = ext_prop_data; + + memcpy(ext_prop_name, usb_ext_prop_name_ptr(data), + ext_prop->name_len); + /* property name reported to the host as "WCHAR"s */ + ext_prop->name_len *= 2; + ext_prop->name = ext_prop_name; + + t->os_desc->ext_prop_len += + ext_prop->name_len + ext_prop->data_len + 14; + ++t->os_desc->ext_prop_count; + list_add_tail(&ext_prop->entry, &t->os_desc->ext_prop); + } + break; + default: + pr_vdebug("unknown descriptor: %d\n", type); + } + + return length; +} + static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f, struct usb_configuration *c) { @@ -2328,7 +2611,7 @@ static int _ffs_func_bind(struct usb_configuration *c, const int super = gadget_is_superspeed(func->gadget) && func->ffs->ss_descs_count; - int fs_len, hs_len, ret; + int fs_len, hs_len, ss_len, ret, i; /* Make it a single chunk, less management later on */ vla_group(d); @@ -2340,6 +2623,18 @@ static int _ffs_func_bind(struct usb_configuration *c, vla_item_with_sz(d, struct usb_descriptor_header *, ss_descs, super ? ffs->ss_descs_count + 1 : 0); vla_item_with_sz(d, short, inums, ffs->interfaces_count); + vla_item_with_sz(d, struct usb_os_desc_table, os_desc_table, + c->cdev->use_os_string ? ffs->interfaces_count : 0); + vla_item_with_sz(d, char[16], ext_compat, + c->cdev->use_os_string ? ffs->interfaces_count : 0); + vla_item_with_sz(d, struct usb_os_desc, os_desc, + c->cdev->use_os_string ? ffs->interfaces_count : 0); + vla_item_with_sz(d, struct usb_os_desc_ext_prop, ext_prop, + ffs->ms_os_descs_ext_prop_count); + vla_item_with_sz(d, char, ext_prop_name, + ffs->ms_os_descs_ext_prop_name_len); + vla_item_with_sz(d, char, ext_prop_data, + ffs->ms_os_descs_ext_prop_data_len); vla_item_with_sz(d, char, raw_descs, ffs->raw_descs_length); char *vlabuf; @@ -2350,12 +2645,16 @@ static int _ffs_func_bind(struct usb_configuration *c, return -ENOTSUPP; /* Allocate a single chunk, less management later on */ - vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL); + vlabuf = kzalloc(vla_group_size(d), GFP_KERNEL); if (unlikely(!vlabuf)) return -ENOMEM; - /* Zero */ - memset(vla_ptr(vlabuf, d, eps), 0, d_eps__sz); + ffs->ms_os_descs_ext_prop_avail = vla_ptr(vlabuf, d, ext_prop); + ffs->ms_os_descs_ext_prop_name_avail = + vla_ptr(vlabuf, d, ext_prop_name); + ffs->ms_os_descs_ext_prop_data_avail = + vla_ptr(vlabuf, d, ext_prop_data); + /* Copy descriptors */ memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs, ffs->raw_descs_length); @@ -2409,12 +2708,16 @@ static int _ffs_func_bind(struct usb_configuration *c, if (likely(super)) { func->function.ss_descriptors = vla_ptr(vlabuf, d, ss_descs); - ret = ffs_do_descs(ffs->ss_descs_count, + ss_len = ffs_do_descs(ffs->ss_descs_count, vla_ptr(vlabuf, d, raw_descs) + fs_len + hs_len, d_raw_descs__sz - fs_len - hs_len, __ffs_func_bind_do_descs, func); - if (unlikely(ret < 0)) + if (unlikely(ss_len < 0)) { + ret = ss_len; goto error; + } + } else { + ss_len = 0; } /* @@ -2430,6 +2733,28 @@ static int _ffs_func_bind(struct usb_configuration *c, if (unlikely(ret < 0)) goto error; + func->function.os_desc_table = vla_ptr(vlabuf, d, os_desc_table); + if (c->cdev->use_os_string) + for (i = 0; i < ffs->interfaces_count; ++i) { + struct usb_os_desc *desc; + + desc = func->function.os_desc_table[i].os_desc = + vla_ptr(vlabuf, d, os_desc) + + i * sizeof(struct usb_os_desc); + desc->ext_compat_id = + vla_ptr(vlabuf, d, ext_compat) + i * 16; + INIT_LIST_HEAD(&desc->ext_prop); + } + ret = ffs_do_os_descs(ffs->ms_os_descs_count, + vla_ptr(vlabuf, d, raw_descs) + + fs_len + hs_len + ss_len, + d_raw_descs__sz - fs_len - hs_len - ss_len, + __ffs_func_bind_do_os_desc, func); + if (unlikely(ret < 0)) + goto error; + func->function.os_desc_n = + c->cdev->use_os_string ? ffs->interfaces_count : 0; + /* And we're done */ ffs_event_add(ffs, FUNCTIONFS_BIND); return 0; diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/u_fs.h index bf0ba375d459..63d6e71569c1 100644 --- a/drivers/usb/gadget/u_fs.h +++ b/drivers/usb/gadget/u_fs.h @@ -216,6 +216,13 @@ struct ffs_data { unsigned fs_descs_count; unsigned hs_descs_count; unsigned ss_descs_count; + unsigned ms_os_descs_count; + unsigned ms_os_descs_ext_prop_count; + unsigned ms_os_descs_ext_prop_name_len; + unsigned ms_os_descs_ext_prop_data_len; + void *ms_os_descs_ext_prop_avail; + void *ms_os_descs_ext_prop_name_avail; + void *ms_os_descs_ext_prop_data_avail; unsigned short strings_count; unsigned short interfaces_count; diff --git a/include/uapi/linux/usb/functionfs.h b/include/uapi/linux/usb/functionfs.h index 2a4b4a72a4f9..b66fae77c08c 100644 --- a/include/uapi/linux/usb/functionfs.h +++ b/include/uapi/linux/usb/functionfs.h @@ -18,10 +18,9 @@ enum functionfs_flags { FUNCTIONFS_HAS_FS_DESC = 1, FUNCTIONFS_HAS_HS_DESC = 2, FUNCTIONFS_HAS_SS_DESC = 4, + FUNCTIONFS_HAS_MS_OS_DESC = 8, }; -#ifndef __KERNEL__ - /* Descriptor of an non-audio endpoint */ struct usb_endpoint_descriptor_no_audio { __u8 bLength; @@ -33,6 +32,36 @@ struct usb_endpoint_descriptor_no_audio { __u8 bInterval; } __attribute__((packed)); +/* MS OS Descriptor header */ +struct usb_os_desc_header { + __u8 interface; + __le32 dwLength; + __le16 bcdVersion; + __le16 wIndex; + union { + struct { + __u8 bCount; + __u8 Reserved; + }; + __le16 wCount; + }; +} __attribute__((packed)); + +struct usb_ext_compat_desc { + __u8 bFirstInterfaceNumber; + __u8 Reserved1; + __u8 CompatibleID[8]; + __u8 SubCompatibleID[8]; + __u8 Reserved2[6]; +}; + +struct usb_ext_prop_desc { + __le32 dwSize; + __le32 dwPropertyDataType; + __le16 wPropertyNameLength; +} __attribute__((packed)); + +#ifndef __KERNEL__ /* * Descriptors format: @@ -45,9 +74,11 @@ struct usb_endpoint_descriptor_no_audio { * | | fs_count | LE32 | number of full-speed descriptors | * | | hs_count | LE32 | number of high-speed descriptors | * | | ss_count | LE32 | number of super-speed descriptors | + * | | os_count | LE32 | number of MS OS descriptors | * | | fs_descrs | Descriptor[] | list of full-speed descriptors | * | | hs_descrs | Descriptor[] | list of high-speed descriptors | * | | ss_descrs | Descriptor[] | list of super-speed descriptors | + * | | os_descrs | OSDesc[] | list of MS OS descriptors | * * Depending on which flags are set, various fields may be missing in the * structure. Any flags that are not recognised cause the whole block to be @@ -74,6 +105,52 @@ struct usb_endpoint_descriptor_no_audio { * | 0 | bLength | U8 | length of the descriptor | * | 1 | bDescriptorType | U8 | descriptor type | * | 2 | payload | | descriptor's payload | + * + * OSDesc[] is an array of valid MS OS Feature Descriptors which have one of + * the following formats: + * + * | off | name | type | description | + * |-----+-----------------+------+--------------------------| + * | 0 | inteface | U8 | related interface number | + * | 1 | dwLength | U32 | length of the descriptor | + * | 5 | bcdVersion | U16 | currently supported: 1 | + * | 7 | wIndex | U16 | currently supported: 4 | + * | 9 | bCount | U8 | number of ext. compat. | + * | 10 | Reserved | U8 | 0 | + * | 11 | ExtCompat[] | | list of ext. compat. d. | + * + * | off | name | type | description | + * |-----+-----------------+------+--------------------------| + * | 0 | inteface | U8 | related interface number | + * | 1 | dwLength | U32 | length of the descriptor | + * | 5 | bcdVersion | U16 | currently supported: 1 | + * | 7 | wIndex | U16 | currently supported: 5 | + * | 9 | wCount | U16 | number of ext. compat. | + * | 11 | ExtProp[] | | list of ext. prop. d. | + * + * ExtCompat[] is an array of valid Extended Compatiblity descriptors + * which have the following format: + * + * | off | name | type | description | + * |-----+-----------------------+------+-------------------------------------| + * | 0 | bFirstInterfaceNumber | U8 | index of the interface or of the 1st| + * | | | | interface in an IAD group | + * | 1 | Reserved | U8 | 0 | + * | 2 | CompatibleID | U8[8]| compatible ID string | + * | 10 | SubCompatibleID | U8[8]| subcompatible ID string | + * | 18 | Reserved | U8[6]| 0 | + * + * ExtProp[] is an array of valid Extended Properties descriptors + * which have the following format: + * + * | off | name | type | description | + * |-----+-----------------------+------+-------------------------------------| + * | 0 | dwSize | U32 | length of the descriptor | + * | 4 | dwPropertyDataType | U32 | 1..7 | + * | 8 | wPropertyNameLength | U16 | bPropertyName length (NL) | + * | 10 | bPropertyName |U8[NL]| name of this property | + * |10+NL| dwPropertyDataLength | U32 | bPropertyData length (DL) | + * |14+NL| bProperty |U8[DL]| payload of this property | */ struct usb_functionfs_strings_head { -- cgit v1.2.3 From 7ec3ea181d8cffb669982d79664f119ef6a71fc3 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 9 Jul 2014 18:09:55 +0200 Subject: usb: gadget: Add helper macro for usb_composite_driver boilerplate Introduce the module_usb_composite_driver macro as a convenience macro for USB gadget composite driver modules, similar to module_usb_driver. It is intended to be used by drivers which init/exit section does nothing but calling usb_composite_probe/usb_composite_unrregister. By using this macro it is possible to eliminate a few lines of boilerplate code per USB gadget composite driver. Based on f3a6a4b6 ("USB: Add helper macro for usb_driver boilerplate") which introduced the according macro for USB drivers. Signed-off-by: Tobias Klauser Signed-off-by: Felipe Balbi --- include/linux/usb/composite.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'include') diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 7373203140e7..c330f5ef42cf 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -386,6 +386,21 @@ struct usb_composite_driver { extern int usb_composite_probe(struct usb_composite_driver *driver); extern void usb_composite_unregister(struct usb_composite_driver *driver); + +/** + * module_usb_composite_driver() - Helper macro for registering a USB gadget + * composite driver + * @__usb_composite_driver: usb_composite_driver struct + * + * Helper macro for USB gadget composite drivers which do not do anything + * special in module init/exit. This eliminates a lot of boilerplate. Each + * module may only use this macro once, and calling it replaces module_init() + * and module_exit() + */ +#define module_usb_composite_driver(__usb_composite_driver) \ + module_driver(__usb_composite_driver, usb_composite_probe, \ + usb_composite_unregister) + extern void usb_composite_setup_continue(struct usb_composite_dev *cdev); extern int composite_dev_prepare(struct usb_composite_driver *composite, struct usb_composite_dev *cdev); -- cgit v1.2.3 From 4da6daf4d3df5a977e4623963f141a627fd2efce Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Thu, 10 Jul 2014 10:17:48 -0400 Subject: selinux: fix the default socket labeling in sock_graft() The sock_graft() hook has special handling for AF_INET, AF_INET, and AF_UNIX sockets as those address families have special hooks which label the sock before it is attached its associated socket. Unfortunately, the sock_graft() hook was missing a default approach to labeling sockets which meant that any other address family which made use of connections or the accept() syscall would find the returned socket to be in an "unlabeled" state. This was recently demonstrated by the kcrypto/AF_ALG subsystem and the newly released cryptsetup package (cryptsetup v1.6.5 and later). This patch preserves the special handling in selinux_sock_graft(), but adds a default behavior - setting the sock's label equal to the associated socket - which resolves the problem with AF_ALG and presumably any other address family which makes use of accept(). Cc: stable@vger.kernel.org Signed-off-by: Paul Moore Tested-by: Milan Broz --- include/linux/security.h | 5 ++++- security/selinux/hooks.c | 13 +++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/security.h b/include/linux/security.h index 6478ce3252c7..794be735ff4b 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -987,7 +987,10 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * Retrieve the LSM-specific secid for the sock to enable caching of network * authorizations. * @sock_graft: - * Sets the socket's isec sid to the sock's sid. + * This hook is called in response to a newly created sock struct being + * grafted onto an existing socket and allows the security module to + * perform whatever security attribute management is necessary for both + * the sock and socket. * @inet_conn_request: * Sets the openreq's sid to socket's sid with MLS portion taken from peer sid. * @inet_csk_clone: diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 336f0a04450e..b3a6754e932b 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4499,9 +4499,18 @@ static void selinux_sock_graft(struct sock *sk, struct socket *parent) struct inode_security_struct *isec = SOCK_INODE(parent)->i_security; struct sk_security_struct *sksec = sk->sk_security; - if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 || - sk->sk_family == PF_UNIX) + switch (sk->sk_family) { + case PF_INET: + case PF_INET6: + case PF_UNIX: isec->sid = sksec->sid; + break; + default: + /* by default there is no special labeling mechanism for the + * sksec label so inherit the label from the parent socket */ + BUG_ON(sksec->sid != SECINITSID_UNLABELED); + sksec->sid = isec->sid; + } sksec->sclass = isec->sclass; } -- cgit v1.2.3 From 0ac8a52d4541adc284a4f3e8a1f6847236de1d8a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 8 Jul 2014 16:51:49 +0200 Subject: ALSA: Provide a CLOCK_MONOTONIC_RAW timestamp type For applications which need to synchronise with external timebases such as broadcast TV applications the kernel monotonic time is not optimal as it includes adjustments from NTP and so may still include discontinuities due to that. A raw monotonic time which does not include any adjustments is available in the kernel from getrawmonotonic() so provide userspace with a new timestamp type SNDRV_PCM_TSTAMP_TYPE_MONOTONIC_RAW which provides timestamps based on this as an option. [dropped tstamp_type assignment code, as it's no longer needed -- tiwai] Reported-by: Daniel Thompson Signed-off-by: Mark Brown Acked-by: Jaroslav Kysela Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 11 +++++++++-- include/uapi/sound/asound.h | 3 ++- 2 files changed, 11 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index d854fb31c000..6f3e10ca0e32 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -931,10 +931,17 @@ void snd_pcm_timer_done(struct snd_pcm_substream *substream); static inline void snd_pcm_gettime(struct snd_pcm_runtime *runtime, struct timespec *tv) { - if (runtime->tstamp_type == SNDRV_PCM_TSTAMP_TYPE_MONOTONIC) + switch (runtime->tstamp_type) { + case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC: ktime_get_ts(tv); - else + break; + case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC_RAW: + getrawmonotonic(tv); + break; + default: getnstimeofday(tv); + break; + } } /* diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index 224948342f14..cbf7dc850a46 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -462,7 +462,8 @@ struct snd_xfern { enum { SNDRV_PCM_TSTAMP_TYPE_GETTIMEOFDAY = 0, /* gettimeofday equivalent */ SNDRV_PCM_TSTAMP_TYPE_MONOTONIC, /* posix_clock_monotonic equivalent */ - SNDRV_PCM_TSTAMP_TYPE_LAST = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC, + SNDRV_PCM_TSTAMP_TYPE_MONOTONIC_RAW, /* monotonic_raw (no NTP) */ + SNDRV_PCM_TSTAMP_TYPE_LAST = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC_RAW, }; /* channel positions */ -- cgit v1.2.3 From 5646eda5851e6cfdfa22d41895e3f5daffa643d3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jul 2014 09:50:19 +0200 Subject: ALSA: pcm: Add timestamp type to sw_params For allowing adjusting the timestamp type on the fly, add it to sw_params. The existing ioctl is still kept for compatibility. Along with this, increment the PCM protocol version. The extension was suggested by Clemens Ladisch. Acked-by: Jaroslav Kysela Reviewed-by: Mark Brown Signed-off-by: Takashi Iwai --- include/uapi/sound/asound.h | 6 ++++-- sound/core/pcm_native.c | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index cbf7dc850a46..a7e062f91f39 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -139,7 +139,7 @@ struct snd_hwdep_dsp_image { * * *****************************************************************************/ -#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 11) +#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 12) typedef unsigned long snd_pcm_uframes_t; typedef signed long snd_pcm_sframes_t; @@ -391,7 +391,9 @@ struct snd_pcm_sw_params { snd_pcm_uframes_t silence_threshold; /* min distance from noise for silence filling */ snd_pcm_uframes_t silence_size; /* silence block size */ snd_pcm_uframes_t boundary; /* pointers wrap point */ - unsigned char reserved[64]; /* reserved for future */ + unsigned int tstamp_type; /* timestamp type */ + int pads; /* alignment, reserved */ + unsigned char reserved[56]; /* reserved for future */ }; struct snd_pcm_channel_info { diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 2372c49a8e84..81dedc381efd 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -543,6 +543,8 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream, if (params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST) return -EINVAL; + if (params->tstamp_type > SNDRV_PCM_TSTAMP_TYPE_LAST) + return -EINVAL; if (params->avail_min == 0) return -EINVAL; if (params->silence_size >= runtime->boundary) { @@ -557,6 +559,7 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream, err = 0; snd_pcm_stream_lock_irq(substream); runtime->tstamp_mode = params->tstamp_mode; + runtime->tstamp_type = params->tstamp_type; runtime->period_step = params->period_step; runtime->control->avail_min = params->avail_min; runtime->start_threshold = params->start_threshold; -- cgit v1.2.3 From aaaba34576407857f6146ff6c330f06e63fb2bf2 Mon Sep 17 00:00:00 2001 From: Lars Ellenberg Date: Tue, 18 Mar 2014 12:30:09 +0100 Subject: drbd: implement csums-after-crash-only Checksum based resync trades CPU cycles for network bandwidth, in situations where we expect much of the to-be-resynced blocks to be actually identical on both sides already. In a "network hickup" scenario, it won't help: all to-be-resynced blocks will typically be different. The use case is for the resync of *potentially* different blocks after crash recovery -- the crash recovery had marked larger areas (those covered by the activity log) as need-to-be-resynced, just in case. Most of those blocks will be identical. This option makes it possible to configure checksum based resync, but only actually use it for the first resync after primary crash. Signed-off-by: Philipp Reisner Signed-off-by: Lars Ellenberg --- drivers/block/drbd/drbd_int.h | 2 ++ drivers/block/drbd/drbd_receiver.c | 2 ++ drivers/block/drbd/drbd_worker.c | 24 ++++++++++++++++++++---- include/linux/drbd_genl.h | 3 +++ include/linux/drbd_limits.h | 1 + 5 files changed, 28 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h index abf5aefd9790..fe6595a96a9a 100644 --- a/drivers/block/drbd/drbd_int.h +++ b/drivers/block/drbd/drbd_int.h @@ -738,6 +738,8 @@ struct drbd_device { struct rb_root read_requests; struct rb_root write_requests; + /* use checksums for *this* resync */ + bool use_csums; /* blocks to resync in this run [unit BM_BLOCK_SIZE] */ unsigned long rs_total; /* number of resync blocks that failed in this run */ diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c index 5626c5babc3f..d326af67c27e 100644 --- a/drivers/block/drbd/drbd_receiver.c +++ b/drivers/block/drbd/drbd_receiver.c @@ -2555,6 +2555,8 @@ static int receive_DataRequest(struct drbd_connection *connection, struct packet peer_req->w.cb = w_e_end_csum_rs_req; /* used in the sector offset progress display */ device->bm_resync_fo = BM_SECT_TO_BIT(sector); + /* remember to report stats in drbd_resync_finished */ + device->use_csums = true; } else if (pi->cmd == P_OV_REPLY) { /* track progress, we may need to throttle */ atomic_add(size >> 9, &device->rs_sect_in); diff --git a/drivers/block/drbd/drbd_worker.c b/drivers/block/drbd/drbd_worker.c index 2ff5fd49a3b1..6532a697cf49 100644 --- a/drivers/block/drbd/drbd_worker.c +++ b/drivers/block/drbd/drbd_worker.c @@ -698,8 +698,8 @@ next_sector: /* adjust very last sectors, in case we are oddly sized */ if (sector + (size>>9) > capacity) size = (capacity-sector)<<9; - if (connection->agreed_pro_version >= 89 && - connection->csums_tfm) { + + if (device->use_csums) { switch (read_for_csum(peer_device, sector, size)) { case -EIO: /* Disk failure */ put_ldev(device); @@ -913,7 +913,7 @@ int drbd_resync_finished(struct drbd_device *device) if (os.conn == C_SYNC_TARGET || os.conn == C_PAUSED_SYNC_T) khelper_cmd = "after-resync-target"; - if (first_peer_device(device)->connection->csums_tfm && device->rs_total) { + if (device->use_csums && device->rs_total) { const unsigned long s = device->rs_same_csum; const unsigned long t = device->rs_total; const int ratio = @@ -1622,6 +1622,18 @@ static void do_start_resync(struct drbd_device *device) clear_bit(AHEAD_TO_SYNC_SOURCE, &device->flags); } +static bool use_checksum_based_resync(struct drbd_connection *connection, struct drbd_device *device) +{ + bool csums_after_crash_only; + rcu_read_lock(); + csums_after_crash_only = rcu_dereference(connection->net_conf)->csums_after_crash_only; + rcu_read_unlock(); + return connection->agreed_pro_version >= 89 && /* supported? */ + connection->csums_tfm && /* configured? */ + (csums_after_crash_only == 0 /* use for each resync? */ + || test_bit(CRASHED_PRIMARY, &device->flags)); /* or only after Primary crash? */ +} + /** * drbd_start_resync() - Start the resync process * @device: DRBD device. @@ -1756,8 +1768,12 @@ void drbd_start_resync(struct drbd_device *device, enum drbd_conns side) drbd_conn_str(ns.conn), (unsigned long) device->rs_total << (BM_BLOCK_SHIFT-10), (unsigned long) device->rs_total); - if (side == C_SYNC_TARGET) + if (side == C_SYNC_TARGET) { device->bm_resync_fo = 0; + device->use_csums = use_checksum_based_resync(connection, device); + } else { + device->use_csums = 0; + } /* Since protocol 96, we must serialize drbd_gen_and_send_sync_uuid * with w_send_oos, or the sync target will get confused as to diff --git a/include/linux/drbd_genl.h b/include/linux/drbd_genl.h index 4193f5f2636c..71fc924c53fa 100644 --- a/include/linux/drbd_genl.h +++ b/include/linux/drbd_genl.h @@ -171,6 +171,9 @@ GENL_struct(DRBD_NLA_NET_CONF, 5, net_conf, __flg_field(28, DRBD_GENLA_F_MANDATORY | DRBD_F_INVARIANT, tentative) __flg_field_def(29, DRBD_GENLA_F_MANDATORY, use_rle, DRBD_USE_RLE_DEF) /* 9: __u32_field_def(30, DRBD_GENLA_F_MANDATORY, fencing_policy, DRBD_FENCING_DEF) */ + /* 9: __str_field_def(31, DRBD_GENLA_F_MANDATORY, name, SHARED_SECRET_MAX) */ + /* 9: __u32_field(32, DRBD_F_REQUIRED | DRBD_F_INVARIANT, peer_node_id) */ + __flg_field_def(33, 0 /* OPTIONAL */, csums_after_crash_only, DRBD_CSUMS_AFTER_CRASH_ONLY_DEF) ) GENL_struct(DRBD_NLA_SET_ROLE_PARMS, 6, set_role_parms, diff --git a/include/linux/drbd_limits.h b/include/linux/drbd_limits.h index 17e50bb00521..9d2df1d51414 100644 --- a/include/linux/drbd_limits.h +++ b/include/linux/drbd_limits.h @@ -214,6 +214,7 @@ #define DRBD_ALLOW_TWO_PRIMARIES_DEF 0 #define DRBD_ALWAYS_ASBP_DEF 0 #define DRBD_USE_RLE_DEF 1 +#define DRBD_CSUMS_AFTER_CRASH_ONLY_DEF 0 #define DRBD_AL_STRIPES_MIN 1 #define DRBD_AL_STRIPES_MAX 1024 -- cgit v1.2.3 From 5d0b17f1a29e8189d04aef447a3a53cfd05529b2 Mon Sep 17 00:00:00 2001 From: Philipp Reisner Date: Tue, 18 Mar 2014 14:24:35 +0100 Subject: drbd: New net configuration option socket-check-timeout In setups involving a DRBD-proxy and connections that experience a lot of buffer-bloat it might be necessary to set ping-timeout to an unusual high value. By default DRBD uses the same value to wait if a newly established TCP-connection is stable. Since the DRBD-proxy is usually located in the same data center such a long wait time may hinder DRBD's connect process. In such setups socket-check-timeout should be set to at least to the round trip time between DRBD and DRBD-proxy. I.e. in most cases to 1. Signed-off-by: Philipp Reisner Signed-off-by: Lars Ellenberg --- drivers/block/drbd/drbd_receiver.c | 46 +++++++++++++++++++++++++------------- include/linux/drbd_genl.h | 1 + include/linux/drbd_limits.h | 5 +++++ 3 files changed, 36 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c index 7da83f3a61eb..b89e6fb468c6 100644 --- a/drivers/block/drbd/drbd_receiver.c +++ b/drivers/block/drbd/drbd_receiver.c @@ -819,7 +819,7 @@ static int receive_first_packet(struct drbd_connection *connection, struct socke * drbd_socket_okay() - Free the socket if its connection is not okay * @sock: pointer to the pointer to the socket. */ -static int drbd_socket_okay(struct socket **sock) +static bool drbd_socket_okay(struct socket **sock) { int rr; char tb[4]; @@ -837,6 +837,30 @@ static int drbd_socket_okay(struct socket **sock) return false; } } + +static bool connection_established(struct drbd_connection *connection, + struct socket **sock1, + struct socket **sock2) +{ + struct net_conf *nc; + int timeout; + bool ok; + + if (!*sock1 || !*sock2) + return false; + + rcu_read_lock(); + nc = rcu_dereference(connection->net_conf); + timeout = (nc->sock_check_timeo ?: nc->ping_timeo) * HZ / 10; + rcu_read_unlock(); + schedule_timeout_interruptible(timeout); + + ok = drbd_socket_okay(sock1); + ok = drbd_socket_okay(sock2) && ok; + + return ok; +} + /* Gets called if a connection is established, or if a new minor gets created in a connection */ int drbd_connected(struct drbd_peer_device *peer_device) @@ -878,8 +902,8 @@ static int conn_connect(struct drbd_connection *connection) struct drbd_socket sock, msock; struct drbd_peer_device *peer_device; struct net_conf *nc; - int vnr, timeout, h, ok; - bool discard_my_data; + int vnr, timeout, h; + bool discard_my_data, ok; enum drbd_state_rv rv; struct accept_wait_data ad = { .connection = connection, @@ -923,17 +947,8 @@ static int conn_connect(struct drbd_connection *connection) } } - if (sock.socket && msock.socket) { - rcu_read_lock(); - nc = rcu_dereference(connection->net_conf); - timeout = nc->ping_timeo * HZ / 10; - rcu_read_unlock(); - schedule_timeout_interruptible(timeout); - ok = drbd_socket_okay(&sock.socket); - ok = drbd_socket_okay(&msock.socket) && ok; - if (ok) - break; - } + if (connection_established(connection, &sock.socket, &msock.socket)) + break; retry: s = drbd_wait_for_connect(connection, &ad); @@ -979,8 +994,7 @@ randomize: goto out_release_sockets; } - ok = drbd_socket_okay(&sock.socket); - ok = drbd_socket_okay(&msock.socket) && ok; + ok = connection_established(connection, &sock.socket, &msock.socket); } while (!ok); if (ad.s_listen) diff --git a/include/linux/drbd_genl.h b/include/linux/drbd_genl.h index 71fc924c53fa..7b131ed8f9c6 100644 --- a/include/linux/drbd_genl.h +++ b/include/linux/drbd_genl.h @@ -174,6 +174,7 @@ GENL_struct(DRBD_NLA_NET_CONF, 5, net_conf, /* 9: __str_field_def(31, DRBD_GENLA_F_MANDATORY, name, SHARED_SECRET_MAX) */ /* 9: __u32_field(32, DRBD_F_REQUIRED | DRBD_F_INVARIANT, peer_node_id) */ __flg_field_def(33, 0 /* OPTIONAL */, csums_after_crash_only, DRBD_CSUMS_AFTER_CRASH_ONLY_DEF) + __u32_field_def(34, 0 /* OPTIONAL */, sock_check_timeo, DRBD_SOCKET_CHECK_TIMEO_DEF) ) GENL_struct(DRBD_NLA_SET_ROLE_PARMS, 6, set_role_parms, diff --git a/include/linux/drbd_limits.h b/include/linux/drbd_limits.h index 9d2df1d51414..8ac8c5d9a3ad 100644 --- a/include/linux/drbd_limits.h +++ b/include/linux/drbd_limits.h @@ -225,4 +225,9 @@ #define DRBD_AL_STRIPE_SIZE_MAX 16777216 #define DRBD_AL_STRIPE_SIZE_DEF 32 #define DRBD_AL_STRIPE_SIZE_SCALE 'k' /* kilobytes */ + +#define DRBD_SOCKET_CHECK_TIMEO_MIN 0 +#define DRBD_SOCKET_CHECK_TIMEO_MAX DRBD_PING_TIMEO_MAX +#define DRBD_SOCKET_CHECK_TIMEO_DEF 0 +#define DRBD_SOCKET_CHECK_TIMEO_SCALE '1' #endif -- cgit v1.2.3 From bf0d6e4a1138e71cafdbbb99cde430eee50c4ff1 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 6 May 2014 14:28:32 +0300 Subject: drbd: silence underflow warning in read_in_block() My static checker warns that "data_size" could be negative and underflow the limit check. The code looks suspicious but I don't know if it is a real bug. Signed-off-by: Dan Carpenter Signed-off-by: Philipp Reisner Signed-off-by: Lars Ellenberg --- drivers/block/drbd/drbd_receiver.c | 2 +- include/linux/drbd.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c index f972988291c5..9342b8da73ab 100644 --- a/drivers/block/drbd/drbd_receiver.c +++ b/drivers/block/drbd/drbd_receiver.c @@ -1592,7 +1592,7 @@ read_in_block(struct drbd_peer_device *peer_device, u64 id, sector_t sector, struct drbd_peer_request *peer_req; struct page *page; int dgs, ds, err; - int data_size = pi->size; + unsigned int data_size = pi->size; void *dig_in = peer_device->connection->int_dig_in; void *dig_vv = peer_device->connection->int_dig_vv; unsigned long *data; diff --git a/include/linux/drbd.h b/include/linux/drbd.h index 20ec8903b1e4..debb70d40547 100644 --- a/include/linux/drbd.h +++ b/include/linux/drbd.h @@ -52,7 +52,7 @@ #endif extern const char *drbd_buildtag(void); -#define REL_VERSION "8.4.3" +#define REL_VERSION "8.4.5" #define API_VERSION 1 #define PRO_VERSION_MIN 86 #define PRO_VERSION_MAX 101 -- cgit v1.2.3 From 5d5eacb34c9e1fdc0a47b885d832eaa4de860dc7 Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Thu, 10 Jul 2014 07:01:58 -0400 Subject: bridge: fdb dumping takes a filter device Dumping a bridge fdb dumps every fdb entry held. With this change we are going to filter on selected bridge port. Signed-off-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/i40e/i40e_main.c | 3 ++- drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 7 ++++--- drivers/net/vxlan.c | 3 ++- include/linux/netdevice.h | 4 +++- include/linux/rtnetlink.h | 1 + net/bridge/br_fdb.c | 5 +++++ net/bridge/br_private.h | 2 +- net/core/rtnetlink.c | 9 ++++++--- 8 files changed, 24 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index e49352d68ede..2899f783ee1d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -7095,13 +7095,14 @@ static int i40e_ndo_fdb_del(struct ndmsg *ndm, static int i40e_ndo_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, struct net_device *dev, + struct net_device *filter_dev, int idx) { struct i40e_netdev_priv *np = netdev_priv(dev); struct i40e_pf *pf = np->vsi->back; if (pf->flags & I40E_FLAG_SRIOV_ENABLED) - idx = ndo_dflt_fdb_dump(skb, cb, dev, idx); + idx = ndo_dflt_fdb_dump(skb, cb, dev, filter_dev, idx); return idx; } diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index f8de2ae01a5a..0fdbcc8319f7 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -427,16 +427,17 @@ static int qlcnic_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], } static int qlcnic_fdb_dump(struct sk_buff *skb, struct netlink_callback *ncb, - struct net_device *netdev, int idx) + struct net_device *netdev, + struct net_device *filter_dev, int idx) { struct qlcnic_adapter *adapter = netdev_priv(netdev); if (!adapter->fdb_mac_learn) - return ndo_dflt_fdb_dump(skb, ncb, netdev, idx); + return ndo_dflt_fdb_dump(skb, ncb, netdev, filter_dev, idx); if ((adapter->flags & QLCNIC_ESWITCH_ENABLED) || qlcnic_sriov_check(adapter)) - idx = ndo_dflt_fdb_dump(skb, ncb, netdev, idx); + idx = ndo_dflt_fdb_dump(skb, ncb, netdev, filter_dev, idx); return idx; } diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index c2d360150804..e6808f7e4e32 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -933,7 +933,8 @@ out: /* Dump forwarding table */ static int vxlan_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, - struct net_device *dev, int idx) + struct net_device *dev, + struct net_device *filter_dev, int idx) { struct vxlan_dev *vxlan = netdev_priv(dev); unsigned int h; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 8b43a28ee0bc..3a320db96180 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -943,7 +943,8 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev, * const unsigned char *addr) * Deletes the FDB entry from dev coresponding to addr. * int (*ndo_fdb_dump)(struct sk_buff *skb, struct netlink_callback *cb, - * struct net_device *dev, int idx) + * struct net_device *dev, struct net_device *filter_dev, + * int idx) * Used to add FDB entries to dump requests. Implementers should add * entries to skb and update idx with the number of entries. * @@ -1114,6 +1115,7 @@ struct net_device_ops { int (*ndo_fdb_dump)(struct sk_buff *skb, struct netlink_callback *cb, struct net_device *dev, + struct net_device *filter_dev, int idx); int (*ndo_bridge_setlink)(struct net_device *dev, diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index 953937ea5233..167bae7bdfa4 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -78,6 +78,7 @@ extern void __rtnl_unlock(void); extern int ndo_dflt_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, struct net_device *dev, + struct net_device *filter_dev, int idx); extern int ndo_dflt_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 7be33667a839..6edecd11ecf0 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -676,6 +676,7 @@ errout: int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, struct net_device *dev, + struct net_device *filter_dev, int idx) { struct net_bridge *br = netdev_priv(dev); @@ -691,6 +692,10 @@ int br_fdb_dump(struct sk_buff *skb, if (idx < cb->args[0]) goto skip; + if (filter_dev && (!f->dst || !f->dst->dev || + f->dst->dev != filter_dev)) + goto skip; + if (fdb_fill_info(skb, br, f, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 23caf5b0309e..62a7fa2e3569 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -399,7 +399,7 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev, const unsigned char *addr, u16 nlh_flags); int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, - struct net_device *dev, int idx); + struct net_device *dev, struct net_device *fdev, int idx); int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p); void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 27acaf7ff6d7..90a906e7ac26 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2517,6 +2517,7 @@ skip: int ndo_dflt_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, struct net_device *dev, + struct net_device *filter_dev, int idx) { int err; @@ -2547,13 +2548,15 @@ static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb) br_dev = netdev_master_upper_dev_get(dev); ops = br_dev->netdev_ops; if (ops->ndo_fdb_dump) - idx = ops->ndo_fdb_dump(skb, cb, dev, idx); + idx = ops->ndo_fdb_dump(skb, cb, dev, NULL, + idx); } if (dev->netdev_ops->ndo_fdb_dump) - idx = dev->netdev_ops->ndo_fdb_dump(skb, cb, dev, idx); + idx = dev->netdev_ops->ndo_fdb_dump(skb, cb, dev, NULL, + idx); else - idx = ndo_dflt_fdb_dump(skb, cb, dev, idx); + idx = ndo_dflt_fdb_dump(skb, cb, dev, NULL, idx); } rcu_read_unlock(); -- cgit v1.2.3 From 8e2614bbf619e210674c3eae7f087db7c55ff89b Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Thu, 10 Jul 2014 14:05:11 +0200 Subject: PCI: Add include guard to include/linux/pci_ids.h Adding an include guard frees the preprocessor from reparsing over 2600 #defines in the cases where pci_ids.h is somehow included more than once. This gives a tiny-but-measurable performance improvement when compiling such files. Signed-off-by: Rasmus Villemoes Signed-off-by: Bjorn Helgaas --- include/linux/pci_ids.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 7fa31731c854..6ed0bb73a864 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -6,6 +6,8 @@ * Do not add new entries to this file unless the definitions * are shared between multiple drivers. */ +#ifndef _LINUX_PCI_IDS_H +#define _LINUX_PCI_IDS_H /* Device classes and subclasses */ @@ -2968,3 +2970,5 @@ #define PCI_DEVICE_ID_XEN_PLATFORM 0x0001 #define PCI_VENDOR_ID_OCZ 0x1b85 + +#endif /* _LINUX_PCI_IDS_H */ -- cgit v1.2.3 From c0fb262bf226a5c943e4309662353d5fb905310a Mon Sep 17 00:00:00 2001 From: Arun Kumar K Date: Fri, 11 Jul 2014 08:03:59 +0900 Subject: clk: exynos5420: Add IDs for clocks used in PD mfc Adds IDs for MUX clocks to be used by power domain for MFC for doing re-parenting while pd on/off. Signed-off-by: Arun Kumar K Signed-off-by: Shaik Ameer Basha Acked-by: Tomasz Figa Signed-off-by: Kukjin Kim --- drivers/clk/samsung/clk-exynos5420.c | 6 ++++-- include/dt-bindings/clock/exynos5420.h | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index 9d7d7eed03fd..f74f882f7f29 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -631,7 +631,8 @@ static struct samsung_mux_clock exynos5x_mux_clks[] __initdata = { SRC_TOP4, 16, 1), MUX(0, "mout_user_aclk266", mout_user_aclk266_p, SRC_TOP4, 20, 1), MUX(0, "mout_user_aclk166", mout_user_aclk166_p, SRC_TOP4, 24, 1), - MUX(0, "mout_user_aclk333", mout_user_aclk333_p, SRC_TOP4, 28, 1), + MUX(CLK_MOUT_USER_ACLK333, "mout_user_aclk333", mout_user_aclk333_p, + SRC_TOP4, 28, 1), MUX(0, "mout_user_aclk400_disp1", mout_user_aclk400_disp1_p, SRC_TOP5, 0, 1), @@ -684,7 +685,8 @@ static struct samsung_mux_clock exynos5x_mux_clks[] __initdata = { SRC_TOP11, 12, 1), MUX(0, "mout_sw_aclk266", mout_sw_aclk266_p, SRC_TOP11, 20, 1), MUX(0, "mout_sw_aclk166", mout_sw_aclk166_p, SRC_TOP11, 24, 1), - MUX(0, "mout_sw_aclk333", mout_sw_aclk333_p, SRC_TOP11, 28, 1), + MUX(CLK_MOUT_SW_ACLK333, "mout_sw_aclk333", mout_sw_aclk333_p, + SRC_TOP11, 28, 1), MUX(0, "mout_sw_aclk400_disp1", mout_sw_aclk400_disp1_p, SRC_TOP12, 4, 1), diff --git a/include/dt-bindings/clock/exynos5420.h b/include/dt-bindings/clock/exynos5420.h index 97dcb89d37d3..3fc08ff24fa9 100644 --- a/include/dt-bindings/clock/exynos5420.h +++ b/include/dt-bindings/clock/exynos5420.h @@ -203,6 +203,8 @@ #define CLK_MOUT_G3D 641 #define CLK_MOUT_VPLL 642 #define CLK_MOUT_MAUDIO0 643 +#define CLK_MOUT_USER_ACLK333 644 +#define CLK_MOUT_SW_ACLK333 645 /* divider clocks */ #define CLK_DOUT_PIXEL 768 -- cgit v1.2.3 From ed617e44234ee03a60edbf0809f696f0b9cf1b90 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 16 Jun 2014 09:16:58 -0400 Subject: tty: Document locking for tty driver methods The tty core calls the tty driver's open, close and hangup methods holding the tty lock. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- include/linux/tty_driver.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index 756a60989294..e48c608a8fa8 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -35,14 +35,14 @@ * This routine is mandatory; if this routine is not filled in, * the attempted open will fail with ENODEV. * - * Required method. - * + * Required method. Called with tty lock held. + * * void (*close)(struct tty_struct * tty, struct file * filp); * * This routine is called when a particular tty device is closed. * Note: called even if the corresponding open() failed. * - * Required method. + * Required method. Called with tty lock held. * * void (*shutdown)(struct tty_struct * tty); * @@ -172,6 +172,8 @@ * * Optional: * + * Called with tty lock held. + * * int (*break_ctl)(struct tty_struct *tty, int state); * * This optional routine requests the tty driver to turn on or -- cgit v1.2.3 From f49daa8190ebf25eae907048266b590a9cdccb95 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 11 Jul 2014 04:58:29 +0200 Subject: Bluetooth: Move HCI socket definitions into its own header file All the HCI sockets and ioctl based definitions have been in a global header file that also includes all the HCI protocol structures. To make this a bit cleaner, move them into its own file. This also adjusts fs/compat_ioctl.c to only include this new file and not all the protocol structures that are not needed. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- fs/compat_ioctl.c | 2 +- include/net/bluetooth/hci.h | 149 --------------------------------- include/net/bluetooth/hci_core.h | 1 + include/net/bluetooth/hci_sock.h | 175 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 177 insertions(+), 150 deletions(-) create mode 100644 include/net/bluetooth/hci_sock.h (limited to 'include') diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index e82289047272..afec6450450f 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -59,7 +59,7 @@ #include #include -#include +#include #include #include diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index a01236e2df13..7f754a246ca5 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -202,33 +202,6 @@ enum { #define HCI_PERSISTENT_MASK (BIT(HCI_LE_SCAN) | BIT(HCI_PERIODIC_INQ) | \ BIT(HCI_FAST_CONNECTABLE) | BIT(HCI_LE_ADV)) -/* HCI ioctl defines */ -#define HCIDEVUP _IOW('H', 201, int) -#define HCIDEVDOWN _IOW('H', 202, int) -#define HCIDEVRESET _IOW('H', 203, int) -#define HCIDEVRESTAT _IOW('H', 204, int) - -#define HCIGETDEVLIST _IOR('H', 210, int) -#define HCIGETDEVINFO _IOR('H', 211, int) -#define HCIGETCONNLIST _IOR('H', 212, int) -#define HCIGETCONNINFO _IOR('H', 213, int) -#define HCIGETAUTHINFO _IOR('H', 215, int) - -#define HCISETRAW _IOW('H', 220, int) -#define HCISETSCAN _IOW('H', 221, int) -#define HCISETAUTH _IOW('H', 222, int) -#define HCISETENCRYPT _IOW('H', 223, int) -#define HCISETPTYPE _IOW('H', 224, int) -#define HCISETLINKPOL _IOW('H', 225, int) -#define HCISETLINKMODE _IOW('H', 226, int) -#define HCISETACLMTU _IOW('H', 227, int) -#define HCISETSCOMTU _IOW('H', 228, int) - -#define HCIBLOCKADDR _IOW('H', 230, int) -#define HCIUNBLOCKADDR _IOW('H', 231, int) - -#define HCIINQUIRY _IOR('H', 240, int) - /* HCI timeouts */ #define HCI_DISCONN_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ #define HCI_PAIRING_TIMEOUT msecs_to_jiffies(60000) /* 60 seconds */ @@ -1871,126 +1844,4 @@ static inline struct hci_sco_hdr *hci_sco_hdr(const struct sk_buff *skb) #define hci_handle(h) (h & 0x0fff) #define hci_flags(h) (h >> 12) -/* ---- HCI Sockets ---- */ - -/* Socket options */ -#define HCI_DATA_DIR 1 -#define HCI_FILTER 2 -#define HCI_TIME_STAMP 3 - -/* CMSG flags */ -#define HCI_CMSG_DIR 0x0001 -#define HCI_CMSG_TSTAMP 0x0002 - -struct sockaddr_hci { - sa_family_t hci_family; - unsigned short hci_dev; - unsigned short hci_channel; -}; -#define HCI_DEV_NONE 0xffff - -#define HCI_CHANNEL_RAW 0 -#define HCI_CHANNEL_USER 1 -#define HCI_CHANNEL_MONITOR 2 -#define HCI_CHANNEL_CONTROL 3 - -struct hci_filter { - unsigned long type_mask; - unsigned long event_mask[2]; - __le16 opcode; -}; - -struct hci_ufilter { - __u32 type_mask; - __u32 event_mask[2]; - __le16 opcode; -}; - -#define HCI_FLT_TYPE_BITS 31 -#define HCI_FLT_EVENT_BITS 63 -#define HCI_FLT_OGF_BITS 63 -#define HCI_FLT_OCF_BITS 127 - -/* ---- HCI Ioctl requests structures ---- */ -struct hci_dev_stats { - __u32 err_rx; - __u32 err_tx; - __u32 cmd_tx; - __u32 evt_rx; - __u32 acl_tx; - __u32 acl_rx; - __u32 sco_tx; - __u32 sco_rx; - __u32 byte_rx; - __u32 byte_tx; -}; - -struct hci_dev_info { - __u16 dev_id; - char name[8]; - - bdaddr_t bdaddr; - - __u32 flags; - __u8 type; - - __u8 features[8]; - - __u32 pkt_type; - __u32 link_policy; - __u32 link_mode; - - __u16 acl_mtu; - __u16 acl_pkts; - __u16 sco_mtu; - __u16 sco_pkts; - - struct hci_dev_stats stat; -}; - -struct hci_conn_info { - __u16 handle; - bdaddr_t bdaddr; - __u8 type; - __u8 out; - __u16 state; - __u32 link_mode; -}; - -struct hci_dev_req { - __u16 dev_id; - __u32 dev_opt; -}; - -struct hci_dev_list_req { - __u16 dev_num; - struct hci_dev_req dev_req[0]; /* hci_dev_req structures */ -}; - -struct hci_conn_list_req { - __u16 dev_id; - __u16 conn_num; - struct hci_conn_info conn_info[0]; -}; - -struct hci_conn_info_req { - bdaddr_t bdaddr; - __u8 type; - struct hci_conn_info conn_info[0]; -}; - -struct hci_auth_info_req { - bdaddr_t bdaddr; - __u8 type; -}; - -struct hci_inquiry_req { - __u16 dev_id; - __u16 flags; - __u8 lap[3]; - __u8 length; - __u8 num_rsp; -}; -#define IREQ_CACHE_FLUSH 0x0001 - #endif /* __HCI_H */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 7e9e95633a85..5c52a17d6326 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -26,6 +26,7 @@ #define __HCI_CORE_H #include +#include /* HCI priority */ #define HCI_PRIO_MAX 7 diff --git a/include/net/bluetooth/hci_sock.h b/include/net/bluetooth/hci_sock.h new file mode 100644 index 000000000000..9a46d665c1b5 --- /dev/null +++ b/include/net/bluetooth/hci_sock.h @@ -0,0 +1,175 @@ +/* + BlueZ - Bluetooth protocol stack for Linux + Copyright (C) 2000-2001 Qualcomm Incorporated + + Written 2000,2001 by Maxim Krasnyansky + + 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; + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + 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 + 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 + SOFTWARE IS DISCLAIMED. +*/ + +#ifndef __HCI_SOCK_H +#define __HCI_SOCK_H + +/* Socket options */ +#define HCI_DATA_DIR 1 +#define HCI_FILTER 2 +#define HCI_TIME_STAMP 3 + +/* CMSG flags */ +#define HCI_CMSG_DIR 0x0001 +#define HCI_CMSG_TSTAMP 0x0002 + +struct sockaddr_hci { + sa_family_t hci_family; + unsigned short hci_dev; + unsigned short hci_channel; +}; +#define HCI_DEV_NONE 0xffff + +#define HCI_CHANNEL_RAW 0 +#define HCI_CHANNEL_USER 1 +#define HCI_CHANNEL_MONITOR 2 +#define HCI_CHANNEL_CONTROL 3 + +struct hci_filter { + unsigned long type_mask; + unsigned long event_mask[2]; + __le16 opcode; +}; + +struct hci_ufilter { + __u32 type_mask; + __u32 event_mask[2]; + __le16 opcode; +}; + +#define HCI_FLT_TYPE_BITS 31 +#define HCI_FLT_EVENT_BITS 63 +#define HCI_FLT_OGF_BITS 63 +#define HCI_FLT_OCF_BITS 127 + +/* Ioctl defines */ +#define HCIDEVUP _IOW('H', 201, int) +#define HCIDEVDOWN _IOW('H', 202, int) +#define HCIDEVRESET _IOW('H', 203, int) +#define HCIDEVRESTAT _IOW('H', 204, int) + +#define HCIGETDEVLIST _IOR('H', 210, int) +#define HCIGETDEVINFO _IOR('H', 211, int) +#define HCIGETCONNLIST _IOR('H', 212, int) +#define HCIGETCONNINFO _IOR('H', 213, int) +#define HCIGETAUTHINFO _IOR('H', 215, int) + +#define HCISETRAW _IOW('H', 220, int) +#define HCISETSCAN _IOW('H', 221, int) +#define HCISETAUTH _IOW('H', 222, int) +#define HCISETENCRYPT _IOW('H', 223, int) +#define HCISETPTYPE _IOW('H', 224, int) +#define HCISETLINKPOL _IOW('H', 225, int) +#define HCISETLINKMODE _IOW('H', 226, int) +#define HCISETACLMTU _IOW('H', 227, int) +#define HCISETSCOMTU _IOW('H', 228, int) + +#define HCIBLOCKADDR _IOW('H', 230, int) +#define HCIUNBLOCKADDR _IOW('H', 231, int) + +#define HCIINQUIRY _IOR('H', 240, int) + +/* Ioctl requests structures */ +struct hci_dev_stats { + __u32 err_rx; + __u32 err_tx; + __u32 cmd_tx; + __u32 evt_rx; + __u32 acl_tx; + __u32 acl_rx; + __u32 sco_tx; + __u32 sco_rx; + __u32 byte_rx; + __u32 byte_tx; +}; + +struct hci_dev_info { + __u16 dev_id; + char name[8]; + + bdaddr_t bdaddr; + + __u32 flags; + __u8 type; + + __u8 features[8]; + + __u32 pkt_type; + __u32 link_policy; + __u32 link_mode; + + __u16 acl_mtu; + __u16 acl_pkts; + __u16 sco_mtu; + __u16 sco_pkts; + + struct hci_dev_stats stat; +}; + +struct hci_conn_info { + __u16 handle; + bdaddr_t bdaddr; + __u8 type; + __u8 out; + __u16 state; + __u32 link_mode; +}; + +struct hci_dev_req { + __u16 dev_id; + __u32 dev_opt; +}; + +struct hci_dev_list_req { + __u16 dev_num; + struct hci_dev_req dev_req[0]; /* hci_dev_req structures */ +}; + +struct hci_conn_list_req { + __u16 dev_id; + __u16 conn_num; + struct hci_conn_info conn_info[0]; +}; + +struct hci_conn_info_req { + bdaddr_t bdaddr; + __u8 type; + struct hci_conn_info conn_info[0]; +}; + +struct hci_auth_info_req { + bdaddr_t bdaddr; + __u8 type; +}; + +struct hci_inquiry_req { + __u16 dev_id; + __u16 flags; + __u8 lap[3]; + __u8 length; + __u8 num_rsp; +}; +#define IREQ_CACHE_FLUSH 0x0001 + +#endif /* __HCI_SOCK_H */ -- cgit v1.2.3 From 3ad254f7f6fd3b66ce47a156cbb92cb02002103e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 11 Jul 2014 05:36:39 +0200 Subject: Bluetooth: Move struct hci_sec_filter next to its user There is only single location using struct hci_sec_filter and with that there is no point in putting this declaration into a global header file. So move it right next to its user and make the code a lot more simpler. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 9 --------- net/bluetooth/hci_sock.c | 8 ++++++++ 2 files changed, 8 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 5c52a17d6326..e1930749fac7 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1360,15 +1360,6 @@ struct hci_pinfo { unsigned short channel; }; -/* HCI security filter */ -#define HCI_SFLT_MAX_OGF 5 - -struct hci_sec_filter { - __u32 type_mask; - __u32 event_mask[2]; - __u32 ocf_mask[HCI_SFLT_MAX_OGF + 1][4]; -}; - /* ----- HCI requests ----- */ #define HCI_REQ_DONE 0 #define HCI_REQ_PEND 1 diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index c64728d571ae..7805fd1b4a78 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -41,6 +41,14 @@ static inline int hci_test_bit(int nr, void *addr) } /* Security filter */ +#define HCI_SFLT_MAX_OGF 5 + +struct hci_sec_filter { + __u32 type_mask; + __u32 event_mask[2]; + __u32 ocf_mask[HCI_SFLT_MAX_OGF + 1][4]; +}; + static struct hci_sec_filter hci_sec_filter = { /* Packet types */ 0x10, -- cgit v1.2.3 From 863def58fec2fa494c8e9ca45471819c6d731ec3 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 11 Jul 2014 05:41:00 +0200 Subject: Bluetooth: Move struct hci_pinfo into net/bluetooth/hci_sock.c There exists no external user of struct hci_pinfo and hci_pi and thus move it into the one place that is actually using it. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 11 ----------- net/bluetooth/hci_sock.c | 11 +++++++++++ 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index e1930749fac7..fffd0da2e0fc 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1349,17 +1349,6 @@ void mgmt_new_conn_param(struct hci_dev *hdev, bdaddr_t *bdaddr, void mgmt_reenable_advertising(struct hci_dev *hdev); void mgmt_smp_complete(struct hci_conn *conn, bool complete); -/* HCI info for socket */ -#define hci_pi(sk) ((struct hci_pinfo *) sk) - -struct hci_pinfo { - struct bt_sock bt; - struct hci_dev *hdev; - struct hci_filter filter; - __u32 cmsg_mask; - unsigned short channel; -}; - /* ----- HCI requests ----- */ #define HCI_REQ_DONE 0 #define HCI_REQ_PEND 1 diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 0753fa4970c0..115f149362ba 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -35,6 +35,17 @@ static atomic_t monitor_promisc = ATOMIC_INIT(0); /* ----- HCI socket interface ----- */ +/* Socket info */ +#define hci_pi(sk) ((struct hci_pinfo *) sk) + +struct hci_pinfo { + struct bt_sock bt; + struct hci_dev *hdev; + struct hci_filter filter; + __u32 cmsg_mask; + unsigned short channel; +}; + static inline int hci_test_bit(int nr, void *addr) { return *((__u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31)); -- cgit v1.2.3 From 899de765667b63bb51526f0a31693aed6ad5f828 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 11 Jul 2014 05:51:58 +0200 Subject: Bluetooth: Move HCI request internals to net/bluetooth/hci_core.c The internals of the HCI request framework should not be leaking to its users. Move them all into net/bluetooth/hci_core.c and provide a simple hci_req_pending helper function for the one user outside the framework. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 9 +-------- net/bluetooth/hci_core.c | 14 ++++++++++++++ net/bluetooth/hci_event.c | 2 +- 3 files changed, 16 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index fffd0da2e0fc..b52c2ef3f56d 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1240,6 +1240,7 @@ void hci_req_add(struct hci_request *req, u16 opcode, u32 plen, void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen, const void *param, u8 event); void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status); +bool hci_req_pending(struct hci_dev *hdev); void hci_req_add_le_scan_disable(struct hci_request *req); void hci_req_add_le_passive_scan(struct hci_request *req); @@ -1349,14 +1350,6 @@ void mgmt_new_conn_param(struct hci_dev *hdev, bdaddr_t *bdaddr, void mgmt_reenable_advertising(struct hci_dev *hdev); void mgmt_smp_complete(struct hci_conn *conn, bool complete); -/* ----- HCI requests ----- */ -#define HCI_REQ_DONE 0 -#define HCI_REQ_PEND 1 -#define HCI_REQ_CANCELED 2 - -#define hci_req_lock(d) mutex_lock(&d->req_lock) -#define hci_req_unlock(d) mutex_unlock(&d->req_lock) - u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, u16 to_multiplier); void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand, diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 52e8c91ea3e9..347f84fb66f9 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -54,6 +54,15 @@ DEFINE_RWLOCK(hci_cb_list_lock); /* HCI ID Numbering */ static DEFINE_IDA(hci_index_ida); +/* ----- HCI requests ----- */ + +#define HCI_REQ_DONE 0 +#define HCI_REQ_PEND 1 +#define HCI_REQ_CANCELED 2 + +#define hci_req_lock(d) mutex_lock(&d->req_lock) +#define hci_req_unlock(d) mutex_unlock(&d->req_lock) + /* ---- HCI notifications ---- */ static void hci_notify(struct hci_dev *hdev, int event) @@ -4432,6 +4441,11 @@ int hci_req_run(struct hci_request *req, hci_req_complete_t complete) return 0; } +bool hci_req_pending(struct hci_dev *hdev) +{ + return (hdev->req_status == HCI_REQ_PEND); +} + static struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen, const void *param) { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index a62e918d2641..f0f220057f21 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4624,7 +4624,7 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) /* Received events are (currently) only needed when a request is * ongoing so avoid unnecessary memory allocation. */ - if (hdev->req_status == HCI_REQ_PEND) { + if (hci_req_pending(hdev)) { kfree_skb(hdev->recv_evt); hdev->recv_evt = skb_clone(skb, GFP_KERNEL); } -- cgit v1.2.3 From a6801ca9856c24298cba2c1fafd3da818acdaab6 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 11 Jul 2014 06:03:08 +0200 Subject: Bluetooth: Update the list of L2CAP fixed channels The list of L2CAP fixed channels increased with newer versions of the specification. This just updates the constants for it. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/l2cap.h | 5 ++++- net/bluetooth/l2cap_core.c | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index e0c6a9abdb62..d8e7b9330ab8 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -134,9 +134,12 @@ struct l2cap_conninfo { #define L2CAP_FCS_CRC16 0x01 /* L2CAP fixed channels */ -#define L2CAP_FC_L2CAP 0x02 +#define L2CAP_FC_SIG_BREDR 0x02 #define L2CAP_FC_CONNLESS 0x04 #define L2CAP_FC_A2MP 0x08 +#define L2CAP_FC_ATT 0x10 +#define L2CAP_FC_SIG_LE 0x20 +#define L2CAP_FC_SMP_LE 0x40 /* L2CAP Control Field bit masks */ #define L2CAP_CTRL_SAR 0xC000 diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 8680aae678ce..0c3c4935f635 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -46,7 +46,7 @@ bool disable_ertm; static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN | L2CAP_FEAT_UCD; -static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP | L2CAP_FC_CONNLESS, }; +static u8 l2cap_fixed_chan[8] = { L2CAP_FC_SIG_BREDR | L2CAP_FC_CONNLESS, }; static LIST_HEAD(chan_list); static DEFINE_RWLOCK(chan_list_lock); -- cgit v1.2.3 From 2a0dccb3df2c8775d1a6e962aa88c4c22693c887 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 11 Jul 2014 06:19:41 +0200 Subject: Bluetooth: Move struct sco_pinfo into net/bluetooth/sco.c There exists no external user of struct sco_pinfo and sco_pi and thus move it into the one place that is actually using it. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/sco.h | 12 ------------ net/bluetooth/sco.c | 12 ++++++++++++ 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/sco.h b/include/net/bluetooth/sco.h index 2019d1a0996a..7fec7cbd7396 100644 --- a/include/net/bluetooth/sco.h +++ b/include/net/bluetooth/sco.h @@ -64,16 +64,4 @@ struct sco_conn { #define sco_conn_lock(c) spin_lock(&c->lock); #define sco_conn_unlock(c) spin_unlock(&c->lock); -/* ----- SCO socket info ----- */ -#define sco_pi(sk) ((struct sco_pinfo *) sk) - -struct sco_pinfo { - struct bt_sock bt; - bdaddr_t src; - bdaddr_t dst; - __u32 flags; - __u16 setting; - struct sco_conn *conn; -}; - #endif /* __SCO_H */ diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index c06dbd3938e8..c71698642ec6 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -46,6 +46,18 @@ static void sco_chan_del(struct sock *sk, int err); static void sco_sock_close(struct sock *sk); static void sco_sock_kill(struct sock *sk); +/* ----- SCO socket info ----- */ +#define sco_pi(sk) ((struct sco_pinfo *) sk) + +struct sco_pinfo { + struct bt_sock bt; + bdaddr_t src; + bdaddr_t dst; + __u32 flags; + __u16 setting; + struct sco_conn *conn; +}; + /* ---- SCO timers ---- */ static void sco_sock_timeout(unsigned long arg) { -- cgit v1.2.3 From fc8f525a6f7c81743bec6b5d497988313b211383 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 11 Jul 2014 06:19:42 +0200 Subject: Bluetooth: Move struct sco_conn into net/bluetooth/sco.c There exists no external user of struct sco_conn and thus move it into the one place that is actually using it. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/sco.h | 13 ------------- net/bluetooth/sco.c | 13 +++++++++++++ 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/sco.h b/include/net/bluetooth/sco.h index 7fec7cbd7396..42a0d089d715 100644 --- a/include/net/bluetooth/sco.h +++ b/include/net/bluetooth/sco.h @@ -51,17 +51,4 @@ struct sco_conninfo { __u8 dev_class[3]; }; -/* ---- SCO connections ---- */ -struct sco_conn { - struct hci_conn *hcon; - - spinlock_t lock; - struct sock *sk; - - unsigned int mtu; -}; - -#define sco_conn_lock(c) spin_lock(&c->lock); -#define sco_conn_unlock(c) spin_unlock(&c->lock); - #endif /* __SCO_H */ diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index c71698642ec6..9868ec7c7dc4 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -40,6 +40,19 @@ static struct bt_sock_list sco_sk_list = { .lock = __RW_LOCK_UNLOCKED(sco_sk_list.lock) }; +/* ---- SCO connections ---- */ +struct sco_conn { + struct hci_conn *hcon; + + spinlock_t lock; + struct sock *sk; + + unsigned int mtu; +}; + +#define sco_conn_lock(c) spin_lock(&c->lock); +#define sco_conn_unlock(c) spin_unlock(&c->lock); + static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent); static void sco_chan_del(struct sock *sk, int err); -- cgit v1.2.3 From 6190ae7a184bf488aeb051a5713aa57b07d4c59b Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 11 Jul 2014 06:19:43 +0200 Subject: Bluetooth: Remove unused SCO_DEFAULT_FLUSH_TO constant The SCO_DEFAULT_FLUSH_TO constant has been defined, but it is not used anywhere and so just remove it. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/sco.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/sco.h b/include/net/bluetooth/sco.h index 42a0d089d715..ab8b1ed550d2 100644 --- a/include/net/bluetooth/sco.h +++ b/include/net/bluetooth/sco.h @@ -27,7 +27,6 @@ /* SCO defaults */ #define SCO_DEFAULT_MTU 500 -#define SCO_DEFAULT_FLUSH_TO 0xFFFF #define SCO_CONN_TIMEOUT (HZ * 40) #define SCO_DISCONN_TIMEOUT (HZ * 2) -- cgit v1.2.3 From 068d69e5bbd85291aa8ad0d81062f047609a173c Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 11 Jul 2014 06:19:44 +0200 Subject: Bluetooth: Move SCO timeout constants into net/bluetooth/sco.c There is no external user of the SCO timeout constants and thus move them into net/bluetooth/sco.c where they are actuallu used. In addition just remove SCO_CONN_IDLE_TIMEOUT since it is unused. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/sco.h | 4 ---- net/bluetooth/sco.c | 3 +++ 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/sco.h b/include/net/bluetooth/sco.h index ab8b1ed550d2..f40ddb4264fc 100644 --- a/include/net/bluetooth/sco.h +++ b/include/net/bluetooth/sco.h @@ -28,10 +28,6 @@ /* SCO defaults */ #define SCO_DEFAULT_MTU 500 -#define SCO_CONN_TIMEOUT (HZ * 40) -#define SCO_DISCONN_TIMEOUT (HZ * 2) -#define SCO_CONN_IDLE_TIMEOUT (HZ * 60) - /* SCO socket address */ struct sockaddr_sco { sa_family_t sco_family; diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 9868ec7c7dc4..e6c7b636b901 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -72,6 +72,9 @@ struct sco_pinfo { }; /* ---- SCO timers ---- */ +#define SCO_CONN_TIMEOUT (HZ * 40) +#define SCO_DISCONN_TIMEOUT (HZ * 2) + static void sco_sock_timeout(unsigned long arg) { struct sock *sk = (struct sock *) arg; -- cgit v1.2.3 From 1df08ba0aa95f1a8832b7162eec51069bd9be7ae Mon Sep 17 00:00:00 2001 From: Alex Bennée Date: Fri, 4 Jul 2014 15:54:14 +0100 Subject: arm64: KVM: allow export and import of generic timer regs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For correct guest suspend/resume behaviour we need to ensure we include the generic timer registers for 64 bit guests. As CONFIG_KVM_ARM_TIMER is always set for arm64 we don't need to worry about null implementations. However I have re-jigged the kvm_arm_timer_set/get_reg declarations to be in the common include/kvm/arm_arch_timer.h headers. Acked-by: Marc Zyngier Reviewed-by: Christoffer Dall Signed-off-by: Alex Bennée Signed-off-by: Christoffer Dall --- arch/arm/include/asm/kvm_host.h | 3 -- arch/arm/kvm/guest.c | 10 ------ arch/arm64/kvm/guest.c | 68 ++++++++++++++++++++++++++++++++++++++++- include/kvm/arm_arch_timer.h | 14 +++++++++ 4 files changed, 81 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h index 193ceaf01bfd..dc4e3edf39cc 100644 --- a/arch/arm/include/asm/kvm_host.h +++ b/arch/arm/include/asm/kvm_host.h @@ -228,7 +228,4 @@ static inline int kvm_arch_dev_ioctl_check_extension(long ext) int kvm_perf_init(void); int kvm_perf_teardown(void); -u64 kvm_arm_timer_get_reg(struct kvm_vcpu *, u64 regid); -int kvm_arm_timer_set_reg(struct kvm_vcpu *, u64 regid, u64 value); - #endif /* __ARM_KVM_HOST_H__ */ diff --git a/arch/arm/kvm/guest.c b/arch/arm/kvm/guest.c index b23a59c1c522..986e625b5dbd 100644 --- a/arch/arm/kvm/guest.c +++ b/arch/arm/kvm/guest.c @@ -124,16 +124,6 @@ static bool is_timer_reg(u64 index) return false; } -int kvm_arm_timer_set_reg(struct kvm_vcpu *vcpu, u64 regid, u64 value) -{ - return 0; -} - -u64 kvm_arm_timer_get_reg(struct kvm_vcpu *vcpu, u64 regid) -{ - return 0; -} - #else #define NUM_TIMER_REGS 3 diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c index 60b5c31f3c10..8d1ec2887a26 100644 --- a/arch/arm64/kvm/guest.c +++ b/arch/arm64/kvm/guest.c @@ -135,6 +135,59 @@ static unsigned long num_core_regs(void) return sizeof(struct kvm_regs) / sizeof(__u32); } +/** + * ARM64 versions of the TIMER registers, always available on arm64 + */ + +#define NUM_TIMER_REGS 3 + +static bool is_timer_reg(u64 index) +{ + switch (index) { + case KVM_REG_ARM_TIMER_CTL: + case KVM_REG_ARM_TIMER_CNT: + case KVM_REG_ARM_TIMER_CVAL: + return true; + } + return false; +} + +static int copy_timer_indices(struct kvm_vcpu *vcpu, u64 __user *uindices) +{ + if (put_user(KVM_REG_ARM_TIMER_CTL, uindices)) + return -EFAULT; + uindices++; + if (put_user(KVM_REG_ARM_TIMER_CNT, uindices)) + return -EFAULT; + uindices++; + if (put_user(KVM_REG_ARM_TIMER_CVAL, uindices)) + return -EFAULT; + + return 0; +} + +static int set_timer_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) +{ + void __user *uaddr = (void __user *)(long)reg->addr; + u64 val; + int ret; + + ret = copy_from_user(&val, uaddr, KVM_REG_SIZE(reg->id)); + if (ret != 0) + return ret; + + return kvm_arm_timer_set_reg(vcpu, reg->id, val); +} + +static int get_timer_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) +{ + void __user *uaddr = (void __user *)(long)reg->addr; + u64 val; + + val = kvm_arm_timer_get_reg(vcpu, reg->id); + return copy_to_user(uaddr, &val, KVM_REG_SIZE(reg->id)); +} + /** * kvm_arm_num_regs - how many registers do we present via KVM_GET_ONE_REG * @@ -142,7 +195,8 @@ static unsigned long num_core_regs(void) */ unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu) { - return num_core_regs() + kvm_arm_num_sys_reg_descs(vcpu); + return num_core_regs() + kvm_arm_num_sys_reg_descs(vcpu) + + NUM_TIMER_REGS; } /** @@ -154,6 +208,7 @@ int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices) { unsigned int i; const u64 core_reg = KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE; + int ret; for (i = 0; i < sizeof(struct kvm_regs) / sizeof(__u32); i++) { if (put_user(core_reg | i, uindices)) @@ -161,6 +216,11 @@ int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices) uindices++; } + ret = copy_timer_indices(vcpu, uindices); + if (ret) + return ret; + uindices += NUM_TIMER_REGS; + return kvm_arm_copy_sys_reg_indices(vcpu, uindices); } @@ -174,6 +234,9 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE) return get_core_reg(vcpu, reg); + if (is_timer_reg(reg->id)) + return get_timer_reg(vcpu, reg); + return kvm_arm_sys_reg_get_reg(vcpu, reg); } @@ -187,6 +250,9 @@ int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE) return set_core_reg(vcpu, reg); + if (is_timer_reg(reg->id)) + return set_timer_reg(vcpu, reg); + return kvm_arm_sys_reg_set_reg(vcpu, reg); } diff --git a/include/kvm/arm_arch_timer.h b/include/kvm/arm_arch_timer.h index 6d9aeddc09bf..ad9db6045b2f 100644 --- a/include/kvm/arm_arch_timer.h +++ b/include/kvm/arm_arch_timer.h @@ -67,6 +67,10 @@ void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu); void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu); void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu); void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu); + +u64 kvm_arm_timer_get_reg(struct kvm_vcpu *, u64 regid); +int kvm_arm_timer_set_reg(struct kvm_vcpu *, u64 regid, u64 value); + #else static inline int kvm_timer_hyp_init(void) { @@ -84,6 +88,16 @@ static inline void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu) {} static inline void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu) {} static inline void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu) {} static inline void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu) {} + +static inline int kvm_arm_timer_set_reg(struct kvm_vcpu *vcpu, u64 regid, u64 value) +{ + return 0; +} + +static inline u64 kvm_arm_timer_get_reg(struct kvm_vcpu *vcpu, u64 regid) +{ + return 0; +} #endif #endif -- cgit v1.2.3 From eede821dbfd58df89edb072da64e006321eaef58 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Thu, 30 May 2013 10:20:36 +0100 Subject: KVM: arm/arm64: vgic: move GICv2 registers to their own structure In order to make way for the GICv3 registers, move the v2-specific registers to their own structure. Acked-by: Catalin Marinas Reviewed-by: Christoffer Dall Signed-off-by: Marc Zyngier --- arch/arm/kernel/asm-offsets.c | 14 +++++------ arch/arm/kvm/interrupts_head.S | 26 +++++++++---------- arch/arm64/kernel/asm-offsets.c | 14 +++++------ arch/arm64/kvm/hyp.S | 26 +++++++++---------- include/kvm/arm_vgic.h | 20 +++++++++------ virt/kvm/arm/vgic.c | 56 ++++++++++++++++++++--------------------- 6 files changed, 81 insertions(+), 75 deletions(-) (limited to 'include') diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c index 85598b5d1efd..713e807621d2 100644 --- a/arch/arm/kernel/asm-offsets.c +++ b/arch/arm/kernel/asm-offsets.c @@ -182,13 +182,13 @@ int main(void) DEFINE(VCPU_HYP_PC, offsetof(struct kvm_vcpu, arch.fault.hyp_pc)); #ifdef CONFIG_KVM_ARM_VGIC DEFINE(VCPU_VGIC_CPU, offsetof(struct kvm_vcpu, arch.vgic_cpu)); - DEFINE(VGIC_CPU_HCR, offsetof(struct vgic_cpu, vgic_hcr)); - DEFINE(VGIC_CPU_VMCR, offsetof(struct vgic_cpu, vgic_vmcr)); - DEFINE(VGIC_CPU_MISR, offsetof(struct vgic_cpu, vgic_misr)); - DEFINE(VGIC_CPU_EISR, offsetof(struct vgic_cpu, vgic_eisr)); - DEFINE(VGIC_CPU_ELRSR, offsetof(struct vgic_cpu, vgic_elrsr)); - DEFINE(VGIC_CPU_APR, offsetof(struct vgic_cpu, vgic_apr)); - DEFINE(VGIC_CPU_LR, offsetof(struct vgic_cpu, vgic_lr)); + DEFINE(VGIC_V2_CPU_HCR, offsetof(struct vgic_cpu, vgic_v2.vgic_hcr)); + DEFINE(VGIC_V2_CPU_VMCR, offsetof(struct vgic_cpu, vgic_v2.vgic_vmcr)); + DEFINE(VGIC_V2_CPU_MISR, offsetof(struct vgic_cpu, vgic_v2.vgic_misr)); + DEFINE(VGIC_V2_CPU_EISR, offsetof(struct vgic_cpu, vgic_v2.vgic_eisr)); + DEFINE(VGIC_V2_CPU_ELRSR, offsetof(struct vgic_cpu, vgic_v2.vgic_elrsr)); + DEFINE(VGIC_V2_CPU_APR, offsetof(struct vgic_cpu, vgic_v2.vgic_apr)); + DEFINE(VGIC_V2_CPU_LR, offsetof(struct vgic_cpu, vgic_v2.vgic_lr)); DEFINE(VGIC_CPU_NR_LR, offsetof(struct vgic_cpu, nr_lr)); #ifdef CONFIG_KVM_ARM_TIMER DEFINE(VCPU_TIMER_CNTV_CTL, offsetof(struct kvm_vcpu, arch.timer_cpu.cntv_ctl)); diff --git a/arch/arm/kvm/interrupts_head.S b/arch/arm/kvm/interrupts_head.S index 76af93025574..e4eaf30205c5 100644 --- a/arch/arm/kvm/interrupts_head.S +++ b/arch/arm/kvm/interrupts_head.S @@ -421,14 +421,14 @@ vcpu .req r0 @ vcpu pointer always in r0 ldr r9, [r2, #GICH_ELRSR1] ldr r10, [r2, #GICH_APR] - str r3, [r11, #VGIC_CPU_HCR] - str r4, [r11, #VGIC_CPU_VMCR] - str r5, [r11, #VGIC_CPU_MISR] - str r6, [r11, #VGIC_CPU_EISR] - str r7, [r11, #(VGIC_CPU_EISR + 4)] - str r8, [r11, #VGIC_CPU_ELRSR] - str r9, [r11, #(VGIC_CPU_ELRSR + 4)] - str r10, [r11, #VGIC_CPU_APR] + str r3, [r11, #VGIC_V2_CPU_HCR] + str r4, [r11, #VGIC_V2_CPU_VMCR] + str r5, [r11, #VGIC_V2_CPU_MISR] + str r6, [r11, #VGIC_V2_CPU_EISR] + str r7, [r11, #(VGIC_V2_CPU_EISR + 4)] + str r8, [r11, #VGIC_V2_CPU_ELRSR] + str r9, [r11, #(VGIC_V2_CPU_ELRSR + 4)] + str r10, [r11, #VGIC_V2_CPU_APR] /* Clear GICH_HCR */ mov r5, #0 @@ -436,7 +436,7 @@ vcpu .req r0 @ vcpu pointer always in r0 /* Save list registers */ add r2, r2, #GICH_LR0 - add r3, r11, #VGIC_CPU_LR + add r3, r11, #VGIC_V2_CPU_LR ldr r4, [r11, #VGIC_CPU_NR_LR] 1: ldr r6, [r2], #4 str r6, [r3], #4 @@ -463,9 +463,9 @@ vcpu .req r0 @ vcpu pointer always in r0 add r11, vcpu, #VCPU_VGIC_CPU /* We only restore a minimal set of registers */ - ldr r3, [r11, #VGIC_CPU_HCR] - ldr r4, [r11, #VGIC_CPU_VMCR] - ldr r8, [r11, #VGIC_CPU_APR] + ldr r3, [r11, #VGIC_V2_CPU_HCR] + ldr r4, [r11, #VGIC_V2_CPU_VMCR] + ldr r8, [r11, #VGIC_V2_CPU_APR] str r3, [r2, #GICH_HCR] str r4, [r2, #GICH_VMCR] @@ -473,7 +473,7 @@ vcpu .req r0 @ vcpu pointer always in r0 /* Restore list registers */ add r2, r2, #GICH_LR0 - add r3, r11, #VGIC_CPU_LR + add r3, r11, #VGIC_V2_CPU_LR ldr r4, [r11, #VGIC_CPU_NR_LR] 1: ldr r6, [r3], #4 str r6, [r2], #4 diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c index 646f888387cd..20fd4887aab6 100644 --- a/arch/arm64/kernel/asm-offsets.c +++ b/arch/arm64/kernel/asm-offsets.c @@ -129,13 +129,13 @@ int main(void) DEFINE(KVM_TIMER_ENABLED, offsetof(struct kvm, arch.timer.enabled)); DEFINE(VCPU_KVM, offsetof(struct kvm_vcpu, kvm)); DEFINE(VCPU_VGIC_CPU, offsetof(struct kvm_vcpu, arch.vgic_cpu)); - DEFINE(VGIC_CPU_HCR, offsetof(struct vgic_cpu, vgic_hcr)); - DEFINE(VGIC_CPU_VMCR, offsetof(struct vgic_cpu, vgic_vmcr)); - DEFINE(VGIC_CPU_MISR, offsetof(struct vgic_cpu, vgic_misr)); - DEFINE(VGIC_CPU_EISR, offsetof(struct vgic_cpu, vgic_eisr)); - DEFINE(VGIC_CPU_ELRSR, offsetof(struct vgic_cpu, vgic_elrsr)); - DEFINE(VGIC_CPU_APR, offsetof(struct vgic_cpu, vgic_apr)); - DEFINE(VGIC_CPU_LR, offsetof(struct vgic_cpu, vgic_lr)); + DEFINE(VGIC_V2_CPU_HCR, offsetof(struct vgic_cpu, vgic_v2.vgic_hcr)); + DEFINE(VGIC_V2_CPU_VMCR, offsetof(struct vgic_cpu, vgic_v2.vgic_vmcr)); + DEFINE(VGIC_V2_CPU_MISR, offsetof(struct vgic_cpu, vgic_v2.vgic_misr)); + DEFINE(VGIC_V2_CPU_EISR, offsetof(struct vgic_cpu, vgic_v2.vgic_eisr)); + DEFINE(VGIC_V2_CPU_ELRSR, offsetof(struct vgic_cpu, vgic_v2.vgic_elrsr)); + DEFINE(VGIC_V2_CPU_APR, offsetof(struct vgic_cpu, vgic_v2.vgic_apr)); + DEFINE(VGIC_V2_CPU_LR, offsetof(struct vgic_cpu, vgic_v2.vgic_lr)); DEFINE(VGIC_CPU_NR_LR, offsetof(struct vgic_cpu, nr_lr)); DEFINE(KVM_VTTBR, offsetof(struct kvm, arch.vttbr)); DEFINE(KVM_VGIC_VCTRL, offsetof(struct kvm, arch.vgic.vctrl_base)); diff --git a/arch/arm64/kvm/hyp.S b/arch/arm64/kvm/hyp.S index b0d1512acf08..877d82a134bc 100644 --- a/arch/arm64/kvm/hyp.S +++ b/arch/arm64/kvm/hyp.S @@ -412,14 +412,14 @@ CPU_BE( rev w9, w9 ) CPU_BE( rev w10, w10 ) CPU_BE( rev w11, w11 ) - str w4, [x3, #VGIC_CPU_HCR] - str w5, [x3, #VGIC_CPU_VMCR] - str w6, [x3, #VGIC_CPU_MISR] - str w7, [x3, #VGIC_CPU_EISR] - str w8, [x3, #(VGIC_CPU_EISR + 4)] - str w9, [x3, #VGIC_CPU_ELRSR] - str w10, [x3, #(VGIC_CPU_ELRSR + 4)] - str w11, [x3, #VGIC_CPU_APR] + str w4, [x3, #VGIC_V2_CPU_HCR] + str w5, [x3, #VGIC_V2_CPU_VMCR] + str w6, [x3, #VGIC_V2_CPU_MISR] + str w7, [x3, #VGIC_V2_CPU_EISR] + str w8, [x3, #(VGIC_V2_CPU_EISR + 4)] + str w9, [x3, #VGIC_V2_CPU_ELRSR] + str w10, [x3, #(VGIC_V2_CPU_ELRSR + 4)] + str w11, [x3, #VGIC_V2_CPU_APR] /* Clear GICH_HCR */ str wzr, [x2, #GICH_HCR] @@ -427,7 +427,7 @@ CPU_BE( rev w11, w11 ) /* Save list registers */ add x2, x2, #GICH_LR0 ldr w4, [x3, #VGIC_CPU_NR_LR] - add x3, x3, #VGIC_CPU_LR + add x3, x3, #VGIC_V2_CPU_LR 1: ldr w5, [x2], #4 CPU_BE( rev w5, w5 ) str w5, [x3], #4 @@ -452,9 +452,9 @@ CPU_BE( rev w5, w5 ) add x3, x0, #VCPU_VGIC_CPU /* We only restore a minimal set of registers */ - ldr w4, [x3, #VGIC_CPU_HCR] - ldr w5, [x3, #VGIC_CPU_VMCR] - ldr w6, [x3, #VGIC_CPU_APR] + ldr w4, [x3, #VGIC_V2_CPU_HCR] + ldr w5, [x3, #VGIC_V2_CPU_VMCR] + ldr w6, [x3, #VGIC_V2_CPU_APR] CPU_BE( rev w4, w4 ) CPU_BE( rev w5, w5 ) CPU_BE( rev w6, w6 ) @@ -466,7 +466,7 @@ CPU_BE( rev w6, w6 ) /* Restore list registers */ add x2, x2, #GICH_LR0 ldr w4, [x3, #VGIC_CPU_NR_LR] - add x3, x3, #VGIC_CPU_LR + add x3, x3, #VGIC_V2_CPU_LR 1: ldr w5, [x3], #4 CPU_BE( rev w5, w5 ) str w5, [x2], #4 diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index f27000f55a83..f738e5a69ee9 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -110,6 +110,16 @@ struct vgic_dist { #endif }; +struct vgic_v2_cpu_if { + u32 vgic_hcr; + u32 vgic_vmcr; + u32 vgic_misr; /* Saved only */ + u32 vgic_eisr[2]; /* Saved only */ + u32 vgic_elrsr[2]; /* Saved only */ + u32 vgic_apr; + u32 vgic_lr[VGIC_MAX_LRS]; +}; + struct vgic_cpu { #ifdef CONFIG_KVM_ARM_VGIC /* per IRQ to LR mapping */ @@ -126,13 +136,9 @@ struct vgic_cpu { int nr_lr; /* CPU vif control registers for world switch */ - u32 vgic_hcr; - u32 vgic_vmcr; - u32 vgic_misr; /* Saved only */ - u32 vgic_eisr[2]; /* Saved only */ - u32 vgic_elrsr[2]; /* Saved only */ - u32 vgic_apr; - u32 vgic_lr[VGIC_MAX_LRS]; + union { + struct vgic_v2_cpu_if vgic_v2; + }; #endif }; diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c index 56ff9bebb577..0ba1ab0721fd 100644 --- a/virt/kvm/arm/vgic.c +++ b/virt/kvm/arm/vgic.c @@ -601,7 +601,7 @@ static bool handle_mmio_sgi_reg(struct kvm_vcpu *vcpu, static void vgic_retire_lr(int lr_nr, int irq, struct vgic_cpu *vgic_cpu) { clear_bit(lr_nr, vgic_cpu->lr_used); - vgic_cpu->vgic_lr[lr_nr] &= ~GICH_LR_STATE; + vgic_cpu->vgic_v2.vgic_lr[lr_nr] &= ~GICH_LR_STATE; vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY; } @@ -626,7 +626,7 @@ static void vgic_unqueue_irqs(struct kvm_vcpu *vcpu) u32 *lr; for_each_set_bit(i, vgic_cpu->lr_used, vgic_cpu->nr_lr) { - lr = &vgic_cpu->vgic_lr[i]; + lr = &vgic_cpu->vgic_v2.vgic_lr[i]; irq = LR_IRQID(*lr); source_cpu = LR_CPUID(*lr); @@ -1007,7 +1007,7 @@ static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu) int lr; for_each_set_bit(lr, vgic_cpu->lr_used, vgic_cpu->nr_lr) { - int irq = vgic_cpu->vgic_lr[lr] & GICH_LR_VIRTUALID; + int irq = vgic_cpu->vgic_v2.vgic_lr[lr] & GICH_LR_VIRTUALID; if (!vgic_irq_is_enabled(vcpu, irq)) { vgic_retire_lr(lr, irq, vgic_cpu); @@ -1037,11 +1037,11 @@ static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq) /* Do we have an active interrupt for the same CPUID? */ if (lr != LR_EMPTY && - (LR_CPUID(vgic_cpu->vgic_lr[lr]) == sgi_source_id)) { + (LR_CPUID(vgic_cpu->vgic_v2.vgic_lr[lr]) == sgi_source_id)) { kvm_debug("LR%d piggyback for IRQ%d %x\n", - lr, irq, vgic_cpu->vgic_lr[lr]); + lr, irq, vgic_cpu->vgic_v2.vgic_lr[lr]); BUG_ON(!test_bit(lr, vgic_cpu->lr_used)); - vgic_cpu->vgic_lr[lr] |= GICH_LR_PENDING_BIT; + vgic_cpu->vgic_v2.vgic_lr[lr] |= GICH_LR_PENDING_BIT; return true; } @@ -1052,12 +1052,12 @@ static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq) return false; kvm_debug("LR%d allocated for IRQ%d %x\n", lr, irq, sgi_source_id); - vgic_cpu->vgic_lr[lr] = MK_LR_PEND(sgi_source_id, irq); + vgic_cpu->vgic_v2.vgic_lr[lr] = MK_LR_PEND(sgi_source_id, irq); vgic_cpu->vgic_irq_lr_map[irq] = lr; set_bit(lr, vgic_cpu->lr_used); if (!vgic_irq_is_edge(vcpu, irq)) - vgic_cpu->vgic_lr[lr] |= GICH_LR_EOI; + vgic_cpu->vgic_v2.vgic_lr[lr] |= GICH_LR_EOI; return true; } @@ -1155,9 +1155,9 @@ static void __kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu) epilog: if (overflow) { - vgic_cpu->vgic_hcr |= GICH_HCR_UIE; + vgic_cpu->vgic_v2.vgic_hcr |= GICH_HCR_UIE; } else { - vgic_cpu->vgic_hcr &= ~GICH_HCR_UIE; + vgic_cpu->vgic_v2.vgic_hcr &= ~GICH_HCR_UIE; /* * We're about to run this VCPU, and we've consumed * everything the distributor had in store for @@ -1173,21 +1173,21 @@ static bool vgic_process_maintenance(struct kvm_vcpu *vcpu) struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; bool level_pending = false; - kvm_debug("MISR = %08x\n", vgic_cpu->vgic_misr); + kvm_debug("MISR = %08x\n", vgic_cpu->vgic_v2.vgic_misr); - if (vgic_cpu->vgic_misr & GICH_MISR_EOI) { + if (vgic_cpu->vgic_v2.vgic_misr & GICH_MISR_EOI) { /* * Some level interrupts have been EOIed. Clear their * active bit. */ int lr, irq; - for_each_set_bit(lr, (unsigned long *)vgic_cpu->vgic_eisr, + for_each_set_bit(lr, (unsigned long *)vgic_cpu->vgic_v2.vgic_eisr, vgic_cpu->nr_lr) { - irq = vgic_cpu->vgic_lr[lr] & GICH_LR_VIRTUALID; + irq = vgic_cpu->vgic_v2.vgic_lr[lr] & GICH_LR_VIRTUALID; vgic_irq_clear_active(vcpu, irq); - vgic_cpu->vgic_lr[lr] &= ~GICH_LR_EOI; + vgic_cpu->vgic_v2.vgic_lr[lr] &= ~GICH_LR_EOI; /* Any additional pending interrupt? */ if (vgic_dist_irq_is_pending(vcpu, irq)) { @@ -1201,13 +1201,13 @@ static bool vgic_process_maintenance(struct kvm_vcpu *vcpu) * Despite being EOIed, the LR may not have * been marked as empty. */ - set_bit(lr, (unsigned long *)vgic_cpu->vgic_elrsr); - vgic_cpu->vgic_lr[lr] &= ~GICH_LR_ACTIVE_BIT; + set_bit(lr, (unsigned long *)vgic_cpu->vgic_v2.vgic_elrsr); + vgic_cpu->vgic_v2.vgic_lr[lr] &= ~GICH_LR_ACTIVE_BIT; } } - if (vgic_cpu->vgic_misr & GICH_MISR_U) - vgic_cpu->vgic_hcr &= ~GICH_HCR_UIE; + if (vgic_cpu->vgic_v2.vgic_misr & GICH_MISR_U) + vgic_cpu->vgic_v2.vgic_hcr &= ~GICH_HCR_UIE; return level_pending; } @@ -1226,21 +1226,21 @@ static void __kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu) level_pending = vgic_process_maintenance(vcpu); /* Clear mappings for empty LRs */ - for_each_set_bit(lr, (unsigned long *)vgic_cpu->vgic_elrsr, + for_each_set_bit(lr, (unsigned long *)vgic_cpu->vgic_v2.vgic_elrsr, vgic_cpu->nr_lr) { int irq; if (!test_and_clear_bit(lr, vgic_cpu->lr_used)) continue; - irq = vgic_cpu->vgic_lr[lr] & GICH_LR_VIRTUALID; + irq = vgic_cpu->vgic_v2.vgic_lr[lr] & GICH_LR_VIRTUALID; BUG_ON(irq >= VGIC_NR_IRQS); vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY; } /* Check if we still have something up our sleeve... */ - pending = find_first_zero_bit((unsigned long *)vgic_cpu->vgic_elrsr, + pending = find_first_zero_bit((unsigned long *)vgic_cpu->vgic_v2.vgic_elrsr, vgic_cpu->nr_lr); if (level_pending || pending < vgic_cpu->nr_lr) set_bit(vcpu->vcpu_id, &dist->irq_pending_on_cpu); @@ -1436,10 +1436,10 @@ int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu) * points to their reset values. Anything else resets to zero * anyway. */ - vgic_cpu->vgic_vmcr = 0; + vgic_cpu->vgic_v2.vgic_vmcr = 0; vgic_cpu->nr_lr = vgic_nr_lr; - vgic_cpu->vgic_hcr = GICH_HCR_EN; /* Get the show on the road... */ + vgic_cpu->vgic_v2.vgic_hcr = GICH_HCR_EN; /* Get the show on the road... */ return 0; } @@ -1746,15 +1746,15 @@ static bool handle_cpu_mmio_misc(struct kvm_vcpu *vcpu, } if (!mmio->is_write) { - reg = (vgic_cpu->vgic_vmcr & mask) >> shift; + reg = (vgic_cpu->vgic_v2.vgic_vmcr & mask) >> shift; mmio_data_write(mmio, ~0, reg); } else { reg = mmio_data_read(mmio, ~0); reg = (reg << shift) & mask; - if (reg != (vgic_cpu->vgic_vmcr & mask)) + if (reg != (vgic_cpu->vgic_v2.vgic_vmcr & mask)) updated = true; - vgic_cpu->vgic_vmcr &= ~mask; - vgic_cpu->vgic_vmcr |= reg; + vgic_cpu->vgic_v2.vgic_vmcr &= ~mask; + vgic_cpu->vgic_v2.vgic_vmcr |= reg; } return updated; } -- cgit v1.2.3 From 8d5c6b06a5d5f8ebcf40558e566781d572920740 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 3 Jun 2013 15:55:02 +0100 Subject: KVM: ARM: vgic: introduce vgic_ops and LR manipulation primitives In order to split the various register manipulation from the main vgic code, introduce a vgic_ops structure, and start by abstracting the LR manipulation code with a couple of accessors. Reviewed-by: Christoffer Dall Signed-off-by: Marc Zyngier --- include/kvm/arm_vgic.h | 18 ++++++ virt/kvm/arm/vgic.c | 162 +++++++++++++++++++++++++++++++++---------------- 2 files changed, 128 insertions(+), 52 deletions(-) (limited to 'include') diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index f738e5a69ee9..17bbe51b79a1 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -68,6 +68,24 @@ struct vgic_bytemap { u32 shared[VGIC_NR_SHARED_IRQS / 4]; }; +struct kvm_vcpu; + +#define LR_STATE_PENDING (1 << 0) +#define LR_STATE_ACTIVE (1 << 1) +#define LR_STATE_MASK (3 << 0) +#define LR_EOI_INT (1 << 2) + +struct vgic_lr { + u16 irq; + u8 source; + u8 state; +}; + +struct vgic_ops { + struct vgic_lr (*get_lr)(const struct kvm_vcpu *, int); + void (*set_lr)(struct kvm_vcpu *, int, struct vgic_lr); +}; + struct vgic_dist { #ifdef CONFIG_KVM_ARM_VGIC spinlock_t lock; diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c index 0ba1ab0721fd..11408fee600e 100644 --- a/virt/kvm/arm/vgic.c +++ b/virt/kvm/arm/vgic.c @@ -94,9 +94,12 @@ static struct device_node *vgic_node; #define ACCESS_WRITE_MASK(x) ((x) & (3 << 1)) static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu); +static void vgic_retire_lr(int lr_nr, int irq, struct kvm_vcpu *vcpu); static void vgic_update_state(struct kvm *kvm); static void vgic_kick_vcpus(struct kvm *kvm); static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg); +static struct vgic_lr vgic_get_lr(const struct kvm_vcpu *vcpu, int lr); +static void vgic_set_lr(struct kvm_vcpu *vcpu, int lr, struct vgic_lr lr_desc); static u32 vgic_nr_lr; static unsigned int vgic_maint_irq; @@ -593,18 +596,6 @@ static bool handle_mmio_sgi_reg(struct kvm_vcpu *vcpu, return false; } -#define LR_CPUID(lr) \ - (((lr) & GICH_LR_PHYSID_CPUID) >> GICH_LR_PHYSID_CPUID_SHIFT) -#define LR_IRQID(lr) \ - ((lr) & GICH_LR_VIRTUALID) - -static void vgic_retire_lr(int lr_nr, int irq, struct vgic_cpu *vgic_cpu) -{ - clear_bit(lr_nr, vgic_cpu->lr_used); - vgic_cpu->vgic_v2.vgic_lr[lr_nr] &= ~GICH_LR_STATE; - vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY; -} - /** * vgic_unqueue_irqs - move pending IRQs from LRs to the distributor * @vgic_cpu: Pointer to the vgic_cpu struct holding the LRs @@ -622,13 +613,10 @@ static void vgic_unqueue_irqs(struct kvm_vcpu *vcpu) struct vgic_dist *dist = &vcpu->kvm->arch.vgic; struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; int vcpu_id = vcpu->vcpu_id; - int i, irq, source_cpu; - u32 *lr; + int i; for_each_set_bit(i, vgic_cpu->lr_used, vgic_cpu->nr_lr) { - lr = &vgic_cpu->vgic_v2.vgic_lr[i]; - irq = LR_IRQID(*lr); - source_cpu = LR_CPUID(*lr); + struct vgic_lr lr = vgic_get_lr(vcpu, i); /* * There are three options for the state bits: @@ -640,7 +628,7 @@ static void vgic_unqueue_irqs(struct kvm_vcpu *vcpu) * If the LR holds only an active interrupt (not pending) then * just leave it alone. */ - if ((*lr & GICH_LR_STATE) == GICH_LR_ACTIVE_BIT) + if ((lr.state & LR_STATE_MASK) == LR_STATE_ACTIVE) continue; /* @@ -649,18 +637,19 @@ static void vgic_unqueue_irqs(struct kvm_vcpu *vcpu) * is fine, then we are only setting a few bits that were * already set. */ - vgic_dist_irq_set(vcpu, irq); - if (irq < VGIC_NR_SGIS) - dist->irq_sgi_sources[vcpu_id][irq] |= 1 << source_cpu; - *lr &= ~GICH_LR_PENDING_BIT; + vgic_dist_irq_set(vcpu, lr.irq); + if (lr.irq < VGIC_NR_SGIS) + dist->irq_sgi_sources[vcpu_id][lr.irq] |= 1 << lr.source; + lr.state &= ~LR_STATE_PENDING; + vgic_set_lr(vcpu, i, lr); /* * If there's no state left on the LR (it could still be * active), then the LR does not hold any useful info and can * be marked as free for other use. */ - if (!(*lr & GICH_LR_STATE)) - vgic_retire_lr(i, irq, vgic_cpu); + if (!(lr.state & LR_STATE_MASK)) + vgic_retire_lr(i, lr.irq, vcpu); /* Finally update the VGIC state. */ vgic_update_state(vcpu->kvm); @@ -989,8 +978,69 @@ static void vgic_update_state(struct kvm *kvm) } } -#define MK_LR_PEND(src, irq) \ - (GICH_LR_PENDING_BIT | ((src) << GICH_LR_PHYSID_CPUID_SHIFT) | (irq)) +static struct vgic_lr vgic_v2_get_lr(const struct kvm_vcpu *vcpu, int lr) +{ + struct vgic_lr lr_desc; + u32 val = vcpu->arch.vgic_cpu.vgic_v2.vgic_lr[lr]; + + lr_desc.irq = val & GICH_LR_VIRTUALID; + if (lr_desc.irq <= 15) + lr_desc.source = (val >> GICH_LR_PHYSID_CPUID_SHIFT) & 0x7; + else + lr_desc.source = 0; + lr_desc.state = 0; + + if (val & GICH_LR_PENDING_BIT) + lr_desc.state |= LR_STATE_PENDING; + if (val & GICH_LR_ACTIVE_BIT) + lr_desc.state |= LR_STATE_ACTIVE; + if (val & GICH_LR_EOI) + lr_desc.state |= LR_EOI_INT; + + return lr_desc; +} + +static void vgic_v2_set_lr(struct kvm_vcpu *vcpu, int lr, + struct vgic_lr lr_desc) +{ + u32 lr_val = (lr_desc.source << GICH_LR_PHYSID_CPUID_SHIFT) | lr_desc.irq; + + if (lr_desc.state & LR_STATE_PENDING) + lr_val |= GICH_LR_PENDING_BIT; + if (lr_desc.state & LR_STATE_ACTIVE) + lr_val |= GICH_LR_ACTIVE_BIT; + if (lr_desc.state & LR_EOI_INT) + lr_val |= GICH_LR_EOI; + + vcpu->arch.vgic_cpu.vgic_v2.vgic_lr[lr] = lr_val; +} + +static const struct vgic_ops vgic_ops = { + .get_lr = vgic_v2_get_lr, + .set_lr = vgic_v2_set_lr, +}; + +static struct vgic_lr vgic_get_lr(const struct kvm_vcpu *vcpu, int lr) +{ + return vgic_ops.get_lr(vcpu, lr); +} + +static void vgic_set_lr(struct kvm_vcpu *vcpu, int lr, + struct vgic_lr vlr) +{ + vgic_ops.set_lr(vcpu, lr, vlr); +} + +static void vgic_retire_lr(int lr_nr, int irq, struct kvm_vcpu *vcpu) +{ + struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; + struct vgic_lr vlr = vgic_get_lr(vcpu, lr_nr); + + vlr.state = 0; + vgic_set_lr(vcpu, lr_nr, vlr); + clear_bit(lr_nr, vgic_cpu->lr_used); + vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY; +} /* * An interrupt may have been disabled after being made pending on the @@ -1007,12 +1057,12 @@ static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu) int lr; for_each_set_bit(lr, vgic_cpu->lr_used, vgic_cpu->nr_lr) { - int irq = vgic_cpu->vgic_v2.vgic_lr[lr] & GICH_LR_VIRTUALID; + struct vgic_lr vlr = vgic_get_lr(vcpu, lr); - if (!vgic_irq_is_enabled(vcpu, irq)) { - vgic_retire_lr(lr, irq, vgic_cpu); - if (vgic_irq_is_active(vcpu, irq)) - vgic_irq_clear_active(vcpu, irq); + if (!vgic_irq_is_enabled(vcpu, vlr.irq)) { + vgic_retire_lr(lr, vlr.irq, vcpu); + if (vgic_irq_is_active(vcpu, vlr.irq)) + vgic_irq_clear_active(vcpu, vlr.irq); } } } @@ -1024,6 +1074,7 @@ static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu) static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq) { struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; + struct vgic_lr vlr; int lr; /* Sanitize the input... */ @@ -1036,13 +1087,15 @@ static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq) lr = vgic_cpu->vgic_irq_lr_map[irq]; /* Do we have an active interrupt for the same CPUID? */ - if (lr != LR_EMPTY && - (LR_CPUID(vgic_cpu->vgic_v2.vgic_lr[lr]) == sgi_source_id)) { - kvm_debug("LR%d piggyback for IRQ%d %x\n", - lr, irq, vgic_cpu->vgic_v2.vgic_lr[lr]); - BUG_ON(!test_bit(lr, vgic_cpu->lr_used)); - vgic_cpu->vgic_v2.vgic_lr[lr] |= GICH_LR_PENDING_BIT; - return true; + if (lr != LR_EMPTY) { + vlr = vgic_get_lr(vcpu, lr); + if (vlr.source == sgi_source_id) { + kvm_debug("LR%d piggyback for IRQ%d\n", lr, vlr.irq); + BUG_ON(!test_bit(lr, vgic_cpu->lr_used)); + vlr.state |= LR_STATE_PENDING; + vgic_set_lr(vcpu, lr, vlr); + return true; + } } /* Try to use another LR for this interrupt */ @@ -1052,12 +1105,16 @@ static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq) return false; kvm_debug("LR%d allocated for IRQ%d %x\n", lr, irq, sgi_source_id); - vgic_cpu->vgic_v2.vgic_lr[lr] = MK_LR_PEND(sgi_source_id, irq); vgic_cpu->vgic_irq_lr_map[irq] = lr; set_bit(lr, vgic_cpu->lr_used); + vlr.irq = irq; + vlr.source = sgi_source_id; + vlr.state = LR_STATE_PENDING; if (!vgic_irq_is_edge(vcpu, irq)) - vgic_cpu->vgic_v2.vgic_lr[lr] |= GICH_LR_EOI; + vlr.state |= LR_EOI_INT; + + vgic_set_lr(vcpu, lr, vlr); return true; } @@ -1180,21 +1237,23 @@ static bool vgic_process_maintenance(struct kvm_vcpu *vcpu) * Some level interrupts have been EOIed. Clear their * active bit. */ - int lr, irq; + int lr; for_each_set_bit(lr, (unsigned long *)vgic_cpu->vgic_v2.vgic_eisr, vgic_cpu->nr_lr) { - irq = vgic_cpu->vgic_v2.vgic_lr[lr] & GICH_LR_VIRTUALID; + struct vgic_lr vlr = vgic_get_lr(vcpu, lr); - vgic_irq_clear_active(vcpu, irq); - vgic_cpu->vgic_v2.vgic_lr[lr] &= ~GICH_LR_EOI; + vgic_irq_clear_active(vcpu, vlr.irq); + WARN_ON(vlr.state & LR_STATE_MASK); + vlr.state = 0; + vgic_set_lr(vcpu, lr, vlr); /* Any additional pending interrupt? */ - if (vgic_dist_irq_is_pending(vcpu, irq)) { - vgic_cpu_irq_set(vcpu, irq); + if (vgic_dist_irq_is_pending(vcpu, vlr.irq)) { + vgic_cpu_irq_set(vcpu, vlr.irq); level_pending = true; } else { - vgic_cpu_irq_clear(vcpu, irq); + vgic_cpu_irq_clear(vcpu, vlr.irq); } /* @@ -1202,7 +1261,6 @@ static bool vgic_process_maintenance(struct kvm_vcpu *vcpu) * been marked as empty. */ set_bit(lr, (unsigned long *)vgic_cpu->vgic_v2.vgic_elrsr); - vgic_cpu->vgic_v2.vgic_lr[lr] &= ~GICH_LR_ACTIVE_BIT; } } @@ -1228,15 +1286,15 @@ static void __kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu) /* Clear mappings for empty LRs */ for_each_set_bit(lr, (unsigned long *)vgic_cpu->vgic_v2.vgic_elrsr, vgic_cpu->nr_lr) { - int irq; + struct vgic_lr vlr; if (!test_and_clear_bit(lr, vgic_cpu->lr_used)) continue; - irq = vgic_cpu->vgic_v2.vgic_lr[lr] & GICH_LR_VIRTUALID; + vlr = vgic_get_lr(vcpu, lr); - BUG_ON(irq >= VGIC_NR_IRQS); - vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY; + BUG_ON(vlr.irq >= VGIC_NR_IRQS); + vgic_cpu->vgic_irq_lr_map[vlr.irq] = LR_EMPTY; } /* Check if we still have something up our sleeve... */ -- cgit v1.2.3 From 69bb2c9fbc11d9d4358fbb798db15c9092eb4d8c Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 4 Jun 2013 10:29:39 +0100 Subject: KVM: ARM: vgic: abstract access to the ELRSR bitmap Move the GICH_ELRSR access to its own functions, and add them to the vgic_ops structure. Acked-by: Catalin Marinas Reviewed-by: Christoffer Dall Signed-off-by: Marc Zyngier --- include/kvm/arm_vgic.h | 2 ++ virt/kvm/arm/vgic.c | 46 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 43 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 17bbe51b79a1..38864f5e47bc 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -84,6 +84,8 @@ struct vgic_lr { struct vgic_ops { struct vgic_lr (*get_lr)(const struct kvm_vcpu *, int); void (*set_lr)(struct kvm_vcpu *, int, struct vgic_lr); + void (*sync_lr_elrsr)(struct kvm_vcpu *, int, struct vgic_lr); + u64 (*get_elrsr)(const struct kvm_vcpu *vcpu); }; struct vgic_dist { diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c index 11408fee600e..6dcc974fa2b4 100644 --- a/virt/kvm/arm/vgic.c +++ b/virt/kvm/arm/vgic.c @@ -1015,9 +1015,32 @@ static void vgic_v2_set_lr(struct kvm_vcpu *vcpu, int lr, vcpu->arch.vgic_cpu.vgic_v2.vgic_lr[lr] = lr_val; } +static void vgic_v2_sync_lr_elrsr(struct kvm_vcpu *vcpu, int lr, + struct vgic_lr lr_desc) +{ + if (!(lr_desc.state & LR_STATE_MASK)) + set_bit(lr, (unsigned long *)vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr); +} + +static u64 vgic_v2_get_elrsr(const struct kvm_vcpu *vcpu) +{ + u64 val; + +#if BITS_PER_LONG == 64 + val = vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr[1]; + val <<= 32; + val |= vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr[0]; +#else + val = *(u64 *)vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr; +#endif + return val; +} + static const struct vgic_ops vgic_ops = { .get_lr = vgic_v2_get_lr, .set_lr = vgic_v2_set_lr, + .sync_lr_elrsr = vgic_v2_sync_lr_elrsr, + .get_elrsr = vgic_v2_get_elrsr, }; static struct vgic_lr vgic_get_lr(const struct kvm_vcpu *vcpu, int lr) @@ -1031,6 +1054,17 @@ static void vgic_set_lr(struct kvm_vcpu *vcpu, int lr, vgic_ops.set_lr(vcpu, lr, vlr); } +static void vgic_sync_lr_elrsr(struct kvm_vcpu *vcpu, int lr, + struct vgic_lr vlr) +{ + vgic_ops.sync_lr_elrsr(vcpu, lr, vlr); +} + +static inline u64 vgic_get_elrsr(struct kvm_vcpu *vcpu) +{ + return vgic_ops.get_elrsr(vcpu); +} + static void vgic_retire_lr(int lr_nr, int irq, struct kvm_vcpu *vcpu) { struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; @@ -1260,7 +1294,7 @@ static bool vgic_process_maintenance(struct kvm_vcpu *vcpu) * Despite being EOIed, the LR may not have * been marked as empty. */ - set_bit(lr, (unsigned long *)vgic_cpu->vgic_v2.vgic_elrsr); + vgic_sync_lr_elrsr(vcpu, lr, vlr); } } @@ -1278,14 +1312,17 @@ static void __kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu) { struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; struct vgic_dist *dist = &vcpu->kvm->arch.vgic; + u64 elrsr; + unsigned long *elrsr_ptr; int lr, pending; bool level_pending; level_pending = vgic_process_maintenance(vcpu); + elrsr = vgic_get_elrsr(vcpu); + elrsr_ptr = (unsigned long *)&elrsr; /* Clear mappings for empty LRs */ - for_each_set_bit(lr, (unsigned long *)vgic_cpu->vgic_v2.vgic_elrsr, - vgic_cpu->nr_lr) { + for_each_set_bit(lr, elrsr_ptr, vgic_cpu->nr_lr) { struct vgic_lr vlr; if (!test_and_clear_bit(lr, vgic_cpu->lr_used)) @@ -1298,8 +1335,7 @@ static void __kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu) } /* Check if we still have something up our sleeve... */ - pending = find_first_zero_bit((unsigned long *)vgic_cpu->vgic_v2.vgic_elrsr, - vgic_cpu->nr_lr); + pending = find_first_zero_bit(elrsr_ptr, vgic_cpu->nr_lr); if (level_pending || pending < vgic_cpu->nr_lr) set_bit(vcpu->vcpu_id, &dist->irq_pending_on_cpu); } -- cgit v1.2.3 From 8d6a0313c125c3c7b208b75695fe6ab00afab4c5 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 4 Jun 2013 10:33:43 +0100 Subject: KVM: ARM: vgic: abstract EISR bitmap access Move the GICH_EISR access to its own function. Acked-by: Catalin Marinas Reviewed-by: Christoffer Dall Signed-off-by: Marc Zyngier --- include/kvm/arm_vgic.h | 1 + virt/kvm/arm/vgic.c | 25 +++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 38864f5e47bc..ccb9b59818f4 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -86,6 +86,7 @@ struct vgic_ops { void (*set_lr)(struct kvm_vcpu *, int, struct vgic_lr); void (*sync_lr_elrsr)(struct kvm_vcpu *, int, struct vgic_lr); u64 (*get_elrsr)(const struct kvm_vcpu *vcpu); + u64 (*get_eisr)(const struct kvm_vcpu *vcpu); }; struct vgic_dist { diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c index 6dcc974fa2b4..1e857e6e66b5 100644 --- a/virt/kvm/arm/vgic.c +++ b/virt/kvm/arm/vgic.c @@ -1036,11 +1036,26 @@ static u64 vgic_v2_get_elrsr(const struct kvm_vcpu *vcpu) return val; } +static u64 vgic_v2_get_eisr(const struct kvm_vcpu *vcpu) +{ + u64 val; + +#if BITS_PER_LONG == 64 + val = vcpu->arch.vgic_cpu.vgic_v2.vgic_eisr[1]; + val <<= 32; + val |= vcpu->arch.vgic_cpu.vgic_v2.vgic_eisr[0]; +#else + val = *(u64 *)vcpu->arch.vgic_cpu.vgic_v2.vgic_eisr; +#endif + return val; +} + static const struct vgic_ops vgic_ops = { .get_lr = vgic_v2_get_lr, .set_lr = vgic_v2_set_lr, .sync_lr_elrsr = vgic_v2_sync_lr_elrsr, .get_elrsr = vgic_v2_get_elrsr, + .get_eisr = vgic_v2_get_eisr, }; static struct vgic_lr vgic_get_lr(const struct kvm_vcpu *vcpu, int lr) @@ -1065,6 +1080,11 @@ static inline u64 vgic_get_elrsr(struct kvm_vcpu *vcpu) return vgic_ops.get_elrsr(vcpu); } +static inline u64 vgic_get_eisr(struct kvm_vcpu *vcpu) +{ + return vgic_ops.get_eisr(vcpu); +} + static void vgic_retire_lr(int lr_nr, int irq, struct kvm_vcpu *vcpu) { struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; @@ -1271,10 +1291,11 @@ static bool vgic_process_maintenance(struct kvm_vcpu *vcpu) * Some level interrupts have been EOIed. Clear their * active bit. */ + u64 eisr = vgic_get_eisr(vcpu); + unsigned long *eisr_ptr = (unsigned long *)&eisr; int lr; - for_each_set_bit(lr, (unsigned long *)vgic_cpu->vgic_v2.vgic_eisr, - vgic_cpu->nr_lr) { + for_each_set_bit(lr, eisr_ptr, vgic_cpu->nr_lr) { struct vgic_lr vlr = vgic_get_lr(vcpu, lr); vgic_irq_clear_active(vcpu, vlr.irq); -- cgit v1.2.3 From 495dd859f304689a7cd5ef413c439cb090dc25e6 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 4 Jun 2013 11:02:10 +0100 Subject: KVM: ARM: vgic: abstract MISR decoding Instead of directly dealing with the GICH_MISR bits, move the code to its own function and use a couple of public flags to represent the actual state. Acked-by: Catalin Marinas Reviewed-by: Christoffer Dall Signed-off-by: Marc Zyngier --- include/kvm/arm_vgic.h | 4 ++++ virt/kvm/arm/vgic.c | 26 +++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index ccb9b59818f4..4857508b12e7 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -87,6 +87,7 @@ struct vgic_ops { void (*sync_lr_elrsr)(struct kvm_vcpu *, int, struct vgic_lr); u64 (*get_elrsr)(const struct kvm_vcpu *vcpu); u64 (*get_eisr)(const struct kvm_vcpu *vcpu); + u32 (*get_interrupt_status)(const struct kvm_vcpu *vcpu); }; struct vgic_dist { @@ -165,6 +166,9 @@ struct vgic_cpu { #define LR_EMPTY 0xff +#define INT_STATUS_EOI (1 << 0) +#define INT_STATUS_UNDERFLOW (1 << 1) + struct kvm; struct kvm_vcpu; struct kvm_run; diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c index 1e857e6e66b5..c0bcc9735424 100644 --- a/virt/kvm/arm/vgic.c +++ b/virt/kvm/arm/vgic.c @@ -1050,12 +1050,26 @@ static u64 vgic_v2_get_eisr(const struct kvm_vcpu *vcpu) return val; } +static u32 vgic_v2_get_interrupt_status(const struct kvm_vcpu *vcpu) +{ + u32 misr = vcpu->arch.vgic_cpu.vgic_v2.vgic_misr; + u32 ret = 0; + + if (misr & GICH_MISR_EOI) + ret |= INT_STATUS_EOI; + if (misr & GICH_MISR_U) + ret |= INT_STATUS_UNDERFLOW; + + return ret; +} + static const struct vgic_ops vgic_ops = { .get_lr = vgic_v2_get_lr, .set_lr = vgic_v2_set_lr, .sync_lr_elrsr = vgic_v2_sync_lr_elrsr, .get_elrsr = vgic_v2_get_elrsr, .get_eisr = vgic_v2_get_eisr, + .get_interrupt_status = vgic_v2_get_interrupt_status, }; static struct vgic_lr vgic_get_lr(const struct kvm_vcpu *vcpu, int lr) @@ -1085,6 +1099,11 @@ static inline u64 vgic_get_eisr(struct kvm_vcpu *vcpu) return vgic_ops.get_eisr(vcpu); } +static inline u32 vgic_get_interrupt_status(struct kvm_vcpu *vcpu) +{ + return vgic_ops.get_interrupt_status(vcpu); +} + static void vgic_retire_lr(int lr_nr, int irq, struct kvm_vcpu *vcpu) { struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; @@ -1282,11 +1301,12 @@ epilog: static bool vgic_process_maintenance(struct kvm_vcpu *vcpu) { struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; + u32 status = vgic_get_interrupt_status(vcpu); bool level_pending = false; - kvm_debug("MISR = %08x\n", vgic_cpu->vgic_v2.vgic_misr); + kvm_debug("STATUS = %08x\n", status); - if (vgic_cpu->vgic_v2.vgic_misr & GICH_MISR_EOI) { + if (status & INT_STATUS_EOI) { /* * Some level interrupts have been EOIed. Clear their * active bit. @@ -1319,7 +1339,7 @@ static bool vgic_process_maintenance(struct kvm_vcpu *vcpu) } } - if (vgic_cpu->vgic_v2.vgic_misr & GICH_MISR_U) + if (status & INT_STATUS_UNDERFLOW) vgic_cpu->vgic_v2.vgic_hcr &= ~GICH_HCR_UIE; return level_pending; -- cgit v1.2.3 From 909d9b5025f149af6cfc304a76ad6218e6622cc0 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 4 Jun 2013 11:24:17 +0100 Subject: KVM: ARM: vgic: move underflow handling to vgic_ops Move the code dealing with LR underflow handling to its own functions, and make them accessible through vgic_ops. Acked-by: Catalin Marinas Reviewed-by: Christoffer Dall Signed-off-by: Marc Zyngier --- include/kvm/arm_vgic.h | 2 ++ virt/kvm/arm/vgic.c | 28 +++++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 4857508b12e7..cdfa5d9567c6 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -88,6 +88,8 @@ struct vgic_ops { u64 (*get_elrsr)(const struct kvm_vcpu *vcpu); u64 (*get_eisr)(const struct kvm_vcpu *vcpu); u32 (*get_interrupt_status)(const struct kvm_vcpu *vcpu); + void (*enable_underflow)(struct kvm_vcpu *vcpu); + void (*disable_underflow)(struct kvm_vcpu *vcpu); }; struct vgic_dist { diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c index c0bcc9735424..6d618e0b08a1 100644 --- a/virt/kvm/arm/vgic.c +++ b/virt/kvm/arm/vgic.c @@ -1063,6 +1063,16 @@ static u32 vgic_v2_get_interrupt_status(const struct kvm_vcpu *vcpu) return ret; } +static void vgic_v2_enable_underflow(struct kvm_vcpu *vcpu) +{ + vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr |= GICH_HCR_UIE; +} + +static void vgic_v2_disable_underflow(struct kvm_vcpu *vcpu) +{ + vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr &= ~GICH_HCR_UIE; +} + static const struct vgic_ops vgic_ops = { .get_lr = vgic_v2_get_lr, .set_lr = vgic_v2_set_lr, @@ -1070,6 +1080,8 @@ static const struct vgic_ops vgic_ops = { .get_elrsr = vgic_v2_get_elrsr, .get_eisr = vgic_v2_get_eisr, .get_interrupt_status = vgic_v2_get_interrupt_status, + .enable_underflow = vgic_v2_enable_underflow, + .disable_underflow = vgic_v2_disable_underflow, }; static struct vgic_lr vgic_get_lr(const struct kvm_vcpu *vcpu, int lr) @@ -1104,6 +1116,16 @@ static inline u32 vgic_get_interrupt_status(struct kvm_vcpu *vcpu) return vgic_ops.get_interrupt_status(vcpu); } +static inline void vgic_enable_underflow(struct kvm_vcpu *vcpu) +{ + vgic_ops.enable_underflow(vcpu); +} + +static inline void vgic_disable_underflow(struct kvm_vcpu *vcpu) +{ + vgic_ops.disable_underflow(vcpu); +} + static void vgic_retire_lr(int lr_nr, int irq, struct kvm_vcpu *vcpu) { struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; @@ -1285,9 +1307,9 @@ static void __kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu) epilog: if (overflow) { - vgic_cpu->vgic_v2.vgic_hcr |= GICH_HCR_UIE; + vgic_enable_underflow(vcpu); } else { - vgic_cpu->vgic_v2.vgic_hcr &= ~GICH_HCR_UIE; + vgic_disable_underflow(vcpu); /* * We're about to run this VCPU, and we've consumed * everything the distributor had in store for @@ -1340,7 +1362,7 @@ static bool vgic_process_maintenance(struct kvm_vcpu *vcpu) } if (status & INT_STATUS_UNDERFLOW) - vgic_cpu->vgic_v2.vgic_hcr &= ~GICH_HCR_UIE; + vgic_disable_underflow(vcpu); return level_pending; } -- cgit v1.2.3 From beee38b9d0c0ea6cf2a7f35c3108f7d8281d4545 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 4 Feb 2014 17:48:10 +0000 Subject: KVM: ARM: vgic: abstract VMCR access Instead of directly messing with with the GICH_VMCR bits for the CPU interface save/restore code, add accessors that encode/decode the entire set of registers exposed by VMCR. Not the most efficient thing, but given that this code is only used by the save/restore code, performance is far from being critical. Reviewed-by: Christoffer Dall Signed-off-by: Marc Zyngier --- include/kvm/arm_vgic.h | 9 +++++++ virt/kvm/arm/vgic.c | 69 ++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 62 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index cdfa5d9567c6..f51580043170 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -81,6 +81,13 @@ struct vgic_lr { u8 state; }; +struct vgic_vmcr { + u32 ctlr; + u32 abpr; + u32 bpr; + u32 pmr; +}; + struct vgic_ops { struct vgic_lr (*get_lr)(const struct kvm_vcpu *, int); void (*set_lr)(struct kvm_vcpu *, int, struct vgic_lr); @@ -90,6 +97,8 @@ struct vgic_ops { u32 (*get_interrupt_status)(const struct kvm_vcpu *vcpu); void (*enable_underflow)(struct kvm_vcpu *vcpu); void (*disable_underflow)(struct kvm_vcpu *vcpu); + void (*get_vmcr)(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr); + void (*set_vmcr)(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr); }; struct vgic_dist { diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c index 6d618e0b08a1..5c706393956d 100644 --- a/virt/kvm/arm/vgic.c +++ b/virt/kvm/arm/vgic.c @@ -100,8 +100,10 @@ static void vgic_kick_vcpus(struct kvm *kvm); static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg); static struct vgic_lr vgic_get_lr(const struct kvm_vcpu *vcpu, int lr); static void vgic_set_lr(struct kvm_vcpu *vcpu, int lr, struct vgic_lr lr_desc); -static u32 vgic_nr_lr; +static void vgic_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr); +static void vgic_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr); +static u32 vgic_nr_lr; static unsigned int vgic_maint_irq; static u32 *vgic_bitmap_get_reg(struct vgic_bitmap *x, @@ -1073,6 +1075,28 @@ static void vgic_v2_disable_underflow(struct kvm_vcpu *vcpu) vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr &= ~GICH_HCR_UIE; } +static void vgic_v2_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp) +{ + u32 vmcr = vcpu->arch.vgic_cpu.vgic_v2.vgic_vmcr; + + vmcrp->ctlr = (vmcr & GICH_VMCR_CTRL_MASK) >> GICH_VMCR_CTRL_SHIFT; + vmcrp->abpr = (vmcr & GICH_VMCR_ALIAS_BINPOINT_MASK) >> GICH_VMCR_ALIAS_BINPOINT_SHIFT; + vmcrp->bpr = (vmcr & GICH_VMCR_BINPOINT_MASK) >> GICH_VMCR_BINPOINT_SHIFT; + vmcrp->pmr = (vmcr & GICH_VMCR_PRIMASK_MASK) >> GICH_VMCR_PRIMASK_SHIFT; +} + +static void vgic_v2_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp) +{ + u32 vmcr; + + vmcr = (vmcrp->ctlr << GICH_VMCR_CTRL_SHIFT) & GICH_VMCR_CTRL_MASK; + vmcr |= (vmcrp->abpr << GICH_VMCR_ALIAS_BINPOINT_SHIFT) & GICH_VMCR_ALIAS_BINPOINT_MASK; + vmcr |= (vmcrp->bpr << GICH_VMCR_BINPOINT_SHIFT) & GICH_VMCR_BINPOINT_MASK; + vmcr |= (vmcrp->pmr << GICH_VMCR_PRIMASK_SHIFT) & GICH_VMCR_PRIMASK_MASK; + + vcpu->arch.vgic_cpu.vgic_v2.vgic_vmcr = vmcr; +} + static const struct vgic_ops vgic_ops = { .get_lr = vgic_v2_get_lr, .set_lr = vgic_v2_set_lr, @@ -1082,6 +1106,8 @@ static const struct vgic_ops vgic_ops = { .get_interrupt_status = vgic_v2_get_interrupt_status, .enable_underflow = vgic_v2_enable_underflow, .disable_underflow = vgic_v2_disable_underflow, + .get_vmcr = vgic_v2_get_vmcr, + .set_vmcr = vgic_v2_set_vmcr, }; static struct vgic_lr vgic_get_lr(const struct kvm_vcpu *vcpu, int lr) @@ -1126,6 +1152,16 @@ static inline void vgic_disable_underflow(struct kvm_vcpu *vcpu) vgic_ops.disable_underflow(vcpu); } +static inline void vgic_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr) +{ + vgic_ops.get_vmcr(vcpu, vmcr); +} + +static void vgic_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr) +{ + vgic_ops.set_vmcr(vcpu, vmcr); +} + static void vgic_retire_lr(int lr_nr, int irq, struct kvm_vcpu *vcpu) { struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; @@ -1879,39 +1915,40 @@ int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write) static bool handle_cpu_mmio_misc(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio, phys_addr_t offset) { - struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; - u32 reg, mask = 0, shift = 0; bool updated = false; + struct vgic_vmcr vmcr; + u32 *vmcr_field; + u32 reg; + + vgic_get_vmcr(vcpu, &vmcr); switch (offset & ~0x3) { case GIC_CPU_CTRL: - mask = GICH_VMCR_CTRL_MASK; - shift = GICH_VMCR_CTRL_SHIFT; + vmcr_field = &vmcr.ctlr; break; case GIC_CPU_PRIMASK: - mask = GICH_VMCR_PRIMASK_MASK; - shift = GICH_VMCR_PRIMASK_SHIFT; + vmcr_field = &vmcr.pmr; break; case GIC_CPU_BINPOINT: - mask = GICH_VMCR_BINPOINT_MASK; - shift = GICH_VMCR_BINPOINT_SHIFT; + vmcr_field = &vmcr.bpr; break; case GIC_CPU_ALIAS_BINPOINT: - mask = GICH_VMCR_ALIAS_BINPOINT_MASK; - shift = GICH_VMCR_ALIAS_BINPOINT_SHIFT; + vmcr_field = &vmcr.abpr; break; + default: + BUG(); } if (!mmio->is_write) { - reg = (vgic_cpu->vgic_v2.vgic_vmcr & mask) >> shift; + reg = *vmcr_field; mmio_data_write(mmio, ~0, reg); } else { reg = mmio_data_read(mmio, ~0); - reg = (reg << shift) & mask; - if (reg != (vgic_cpu->vgic_v2.vgic_vmcr & mask)) + if (reg != *vmcr_field) { + *vmcr_field = reg; + vgic_set_vmcr(vcpu, &vmcr); updated = true; - vgic_cpu->vgic_v2.vgic_vmcr &= ~mask; - vgic_cpu->vgic_v2.vgic_vmcr |= reg; + } } return updated; } -- cgit v1.2.3 From da8dafd1777cdd93091207952297d221a88e6479 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 4 Jun 2013 11:36:38 +0100 Subject: KVM: ARM: vgic: introduce vgic_enable Move the code dealing with enabling the VGIC on to vgic_ops. Acked-by: Catalin Marinas Reviewed-by: Christoffer Dall Signed-off-by: Marc Zyngier --- include/kvm/arm_vgic.h | 1 + virt/kvm/arm/vgic.c | 29 +++++++++++++++++++++-------- 2 files changed, 22 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index f51580043170..2228973ea8e4 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -99,6 +99,7 @@ struct vgic_ops { void (*disable_underflow)(struct kvm_vcpu *vcpu); void (*get_vmcr)(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr); void (*set_vmcr)(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr); + void (*enable)(struct kvm_vcpu *vcpu); }; struct vgic_dist { diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c index 5c706393956d..70f674bb13a1 100644 --- a/virt/kvm/arm/vgic.c +++ b/virt/kvm/arm/vgic.c @@ -1097,6 +1097,19 @@ static void vgic_v2_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp) vcpu->arch.vgic_cpu.vgic_v2.vgic_vmcr = vmcr; } +static void vgic_v2_enable(struct kvm_vcpu *vcpu) +{ + /* + * By forcing VMCR to zero, the GIC will restore the binary + * points to their reset values. Anything else resets to zero + * anyway. + */ + vcpu->arch.vgic_cpu.vgic_v2.vgic_vmcr = 0; + + /* Get the show on the road... */ + vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr = GICH_HCR_EN; +} + static const struct vgic_ops vgic_ops = { .get_lr = vgic_v2_get_lr, .set_lr = vgic_v2_set_lr, @@ -1108,6 +1121,7 @@ static const struct vgic_ops vgic_ops = { .disable_underflow = vgic_v2_disable_underflow, .get_vmcr = vgic_v2_get_vmcr, .set_vmcr = vgic_v2_set_vmcr, + .enable = vgic_v2_enable, }; static struct vgic_lr vgic_get_lr(const struct kvm_vcpu *vcpu, int lr) @@ -1162,6 +1176,11 @@ static void vgic_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr) vgic_ops.set_vmcr(vcpu, vmcr); } +static inline void vgic_enable(struct kvm_vcpu *vcpu) +{ + vgic_ops.enable(vcpu); +} + static void vgic_retire_lr(int lr_nr, int irq, struct kvm_vcpu *vcpu) { struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; @@ -1624,15 +1643,9 @@ int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu) vgic_cpu->vgic_irq_lr_map[i] = LR_EMPTY; } - /* - * By forcing VMCR to zero, the GIC will restore the binary - * points to their reset values. Anything else resets to zero - * anyway. - */ - vgic_cpu->vgic_v2.vgic_vmcr = 0; - vgic_cpu->nr_lr = vgic_nr_lr; - vgic_cpu->vgic_v2.vgic_hcr = GICH_HCR_EN; /* Get the show on the road... */ + + vgic_enable(vcpu); return 0; } -- cgit v1.2.3 From ca85f623e37d096206e092ef037a145a60fa7f85 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 18 Jun 2013 19:17:28 +0100 Subject: KVM: ARM: introduce vgic_params structure Move all the data specific to a given GIC implementation into its own little structure. Acked-by: Catalin Marinas Reviewed-by: Christoffer Dall Signed-off-by: Marc Zyngier --- include/kvm/arm_vgic.h | 11 ++++++++ virt/kvm/arm/vgic.c | 70 ++++++++++++++++++++++++-------------------------- 2 files changed, 44 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 2228973ea8e4..ce2e14226dbf 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -102,6 +102,17 @@ struct vgic_ops { void (*enable)(struct kvm_vcpu *vcpu); }; +struct vgic_params { + /* Physical address of vgic virtual cpu interface */ + phys_addr_t vcpu_base; + /* Number of list registers */ + u32 nr_lr; + /* Interrupt number */ + unsigned int maint_irq; + /* Virtual control interface base address */ + void __iomem *vctrl_base; +}; + struct vgic_dist { #ifdef CONFIG_KVM_ARM_VGIC spinlock_t lock; diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c index 70f674bb13a1..f3a996d0a100 100644 --- a/virt/kvm/arm/vgic.c +++ b/virt/kvm/arm/vgic.c @@ -76,14 +76,6 @@ #define IMPLEMENTER_ARM 0x43b #define GICC_ARCH_VERSION_V2 0x2 -/* Physical address of vgic virtual cpu interface */ -static phys_addr_t vgic_vcpu_base; - -/* Virtual control interface base address */ -static void __iomem *vgic_vctrl_base; - -static struct device_node *vgic_node; - #define ACCESS_READ_VALUE (1 << 0) #define ACCESS_READ_RAZ (0 << 0) #define ACCESS_READ_MASK(x) ((x) & (1 << 0)) @@ -103,8 +95,7 @@ static void vgic_set_lr(struct kvm_vcpu *vcpu, int lr, struct vgic_lr lr_desc); static void vgic_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr); static void vgic_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr); -static u32 vgic_nr_lr; -static unsigned int vgic_maint_irq; +static struct vgic_params vgic; static u32 *vgic_bitmap_get_reg(struct vgic_bitmap *x, int cpuid, u32 offset) @@ -1206,7 +1197,7 @@ static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu) struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; int lr; - for_each_set_bit(lr, vgic_cpu->lr_used, vgic_cpu->nr_lr) { + for_each_set_bit(lr, vgic_cpu->lr_used, vgic.nr_lr) { struct vgic_lr vlr = vgic_get_lr(vcpu, lr); if (!vgic_irq_is_enabled(vcpu, vlr.irq)) { @@ -1250,8 +1241,8 @@ static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq) /* Try to use another LR for this interrupt */ lr = find_first_zero_bit((unsigned long *)vgic_cpu->lr_used, - vgic_cpu->nr_lr); - if (lr >= vgic_cpu->nr_lr) + vgic.nr_lr); + if (lr >= vgic.nr_lr) return false; kvm_debug("LR%d allocated for IRQ%d %x\n", lr, irq, sgi_source_id); @@ -1377,7 +1368,6 @@ epilog: static bool vgic_process_maintenance(struct kvm_vcpu *vcpu) { - struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; u32 status = vgic_get_interrupt_status(vcpu); bool level_pending = false; @@ -1392,7 +1382,7 @@ static bool vgic_process_maintenance(struct kvm_vcpu *vcpu) unsigned long *eisr_ptr = (unsigned long *)&eisr; int lr; - for_each_set_bit(lr, eisr_ptr, vgic_cpu->nr_lr) { + for_each_set_bit(lr, eisr_ptr, vgic.nr_lr) { struct vgic_lr vlr = vgic_get_lr(vcpu, lr); vgic_irq_clear_active(vcpu, vlr.irq); @@ -1440,7 +1430,7 @@ static void __kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu) elrsr_ptr = (unsigned long *)&elrsr; /* Clear mappings for empty LRs */ - for_each_set_bit(lr, elrsr_ptr, vgic_cpu->nr_lr) { + for_each_set_bit(lr, elrsr_ptr, vgic.nr_lr) { struct vgic_lr vlr; if (!test_and_clear_bit(lr, vgic_cpu->lr_used)) @@ -1453,8 +1443,8 @@ static void __kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu) } /* Check if we still have something up our sleeve... */ - pending = find_first_zero_bit(elrsr_ptr, vgic_cpu->nr_lr); - if (level_pending || pending < vgic_cpu->nr_lr) + pending = find_first_zero_bit(elrsr_ptr, vgic.nr_lr); + if (level_pending || pending < vgic.nr_lr) set_bit(vcpu->vcpu_id, &dist->irq_pending_on_cpu); } @@ -1643,7 +1633,12 @@ int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu) vgic_cpu->vgic_irq_lr_map[i] = LR_EMPTY; } - vgic_cpu->nr_lr = vgic_nr_lr; + /* + * Store the number of LRs per vcpu, so we don't have to go + * all the way to the distributor structure to find out. Only + * assembly code should use this one. + */ + vgic_cpu->nr_lr = vgic.nr_lr; vgic_enable(vcpu); @@ -1652,7 +1647,7 @@ int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu) static void vgic_init_maintenance_interrupt(void *info) { - enable_percpu_irq(vgic_maint_irq, 0); + enable_percpu_irq(vgic.maint_irq, 0); } static int vgic_cpu_notify(struct notifier_block *self, @@ -1665,7 +1660,7 @@ static int vgic_cpu_notify(struct notifier_block *self, break; case CPU_DYING: case CPU_DYING_FROZEN: - disable_percpu_irq(vgic_maint_irq); + disable_percpu_irq(vgic.maint_irq); break; } @@ -1681,6 +1676,7 @@ int kvm_vgic_hyp_init(void) int ret; struct resource vctrl_res; struct resource vcpu_res; + struct device_node *vgic_node; vgic_node = of_find_compatible_node(NULL, NULL, "arm,cortex-a15-gic"); if (!vgic_node) { @@ -1688,17 +1684,17 @@ int kvm_vgic_hyp_init(void) return -ENODEV; } - vgic_maint_irq = irq_of_parse_and_map(vgic_node, 0); - if (!vgic_maint_irq) { + vgic.maint_irq = irq_of_parse_and_map(vgic_node, 0); + if (!vgic.maint_irq) { kvm_err("error getting vgic maintenance irq from DT\n"); ret = -ENXIO; goto out; } - ret = request_percpu_irq(vgic_maint_irq, vgic_maintenance_handler, + ret = request_percpu_irq(vgic.maint_irq, vgic_maintenance_handler, "vgic", kvm_get_running_vcpus()); if (ret) { - kvm_err("Cannot register interrupt %d\n", vgic_maint_irq); + kvm_err("Cannot register interrupt %d\n", vgic.maint_irq); goto out; } @@ -1714,18 +1710,18 @@ int kvm_vgic_hyp_init(void) goto out_free_irq; } - vgic_vctrl_base = of_iomap(vgic_node, 2); - if (!vgic_vctrl_base) { + vgic.vctrl_base = of_iomap(vgic_node, 2); + if (!vgic.vctrl_base) { kvm_err("Cannot ioremap VCTRL\n"); ret = -ENOMEM; goto out_free_irq; } - vgic_nr_lr = readl_relaxed(vgic_vctrl_base + GICH_VTR); - vgic_nr_lr = (vgic_nr_lr & 0x3f) + 1; + vgic.nr_lr = readl_relaxed(vgic.vctrl_base + GICH_VTR); + vgic.nr_lr = (vgic.nr_lr & 0x3f) + 1; - ret = create_hyp_io_mappings(vgic_vctrl_base, - vgic_vctrl_base + resource_size(&vctrl_res), + ret = create_hyp_io_mappings(vgic.vctrl_base, + vgic.vctrl_base + resource_size(&vctrl_res), vctrl_res.start); if (ret) { kvm_err("Cannot map VCTRL into hyp\n"); @@ -1733,7 +1729,7 @@ int kvm_vgic_hyp_init(void) } kvm_info("%s@%llx IRQ%d\n", vgic_node->name, - vctrl_res.start, vgic_maint_irq); + vctrl_res.start, vgic.maint_irq); on_each_cpu(vgic_init_maintenance_interrupt, NULL, 1); if (of_address_to_resource(vgic_node, 3, &vcpu_res)) { @@ -1741,14 +1737,14 @@ int kvm_vgic_hyp_init(void) ret = -ENXIO; goto out_unmap; } - vgic_vcpu_base = vcpu_res.start; + vgic.vcpu_base = vcpu_res.start; goto out; out_unmap: - iounmap(vgic_vctrl_base); + iounmap(vgic.vctrl_base); out_free_irq: - free_percpu_irq(vgic_maint_irq, kvm_get_running_vcpus()); + free_percpu_irq(vgic.maint_irq, kvm_get_running_vcpus()); out: of_node_put(vgic_node); return ret; @@ -1783,7 +1779,7 @@ int kvm_vgic_init(struct kvm *kvm) } ret = kvm_phys_addr_ioremap(kvm, kvm->arch.vgic.vgic_cpu_base, - vgic_vcpu_base, KVM_VGIC_V2_CPU_SIZE); + vgic.vcpu_base, KVM_VGIC_V2_CPU_SIZE); if (ret) { kvm_err("Unable to remap VGIC CPU to VCPU\n"); goto out; @@ -1829,7 +1825,7 @@ int kvm_vgic_create(struct kvm *kvm) } spin_lock_init(&kvm->arch.vgic.lock); - kvm->arch.vgic.vctrl_base = vgic_vctrl_base; + kvm->arch.vgic.vctrl_base = vgic.vctrl_base; kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF; kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF; -- cgit v1.2.3 From 8f186d522c69bb18dd9b93a634da4953228c67d4 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 4 Feb 2014 18:13:03 +0000 Subject: KVM: ARM: vgic: split GICv2 backend from the main vgic code Brutally hack the innocent vgic code, and move the GICv2 specific code to its own file, using vgic_ops and vgic_params as a way to pass information between the two blocks. Acked-by: Catalin Marinas Reviewed-by: Christoffer Dall Signed-off-by: Marc Zyngier --- arch/arm/kvm/Makefile | 1 + arch/arm64/kvm/Makefile | 2 +- include/kvm/arm_vgic.h | 11 +- virt/kvm/arm/vgic-v2.c | 248 ++++++++++++++++++++++++++++++++++++++++++++ virt/kvm/arm/vgic.c | 267 +++++++++--------------------------------------- 5 files changed, 304 insertions(+), 225 deletions(-) create mode 100644 virt/kvm/arm/vgic-v2.c (limited to 'include') diff --git a/arch/arm/kvm/Makefile b/arch/arm/kvm/Makefile index 789bca9e64a7..f7057ed045b6 100644 --- a/arch/arm/kvm/Makefile +++ b/arch/arm/kvm/Makefile @@ -21,4 +21,5 @@ obj-y += kvm-arm.o init.o interrupts.o obj-y += arm.o handle_exit.o guest.o mmu.o emulate.o reset.o obj-y += coproc.o coproc_a15.o coproc_a7.o mmio.o psci.o perf.o obj-$(CONFIG_KVM_ARM_VGIC) += $(KVM)/arm/vgic.o +obj-$(CONFIG_KVM_ARM_VGIC) += $(KVM)/arm/vgic-v2.o obj-$(CONFIG_KVM_ARM_TIMER) += $(KVM)/arm/arch_timer.o diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile index 72a9fd583ad3..7e92952d139e 100644 --- a/arch/arm64/kvm/Makefile +++ b/arch/arm64/kvm/Makefile @@ -19,5 +19,5 @@ kvm-$(CONFIG_KVM_ARM_HOST) += emulate.o inject_fault.o regmap.o kvm-$(CONFIG_KVM_ARM_HOST) += hyp.o hyp-init.o handle_exit.o kvm-$(CONFIG_KVM_ARM_HOST) += guest.o reset.o sys_regs.o sys_regs_generic_v8.o -kvm-$(CONFIG_KVM_ARM_VGIC) += $(KVM)/arm/vgic.o +kvm-$(CONFIG_KVM_ARM_VGIC) += $(KVM)/arm/vgic.o $(KVM)/arm/vgic-v2.o kvm-$(CONFIG_KVM_ARM_TIMER) += $(KVM)/arm/arch_timer.o diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index ce2e14226dbf..d8d52a9ca6a1 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -32,7 +32,8 @@ #define VGIC_NR_PRIVATE_IRQS (VGIC_NR_SGIS + VGIC_NR_PPIS) #define VGIC_NR_SHARED_IRQS (VGIC_NR_IRQS - VGIC_NR_PRIVATE_IRQS) #define VGIC_MAX_CPUS KVM_MAX_VCPUS -#define VGIC_MAX_LRS (1 << 6) + +#define VGIC_V2_MAX_LRS (1 << 6) /* Sanity checks... */ #if (VGIC_MAX_CPUS > 8) @@ -162,7 +163,7 @@ struct vgic_v2_cpu_if { u32 vgic_eisr[2]; /* Saved only */ u32 vgic_elrsr[2]; /* Saved only */ u32 vgic_apr; - u32 vgic_lr[VGIC_MAX_LRS]; + u32 vgic_lr[VGIC_V2_MAX_LRS]; }; struct vgic_cpu { @@ -175,7 +176,7 @@ struct vgic_cpu { DECLARE_BITMAP( pending_shared, VGIC_NR_SHARED_IRQS); /* Bitmap of used/free list registers */ - DECLARE_BITMAP( lr_used, VGIC_MAX_LRS); + DECLARE_BITMAP( lr_used, VGIC_V2_MAX_LRS); /* Number of list registers on this CPU */ int nr_lr; @@ -214,6 +215,10 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, #define irqchip_in_kernel(k) (!!((k)->arch.vgic.vctrl_base)) #define vgic_initialized(k) ((k)->arch.vgic.ready) +int vgic_v2_probe(struct device_node *vgic_node, + const struct vgic_ops **ops, + const struct vgic_params **params); + #else static inline int kvm_vgic_hyp_init(void) { diff --git a/virt/kvm/arm/vgic-v2.c b/virt/kvm/arm/vgic-v2.c new file mode 100644 index 000000000000..940418ebd0d0 --- /dev/null +++ b/virt/kvm/arm/vgic-v2.c @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2012,2013 ARM Limited, All Rights Reserved. + * Author: Marc Zyngier + * + * 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 + +#include + +#include +#include +#include + +static struct vgic_lr vgic_v2_get_lr(const struct kvm_vcpu *vcpu, int lr) +{ + struct vgic_lr lr_desc; + u32 val = vcpu->arch.vgic_cpu.vgic_v2.vgic_lr[lr]; + + lr_desc.irq = val & GICH_LR_VIRTUALID; + if (lr_desc.irq <= 15) + lr_desc.source = (val >> GICH_LR_PHYSID_CPUID_SHIFT) & 0x7; + else + lr_desc.source = 0; + lr_desc.state = 0; + + if (val & GICH_LR_PENDING_BIT) + lr_desc.state |= LR_STATE_PENDING; + if (val & GICH_LR_ACTIVE_BIT) + lr_desc.state |= LR_STATE_ACTIVE; + if (val & GICH_LR_EOI) + lr_desc.state |= LR_EOI_INT; + + return lr_desc; +} + +static void vgic_v2_set_lr(struct kvm_vcpu *vcpu, int lr, + struct vgic_lr lr_desc) +{ + u32 lr_val = (lr_desc.source << GICH_LR_PHYSID_CPUID_SHIFT) | lr_desc.irq; + + if (lr_desc.state & LR_STATE_PENDING) + lr_val |= GICH_LR_PENDING_BIT; + if (lr_desc.state & LR_STATE_ACTIVE) + lr_val |= GICH_LR_ACTIVE_BIT; + if (lr_desc.state & LR_EOI_INT) + lr_val |= GICH_LR_EOI; + + vcpu->arch.vgic_cpu.vgic_v2.vgic_lr[lr] = lr_val; +} + +static void vgic_v2_sync_lr_elrsr(struct kvm_vcpu *vcpu, int lr, + struct vgic_lr lr_desc) +{ + if (!(lr_desc.state & LR_STATE_MASK)) + set_bit(lr, (unsigned long *)vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr); +} + +static u64 vgic_v2_get_elrsr(const struct kvm_vcpu *vcpu) +{ + u64 val; + +#if BITS_PER_LONG == 64 + val = vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr[1]; + val <<= 32; + val |= vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr[0]; +#else + val = *(u64 *)vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr; +#endif + return val; +} + +static u64 vgic_v2_get_eisr(const struct kvm_vcpu *vcpu) +{ + u64 val; + +#if BITS_PER_LONG == 64 + val = vcpu->arch.vgic_cpu.vgic_v2.vgic_eisr[1]; + val <<= 32; + val |= vcpu->arch.vgic_cpu.vgic_v2.vgic_eisr[0]; +#else + val = *(u64 *)vcpu->arch.vgic_cpu.vgic_v2.vgic_eisr; +#endif + return val; +} + +static u32 vgic_v2_get_interrupt_status(const struct kvm_vcpu *vcpu) +{ + u32 misr = vcpu->arch.vgic_cpu.vgic_v2.vgic_misr; + u32 ret = 0; + + if (misr & GICH_MISR_EOI) + ret |= INT_STATUS_EOI; + if (misr & GICH_MISR_U) + ret |= INT_STATUS_UNDERFLOW; + + return ret; +} + +static void vgic_v2_enable_underflow(struct kvm_vcpu *vcpu) +{ + vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr |= GICH_HCR_UIE; +} + +static void vgic_v2_disable_underflow(struct kvm_vcpu *vcpu) +{ + vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr &= ~GICH_HCR_UIE; +} + +static void vgic_v2_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp) +{ + u32 vmcr = vcpu->arch.vgic_cpu.vgic_v2.vgic_vmcr; + + vmcrp->ctlr = (vmcr & GICH_VMCR_CTRL_MASK) >> GICH_VMCR_CTRL_SHIFT; + vmcrp->abpr = (vmcr & GICH_VMCR_ALIAS_BINPOINT_MASK) >> GICH_VMCR_ALIAS_BINPOINT_SHIFT; + vmcrp->bpr = (vmcr & GICH_VMCR_BINPOINT_MASK) >> GICH_VMCR_BINPOINT_SHIFT; + vmcrp->pmr = (vmcr & GICH_VMCR_PRIMASK_MASK) >> GICH_VMCR_PRIMASK_SHIFT; +} + +static void vgic_v2_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp) +{ + u32 vmcr; + + vmcr = (vmcrp->ctlr << GICH_VMCR_CTRL_SHIFT) & GICH_VMCR_CTRL_MASK; + vmcr |= (vmcrp->abpr << GICH_VMCR_ALIAS_BINPOINT_SHIFT) & GICH_VMCR_ALIAS_BINPOINT_MASK; + vmcr |= (vmcrp->bpr << GICH_VMCR_BINPOINT_SHIFT) & GICH_VMCR_BINPOINT_MASK; + vmcr |= (vmcrp->pmr << GICH_VMCR_PRIMASK_SHIFT) & GICH_VMCR_PRIMASK_MASK; + + vcpu->arch.vgic_cpu.vgic_v2.vgic_vmcr = vmcr; +} + +static void vgic_v2_enable(struct kvm_vcpu *vcpu) +{ + /* + * By forcing VMCR to zero, the GIC will restore the binary + * points to their reset values. Anything else resets to zero + * anyway. + */ + vcpu->arch.vgic_cpu.vgic_v2.vgic_vmcr = 0; + + /* Get the show on the road... */ + vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr = GICH_HCR_EN; +} + +static const struct vgic_ops vgic_v2_ops = { + .get_lr = vgic_v2_get_lr, + .set_lr = vgic_v2_set_lr, + .sync_lr_elrsr = vgic_v2_sync_lr_elrsr, + .get_elrsr = vgic_v2_get_elrsr, + .get_eisr = vgic_v2_get_eisr, + .get_interrupt_status = vgic_v2_get_interrupt_status, + .enable_underflow = vgic_v2_enable_underflow, + .disable_underflow = vgic_v2_disable_underflow, + .get_vmcr = vgic_v2_get_vmcr, + .set_vmcr = vgic_v2_set_vmcr, + .enable = vgic_v2_enable, +}; + +static struct vgic_params vgic_v2_params; + +/** + * vgic_v2_probe - probe for a GICv2 compatible interrupt controller in DT + * @node: pointer to the DT node + * @ops: address of a pointer to the GICv2 operations + * @params: address of a pointer to HW-specific parameters + * + * Returns 0 if a GICv2 has been found, with the low level operations + * in *ops and the HW parameters in *params. Returns an error code + * otherwise. + */ +int vgic_v2_probe(struct device_node *vgic_node, + const struct vgic_ops **ops, + const struct vgic_params **params) +{ + int ret; + struct resource vctrl_res; + struct resource vcpu_res; + struct vgic_params *vgic = &vgic_v2_params; + + vgic->maint_irq = irq_of_parse_and_map(vgic_node, 0); + if (!vgic->maint_irq) { + kvm_err("error getting vgic maintenance irq from DT\n"); + ret = -ENXIO; + goto out; + } + + ret = of_address_to_resource(vgic_node, 2, &vctrl_res); + if (ret) { + kvm_err("Cannot obtain GICH resource\n"); + goto out; + } + + vgic->vctrl_base = of_iomap(vgic_node, 2); + if (!vgic->vctrl_base) { + kvm_err("Cannot ioremap GICH\n"); + ret = -ENOMEM; + goto out; + } + + vgic->nr_lr = readl_relaxed(vgic->vctrl_base + GICH_VTR); + vgic->nr_lr = (vgic->nr_lr & 0x3f) + 1; + + ret = create_hyp_io_mappings(vgic->vctrl_base, + vgic->vctrl_base + resource_size(&vctrl_res), + vctrl_res.start); + if (ret) { + kvm_err("Cannot map VCTRL into hyp\n"); + goto out_unmap; + } + + if (of_address_to_resource(vgic_node, 3, &vcpu_res)) { + kvm_err("Cannot obtain GICV resource\n"); + ret = -ENXIO; + goto out_unmap; + } + vgic->vcpu_base = vcpu_res.start; + + kvm_info("%s@%llx IRQ%d\n", vgic_node->name, + vctrl_res.start, vgic->maint_irq); + + *ops = &vgic_v2_ops; + *params = vgic; + goto out; + +out_unmap: + iounmap(vgic->vctrl_base); +out: + of_node_put(vgic_node); + return ret; +} diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c index f3a996d0a100..e4b9cbbbee4c 100644 --- a/virt/kvm/arm/vgic.c +++ b/virt/kvm/arm/vgic.c @@ -95,7 +95,8 @@ static void vgic_set_lr(struct kvm_vcpu *vcpu, int lr, struct vgic_lr lr_desc); static void vgic_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr); static void vgic_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr); -static struct vgic_params vgic; +static const struct vgic_ops *vgic_ops; +static const struct vgic_params *vgic; static u32 *vgic_bitmap_get_reg(struct vgic_bitmap *x, int cpuid, u32 offset) @@ -971,205 +972,61 @@ static void vgic_update_state(struct kvm *kvm) } } -static struct vgic_lr vgic_v2_get_lr(const struct kvm_vcpu *vcpu, int lr) -{ - struct vgic_lr lr_desc; - u32 val = vcpu->arch.vgic_cpu.vgic_v2.vgic_lr[lr]; - - lr_desc.irq = val & GICH_LR_VIRTUALID; - if (lr_desc.irq <= 15) - lr_desc.source = (val >> GICH_LR_PHYSID_CPUID_SHIFT) & 0x7; - else - lr_desc.source = 0; - lr_desc.state = 0; - - if (val & GICH_LR_PENDING_BIT) - lr_desc.state |= LR_STATE_PENDING; - if (val & GICH_LR_ACTIVE_BIT) - lr_desc.state |= LR_STATE_ACTIVE; - if (val & GICH_LR_EOI) - lr_desc.state |= LR_EOI_INT; - - return lr_desc; -} - -static void vgic_v2_set_lr(struct kvm_vcpu *vcpu, int lr, - struct vgic_lr lr_desc) -{ - u32 lr_val = (lr_desc.source << GICH_LR_PHYSID_CPUID_SHIFT) | lr_desc.irq; - - if (lr_desc.state & LR_STATE_PENDING) - lr_val |= GICH_LR_PENDING_BIT; - if (lr_desc.state & LR_STATE_ACTIVE) - lr_val |= GICH_LR_ACTIVE_BIT; - if (lr_desc.state & LR_EOI_INT) - lr_val |= GICH_LR_EOI; - - vcpu->arch.vgic_cpu.vgic_v2.vgic_lr[lr] = lr_val; -} - -static void vgic_v2_sync_lr_elrsr(struct kvm_vcpu *vcpu, int lr, - struct vgic_lr lr_desc) -{ - if (!(lr_desc.state & LR_STATE_MASK)) - set_bit(lr, (unsigned long *)vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr); -} - -static u64 vgic_v2_get_elrsr(const struct kvm_vcpu *vcpu) -{ - u64 val; - -#if BITS_PER_LONG == 64 - val = vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr[1]; - val <<= 32; - val |= vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr[0]; -#else - val = *(u64 *)vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr; -#endif - return val; -} - -static u64 vgic_v2_get_eisr(const struct kvm_vcpu *vcpu) -{ - u64 val; - -#if BITS_PER_LONG == 64 - val = vcpu->arch.vgic_cpu.vgic_v2.vgic_eisr[1]; - val <<= 32; - val |= vcpu->arch.vgic_cpu.vgic_v2.vgic_eisr[0]; -#else - val = *(u64 *)vcpu->arch.vgic_cpu.vgic_v2.vgic_eisr; -#endif - return val; -} - -static u32 vgic_v2_get_interrupt_status(const struct kvm_vcpu *vcpu) -{ - u32 misr = vcpu->arch.vgic_cpu.vgic_v2.vgic_misr; - u32 ret = 0; - - if (misr & GICH_MISR_EOI) - ret |= INT_STATUS_EOI; - if (misr & GICH_MISR_U) - ret |= INT_STATUS_UNDERFLOW; - - return ret; -} - -static void vgic_v2_enable_underflow(struct kvm_vcpu *vcpu) -{ - vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr |= GICH_HCR_UIE; -} - -static void vgic_v2_disable_underflow(struct kvm_vcpu *vcpu) -{ - vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr &= ~GICH_HCR_UIE; -} - -static void vgic_v2_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp) -{ - u32 vmcr = vcpu->arch.vgic_cpu.vgic_v2.vgic_vmcr; - - vmcrp->ctlr = (vmcr & GICH_VMCR_CTRL_MASK) >> GICH_VMCR_CTRL_SHIFT; - vmcrp->abpr = (vmcr & GICH_VMCR_ALIAS_BINPOINT_MASK) >> GICH_VMCR_ALIAS_BINPOINT_SHIFT; - vmcrp->bpr = (vmcr & GICH_VMCR_BINPOINT_MASK) >> GICH_VMCR_BINPOINT_SHIFT; - vmcrp->pmr = (vmcr & GICH_VMCR_PRIMASK_MASK) >> GICH_VMCR_PRIMASK_SHIFT; -} - -static void vgic_v2_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp) -{ - u32 vmcr; - - vmcr = (vmcrp->ctlr << GICH_VMCR_CTRL_SHIFT) & GICH_VMCR_CTRL_MASK; - vmcr |= (vmcrp->abpr << GICH_VMCR_ALIAS_BINPOINT_SHIFT) & GICH_VMCR_ALIAS_BINPOINT_MASK; - vmcr |= (vmcrp->bpr << GICH_VMCR_BINPOINT_SHIFT) & GICH_VMCR_BINPOINT_MASK; - vmcr |= (vmcrp->pmr << GICH_VMCR_PRIMASK_SHIFT) & GICH_VMCR_PRIMASK_MASK; - - vcpu->arch.vgic_cpu.vgic_v2.vgic_vmcr = vmcr; -} - -static void vgic_v2_enable(struct kvm_vcpu *vcpu) -{ - /* - * By forcing VMCR to zero, the GIC will restore the binary - * points to their reset values. Anything else resets to zero - * anyway. - */ - vcpu->arch.vgic_cpu.vgic_v2.vgic_vmcr = 0; - - /* Get the show on the road... */ - vcpu->arch.vgic_cpu.vgic_v2.vgic_hcr = GICH_HCR_EN; -} - -static const struct vgic_ops vgic_ops = { - .get_lr = vgic_v2_get_lr, - .set_lr = vgic_v2_set_lr, - .sync_lr_elrsr = vgic_v2_sync_lr_elrsr, - .get_elrsr = vgic_v2_get_elrsr, - .get_eisr = vgic_v2_get_eisr, - .get_interrupt_status = vgic_v2_get_interrupt_status, - .enable_underflow = vgic_v2_enable_underflow, - .disable_underflow = vgic_v2_disable_underflow, - .get_vmcr = vgic_v2_get_vmcr, - .set_vmcr = vgic_v2_set_vmcr, - .enable = vgic_v2_enable, -}; - static struct vgic_lr vgic_get_lr(const struct kvm_vcpu *vcpu, int lr) { - return vgic_ops.get_lr(vcpu, lr); + return vgic_ops->get_lr(vcpu, lr); } static void vgic_set_lr(struct kvm_vcpu *vcpu, int lr, struct vgic_lr vlr) { - vgic_ops.set_lr(vcpu, lr, vlr); + vgic_ops->set_lr(vcpu, lr, vlr); } static void vgic_sync_lr_elrsr(struct kvm_vcpu *vcpu, int lr, struct vgic_lr vlr) { - vgic_ops.sync_lr_elrsr(vcpu, lr, vlr); + vgic_ops->sync_lr_elrsr(vcpu, lr, vlr); } static inline u64 vgic_get_elrsr(struct kvm_vcpu *vcpu) { - return vgic_ops.get_elrsr(vcpu); + return vgic_ops->get_elrsr(vcpu); } static inline u64 vgic_get_eisr(struct kvm_vcpu *vcpu) { - return vgic_ops.get_eisr(vcpu); + return vgic_ops->get_eisr(vcpu); } static inline u32 vgic_get_interrupt_status(struct kvm_vcpu *vcpu) { - return vgic_ops.get_interrupt_status(vcpu); + return vgic_ops->get_interrupt_status(vcpu); } static inline void vgic_enable_underflow(struct kvm_vcpu *vcpu) { - vgic_ops.enable_underflow(vcpu); + vgic_ops->enable_underflow(vcpu); } static inline void vgic_disable_underflow(struct kvm_vcpu *vcpu) { - vgic_ops.disable_underflow(vcpu); + vgic_ops->disable_underflow(vcpu); } static inline void vgic_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr) { - vgic_ops.get_vmcr(vcpu, vmcr); + vgic_ops->get_vmcr(vcpu, vmcr); } static void vgic_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr) { - vgic_ops.set_vmcr(vcpu, vmcr); + vgic_ops->set_vmcr(vcpu, vmcr); } static inline void vgic_enable(struct kvm_vcpu *vcpu) { - vgic_ops.enable(vcpu); + vgic_ops->enable(vcpu); } static void vgic_retire_lr(int lr_nr, int irq, struct kvm_vcpu *vcpu) @@ -1197,7 +1054,7 @@ static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu) struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; int lr; - for_each_set_bit(lr, vgic_cpu->lr_used, vgic.nr_lr) { + for_each_set_bit(lr, vgic_cpu->lr_used, vgic->nr_lr) { struct vgic_lr vlr = vgic_get_lr(vcpu, lr); if (!vgic_irq_is_enabled(vcpu, vlr.irq)) { @@ -1241,8 +1098,8 @@ static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq) /* Try to use another LR for this interrupt */ lr = find_first_zero_bit((unsigned long *)vgic_cpu->lr_used, - vgic.nr_lr); - if (lr >= vgic.nr_lr) + vgic->nr_lr); + if (lr >= vgic->nr_lr) return false; kvm_debug("LR%d allocated for IRQ%d %x\n", lr, irq, sgi_source_id); @@ -1382,7 +1239,7 @@ static bool vgic_process_maintenance(struct kvm_vcpu *vcpu) unsigned long *eisr_ptr = (unsigned long *)&eisr; int lr; - for_each_set_bit(lr, eisr_ptr, vgic.nr_lr) { + for_each_set_bit(lr, eisr_ptr, vgic->nr_lr) { struct vgic_lr vlr = vgic_get_lr(vcpu, lr); vgic_irq_clear_active(vcpu, vlr.irq); @@ -1430,7 +1287,7 @@ static void __kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu) elrsr_ptr = (unsigned long *)&elrsr; /* Clear mappings for empty LRs */ - for_each_set_bit(lr, elrsr_ptr, vgic.nr_lr) { + for_each_set_bit(lr, elrsr_ptr, vgic->nr_lr) { struct vgic_lr vlr; if (!test_and_clear_bit(lr, vgic_cpu->lr_used)) @@ -1443,8 +1300,8 @@ static void __kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu) } /* Check if we still have something up our sleeve... */ - pending = find_first_zero_bit(elrsr_ptr, vgic.nr_lr); - if (level_pending || pending < vgic.nr_lr) + pending = find_first_zero_bit(elrsr_ptr, vgic->nr_lr); + if (level_pending || pending < vgic->nr_lr) set_bit(vcpu->vcpu_id, &dist->irq_pending_on_cpu); } @@ -1638,7 +1495,7 @@ int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu) * all the way to the distributor structure to find out. Only * assembly code should use this one. */ - vgic_cpu->nr_lr = vgic.nr_lr; + vgic_cpu->nr_lr = vgic->nr_lr; vgic_enable(vcpu); @@ -1647,7 +1504,7 @@ int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu) static void vgic_init_maintenance_interrupt(void *info) { - enable_percpu_irq(vgic.maint_irq, 0); + enable_percpu_irq(vgic->maint_irq, 0); } static int vgic_cpu_notify(struct notifier_block *self, @@ -1660,7 +1517,7 @@ static int vgic_cpu_notify(struct notifier_block *self, break; case CPU_DYING: case CPU_DYING_FROZEN: - disable_percpu_irq(vgic.maint_irq); + disable_percpu_irq(vgic->maint_irq); break; } @@ -1671,31 +1528,36 @@ static struct notifier_block vgic_cpu_nb = { .notifier_call = vgic_cpu_notify, }; +static const struct of_device_id vgic_ids[] = { + { .compatible = "arm,cortex-a15-gic", .data = vgic_v2_probe, }, + {}, +}; + int kvm_vgic_hyp_init(void) { - int ret; - struct resource vctrl_res; - struct resource vcpu_res; + const struct of_device_id *matched_id; + int (*vgic_probe)(struct device_node *,const struct vgic_ops **, + const struct vgic_params **); struct device_node *vgic_node; + int ret; - vgic_node = of_find_compatible_node(NULL, NULL, "arm,cortex-a15-gic"); + vgic_node = of_find_matching_node_and_match(NULL, + vgic_ids, &matched_id); if (!vgic_node) { - kvm_err("error: no compatible vgic node in DT\n"); + kvm_err("error: no compatible GIC node found\n"); return -ENODEV; } - vgic.maint_irq = irq_of_parse_and_map(vgic_node, 0); - if (!vgic.maint_irq) { - kvm_err("error getting vgic maintenance irq from DT\n"); - ret = -ENXIO; - goto out; - } + vgic_probe = matched_id->data; + ret = vgic_probe(vgic_node, &vgic_ops, &vgic); + if (ret) + return ret; - ret = request_percpu_irq(vgic.maint_irq, vgic_maintenance_handler, + ret = request_percpu_irq(vgic->maint_irq, vgic_maintenance_handler, "vgic", kvm_get_running_vcpus()); if (ret) { - kvm_err("Cannot register interrupt %d\n", vgic.maint_irq); - goto out; + kvm_err("Cannot register interrupt %d\n", vgic->maint_irq); + return ret; } ret = __register_cpu_notifier(&vgic_cpu_nb); @@ -1704,49 +1566,12 @@ int kvm_vgic_hyp_init(void) goto out_free_irq; } - ret = of_address_to_resource(vgic_node, 2, &vctrl_res); - if (ret) { - kvm_err("Cannot obtain VCTRL resource\n"); - goto out_free_irq; - } - - vgic.vctrl_base = of_iomap(vgic_node, 2); - if (!vgic.vctrl_base) { - kvm_err("Cannot ioremap VCTRL\n"); - ret = -ENOMEM; - goto out_free_irq; - } - - vgic.nr_lr = readl_relaxed(vgic.vctrl_base + GICH_VTR); - vgic.nr_lr = (vgic.nr_lr & 0x3f) + 1; - - ret = create_hyp_io_mappings(vgic.vctrl_base, - vgic.vctrl_base + resource_size(&vctrl_res), - vctrl_res.start); - if (ret) { - kvm_err("Cannot map VCTRL into hyp\n"); - goto out_unmap; - } - - kvm_info("%s@%llx IRQ%d\n", vgic_node->name, - vctrl_res.start, vgic.maint_irq); on_each_cpu(vgic_init_maintenance_interrupt, NULL, 1); - if (of_address_to_resource(vgic_node, 3, &vcpu_res)) { - kvm_err("Cannot obtain VCPU resource\n"); - ret = -ENXIO; - goto out_unmap; - } - vgic.vcpu_base = vcpu_res.start; - - goto out; + return 0; -out_unmap: - iounmap(vgic.vctrl_base); out_free_irq: - free_percpu_irq(vgic.maint_irq, kvm_get_running_vcpus()); -out: - of_node_put(vgic_node); + free_percpu_irq(vgic->maint_irq, kvm_get_running_vcpus()); return ret; } @@ -1779,7 +1604,7 @@ int kvm_vgic_init(struct kvm *kvm) } ret = kvm_phys_addr_ioremap(kvm, kvm->arch.vgic.vgic_cpu_base, - vgic.vcpu_base, KVM_VGIC_V2_CPU_SIZE); + vgic->vcpu_base, KVM_VGIC_V2_CPU_SIZE); if (ret) { kvm_err("Unable to remap VGIC CPU to VCPU\n"); goto out; @@ -1825,7 +1650,7 @@ int kvm_vgic_create(struct kvm *kvm) } spin_lock_init(&kvm->arch.vgic.lock); - kvm->arch.vgic.vctrl_base = vgic.vctrl_base; + kvm->arch.vgic.vctrl_base = vgic->vctrl_base; kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF; kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF; -- cgit v1.2.3 From f982cf4e9c37b19478c7bc6e0484a43a7e78cf57 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Thu, 15 May 2014 10:03:25 +0100 Subject: KVM: ARM: vgic: revisit implementation of irqchip_in_kernel So far, irqchip_in_kernel() was implemented by testing the value of vctrl_base, which worked fine with GICv2. With GICv3, this field is useless, as we're using system registers instead of a emmory mapped interface. To solve this, add a boolean flag indicating if the we're using a vgic or not. Reviewed-by: Christoffer Dall Signed-off-by: Marc Zyngier --- include/kvm/arm_vgic.h | 3 ++- virt/kvm/arm/vgic.c | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index d8d52a9ca6a1..f6b9fec6fcac 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -117,6 +117,7 @@ struct vgic_params { struct vgic_dist { #ifdef CONFIG_KVM_ARM_VGIC spinlock_t lock; + bool in_kernel; bool ready; /* Virtual control interface mapping */ @@ -212,7 +213,7 @@ int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu); bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exit_mmio *mmio); -#define irqchip_in_kernel(k) (!!((k)->arch.vgic.vctrl_base)) +#define irqchip_in_kernel(k) (!!((k)->arch.vgic.in_kernel)) #define vgic_initialized(k) ((k)->arch.vgic.ready) int vgic_v2_probe(struct device_node *vgic_node, diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c index e4b9cbbbee4c..1348e74c4d11 100644 --- a/virt/kvm/arm/vgic.c +++ b/virt/kvm/arm/vgic.c @@ -1650,6 +1650,7 @@ int kvm_vgic_create(struct kvm *kvm) } spin_lock_init(&kvm->arch.vgic.lock); + kvm->arch.vgic.in_kernel = true; kvm->arch.vgic.vctrl_base = vgic->vctrl_base; kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF; kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF; -- cgit v1.2.3 From 1a9b13056dde7e3092304d6041ccc60a913042ea Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 21 Jun 2013 11:57:56 +0100 Subject: arm64: KVM: split GICv2 world switch from hyp code Move the GICv2 world switch code into its own file, and add the necessary indirection to the arm64 switch code. Also introduce a new type field to the vgic_params structure. Acked-by: Catalin Marinas Reviewed-by: Christoffer Dall Signed-off-by: Marc Zyngier --- arch/arm/include/asm/kvm_host.h | 5 ++ arch/arm64/include/asm/kvm_asm.h | 4 ++ arch/arm64/include/asm/kvm_host.h | 21 ++++++ arch/arm64/kernel/asm-offsets.c | 3 + arch/arm64/kvm/Makefile | 4 +- arch/arm64/kvm/hyp.S | 104 +++++------------------------ arch/arm64/kvm/vgic-v2-switch.S | 133 ++++++++++++++++++++++++++++++++++++++ include/kvm/arm_vgic.h | 7 +- virt/kvm/arm/vgic-v2.c | 1 + virt/kvm/arm/vgic.c | 3 + 10 files changed, 195 insertions(+), 90 deletions(-) create mode 100644 arch/arm64/kvm/vgic-v2-switch.S (limited to 'include') diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h index dc4e3edf39cc..6dfb404f6c46 100644 --- a/arch/arm/include/asm/kvm_host.h +++ b/arch/arm/include/asm/kvm_host.h @@ -225,6 +225,11 @@ static inline int kvm_arch_dev_ioctl_check_extension(long ext) return 0; } +static inline void vgic_arch_setup(const struct vgic_params *vgic) +{ + BUG_ON(vgic->type != VGIC_V2); +} + int kvm_perf_init(void); int kvm_perf_teardown(void); diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h index d0bfc4ba82c0..6252264341c8 100644 --- a/arch/arm64/include/asm/kvm_asm.h +++ b/arch/arm64/include/asm/kvm_asm.h @@ -105,6 +105,10 @@ extern void __kvm_flush_vm_context(void); extern void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa); extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu); + +extern char __save_vgic_v2_state[]; +extern char __restore_vgic_v2_state[]; + #endif #endif /* __ARM_KVM_ASM_H__ */ diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 92242ce06309..4c182d0aae70 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -200,4 +200,25 @@ static inline void __cpu_init_hyp_mode(phys_addr_t boot_pgd_ptr, hyp_stack_ptr, vector_ptr); } +struct vgic_sr_vectors { + void *save_vgic; + void *restore_vgic; +}; + +static inline void vgic_arch_setup(const struct vgic_params *vgic) +{ + extern struct vgic_sr_vectors __vgic_sr_vectors; + + switch(vgic->type) + { + case VGIC_V2: + __vgic_sr_vectors.save_vgic = __save_vgic_v2_state; + __vgic_sr_vectors.restore_vgic = __restore_vgic_v2_state; + break; + + default: + BUG(); + } +} + #endif /* __ARM64_KVM_HOST_H__ */ diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c index 20fd4887aab6..dafc415dd52d 100644 --- a/arch/arm64/kernel/asm-offsets.c +++ b/arch/arm64/kernel/asm-offsets.c @@ -129,6 +129,9 @@ int main(void) DEFINE(KVM_TIMER_ENABLED, offsetof(struct kvm, arch.timer.enabled)); DEFINE(VCPU_KVM, offsetof(struct kvm_vcpu, kvm)); DEFINE(VCPU_VGIC_CPU, offsetof(struct kvm_vcpu, arch.vgic_cpu)); + DEFINE(VGIC_SAVE_FN, offsetof(struct vgic_sr_vectors, save_vgic)); + DEFINE(VGIC_RESTORE_FN, offsetof(struct vgic_sr_vectors, restore_vgic)); + DEFINE(VGIC_SR_VECTOR_SZ, sizeof(struct vgic_sr_vectors)); DEFINE(VGIC_V2_CPU_HCR, offsetof(struct vgic_cpu, vgic_v2.vgic_hcr)); DEFINE(VGIC_V2_CPU_VMCR, offsetof(struct vgic_cpu, vgic_v2.vgic_vmcr)); DEFINE(VGIC_V2_CPU_MISR, offsetof(struct vgic_cpu, vgic_v2.vgic_misr)); diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile index 7e92952d139e..daf24dc59e2c 100644 --- a/arch/arm64/kvm/Makefile +++ b/arch/arm64/kvm/Makefile @@ -19,5 +19,7 @@ kvm-$(CONFIG_KVM_ARM_HOST) += emulate.o inject_fault.o regmap.o kvm-$(CONFIG_KVM_ARM_HOST) += hyp.o hyp-init.o handle_exit.o kvm-$(CONFIG_KVM_ARM_HOST) += guest.o reset.o sys_regs.o sys_regs_generic_v8.o -kvm-$(CONFIG_KVM_ARM_VGIC) += $(KVM)/arm/vgic.o $(KVM)/arm/vgic-v2.o +kvm-$(CONFIG_KVM_ARM_VGIC) += $(KVM)/arm/vgic.o +kvm-$(CONFIG_KVM_ARM_VGIC) += $(KVM)/arm/vgic-v2.o +kvm-$(CONFIG_KVM_ARM_VGIC) += vgic-v2-switch.o kvm-$(CONFIG_KVM_ARM_TIMER) += $(KVM)/arm/arch_timer.o diff --git a/arch/arm64/kvm/hyp.S b/arch/arm64/kvm/hyp.S index 9c5d0acb3654..56df9a352a81 100644 --- a/arch/arm64/kvm/hyp.S +++ b/arch/arm64/kvm/hyp.S @@ -16,7 +16,6 @@ */ #include -#include #include #include @@ -376,100 +375,23 @@ .endm /* - * Save the VGIC CPU state into memory - * x0: Register pointing to VCPU struct - * Do not corrupt x1!!! + * Call into the vgic backend for state saving */ .macro save_vgic_state - /* Get VGIC VCTRL base into x2 */ - ldr x2, [x0, #VCPU_KVM] - kern_hyp_va x2 - ldr x2, [x2, #KVM_VGIC_VCTRL] - kern_hyp_va x2 - cbz x2, 2f // disabled - - /* Compute the address of struct vgic_cpu */ - add x3, x0, #VCPU_VGIC_CPU - - /* Save all interesting registers */ - ldr w4, [x2, #GICH_HCR] - ldr w5, [x2, #GICH_VMCR] - ldr w6, [x2, #GICH_MISR] - ldr w7, [x2, #GICH_EISR0] - ldr w8, [x2, #GICH_EISR1] - ldr w9, [x2, #GICH_ELRSR0] - ldr w10, [x2, #GICH_ELRSR1] - ldr w11, [x2, #GICH_APR] -CPU_BE( rev w4, w4 ) -CPU_BE( rev w5, w5 ) -CPU_BE( rev w6, w6 ) -CPU_BE( rev w7, w7 ) -CPU_BE( rev w8, w8 ) -CPU_BE( rev w9, w9 ) -CPU_BE( rev w10, w10 ) -CPU_BE( rev w11, w11 ) - - str w4, [x3, #VGIC_V2_CPU_HCR] - str w5, [x3, #VGIC_V2_CPU_VMCR] - str w6, [x3, #VGIC_V2_CPU_MISR] - str w7, [x3, #VGIC_V2_CPU_EISR] - str w8, [x3, #(VGIC_V2_CPU_EISR + 4)] - str w9, [x3, #VGIC_V2_CPU_ELRSR] - str w10, [x3, #(VGIC_V2_CPU_ELRSR + 4)] - str w11, [x3, #VGIC_V2_CPU_APR] - - /* Clear GICH_HCR */ - str wzr, [x2, #GICH_HCR] - - /* Save list registers */ - add x2, x2, #GICH_LR0 - ldr w4, [x3, #VGIC_CPU_NR_LR] - add x3, x3, #VGIC_V2_CPU_LR -1: ldr w5, [x2], #4 -CPU_BE( rev w5, w5 ) - str w5, [x3], #4 - sub w4, w4, #1 - cbnz w4, 1b -2: + adr x24, __vgic_sr_vectors + ldr x24, [x24, VGIC_SAVE_FN] + kern_hyp_va x24 + blr x24 .endm /* - * Restore the VGIC CPU state from memory - * x0: Register pointing to VCPU struct + * Call into the vgic backend for state restoring */ .macro restore_vgic_state - /* Get VGIC VCTRL base into x2 */ - ldr x2, [x0, #VCPU_KVM] - kern_hyp_va x2 - ldr x2, [x2, #KVM_VGIC_VCTRL] - kern_hyp_va x2 - cbz x2, 2f // disabled - - /* Compute the address of struct vgic_cpu */ - add x3, x0, #VCPU_VGIC_CPU - - /* We only restore a minimal set of registers */ - ldr w4, [x3, #VGIC_V2_CPU_HCR] - ldr w5, [x3, #VGIC_V2_CPU_VMCR] - ldr w6, [x3, #VGIC_V2_CPU_APR] -CPU_BE( rev w4, w4 ) -CPU_BE( rev w5, w5 ) -CPU_BE( rev w6, w6 ) - - str w4, [x2, #GICH_HCR] - str w5, [x2, #GICH_VMCR] - str w6, [x2, #GICH_APR] - - /* Restore list registers */ - add x2, x2, #GICH_LR0 - ldr w4, [x3, #VGIC_CPU_NR_LR] - add x3, x3, #VGIC_V2_CPU_LR -1: ldr w5, [x3], #4 -CPU_BE( rev w5, w5 ) - str w5, [x2], #4 - sub w4, w4, #1 - cbnz w4, 1b -2: + adr x24, __vgic_sr_vectors + ldr x24, [x24, #VGIC_RESTORE_FN] + kern_hyp_va x24 + blr x24 .endm .macro save_timer_state @@ -650,6 +572,12 @@ ENTRY(__kvm_flush_vm_context) ret ENDPROC(__kvm_flush_vm_context) + // struct vgic_sr_vectors __vgi_sr_vectors; + .align 3 +ENTRY(__vgic_sr_vectors) + .skip VGIC_SR_VECTOR_SZ +ENDPROC(__vgic_sr_vectors) + __kvm_hyp_panic: // Guess the context by looking at VTTBR: // If zero, then we're already a host. diff --git a/arch/arm64/kvm/vgic-v2-switch.S b/arch/arm64/kvm/vgic-v2-switch.S new file mode 100644 index 000000000000..ae211772f991 --- /dev/null +++ b/arch/arm64/kvm/vgic-v2-switch.S @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2012,2013 - ARM Ltd + * Author: Marc Zyngier + * + * 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 +#include + + .text + .pushsection .hyp.text, "ax" + +/* + * Save the VGIC CPU state into memory + * x0: Register pointing to VCPU struct + * Do not corrupt x1!!! + */ +ENTRY(__save_vgic_v2_state) +__save_vgic_v2_state: + /* Get VGIC VCTRL base into x2 */ + ldr x2, [x0, #VCPU_KVM] + kern_hyp_va x2 + ldr x2, [x2, #KVM_VGIC_VCTRL] + kern_hyp_va x2 + cbz x2, 2f // disabled + + /* Compute the address of struct vgic_cpu */ + add x3, x0, #VCPU_VGIC_CPU + + /* Save all interesting registers */ + ldr w4, [x2, #GICH_HCR] + ldr w5, [x2, #GICH_VMCR] + ldr w6, [x2, #GICH_MISR] + ldr w7, [x2, #GICH_EISR0] + ldr w8, [x2, #GICH_EISR1] + ldr w9, [x2, #GICH_ELRSR0] + ldr w10, [x2, #GICH_ELRSR1] + ldr w11, [x2, #GICH_APR] +CPU_BE( rev w4, w4 ) +CPU_BE( rev w5, w5 ) +CPU_BE( rev w6, w6 ) +CPU_BE( rev w7, w7 ) +CPU_BE( rev w8, w8 ) +CPU_BE( rev w9, w9 ) +CPU_BE( rev w10, w10 ) +CPU_BE( rev w11, w11 ) + + str w4, [x3, #VGIC_V2_CPU_HCR] + str w5, [x3, #VGIC_V2_CPU_VMCR] + str w6, [x3, #VGIC_V2_CPU_MISR] + str w7, [x3, #VGIC_V2_CPU_EISR] + str w8, [x3, #(VGIC_V2_CPU_EISR + 4)] + str w9, [x3, #VGIC_V2_CPU_ELRSR] + str w10, [x3, #(VGIC_V2_CPU_ELRSR + 4)] + str w11, [x3, #VGIC_V2_CPU_APR] + + /* Clear GICH_HCR */ + str wzr, [x2, #GICH_HCR] + + /* Save list registers */ + add x2, x2, #GICH_LR0 + ldr w4, [x3, #VGIC_CPU_NR_LR] + add x3, x3, #VGIC_V2_CPU_LR +1: ldr w5, [x2], #4 +CPU_BE( rev w5, w5 ) + str w5, [x3], #4 + sub w4, w4, #1 + cbnz w4, 1b +2: + ret +ENDPROC(__save_vgic_v2_state) + +/* + * Restore the VGIC CPU state from memory + * x0: Register pointing to VCPU struct + */ +ENTRY(__restore_vgic_v2_state) +__restore_vgic_v2_state: + /* Get VGIC VCTRL base into x2 */ + ldr x2, [x0, #VCPU_KVM] + kern_hyp_va x2 + ldr x2, [x2, #KVM_VGIC_VCTRL] + kern_hyp_va x2 + cbz x2, 2f // disabled + + /* Compute the address of struct vgic_cpu */ + add x3, x0, #VCPU_VGIC_CPU + + /* We only restore a minimal set of registers */ + ldr w4, [x3, #VGIC_V2_CPU_HCR] + ldr w5, [x3, #VGIC_V2_CPU_VMCR] + ldr w6, [x3, #VGIC_V2_CPU_APR] +CPU_BE( rev w4, w4 ) +CPU_BE( rev w5, w5 ) +CPU_BE( rev w6, w6 ) + + str w4, [x2, #GICH_HCR] + str w5, [x2, #GICH_VMCR] + str w6, [x2, #GICH_APR] + + /* Restore list registers */ + add x2, x2, #GICH_LR0 + ldr w4, [x3, #VGIC_CPU_NR_LR] + add x3, x3, #VGIC_V2_CPU_LR +1: ldr w5, [x3], #4 +CPU_BE( rev w5, w5 ) + str w5, [x2], #4 + sub w4, w4, #1 + cbnz w4, 1b +2: + ret +ENDPROC(__restore_vgic_v2_state) + + .popsection diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index f6b9fec6fcac..65f1121a3beb 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -24,7 +24,6 @@ #include #include #include -#include #define VGIC_NR_IRQS 256 #define VGIC_NR_SGIS 16 @@ -71,6 +70,10 @@ struct vgic_bytemap { struct kvm_vcpu; +enum vgic_type { + VGIC_V2, /* Good ol' GICv2 */ +}; + #define LR_STATE_PENDING (1 << 0) #define LR_STATE_ACTIVE (1 << 1) #define LR_STATE_MASK (3 << 0) @@ -104,6 +107,8 @@ struct vgic_ops { }; struct vgic_params { + /* vgic type */ + enum vgic_type type; /* Physical address of vgic virtual cpu interface */ phys_addr_t vcpu_base; /* Number of list registers */ diff --git a/virt/kvm/arm/vgic-v2.c b/virt/kvm/arm/vgic-v2.c index 940418ebd0d0..d6c9c142f813 100644 --- a/virt/kvm/arm/vgic-v2.c +++ b/virt/kvm/arm/vgic-v2.c @@ -236,6 +236,7 @@ int vgic_v2_probe(struct device_node *vgic_node, kvm_info("%s@%llx IRQ%d\n", vgic_node->name, vctrl_res.start, vgic->maint_irq); + vgic->type = VGIC_V2; *ops = &vgic_v2_ops; *params = vgic; goto out; diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c index 1348e74c4d11..7867b9a1f694 100644 --- a/virt/kvm/arm/vgic.c +++ b/virt/kvm/arm/vgic.c @@ -1568,6 +1568,9 @@ int kvm_vgic_hyp_init(void) on_each_cpu(vgic_init_maintenance_interrupt, NULL, 1); + /* Callback into for arch code for setup */ + vgic_arch_setup(vgic); + return 0; out_free_irq: -- cgit v1.2.3 From b2fb1c0d378399e1427a91bb991c094f2ca09a2f Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 12 Jul 2013 15:15:23 +0100 Subject: KVM: ARM: vgic: add the GICv3 backend Introduce the support code for emulating a GICv2 on top of GICv3 hardware. Acked-by: Catalin Marinas Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_asm.h | 2 + arch/arm64/kvm/vgic-v3-switch.S | 29 +++++ include/kvm/arm_vgic.h | 28 +++++ virt/kvm/arm/vgic-v3.c | 231 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 290 insertions(+) create mode 100644 arch/arm64/kvm/vgic-v3-switch.S create mode 100644 virt/kvm/arm/vgic-v3.c (limited to 'include') diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h index 6252264341c8..ed4987bf9ac7 100644 --- a/arch/arm64/include/asm/kvm_asm.h +++ b/arch/arm64/include/asm/kvm_asm.h @@ -106,6 +106,8 @@ extern void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa); extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu); +extern u64 __vgic_v3_get_ich_vtr_el2(void); + extern char __save_vgic_v2_state[]; extern char __restore_vgic_v2_state[]; diff --git a/arch/arm64/kvm/vgic-v3-switch.S b/arch/arm64/kvm/vgic-v3-switch.S new file mode 100644 index 000000000000..9fbf27350c84 --- /dev/null +++ b/arch/arm64/kvm/vgic-v3-switch.S @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2012,2013 - ARM Ltd + * Author: Marc Zyngier + * + * 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 + + .text + .pushsection .hyp.text, "ax" + +ENTRY(__vgic_v3_get_ich_vtr_el2) + mrs x0, ICH_VTR_EL2 + ret +ENDPROC(__vgic_v3_get_ich_vtr_el2) + + .popsection diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 65f1121a3beb..35b0c121bb65 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -33,6 +33,7 @@ #define VGIC_MAX_CPUS KVM_MAX_VCPUS #define VGIC_V2_MAX_LRS (1 << 6) +#define VGIC_V3_MAX_LRS 16 /* Sanity checks... */ #if (VGIC_MAX_CPUS > 8) @@ -72,6 +73,7 @@ struct kvm_vcpu; enum vgic_type { VGIC_V2, /* Good ol' GICv2 */ + VGIC_V3, /* New fancy GICv3 */ }; #define LR_STATE_PENDING (1 << 0) @@ -172,6 +174,19 @@ struct vgic_v2_cpu_if { u32 vgic_lr[VGIC_V2_MAX_LRS]; }; +struct vgic_v3_cpu_if { +#ifdef CONFIG_ARM_GIC_V3 + u32 vgic_hcr; + u32 vgic_vmcr; + u32 vgic_misr; /* Saved only */ + u32 vgic_eisr; /* Saved only */ + u32 vgic_elrsr; /* Saved only */ + u32 vgic_ap0r[4]; + u32 vgic_ap1r[4]; + u64 vgic_lr[VGIC_V3_MAX_LRS]; +#endif +}; + struct vgic_cpu { #ifdef CONFIG_KVM_ARM_VGIC /* per IRQ to LR mapping */ @@ -190,6 +205,7 @@ struct vgic_cpu { /* CPU vif control registers for world switch */ union { struct vgic_v2_cpu_if vgic_v2; + struct vgic_v3_cpu_if vgic_v3; }; #endif }; @@ -224,6 +240,18 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, int vgic_v2_probe(struct device_node *vgic_node, const struct vgic_ops **ops, const struct vgic_params **params); +#ifdef CONFIG_ARM_GIC_V3 +int vgic_v3_probe(struct device_node *vgic_node, + const struct vgic_ops **ops, + const struct vgic_params **params); +#else +static inline int vgic_v3_probe(struct device_node *vgic_node, + const struct vgic_ops **ops, + const struct vgic_params **params) +{ + return -ENODEV; +} +#endif #else static inline int kvm_vgic_hyp_init(void) diff --git a/virt/kvm/arm/vgic-v3.c b/virt/kvm/arm/vgic-v3.c new file mode 100644 index 000000000000..f01d44685720 --- /dev/null +++ b/virt/kvm/arm/vgic-v3.c @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2013 ARM Limited, All Rights Reserved. + * Author: Marc Zyngier + * + * 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 + +#include + +#include +#include +#include + +/* These are for GICv2 emulation only */ +#define GICH_LR_VIRTUALID (0x3ffUL << 0) +#define GICH_LR_PHYSID_CPUID_SHIFT (10) +#define GICH_LR_PHYSID_CPUID (7UL << GICH_LR_PHYSID_CPUID_SHIFT) + +/* + * LRs are stored in reverse order in memory. make sure we index them + * correctly. + */ +#define LR_INDEX(lr) (VGIC_V3_MAX_LRS - 1 - lr) + +static u32 ich_vtr_el2; + +static struct vgic_lr vgic_v3_get_lr(const struct kvm_vcpu *vcpu, int lr) +{ + struct vgic_lr lr_desc; + u64 val = vcpu->arch.vgic_cpu.vgic_v3.vgic_lr[LR_INDEX(lr)]; + + lr_desc.irq = val & GICH_LR_VIRTUALID; + if (lr_desc.irq <= 15) + lr_desc.source = (val >> GICH_LR_PHYSID_CPUID_SHIFT) & 0x7; + else + lr_desc.source = 0; + lr_desc.state = 0; + + if (val & ICH_LR_PENDING_BIT) + lr_desc.state |= LR_STATE_PENDING; + if (val & ICH_LR_ACTIVE_BIT) + lr_desc.state |= LR_STATE_ACTIVE; + if (val & ICH_LR_EOI) + lr_desc.state |= LR_EOI_INT; + + return lr_desc; +} + +static void vgic_v3_set_lr(struct kvm_vcpu *vcpu, int lr, + struct vgic_lr lr_desc) +{ + u64 lr_val = (((u32)lr_desc.source << GICH_LR_PHYSID_CPUID_SHIFT) | + lr_desc.irq); + + if (lr_desc.state & LR_STATE_PENDING) + lr_val |= ICH_LR_PENDING_BIT; + if (lr_desc.state & LR_STATE_ACTIVE) + lr_val |= ICH_LR_ACTIVE_BIT; + if (lr_desc.state & LR_EOI_INT) + lr_val |= ICH_LR_EOI; + + vcpu->arch.vgic_cpu.vgic_v3.vgic_lr[LR_INDEX(lr)] = lr_val; +} + +static void vgic_v3_sync_lr_elrsr(struct kvm_vcpu *vcpu, int lr, + struct vgic_lr lr_desc) +{ + if (!(lr_desc.state & LR_STATE_MASK)) + vcpu->arch.vgic_cpu.vgic_v3.vgic_elrsr |= (1U << lr); +} + +static u64 vgic_v3_get_elrsr(const struct kvm_vcpu *vcpu) +{ + return vcpu->arch.vgic_cpu.vgic_v3.vgic_elrsr; +} + +static u64 vgic_v3_get_eisr(const struct kvm_vcpu *vcpu) +{ + return vcpu->arch.vgic_cpu.vgic_v3.vgic_eisr; +} + +static u32 vgic_v3_get_interrupt_status(const struct kvm_vcpu *vcpu) +{ + u32 misr = vcpu->arch.vgic_cpu.vgic_v3.vgic_misr; + u32 ret = 0; + + if (misr & ICH_MISR_EOI) + ret |= INT_STATUS_EOI; + if (misr & ICH_MISR_U) + ret |= INT_STATUS_UNDERFLOW; + + return ret; +} + +static void vgic_v3_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp) +{ + u32 vmcr = vcpu->arch.vgic_cpu.vgic_v3.vgic_vmcr; + + vmcrp->ctlr = (vmcr & ICH_VMCR_CTLR_MASK) >> ICH_VMCR_CTLR_SHIFT; + vmcrp->abpr = (vmcr & ICH_VMCR_BPR1_MASK) >> ICH_VMCR_BPR1_SHIFT; + vmcrp->bpr = (vmcr & ICH_VMCR_BPR0_MASK) >> ICH_VMCR_BPR0_SHIFT; + vmcrp->pmr = (vmcr & ICH_VMCR_PMR_MASK) >> ICH_VMCR_PMR_SHIFT; +} + +static void vgic_v3_enable_underflow(struct kvm_vcpu *vcpu) +{ + vcpu->arch.vgic_cpu.vgic_v3.vgic_hcr |= ICH_HCR_UIE; +} + +static void vgic_v3_disable_underflow(struct kvm_vcpu *vcpu) +{ + vcpu->arch.vgic_cpu.vgic_v3.vgic_hcr &= ~ICH_HCR_UIE; +} + +static void vgic_v3_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp) +{ + u32 vmcr; + + vmcr = (vmcrp->ctlr << ICH_VMCR_CTLR_SHIFT) & ICH_VMCR_CTLR_MASK; + vmcr |= (vmcrp->abpr << ICH_VMCR_BPR1_SHIFT) & ICH_VMCR_BPR1_MASK; + vmcr |= (vmcrp->bpr << ICH_VMCR_BPR0_SHIFT) & ICH_VMCR_BPR0_MASK; + vmcr |= (vmcrp->pmr << ICH_VMCR_PMR_SHIFT) & ICH_VMCR_PMR_MASK; + + vcpu->arch.vgic_cpu.vgic_v3.vgic_vmcr = vmcr; +} + +static void vgic_v3_enable(struct kvm_vcpu *vcpu) +{ + /* + * By forcing VMCR to zero, the GIC will restore the binary + * points to their reset values. Anything else resets to zero + * anyway. + */ + vcpu->arch.vgic_cpu.vgic_v3.vgic_vmcr = 0; + + /* Get the show on the road... */ + vcpu->arch.vgic_cpu.vgic_v3.vgic_hcr = ICH_HCR_EN; +} + +static const struct vgic_ops vgic_v3_ops = { + .get_lr = vgic_v3_get_lr, + .set_lr = vgic_v3_set_lr, + .sync_lr_elrsr = vgic_v3_sync_lr_elrsr, + .get_elrsr = vgic_v3_get_elrsr, + .get_eisr = vgic_v3_get_eisr, + .get_interrupt_status = vgic_v3_get_interrupt_status, + .enable_underflow = vgic_v3_enable_underflow, + .disable_underflow = vgic_v3_disable_underflow, + .get_vmcr = vgic_v3_get_vmcr, + .set_vmcr = vgic_v3_set_vmcr, + .enable = vgic_v3_enable, +}; + +static struct vgic_params vgic_v3_params; + +/** + * vgic_v3_probe - probe for a GICv3 compatible interrupt controller in DT + * @node: pointer to the DT node + * @ops: address of a pointer to the GICv3 operations + * @params: address of a pointer to HW-specific parameters + * + * Returns 0 if a GICv3 has been found, with the low level operations + * in *ops and the HW parameters in *params. Returns an error code + * otherwise. + */ +int vgic_v3_probe(struct device_node *vgic_node, + const struct vgic_ops **ops, + const struct vgic_params **params) +{ + int ret = 0; + u32 gicv_idx; + struct resource vcpu_res; + struct vgic_params *vgic = &vgic_v3_params; + + vgic->maint_irq = irq_of_parse_and_map(vgic_node, 0); + if (!vgic->maint_irq) { + kvm_err("error getting vgic maintenance irq from DT\n"); + ret = -ENXIO; + goto out; + } + + ich_vtr_el2 = kvm_call_hyp(__vgic_v3_get_ich_vtr_el2); + + /* + * The ListRegs field is 5 bits, but there is a architectural + * maximum of 16 list registers. Just ignore bit 4... + */ + vgic->nr_lr = (ich_vtr_el2 & 0xf) + 1; + + if (of_property_read_u32(vgic_node, "#redistributor-regions", &gicv_idx)) + gicv_idx = 1; + + gicv_idx += 3; /* Also skip GICD, GICC, GICH */ + if (of_address_to_resource(vgic_node, gicv_idx, &vcpu_res)) { + kvm_err("Cannot obtain GICV region\n"); + ret = -ENXIO; + goto out; + } + vgic->vcpu_base = vcpu_res.start; + vgic->vctrl_base = NULL; + vgic->type = VGIC_V3; + + kvm_info("%s@%llx IRQ%d\n", vgic_node->name, + vcpu_res.start, vgic->maint_irq); + + *ops = &vgic_v3_ops; + *params = vgic; + +out: + of_node_put(vgic_node); + return ret; +} -- cgit v1.2.3 From 2243a87d90b42eb38bc281957df3e57c712b5e56 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Mon, 9 Jun 2014 09:37:56 +0800 Subject: pinctrl: avoid duplicated calling enable_pinmux_setting for a pin What the patch does: 1. Call pinmux_disable_setting ahead of pinmux_enable_setting each time pinctrl_select_state is called 2. Remove the HW disable operation in pinmux_disable_setting function. 3. Remove the disable ops in struct pinmux_ops 4. Remove all the disable ops users in current code base. Notes: 1. Great thanks for the suggestion from Linus, Tony Lindgren and Stephen Warren and Everyone that shared comments on this patch. 2. The patch also includes comment fixes from Stephen Warren. The reason why we do this: 1. To avoid duplicated calling of the enable_setting operation without disabling operation inbetween which will let the pin descriptor desc->mux_usecount increase monotonously. 2. The HW pin disable operation is not useful for any of the existing platforms. And this can be used to avoid the HW glitch after using the item #1 modification. In the following case, the issue can be reproduced: 1. There is a driver that need to switch pin state dynamically, e.g. between "sleep" and "default" state 2. The pin setting configuration in a DTS node may be like this: component a { pinctrl-names = "default", "sleep"; pinctrl-0 = <&a_grp_setting &c_grp_setting>; pinctrl-1 = <&b_grp_setting &c_grp_setting>; } The "c_grp_setting" config node is totally identical, maybe like following one: c_grp_setting: c_grp_setting { pinctrl-single,pins = ; } 3. When switching the pin state in the following official pinctrl sequence: pin = pinctrl_get(); state = pinctrl_lookup_state(wanted_state); pinctrl_select_state(state); pinctrl_put(); Test Result: 1. The switch is completed as expected, that is: the device's pin configuration is changed according to the description in the "wanted_state" group setting 2. The "desc->mux_usecount" of the corresponding pins in "c_group" is increased without being decreased, because the "desc" is for each physical pin while the setting is for each setting node in the DTS. Thus, if the "c_grp_setting" in pinctrl-0 is not disabled ahead of enabling "c_grp_setting" in pinctrl-1, the desc->mux_usecount will keep increasing without any chance to be decreased. According to the comments in the original code, only the setting, in old state but not in new state, will be "disabled" (calling pinmux_disable_setting), which is correct logic but not intact. We still need consider case that the setting is in both old state and new state. We can do this in the following two ways: 1. Avoid to "enable"(calling pinmux_enable_setting) the "same pin setting" repeatedly 2. "Disable"(calling pinmux_disable_setting) the "same pin setting", actually two setting instances, ahead of enabling them. Analysis: 1. The solution #2 is better because it can avoid too much iteration. 2. If we disable all of the settings in the old state and one of the setting(s) exist in the new state, the pins mux function change may happen when some SoC vendors defined the "pinctrl-single,function-off" in their DTS file. old_setting => disabled_setting => new_setting. 3. In the pinmux framework, when a pin state is switched, the setting in the old state should be marked as "disabled". Conclusion: 1. To Remove the HW disabling operation to above the glitch mentioned above. 2. Handle the issue mentioned above by disabling all of the settings in old state and then enable the all of the settings in new state. Signed-off-by: Fan Wu Acked-by: Stephen Warren Acked-by: Patrice Chotard Acked-by: Heiko Stuebner Acked-by: Maxime Coquelin Signed-off-by: Linus Walleij --- drivers/pinctrl/core.c | 24 +++------------ drivers/pinctrl/pinctrl-abx500.c | 15 --------- drivers/pinctrl/pinctrl-adi2.c | 30 ------------------ drivers/pinctrl/pinctrl-at91.c | 21 ------------- drivers/pinctrl/pinctrl-bcm2835.c | 11 ------- drivers/pinctrl/pinctrl-exynos5440.c | 8 ----- drivers/pinctrl/pinctrl-msm.c | 25 --------------- drivers/pinctrl/pinctrl-nomadik.c | 16 ---------- drivers/pinctrl/pinctrl-rockchip.c | 18 ----------- drivers/pinctrl/pinctrl-samsung.c | 8 ----- drivers/pinctrl/pinctrl-single.c | 56 --------------------------------- drivers/pinctrl/pinctrl-st.c | 6 ---- drivers/pinctrl/pinctrl-tb10x.c | 17 ---------- drivers/pinctrl/pinctrl-tegra.c | 13 -------- drivers/pinctrl/pinctrl-tz1090-pdc.c | 28 ----------------- drivers/pinctrl/pinctrl-tz1090.c | 58 ----------------------------------- drivers/pinctrl/pinctrl-u300.c | 14 --------- drivers/pinctrl/pinmux.c | 4 --- drivers/pinctrl/sh-pfc/pinctrl.c | 22 ------------- drivers/pinctrl/sirf/pinctrl-sirf.c | 10 ------ drivers/pinctrl/spear/pinctrl-spear.c | 7 ----- drivers/pinctrl/vt8500/pinctrl-wmt.c | 12 -------- include/linux/pinctrl/pinmux.h | 2 -- 23 files changed, 5 insertions(+), 420 deletions(-) (limited to 'include') diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index e09474ecde23..e4f65510c87e 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -992,29 +992,15 @@ int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state) if (p->state) { /* - * The set of groups with a mux configuration in the old state - * may not be identical to the set of groups with a mux setting - * in the new state. While this might be unusual, it's entirely - * possible for the "user"-supplied mapping table to be written - * that way. For each group that was configured in the old state - * but not in the new state, this code puts that group into a - * safe/disabled state. + * For each pinmux setting in the old state, forget SW's record + * of mux owner for that pingroup. Any pingroups which are + * still owned by the new state will be re-acquired by the call + * to pinmux_enable_setting() in the loop below. */ list_for_each_entry(setting, &p->state->settings, node) { - bool found = false; if (setting->type != PIN_MAP_TYPE_MUX_GROUP) continue; - list_for_each_entry(setting2, &state->settings, node) { - if (setting2->type != PIN_MAP_TYPE_MUX_GROUP) - continue; - if (setting2->data.mux.group == - setting->data.mux.group) { - found = true; - break; - } - } - if (!found) - pinmux_disable_setting(setting); + pinmux_disable_setting(setting); } } diff --git a/drivers/pinctrl/pinctrl-abx500.c b/drivers/pinctrl/pinctrl-abx500.c index 163da9c3ea0e..f3f8b24efe54 100644 --- a/drivers/pinctrl/pinctrl-abx500.c +++ b/drivers/pinctrl/pinctrl-abx500.c @@ -737,20 +737,6 @@ static int abx500_pmx_enable(struct pinctrl_dev *pctldev, unsigned function, return ret; } -static void abx500_pmx_disable(struct pinctrl_dev *pctldev, - unsigned function, unsigned group) -{ - struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev); - const struct abx500_pingroup *g; - - g = &pct->soc->groups[group]; - if (g->altsetting < 0) - return; - - /* FIXME: poke out the mux, set the pin to some default state? */ - dev_dbg(pct->dev, "disable group %s, %u pins\n", g->name, g->npins); -} - static int abx500_gpio_request_enable(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset) @@ -799,7 +785,6 @@ static const struct pinmux_ops abx500_pinmux_ops = { .get_function_name = abx500_pmx_get_func_name, .get_function_groups = abx500_pmx_get_func_groups, .enable = abx500_pmx_enable, - .disable = abx500_pmx_disable, .gpio_request_enable = abx500_gpio_request_enable, .gpio_disable_free = abx500_gpio_disable_free, }; diff --git a/drivers/pinctrl/pinctrl-adi2.c b/drivers/pinctrl/pinctrl-adi2.c index 5c44feb54ebb..b02ee4f882c0 100644 --- a/drivers/pinctrl/pinctrl-adi2.c +++ b/drivers/pinctrl/pinctrl-adi2.c @@ -652,35 +652,6 @@ static int adi_pinmux_enable(struct pinctrl_dev *pctldev, unsigned func_id, return 0; } -static void adi_pinmux_disable(struct pinctrl_dev *pctldev, unsigned func_id, - unsigned group_id) -{ - struct adi_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctldev); - struct gpio_port *port; - struct pinctrl_gpio_range *range; - unsigned long flags; - unsigned short *mux, pin; - - mux = (unsigned short *)pinctrl->soc->groups[group_id].mux; - - while (*mux) { - pin = P_IDENT(*mux); - - range = pinctrl_find_gpio_range_from_pin(pctldev, pin); - if (range == NULL) /* should not happen */ - return; - - port = container_of(range->gc, struct gpio_port, chip); - - spin_lock_irqsave(&port->lock, flags); - - port_setup(port, pin_to_offset(range, pin), true); - mux++; - - spin_unlock_irqrestore(&port->lock, flags); - } -} - static int adi_pinmux_get_funcs_count(struct pinctrl_dev *pctldev) { struct adi_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctldev); @@ -728,7 +699,6 @@ static int adi_pinmux_request_gpio(struct pinctrl_dev *pctldev, static struct pinmux_ops adi_pinmux_ops = { .enable = adi_pinmux_enable, - .disable = adi_pinmux_disable, .get_functions_count = adi_pinmux_get_funcs_count, .get_function_name = adi_pinmux_get_func_name, .get_function_groups = adi_pinmux_get_groups, diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index 421493cb490c..bd57ab514aa4 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -611,26 +611,6 @@ static int at91_pmx_enable(struct pinctrl_dev *pctldev, unsigned selector, return 0; } -static void at91_pmx_disable(struct pinctrl_dev *pctldev, unsigned selector, - unsigned group) -{ - struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); - const struct at91_pmx_pin *pins_conf = info->groups[group].pins_conf; - const struct at91_pmx_pin *pin; - uint32_t npins = info->groups[group].npins; - int i; - unsigned mask; - void __iomem *pio; - - for (i = 0; i < npins; i++) { - pin = &pins_conf[i]; - at91_pin_dbg(info->dev, pin); - pio = pin_to_controller(info, pin->bank); - mask = pin_to_mask(pin->pin); - at91_mux_gpio_enable(pio, mask, 1); - } -} - static int at91_pmx_get_funcs_count(struct pinctrl_dev *pctldev) { struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); @@ -705,7 +685,6 @@ static const struct pinmux_ops at91_pmx_ops = { .get_function_name = at91_pmx_get_func_name, .get_function_groups = at91_pmx_get_groups, .enable = at91_pmx_enable, - .disable = at91_pmx_disable, .gpio_request_enable = at91_gpio_request_enable, .gpio_disable_free = at91_gpio_disable_free, }; diff --git a/drivers/pinctrl/pinctrl-bcm2835.c b/drivers/pinctrl/pinctrl-bcm2835.c index 3d907de9bc91..5bcfd7ace0cd 100644 --- a/drivers/pinctrl/pinctrl-bcm2835.c +++ b/drivers/pinctrl/pinctrl-bcm2835.c @@ -841,16 +841,6 @@ static int bcm2835_pmx_enable(struct pinctrl_dev *pctldev, return 0; } -static void bcm2835_pmx_disable(struct pinctrl_dev *pctldev, - unsigned func_selector, - unsigned group_selector) -{ - struct bcm2835_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev); - - /* disable by setting to GPIO_IN */ - bcm2835_pinctrl_fsel_set(pc, group_selector, BCM2835_FSEL_GPIO_IN); -} - static void bcm2835_pmx_gpio_disable_free(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset) @@ -880,7 +870,6 @@ static const struct pinmux_ops bcm2835_pmx_ops = { .get_function_name = bcm2835_pmx_get_function_name, .get_function_groups = bcm2835_pmx_get_function_groups, .enable = bcm2835_pmx_enable, - .disable = bcm2835_pmx_disable, .gpio_disable_free = bcm2835_pmx_gpio_disable_free, .gpio_set_direction = bcm2835_pmx_gpio_set_direction, }; diff --git a/drivers/pinctrl/pinctrl-exynos5440.c b/drivers/pinctrl/pinctrl-exynos5440.c index 8fe2ab0a7698..4b145b5db7a6 100644 --- a/drivers/pinctrl/pinctrl-exynos5440.c +++ b/drivers/pinctrl/pinctrl-exynos5440.c @@ -371,13 +371,6 @@ static int exynos5440_pinmux_enable(struct pinctrl_dev *pctldev, unsigned select return 0; } -/* disable a specified pinmux by writing to registers */ -static void exynos5440_pinmux_disable(struct pinctrl_dev *pctldev, - unsigned selector, unsigned group) -{ - exynos5440_pinmux_setup(pctldev, selector, group, false); -} - /* * The calls to gpio_direction_output() and gpio_direction_input() * leads to this function call (via the pinctrl_gpio_direction_{input|output}() @@ -395,7 +388,6 @@ static const struct pinmux_ops exynos5440_pinmux_ops = { .get_function_name = exynos5440_pinmux_get_fname, .get_function_groups = exynos5440_pinmux_get_groups, .enable = exynos5440_pinmux_enable, - .disable = exynos5440_pinmux_disable, .gpio_set_direction = exynos5440_pinmux_gpio_set_direction, }; diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c index df6dda4ce803..bdfaba4430f2 100644 --- a/drivers/pinctrl/pinctrl-msm.c +++ b/drivers/pinctrl/pinctrl-msm.c @@ -165,36 +165,11 @@ static int msm_pinmux_enable(struct pinctrl_dev *pctldev, return 0; } -static void msm_pinmux_disable(struct pinctrl_dev *pctldev, - unsigned function, - unsigned group) -{ - struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); - const struct msm_pingroup *g; - unsigned long flags; - u32 val; - - g = &pctrl->soc->groups[group]; - - if (WARN_ON(g->mux_bit < 0)) - return; - - spin_lock_irqsave(&pctrl->lock, flags); - - /* Clear the mux bits to select gpio mode */ - val = readl(pctrl->regs + g->ctl_reg); - val &= ~(0x7 << g->mux_bit); - writel(val, pctrl->regs + g->ctl_reg); - - spin_unlock_irqrestore(&pctrl->lock, flags); -} - static const struct pinmux_ops msm_pinmux_ops = { .get_functions_count = msm_get_functions_count, .get_function_name = msm_get_function_name, .get_function_groups = msm_get_function_groups, .enable = msm_pinmux_enable, - .disable = msm_pinmux_disable, }; static int msm_config_reg(struct msm_pinctrl *pctrl, diff --git a/drivers/pinctrl/pinctrl-nomadik.c b/drivers/pinctrl/pinctrl-nomadik.c index 8f6f16ef73f3..a564251fe093 100644 --- a/drivers/pinctrl/pinctrl-nomadik.c +++ b/drivers/pinctrl/pinctrl-nomadik.c @@ -1765,21 +1765,6 @@ out_glitch: return ret; } -static void nmk_pmx_disable(struct pinctrl_dev *pctldev, - unsigned function, unsigned group) -{ - struct nmk_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev); - const struct nmk_pingroup *g; - - g = &npct->soc->groups[group]; - - if (g->altsetting < 0) - return; - - /* Poke out the mux, set the pin to some default state? */ - dev_dbg(npct->dev, "disable group %s, %u pins\n", g->name, g->npins); -} - static int nmk_gpio_request_enable(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset) @@ -1826,7 +1811,6 @@ static const struct pinmux_ops nmk_pinmux_ops = { .get_function_name = nmk_pmx_get_func_name, .get_function_groups = nmk_pmx_get_func_groups, .enable = nmk_pmx_enable, - .disable = nmk_pmx_disable, .gpio_request_enable = nmk_gpio_request_enable, .gpio_disable_free = nmk_gpio_disable_free, }; diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c index bb805d5e9ff0..51f67a6eadcb 100644 --- a/drivers/pinctrl/pinctrl-rockchip.c +++ b/drivers/pinctrl/pinctrl-rockchip.c @@ -657,23 +657,6 @@ static int rockchip_pmx_enable(struct pinctrl_dev *pctldev, unsigned selector, return 0; } -static void rockchip_pmx_disable(struct pinctrl_dev *pctldev, - unsigned selector, unsigned group) -{ - struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); - const unsigned int *pins = info->groups[group].pins; - struct rockchip_pin_bank *bank; - int cnt; - - dev_dbg(info->dev, "disable function %s group %s\n", - info->functions[selector].name, info->groups[group].name); - - for (cnt = 0; cnt < info->groups[group].npins; cnt++) { - bank = pin_to_bank(info, pins[cnt]); - rockchip_set_mux(bank, pins[cnt] - bank->pin_base, 0); - } -} - /* * The calls to gpio_direction_output() and gpio_direction_input() * leads to this function call (via the pinctrl_gpio_direction_{input|output}() @@ -716,7 +699,6 @@ static const struct pinmux_ops rockchip_pmx_ops = { .get_function_name = rockchip_pmx_get_func_name, .get_function_groups = rockchip_pmx_get_groups, .enable = rockchip_pmx_enable, - .disable = rockchip_pmx_disable, .gpio_set_direction = rockchip_pmx_gpio_set_direction, }; diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c index 3e61d0f8f146..089abde35d44 100644 --- a/drivers/pinctrl/pinctrl-samsung.c +++ b/drivers/pinctrl/pinctrl-samsung.c @@ -333,13 +333,6 @@ static int samsung_pinmux_enable(struct pinctrl_dev *pctldev, unsigned selector, return 0; } -/* disable a specified pinmux by writing to registers */ -static void samsung_pinmux_disable(struct pinctrl_dev *pctldev, - unsigned selector, unsigned group) -{ - samsung_pinmux_setup(pctldev, selector, group, false); -} - /* * The calls to gpio_direction_output() and gpio_direction_input() * leads to this function call (via the pinctrl_gpio_direction_{input|output}() @@ -390,7 +383,6 @@ static const struct pinmux_ops samsung_pinmux_ops = { .get_function_name = samsung_pinmux_get_fname, .get_function_groups = samsung_pinmux_get_groups, .enable = samsung_pinmux_enable, - .disable = samsung_pinmux_disable, .gpio_set_direction = samsung_pinmux_gpio_set_direction, }; diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index 2960557bfed9..ff6a2bda52e5 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -488,61 +488,6 @@ static int pcs_enable(struct pinctrl_dev *pctldev, unsigned fselector, return 0; } -static void pcs_disable(struct pinctrl_dev *pctldev, unsigned fselector, - unsigned group) -{ - struct pcs_device *pcs; - struct pcs_function *func; - int i; - - pcs = pinctrl_dev_get_drvdata(pctldev); - /* If function mask is null, needn't disable it. */ - if (!pcs->fmask) - return; - - func = radix_tree_lookup(&pcs->ftree, fselector); - if (!func) { - dev_err(pcs->dev, "%s could not find function%i\n", - __func__, fselector); - return; - } - - /* - * Ignore disable if function-off is not specified. Some hardware - * does not have clearly defined disable function. For pin specific - * off modes, you can use alternate named states as described in - * pinctrl-bindings.txt. - */ - if (pcs->foff == PCS_OFF_DISABLED) { - dev_dbg(pcs->dev, "ignoring disable for %s function%i\n", - func->name, fselector); - return; - } - - dev_dbg(pcs->dev, "disabling function%i %s\n", - fselector, func->name); - - for (i = 0; i < func->nvals; i++) { - struct pcs_func_vals *vals; - unsigned long flags; - unsigned val, mask; - - vals = &func->vals[i]; - raw_spin_lock_irqsave(&pcs->lock, flags); - val = pcs->read(vals->reg); - - if (pcs->bits_per_mux) - mask = vals->mask; - else - mask = pcs->fmask; - - val &= ~mask; - val |= pcs->foff << pcs->fshift; - pcs->write(val, vals->reg); - raw_spin_unlock_irqrestore(&pcs->lock, flags); - } -} - static int pcs_request_gpio(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned pin) { @@ -575,7 +520,6 @@ static const struct pinmux_ops pcs_pinmux_ops = { .get_function_name = pcs_get_function_name, .get_function_groups = pcs_get_function_groups, .enable = pcs_enable, - .disable = pcs_disable, .gpio_request_enable = pcs_request_gpio, }; diff --git a/drivers/pinctrl/pinctrl-st.c b/drivers/pinctrl/pinctrl-st.c index 1bd6363bc95e..e1919cd43117 100644 --- a/drivers/pinctrl/pinctrl-st.c +++ b/drivers/pinctrl/pinctrl-st.c @@ -930,11 +930,6 @@ static int st_pmx_enable(struct pinctrl_dev *pctldev, unsigned fselector, return 0; } -static void st_pmx_disable(struct pinctrl_dev *pctldev, unsigned selector, - unsigned group) -{ -} - static int st_pmx_set_gpio_direction(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned gpio, bool input) @@ -957,7 +952,6 @@ static struct pinmux_ops st_pmxops = { .get_function_name = st_pmx_get_fname, .get_function_groups = st_pmx_get_groups, .enable = st_pmx_enable, - .disable = st_pmx_disable, .gpio_set_direction = st_pmx_set_gpio_direction, }; diff --git a/drivers/pinctrl/pinctrl-tb10x.c b/drivers/pinctrl/pinctrl-tb10x.c index 26ca6855f478..71c5d4f0c538 100644 --- a/drivers/pinctrl/pinctrl-tb10x.c +++ b/drivers/pinctrl/pinctrl-tb10x.c @@ -738,22 +738,6 @@ static int tb10x_pctl_enable(struct pinctrl_dev *pctl, return 0; } -static void tb10x_pctl_disable(struct pinctrl_dev *pctl, - unsigned func_selector, unsigned group_selector) -{ - struct tb10x_pinctrl *state = pinctrl_dev_get_drvdata(pctl); - const struct tb10x_pinfuncgrp *grp = &state->pingroups[group_selector]; - - if (grp->port < 0) - return; - - mutex_lock(&state->mutex); - - state->ports[grp->port].count--; - - mutex_unlock(&state->mutex); -} - static struct pinmux_ops tb10x_pinmux_ops = { .get_functions_count = tb10x_get_functions_count, .get_function_name = tb10x_get_function_name, @@ -761,7 +745,6 @@ static struct pinmux_ops tb10x_pinmux_ops = { .gpio_request_enable = tb10x_gpio_request_enable, .gpio_disable_free = tb10x_gpio_disable_free, .enable = tb10x_pctl_enable, - .disable = tb10x_pctl_disable, }; static struct pinctrl_desc tb10x_pindesc = { diff --git a/drivers/pinctrl/pinctrl-tegra.c b/drivers/pinctrl/pinctrl-tegra.c index 2d43bff74f59..150af5503c09 100644 --- a/drivers/pinctrl/pinctrl-tegra.c +++ b/drivers/pinctrl/pinctrl-tegra.c @@ -290,24 +290,11 @@ static int tegra_pinctrl_enable(struct pinctrl_dev *pctldev, unsigned function, return 0; } -static void tegra_pinctrl_disable(struct pinctrl_dev *pctldev, - unsigned function, unsigned group) -{ - struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); - const struct tegra_pingroup *g; - - g = &pmx->soc->groups[group]; - - if (WARN_ON(g->mux_reg < 0)) - return; -} - static const struct pinmux_ops tegra_pinmux_ops = { .get_functions_count = tegra_pinctrl_get_funcs_count, .get_function_name = tegra_pinctrl_get_func_name, .get_function_groups = tegra_pinctrl_get_func_groups, .enable = tegra_pinctrl_enable, - .disable = tegra_pinctrl_disable, }; static int tegra_pinconf_reg(struct tegra_pmx *pmx, diff --git a/drivers/pinctrl/pinctrl-tz1090-pdc.c b/drivers/pinctrl/pinctrl-tz1090-pdc.c index 5bf01c28925e..41e81a35cabb 100644 --- a/drivers/pinctrl/pinctrl-tz1090-pdc.c +++ b/drivers/pinctrl/pinctrl-tz1090-pdc.c @@ -574,33 +574,6 @@ static int tz1090_pdc_pinctrl_enable(struct pinctrl_dev *pctldev, return 0; } -static void tz1090_pdc_pinctrl_disable(struct pinctrl_dev *pctldev, - unsigned int function, - unsigned int group) -{ - struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); - const struct tz1090_pdc_pingroup *grp = &tz1090_pdc_groups[group]; - - dev_dbg(pctldev->dev, "%s(func=%u (%s), group=%u (%s))\n", - __func__, - function, tz1090_pdc_functions[function].name, - group, tz1090_pdc_groups[group].name); - - /* is it even a mux? */ - if (grp->drv) - return; - - /* does this group even control the function? */ - if (function != grp->func) - return; - - /* record the pin being unmuxed and update mux bit */ - spin_lock(&pmx->lock); - pmx->mux_en &= ~BIT(grp->pins[0]); - tz1090_pdc_pinctrl_mux(pmx, grp); - spin_unlock(&pmx->lock); -} - static const struct tz1090_pdc_pingroup *find_mux_group( struct tz1090_pdc_pmx *pmx, unsigned int pin) @@ -662,7 +635,6 @@ static struct pinmux_ops tz1090_pdc_pinmux_ops = { .get_function_name = tz1090_pdc_pinctrl_get_func_name, .get_function_groups = tz1090_pdc_pinctrl_get_func_groups, .enable = tz1090_pdc_pinctrl_enable, - .disable = tz1090_pdc_pinctrl_disable, .gpio_request_enable = tz1090_pdc_pinctrl_gpio_request_enable, .gpio_disable_free = tz1090_pdc_pinctrl_gpio_disable_free, }; diff --git a/drivers/pinctrl/pinctrl-tz1090.c b/drivers/pinctrl/pinctrl-tz1090.c index bc9cd7a7602e..24082216842e 100644 --- a/drivers/pinctrl/pinctrl-tz1090.c +++ b/drivers/pinctrl/pinctrl-tz1090.c @@ -1478,63 +1478,6 @@ mux_pins: return 0; } -/** - * tz1090_pinctrl_disable() - Disable a function on a pin group. - * @pctldev: Pin control data - * @function: Function index to disable - * @group: Group index to disable - * - * Disable a particular function on a group of pins. The per GPIO pin pseudo pin - * groups can be used (in which case the pin will be taken out of peripheral - * mode. Some convenience pin groups can also be used in which case the effect - * is the same as enabling the function on each individual pin in the group. - */ -static void tz1090_pinctrl_disable(struct pinctrl_dev *pctldev, - unsigned int function, unsigned int group) -{ - struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); - struct tz1090_pingroup *grp; - unsigned int pin_num, mux_group, i, npins; - const unsigned int *pins; - - /* group of pins? */ - if (group < ARRAY_SIZE(tz1090_groups)) { - grp = &tz1090_groups[group]; - npins = grp->npins; - pins = grp->pins; - /* - * All pins in the group must belong to the same mux group, - * which allows us to just use the mux group of the first pin. - * By explicitly listing permitted pingroups for each function - * the pinmux core should ensure this is always the case. - */ - } else { - pin_num = group - ARRAY_SIZE(tz1090_groups); - npins = 1; - pins = &pin_num; - } - mux_group = tz1090_mux_pins[*pins]; - - /* no mux group, but can still be individually muxed to peripheral */ - if (mux_group >= TZ1090_MUX_GROUP_MAX) { - if (function == TZ1090_MUX_PERIP) - goto unmux_pins; - return; - } - - /* mux group already set to a different function? */ - grp = &tz1090_mux_groups[mux_group]; - dev_dbg(pctldev->dev, "%s: unmuxing %u pin(s) in '%s' from '%s'\n", - __func__, npins, grp->name, tz1090_functions[function].name); - - /* subtract pins from ref count and unmux individually */ - WARN_ON(grp->func_count < npins); - grp->func_count -= npins; -unmux_pins: - for (i = 0; i < npins; ++i) - tz1090_pinctrl_perip_select(pmx, pins[i], false); -} - /** * tz1090_pinctrl_gpio_request_enable() - Put pin in GPIO mode. * @pctldev: Pin control data @@ -1575,7 +1518,6 @@ static struct pinmux_ops tz1090_pinmux_ops = { .get_function_name = tz1090_pinctrl_get_func_name, .get_function_groups = tz1090_pinctrl_get_func_groups, .enable = tz1090_pinctrl_enable, - .disable = tz1090_pinctrl_disable, .gpio_request_enable = tz1090_pinctrl_gpio_request_enable, .gpio_disable_free = tz1090_pinctrl_gpio_disable_free, }; diff --git a/drivers/pinctrl/pinctrl-u300.c b/drivers/pinctrl/pinctrl-u300.c index 209a01b8bd3b..0959bb36450f 100644 --- a/drivers/pinctrl/pinctrl-u300.c +++ b/drivers/pinctrl/pinctrl-u300.c @@ -970,19 +970,6 @@ static int u300_pmx_enable(struct pinctrl_dev *pctldev, unsigned selector, return 0; } -static void u300_pmx_disable(struct pinctrl_dev *pctldev, unsigned selector, - unsigned group) -{ - struct u300_pmx *upmx; - - /* There is nothing to do with the power pins */ - if (selector == 0) - return; - - upmx = pinctrl_dev_get_drvdata(pctldev); - u300_pmx_endisable(upmx, selector, false); -} - static int u300_pmx_get_funcs_count(struct pinctrl_dev *pctldev) { return ARRAY_SIZE(u300_pmx_functions); @@ -1008,7 +995,6 @@ static const struct pinmux_ops u300_pmx_ops = { .get_function_name = u300_pmx_get_func_name, .get_function_groups = u300_pmx_get_groups, .enable = u300_pmx_enable, - .disable = u300_pmx_disable, }; static int u300_pin_config_get(struct pinctrl_dev *pctldev, unsigned pin, diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c index 051e8592990e..c055daf9a80f 100644 --- a/drivers/pinctrl/pinmux.c +++ b/drivers/pinctrl/pinmux.c @@ -471,7 +471,6 @@ void pinmux_disable_setting(struct pinctrl_setting const *setting) { struct pinctrl_dev *pctldev = setting->pctldev; const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; - const struct pinmux_ops *ops = pctldev->desc->pmxops; int ret = 0; const unsigned *pins = NULL; unsigned num_pins = 0; @@ -518,9 +517,6 @@ void pinmux_disable_setting(struct pinctrl_setting const *setting) pins[i], desc->name, gname); } } - - if (ops->disable) - ops->disable(pctldev, setting->data.mux.func, setting->data.mux.group); } #ifdef CONFIG_DEBUG_FS diff --git a/drivers/pinctrl/sh-pfc/pinctrl.c b/drivers/pinctrl/sh-pfc/pinctrl.c index e758af95c209..11db3ee39d40 100644 --- a/drivers/pinctrl/sh-pfc/pinctrl.c +++ b/drivers/pinctrl/sh-pfc/pinctrl.c @@ -345,27 +345,6 @@ done: return ret; } -static void sh_pfc_func_disable(struct pinctrl_dev *pctldev, unsigned selector, - unsigned group) -{ - struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); - struct sh_pfc *pfc = pmx->pfc; - const struct sh_pfc_pin_group *grp = &pfc->info->groups[group]; - unsigned long flags; - unsigned int i; - - spin_lock_irqsave(&pfc->lock, flags); - - for (i = 0; i < grp->nr_pins; ++i) { - int idx = sh_pfc_get_pin_index(pfc, grp->pins[i]); - struct sh_pfc_pin_config *cfg = &pmx->configs[idx]; - - cfg->type = PINMUX_TYPE_NONE; - } - - spin_unlock_irqrestore(&pfc->lock, flags); -} - static int sh_pfc_gpio_request_enable(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset) @@ -464,7 +443,6 @@ static const struct pinmux_ops sh_pfc_pinmux_ops = { .get_function_name = sh_pfc_get_function_name, .get_function_groups = sh_pfc_get_function_groups, .enable = sh_pfc_func_enable, - .disable = sh_pfc_func_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, diff --git a/drivers/pinctrl/sirf/pinctrl-sirf.c b/drivers/pinctrl/sirf/pinctrl-sirf.c index 014f5b1fee55..4c1d7c68666d 100644 --- a/drivers/pinctrl/sirf/pinctrl-sirf.c +++ b/drivers/pinctrl/sirf/pinctrl-sirf.c @@ -186,15 +186,6 @@ static int sirfsoc_pinmux_enable(struct pinctrl_dev *pmxdev, unsigned selector, return 0; } -static void sirfsoc_pinmux_disable(struct pinctrl_dev *pmxdev, unsigned selector, - unsigned group) -{ - struct sirfsoc_pmx *spmx; - - spmx = pinctrl_dev_get_drvdata(pmxdev); - sirfsoc_pinmux_endisable(spmx, selector, false); -} - static int sirfsoc_pinmux_get_funcs_count(struct pinctrl_dev *pmxdev) { return sirfsoc_pmxfunc_cnt; @@ -240,7 +231,6 @@ static int sirfsoc_pinmux_request_gpio(struct pinctrl_dev *pmxdev, static struct pinmux_ops sirfsoc_pinmux_ops = { .enable = sirfsoc_pinmux_enable, - .disable = sirfsoc_pinmux_disable, .get_functions_count = sirfsoc_pinmux_get_funcs_count, .get_function_name = sirfsoc_pinmux_get_func_name, .get_function_groups = sirfsoc_pinmux_get_groups, diff --git a/drivers/pinctrl/spear/pinctrl-spear.c b/drivers/pinctrl/spear/pinctrl-spear.c index 58bf6867aa17..f72cc4e192bd 100644 --- a/drivers/pinctrl/spear/pinctrl-spear.c +++ b/drivers/pinctrl/spear/pinctrl-spear.c @@ -274,12 +274,6 @@ static int spear_pinctrl_enable(struct pinctrl_dev *pctldev, unsigned function, return spear_pinctrl_endisable(pctldev, function, group, true); } -static void spear_pinctrl_disable(struct pinctrl_dev *pctldev, - unsigned function, unsigned group) -{ - spear_pinctrl_endisable(pctldev, function, group, false); -} - /* gpio with pinmux */ static struct spear_gpio_pingroup *get_gpio_pingroup(struct spear_pmx *pmx, unsigned pin) @@ -345,7 +339,6 @@ static const struct pinmux_ops spear_pinmux_ops = { .get_function_name = spear_pinctrl_get_func_name, .get_function_groups = spear_pinctrl_get_func_groups, .enable = spear_pinctrl_enable, - .disable = spear_pinctrl_disable, .gpio_request_enable = gpio_request_enable, .gpio_disable_free = gpio_disable_free, }; diff --git a/drivers/pinctrl/vt8500/pinctrl-wmt.c b/drivers/pinctrl/vt8500/pinctrl-wmt.c index 2c61281bebd7..8c976c21eeee 100644 --- a/drivers/pinctrl/vt8500/pinctrl-wmt.c +++ b/drivers/pinctrl/vt8500/pinctrl-wmt.c @@ -141,17 +141,6 @@ static int wmt_pmx_enable(struct pinctrl_dev *pctldev, return wmt_set_pinmux(data, func_selector, pinnum); } -static void wmt_pmx_disable(struct pinctrl_dev *pctldev, - unsigned func_selector, - unsigned group_selector) -{ - struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); - u32 pinnum = data->pins[group_selector].number; - - /* disable by setting GPIO_IN */ - wmt_set_pinmux(data, WMT_FSEL_GPIO_IN, pinnum); -} - static void wmt_pmx_gpio_disable_free(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset) @@ -180,7 +169,6 @@ static struct pinmux_ops wmt_pinmux_ops = { .get_function_name = wmt_pmx_get_function_name, .get_function_groups = wmt_pmx_get_function_groups, .enable = wmt_pmx_enable, - .disable = wmt_pmx_disable, .gpio_disable_free = wmt_pmx_gpio_disable_free, .gpio_set_direction = wmt_pmx_gpio_set_direction, }; diff --git a/include/linux/pinctrl/pinmux.h b/include/linux/pinctrl/pinmux.h index c15395031cb3..3097aafbeb24 100644 --- a/include/linux/pinctrl/pinmux.h +++ b/include/linux/pinctrl/pinmux.h @@ -70,8 +70,6 @@ struct pinmux_ops { unsigned * const num_groups); int (*enable) (struct pinctrl_dev *pctldev, unsigned func_selector, unsigned group_selector); - void (*disable) (struct pinctrl_dev *pctldev, unsigned func_selector, - unsigned group_selector); int (*gpio_request_enable) (struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset); -- cgit v1.2.3 From be306dac534008f7f47839fbbe241c1608c25ce1 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 19 Jun 2014 13:37:07 +0200 Subject: of: Add NVIDIA Tegra XUSB pad controller binding This patch adds the device tree binding documentation for the XUSB pad controller found on NVIDIA Tegra SoCs. It exposes both pinmuxing and PHY capabilities. Tested-by: Mikko Perttunen Acked-by: Linus Walleij Signed-off-by: Thierry Reding --- .../pinctrl/nvidia,tegra124-xusb-padctl.txt | 127 +++++++++++++++++++++ include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h | 7 ++ 2 files changed, 134 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/nvidia,tegra124-xusb-padctl.txt create mode 100644 include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra124-xusb-padctl.txt b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra124-xusb-padctl.txt new file mode 100644 index 000000000000..2f9c0bd66457 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra124-xusb-padctl.txt @@ -0,0 +1,127 @@ +Device tree binding for NVIDIA Tegra XUSB pad controller +======================================================== + +The Tegra XUSB pad controller manages a set of lanes, each of which can be +assigned to one out of a set of different pads. Some of these pads have an +associated PHY that must be powered up before the pad can be used. + +This document defines the device-specific binding for the XUSB pad controller. + +Refer to pinctrl-bindings.txt in this directory for generic information about +pin controller device tree bindings and ../phy/phy-bindings.txt for details on +how to describe and reference PHYs in device trees. + +Required properties: +-------------------- +- compatible: should be "nvidia,tegra124-xusb-padctl" +- reg: Physical base address and length of the controller's registers. +- resets: Must contain an entry for each entry in reset-names. + See ../reset/reset.txt for details. +- reset-names: Must include the following entries: + - padctl +- #phy-cells: Should be 1. The specifier is the index of the PHY to reference. + See for the list of valid values. + +Lane muxing: +------------ + +Child nodes contain the pinmux configurations following the conventions from +the pinctrl-bindings.txt document. Typically a single, static configuration is +given and applied at boot time. + +Each subnode describes groups of lanes along with parameters and pads that +they should be assigned to. The name of these subnodes is not important. All +subnodes should be parsed solely based on their content. + +Each subnode only applies the parameters that are explicitly listed. In other +words, if a subnode that lists a function but no pin configuration parameters +implies no information about any pin configuration parameters. Similarly, a +subnode that describes only an IDDQ parameter implies no information about +what function the pins are assigned to. For this reason even seemingly boolean +values are actually tristates in this binding: unspecified, off or on. +Unspecified is represented as an absent property, and off/on are represented +as integer values 0 and 1. + +Required properties: +- nvidia,lanes: An array of strings. Each string is the name of a lane. + +Optional properties: +- nvidia,function: A string that is the name of the function (pad) that the + pin or group should be assigned to. Valid values for function names are + listed below. +- nvidia,iddq: Enables IDDQ mode of the lane. (0: no, 1: yes) + +Note that not all of these properties are valid for all lanes. Lanes can be +divided into three groups: + + - otg-0, otg-1, otg-2: + + Valid functions for this group are: "snps", "xusb", "uart", "rsvd". + + The nvidia,iddq property does not apply to this group. + + - ulpi-0, hsic-0, hsic-1: + + Valid functions for this group are: "snps", "xusb". + + The nvidia,iddq property does not apply to this group. + + - pcie-0, pcie-1, pcie-2, pcie-3, pcie-4, sata-0: + + Valid functions for this group are: "pcie", "usb3", "sata", "rsvd". + + +Example: +======== + +SoC file extract: +----------------- + + padctl@0,7009f000 { + compatible = "nvidia,tegra124-xusb-padctl"; + reg = <0x0 0x7009f000 0x0 0x1000>; + resets = <&tegra_car 142>; + reset-names = "padctl"; + + #phy-cells = <1>; + }; + +Board file extract: +------------------- + + pcie-controller@0,01003000 { + ... + + phys = <&padctl 0>; + phy-names = "pcie"; + + ... + }; + + ... + + padctl: padctl@0,7009f000 { + pinctrl-0 = <&padctl_default>; + pinctrl-names = "default"; + + padctl_default: pinmux { + usb3 { + nvidia,lanes = "pcie-0", "pcie-1"; + nvidia,function = "usb3"; + nvidia,iddq = <0>; + }; + + pcie { + nvidia,lanes = "pcie-2", "pcie-3", + "pcie-4"; + nvidia,function = "pcie"; + nvidia,iddq = <0>; + }; + + sata { + nvidia,lanes = "sata-0"; + nvidia,function = "sata"; + nvidia,iddq = <0>; + }; + }; + }; diff --git a/include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h b/include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h new file mode 100644 index 000000000000..914d56da9324 --- /dev/null +++ b/include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h @@ -0,0 +1,7 @@ +#ifndef _DT_BINDINGS_PINCTRL_TEGRA_XUSB_H +#define _DT_BINDINGS_PINCTRL_TEGRA_XUSB_H 1 + +#define TEGRA_XUSB_PADCTL_PCIE 0 +#define TEGRA_XUSB_PADCTL_SATA 1 + +#endif /* _DT_BINDINGS_PINCTRL_TEGRA_XUSB_H */ -- cgit v1.2.3 From 7e435aad38083f378ba7889d8b8fdcdbfe7088e7 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 15 Jun 2014 11:07:12 +0100 Subject: drm: add of_graph endpoint helper to find possible CRTCs Add a helper to allow encoders to find their possible CRTCs from the OF graph without having to re-implement this functionality. We add a device_node to drm_crtc which corresponds with the port node in the DT description of the CRTC device. We can then scan the DRM device list for CRTCs to find their index, matching the appropriate CRTC using the port device_node, thus building up the possible CRTC mask. Reviewed-by: Rob Clark Signed-off-by: Russell King --- drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/drm_of.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++ include/drm/drm_crtc.h | 2 ++ include/drm/drm_of.h | 18 +++++++++++++ 4 files changed, 88 insertions(+) create mode 100644 drivers/gpu/drm/drm_of.c create mode 100644 include/drm/drm_of.h (limited to 'include') diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index dd2ba4269740..533d011eab3e 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -20,6 +20,7 @@ drm-$(CONFIG_COMPAT) += drm_ioc32.o drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o drm-$(CONFIG_PCI) += ati_pcigart.o drm-$(CONFIG_DRM_PANEL) += drm_panel.o +drm-$(CONFIG_OF) += drm_of.o drm-usb-y := drm_usb.o diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c new file mode 100644 index 000000000000..16150a00c237 --- /dev/null +++ b/drivers/gpu/drm/drm_of.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include +#include + +/** + * drm_crtc_port_mask - find the mask of a registered CRTC by port OF node + * @dev: DRM device + * @port: port OF node + * + * Given a port OF node, return the possible mask of the corresponding + * CRTC within a device's list of CRTCs. Returns zero if not found. + */ +static uint32_t drm_crtc_port_mask(struct drm_device *dev, + struct device_node *port) +{ + unsigned int index = 0; + struct drm_crtc *tmp; + + list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) { + if (tmp->port == port) + return 1 << index; + + index++; + } + + return 0; +} + +/** + * drm_of_find_possible_crtcs - find the possible CRTCs for an encoder port + * @dev: DRM device + * @port: encoder port to scan for endpoints + * + * Scan all endpoints attached to a port, locate their attached CRTCs, + * and generate the DRM mask of CRTCs which may be attached to this + * encoder. + * + * See Documentation/devicetree/bindings/graph.txt for the bindings. + */ +uint32_t drm_of_find_possible_crtcs(struct drm_device *dev, + struct device_node *port) +{ + struct device_node *remote_port, *ep = NULL; + uint32_t possible_crtcs = 0; + + do { + ep = of_graph_get_next_endpoint(port, ep); + if (!ep) + break; + + remote_port = of_graph_get_remote_port(ep); + if (!remote_port) { + of_node_put(ep); + return 0; + } + + possible_crtcs |= drm_crtc_port_mask(dev, remote_port); + + of_node_put(remote_port); + } while (1); + + return possible_crtcs; +} +EXPORT_SYMBOL(drm_of_find_possible_crtcs); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 251b75e6bf7a..6a94909f1ca9 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -41,6 +41,7 @@ struct drm_framebuffer; struct drm_object_properties; struct drm_file; struct drm_clip_rect; +struct device_node; #define DRM_MODE_OBJECT_CRTC 0xcccccccc #define DRM_MODE_OBJECT_CONNECTOR 0xc0c0c0c0 @@ -314,6 +315,7 @@ struct drm_crtc_funcs { */ struct drm_crtc { struct drm_device *dev; + struct device_node *port; struct list_head head; /** diff --git a/include/drm/drm_of.h b/include/drm/drm_of.h new file mode 100644 index 000000000000..2441f7112074 --- /dev/null +++ b/include/drm/drm_of.h @@ -0,0 +1,18 @@ +#ifndef __DRM_OF_H__ +#define __DRM_OF_H__ + +struct drm_device; +struct device_node; + +#ifdef CONFIG_OF +extern uint32_t drm_of_find_possible_crtcs(struct drm_device *dev, + struct device_node *port); +#else +static inline uint32_t drm_of_find_possible_crtcs(struct drm_device *dev, + struct device_node *port) +{ + return 0; +} +#endif + +#endif /* __DRM_OF_H__ */ -- cgit v1.2.3 From 2d7768a872b73a7105d6355948dae0acff72f061 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 7 Aug 2014 08:16:00 +0100 Subject: iio: st_sensors: add devicetree probing support The I2C devices that make up the STMicroelectronics MEMS sensors may be sneakily enabled by cleverly giving the device node the same name as a string match from the platform device ID table. However the right method is to use the compatible string. On detection, the ST sensors use the ID string to probe and instatiate the right sensor driver, so pass the kernel-internal ID string in the .data field of the OF match table, and set the I2C client name to this name when a compatible match is used. This avoids having misc Linux-specific strings floating around in the device tree. Cc: Lee Jones Cc: Denis CIOCCA Signed-off-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel_i2c.c | 51 ++++++++++++++++++++++++++ drivers/iio/common/st_sensors/st_sensors_i2c.c | 30 +++++++++++++++ drivers/iio/gyro/st_gyro_i2c.c | 39 ++++++++++++++++++++ drivers/iio/magnetometer/st_magn_i2c.c | 23 ++++++++++++ drivers/iio/pressure/st_pressure_i2c.c | 23 ++++++++++++ include/linux/iio/common/st_sensors_i2c.h | 11 ++++++ 6 files changed, 177 insertions(+) (limited to 'include') diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index d7bedbdfc81d..7164aeff3ab1 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -18,6 +18,55 @@ #include #include "st_accel.h" +#ifdef CONFIG_OF +static const struct of_device_id st_accel_of_match[] = { + { + .compatible = "st,lsm303dlh-accel", + .data = LSM303DLH_ACCEL_DEV_NAME, + }, + { + .compatible = "st,lsm303dlhc-accel", + .data = LSM303DLHC_ACCEL_DEV_NAME, + }, + { + .compatible = "st,lis3dh-accel", + .data = LIS3DH_ACCEL_DEV_NAME, + }, + { + .compatible = "st,lsm330d-accel", + .data = LSM330D_ACCEL_DEV_NAME, + }, + { + .compatible = "st,lsm330dl-accel", + .data = LSM330DL_ACCEL_DEV_NAME, + }, + { + .compatible = "st,lsm330dlc-accel", + .data = LSM330DLC_ACCEL_DEV_NAME, + }, + { + .compatible = "st,lis331dlh-accel", + .data = LIS331DLH_ACCEL_DEV_NAME, + }, + { + .compatible = "st,lsm303dl-accel", + .data = LSM303DL_ACCEL_DEV_NAME, + }, + { + .compatible = "st,lsm303dlm-accel", + .data = LSM303DLM_ACCEL_DEV_NAME, + }, + { + .compatible = "st,lsm330-accel", + .data = LSM330_ACCEL_DEV_NAME, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, st_accel_of_match); +#else +#define st_accel_of_match NULL +#endif + static int st_accel_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -31,6 +80,7 @@ static int st_accel_i2c_probe(struct i2c_client *client, adata = iio_priv(indio_dev); adata->dev = &client->dev; + st_sensors_of_i2c_probe(client, st_accel_of_match); st_sensors_i2c_configure(indio_dev, client, adata); @@ -67,6 +117,7 @@ static struct i2c_driver st_accel_driver = { .driver = { .owner = THIS_MODULE, .name = "st-accel-i2c", + .of_match_table = of_match_ptr(st_accel_of_match), }, .probe = st_accel_i2c_probe, .remove = st_accel_i2c_remove, diff --git a/drivers/iio/common/st_sensors/st_sensors_i2c.c b/drivers/iio/common/st_sensors/st_sensors_i2c.c index 38af9440c103..bb6f3085f57b 100644 --- a/drivers/iio/common/st_sensors/st_sensors_i2c.c +++ b/drivers/iio/common/st_sensors/st_sensors_i2c.c @@ -12,6 +12,7 @@ #include #include #include +#include #include @@ -76,6 +77,35 @@ void st_sensors_i2c_configure(struct iio_dev *indio_dev, } EXPORT_SYMBOL(st_sensors_i2c_configure); +#ifdef CONFIG_OF +/** + * st_sensors_of_i2c_probe() - device tree probe for ST I2C sensors + * @client: the I2C client device for the sensor + * @match: the OF match table for the device, containing compatible strings + * but also a .data field with the corresponding internal kernel name + * used by this sensor. + * + * In effect this function matches a compatible string to an internal kernel + * name for a certain sensor device, so that the rest of the autodetection can + * rely on that name from this point on. I2C client devices will be renamed + * to match the internal kernel convention. + */ +void st_sensors_of_i2c_probe(struct i2c_client *client, + const struct of_device_id *match) +{ + const struct of_device_id *of_id; + + of_id = of_match_device(match, &client->dev); + if (!of_id) + return; + + /* The name from the OF match takes precedence if present */ + strncpy(client->name, of_id->data, sizeof(client->name)); + client->name[sizeof(client->name) - 1] = '\0'; +} +EXPORT_SYMBOL(st_sensors_of_i2c_probe); +#endif + MODULE_AUTHOR("Denis Ciocca "); MODULE_DESCRIPTION("STMicroelectronics ST-sensors i2c driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/gyro/st_gyro_i2c.c b/drivers/iio/gyro/st_gyro_i2c.c index 23c12f361b05..8fa0ad2ef4ef 100644 --- a/drivers/iio/gyro/st_gyro_i2c.c +++ b/drivers/iio/gyro/st_gyro_i2c.c @@ -18,6 +18,43 @@ #include #include "st_gyro.h" +#ifdef CONFIG_OF +static const struct of_device_id st_gyro_of_match[] = { + { + .compatible = "st,l3g4200d-gyro", + .data = L3G4200D_GYRO_DEV_NAME, + }, + { + .compatible = "st,lsm330d-gyro", + .data = LSM330D_GYRO_DEV_NAME, + }, + { + .compatible = "st,lsm330dl-gyro", + .data = LSM330DL_GYRO_DEV_NAME, + }, + { + .compatible = "st,lsm330dlc-gyro", + .data = LSM330DLC_GYRO_DEV_NAME, + }, + { + .compatible = "st,l3gd20-gyro", + .data = L3GD20_GYRO_DEV_NAME, + }, + { + .compatible = "st,l3g4is-gyro", + .data = L3G4IS_GYRO_DEV_NAME, + }, + { + .compatible = "st,lsm330-gyro", + .data = LSM330_GYRO_DEV_NAME, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, st_gyro_of_match); +#else +#define st_gyro_of_match NULL +#endif + static int st_gyro_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -31,6 +68,7 @@ static int st_gyro_i2c_probe(struct i2c_client *client, gdata = iio_priv(indio_dev); gdata->dev = &client->dev; + st_sensors_of_i2c_probe(client, st_gyro_of_match); st_sensors_i2c_configure(indio_dev, client, gdata); @@ -65,6 +103,7 @@ static struct i2c_driver st_gyro_driver = { .driver = { .owner = THIS_MODULE, .name = "st-gyro-i2c", + .of_match_table = of_match_ptr(st_gyro_of_match), }, .probe = st_gyro_i2c_probe, .remove = st_gyro_i2c_remove, diff --git a/drivers/iio/magnetometer/st_magn_i2c.c b/drivers/iio/magnetometer/st_magn_i2c.c index 892e0feeb5c1..689250058442 100644 --- a/drivers/iio/magnetometer/st_magn_i2c.c +++ b/drivers/iio/magnetometer/st_magn_i2c.c @@ -18,6 +18,27 @@ #include #include "st_magn.h" +#ifdef CONFIG_OF +static const struct of_device_id st_magn_of_match[] = { + { + .compatible = "st,lsm303dlhc-magn", + .data = LSM303DLHC_MAGN_DEV_NAME, + }, + { + .compatible = "st,lsm303dlm-magn", + .data = LSM303DLM_MAGN_DEV_NAME, + }, + { + .compatible = "st,lis3mdl-magn", + .data = LIS3MDL_MAGN_DEV_NAME, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, st_magn_of_match); +#else +#define st_magn_of_match NULL +#endif + static int st_magn_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -31,6 +52,7 @@ static int st_magn_i2c_probe(struct i2c_client *client, mdata = iio_priv(indio_dev); mdata->dev = &client->dev; + st_sensors_of_i2c_probe(client, st_magn_of_match); st_sensors_i2c_configure(indio_dev, client, mdata); @@ -61,6 +83,7 @@ static struct i2c_driver st_magn_driver = { .driver = { .owner = THIS_MODULE, .name = "st-magn-i2c", + .of_match_table = of_match_ptr(st_magn_of_match), }, .probe = st_magn_i2c_probe, .remove = st_magn_i2c_remove, diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c index 3cd73e39b840..acaf165260bb 100644 --- a/drivers/iio/pressure/st_pressure_i2c.c +++ b/drivers/iio/pressure/st_pressure_i2c.c @@ -18,6 +18,27 @@ #include #include "st_pressure.h" +#ifdef CONFIG_OF +static const struct of_device_id st_press_of_match[] = { + { + .compatible = "st,lps001wp-press", + .data = LPS001WP_PRESS_DEV_NAME, + }, + { + .compatible = "st,lps25h-press", + .data = LPS25H_PRESS_DEV_NAME, + }, + { + .compatible = "st,lps331ap-press", + .data = LPS331AP_PRESS_DEV_NAME, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, st_press_of_match); +#else +#define st_press_of_match NULL +#endif + static int st_press_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -31,6 +52,7 @@ static int st_press_i2c_probe(struct i2c_client *client, pdata = iio_priv(indio_dev); pdata->dev = &client->dev; + st_sensors_of_i2c_probe(client, st_press_of_match); st_sensors_i2c_configure(indio_dev, client, pdata); @@ -60,6 +82,7 @@ static struct i2c_driver st_press_driver = { .driver = { .owner = THIS_MODULE, .name = "st-press-i2c", + .of_match_table = of_match_ptr(st_press_of_match), }, .probe = st_press_i2c_probe, .remove = st_press_i2c_remove, diff --git a/include/linux/iio/common/st_sensors_i2c.h b/include/linux/iio/common/st_sensors_i2c.h index 67d845385ae2..1796af093368 100644 --- a/include/linux/iio/common/st_sensors_i2c.h +++ b/include/linux/iio/common/st_sensors_i2c.h @@ -13,8 +13,19 @@ #include #include +#include void st_sensors_i2c_configure(struct iio_dev *indio_dev, struct i2c_client *client, struct st_sensor_data *sdata); +#ifdef CONFIG_OF +void st_sensors_of_i2c_probe(struct i2c_client *client, + const struct of_device_id *match); +#else +static inline void st_sensors_of_i2c_probe(struct i2c_client *client, + const struct of_device_id *match) +{ +} +#endif + #endif /* ST_SENSORS_I2C_H */ -- cgit v1.2.3 From 77a533c73f032050be8b447828358a228a0a5736 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Thu, 7 Aug 2014 23:29:00 +0100 Subject: iio: core : events ABI for specifying period The iio sysfs ABI defines a way to specify period for roc and thresholds. What: /sys/.../events/in_accel_x_thresh_rising_period What: /sys/.../events/in_accel_x_thresh_falling_period what: /sys/.../events/in_accel_x_roc_rising_period What: /sys/.../events/in_accel_x_roc_falling_period But there is no way to add period with the current event info enum. Added IIO_EV_INFO_PERIOD and corresponding string. Signed-off-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-event.c | 1 + include/linux/iio/types.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include') diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index 258a973a1fb8..35a5b0311dae 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -209,6 +209,7 @@ static const char * const iio_ev_info_text[] = { [IIO_EV_INFO_ENABLE] = "en", [IIO_EV_INFO_VALUE] = "value", [IIO_EV_INFO_HYSTERESIS] = "hysteresis", + [IIO_EV_INFO_PERIOD] = "period", }; static enum iio_event_direction iio_ev_attr_dir(struct iio_dev_attr *attr) diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index d480631eabc2..4a848d6be3bf 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -70,6 +70,7 @@ enum iio_event_info { IIO_EV_INFO_ENABLE, IIO_EV_INFO_VALUE, IIO_EV_INFO_HYSTERESIS, + IIO_EV_INFO_PERIOD, }; enum iio_event_direction { -- cgit v1.2.3 From 5f775498bdc44f294c37eaa7a205335e3b6667df Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 10 Jul 2014 09:18:29 +0100 Subject: clk: qcom: Fully support apq8064 global clock control Add in the handful of new clocks and introduce a new reset table with the few new resets. Signed-off-by: Stephen Boyd --- drivers/clk/qcom/gcc-msm8960.c | 573 ++++++++++++++++++++++++++- include/dt-bindings/clock/qcom,gcc-msm8960.h | 11 + include/dt-bindings/reset/qcom,gcc-msm8960.h | 16 + 3 files changed, 596 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c index 4cb2927889ce..007534f7a2d7 100644 --- a/drivers/clk/qcom/gcc-msm8960.c +++ b/drivers/clk/qcom/gcc-msm8960.c @@ -104,6 +104,7 @@ static struct clk_regmap pll14_vote = { #define P_PXO 0 #define P_PLL8 1 +#define P_PLL3 2 #define P_CXO 2 static const u8 gcc_pxo_pll8_map[] = { @@ -128,6 +129,18 @@ static const char *gcc_pxo_pll8_cxo[] = { "cxo", }; +static const u8 gcc_pxo_pll8_pll3_map[] = { + [P_PXO] = 0, + [P_PLL8] = 3, + [P_PLL3] = 6, +}; + +static const char *gcc_pxo_pll8_pll3[] = { + "pxo", + "pll8_vote", + "pll3", +}; + static struct freq_tbl clk_tbl_gsbi_uart[] = { { 1843200, P_PLL8, 2, 6, 625 }, { 3686400, P_PLL8, 2, 12, 625 }, @@ -1928,6 +1941,104 @@ static struct clk_branch usb_hs1_xcvr_clk = { }, }; +static struct clk_rcg usb_hs3_xcvr_src = { + .ns_reg = 0x370c, + .md_reg = 0x3708, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll8_map, + }, + .freq_tbl = clk_tbl_usb, + .clkr = { + .enable_reg = 0x370c, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "usb_hs3_xcvr_src", + .parent_names = gcc_pxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + } +}; + +static struct clk_branch usb_hs3_xcvr_clk = { + .halt_reg = 0x2fc8, + .halt_bit = 30, + .clkr = { + .enable_reg = 0x370c, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "usb_hs3_xcvr_clk", + .parent_names = (const char *[]){ "usb_hs3_xcvr_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg usb_hs4_xcvr_src = { + .ns_reg = 0x372c, + .md_reg = 0x3728, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll8_map, + }, + .freq_tbl = clk_tbl_usb, + .clkr = { + .enable_reg = 0x372c, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "usb_hs4_xcvr_src", + .parent_names = gcc_pxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + } +}; + +static struct clk_branch usb_hs4_xcvr_clk = { + .halt_reg = 0x2fc8, + .halt_bit = 2, + .clkr = { + .enable_reg = 0x372c, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "usb_hs4_xcvr_clk", + .parent_names = (const char *[]){ "usb_hs4_xcvr_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + static struct clk_rcg usb_hsic_xcvr_fs_src = { .ns_reg = 0x2928, .md_reg = 0x2924, @@ -2456,6 +2567,34 @@ static struct clk_branch usb_hs1_h_clk = { }, }; +static struct clk_branch usb_hs3_h_clk = { + .halt_reg = 0x2fc8, + .halt_bit = 31, + .clkr = { + .enable_reg = 0x3700, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "usb_hs3_h_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch usb_hs4_h_clk = { + .halt_reg = 0x2fc8, + .halt_bit = 7, + .clkr = { + .enable_reg = 0x3720, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "usb_hs4_h_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + static struct clk_branch usb_hsic_h_clk = { .halt_reg = 0x2fcc, .halt_bit = 28, @@ -2582,6 +2721,244 @@ static struct clk_branch adm0_pbus_clk = { }, }; +static struct freq_tbl clk_tbl_ce3[] = { + { 48000000, P_PLL8, 8 }, + { 100000000, P_PLL3, 12 }, + { 120000000, P_PLL3, 10 }, + { } +}; + +static struct clk_rcg ce3_src = { + .ns_reg = 0x36c0, + .p = { + .pre_div_shift = 3, + .pre_div_width = 4, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll8_pll3_map, + }, + .freq_tbl = clk_tbl_ce3, + .clkr = { + .enable_reg = 0x2c08, + .enable_mask = BIT(7), + .hw.init = &(struct clk_init_data){ + .name = "ce3_src", + .parent_names = gcc_pxo_pll8_pll3, + .num_parents = 3, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + }, +}; + +static struct clk_branch ce3_core_clk = { + .halt_reg = 0x2fdc, + .halt_bit = 5, + .clkr = { + .enable_reg = 0x36c4, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "ce3_core_clk", + .parent_names = (const char *[]){ "ce3_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch ce3_h_clk = { + .halt_reg = 0x2fc4, + .halt_bit = 16, + .clkr = { + .enable_reg = 0x36c4, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "ce3_h_clk", + .parent_names = (const char *[]){ "ce3_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static const struct freq_tbl clk_tbl_sata_ref[] = { + { 48000000, P_PLL8, 8, 0, 0 }, + { 100000000, P_PLL3, 12, 0, 0 }, + { } +}; + +static struct clk_rcg sata_clk_src = { + .ns_reg = 0x2c08, + .p = { + .pre_div_shift = 3, + .pre_div_width = 4, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll8_pll3_map, + }, + .freq_tbl = clk_tbl_sata_ref, + .clkr = { + .enable_reg = 0x2c08, + .enable_mask = BIT(7), + .hw.init = &(struct clk_init_data){ + .name = "sata_clk_src", + .parent_names = gcc_pxo_pll8_pll3, + .num_parents = 3, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + }, +}; + +static struct clk_branch sata_rxoob_clk = { + .halt_reg = 0x2fdc, + .halt_bit = 26, + .clkr = { + .enable_reg = 0x2c0c, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "sata_rxoob_clk", + .parent_names = (const char *[]){ "sata_clk_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch sata_pmalive_clk = { + .halt_reg = 0x2fdc, + .halt_bit = 25, + .clkr = { + .enable_reg = 0x2c10, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "sata_pmalive_clk", + .parent_names = (const char *[]){ "sata_clk_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch sata_phy_ref_clk = { + .halt_reg = 0x2fdc, + .halt_bit = 24, + .clkr = { + .enable_reg = 0x2c14, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "sata_phy_ref_clk", + .parent_names = (const char *[]){ "pxo" }, + .num_parents = 1, + .ops = &clk_branch_ops, + }, + }, +}; + +static struct clk_branch sata_a_clk = { + .halt_reg = 0x2fc0, + .halt_bit = 12, + .clkr = { + .enable_reg = 0x2c20, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "sata_a_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch sata_h_clk = { + .halt_reg = 0x2fdc, + .halt_bit = 27, + .clkr = { + .enable_reg = 0x2c00, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "sata_h_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch sfab_sata_s_h_clk = { + .halt_reg = 0x2fc4, + .halt_bit = 14, + .clkr = { + .enable_reg = 0x2480, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "sfab_sata_s_h_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch sata_phy_cfg_clk = { + .halt_reg = 0x2fcc, + .halt_bit = 12, + .clkr = { + .enable_reg = 0x2c40, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "sata_phy_cfg_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch pcie_phy_ref_clk = { + .halt_reg = 0x2fdc, + .halt_bit = 29, + .clkr = { + .enable_reg = 0x22d0, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "pcie_phy_ref_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch pcie_h_clk = { + .halt_reg = 0x2fd4, + .halt_bit = 8, + .clkr = { + .enable_reg = 0x22cc, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "pcie_h_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch pcie_a_clk = { + .halt_reg = 0x2fc0, + .halt_bit = 13, + .clkr = { + .enable_reg = 0x22c0, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "pcie_a_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + static struct clk_branch pmic_arb0_h_clk = { .halt_reg = 0x2fd8, .halt_check = BRANCH_HALT_VOTED, @@ -2869,13 +3246,48 @@ static const struct qcom_reset_map gcc_msm8960_resets[] = { }; static struct clk_regmap *gcc_apq8064_clks[] = { + [PLL3] = &pll3.clkr, [PLL8] = &pll8.clkr, [PLL8_VOTE] = &pll8_vote, + [PLL14] = &pll14.clkr, + [PLL14_VOTE] = &pll14_vote, + [GSBI1_UART_SRC] = &gsbi1_uart_src.clkr, + [GSBI1_UART_CLK] = &gsbi1_uart_clk.clkr, + [GSBI2_UART_SRC] = &gsbi2_uart_src.clkr, + [GSBI2_UART_CLK] = &gsbi2_uart_clk.clkr, + [GSBI3_UART_SRC] = &gsbi3_uart_src.clkr, + [GSBI3_UART_CLK] = &gsbi3_uart_clk.clkr, + [GSBI4_UART_SRC] = &gsbi4_uart_src.clkr, + [GSBI4_UART_CLK] = &gsbi4_uart_clk.clkr, + [GSBI5_UART_SRC] = &gsbi5_uart_src.clkr, + [GSBI5_UART_CLK] = &gsbi5_uart_clk.clkr, + [GSBI6_UART_SRC] = &gsbi6_uart_src.clkr, + [GSBI6_UART_CLK] = &gsbi6_uart_clk.clkr, [GSBI7_UART_SRC] = &gsbi7_uart_src.clkr, [GSBI7_UART_CLK] = &gsbi7_uart_clk.clkr, + [GSBI1_QUP_SRC] = &gsbi1_qup_src.clkr, + [GSBI1_QUP_CLK] = &gsbi1_qup_clk.clkr, + [GSBI2_QUP_SRC] = &gsbi2_qup_src.clkr, + [GSBI2_QUP_CLK] = &gsbi2_qup_clk.clkr, + [GSBI3_QUP_SRC] = &gsbi3_qup_src.clkr, + [GSBI3_QUP_CLK] = &gsbi3_qup_clk.clkr, + [GSBI4_QUP_SRC] = &gsbi4_qup_src.clkr, + [GSBI4_QUP_CLK] = &gsbi4_qup_clk.clkr, + [GSBI5_QUP_SRC] = &gsbi5_qup_src.clkr, + [GSBI5_QUP_CLK] = &gsbi5_qup_clk.clkr, + [GSBI6_QUP_SRC] = &gsbi6_qup_src.clkr, + [GSBI6_QUP_CLK] = &gsbi6_qup_clk.clkr, [GSBI7_QUP_SRC] = &gsbi7_qup_src.clkr, [GSBI7_QUP_CLK] = &gsbi7_qup_clk.clkr, - [GSBI7_H_CLK] = &gsbi7_h_clk.clkr, + [GP0_SRC] = &gp0_src.clkr, + [GP0_CLK] = &gp0_clk.clkr, + [GP1_SRC] = &gp1_src.clkr, + [GP1_CLK] = &gp1_clk.clkr, + [GP2_SRC] = &gp2_src.clkr, + [GP2_CLK] = &gp2_clk.clkr, + [PMEM_A_CLK] = &pmem_clk.clkr, + [PRNG_SRC] = &prng_src.clkr, + [PRNG_CLK] = &prng_clk.clkr, [SDC1_SRC] = &sdc1_src.clkr, [SDC1_CLK] = &sdc1_clk.clkr, [SDC2_SRC] = &sdc2_src.clkr, @@ -2884,10 +3296,155 @@ static struct clk_regmap *gcc_apq8064_clks[] = { [SDC3_CLK] = &sdc3_clk.clkr, [SDC4_SRC] = &sdc4_src.clkr, [SDC4_CLK] = &sdc4_clk.clkr, + [TSIF_REF_SRC] = &tsif_ref_src.clkr, + [TSIF_REF_CLK] = &tsif_ref_clk.clkr, + [USB_HS1_XCVR_SRC] = &usb_hs1_xcvr_src.clkr, + [USB_HS1_XCVR_CLK] = &usb_hs1_xcvr_clk.clkr, + [USB_HS3_XCVR_SRC] = &usb_hs3_xcvr_src.clkr, + [USB_HS3_XCVR_CLK] = &usb_hs3_xcvr_clk.clkr, + [USB_HS4_XCVR_SRC] = &usb_hs4_xcvr_src.clkr, + [USB_HS4_XCVR_CLK] = &usb_hs4_xcvr_clk.clkr, + [USB_HSIC_XCVR_FS_SRC] = &usb_hsic_xcvr_fs_src.clkr, + [USB_HSIC_XCVR_FS_CLK] = &usb_hsic_xcvr_fs_clk.clkr, + [USB_HSIC_SYSTEM_CLK] = &usb_hsic_system_clk.clkr, + [USB_HSIC_HSIC_CLK] = &usb_hsic_hsic_clk.clkr, + [USB_HSIC_HSIO_CAL_CLK] = &usb_hsic_hsio_cal_clk.clkr, + [USB_FS1_XCVR_FS_SRC] = &usb_fs1_xcvr_fs_src.clkr, + [USB_FS1_XCVR_FS_CLK] = &usb_fs1_xcvr_fs_clk.clkr, + [USB_FS1_SYSTEM_CLK] = &usb_fs1_system_clk.clkr, + [SATA_H_CLK] = &sata_h_clk.clkr, + [SATA_CLK_SRC] = &sata_clk_src.clkr, + [SATA_RXOOB_CLK] = &sata_rxoob_clk.clkr, + [SATA_PMALIVE_CLK] = &sata_pmalive_clk.clkr, + [SATA_PHY_REF_CLK] = &sata_phy_ref_clk.clkr, + [SATA_PHY_CFG_CLK] = &sata_phy_cfg_clk.clkr, + [SATA_A_CLK] = &sata_a_clk.clkr, + [SFAB_SATA_S_H_CLK] = &sfab_sata_s_h_clk.clkr, + [CE3_SRC] = &ce3_src.clkr, + [CE3_CORE_CLK] = &ce3_core_clk.clkr, + [CE3_H_CLK] = &ce3_h_clk.clkr, + [DMA_BAM_H_CLK] = &dma_bam_h_clk.clkr, + [GSBI1_H_CLK] = &gsbi1_h_clk.clkr, + [GSBI2_H_CLK] = &gsbi2_h_clk.clkr, + [GSBI3_H_CLK] = &gsbi3_h_clk.clkr, + [GSBI4_H_CLK] = &gsbi4_h_clk.clkr, + [GSBI5_H_CLK] = &gsbi5_h_clk.clkr, + [GSBI6_H_CLK] = &gsbi6_h_clk.clkr, + [GSBI7_H_CLK] = &gsbi7_h_clk.clkr, + [TSIF_H_CLK] = &tsif_h_clk.clkr, + [USB_FS1_H_CLK] = &usb_fs1_h_clk.clkr, + [USB_HS1_H_CLK] = &usb_hs1_h_clk.clkr, + [USB_HSIC_H_CLK] = &usb_hsic_h_clk.clkr, + [USB_HS3_H_CLK] = &usb_hs3_h_clk.clkr, + [USB_HS4_H_CLK] = &usb_hs4_h_clk.clkr, [SDC1_H_CLK] = &sdc1_h_clk.clkr, [SDC2_H_CLK] = &sdc2_h_clk.clkr, [SDC3_H_CLK] = &sdc3_h_clk.clkr, [SDC4_H_CLK] = &sdc4_h_clk.clkr, + [ADM0_CLK] = &adm0_clk.clkr, + [ADM0_PBUS_CLK] = &adm0_pbus_clk.clkr, + [PCIE_A_CLK] = &pcie_a_clk.clkr, + [PCIE_PHY_REF_CLK] = &pcie_phy_ref_clk.clkr, + [PCIE_H_CLK] = &pcie_h_clk.clkr, + [PMIC_ARB0_H_CLK] = &pmic_arb0_h_clk.clkr, + [PMIC_ARB1_H_CLK] = &pmic_arb1_h_clk.clkr, + [PMIC_SSBI2_CLK] = &pmic_ssbi2_clk.clkr, + [RPM_MSG_RAM_H_CLK] = &rpm_msg_ram_h_clk.clkr, +}; + +static const struct qcom_reset_map gcc_apq8064_resets[] = { + [QDSS_STM_RESET] = { 0x2060, 6 }, + [AFAB_SMPSS_S_RESET] = { 0x20b8, 2 }, + [AFAB_SMPSS_M1_RESET] = { 0x20b8, 1 }, + [AFAB_SMPSS_M0_RESET] = { 0x20b8 }, + [AFAB_EBI1_CH0_RESET] = { 0x20c0, 7 }, + [AFAB_EBI1_CH1_RESET] = { 0x20c4, 7}, + [SFAB_ADM0_M0_RESET] = { 0x21e0, 7 }, + [SFAB_ADM0_M1_RESET] = { 0x21e4, 7 }, + [SFAB_ADM0_M2_RESET] = { 0x21e8, 7 }, + [ADM0_C2_RESET] = { 0x220c, 4}, + [ADM0_C1_RESET] = { 0x220c, 3}, + [ADM0_C0_RESET] = { 0x220c, 2}, + [ADM0_PBUS_RESET] = { 0x220c, 1 }, + [ADM0_RESET] = { 0x220c }, + [QDSS_CLKS_SW_RESET] = { 0x2260, 5 }, + [QDSS_POR_RESET] = { 0x2260, 4 }, + [QDSS_TSCTR_RESET] = { 0x2260, 3 }, + [QDSS_HRESET_RESET] = { 0x2260, 2 }, + [QDSS_AXI_RESET] = { 0x2260, 1 }, + [QDSS_DBG_RESET] = { 0x2260 }, + [SFAB_PCIE_M_RESET] = { 0x22d8, 1 }, + [SFAB_PCIE_S_RESET] = { 0x22d8 }, + [PCIE_EXT_PCI_RESET] = { 0x22dc, 6 }, + [PCIE_PHY_RESET] = { 0x22dc, 5 }, + [PCIE_PCI_RESET] = { 0x22dc, 4 }, + [PCIE_POR_RESET] = { 0x22dc, 3 }, + [PCIE_HCLK_RESET] = { 0x22dc, 2 }, + [PCIE_ACLK_RESET] = { 0x22dc }, + [SFAB_USB3_M_RESET] = { 0x2360, 7 }, + [SFAB_RIVA_M_RESET] = { 0x2380, 7 }, + [SFAB_LPASS_RESET] = { 0x23a0, 7 }, + [SFAB_AFAB_M_RESET] = { 0x23e0, 7 }, + [AFAB_SFAB_M0_RESET] = { 0x2420, 7 }, + [AFAB_SFAB_M1_RESET] = { 0x2424, 7 }, + [SFAB_SATA_S_RESET] = { 0x2480, 7 }, + [SFAB_DFAB_M_RESET] = { 0x2500, 7 }, + [DFAB_SFAB_M_RESET] = { 0x2520, 7 }, + [DFAB_SWAY0_RESET] = { 0x2540, 7 }, + [DFAB_SWAY1_RESET] = { 0x2544, 7 }, + [DFAB_ARB0_RESET] = { 0x2560, 7 }, + [DFAB_ARB1_RESET] = { 0x2564, 7 }, + [PPSS_PROC_RESET] = { 0x2594, 1 }, + [PPSS_RESET] = { 0x2594}, + [DMA_BAM_RESET] = { 0x25c0, 7 }, + [SPS_TIC_H_RESET] = { 0x2600, 7 }, + [SFAB_CFPB_M_RESET] = { 0x2680, 7 }, + [SFAB_CFPB_S_RESET] = { 0x26c0, 7 }, + [TSIF_H_RESET] = { 0x2700, 7 }, + [CE1_H_RESET] = { 0x2720, 7 }, + [CE1_CORE_RESET] = { 0x2724, 7 }, + [CE1_SLEEP_RESET] = { 0x2728, 7 }, + [CE2_H_RESET] = { 0x2740, 7 }, + [CE2_CORE_RESET] = { 0x2744, 7 }, + [SFAB_SFPB_M_RESET] = { 0x2780, 7 }, + [SFAB_SFPB_S_RESET] = { 0x27a0, 7 }, + [RPM_PROC_RESET] = { 0x27c0, 7 }, + [PMIC_SSBI2_RESET] = { 0x280c, 12 }, + [SDC1_RESET] = { 0x2830 }, + [SDC2_RESET] = { 0x2850 }, + [SDC3_RESET] = { 0x2870 }, + [SDC4_RESET] = { 0x2890 }, + [USB_HS1_RESET] = { 0x2910 }, + [USB_HSIC_RESET] = { 0x2934 }, + [USB_FS1_XCVR_RESET] = { 0x2974, 1 }, + [USB_FS1_RESET] = { 0x2974 }, + [GSBI1_RESET] = { 0x29dc }, + [GSBI2_RESET] = { 0x29fc }, + [GSBI3_RESET] = { 0x2a1c }, + [GSBI4_RESET] = { 0x2a3c }, + [GSBI5_RESET] = { 0x2a5c }, + [GSBI6_RESET] = { 0x2a7c }, + [GSBI7_RESET] = { 0x2a9c }, + [SPDM_RESET] = { 0x2b6c }, + [TLMM_H_RESET] = { 0x2ba0, 7 }, + [SATA_SFAB_M_RESET] = { 0x2c18 }, + [SATA_RESET] = { 0x2c1c }, + [GSS_SLP_RESET] = { 0x2c60, 7 }, + [GSS_RESET] = { 0x2c64 }, + [TSSC_RESET] = { 0x2ca0, 7 }, + [PDM_RESET] = { 0x2cc0, 12 }, + [MPM_H_RESET] = { 0x2da0, 7 }, + [MPM_RESET] = { 0x2da4 }, + [SFAB_SMPSS_S_RESET] = { 0x2e00, 7 }, + [PRNG_RESET] = { 0x2e80, 12 }, + [RIVA_RESET] = { 0x35e0 }, + [CE3_H_RESET] = { 0x36c4, 7 }, + [SFAB_CE3_M_RESET] = { 0x36c8, 1 }, + [SFAB_CE3_S_RESET] = { 0x36c8 }, + [CE3_RESET] = { 0x36cc, 7 }, + [CE3_SLEEP_RESET] = { 0x36d0, 7 }, + [USB_HS3_RESET] = { 0x3710 }, + [USB_HS4_RESET] = { 0x3730 }, }; static const struct regmap_config gcc_msm8960_regmap_config = { @@ -2898,6 +3455,14 @@ static const struct regmap_config gcc_msm8960_regmap_config = { .fast_io = true, }; +static const struct regmap_config gcc_apq8064_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x3880, + .fast_io = true, +}; + static const struct qcom_cc_desc gcc_msm8960_desc = { .config = &gcc_msm8960_regmap_config, .clks = gcc_msm8960_clks, @@ -2907,11 +3472,11 @@ static const struct qcom_cc_desc gcc_msm8960_desc = { }; static const struct qcom_cc_desc gcc_apq8064_desc = { - .config = &gcc_msm8960_regmap_config, + .config = &gcc_apq8064_regmap_config, .clks = gcc_apq8064_clks, .num_clks = ARRAY_SIZE(gcc_apq8064_clks), - .resets = gcc_msm8960_resets, - .num_resets = ARRAY_SIZE(gcc_msm8960_resets), + .resets = gcc_apq8064_resets, + .num_resets = ARRAY_SIZE(gcc_apq8064_resets), }; static const struct of_device_id gcc_msm8960_match_table[] = { diff --git a/include/dt-bindings/clock/qcom,gcc-msm8960.h b/include/dt-bindings/clock/qcom,gcc-msm8960.h index f9f547146a15..7d20eedfee98 100644 --- a/include/dt-bindings/clock/qcom,gcc-msm8960.h +++ b/include/dt-bindings/clock/qcom,gcc-msm8960.h @@ -308,5 +308,16 @@ #define PLL13 292 #define PLL14 293 #define PLL14_VOTE 294 +#define USB_HS3_H_CLK 295 +#define USB_HS3_XCVR_SRC 296 +#define USB_HS3_XCVR_CLK 297 +#define USB_HS4_H_CLK 298 +#define USB_HS4_XCVR_SRC 299 +#define USB_HS4_XCVR_CLK 300 +#define SATA_PHY_CFG_CLK 301 +#define SATA_A_CLK 302 +#define CE3_SRC 303 +#define CE3_CORE_CLK 304 +#define CE3_H_CLK 305 #endif diff --git a/include/dt-bindings/reset/qcom,gcc-msm8960.h b/include/dt-bindings/reset/qcom,gcc-msm8960.h index 07edd0e65eed..47c8686955da 100644 --- a/include/dt-bindings/reset/qcom,gcc-msm8960.h +++ b/include/dt-bindings/reset/qcom,gcc-msm8960.h @@ -114,5 +114,21 @@ #define SFAB_SMPSS_S_RESET 97 #define PRNG_RESET 98 #define RIVA_RESET 99 +#define USB_HS3_RESET 100 +#define USB_HS4_RESET 101 +#define CE3_RESET 102 +#define PCIE_EXT_PCI_RESET 103 +#define PCIE_PHY_RESET 104 +#define PCIE_PCI_RESET 105 +#define PCIE_POR_RESET 106 +#define PCIE_HCLK_RESET 107 +#define PCIE_ACLK_RESET 108 +#define CE3_H_RESET 109 +#define SFAB_CE3_M_RESET 110 +#define SFAB_CE3_S_RESET 111 +#define SATA_RESET 112 +#define CE3_SLEEP_RESET 113 +#define GSS_SLP_RESET 114 +#define GSS_RESET 115 #endif -- cgit v1.2.3 From 02824653200b31bd411f0def8006dc3d1c3ac4f4 Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Tue, 3 Jun 2014 17:24:09 +0300 Subject: clk: qcom: Add APQ8084 Global Clock Controller support This patch adds support for the global clock controller found on the APQ8084 based devices. This includes UART, I2C, SPI etc. Signed-off-by: Georgi Djakov Signed-off-by: Stephen Boyd --- drivers/clk/qcom/Kconfig | 8 + drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/gcc-apq8084.c | 2944 ++++++++++++++++++++++++++ include/dt-bindings/clock/qcom,gcc-apq8084.h | 337 +++ include/dt-bindings/reset/qcom,gcc-apq8084.h | 109 + 5 files changed, 3399 insertions(+) create mode 100644 drivers/clk/qcom/gcc-apq8084.c create mode 100644 include/dt-bindings/clock/qcom,gcc-apq8084.h create mode 100644 include/dt-bindings/reset/qcom,gcc-apq8084.h (limited to 'include') diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index 7f696b7d4422..e7516c2a2502 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -4,6 +4,14 @@ config COMMON_CLK_QCOM select REGMAP_MMIO select RESET_CONTROLLER +config APQ_GCC_8084 + tristate "APQ8084 Global Clock Controller" + depends on COMMON_CLK_QCOM + help + Support for the global clock controller on apq8084 devices. + Say Y if you want to use peripheral devices such as UART, SPI, + i2c, USB, SD/eMMC, SATA, PCIe, etc. + config MSM_GCC_8660 tristate "MSM8660 Global Clock Controller" depends on COMMON_CLK_QCOM diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 689e05bf4f95..196a5df9125a 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -8,6 +8,7 @@ clk-qcom-y += clk-rcg2.o clk-qcom-y += clk-branch.o clk-qcom-y += reset.o +obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o obj-$(CONFIG_MSM_GCC_8660) += gcc-msm8660.o obj-$(CONFIG_MSM_GCC_8960) += gcc-msm8960.o obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o diff --git a/drivers/clk/qcom/gcc-apq8084.c b/drivers/clk/qcom/gcc-apq8084.c new file mode 100644 index 000000000000..b9e6140674cc --- /dev/null +++ b/drivers/clk/qcom/gcc-apq8084.c @@ -0,0 +1,2944 @@ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "common.h" +#include "clk-regmap.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-branch.h" +#include "reset.h" + +#define P_XO 0 +#define P_GPLL0 1 +#define P_GPLL1 1 +#define P_GPLL4 2 + +static const u8 gcc_xo_gpll0_map[] = { + [P_XO] = 0, + [P_GPLL0] = 1, +}; + +static const char *gcc_xo_gpll0[] = { + "xo", + "gpll0_vote", +}; + +static const u8 gcc_xo_gpll0_gpll4_map[] = { + [P_XO] = 0, + [P_GPLL0] = 1, + [P_GPLL4] = 5, +}; + +static const char *gcc_xo_gpll0_gpll4[] = { + "xo", + "gpll0_vote", + "gpll4_vote", +}; + +#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } + +static struct clk_pll gpll0 = { + .l_reg = 0x0004, + .m_reg = 0x0008, + .n_reg = 0x000c, + .config_reg = 0x0014, + .mode_reg = 0x0000, + .status_reg = 0x001c, + .status_bit = 17, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gpll0", + .parent_names = (const char *[]){ "xo" }, + .num_parents = 1, + .ops = &clk_pll_ops, + }, +}; + +static struct clk_regmap gpll0_vote = { + .enable_reg = 0x1480, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpll0_vote", + .parent_names = (const char *[]){ "gpll0" }, + .num_parents = 1, + .ops = &clk_pll_vote_ops, + }, +}; + +static struct clk_rcg2 config_noc_clk_src = { + .cmd_rcgr = 0x0150, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "config_noc_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 periph_noc_clk_src = { + .cmd_rcgr = 0x0190, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "periph_noc_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 system_noc_clk_src = { + .cmd_rcgr = 0x0120, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "system_noc_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_pll gpll1 = { + .l_reg = 0x0044, + .m_reg = 0x0048, + .n_reg = 0x004c, + .config_reg = 0x0054, + .mode_reg = 0x0040, + .status_reg = 0x005c, + .status_bit = 17, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gpll1", + .parent_names = (const char *[]){ "xo" }, + .num_parents = 1, + .ops = &clk_pll_ops, + }, +}; + +static struct clk_regmap gpll1_vote = { + .enable_reg = 0x1480, + .enable_mask = BIT(1), + .hw.init = &(struct clk_init_data){ + .name = "gpll1_vote", + .parent_names = (const char *[]){ "gpll1" }, + .num_parents = 1, + .ops = &clk_pll_vote_ops, + }, +}; + +static struct clk_pll gpll4 = { + .l_reg = 0x1dc4, + .m_reg = 0x1dc8, + .n_reg = 0x1dcc, + .config_reg = 0x1dd4, + .mode_reg = 0x1dc0, + .status_reg = 0x1ddc, + .status_bit = 17, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gpll4", + .parent_names = (const char *[]){ "xo" }, + .num_parents = 1, + .ops = &clk_pll_ops, + }, +}; + +static struct clk_regmap gpll4_vote = { + .enable_reg = 0x1480, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "gpll4_vote", + .parent_names = (const char *[]){ "gpll4" }, + .num_parents = 1, + .ops = &clk_pll_vote_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_usb30_master_clk[] = { + F(125000000, P_GPLL0, 1, 5, 24), + { } +}; + +static struct clk_rcg2 usb30_master_clk_src = { + .cmd_rcgr = 0x03d4, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_usb30_master_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "usb30_master_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_usb30_sec_master_clk[] = { + F(125000000, P_GPLL0, 1, 5, 24), + { } +}; + +static struct clk_rcg2 usb30_sec_master_clk_src = { + .cmd_rcgr = 0x1bd4, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_usb30_sec_master_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "usb30_sec_master_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_branch gcc_usb30_sec_mock_utmi_clk = { + .halt_reg = 0x1bd0, + .clkr = { + .enable_reg = 0x1bd0, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb30_sec_mock_utmi_clk", + .parent_names = (const char *[]){ + "usb30_sec_mock_utmi_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb30_sec_sleep_clk = { + .halt_reg = 0x1bcc, + .clkr = { + .enable_reg = 0x1bcc, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb30_sec_sleep_clk", + .parent_names = (const char *[]){ + "sleep_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static const struct freq_tbl ftbl_gcc_blsp1_2_qup1_6_i2c_apps_clk[] = { + F(19200000, P_XO, 1, 0, 0), + F(50000000, P_GPLL0, 12, 0, 0), + { } +}; + +static struct clk_rcg2 blsp1_qup1_i2c_apps_clk_src = { + .cmd_rcgr = 0x0660, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_i2c_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup1_i2c_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_blsp1_2_qup1_6_spi_apps_clk[] = { + F(960000, P_XO, 10, 1, 2), + F(4800000, P_XO, 4, 0, 0), + F(9600000, P_XO, 2, 0, 0), + F(15000000, P_GPLL0, 10, 1, 4), + F(19200000, P_XO, 1, 0, 0), + F(25000000, P_GPLL0, 12, 1, 2), + F(50000000, P_GPLL0, 12, 0, 0), + { } +}; + +static struct clk_rcg2 blsp1_qup1_spi_apps_clk_src = { + .cmd_rcgr = 0x064c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_spi_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup1_spi_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_qup2_i2c_apps_clk_src = { + .cmd_rcgr = 0x06e0, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_i2c_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup2_i2c_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_qup2_spi_apps_clk_src = { + .cmd_rcgr = 0x06cc, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_spi_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup2_spi_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_qup3_i2c_apps_clk_src = { + .cmd_rcgr = 0x0760, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_i2c_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup3_i2c_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_qup3_spi_apps_clk_src = { + .cmd_rcgr = 0x074c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_spi_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup3_spi_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_qup4_i2c_apps_clk_src = { + .cmd_rcgr = 0x07e0, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_i2c_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup4_i2c_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_qup4_spi_apps_clk_src = { + .cmd_rcgr = 0x07cc, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_spi_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup4_spi_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_qup5_i2c_apps_clk_src = { + .cmd_rcgr = 0x0860, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_i2c_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup5_i2c_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_qup5_spi_apps_clk_src = { + .cmd_rcgr = 0x084c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_spi_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup5_spi_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_qup6_i2c_apps_clk_src = { + .cmd_rcgr = 0x08e0, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_i2c_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup6_i2c_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_qup6_spi_apps_clk_src = { + .cmd_rcgr = 0x08cc, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_spi_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_qup6_spi_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_blsp1_2_uart1_6_apps_clk[] = { + F(3686400, P_GPLL0, 1, 96, 15625), + F(7372800, P_GPLL0, 1, 192, 15625), + F(14745600, P_GPLL0, 1, 384, 15625), + F(16000000, P_GPLL0, 5, 2, 15), + F(19200000, P_XO, 1, 0, 0), + F(24000000, P_GPLL0, 5, 1, 5), + F(32000000, P_GPLL0, 1, 4, 75), + F(40000000, P_GPLL0, 15, 0, 0), + F(46400000, P_GPLL0, 1, 29, 375), + F(48000000, P_GPLL0, 12.5, 0, 0), + F(51200000, P_GPLL0, 1, 32, 375), + F(56000000, P_GPLL0, 1, 7, 75), + F(58982400, P_GPLL0, 1, 1536, 15625), + F(60000000, P_GPLL0, 10, 0, 0), + F(63160000, P_GPLL0, 9.5, 0, 0), + { } +}; + +static struct clk_rcg2 blsp1_uart1_apps_clk_src = { + .cmd_rcgr = 0x068c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_uart1_6_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_uart1_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_uart2_apps_clk_src = { + .cmd_rcgr = 0x070c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_uart1_6_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_uart2_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_uart3_apps_clk_src = { + .cmd_rcgr = 0x078c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_uart1_6_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_uart3_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_uart4_apps_clk_src = { + .cmd_rcgr = 0x080c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_uart1_6_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_uart4_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_uart5_apps_clk_src = { + .cmd_rcgr = 0x088c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_uart1_6_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_uart5_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp1_uart6_apps_clk_src = { + .cmd_rcgr = 0x090c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_uart1_6_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp1_uart6_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_qup1_i2c_apps_clk_src = { + .cmd_rcgr = 0x09a0, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_i2c_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp2_qup1_i2c_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_qup1_spi_apps_clk_src = { + .cmd_rcgr = 0x098c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_spi_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp2_qup1_spi_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_qup2_i2c_apps_clk_src = { + .cmd_rcgr = 0x0a20, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_i2c_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp2_qup2_i2c_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_qup2_spi_apps_clk_src = { + .cmd_rcgr = 0x0a0c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_spi_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp2_qup2_spi_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_qup3_i2c_apps_clk_src = { + .cmd_rcgr = 0x0aa0, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_i2c_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp2_qup3_i2c_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_qup3_spi_apps_clk_src = { + .cmd_rcgr = 0x0a8c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_spi_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp2_qup3_spi_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_qup4_i2c_apps_clk_src = { + .cmd_rcgr = 0x0b20, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_i2c_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp2_qup4_i2c_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_qup4_spi_apps_clk_src = { + .cmd_rcgr = 0x0b0c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_spi_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp2_qup4_spi_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_qup5_i2c_apps_clk_src = { + .cmd_rcgr = 0x0ba0, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_i2c_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp2_qup5_i2c_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_qup5_spi_apps_clk_src = { + .cmd_rcgr = 0x0b8c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_spi_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp2_qup5_spi_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_qup6_i2c_apps_clk_src = { + .cmd_rcgr = 0x0c20, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_i2c_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp2_qup6_i2c_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_qup6_spi_apps_clk_src = { + .cmd_rcgr = 0x0c0c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_qup1_6_spi_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp2_qup6_spi_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_uart1_apps_clk_src = { + .cmd_rcgr = 0x09cc, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_uart1_6_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp2_uart1_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_uart2_apps_clk_src = { + .cmd_rcgr = 0x0a4c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_uart1_6_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp2_uart2_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_uart3_apps_clk_src = { + .cmd_rcgr = 0x0acc, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_uart1_6_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp2_uart3_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_uart4_apps_clk_src = { + .cmd_rcgr = 0x0b4c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_uart1_6_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp2_uart4_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_uart5_apps_clk_src = { + .cmd_rcgr = 0x0bcc, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_uart1_6_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp2_uart5_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 blsp2_uart6_apps_clk_src = { + .cmd_rcgr = 0x0c4c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_blsp1_2_uart1_6_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "blsp2_uart6_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_ce1_clk[] = { + F(50000000, P_GPLL0, 12, 0, 0), + F(85710000, P_GPLL0, 7, 0, 0), + F(100000000, P_GPLL0, 6, 0, 0), + F(171430000, P_GPLL0, 3.5, 0, 0), + { } +}; + +static struct clk_rcg2 ce1_clk_src = { + .cmd_rcgr = 0x1050, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_ce1_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "ce1_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_ce2_clk[] = { + F(50000000, P_GPLL0, 12, 0, 0), + F(85710000, P_GPLL0, 7, 0, 0), + F(100000000, P_GPLL0, 6, 0, 0), + F(171430000, P_GPLL0, 3.5, 0, 0), + { } +}; + +static struct clk_rcg2 ce2_clk_src = { + .cmd_rcgr = 0x1090, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_ce2_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "ce2_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_ce3_clk[] = { + F(50000000, P_GPLL0, 12, 0, 0), + F(85710000, P_GPLL0, 7, 0, 0), + F(100000000, P_GPLL0, 6, 0, 0), + F(171430000, P_GPLL0, 3.5, 0, 0), + { } +}; + +static struct clk_rcg2 ce3_clk_src = { + .cmd_rcgr = 0x1d10, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_ce3_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "ce3_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_gp_clk[] = { + F(19200000, P_XO, 1, 0, 0), + F(100000000, P_GPLL0, 6, 0, 0), + F(200000000, P_GPLL0, 3, 0, 0), + { } +}; + +static struct clk_rcg2 gp1_clk_src = { + .cmd_rcgr = 0x1904, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_gp_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gp1_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gp2_clk_src = { + .cmd_rcgr = 0x1944, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_gp_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gp2_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gp3_clk_src = { + .cmd_rcgr = 0x1984, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_gp_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gp3_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pdm2_clk[] = { + F(60000000, P_GPLL0, 10, 0, 0), + { } +}; + +static struct clk_rcg2 pdm2_clk_src = { + .cmd_rcgr = 0x0cd0, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_pdm2_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pdm2_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_sdcc1_4_apps_clk[] = { + F(144000, P_XO, 16, 3, 25), + F(400000, P_XO, 12, 1, 4), + F(20000000, P_GPLL0, 15, 1, 2), + F(25000000, P_GPLL0, 12, 1, 2), + F(50000000, P_GPLL0, 12, 0, 0), + F(100000000, P_GPLL0, 6, 0, 0), + F(192000000, P_GPLL4, 4, 0, 0), + F(200000000, P_GPLL0, 3, 0, 0), + F(384000000, P_GPLL4, 2, 0, 0), + { } +}; + +static struct clk_rcg2 sdcc1_apps_clk_src = { + .cmd_rcgr = 0x04d0, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_gpll4_map, + .freq_tbl = ftbl_gcc_sdcc1_4_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "sdcc1_apps_clk_src", + .parent_names = gcc_xo_gpll0_gpll4, + .num_parents = 3, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 sdcc2_apps_clk_src = { + .cmd_rcgr = 0x0510, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_sdcc1_4_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "sdcc2_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 sdcc3_apps_clk_src = { + .cmd_rcgr = 0x0550, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_sdcc1_4_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "sdcc3_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 sdcc4_apps_clk_src = { + .cmd_rcgr = 0x0590, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_sdcc1_4_apps_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "sdcc4_apps_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_tsif_ref_clk[] = { + F(105000, P_XO, 2, 1, 91), + { } +}; + +static struct clk_rcg2 tsif_ref_clk_src = { + .cmd_rcgr = 0x0d90, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_tsif_ref_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "tsif_ref_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_usb30_mock_utmi_clk[] = { + F(60000000, P_GPLL0, 10, 0, 0), + { } +}; + +static struct clk_rcg2 usb30_mock_utmi_clk_src = { + .cmd_rcgr = 0x03e8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_usb30_mock_utmi_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "usb30_mock_utmi_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_usb30_sec_mock_utmi_clk[] = { + F(125000000, P_GPLL0, 1, 5, 24), + { } +}; + +static struct clk_rcg2 usb30_sec_mock_utmi_clk_src = { + .cmd_rcgr = 0x1be8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_usb30_sec_mock_utmi_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "usb30_sec_mock_utmi_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_usb_hs_system_clk[] = { + F(75000000, P_GPLL0, 8, 0, 0), + { } +}; + +static struct clk_rcg2 usb_hs_system_clk_src = { + .cmd_rcgr = 0x0490, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_usb_hs_system_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "usb_hs_system_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_usb_hsic_clk[] = { + F(480000000, P_GPLL1, 1, 0, 0), + { } +}; + +static u8 usb_hsic_clk_src_map[] = { + [P_XO] = 0, + [P_GPLL1] = 4, +}; + +static struct clk_rcg2 usb_hsic_clk_src = { + .cmd_rcgr = 0x0440, + .hid_width = 5, + .parent_map = usb_hsic_clk_src_map, + .freq_tbl = ftbl_gcc_usb_hsic_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "usb_hsic_clk_src", + .parent_names = (const char *[]){ + "xo", + "gpll1_vote", + }, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_usb_hsic_ahb_clk_src[] = { + F(60000000, P_GPLL1, 8, 0, 0), + { } +}; + +static struct clk_rcg2 usb_hsic_ahb_clk_src = { + .cmd_rcgr = 0x046c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = usb_hsic_clk_src_map, + .freq_tbl = ftbl_gcc_usb_hsic_ahb_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "usb_hsic_ahb_clk_src", + .parent_names = (const char *[]){ + "xo", + "gpll1_vote", + }, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_usb_hsic_io_cal_clk[] = { + F(9600000, P_XO, 2, 0, 0), + { } +}; + +static struct clk_rcg2 usb_hsic_io_cal_clk_src = { + .cmd_rcgr = 0x0458, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_usb_hsic_io_cal_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "usb_hsic_io_cal_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 1, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_branch gcc_usb_hsic_mock_utmi_clk = { + .halt_reg = 0x1f14, + .clkr = { + .enable_reg = 0x1f14, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb_hsic_mock_utmi_clk", + .parent_names = (const char *[]){ + "usb_hsic_mock_utmi_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static const struct freq_tbl ftbl_gcc_usb_hsic_mock_utmi_clk[] = { + F(60000000, P_GPLL0, 10, 0, 0), + { } +}; + +static struct clk_rcg2 usb_hsic_mock_utmi_clk_src = { + .cmd_rcgr = 0x1f00, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_usb_hsic_mock_utmi_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "usb_hsic_mock_utmi_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 1, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_usb_hsic_system_clk[] = { + F(75000000, P_GPLL0, 8, 0, 0), + { } +}; + +static struct clk_rcg2 usb_hsic_system_clk_src = { + .cmd_rcgr = 0x041c, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_usb_hsic_system_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "usb_hsic_system_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_branch gcc_bam_dma_ahb_clk = { + .halt_reg = 0x0d44, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1484, + .enable_mask = BIT(12), + .hw.init = &(struct clk_init_data){ + .name = "gcc_bam_dma_ahb_clk", + .parent_names = (const char *[]){ + "periph_noc_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_ahb_clk = { + .halt_reg = 0x05c4, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1484, + .enable_mask = BIT(17), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_ahb_clk", + .parent_names = (const char *[]){ + "periph_noc_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup1_i2c_apps_clk = { + .halt_reg = 0x0648, + .clkr = { + .enable_reg = 0x0648, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup1_i2c_apps_clk", + .parent_names = (const char *[]){ + "blsp1_qup1_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup1_spi_apps_clk = { + .halt_reg = 0x0644, + .clkr = { + .enable_reg = 0x0644, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup1_spi_apps_clk", + .parent_names = (const char *[]){ + "blsp1_qup1_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup2_i2c_apps_clk = { + .halt_reg = 0x06c8, + .clkr = { + .enable_reg = 0x06c8, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup2_i2c_apps_clk", + .parent_names = (const char *[]){ + "blsp1_qup2_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup2_spi_apps_clk = { + .halt_reg = 0x06c4, + .clkr = { + .enable_reg = 0x06c4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup2_spi_apps_clk", + .parent_names = (const char *[]){ + "blsp1_qup2_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup3_i2c_apps_clk = { + .halt_reg = 0x0748, + .clkr = { + .enable_reg = 0x0748, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup3_i2c_apps_clk", + .parent_names = (const char *[]){ + "blsp1_qup3_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup3_spi_apps_clk = { + .halt_reg = 0x0744, + .clkr = { + .enable_reg = 0x0744, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup3_spi_apps_clk", + .parent_names = (const char *[]){ + "blsp1_qup3_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup4_i2c_apps_clk = { + .halt_reg = 0x07c8, + .clkr = { + .enable_reg = 0x07c8, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup4_i2c_apps_clk", + .parent_names = (const char *[]){ + "blsp1_qup4_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup4_spi_apps_clk = { + .halt_reg = 0x07c4, + .clkr = { + .enable_reg = 0x07c4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup4_spi_apps_clk", + .parent_names = (const char *[]){ + "blsp1_qup4_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup5_i2c_apps_clk = { + .halt_reg = 0x0848, + .clkr = { + .enable_reg = 0x0848, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup5_i2c_apps_clk", + .parent_names = (const char *[]){ + "blsp1_qup5_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup5_spi_apps_clk = { + .halt_reg = 0x0844, + .clkr = { + .enable_reg = 0x0844, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup5_spi_apps_clk", + .parent_names = (const char *[]){ + "blsp1_qup5_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup6_i2c_apps_clk = { + .halt_reg = 0x08c8, + .clkr = { + .enable_reg = 0x08c8, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup6_i2c_apps_clk", + .parent_names = (const char *[]){ + "blsp1_qup6_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_qup6_spi_apps_clk = { + .halt_reg = 0x08c4, + .clkr = { + .enable_reg = 0x08c4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_qup6_spi_apps_clk", + .parent_names = (const char *[]){ + "blsp1_qup6_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_uart1_apps_clk = { + .halt_reg = 0x0684, + .clkr = { + .enable_reg = 0x0684, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_uart1_apps_clk", + .parent_names = (const char *[]){ + "blsp1_uart1_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_uart2_apps_clk = { + .halt_reg = 0x0704, + .clkr = { + .enable_reg = 0x0704, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_uart2_apps_clk", + .parent_names = (const char *[]){ + "blsp1_uart2_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_uart3_apps_clk = { + .halt_reg = 0x0784, + .clkr = { + .enable_reg = 0x0784, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_uart3_apps_clk", + .parent_names = (const char *[]){ + "blsp1_uart3_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_uart4_apps_clk = { + .halt_reg = 0x0804, + .clkr = { + .enable_reg = 0x0804, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_uart4_apps_clk", + .parent_names = (const char *[]){ + "blsp1_uart4_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_uart5_apps_clk = { + .halt_reg = 0x0884, + .clkr = { + .enable_reg = 0x0884, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_uart5_apps_clk", + .parent_names = (const char *[]){ + "blsp1_uart5_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp1_uart6_apps_clk = { + .halt_reg = 0x0904, + .clkr = { + .enable_reg = 0x0904, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_uart6_apps_clk", + .parent_names = (const char *[]){ + "blsp1_uart6_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_ahb_clk = { + .halt_reg = 0x0944, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1484, + .enable_mask = BIT(15), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp2_ahb_clk", + .parent_names = (const char *[]){ + "periph_noc_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_qup1_i2c_apps_clk = { + .halt_reg = 0x0988, + .clkr = { + .enable_reg = 0x0988, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp2_qup1_i2c_apps_clk", + .parent_names = (const char *[]){ + "blsp2_qup1_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_qup1_spi_apps_clk = { + .halt_reg = 0x0984, + .clkr = { + .enable_reg = 0x0984, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp2_qup1_spi_apps_clk", + .parent_names = (const char *[]){ + "blsp2_qup1_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_qup2_i2c_apps_clk = { + .halt_reg = 0x0a08, + .clkr = { + .enable_reg = 0x0a08, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp2_qup2_i2c_apps_clk", + .parent_names = (const char *[]){ + "blsp2_qup2_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_qup2_spi_apps_clk = { + .halt_reg = 0x0a04, + .clkr = { + .enable_reg = 0x0a04, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp2_qup2_spi_apps_clk", + .parent_names = (const char *[]){ + "blsp2_qup2_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_qup3_i2c_apps_clk = { + .halt_reg = 0x0a88, + .clkr = { + .enable_reg = 0x0a88, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp2_qup3_i2c_apps_clk", + .parent_names = (const char *[]){ + "blsp2_qup3_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_qup3_spi_apps_clk = { + .halt_reg = 0x0a84, + .clkr = { + .enable_reg = 0x0a84, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp2_qup3_spi_apps_clk", + .parent_names = (const char *[]){ + "blsp2_qup3_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_qup4_i2c_apps_clk = { + .halt_reg = 0x0b08, + .clkr = { + .enable_reg = 0x0b08, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp2_qup4_i2c_apps_clk", + .parent_names = (const char *[]){ + "blsp2_qup4_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_qup4_spi_apps_clk = { + .halt_reg = 0x0b04, + .clkr = { + .enable_reg = 0x0b04, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp2_qup4_spi_apps_clk", + .parent_names = (const char *[]){ + "blsp2_qup4_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_qup5_i2c_apps_clk = { + .halt_reg = 0x0b88, + .clkr = { + .enable_reg = 0x0b88, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp2_qup5_i2c_apps_clk", + .parent_names = (const char *[]){ + "blsp2_qup5_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_qup5_spi_apps_clk = { + .halt_reg = 0x0b84, + .clkr = { + .enable_reg = 0x0b84, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp2_qup5_spi_apps_clk", + .parent_names = (const char *[]){ + "blsp2_qup5_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_qup6_i2c_apps_clk = { + .halt_reg = 0x0c08, + .clkr = { + .enable_reg = 0x0c08, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp2_qup6_i2c_apps_clk", + .parent_names = (const char *[]){ + "blsp2_qup6_i2c_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_qup6_spi_apps_clk = { + .halt_reg = 0x0c04, + .clkr = { + .enable_reg = 0x0c04, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp2_qup6_spi_apps_clk", + .parent_names = (const char *[]){ + "blsp2_qup6_spi_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_uart1_apps_clk = { + .halt_reg = 0x09c4, + .clkr = { + .enable_reg = 0x09c4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp2_uart1_apps_clk", + .parent_names = (const char *[]){ + "blsp2_uart1_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_uart2_apps_clk = { + .halt_reg = 0x0a44, + .clkr = { + .enable_reg = 0x0a44, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp2_uart2_apps_clk", + .parent_names = (const char *[]){ + "blsp2_uart2_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_uart3_apps_clk = { + .halt_reg = 0x0ac4, + .clkr = { + .enable_reg = 0x0ac4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp2_uart3_apps_clk", + .parent_names = (const char *[]){ + "blsp2_uart3_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_uart4_apps_clk = { + .halt_reg = 0x0b44, + .clkr = { + .enable_reg = 0x0b44, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp2_uart4_apps_clk", + .parent_names = (const char *[]){ + "blsp2_uart4_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_uart5_apps_clk = { + .halt_reg = 0x0bc4, + .clkr = { + .enable_reg = 0x0bc4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp2_uart5_apps_clk", + .parent_names = (const char *[]){ + "blsp2_uart5_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_blsp2_uart6_apps_clk = { + .halt_reg = 0x0c44, + .clkr = { + .enable_reg = 0x0c44, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp2_uart6_apps_clk", + .parent_names = (const char *[]){ + "blsp2_uart6_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_boot_rom_ahb_clk = { + .halt_reg = 0x0e04, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1484, + .enable_mask = BIT(10), + .hw.init = &(struct clk_init_data){ + .name = "gcc_boot_rom_ahb_clk", + .parent_names = (const char *[]){ + "config_noc_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ce1_ahb_clk = { + .halt_reg = 0x104c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1484, + .enable_mask = BIT(3), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ce1_ahb_clk", + .parent_names = (const char *[]){ + "config_noc_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ce1_axi_clk = { + .halt_reg = 0x1048, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1484, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ce1_axi_clk", + .parent_names = (const char *[]){ + "system_noc_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ce1_clk = { + .halt_reg = 0x1050, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1484, + .enable_mask = BIT(5), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ce1_clk", + .parent_names = (const char *[]){ + "ce1_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ce2_ahb_clk = { + .halt_reg = 0x108c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1484, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ce2_ahb_clk", + .parent_names = (const char *[]){ + "config_noc_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ce2_axi_clk = { + .halt_reg = 0x1088, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1484, + .enable_mask = BIT(1), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ce2_axi_clk", + .parent_names = (const char *[]){ + "system_noc_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ce2_clk = { + .halt_reg = 0x1090, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1484, + .enable_mask = BIT(2), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ce2_clk", + .parent_names = (const char *[]){ + "ce2_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ce3_ahb_clk = { + .halt_reg = 0x1d0c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1d0c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ce3_ahb_clk", + .parent_names = (const char *[]){ + "config_noc_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ce3_axi_clk = { + .halt_reg = 0x1088, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1d08, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ce3_axi_clk", + .parent_names = (const char *[]){ + "system_noc_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ce3_clk = { + .halt_reg = 0x1090, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1d04, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ce3_clk", + .parent_names = (const char *[]){ + "ce3_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gp1_clk = { + .halt_reg = 0x1900, + .clkr = { + .enable_reg = 0x1900, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_gp1_clk", + .parent_names = (const char *[]){ + "gp1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gp2_clk = { + .halt_reg = 0x1940, + .clkr = { + .enable_reg = 0x1940, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_gp2_clk", + .parent_names = (const char *[]){ + "gp2_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gp3_clk = { + .halt_reg = 0x1980, + .clkr = { + .enable_reg = 0x1980, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_gp3_clk", + .parent_names = (const char *[]){ + "gp3_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ocmem_noc_cfg_ahb_clk = { + .halt_reg = 0x0248, + .clkr = { + .enable_reg = 0x0248, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ocmem_noc_cfg_ahb_clk", + .parent_names = (const char *[]){ + "config_noc_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pdm2_clk = { + .halt_reg = 0x0ccc, + .clkr = { + .enable_reg = 0x0ccc, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pdm2_clk", + .parent_names = (const char *[]){ + "pdm2_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pdm_ahb_clk = { + .halt_reg = 0x0cc4, + .clkr = { + .enable_reg = 0x0cc4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pdm_ahb_clk", + .parent_names = (const char *[]){ + "periph_noc_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_periph_noc_usb_hsic_ahb_clk = { + .halt_reg = 0x01a4, + .clkr = { + .enable_reg = 0x01a4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_periph_noc_usb_hsic_ahb_clk", + .parent_names = (const char *[]){ + "usb_hsic_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_prng_ahb_clk = { + .halt_reg = 0x0d04, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1484, + .enable_mask = BIT(13), + .hw.init = &(struct clk_init_data){ + .name = "gcc_prng_ahb_clk", + .parent_names = (const char *[]){ + "periph_noc_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_ahb_clk = { + .halt_reg = 0x04c8, + .clkr = { + .enable_reg = 0x04c8, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc1_ahb_clk", + .parent_names = (const char *[]){ + "periph_noc_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_apps_clk = { + .halt_reg = 0x04c4, + .clkr = { + .enable_reg = 0x04c4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc1_apps_clk", + .parent_names = (const char *[]){ + "sdcc1_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_cdccal_ff_clk = { + .halt_reg = 0x04e8, + .clkr = { + .enable_reg = 0x04e8, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc1_cdccal_ff_clk", + .parent_names = (const char *[]){ + "xo" + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_cdccal_sleep_clk = { + .halt_reg = 0x04e4, + .clkr = { + .enable_reg = 0x04e4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc1_cdccal_sleep_clk", + .parent_names = (const char *[]){ + "sleep_clk_src" + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc2_ahb_clk = { + .halt_reg = 0x0508, + .clkr = { + .enable_reg = 0x0508, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc2_ahb_clk", + .parent_names = (const char *[]){ + "periph_noc_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc2_apps_clk = { + .halt_reg = 0x0504, + .clkr = { + .enable_reg = 0x0504, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc2_apps_clk", + .parent_names = (const char *[]){ + "sdcc2_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc3_ahb_clk = { + .halt_reg = 0x0548, + .clkr = { + .enable_reg = 0x0548, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc3_ahb_clk", + .parent_names = (const char *[]){ + "periph_noc_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc3_apps_clk = { + .halt_reg = 0x0544, + .clkr = { + .enable_reg = 0x0544, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc3_apps_clk", + .parent_names = (const char *[]){ + "sdcc3_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc4_ahb_clk = { + .halt_reg = 0x0588, + .clkr = { + .enable_reg = 0x0588, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc4_ahb_clk", + .parent_names = (const char *[]){ + "periph_noc_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc4_apps_clk = { + .halt_reg = 0x0584, + .clkr = { + .enable_reg = 0x0584, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc4_apps_clk", + .parent_names = (const char *[]){ + "sdcc4_apps_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sys_noc_usb3_axi_clk = { + .halt_reg = 0x0108, + .clkr = { + .enable_reg = 0x0108, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sys_noc_usb3_axi_clk", + .parent_names = (const char *[]){ + "usb30_master_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sys_noc_usb3_sec_axi_clk = { + .halt_reg = 0x0138, + .clkr = { + .enable_reg = 0x0138, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sys_noc_usb3_sec_axi_clk", + .parent_names = (const char *[]){ + "usb30_sec_master_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_tsif_ahb_clk = { + .halt_reg = 0x0d84, + .clkr = { + .enable_reg = 0x0d84, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_tsif_ahb_clk", + .parent_names = (const char *[]){ + "periph_noc_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_tsif_inactivity_timers_clk = { + .halt_reg = 0x0d8c, + .clkr = { + .enable_reg = 0x0d8c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_tsif_inactivity_timers_clk", + .parent_names = (const char *[]){ + "sleep_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_tsif_ref_clk = { + .halt_reg = 0x0d88, + .clkr = { + .enable_reg = 0x0d88, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_tsif_ref_clk", + .parent_names = (const char *[]){ + "tsif_ref_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb2a_phy_sleep_clk = { + .halt_reg = 0x04ac, + .clkr = { + .enable_reg = 0x04ac, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb2a_phy_sleep_clk", + .parent_names = (const char *[]){ + "sleep_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb2b_phy_sleep_clk = { + .halt_reg = 0x04b4, + .clkr = { + .enable_reg = 0x04b4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb2b_phy_sleep_clk", + .parent_names = (const char *[]){ + "sleep_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb30_master_clk = { + .halt_reg = 0x03c8, + .clkr = { + .enable_reg = 0x03c8, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb30_master_clk", + .parent_names = (const char *[]){ + "usb30_master_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb30_sec_master_clk = { + .halt_reg = 0x1bc8, + .clkr = { + .enable_reg = 0x1bc8, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb30_sec_master_clk", + .parent_names = (const char *[]){ + "usb30_sec_master_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb30_mock_utmi_clk = { + .halt_reg = 0x03d0, + .clkr = { + .enable_reg = 0x03d0, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb30_mock_utmi_clk", + .parent_names = (const char *[]){ + "usb30_mock_utmi_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb30_sleep_clk = { + .halt_reg = 0x03cc, + .clkr = { + .enable_reg = 0x03cc, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb30_sleep_clk", + .parent_names = (const char *[]){ + "sleep_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb_hs_ahb_clk = { + .halt_reg = 0x0488, + .clkr = { + .enable_reg = 0x0488, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb_hs_ahb_clk", + .parent_names = (const char *[]){ + "periph_noc_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb_hs_inactivity_timers_clk = { + .halt_reg = 0x048c, + .clkr = { + .enable_reg = 0x048c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb_hs_inactivity_timers_clk", + .parent_names = (const char *[]){ + "sleep_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb_hs_system_clk = { + .halt_reg = 0x0484, + .clkr = { + .enable_reg = 0x0484, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb_hs_system_clk", + .parent_names = (const char *[]){ + "usb_hs_system_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb_hsic_ahb_clk = { + .halt_reg = 0x0408, + .clkr = { + .enable_reg = 0x0408, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb_hsic_ahb_clk", + .parent_names = (const char *[]){ + "periph_noc_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb_hsic_clk = { + .halt_reg = 0x0410, + .clkr = { + .enable_reg = 0x0410, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb_hsic_clk", + .parent_names = (const char *[]){ + "usb_hsic_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb_hsic_io_cal_clk = { + .halt_reg = 0x0414, + .clkr = { + .enable_reg = 0x0414, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb_hsic_io_cal_clk", + .parent_names = (const char *[]){ + "usb_hsic_io_cal_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb_hsic_io_cal_sleep_clk = { + .halt_reg = 0x0418, + .clkr = { + .enable_reg = 0x0418, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb_hsic_io_cal_sleep_clk", + .parent_names = (const char *[]){ + "sleep_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb_hsic_system_clk = { + .halt_reg = 0x040c, + .clkr = { + .enable_reg = 0x040c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb_hsic_system_clk", + .parent_names = (const char *[]){ + "usb_hsic_system_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap *gcc_apq8084_clocks[] = { + [GPLL0] = &gpll0.clkr, + [GPLL0_VOTE] = &gpll0_vote, + [GPLL1] = &gpll1.clkr, + [GPLL1_VOTE] = &gpll1_vote, + [GPLL4] = &gpll4.clkr, + [GPLL4_VOTE] = &gpll4_vote, + [CONFIG_NOC_CLK_SRC] = &config_noc_clk_src.clkr, + [PERIPH_NOC_CLK_SRC] = &periph_noc_clk_src.clkr, + [SYSTEM_NOC_CLK_SRC] = &system_noc_clk_src.clkr, + [USB30_MASTER_CLK_SRC] = &usb30_master_clk_src.clkr, + [USB30_SEC_MASTER_CLK_SRC] = &usb30_sec_master_clk_src.clkr, + [USB_HSIC_AHB_CLK_SRC] = &usb_hsic_ahb_clk_src.clkr, + [BLSP1_QUP1_I2C_APPS_CLK_SRC] = &blsp1_qup1_i2c_apps_clk_src.clkr, + [BLSP1_QUP1_SPI_APPS_CLK_SRC] = &blsp1_qup1_spi_apps_clk_src.clkr, + [BLSP1_QUP2_I2C_APPS_CLK_SRC] = &blsp1_qup2_i2c_apps_clk_src.clkr, + [BLSP1_QUP2_SPI_APPS_CLK_SRC] = &blsp1_qup2_spi_apps_clk_src.clkr, + [BLSP1_QUP3_I2C_APPS_CLK_SRC] = &blsp1_qup3_i2c_apps_clk_src.clkr, + [BLSP1_QUP3_SPI_APPS_CLK_SRC] = &blsp1_qup3_spi_apps_clk_src.clkr, + [BLSP1_QUP4_I2C_APPS_CLK_SRC] = &blsp1_qup4_i2c_apps_clk_src.clkr, + [BLSP1_QUP4_SPI_APPS_CLK_SRC] = &blsp1_qup4_spi_apps_clk_src.clkr, + [BLSP1_QUP5_I2C_APPS_CLK_SRC] = &blsp1_qup5_i2c_apps_clk_src.clkr, + [BLSP1_QUP5_SPI_APPS_CLK_SRC] = &blsp1_qup5_spi_apps_clk_src.clkr, + [BLSP1_QUP6_I2C_APPS_CLK_SRC] = &blsp1_qup6_i2c_apps_clk_src.clkr, + [BLSP1_QUP6_SPI_APPS_CLK_SRC] = &blsp1_qup6_spi_apps_clk_src.clkr, + [BLSP1_UART1_APPS_CLK_SRC] = &blsp1_uart1_apps_clk_src.clkr, + [BLSP1_UART2_APPS_CLK_SRC] = &blsp1_uart2_apps_clk_src.clkr, + [BLSP1_UART3_APPS_CLK_SRC] = &blsp1_uart3_apps_clk_src.clkr, + [BLSP1_UART4_APPS_CLK_SRC] = &blsp1_uart4_apps_clk_src.clkr, + [BLSP1_UART5_APPS_CLK_SRC] = &blsp1_uart5_apps_clk_src.clkr, + [BLSP1_UART6_APPS_CLK_SRC] = &blsp1_uart6_apps_clk_src.clkr, + [BLSP2_QUP1_I2C_APPS_CLK_SRC] = &blsp2_qup1_i2c_apps_clk_src.clkr, + [BLSP2_QUP1_SPI_APPS_CLK_SRC] = &blsp2_qup1_spi_apps_clk_src.clkr, + [BLSP2_QUP2_I2C_APPS_CLK_SRC] = &blsp2_qup2_i2c_apps_clk_src.clkr, + [BLSP2_QUP2_SPI_APPS_CLK_SRC] = &blsp2_qup2_spi_apps_clk_src.clkr, + [BLSP2_QUP3_I2C_APPS_CLK_SRC] = &blsp2_qup3_i2c_apps_clk_src.clkr, + [BLSP2_QUP3_SPI_APPS_CLK_SRC] = &blsp2_qup3_spi_apps_clk_src.clkr, + [BLSP2_QUP4_I2C_APPS_CLK_SRC] = &blsp2_qup4_i2c_apps_clk_src.clkr, + [BLSP2_QUP4_SPI_APPS_CLK_SRC] = &blsp2_qup4_spi_apps_clk_src.clkr, + [BLSP2_QUP5_I2C_APPS_CLK_SRC] = &blsp2_qup5_i2c_apps_clk_src.clkr, + [BLSP2_QUP5_SPI_APPS_CLK_SRC] = &blsp2_qup5_spi_apps_clk_src.clkr, + [BLSP2_QUP6_I2C_APPS_CLK_SRC] = &blsp2_qup6_i2c_apps_clk_src.clkr, + [BLSP2_QUP6_SPI_APPS_CLK_SRC] = &blsp2_qup6_spi_apps_clk_src.clkr, + [BLSP2_UART1_APPS_CLK_SRC] = &blsp2_uart1_apps_clk_src.clkr, + [BLSP2_UART2_APPS_CLK_SRC] = &blsp2_uart2_apps_clk_src.clkr, + [BLSP2_UART3_APPS_CLK_SRC] = &blsp2_uart3_apps_clk_src.clkr, + [BLSP2_UART4_APPS_CLK_SRC] = &blsp2_uart4_apps_clk_src.clkr, + [BLSP2_UART5_APPS_CLK_SRC] = &blsp2_uart5_apps_clk_src.clkr, + [BLSP2_UART6_APPS_CLK_SRC] = &blsp2_uart6_apps_clk_src.clkr, + [CE1_CLK_SRC] = &ce1_clk_src.clkr, + [CE2_CLK_SRC] = &ce2_clk_src.clkr, + [CE3_CLK_SRC] = &ce3_clk_src.clkr, + [GP1_CLK_SRC] = &gp1_clk_src.clkr, + [GP2_CLK_SRC] = &gp2_clk_src.clkr, + [GP3_CLK_SRC] = &gp3_clk_src.clkr, + [PDM2_CLK_SRC] = &pdm2_clk_src.clkr, + [SDCC1_APPS_CLK_SRC] = &sdcc1_apps_clk_src.clkr, + [SDCC2_APPS_CLK_SRC] = &sdcc2_apps_clk_src.clkr, + [SDCC3_APPS_CLK_SRC] = &sdcc3_apps_clk_src.clkr, + [SDCC4_APPS_CLK_SRC] = &sdcc4_apps_clk_src.clkr, + [TSIF_REF_CLK_SRC] = &tsif_ref_clk_src.clkr, + [USB30_MOCK_UTMI_CLK_SRC] = &usb30_mock_utmi_clk_src.clkr, + [USB30_SEC_MOCK_UTMI_CLK_SRC] = &usb30_sec_mock_utmi_clk_src.clkr, + [USB_HS_SYSTEM_CLK_SRC] = &usb_hs_system_clk_src.clkr, + [USB_HSIC_CLK_SRC] = &usb_hsic_clk_src.clkr, + [USB_HSIC_IO_CAL_CLK_SRC] = &usb_hsic_io_cal_clk_src.clkr, + [USB_HSIC_MOCK_UTMI_CLK_SRC] = &usb_hsic_mock_utmi_clk_src.clkr, + [USB_HSIC_SYSTEM_CLK_SRC] = &usb_hsic_system_clk_src.clkr, + [GCC_BAM_DMA_AHB_CLK] = &gcc_bam_dma_ahb_clk.clkr, + [GCC_BLSP1_AHB_CLK] = &gcc_blsp1_ahb_clk.clkr, + [GCC_BLSP1_QUP1_I2C_APPS_CLK] = &gcc_blsp1_qup1_i2c_apps_clk.clkr, + [GCC_BLSP1_QUP1_SPI_APPS_CLK] = &gcc_blsp1_qup1_spi_apps_clk.clkr, + [GCC_BLSP1_QUP2_I2C_APPS_CLK] = &gcc_blsp1_qup2_i2c_apps_clk.clkr, + [GCC_BLSP1_QUP2_SPI_APPS_CLK] = &gcc_blsp1_qup2_spi_apps_clk.clkr, + [GCC_BLSP1_QUP3_I2C_APPS_CLK] = &gcc_blsp1_qup3_i2c_apps_clk.clkr, + [GCC_BLSP1_QUP3_SPI_APPS_CLK] = &gcc_blsp1_qup3_spi_apps_clk.clkr, + [GCC_BLSP1_QUP4_I2C_APPS_CLK] = &gcc_blsp1_qup4_i2c_apps_clk.clkr, + [GCC_BLSP1_QUP4_SPI_APPS_CLK] = &gcc_blsp1_qup4_spi_apps_clk.clkr, + [GCC_BLSP1_QUP5_I2C_APPS_CLK] = &gcc_blsp1_qup5_i2c_apps_clk.clkr, + [GCC_BLSP1_QUP5_SPI_APPS_CLK] = &gcc_blsp1_qup5_spi_apps_clk.clkr, + [GCC_BLSP1_QUP6_I2C_APPS_CLK] = &gcc_blsp1_qup6_i2c_apps_clk.clkr, + [GCC_BLSP1_QUP6_SPI_APPS_CLK] = &gcc_blsp1_qup6_spi_apps_clk.clkr, + [GCC_BLSP1_UART1_APPS_CLK] = &gcc_blsp1_uart1_apps_clk.clkr, + [GCC_BLSP1_UART2_APPS_CLK] = &gcc_blsp1_uart2_apps_clk.clkr, + [GCC_BLSP1_UART3_APPS_CLK] = &gcc_blsp1_uart3_apps_clk.clkr, + [GCC_BLSP1_UART4_APPS_CLK] = &gcc_blsp1_uart4_apps_clk.clkr, + [GCC_BLSP1_UART5_APPS_CLK] = &gcc_blsp1_uart5_apps_clk.clkr, + [GCC_BLSP1_UART6_APPS_CLK] = &gcc_blsp1_uart6_apps_clk.clkr, + [GCC_BLSP2_AHB_CLK] = &gcc_blsp2_ahb_clk.clkr, + [GCC_BLSP2_QUP1_I2C_APPS_CLK] = &gcc_blsp2_qup1_i2c_apps_clk.clkr, + [GCC_BLSP2_QUP1_SPI_APPS_CLK] = &gcc_blsp2_qup1_spi_apps_clk.clkr, + [GCC_BLSP2_QUP2_I2C_APPS_CLK] = &gcc_blsp2_qup2_i2c_apps_clk.clkr, + [GCC_BLSP2_QUP2_SPI_APPS_CLK] = &gcc_blsp2_qup2_spi_apps_clk.clkr, + [GCC_BLSP2_QUP3_I2C_APPS_CLK] = &gcc_blsp2_qup3_i2c_apps_clk.clkr, + [GCC_BLSP2_QUP3_SPI_APPS_CLK] = &gcc_blsp2_qup3_spi_apps_clk.clkr, + [GCC_BLSP2_QUP4_I2C_APPS_CLK] = &gcc_blsp2_qup4_i2c_apps_clk.clkr, + [GCC_BLSP2_QUP4_SPI_APPS_CLK] = &gcc_blsp2_qup4_spi_apps_clk.clkr, + [GCC_BLSP2_QUP5_I2C_APPS_CLK] = &gcc_blsp2_qup5_i2c_apps_clk.clkr, + [GCC_BLSP2_QUP5_SPI_APPS_CLK] = &gcc_blsp2_qup5_spi_apps_clk.clkr, + [GCC_BLSP2_QUP6_I2C_APPS_CLK] = &gcc_blsp2_qup6_i2c_apps_clk.clkr, + [GCC_BLSP2_QUP6_SPI_APPS_CLK] = &gcc_blsp2_qup6_spi_apps_clk.clkr, + [GCC_BLSP2_UART1_APPS_CLK] = &gcc_blsp2_uart1_apps_clk.clkr, + [GCC_BLSP2_UART2_APPS_CLK] = &gcc_blsp2_uart2_apps_clk.clkr, + [GCC_BLSP2_UART3_APPS_CLK] = &gcc_blsp2_uart3_apps_clk.clkr, + [GCC_BLSP2_UART4_APPS_CLK] = &gcc_blsp2_uart4_apps_clk.clkr, + [GCC_BLSP2_UART5_APPS_CLK] = &gcc_blsp2_uart5_apps_clk.clkr, + [GCC_BLSP2_UART6_APPS_CLK] = &gcc_blsp2_uart6_apps_clk.clkr, + [GCC_BOOT_ROM_AHB_CLK] = &gcc_boot_rom_ahb_clk.clkr, + [GCC_CE1_AHB_CLK] = &gcc_ce1_ahb_clk.clkr, + [GCC_CE1_AXI_CLK] = &gcc_ce1_axi_clk.clkr, + [GCC_CE1_CLK] = &gcc_ce1_clk.clkr, + [GCC_CE2_AHB_CLK] = &gcc_ce2_ahb_clk.clkr, + [GCC_CE2_AXI_CLK] = &gcc_ce2_axi_clk.clkr, + [GCC_CE2_CLK] = &gcc_ce2_clk.clkr, + [GCC_CE3_AHB_CLK] = &gcc_ce3_ahb_clk.clkr, + [GCC_CE3_AXI_CLK] = &gcc_ce3_axi_clk.clkr, + [GCC_CE3_CLK] = &gcc_ce3_clk.clkr, + [GCC_GP1_CLK] = &gcc_gp1_clk.clkr, + [GCC_GP2_CLK] = &gcc_gp2_clk.clkr, + [GCC_GP3_CLK] = &gcc_gp3_clk.clkr, + [GCC_OCMEM_NOC_CFG_AHB_CLK] = &gcc_ocmem_noc_cfg_ahb_clk.clkr, + [GCC_PDM2_CLK] = &gcc_pdm2_clk.clkr, + [GCC_PDM_AHB_CLK] = &gcc_pdm_ahb_clk.clkr, + [GCC_PERIPH_NOC_USB_HSIC_AHB_CLK] = &gcc_periph_noc_usb_hsic_ahb_clk.clkr, + [GCC_PRNG_AHB_CLK] = &gcc_prng_ahb_clk.clkr, + [GCC_SDCC1_AHB_CLK] = &gcc_sdcc1_ahb_clk.clkr, + [GCC_SDCC1_APPS_CLK] = &gcc_sdcc1_apps_clk.clkr, + [GCC_SDCC1_CDCCAL_FF_CLK] = &gcc_sdcc1_cdccal_ff_clk.clkr, + [GCC_SDCC1_CDCCAL_SLEEP_CLK] = &gcc_sdcc1_cdccal_sleep_clk.clkr, + [GCC_SDCC2_AHB_CLK] = &gcc_sdcc2_ahb_clk.clkr, + [GCC_SDCC2_APPS_CLK] = &gcc_sdcc2_apps_clk.clkr, + [GCC_SDCC3_AHB_CLK] = &gcc_sdcc3_ahb_clk.clkr, + [GCC_SDCC3_APPS_CLK] = &gcc_sdcc3_apps_clk.clkr, + [GCC_SDCC4_AHB_CLK] = &gcc_sdcc4_ahb_clk.clkr, + [GCC_SDCC4_APPS_CLK] = &gcc_sdcc4_apps_clk.clkr, + [GCC_SYS_NOC_USB3_AXI_CLK] = &gcc_sys_noc_usb3_axi_clk.clkr, + [GCC_SYS_NOC_USB3_SEC_AXI_CLK] = &gcc_sys_noc_usb3_sec_axi_clk.clkr, + [GCC_TSIF_AHB_CLK] = &gcc_tsif_ahb_clk.clkr, + [GCC_TSIF_INACTIVITY_TIMERS_CLK] = &gcc_tsif_inactivity_timers_clk.clkr, + [GCC_TSIF_REF_CLK] = &gcc_tsif_ref_clk.clkr, + [GCC_USB2A_PHY_SLEEP_CLK] = &gcc_usb2a_phy_sleep_clk.clkr, + [GCC_USB2B_PHY_SLEEP_CLK] = &gcc_usb2b_phy_sleep_clk.clkr, + [GCC_USB30_MASTER_CLK] = &gcc_usb30_master_clk.clkr, + [GCC_USB30_MOCK_UTMI_CLK] = &gcc_usb30_mock_utmi_clk.clkr, + [GCC_USB30_SLEEP_CLK] = &gcc_usb30_sleep_clk.clkr, + [GCC_USB30_SEC_MASTER_CLK] = &gcc_usb30_sec_master_clk.clkr, + [GCC_USB30_SEC_MOCK_UTMI_CLK] = &gcc_usb30_sec_mock_utmi_clk.clkr, + [GCC_USB30_SEC_SLEEP_CLK] = &gcc_usb30_sec_sleep_clk.clkr, + [GCC_USB_HS_AHB_CLK] = &gcc_usb_hs_ahb_clk.clkr, + [GCC_USB_HS_INACTIVITY_TIMERS_CLK] = &gcc_usb_hs_inactivity_timers_clk.clkr, + [GCC_USB_HS_SYSTEM_CLK] = &gcc_usb_hs_system_clk.clkr, + [GCC_USB_HSIC_AHB_CLK] = &gcc_usb_hsic_ahb_clk.clkr, + [GCC_USB_HSIC_CLK] = &gcc_usb_hsic_clk.clkr, + [GCC_USB_HSIC_IO_CAL_CLK] = &gcc_usb_hsic_io_cal_clk.clkr, + [GCC_USB_HSIC_IO_CAL_SLEEP_CLK] = &gcc_usb_hsic_io_cal_sleep_clk.clkr, + [GCC_USB_HSIC_MOCK_UTMI_CLK] = &gcc_usb_hsic_mock_utmi_clk.clkr, + [GCC_USB_HSIC_SYSTEM_CLK] = &gcc_usb_hsic_system_clk.clkr, +}; + +static const struct qcom_reset_map gcc_apq8084_resets[] = { + [GCC_SYSTEM_NOC_BCR] = { 0x0100 }, + [GCC_CONFIG_NOC_BCR] = { 0x0140 }, + [GCC_PERIPH_NOC_BCR] = { 0x0180 }, + [GCC_IMEM_BCR] = { 0x0200 }, + [GCC_MMSS_BCR] = { 0x0240 }, + [GCC_QDSS_BCR] = { 0x0300 }, + [GCC_USB_30_BCR] = { 0x03c0 }, + [GCC_USB3_PHY_BCR] = { 0x03fc }, + [GCC_USB_HS_HSIC_BCR] = { 0x0400 }, + [GCC_USB_HS_BCR] = { 0x0480 }, + [GCC_USB2A_PHY_BCR] = { 0x04a8 }, + [GCC_USB2B_PHY_BCR] = { 0x04b0 }, + [GCC_SDCC1_BCR] = { 0x04c0 }, + [GCC_SDCC2_BCR] = { 0x0500 }, + [GCC_SDCC3_BCR] = { 0x0540 }, + [GCC_SDCC4_BCR] = { 0x0580 }, + [GCC_BLSP1_BCR] = { 0x05c0 }, + [GCC_BLSP1_QUP1_BCR] = { 0x0640 }, + [GCC_BLSP1_UART1_BCR] = { 0x0680 }, + [GCC_BLSP1_QUP2_BCR] = { 0x06c0 }, + [GCC_BLSP1_UART2_BCR] = { 0x0700 }, + [GCC_BLSP1_QUP3_BCR] = { 0x0740 }, + [GCC_BLSP1_UART3_BCR] = { 0x0780 }, + [GCC_BLSP1_QUP4_BCR] = { 0x07c0 }, + [GCC_BLSP1_UART4_BCR] = { 0x0800 }, + [GCC_BLSP1_QUP5_BCR] = { 0x0840 }, + [GCC_BLSP1_UART5_BCR] = { 0x0880 }, + [GCC_BLSP1_QUP6_BCR] = { 0x08c0 }, + [GCC_BLSP1_UART6_BCR] = { 0x0900 }, + [GCC_BLSP2_BCR] = { 0x0940 }, + [GCC_BLSP2_QUP1_BCR] = { 0x0980 }, + [GCC_BLSP2_UART1_BCR] = { 0x09c0 }, + [GCC_BLSP2_QUP2_BCR] = { 0x0a00 }, + [GCC_BLSP2_UART2_BCR] = { 0x0a40 }, + [GCC_BLSP2_QUP3_BCR] = { 0x0a80 }, + [GCC_BLSP2_UART3_BCR] = { 0x0ac0 }, + [GCC_BLSP2_QUP4_BCR] = { 0x0b00 }, + [GCC_BLSP2_UART4_BCR] = { 0x0b40 }, + [GCC_BLSP2_QUP5_BCR] = { 0x0b80 }, + [GCC_BLSP2_UART5_BCR] = { 0x0bc0 }, + [GCC_BLSP2_QUP6_BCR] = { 0x0c00 }, + [GCC_BLSP2_UART6_BCR] = { 0x0c40 }, + [GCC_PDM_BCR] = { 0x0cc0 }, + [GCC_PRNG_BCR] = { 0x0d00 }, + [GCC_BAM_DMA_BCR] = { 0x0d40 }, + [GCC_TSIF_BCR] = { 0x0d80 }, + [GCC_TCSR_BCR] = { 0x0dc0 }, + [GCC_BOOT_ROM_BCR] = { 0x0e00 }, + [GCC_MSG_RAM_BCR] = { 0x0e40 }, + [GCC_TLMM_BCR] = { 0x0e80 }, + [GCC_MPM_BCR] = { 0x0ec0 }, + [GCC_MPM_AHB_RESET] = { 0x0ec4, 1 }, + [GCC_MPM_NON_AHB_RESET] = { 0x0ec4, 2 }, + [GCC_SEC_CTRL_BCR] = { 0x0f40 }, + [GCC_SPMI_BCR] = { 0x0fc0 }, + [GCC_SPDM_BCR] = { 0x1000 }, + [GCC_CE1_BCR] = { 0x1040 }, + [GCC_CE2_BCR] = { 0x1080 }, + [GCC_BIMC_BCR] = { 0x1100 }, + [GCC_SNOC_BUS_TIMEOUT0_BCR] = { 0x1240 }, + [GCC_SNOC_BUS_TIMEOUT2_BCR] = { 0x1248 }, + [GCC_PNOC_BUS_TIMEOUT0_BCR] = { 0x1280 }, + [GCC_PNOC_BUS_TIMEOUT1_BCR] = { 0x1288 }, + [GCC_PNOC_BUS_TIMEOUT2_BCR] = { 0x1290 }, + [GCC_PNOC_BUS_TIMEOUT3_BCR] = { 0x1298 }, + [GCC_PNOC_BUS_TIMEOUT4_BCR] = { 0x12a0 }, + [GCC_CNOC_BUS_TIMEOUT0_BCR] = { 0x12c0 }, + [GCC_CNOC_BUS_TIMEOUT1_BCR] = { 0x12c8 }, + [GCC_CNOC_BUS_TIMEOUT2_BCR] = { 0x12d0 }, + [GCC_CNOC_BUS_TIMEOUT3_BCR] = { 0x12d8 }, + [GCC_CNOC_BUS_TIMEOUT4_BCR] = { 0x12e0 }, + [GCC_CNOC_BUS_TIMEOUT5_BCR] = { 0x12e8 }, + [GCC_CNOC_BUS_TIMEOUT6_BCR] = { 0x12f0 }, + [GCC_DEHR_BCR] = { 0x1300 }, + [GCC_RBCPR_BCR] = { 0x1380 }, + [GCC_MSS_RESTART] = { 0x1680 }, + [GCC_LPASS_RESTART] = { 0x16c0 }, + [GCC_WCSS_RESTART] = { 0x1700 }, + [GCC_VENUS_RESTART] = { 0x1740 }, + [GCC_COPSS_SMMU_BCR] = { 0x1a40 }, + [GCC_SPSS_BCR] = { 0x1a80 }, + [GCC_PCIE_0_BCR] = { 0x1ac0 }, + [GCC_PCIE_0_PHY_BCR] = { 0x1b00 }, + [GCC_PCIE_1_BCR] = { 0x1b40 }, + [GCC_PCIE_1_PHY_BCR] = { 0x1b80 }, + [GCC_USB_30_SEC_BCR] = { 0x1bc0 }, + [GCC_USB3_SEC_PHY_BCR] = { 0x1bfc }, + [GCC_SATA_BCR] = { 0x1c40 }, + [GCC_CE3_BCR] = { 0x1d00 }, + [GCC_UFS_BCR] = { 0x1d40 }, + [GCC_USB30_PHY_COM_BCR] = { 0x1e80 }, +}; + +static const struct regmap_config gcc_apq8084_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1fc0, + .fast_io = true, +}; + +static const struct qcom_cc_desc gcc_apq8084_desc = { + .config = &gcc_apq8084_regmap_config, + .clks = gcc_apq8084_clocks, + .num_clks = ARRAY_SIZE(gcc_apq8084_clocks), + .resets = gcc_apq8084_resets, + .num_resets = ARRAY_SIZE(gcc_apq8084_resets), +}; + +static const struct of_device_id gcc_apq8084_match_table[] = { + { .compatible = "qcom,gcc-apq8084" }, + { } +}; +MODULE_DEVICE_TABLE(of, gcc_apq8084_match_table); + +static int gcc_apq8084_probe(struct platform_device *pdev) +{ + struct clk *clk; + struct device *dev = &pdev->dev; + + /* Temporary until RPM clocks supported */ + clk = clk_register_fixed_rate(dev, "xo", NULL, CLK_IS_ROOT, 19200000); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + clk = clk_register_fixed_rate(dev, "sleep_clk_src", NULL, + CLK_IS_ROOT, 32768); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + return qcom_cc_probe(pdev, &gcc_apq8084_desc); +} + +static int gcc_apq8084_remove(struct platform_device *pdev) +{ + qcom_cc_remove(pdev); + return 0; +} + +static struct platform_driver gcc_apq8084_driver = { + .probe = gcc_apq8084_probe, + .remove = gcc_apq8084_remove, + .driver = { + .name = "gcc-apq8084", + .owner = THIS_MODULE, + .of_match_table = gcc_apq8084_match_table, + }, +}; + +static int __init gcc_apq8084_init(void) +{ + return platform_driver_register(&gcc_apq8084_driver); +} +core_initcall(gcc_apq8084_init); + +static void __exit gcc_apq8084_exit(void) +{ + platform_driver_unregister(&gcc_apq8084_driver); +} +module_exit(gcc_apq8084_exit); + +MODULE_DESCRIPTION("QCOM GCC APQ8084 Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:gcc-apq8084"); diff --git a/include/dt-bindings/clock/qcom,gcc-apq8084.h b/include/dt-bindings/clock/qcom,gcc-apq8084.h new file mode 100644 index 000000000000..ce5bc0f171bd --- /dev/null +++ b/include/dt-bindings/clock/qcom,gcc-apq8084.h @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * 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 _DT_BINDINGS_CLK_APQ_GCC_8084_H +#define _DT_BINDINGS_CLK_APQ_GCC_8084_H + +#define GPLL0 0 +#define GPLL0_VOTE 1 +#define GPLL1 2 +#define GPLL1_VOTE 3 +#define GPLL2 4 +#define GPLL2_VOTE 5 +#define GPLL3 6 +#define GPLL3_VOTE 7 +#define GPLL4 8 +#define GPLL4_VOTE 9 +#define CONFIG_NOC_CLK_SRC 10 +#define PERIPH_NOC_CLK_SRC 11 +#define SYSTEM_NOC_CLK_SRC 12 +#define BLSP_UART_SIM_CLK_SRC 13 +#define QDSS_TSCTR_CLK_SRC 14 +#define UFS_AXI_CLK_SRC 15 +#define RPM_CLK_SRC 16 +#define KPSS_AHB_CLK_SRC 17 +#define QDSS_AT_CLK_SRC 18 +#define BIMC_DDR_CLK_SRC 19 +#define USB30_MASTER_CLK_SRC 20 +#define USB30_SEC_MASTER_CLK_SRC 21 +#define USB_HSIC_AHB_CLK_SRC 22 +#define MMSS_BIMC_GFX_CLK_SRC 23 +#define QDSS_STM_CLK_SRC 24 +#define ACC_CLK_SRC 25 +#define SEC_CTRL_CLK_SRC 26 +#define BLSP1_QUP1_I2C_APPS_CLK_SRC 27 +#define BLSP1_QUP1_SPI_APPS_CLK_SRC 28 +#define BLSP1_QUP2_I2C_APPS_CLK_SRC 29 +#define BLSP1_QUP2_SPI_APPS_CLK_SRC 30 +#define BLSP1_QUP3_I2C_APPS_CLK_SRC 31 +#define BLSP1_QUP3_SPI_APPS_CLK_SRC 32 +#define BLSP1_QUP4_I2C_APPS_CLK_SRC 33 +#define BLSP1_QUP4_SPI_APPS_CLK_SRC 34 +#define BLSP1_QUP5_I2C_APPS_CLK_SRC 35 +#define BLSP1_QUP5_SPI_APPS_CLK_SRC 36 +#define BLSP1_QUP6_I2C_APPS_CLK_SRC 37 +#define BLSP1_QUP6_SPI_APPS_CLK_SRC 38 +#define BLSP1_UART1_APPS_CLK_SRC 39 +#define BLSP1_UART2_APPS_CLK_SRC 40 +#define BLSP1_UART3_APPS_CLK_SRC 41 +#define BLSP1_UART4_APPS_CLK_SRC 42 +#define BLSP1_UART5_APPS_CLK_SRC 43 +#define BLSP1_UART6_APPS_CLK_SRC 44 +#define BLSP2_QUP1_I2C_APPS_CLK_SRC 45 +#define BLSP2_QUP1_SPI_APPS_CLK_SRC 46 +#define BLSP2_QUP2_I2C_APPS_CLK_SRC 47 +#define BLSP2_QUP2_SPI_APPS_CLK_SRC 48 +#define BLSP2_QUP3_I2C_APPS_CLK_SRC 49 +#define BLSP2_QUP3_SPI_APPS_CLK_SRC 50 +#define BLSP2_QUP4_I2C_APPS_CLK_SRC 51 +#define BLSP2_QUP4_SPI_APPS_CLK_SRC 52 +#define BLSP2_QUP5_I2C_APPS_CLK_SRC 53 +#define BLSP2_QUP5_SPI_APPS_CLK_SRC 54 +#define BLSP2_QUP6_I2C_APPS_CLK_SRC 55 +#define BLSP2_QUP6_SPI_APPS_CLK_SRC 56 +#define BLSP2_UART1_APPS_CLK_SRC 57 +#define BLSP2_UART2_APPS_CLK_SRC 58 +#define BLSP2_UART3_APPS_CLK_SRC 59 +#define BLSP2_UART4_APPS_CLK_SRC 60 +#define BLSP2_UART5_APPS_CLK_SRC 61 +#define BLSP2_UART6_APPS_CLK_SRC 62 +#define CE1_CLK_SRC 63 +#define CE2_CLK_SRC 64 +#define CE3_CLK_SRC 65 +#define GP1_CLK_SRC 66 +#define GP2_CLK_SRC 67 +#define GP3_CLK_SRC 68 +#define PDM2_CLK_SRC 69 +#define QDSS_TRACECLKIN_CLK_SRC 70 +#define RBCPR_CLK_SRC 71 +#define SATA_ASIC0_CLK_SRC 72 +#define SATA_PMALIVE_CLK_SRC 73 +#define SATA_RX_CLK_SRC 74 +#define SATA_RX_OOB_CLK_SRC 75 +#define SDCC1_APPS_CLK_SRC 76 +#define SDCC2_APPS_CLK_SRC 77 +#define SDCC3_APPS_CLK_SRC 78 +#define SDCC4_APPS_CLK_SRC 79 +#define GCC_SNOC_BUS_TIMEOUT0_AHB_CLK 80 +#define SPMI_AHB_CLK_SRC 81 +#define SPMI_SER_CLK_SRC 82 +#define TSIF_REF_CLK_SRC 83 +#define USB30_MOCK_UTMI_CLK_SRC 84 +#define USB30_SEC_MOCK_UTMI_CLK_SRC 85 +#define USB_HS_SYSTEM_CLK_SRC 86 +#define USB_HSIC_CLK_SRC 87 +#define USB_HSIC_IO_CAL_CLK_SRC 88 +#define USB_HSIC_MOCK_UTMI_CLK_SRC 89 +#define USB_HSIC_SYSTEM_CLK_SRC 90 +#define GCC_BAM_DMA_AHB_CLK 91 +#define GCC_BAM_DMA_INACTIVITY_TIMERS_CLK 92 +#define DDR_CLK_SRC 93 +#define GCC_BIMC_CFG_AHB_CLK 94 +#define GCC_BIMC_CLK 95 +#define GCC_BIMC_KPSS_AXI_CLK 96 +#define GCC_BIMC_SLEEP_CLK 97 +#define GCC_BIMC_SYSNOC_AXI_CLK 98 +#define GCC_BIMC_XO_CLK 99 +#define GCC_BLSP1_AHB_CLK 100 +#define GCC_BLSP1_SLEEP_CLK 101 +#define GCC_BLSP1_QUP1_I2C_APPS_CLK 102 +#define GCC_BLSP1_QUP1_SPI_APPS_CLK 103 +#define GCC_BLSP1_QUP2_I2C_APPS_CLK 104 +#define GCC_BLSP1_QUP2_SPI_APPS_CLK 105 +#define GCC_BLSP1_QUP3_I2C_APPS_CLK 106 +#define GCC_BLSP1_QUP3_SPI_APPS_CLK 107 +#define GCC_BLSP1_QUP4_I2C_APPS_CLK 108 +#define GCC_BLSP1_QUP4_SPI_APPS_CLK 109 +#define GCC_BLSP1_QUP5_I2C_APPS_CLK 110 +#define GCC_BLSP1_QUP5_SPI_APPS_CLK 111 +#define GCC_BLSP1_QUP6_I2C_APPS_CLK 112 +#define GCC_BLSP1_QUP6_SPI_APPS_CLK 113 +#define GCC_BLSP1_UART1_APPS_CLK 114 +#define GCC_BLSP1_UART1_SIM_CLK 115 +#define GCC_BLSP1_UART2_APPS_CLK 116 +#define GCC_BLSP1_UART2_SIM_CLK 117 +#define GCC_BLSP1_UART3_APPS_CLK 118 +#define GCC_BLSP1_UART3_SIM_CLK 119 +#define GCC_BLSP1_UART4_APPS_CLK 120 +#define GCC_BLSP1_UART4_SIM_CLK 121 +#define GCC_BLSP1_UART5_APPS_CLK 122 +#define GCC_BLSP1_UART5_SIM_CLK 123 +#define GCC_BLSP1_UART6_APPS_CLK 124 +#define GCC_BLSP1_UART6_SIM_CLK 125 +#define GCC_BLSP2_AHB_CLK 126 +#define GCC_BLSP2_SLEEP_CLK 127 +#define GCC_BLSP2_QUP1_I2C_APPS_CLK 128 +#define GCC_BLSP2_QUP1_SPI_APPS_CLK 129 +#define GCC_BLSP2_QUP2_I2C_APPS_CLK 130 +#define GCC_BLSP2_QUP2_SPI_APPS_CLK 131 +#define GCC_BLSP2_QUP3_I2C_APPS_CLK 132 +#define GCC_BLSP2_QUP3_SPI_APPS_CLK 133 +#define GCC_BLSP2_QUP4_I2C_APPS_CLK 134 +#define GCC_BLSP2_QUP4_SPI_APPS_CLK 135 +#define GCC_BLSP2_QUP5_I2C_APPS_CLK 136 +#define GCC_BLSP2_QUP5_SPI_APPS_CLK 137 +#define GCC_BLSP2_QUP6_I2C_APPS_CLK 138 +#define GCC_BLSP2_QUP6_SPI_APPS_CLK 139 +#define GCC_BLSP2_UART1_APPS_CLK 140 +#define GCC_BLSP2_UART1_SIM_CLK 141 +#define GCC_BLSP2_UART2_APPS_CLK 142 +#define GCC_BLSP2_UART2_SIM_CLK 143 +#define GCC_BLSP2_UART3_APPS_CLK 144 +#define GCC_BLSP2_UART3_SIM_CLK 145 +#define GCC_BLSP2_UART4_APPS_CLK 146 +#define GCC_BLSP2_UART4_SIM_CLK 147 +#define GCC_BLSP2_UART5_APPS_CLK 148 +#define GCC_BLSP2_UART5_SIM_CLK 149 +#define GCC_BLSP2_UART6_APPS_CLK 150 +#define GCC_BLSP2_UART6_SIM_CLK 151 +#define GCC_BOOT_ROM_AHB_CLK 152 +#define GCC_CE1_AHB_CLK 153 +#define GCC_CE1_AXI_CLK 154 +#define GCC_CE1_CLK 155 +#define GCC_CE2_AHB_CLK 156 +#define GCC_CE2_AXI_CLK 157 +#define GCC_CE2_CLK 158 +#define GCC_CE3_AHB_CLK 159 +#define GCC_CE3_AXI_CLK 160 +#define GCC_CE3_CLK 161 +#define GCC_CNOC_BUS_TIMEOUT0_AHB_CLK 162 +#define GCC_CNOC_BUS_TIMEOUT1_AHB_CLK 163 +#define GCC_CNOC_BUS_TIMEOUT2_AHB_CLK 164 +#define GCC_CNOC_BUS_TIMEOUT3_AHB_CLK 165 +#define GCC_CNOC_BUS_TIMEOUT4_AHB_CLK 166 +#define GCC_CNOC_BUS_TIMEOUT5_AHB_CLK 167 +#define GCC_CNOC_BUS_TIMEOUT6_AHB_CLK 168 +#define GCC_CNOC_BUS_TIMEOUT7_AHB_CLK 169 +#define GCC_CFG_NOC_AHB_CLK 170 +#define GCC_CFG_NOC_DDR_CFG_CLK 171 +#define GCC_CFG_NOC_RPM_AHB_CLK 172 +#define GCC_COPSS_SMMU_AHB_CLK 173 +#define GCC_COPSS_SMMU_AXI_CLK 174 +#define GCC_DCD_XO_CLK 175 +#define GCC_BIMC_DDR_CH0_CLK 176 +#define GCC_BIMC_DDR_CH1_CLK 177 +#define GCC_BIMC_DDR_CPLL0_CLK 178 +#define GCC_BIMC_DDR_CPLL1_CLK 179 +#define GCC_BIMC_GFX_CLK 180 +#define GCC_DDR_DIM_CFG_CLK 181 +#define GCC_DDR_DIM_SLEEP_CLK 182 +#define GCC_DEHR_CLK 183 +#define GCC_AHB_CLK 184 +#define GCC_IM_SLEEP_CLK 185 +#define GCC_XO_CLK 186 +#define GCC_XO_DIV4_CLK 187 +#define GCC_GP1_CLK 188 +#define GCC_GP2_CLK 189 +#define GCC_GP3_CLK 190 +#define GCC_IMEM_AXI_CLK 191 +#define GCC_IMEM_CFG_AHB_CLK 192 +#define GCC_KPSS_AHB_CLK 193 +#define GCC_KPSS_AXI_CLK 194 +#define GCC_LPASS_MPORT_AXI_CLK 195 +#define GCC_LPASS_Q6_AXI_CLK 196 +#define GCC_LPASS_SWAY_CLK 197 +#define GCC_MMSS_BIMC_GFX_CLK 198 +#define GCC_MMSS_NOC_AT_CLK 199 +#define GCC_MMSS_NOC_CFG_AHB_CLK 200 +#define GCC_MMSS_VPU_MAPLE_SYS_NOC_AXI_CLK 201 +#define GCC_OCMEM_NOC_CFG_AHB_CLK 202 +#define GCC_OCMEM_SYS_NOC_AXI_CLK 203 +#define GCC_MPM_AHB_CLK 204 +#define GCC_MSG_RAM_AHB_CLK 205 +#define GCC_NOC_CONF_XPU_AHB_CLK 206 +#define GCC_PDM2_CLK 207 +#define GCC_PDM_AHB_CLK 208 +#define GCC_PDM_XO4_CLK 209 +#define GCC_PERIPH_NOC_AHB_CLK 210 +#define GCC_PERIPH_NOC_AT_CLK 211 +#define GCC_PERIPH_NOC_CFG_AHB_CLK 212 +#define GCC_PERIPH_NOC_USB_HSIC_AHB_CLK 213 +#define GCC_PERIPH_NOC_MPU_CFG_AHB_CLK 214 +#define GCC_PERIPH_XPU_AHB_CLK 215 +#define GCC_PNOC_BUS_TIMEOUT0_AHB_CLK 216 +#define GCC_PNOC_BUS_TIMEOUT1_AHB_CLK 217 +#define GCC_PNOC_BUS_TIMEOUT2_AHB_CLK 218 +#define GCC_PNOC_BUS_TIMEOUT3_AHB_CLK 219 +#define GCC_PNOC_BUS_TIMEOUT4_AHB_CLK 220 +#define GCC_PRNG_AHB_CLK 221 +#define GCC_QDSS_AT_CLK 222 +#define GCC_QDSS_CFG_AHB_CLK 223 +#define GCC_QDSS_DAP_AHB_CLK 224 +#define GCC_QDSS_DAP_CLK 225 +#define GCC_QDSS_ETR_USB_CLK 226 +#define GCC_QDSS_STM_CLK 227 +#define GCC_QDSS_TRACECLKIN_CLK 228 +#define GCC_QDSS_TSCTR_DIV16_CLK 229 +#define GCC_QDSS_TSCTR_DIV2_CLK 230 +#define GCC_QDSS_TSCTR_DIV3_CLK 231 +#define GCC_QDSS_TSCTR_DIV4_CLK 232 +#define GCC_QDSS_TSCTR_DIV8_CLK 233 +#define GCC_QDSS_RBCPR_XPU_AHB_CLK 234 +#define GCC_RBCPR_AHB_CLK 235 +#define GCC_RBCPR_CLK 236 +#define GCC_RPM_BUS_AHB_CLK 237 +#define GCC_RPM_PROC_HCLK 238 +#define GCC_RPM_SLEEP_CLK 239 +#define GCC_RPM_TIMER_CLK 240 +#define GCC_SATA_ASIC0_CLK 241 +#define GCC_SATA_AXI_CLK 242 +#define GCC_SATA_CFG_AHB_CLK 243 +#define GCC_SATA_PMALIVE_CLK 244 +#define GCC_SATA_RX_CLK 245 +#define GCC_SATA_RX_OOB_CLK 246 +#define GCC_SDCC1_AHB_CLK 247 +#define GCC_SDCC1_APPS_CLK 248 +#define GCC_SDCC1_CDCCAL_FF_CLK 249 +#define GCC_SDCC1_CDCCAL_SLEEP_CLK 250 +#define GCC_SDCC2_AHB_CLK 251 +#define GCC_SDCC2_APPS_CLK 252 +#define GCC_SDCC2_INACTIVITY_TIMERS_CLK 253 +#define GCC_SDCC3_AHB_CLK 254 +#define GCC_SDCC3_APPS_CLK 255 +#define GCC_SDCC3_INACTIVITY_TIMERS_CLK 256 +#define GCC_SDCC4_AHB_CLK 257 +#define GCC_SDCC4_APPS_CLK 258 +#define GCC_SDCC4_INACTIVITY_TIMERS_CLK 259 +#define GCC_SEC_CTRL_ACC_CLK 260 +#define GCC_SEC_CTRL_AHB_CLK 261 +#define GCC_SEC_CTRL_BOOT_ROM_PATCH_CLK 262 +#define GCC_SEC_CTRL_CLK 263 +#define GCC_SEC_CTRL_SENSE_CLK 264 +#define GCC_SNOC_BUS_TIMEOUT2_AHB_CLK 265 +#define GCC_SNOC_BUS_TIMEOUT3_AHB_CLK 266 +#define GCC_SPDM_BIMC_CY_CLK 267 +#define GCC_SPDM_CFG_AHB_CLK 268 +#define GCC_SPDM_DEBUG_CY_CLK 269 +#define GCC_SPDM_FF_CLK 270 +#define GCC_SPDM_MSTR_AHB_CLK 271 +#define GCC_SPDM_PNOC_CY_CLK 272 +#define GCC_SPDM_RPM_CY_CLK 273 +#define GCC_SPDM_SNOC_CY_CLK 274 +#define GCC_SPMI_AHB_CLK 275 +#define GCC_SPMI_CNOC_AHB_CLK 276 +#define GCC_SPMI_SER_CLK 277 +#define GCC_SPSS_AHB_CLK 278 +#define GCC_SNOC_CNOC_AHB_CLK 279 +#define GCC_SNOC_PNOC_AHB_CLK 280 +#define GCC_SYS_NOC_AT_CLK 281 +#define GCC_SYS_NOC_AXI_CLK 282 +#define GCC_SYS_NOC_KPSS_AHB_CLK 283 +#define GCC_SYS_NOC_QDSS_STM_AXI_CLK 284 +#define GCC_SYS_NOC_UFS_AXI_CLK 285 +#define GCC_SYS_NOC_USB3_AXI_CLK 286 +#define GCC_SYS_NOC_USB3_SEC_AXI_CLK 287 +#define GCC_TCSR_AHB_CLK 288 +#define GCC_TLMM_AHB_CLK 289 +#define GCC_TLMM_CLK 290 +#define GCC_TSIF_AHB_CLK 291 +#define GCC_TSIF_INACTIVITY_TIMERS_CLK 292 +#define GCC_TSIF_REF_CLK 293 +#define GCC_UFS_AHB_CLK 294 +#define GCC_UFS_AXI_CLK 295 +#define GCC_UFS_RX_CFG_CLK 296 +#define GCC_UFS_RX_SYMBOL_0_CLK 297 +#define GCC_UFS_RX_SYMBOL_1_CLK 298 +#define GCC_UFS_TX_CFG_CLK 299 +#define GCC_UFS_TX_SYMBOL_0_CLK 300 +#define GCC_UFS_TX_SYMBOL_1_CLK 301 +#define GCC_USB2A_PHY_SLEEP_CLK 302 +#define GCC_USB2B_PHY_SLEEP_CLK 303 +#define GCC_USB30_MASTER_CLK 304 +#define GCC_USB30_MOCK_UTMI_CLK 305 +#define GCC_USB30_SLEEP_CLK 306 +#define GCC_USB30_SEC_MASTER_CLK 307 +#define GCC_USB30_SEC_MOCK_UTMI_CLK 308 +#define GCC_USB30_SEC_SLEEP_CLK 309 +#define GCC_USB_HS_AHB_CLK 310 +#define GCC_USB_HS_INACTIVITY_TIMERS_CLK 311 +#define GCC_USB_HS_SYSTEM_CLK 312 +#define GCC_USB_HSIC_AHB_CLK 313 +#define GCC_USB_HSIC_CLK 314 +#define GCC_USB_HSIC_IO_CAL_CLK 315 +#define GCC_USB_HSIC_IO_CAL_SLEEP_CLK 316 +#define GCC_USB_HSIC_MOCK_UTMI_CLK 317 +#define GCC_USB_HSIC_SYSTEM_CLK 318 + +#endif diff --git a/include/dt-bindings/reset/qcom,gcc-apq8084.h b/include/dt-bindings/reset/qcom,gcc-apq8084.h new file mode 100644 index 000000000000..527caaf48e3d --- /dev/null +++ b/include/dt-bindings/reset/qcom,gcc-apq8084.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * 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 _DT_BINDINGS_RESET_APQ_GCC_8084_H +#define _DT_BINDINGS_RESET_APQ_GCC_8084_H + +#define GCC_SYSTEM_NOC_BCR 0 +#define GCC_CONFIG_NOC_BCR 1 +#define GCC_PERIPH_NOC_BCR 2 +#define GCC_IMEM_BCR 3 +#define GCC_MMSS_BCR 4 +#define GCC_QDSS_BCR 5 +#define GCC_USB_30_BCR 6 +#define GCC_USB3_PHY_BCR 7 +#define GCC_USB_HS_HSIC_BCR 8 +#define GCC_USB_HS_BCR 9 +#define GCC_USB2A_PHY_BCR 10 +#define GCC_USB2B_PHY_BCR 11 +#define GCC_SDCC1_BCR 12 +#define GCC_SDCC2_BCR 13 +#define GCC_SDCC3_BCR 14 +#define GCC_SDCC4_BCR 15 +#define GCC_BLSP1_BCR 16 +#define GCC_BLSP1_QUP1_BCR 17 +#define GCC_BLSP1_UART1_BCR 18 +#define GCC_BLSP1_QUP2_BCR 19 +#define GCC_BLSP1_UART2_BCR 20 +#define GCC_BLSP1_QUP3_BCR 21 +#define GCC_BLSP1_UART3_BCR 22 +#define GCC_BLSP1_QUP4_BCR 23 +#define GCC_BLSP1_UART4_BCR 24 +#define GCC_BLSP1_QUP5_BCR 25 +#define GCC_BLSP1_UART5_BCR 26 +#define GCC_BLSP1_QUP6_BCR 27 +#define GCC_BLSP1_UART6_BCR 28 +#define GCC_BLSP2_BCR 29 +#define GCC_BLSP2_QUP1_BCR 30 +#define GCC_BLSP2_UART1_BCR 31 +#define GCC_BLSP2_QUP2_BCR 32 +#define GCC_BLSP2_UART2_BCR 33 +#define GCC_BLSP2_QUP3_BCR 34 +#define GCC_BLSP2_UART3_BCR 35 +#define GCC_BLSP2_QUP4_BCR 36 +#define GCC_BLSP2_UART4_BCR 37 +#define GCC_BLSP2_QUP5_BCR 38 +#define GCC_BLSP2_UART5_BCR 39 +#define GCC_BLSP2_QUP6_BCR 40 +#define GCC_BLSP2_UART6_BCR 41 +#define GCC_PDM_BCR 42 +#define GCC_PRNG_BCR 43 +#define GCC_BAM_DMA_BCR 44 +#define GCC_TSIF_BCR 45 +#define GCC_TCSR_BCR 46 +#define GCC_BOOT_ROM_BCR 47 +#define GCC_MSG_RAM_BCR 48 +#define GCC_TLMM_BCR 49 +#define GCC_MPM_BCR 50 +#define GCC_MPM_AHB_RESET 51 +#define GCC_MPM_NON_AHB_RESET 52 +#define GCC_SEC_CTRL_BCR 53 +#define GCC_SPMI_BCR 54 +#define GCC_SPDM_BCR 55 +#define GCC_CE1_BCR 56 +#define GCC_CE2_BCR 57 +#define GCC_BIMC_BCR 58 +#define GCC_SNOC_BUS_TIMEOUT0_BCR 59 +#define GCC_SNOC_BUS_TIMEOUT2_BCR 60 +#define GCC_PNOC_BUS_TIMEOUT0_BCR 61 +#define GCC_PNOC_BUS_TIMEOUT1_BCR 62 +#define GCC_PNOC_BUS_TIMEOUT2_BCR 63 +#define GCC_PNOC_BUS_TIMEOUT3_BCR 64 +#define GCC_PNOC_BUS_TIMEOUT4_BCR 65 +#define GCC_CNOC_BUS_TIMEOUT0_BCR 66 +#define GCC_CNOC_BUS_TIMEOUT1_BCR 67 +#define GCC_CNOC_BUS_TIMEOUT2_BCR 68 +#define GCC_CNOC_BUS_TIMEOUT3_BCR 69 +#define GCC_CNOC_BUS_TIMEOUT4_BCR 70 +#define GCC_CNOC_BUS_TIMEOUT5_BCR 71 +#define GCC_CNOC_BUS_TIMEOUT6_BCR 72 +#define GCC_DEHR_BCR 73 +#define GCC_RBCPR_BCR 74 +#define GCC_MSS_RESTART 75 +#define GCC_LPASS_RESTART 76 +#define GCC_WCSS_RESTART 77 +#define GCC_VENUS_RESTART 78 +#define GCC_COPSS_SMMU_BCR 79 +#define GCC_SPSS_BCR 80 +#define GCC_PCIE_0_BCR 81 +#define GCC_PCIE_0_PHY_BCR 82 +#define GCC_PCIE_1_BCR 83 +#define GCC_PCIE_1_PHY_BCR 84 +#define GCC_USB_30_SEC_BCR 85 +#define GCC_USB3_SEC_PHY_BCR 86 +#define GCC_SATA_BCR 87 +#define GCC_CE3_BCR 88 +#define GCC_UFS_BCR 89 +#define GCC_USB30_PHY_COM_BCR 90 + +#endif -- cgit v1.2.3 From 5424e102815762f835b998536170e791681f6411 Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Thu, 19 Jun 2014 21:07:19 +0300 Subject: clk: qcom: Add APQ8084 clocks for SATA, PCIe and UFS Add the necessary clocks for SATA, PCIe and UFS to the APQ8084 global clock controller (GCC). This will allow the above device drivers to control their clocks. Signed-off-by: Georgi Djakov Signed-off-by: Stephen Boyd --- drivers/clk/qcom/gcc-apq8084.c | 667 +++++++++++++++++++++++++++ include/dt-bindings/clock/qcom,gcc-apq8084.h | 14 + 2 files changed, 681 insertions(+) (limited to 'include') diff --git a/drivers/clk/qcom/gcc-apq8084.c b/drivers/clk/qcom/gcc-apq8084.c index b9e6140674cc..ee52eb1c838a 100644 --- a/drivers/clk/qcom/gcc-apq8084.c +++ b/drivers/clk/qcom/gcc-apq8084.c @@ -36,6 +36,10 @@ #define P_GPLL0 1 #define P_GPLL1 1 #define P_GPLL4 2 +#define P_PCIE_0_1_PIPE_CLK 1 +#define P_SATA_ASIC0_CLK 1 +#define P_SATA_RX_CLK 1 +#define P_SLEEP_CLK 1 static const u8 gcc_xo_gpll0_map[] = { [P_XO] = 0, @@ -59,6 +63,46 @@ static const char *gcc_xo_gpll0_gpll4[] = { "gpll4_vote", }; +static const u8 gcc_xo_sata_asic0_map[] = { + [P_XO] = 0, + [P_SATA_ASIC0_CLK] = 2, +}; + +static const char *gcc_xo_sata_asic0[] = { + "xo", + "sata_asic0_clk", +}; + +static const u8 gcc_xo_sata_rx_map[] = { + [P_XO] = 0, + [P_SATA_RX_CLK] = 2, +}; + +static const char *gcc_xo_sata_rx[] = { + "xo", + "sata_rx_clk", +}; + +static const u8 gcc_xo_pcie_map[] = { + [P_XO] = 0, + [P_PCIE_0_1_PIPE_CLK] = 2, +}; + +static const char *gcc_xo_pcie[] = { + "xo", + "pcie_pipe", +}; + +static const u8 gcc_xo_pcie_sleep_map[] = { + [P_XO] = 0, + [P_SLEEP_CLK] = 6, +}; + +static const char *gcc_xo_pcie_sleep[] = { + "xo", + "sleep_clk_src", +}; + #define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } static struct clk_pll gpll0 = { @@ -178,6 +222,27 @@ static struct clk_regmap gpll4_vote = { }, }; +static const struct freq_tbl ftbl_gcc_ufs_axi_clk[] = { + F(100000000, P_GPLL0, 6, 0, 0), + F(200000000, P_GPLL0, 3, 0, 0), + F(240000000, P_GPLL0, 2.5, 0, 0), + { } +}; + +static struct clk_rcg2 ufs_axi_clk_src = { + .cmd_rcgr = 0x1d64, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_ufs_axi_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "ufs_axi_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + static const struct freq_tbl ftbl_gcc_usb30_master_clk[] = { F(125000000, P_GPLL0, 1, 5, 24), { } @@ -890,6 +955,71 @@ static struct clk_rcg2 gp3_clk_src = { }, }; +static const struct freq_tbl ftbl_gcc_pcie_0_1_aux_clk[] = { + F(1010000, P_XO, 1, 1, 19), + { } +}; + +static struct clk_rcg2 pcie_0_aux_clk_src = { + .cmd_rcgr = 0x1b2c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_xo_pcie_sleep_map, + .freq_tbl = ftbl_gcc_pcie_0_1_aux_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pcie_0_aux_clk_src", + .parent_names = gcc_xo_pcie_sleep, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 pcie_1_aux_clk_src = { + .cmd_rcgr = 0x1bac, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_xo_pcie_sleep_map, + .freq_tbl = ftbl_gcc_pcie_0_1_aux_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pcie_1_aux_clk_src", + .parent_names = gcc_xo_pcie_sleep, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pcie_0_1_pipe_clk[] = { + F(125000000, P_PCIE_0_1_PIPE_CLK, 1, 0, 0), + F(250000000, P_PCIE_0_1_PIPE_CLK, 1, 0, 0), + { } +}; + +static struct clk_rcg2 pcie_0_pipe_clk_src = { + .cmd_rcgr = 0x1b18, + .hid_width = 5, + .parent_map = gcc_xo_pcie_map, + .freq_tbl = ftbl_gcc_pcie_0_1_pipe_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pcie_0_pipe_clk_src", + .parent_names = gcc_xo_pcie, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 pcie_1_pipe_clk_src = { + .cmd_rcgr = 0x1b98, + .hid_width = 5, + .parent_map = gcc_xo_pcie_map, + .freq_tbl = ftbl_gcc_pcie_0_1_pipe_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pcie_1_pipe_clk_src", + .parent_names = gcc_xo_pcie, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + static const struct freq_tbl ftbl_gcc_pdm2_clk[] = { F(60000000, P_GPLL0, 10, 0, 0), { } @@ -908,6 +1038,84 @@ static struct clk_rcg2 pdm2_clk_src = { }, }; +static const struct freq_tbl ftbl_gcc_sata_asic0_clk[] = { + F(75000000, P_SATA_ASIC0_CLK, 1, 0, 0), + F(150000000, P_SATA_ASIC0_CLK, 1, 0, 0), + F(300000000, P_SATA_ASIC0_CLK, 1, 0, 0), + { } +}; + +static struct clk_rcg2 sata_asic0_clk_src = { + .cmd_rcgr = 0x1c94, + .hid_width = 5, + .parent_map = gcc_xo_sata_asic0_map, + .freq_tbl = ftbl_gcc_sata_asic0_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "sata_asic0_clk_src", + .parent_names = gcc_xo_sata_asic0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_sata_pmalive_clk[] = { + F(19200000, P_XO, 1, 0, 0), + F(50000000, P_GPLL0, 12, 0, 0), + F(100000000, P_GPLL0, 6, 0, 0), + { } +}; + +static struct clk_rcg2 sata_pmalive_clk_src = { + .cmd_rcgr = 0x1c80, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_sata_pmalive_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "sata_pmalive_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_sata_rx_clk[] = { + F(75000000, P_SATA_RX_CLK, 1, 0, 0), + F(150000000, P_SATA_RX_CLK, 1, 0, 0), + F(300000000, P_SATA_RX_CLK, 1, 0, 0), + { } +}; + +static struct clk_rcg2 sata_rx_clk_src = { + .cmd_rcgr = 0x1ca8, + .hid_width = 5, + .parent_map = gcc_xo_sata_rx_map, + .freq_tbl = ftbl_gcc_sata_rx_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "sata_rx_clk_src", + .parent_names = gcc_xo_sata_rx, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_sata_rx_oob_clk[] = { + F(100000000, P_GPLL0, 6, 0, 0), + { } +}; + +static struct clk_rcg2 sata_rx_oob_clk_src = { + .cmd_rcgr = 0x1c5c, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .freq_tbl = ftbl_gcc_sata_rx_oob_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "sata_rx_oob_clk_src", + .parent_names = gcc_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + static const struct freq_tbl ftbl_gcc_sdcc1_4_apps_clk[] = { F(144000, P_XO, 16, 3, 25), F(400000, P_XO, 12, 1, 4), @@ -2071,6 +2279,176 @@ static struct clk_branch gcc_ocmem_noc_cfg_ahb_clk = { }, }; +static struct clk_branch gcc_pcie_0_aux_clk = { + .halt_reg = 0x1b10, + .clkr = { + .enable_reg = 0x1b10, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie_0_aux_clk", + .parent_names = (const char *[]){ + "pcie_0_aux_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_cfg_ahb_clk = { + .halt_reg = 0x1b0c, + .clkr = { + .enable_reg = 0x1b0c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie_0_cfg_ahb_clk", + .parent_names = (const char *[]){ + "config_noc_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_mstr_axi_clk = { + .halt_reg = 0x1b08, + .clkr = { + .enable_reg = 0x1b08, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie_0_mstr_axi_clk", + .parent_names = (const char *[]){ + "config_noc_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_pipe_clk = { + .halt_reg = 0x1b14, + .clkr = { + .enable_reg = 0x1b14, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie_0_pipe_clk", + .parent_names = (const char *[]){ + "pcie_0_pipe_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_slv_axi_clk = { + .halt_reg = 0x1b04, + .clkr = { + .enable_reg = 0x1b04, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie_0_slv_axi_clk", + .parent_names = (const char *[]){ + "config_noc_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_aux_clk = { + .halt_reg = 0x1b90, + .clkr = { + .enable_reg = 0x1b90, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie_1_aux_clk", + .parent_names = (const char *[]){ + "pcie_1_aux_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_cfg_ahb_clk = { + .halt_reg = 0x1b8c, + .clkr = { + .enable_reg = 0x1b8c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie_1_cfg_ahb_clk", + .parent_names = (const char *[]){ + "config_noc_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_mstr_axi_clk = { + .halt_reg = 0x1b88, + .clkr = { + .enable_reg = 0x1b88, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie_1_mstr_axi_clk", + .parent_names = (const char *[]){ + "config_noc_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_pipe_clk = { + .halt_reg = 0x1b94, + .clkr = { + .enable_reg = 0x1b94, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie_1_pipe_clk", + .parent_names = (const char *[]){ + "pcie_1_pipe_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_slv_axi_clk = { + .halt_reg = 0x1b84, + .clkr = { + .enable_reg = 0x1b84, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie_1_slv_axi_clk", + .parent_names = (const char *[]){ + "config_noc_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + static struct clk_branch gcc_pdm2_clk = { .halt_reg = 0x0ccc, .clkr = { @@ -2138,6 +2516,108 @@ static struct clk_branch gcc_prng_ahb_clk = { }, }; +static struct clk_branch gcc_sata_asic0_clk = { + .halt_reg = 0x1c54, + .clkr = { + .enable_reg = 0x1c54, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sata_asic0_clk", + .parent_names = (const char *[]){ + "sata_asic0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sata_axi_clk = { + .halt_reg = 0x1c44, + .clkr = { + .enable_reg = 0x1c44, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sata_axi_clk", + .parent_names = (const char *[]){ + "config_noc_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sata_cfg_ahb_clk = { + .halt_reg = 0x1c48, + .clkr = { + .enable_reg = 0x1c48, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sata_cfg_ahb_clk", + .parent_names = (const char *[]){ + "config_noc_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sata_pmalive_clk = { + .halt_reg = 0x1c50, + .clkr = { + .enable_reg = 0x1c50, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sata_pmalive_clk", + .parent_names = (const char *[]){ + "sata_pmalive_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sata_rx_clk = { + .halt_reg = 0x1c58, + .clkr = { + .enable_reg = 0x1c58, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sata_rx_clk", + .parent_names = (const char *[]){ + "sata_rx_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sata_rx_oob_clk = { + .halt_reg = 0x1c4c, + .clkr = { + .enable_reg = 0x1c4c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sata_rx_oob_clk", + .parent_names = (const char *[]){ + "sata_rx_oob_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + static struct clk_branch gcc_sdcc1_ahb_clk = { .halt_reg = 0x04c8, .clkr = { @@ -2302,6 +2782,23 @@ static struct clk_branch gcc_sdcc4_apps_clk = { }, }; +static struct clk_branch gcc_sys_noc_ufs_axi_clk = { + .halt_reg = 0x013c, + .clkr = { + .enable_reg = 0x013c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sys_noc_ufs_axi_clk", + .parent_names = (const char *[]){ + "ufs_axi_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + static struct clk_branch gcc_sys_noc_usb3_axi_clk = { .halt_reg = 0x0108, .clkr = { @@ -2386,6 +2883,142 @@ static struct clk_branch gcc_tsif_ref_clk = { }, }; +static struct clk_branch gcc_ufs_ahb_clk = { + .halt_reg = 0x1d48, + .clkr = { + .enable_reg = 0x1d48, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ufs_ahb_clk", + .parent_names = (const char *[]){ + "config_noc_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_axi_clk = { + .halt_reg = 0x1d44, + .clkr = { + .enable_reg = 0x1d44, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ufs_axi_clk", + .parent_names = (const char *[]){ + "ufs_axi_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_rx_cfg_clk = { + .halt_reg = 0x1d50, + .clkr = { + .enable_reg = 0x1d50, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ufs_rx_cfg_clk", + .parent_names = (const char *[]){ + "ufs_axi_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_rx_symbol_0_clk = { + .halt_reg = 0x1d5c, + .clkr = { + .enable_reg = 0x1d5c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ufs_rx_symbol_0_clk", + .parent_names = (const char *[]){ + "ufs_rx_symbol_0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_rx_symbol_1_clk = { + .halt_reg = 0x1d60, + .clkr = { + .enable_reg = 0x1d60, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ufs_rx_symbol_1_clk", + .parent_names = (const char *[]){ + "ufs_rx_symbol_1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_tx_cfg_clk = { + .halt_reg = 0x1d4c, + .clkr = { + .enable_reg = 0x1d4c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ufs_tx_cfg_clk", + .parent_names = (const char *[]){ + "ufs_axi_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_tx_symbol_0_clk = { + .halt_reg = 0x1d54, + .clkr = { + .enable_reg = 0x1d54, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ufs_tx_symbol_0_clk", + .parent_names = (const char *[]){ + "ufs_tx_symbol_0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_tx_symbol_1_clk = { + .halt_reg = 0x1d58, + .clkr = { + .enable_reg = 0x1d58, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ufs_tx_symbol_1_clk", + .parent_names = (const char *[]){ + "ufs_tx_symbol_1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + static struct clk_branch gcc_usb2a_phy_sleep_clk = { .halt_reg = 0x04ac, .clkr = { @@ -2628,6 +3261,7 @@ static struct clk_regmap *gcc_apq8084_clocks[] = { [CONFIG_NOC_CLK_SRC] = &config_noc_clk_src.clkr, [PERIPH_NOC_CLK_SRC] = &periph_noc_clk_src.clkr, [SYSTEM_NOC_CLK_SRC] = &system_noc_clk_src.clkr, + [UFS_AXI_CLK_SRC] = &ufs_axi_clk_src.clkr, [USB30_MASTER_CLK_SRC] = &usb30_master_clk_src.clkr, [USB30_SEC_MASTER_CLK_SRC] = &usb30_sec_master_clk_src.clkr, [USB_HSIC_AHB_CLK_SRC] = &usb_hsic_ahb_clk_src.clkr, @@ -2673,7 +3307,15 @@ static struct clk_regmap *gcc_apq8084_clocks[] = { [GP1_CLK_SRC] = &gp1_clk_src.clkr, [GP2_CLK_SRC] = &gp2_clk_src.clkr, [GP3_CLK_SRC] = &gp3_clk_src.clkr, + [PCIE_0_AUX_CLK_SRC] = &pcie_0_aux_clk_src.clkr, + [PCIE_0_PIPE_CLK_SRC] = &pcie_0_pipe_clk_src.clkr, + [PCIE_1_AUX_CLK_SRC] = &pcie_1_aux_clk_src.clkr, + [PCIE_1_PIPE_CLK_SRC] = &pcie_1_pipe_clk_src.clkr, [PDM2_CLK_SRC] = &pdm2_clk_src.clkr, + [SATA_ASIC0_CLK_SRC] = &sata_asic0_clk_src.clkr, + [SATA_PMALIVE_CLK_SRC] = &sata_pmalive_clk_src.clkr, + [SATA_RX_CLK_SRC] = &sata_rx_clk_src.clkr, + [SATA_RX_OOB_CLK_SRC] = &sata_rx_oob_clk_src.clkr, [SDCC1_APPS_CLK_SRC] = &sdcc1_apps_clk_src.clkr, [SDCC2_APPS_CLK_SRC] = &sdcc2_apps_clk_src.clkr, [SDCC3_APPS_CLK_SRC] = &sdcc3_apps_clk_src.clkr, @@ -2739,10 +3381,26 @@ static struct clk_regmap *gcc_apq8084_clocks[] = { [GCC_GP2_CLK] = &gcc_gp2_clk.clkr, [GCC_GP3_CLK] = &gcc_gp3_clk.clkr, [GCC_OCMEM_NOC_CFG_AHB_CLK] = &gcc_ocmem_noc_cfg_ahb_clk.clkr, + [GCC_PCIE_0_AUX_CLK] = &gcc_pcie_0_aux_clk.clkr, + [GCC_PCIE_0_CFG_AHB_CLK] = &gcc_pcie_0_cfg_ahb_clk.clkr, + [GCC_PCIE_0_MSTR_AXI_CLK] = &gcc_pcie_0_mstr_axi_clk.clkr, + [GCC_PCIE_0_PIPE_CLK] = &gcc_pcie_0_pipe_clk.clkr, + [GCC_PCIE_0_SLV_AXI_CLK] = &gcc_pcie_0_slv_axi_clk.clkr, + [GCC_PCIE_1_AUX_CLK] = &gcc_pcie_1_aux_clk.clkr, + [GCC_PCIE_1_CFG_AHB_CLK] = &gcc_pcie_1_cfg_ahb_clk.clkr, + [GCC_PCIE_1_MSTR_AXI_CLK] = &gcc_pcie_1_mstr_axi_clk.clkr, + [GCC_PCIE_1_PIPE_CLK] = &gcc_pcie_1_pipe_clk.clkr, + [GCC_PCIE_1_SLV_AXI_CLK] = &gcc_pcie_1_slv_axi_clk.clkr, [GCC_PDM2_CLK] = &gcc_pdm2_clk.clkr, [GCC_PDM_AHB_CLK] = &gcc_pdm_ahb_clk.clkr, [GCC_PERIPH_NOC_USB_HSIC_AHB_CLK] = &gcc_periph_noc_usb_hsic_ahb_clk.clkr, [GCC_PRNG_AHB_CLK] = &gcc_prng_ahb_clk.clkr, + [GCC_SATA_ASIC0_CLK] = &gcc_sata_asic0_clk.clkr, + [GCC_SATA_AXI_CLK] = &gcc_sata_axi_clk.clkr, + [GCC_SATA_CFG_AHB_CLK] = &gcc_sata_cfg_ahb_clk.clkr, + [GCC_SATA_PMALIVE_CLK] = &gcc_sata_pmalive_clk.clkr, + [GCC_SATA_RX_CLK] = &gcc_sata_rx_clk.clkr, + [GCC_SATA_RX_OOB_CLK] = &gcc_sata_rx_oob_clk.clkr, [GCC_SDCC1_AHB_CLK] = &gcc_sdcc1_ahb_clk.clkr, [GCC_SDCC1_APPS_CLK] = &gcc_sdcc1_apps_clk.clkr, [GCC_SDCC1_CDCCAL_FF_CLK] = &gcc_sdcc1_cdccal_ff_clk.clkr, @@ -2753,11 +3411,20 @@ static struct clk_regmap *gcc_apq8084_clocks[] = { [GCC_SDCC3_APPS_CLK] = &gcc_sdcc3_apps_clk.clkr, [GCC_SDCC4_AHB_CLK] = &gcc_sdcc4_ahb_clk.clkr, [GCC_SDCC4_APPS_CLK] = &gcc_sdcc4_apps_clk.clkr, + [GCC_SYS_NOC_UFS_AXI_CLK] = &gcc_sys_noc_ufs_axi_clk.clkr, [GCC_SYS_NOC_USB3_AXI_CLK] = &gcc_sys_noc_usb3_axi_clk.clkr, [GCC_SYS_NOC_USB3_SEC_AXI_CLK] = &gcc_sys_noc_usb3_sec_axi_clk.clkr, [GCC_TSIF_AHB_CLK] = &gcc_tsif_ahb_clk.clkr, [GCC_TSIF_INACTIVITY_TIMERS_CLK] = &gcc_tsif_inactivity_timers_clk.clkr, [GCC_TSIF_REF_CLK] = &gcc_tsif_ref_clk.clkr, + [GCC_UFS_AHB_CLK] = &gcc_ufs_ahb_clk.clkr, + [GCC_UFS_AXI_CLK] = &gcc_ufs_axi_clk.clkr, + [GCC_UFS_RX_CFG_CLK] = &gcc_ufs_rx_cfg_clk.clkr, + [GCC_UFS_RX_SYMBOL_0_CLK] = &gcc_ufs_rx_symbol_0_clk.clkr, + [GCC_UFS_RX_SYMBOL_1_CLK] = &gcc_ufs_rx_symbol_1_clk.clkr, + [GCC_UFS_TX_CFG_CLK] = &gcc_ufs_tx_cfg_clk.clkr, + [GCC_UFS_TX_SYMBOL_0_CLK] = &gcc_ufs_tx_symbol_0_clk.clkr, + [GCC_UFS_TX_SYMBOL_1_CLK] = &gcc_ufs_tx_symbol_1_clk.clkr, [GCC_USB2A_PHY_SLEEP_CLK] = &gcc_usb2a_phy_sleep_clk.clkr, [GCC_USB2B_PHY_SLEEP_CLK] = &gcc_usb2b_phy_sleep_clk.clkr, [GCC_USB30_MASTER_CLK] = &gcc_usb30_master_clk.clkr, diff --git a/include/dt-bindings/clock/qcom,gcc-apq8084.h b/include/dt-bindings/clock/qcom,gcc-apq8084.h index ce5bc0f171bd..2c0da566c46a 100644 --- a/include/dt-bindings/clock/qcom,gcc-apq8084.h +++ b/include/dt-bindings/clock/qcom,gcc-apq8084.h @@ -333,5 +333,19 @@ #define GCC_USB_HSIC_IO_CAL_SLEEP_CLK 316 #define GCC_USB_HSIC_MOCK_UTMI_CLK 317 #define GCC_USB_HSIC_SYSTEM_CLK 318 +#define PCIE_0_AUX_CLK_SRC 319 +#define PCIE_0_PIPE_CLK_SRC 320 +#define PCIE_1_AUX_CLK_SRC 321 +#define PCIE_1_PIPE_CLK_SRC 322 +#define GCC_PCIE_0_AUX_CLK 323 +#define GCC_PCIE_0_CFG_AHB_CLK 324 +#define GCC_PCIE_0_MSTR_AXI_CLK 325 +#define GCC_PCIE_0_PIPE_CLK 326 +#define GCC_PCIE_0_SLV_AXI_CLK 327 +#define GCC_PCIE_1_AUX_CLK 328 +#define GCC_PCIE_1_CFG_AHB_CLK 329 +#define GCC_PCIE_1_MSTR_AXI_CLK 330 +#define GCC_PCIE_1_PIPE_CLK 331 +#define GCC_PCIE_1_SLV_AXI_CLK 332 #endif -- cgit v1.2.3 From 06596961966bfeb128bc07f250b68fb9c5f177ef Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 8 Jul 2014 10:31:51 +0530 Subject: drm: Move DRM_ROTATE bits out of omapdrm into drm_crtc.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rotation property stuff should be standardized among all drivers. Move the bits to drm_crtc.h from omap_drv.h. Signed-off-by: Ville Syrjälä Reviewed-by: Imre Deak Acked-by: Dave Airlie Signed-off-by: Daniel Vetter --- drivers/gpu/drm/omapdrm/omap_drv.h | 7 ------- include/drm/drm_crtc.h | 8 ++++++++ 2 files changed, 8 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h index 284b80fc3c54..b08a450d1b5d 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.h +++ b/drivers/gpu/drm/omapdrm/omap_drv.h @@ -119,13 +119,6 @@ struct omap_drm_private { struct omap_drm_irq error_handler; }; -/* this should probably be in drm-core to standardize amongst drivers */ -#define DRM_ROTATE_0 0 -#define DRM_ROTATE_90 1 -#define DRM_ROTATE_180 2 -#define DRM_ROTATE_270 3 -#define DRM_REFLECT_X 4 -#define DRM_REFLECT_Y 5 #ifdef CONFIG_DEBUG_FS int omap_debugfs_init(struct drm_minor *minor); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 4ee7e26a012f..bfc7235a9c0e 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -75,6 +75,14 @@ static inline uint64_t I642U64(int64_t val) return (uint64_t)*((uint64_t *)&val); } +/* rotation property bits */ +#define DRM_ROTATE_0 0 +#define DRM_ROTATE_90 1 +#define DRM_ROTATE_180 2 +#define DRM_ROTATE_270 3 +#define DRM_REFLECT_X 4 +#define DRM_REFLECT_Y 5 + enum drm_connector_force { DRM_FORCE_UNSPECIFIED, DRM_FORCE_OFF, -- cgit v1.2.3 From 7689ffb32fa314cc6f128b433b5a285f2699cb70 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 8 Jul 2014 10:31:52 +0530 Subject: drm: Add support_bits parameter to drm_property_create_bitmask() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make drm_property_create_bitmask() a bit more generic by allowing the caller to specify which bits are in fact supported. This allows multiple callers to use the same enum list, but still create different versions of the same property with different list of supported bits. v2: Populate values[] array as non-sparse Make supported_bits 64bit Fix up omapdrm call site (Rob) Cc: dri-devel@lists.freedesktop.org Signed-off-by: Ville Syrjälä Reviewed-by: Imre Deak Reviewed-by: Sagar Kamble Acked-by: Dave Airlie Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 17 +++++++++++++---- drivers/gpu/drm/omapdrm/omap_plane.c | 5 ++++- include/drm/drm_crtc.h | 3 ++- 3 files changed, 19 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 41c7212081b8..2fbee61d632d 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -3395,19 +3395,28 @@ EXPORT_SYMBOL(drm_property_create_enum); struct drm_property *drm_property_create_bitmask(struct drm_device *dev, int flags, const char *name, const struct drm_prop_enum_list *props, - int num_values) + int num_props, + uint64_t supported_bits) { struct drm_property *property; - int i, ret; + int i, ret, index = 0; + int num_values = hweight64(supported_bits); flags |= DRM_MODE_PROP_BITMASK; property = drm_property_create(dev, flags, name, num_values); if (!property) return NULL; + for (i = 0; i < num_props; i++) { + if (!(supported_bits & (1ULL << props[i].type))) + continue; - for (i = 0; i < num_values; i++) { - ret = drm_property_add_enum(property, i, + if (WARN_ON(index >= num_values)) { + drm_property_destroy(dev, property); + return NULL; + } + + ret = drm_property_add_enum(property, index++, props[i].type, props[i].name); if (ret) { diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c index 3cf31ee59aac..aff06e7a4e5f 100644 --- a/drivers/gpu/drm/omapdrm/omap_plane.c +++ b/drivers/gpu/drm/omapdrm/omap_plane.c @@ -317,7 +317,10 @@ void omap_plane_install_properties(struct drm_plane *plane, { DRM_REFLECT_Y, "reflect-y" }, }; prop = drm_property_create_bitmask(dev, 0, "rotation", - props, ARRAY_SIZE(props)); + props, ARRAY_SIZE(props), + BIT(DRM_ROTATE_0) | BIT(DRM_ROTATE_90) | + BIT(DRM_ROTATE_180) | BIT(DRM_ROTATE_270) | + BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y)); if (prop == NULL) return; priv->rotation_prop = prop; diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index bfc7235a9c0e..cb4850a2b0c1 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1006,7 +1006,8 @@ extern struct drm_property *drm_property_create_enum(struct drm_device *dev, int struct drm_property *drm_property_create_bitmask(struct drm_device *dev, int flags, const char *name, const struct drm_prop_enum_list *props, - int num_values); + int num_props, + uint64_t supported_bits); struct drm_property *drm_property_create_range(struct drm_device *dev, int flags, const char *name, uint64_t min, uint64_t max); -- cgit v1.2.3 From c1df5f3c2d665f1a5b365d1e352e99832f188fda Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 8 Jul 2014 10:31:53 +0530 Subject: drm: Add drm_mode_create_rotation_property() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a function to create a standards compliant rotation property. Signed-off-by: Ville Syrjälä Reviewed-by: Imre Deak Acked-by: Dave Airlie Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 18 ++++++++++++++++++ include/drm/drm_crtc.h | 2 ++ 2 files changed, 20 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 2fbee61d632d..f224d4d1c0ed 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -4959,3 +4959,21 @@ void drm_mode_config_cleanup(struct drm_device *dev) drm_modeset_lock_fini(&dev->mode_config.connection_mutex); } EXPORT_SYMBOL(drm_mode_config_cleanup); + +struct drm_property *drm_mode_create_rotation_property(struct drm_device *dev, + unsigned int supported_rotations) +{ + static const struct drm_prop_enum_list props[] = { + { DRM_ROTATE_0, "rotate-0" }, + { DRM_ROTATE_90, "rotate-90" }, + { DRM_ROTATE_180, "rotate-180" }, + { DRM_ROTATE_270, "rotate-270" }, + { DRM_REFLECT_X, "reflect-x" }, + { DRM_REFLECT_Y, "reflect-y" }, + }; + + return drm_property_create_bitmask(dev, 0, "rotation", + props, ARRAY_SIZE(props), + supported_rotations); +} +EXPORT_SYMBOL(drm_mode_create_rotation_property); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index cb4850a2b0c1..f7b383bcb6b6 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1113,6 +1113,8 @@ extern int drm_format_plane_cpp(uint32_t format, int plane); extern int drm_format_horz_chroma_subsampling(uint32_t format); extern int drm_format_vert_chroma_subsampling(uint32_t format); extern const char *drm_get_format_name(uint32_t format); +extern struct drm_property *drm_mode_create_rotation_property(struct drm_device *dev, + unsigned int supported_rotations); /* Helpers */ -- cgit v1.2.3 From 07074006cd951f7a952512c57d60788ee7ea18db Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 8 Jul 2014 10:31:55 +0530 Subject: drm: Add drm_rect rotation functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add some helper functions to move drm_rects between different rotated coordinate spaces. One function does the forward transform and another does the inverse. Cc: dri-devel@lists.freedesktop.org Signed-off-by: Ville Syrjälä Reviewed-by: Imre Deak Acked-by: Dave Airlie Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_rect.c | 140 +++++++++++++++++++++++++++++++++++++++++++++ include/drm/drm_rect.h | 6 ++ 2 files changed, 146 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_rect.c b/drivers/gpu/drm/drm_rect.c index 7047ca025787..631f5afd451c 100644 --- a/drivers/gpu/drm/drm_rect.c +++ b/drivers/gpu/drm/drm_rect.c @@ -293,3 +293,143 @@ void drm_rect_debug_print(const struct drm_rect *r, bool fixed_point) DRM_DEBUG_KMS("%dx%d%+d%+d\n", w, h, r->x1, r->y1); } EXPORT_SYMBOL(drm_rect_debug_print); + +/** + * drm_rect_rotate - Rotate the rectangle + * @r: rectangle to be rotated + * @width: Width of the coordinate space + * @height: Height of the coordinate space + * @rotation: Transformation to be applied + * + * Apply @rotation to the coordinates of rectangle @r. + * + * @width and @height combined with @rotation define + * the location of the new origin. + * + * @width correcsponds to the horizontal and @height + * to the vertical axis of the untransformed coordinate + * space. + */ +void drm_rect_rotate(struct drm_rect *r, + int width, int height, + unsigned int rotation) +{ + struct drm_rect tmp; + + if (rotation & (BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y))) { + tmp = *r; + + if (rotation & BIT(DRM_REFLECT_X)) { + r->x1 = width - tmp.x2; + r->x2 = width - tmp.x1; + } + + if (rotation & BIT(DRM_REFLECT_Y)) { + r->y1 = height - tmp.y2; + r->y2 = height - tmp.y1; + } + } + + switch (rotation & 0xf) { + case BIT(DRM_ROTATE_0): + break; + case BIT(DRM_ROTATE_90): + tmp = *r; + r->x1 = tmp.y1; + r->x2 = tmp.y2; + r->y1 = width - tmp.x2; + r->y2 = width - tmp.x1; + break; + case BIT(DRM_ROTATE_180): + tmp = *r; + r->x1 = width - tmp.x2; + r->x2 = width - tmp.x1; + r->y1 = height - tmp.y2; + r->y2 = height - tmp.y1; + break; + case BIT(DRM_ROTATE_270): + tmp = *r; + r->x1 = height - tmp.y2; + r->x2 = height - tmp.y1; + r->y1 = tmp.x1; + r->y2 = tmp.x2; + break; + default: + break; + } +} +EXPORT_SYMBOL(drm_rect_rotate); + +/** + * drm_rect_rotate_inv - Inverse rotate the rectangle + * @r: rectangle to be rotated + * @width: Width of the coordinate space + * @height: Height of the coordinate space + * @rotation: Transformation whose inverse is to be applied + * + * Apply the inverse of @rotation to the coordinates + * of rectangle @r. + * + * @width and @height combined with @rotation define + * the location of the new origin. + * + * @width correcsponds to the horizontal and @height + * to the vertical axis of the original untransformed + * coordinate space, so that you never have to flip + * them when doing a rotatation and its inverse. + * That is, if you do: + * + * drm_rotate(&r, width, height, rotation); + * drm_rotate_inv(&r, width, height, rotation); + * + * you will always get back the original rectangle. + */ +void drm_rect_rotate_inv(struct drm_rect *r, + int width, int height, + unsigned int rotation) +{ + struct drm_rect tmp; + + switch (rotation & 0xf) { + case BIT(DRM_ROTATE_0): + break; + case BIT(DRM_ROTATE_90): + tmp = *r; + r->x1 = width - tmp.y2; + r->x2 = width - tmp.y1; + r->y1 = tmp.x1; + r->y2 = tmp.x2; + break; + case BIT(DRM_ROTATE_180): + tmp = *r; + r->x1 = width - tmp.x2; + r->x2 = width - tmp.x1; + r->y1 = height - tmp.y2; + r->y2 = height - tmp.y1; + break; + case BIT(DRM_ROTATE_270): + tmp = *r; + r->x1 = tmp.y1; + r->x2 = tmp.y2; + r->y1 = height - tmp.x2; + r->y2 = height - tmp.x1; + break; + default: + break; + } + + if (rotation & (BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y))) { + tmp = *r; + + if (rotation & BIT(DRM_REFLECT_X)) { + r->x1 = width - tmp.x2; + r->x2 = width - tmp.x1; + } + + if (rotation & BIT(DRM_REFLECT_Y)) { + r->y1 = height - tmp.y2; + r->y2 = height - tmp.y1; + } + } +} +EXPORT_SYMBOL(drm_rect_rotate_inv); diff --git a/include/drm/drm_rect.h b/include/drm/drm_rect.h index d1286297567b..26bb55e9e8b6 100644 --- a/include/drm/drm_rect.h +++ b/include/drm/drm_rect.h @@ -163,5 +163,11 @@ int drm_rect_calc_vscale_relaxed(struct drm_rect *src, struct drm_rect *dst, int min_vscale, int max_vscale); void drm_rect_debug_print(const struct drm_rect *r, bool fixed_point); +void drm_rect_rotate(struct drm_rect *r, + int width, int height, + unsigned int rotation); +void drm_rect_rotate_inv(struct drm_rect *r, + int width, int height, + unsigned int rotation); #endif -- cgit v1.2.3 From 3c9855f6dc1c68f7c4028f49bd8e3df7e4faae67 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 8 Jul 2014 10:31:56 +0530 Subject: drm: Add drm_rotation_simplify() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drm_rotation_simplify() can be used to eliminate unsupported rotation flags. It will check if any unsupported flags are present, and if so it will modify the rotation to an alternate form by adding 180 degrees to rotation angle, and flipping the reflect x and y bits. The hope is that this identity transform will eliminate the unsupported flags. Of course that might not result in any more supported rotation, so the caller is still responsible for checking the result afterwards. Cc: dri-devel@lists.freedesktop.org Signed-off-by: Ville Syrjälä Reviewed-by: Imre Deak Acked-by: Dave Airlie Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 30 ++++++++++++++++++++++++++++++ include/drm/drm_crtc.h | 2 ++ 2 files changed, 32 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index f224d4d1c0ed..89bab66558ef 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -4841,6 +4841,36 @@ int drm_format_vert_chroma_subsampling(uint32_t format) } EXPORT_SYMBOL(drm_format_vert_chroma_subsampling); +/** + * drm_rotation_simplify() - Try to simplify the rotation + * @rotation: Rotation to be simplified + * @supported_rotations: Supported rotations + * + * Attempt to simplify the rotation to a form that is supported. + * Eg. if the hardware supports everything except DRM_REFLECT_X + * one could call this function like this: + * + * drm_rotation_simplify(rotation, BIT(DRM_ROTATE_0) | + * BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_180) | + * BIT(DRM_ROTATE_270) | BIT(DRM_REFLECT_Y)); + * + * to eliminate the DRM_ROTATE_X flag. Depending on what kind of + * transforms the hardware supports, this function may not + * be able to produce a supported transform, so the caller should + * check the result afterwards. + */ +unsigned int drm_rotation_simplify(unsigned int rotation, + unsigned int supported_rotations) +{ + if (rotation & ~supported_rotations) { + rotation ^= BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y); + rotation = (rotation & ~0xf) | BIT((ffs(rotation & 0xf) + 1) % 4); + } + + return rotation; +} +EXPORT_SYMBOL(drm_rotation_simplify); + /** * drm_mode_config_init - initialize DRM mode_configuration structure * @dev: DRM device diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index f7b383bcb6b6..08ed55e02762 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1115,6 +1115,8 @@ extern int drm_format_vert_chroma_subsampling(uint32_t format); extern const char *drm_get_format_name(uint32_t format); extern struct drm_property *drm_mode_create_rotation_property(struct drm_device *dev, unsigned int supported_rotations); +extern unsigned int drm_rotation_simplify(unsigned int rotation, + unsigned int supported_rotations); /* Helpers */ -- cgit v1.2.3 From bc91b0f07ada5535427373a4e2050877bcc12218 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 11 Jul 2014 21:10:18 +0200 Subject: ipv6: addrconf: implement address generation modes This patch introduces a possibility for userspace to set various (so far two) modes of generating addresses. This is useful for example for NetworkManager because it can set the mode to NONE and take care of link local addresses itself. That allow it to have the interface up, monitoring carrier but still don't have any addresses on it. One more use-case by Dan Williams: WWAN devices often have their LL address provided by the firmware of the device, which sometimes refuses to respond to incorrect LL addresses when doing DHCPv6 or IPv6 ND. The kernel cannot generate the correct LL address for two reasons: 1) WWAN pseudo-ethernet interfaces often construct a fake MAC address, or read a meaningless MAC address from the firmware. Thus the EUI64 and the IPv6LL address the kernel assigns will be wrong. The real LL address is often retrieved from the firmware with AT or proprietary commands. 2) WWAN PPP interfaces receive their LL address from IPV6CP, not from kernel assignments. Only after IPV6CP has completed do we know the LL address of the PPP interface and its peer. But the kernel has already assigned an incorrect LL address to the interface. So being able to suppress the kernel LL address generation and assign the one retrieved from the firmware is less complicated and more robust. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/if_inet6.h | 1 + include/uapi/linux/if_link.h | 6 +++++ net/ipv6/addrconf.c | 56 ++++++++++++++++++++++++++++++-------------- 3 files changed, 45 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h index b4956a5fcc3f..d07b1a64b4e7 100644 --- a/include/net/if_inet6.h +++ b/include/net/if_inet6.h @@ -205,6 +205,7 @@ struct inet6_dev { struct timer_list rs_timer; __u8 rs_probes; + __u8 addr_gen_mode; unsigned long tstamp; /* ipv6InterfaceTable update timestamp */ struct rcu_head rcu; }; diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index b38534895db5..ff957604a721 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -204,11 +204,17 @@ enum { IFLA_INET6_CACHEINFO, /* time values and max reasm size */ IFLA_INET6_ICMP6STATS, /* statistics (icmpv6) */ IFLA_INET6_TOKEN, /* device token */ + IFLA_INET6_ADDR_GEN_MODE, /* implicit address generator mode */ __IFLA_INET6_MAX }; #define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1) +enum in6_addr_gen_mode { + IN6_ADDR_GEN_MODE_EUI64, + IN6_ADDR_GEN_MODE_NONE, +}; + enum { BRIDGE_MODE_UNSPEC, BRIDGE_MODE_HAIRPIN, diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 358edd2272ac..4c03c2843094 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -2730,9 +2730,25 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, const struct in6_addr } } +static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route) +{ + if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64) { + struct in6_addr addr; + + ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0); + /* addrconf_add_linklocal also adds a prefix_route and we + * only need to care about prefix routes if ipv6_generate_eui64 + * couldn't generate one. + */ + if (ipv6_generate_eui64(addr.s6_addr + 8, idev->dev) == 0) + addrconf_add_linklocal(idev, &addr); + else if (prefix_route) + addrconf_prefix_route(&addr, 64, idev->dev, 0, 0); + } +} + static void addrconf_dev_config(struct net_device *dev) { - struct in6_addr addr; struct inet6_dev *idev; ASSERT_RTNL(); @@ -2753,11 +2769,7 @@ static void addrconf_dev_config(struct net_device *dev) if (IS_ERR(idev)) return; - memset(&addr, 0, sizeof(struct in6_addr)); - addr.s6_addr32[0] = htonl(0xFE800000); - - if (ipv6_generate_eui64(addr.s6_addr + 8, dev) == 0) - addrconf_add_linklocal(idev, &addr); + addrconf_addr_gen(idev, false); } #if IS_ENABLED(CONFIG_IPV6_SIT) @@ -2779,11 +2791,7 @@ static void addrconf_sit_config(struct net_device *dev) } if (dev->priv_flags & IFF_ISATAP) { - struct in6_addr addr; - - ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0); - if (!ipv6_generate_eui64(addr.s6_addr + 8, dev)) - addrconf_add_linklocal(idev, &addr); + addrconf_addr_gen(idev, false); return; } @@ -2798,7 +2806,6 @@ static void addrconf_sit_config(struct net_device *dev) static void addrconf_gre_config(struct net_device *dev) { struct inet6_dev *idev; - struct in6_addr addr; ASSERT_RTNL(); @@ -2807,11 +2814,7 @@ static void addrconf_gre_config(struct net_device *dev) return; } - ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0); - if (!ipv6_generate_eui64(addr.s6_addr + 8, dev)) - addrconf_add_linklocal(idev, &addr); - else - addrconf_prefix_route(&addr, 64, dev, 0, 0); + addrconf_addr_gen(idev, true); } #endif @@ -4423,6 +4426,10 @@ static int inet6_fill_ifla6_attrs(struct sk_buff *skb, struct inet6_dev *idev) nla = nla_reserve(skb, IFLA_INET6_TOKEN, sizeof(struct in6_addr)); if (nla == NULL) goto nla_put_failure; + + if (nla_put_u8(skb, IFLA_INET6_ADDR_GEN_MODE, idev->addr_gen_mode)) + goto nla_put_failure; + read_lock_bh(&idev->lock); memcpy(nla_data(nla), idev->token.s6_addr, nla_len(nla)); read_unlock_bh(&idev->lock); @@ -4527,8 +4534,21 @@ static int inet6_set_link_af(struct net_device *dev, const struct nlattr *nla) if (nla_parse_nested(tb, IFLA_INET6_MAX, nla, NULL) < 0) BUG(); - if (tb[IFLA_INET6_TOKEN]) + if (tb[IFLA_INET6_TOKEN]) { err = inet6_set_iftoken(idev, nla_data(tb[IFLA_INET6_TOKEN])); + if (err) + return err; + } + + if (tb[IFLA_INET6_ADDR_GEN_MODE]) { + u8 mode = nla_get_u8(tb[IFLA_INET6_ADDR_GEN_MODE]); + + if (mode != IN6_ADDR_GEN_MODE_EUI64 && + mode != IN6_ADDR_GEN_MODE_NONE) + return -EINVAL; + idev->addr_gen_mode = mode; + err = 0; + } return err; } -- cgit v1.2.3 From 2c964a2f4191f2229566895f1a0e85f8339f5dd1 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Thu, 10 Jul 2014 21:01:22 +0200 Subject: drivers: tty: Merge alloc_tty_struct and initialize_tty_struct The two functions alloc_tty_struct and initialize_tty_struct are always called together. Merge them into alloc_tty_struct, updating its prototype and the only two callers of these functions. Signed-off-by: Rasmus Villemoes Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 19 +++++++++---------- drivers/tty/tty_io.c | 37 +++++++++++++------------------------ include/linux/tty.h | 4 +--- 3 files changed, 23 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 25c9bc783722..ac723e3c031a 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -316,7 +316,7 @@ done: * pty_common_install - set up the pty pair * @driver: the pty driver * @tty: the tty being instantiated - * @bool: legacy, true if this is BSD style + * @legacy: true if this is BSD style * * Perform the initial set up for the tty/pty pair. Called from the * tty layer when the port is first opened. @@ -331,18 +331,17 @@ static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty, int idx = tty->index; int retval = -ENOMEM; - o_tty = alloc_tty_struct(); - if (!o_tty) - goto err; ports[0] = kmalloc(sizeof **ports, GFP_KERNEL); ports[1] = kmalloc(sizeof **ports, GFP_KERNEL); if (!ports[0] || !ports[1]) - goto err_free_tty; + goto err; if (!try_module_get(driver->other->owner)) { /* This cannot in fact currently happen */ - goto err_free_tty; + goto err; } - initialize_tty_struct(o_tty, driver->other, idx); + o_tty = alloc_tty_struct(driver->other, idx); + if (!o_tty) + goto err_put_module; if (legacy) { /* We always use new tty termios data so we can do this @@ -387,12 +386,12 @@ err_free_termios: tty_free_termios(tty); err_deinit_tty: deinitialize_tty_struct(o_tty); + free_tty_struct(o_tty); +err_put_module: module_put(o_tty->driver->owner); -err_free_tty: +err: kfree(ports[0]); kfree(ports[1]); - free_tty_struct(o_tty); -err: return retval; } diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 714320b5e525..8fbad3410c75 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -156,20 +156,6 @@ static void release_tty(struct tty_struct *tty, int idx); static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty); static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty); -/** - * alloc_tty_struct - allocate a tty object - * - * Return a new empty tty structure. The data fields have not - * been initialized in any way but has been zeroed - * - * Locking: none - */ - -struct tty_struct *alloc_tty_struct(void) -{ - return kzalloc(sizeof(struct tty_struct), GFP_KERNEL); -} - /** * free_tty_struct - free a disused tty * @tty: tty struct to free @@ -1455,12 +1441,11 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx) if (!try_module_get(driver->owner)) return ERR_PTR(-ENODEV); - tty = alloc_tty_struct(); + tty = alloc_tty_struct(driver, idx); if (!tty) { retval = -ENOMEM; goto err_module_put; } - initialize_tty_struct(tty, driver, idx); tty_lock(tty); retval = tty_driver_install_tty(driver, tty); @@ -3003,19 +2988,21 @@ static struct device *tty_get_device(struct tty_struct *tty) /** - * initialize_tty_struct - * @tty: tty to initialize + * alloc_tty_struct * - * This subroutine initializes a tty structure that has been newly - * allocated. + * This subroutine allocates and initializes a tty structure. * - * Locking: none - tty in question must not be exposed at this point + * Locking: none - tty in question is not exposed at this point */ -void initialize_tty_struct(struct tty_struct *tty, - struct tty_driver *driver, int idx) +struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx) { - memset(tty, 0, sizeof(struct tty_struct)); + struct tty_struct *tty; + + tty = kzalloc(sizeof(*tty), GFP_KERNEL); + if (!tty) + return NULL; + kref_init(&tty->kref); tty->magic = TTY_MAGIC; tty_ldisc_init(tty); @@ -3039,6 +3026,8 @@ void initialize_tty_struct(struct tty_struct *tty, tty->index = idx; tty_line_name(driver, idx, tty->name); tty->dev = tty_get_device(tty); + + return tty; } /** diff --git a/include/linux/tty.h b/include/linux/tty.h index 1c3316a47d7e..84132942902a 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -477,13 +477,11 @@ extern int tty_mode_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); extern int tty_perform_flush(struct tty_struct *tty, unsigned long arg); extern void tty_default_fops(struct file_operations *fops); -extern struct tty_struct *alloc_tty_struct(void); +extern struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx); extern int tty_alloc_file(struct file *file); extern void tty_add_file(struct tty_struct *tty, struct file *file); extern void tty_free_file(struct file *file); extern void free_tty_struct(struct tty_struct *tty); -extern void initialize_tty_struct(struct tty_struct *tty, - struct tty_driver *driver, int idx); extern void deinitialize_tty_struct(struct tty_struct *tty); extern struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx); extern int tty_release(struct inode *inode, struct file *filp); -- cgit v1.2.3 From 726526c3552c5718d5aba11ac2e914b0081a5c88 Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Fri, 11 Jul 2014 14:04:20 -0700 Subject: misc: mic: add a bus driver for virtual MIC devices This MIC virtual bus driver takes the responsibility of creating all the virtual devices connected to the PCIe device on the host and the platform device on the card. The MIC bus hardware operations provide a way to abstract certain hardware details from the base physical devices. Examples of devices added on the MIC virtual bus include host DMA and card DMA. This abstraction enables using a common DMA driver on host and card. Reviewed-by: Ashutosh Dixit Reviewed-by: Nikhil Rao Signed-off-by: Sudeep Dutt Signed-off-by: Siva Yerramreddy Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/Kconfig | 17 ++++ drivers/misc/mic/Makefile | 1 + drivers/misc/mic/bus/Makefile | 5 + drivers/misc/mic/bus/mic_bus.c | 218 +++++++++++++++++++++++++++++++++++++++++ include/linux/mic_bus.h | 110 +++++++++++++++++++++ 5 files changed, 351 insertions(+) create mode 100644 drivers/misc/mic/bus/Makefile create mode 100644 drivers/misc/mic/bus/mic_bus.c create mode 100644 include/linux/mic_bus.h (limited to 'include') diff --git a/drivers/misc/mic/Kconfig b/drivers/misc/mic/Kconfig index 462a5b1d8651..ee1d2ac3cd09 100644 --- a/drivers/misc/mic/Kconfig +++ b/drivers/misc/mic/Kconfig @@ -1,3 +1,20 @@ +comment "Intel MIC Bus Driver" + +config INTEL_MIC_BUS + tristate "Intel MIC Bus Driver" + depends on 64BIT && PCI && X86 && X86_DEV_DMA_OPS + help + This option is selected by any driver which registers a + device or driver on the MIC Bus, such as CONFIG_INTEL_MIC_HOST, + CONFIG_INTEL_MIC_CARD, CONFIG_INTEL_MIC_X100_DMA etc. + + If you are building a host/card kernel with an Intel MIC device + then say M (recommended) or Y, else say N. If unsure say N. + + More information about the Intel MIC family as well as the Linux + OS and tools for MIC to use with this driver are available from + . + comment "Intel MIC Host Driver" config INTEL_MIC_HOST diff --git a/drivers/misc/mic/Makefile b/drivers/misc/mic/Makefile index 05b34d683a58..e9bf148755e2 100644 --- a/drivers/misc/mic/Makefile +++ b/drivers/misc/mic/Makefile @@ -4,3 +4,4 @@ # obj-$(CONFIG_INTEL_MIC_HOST) += host/ obj-$(CONFIG_INTEL_MIC_CARD) += card/ +obj-$(CONFIG_INTEL_MIC_BUS) += bus/ diff --git a/drivers/misc/mic/bus/Makefile b/drivers/misc/mic/bus/Makefile new file mode 100644 index 000000000000..d85c7f2a0af4 --- /dev/null +++ b/drivers/misc/mic/bus/Makefile @@ -0,0 +1,5 @@ +# +# Makefile - Intel MIC Linux driver. +# Copyright(c) 2014, Intel Corporation. +# +obj-$(CONFIG_INTEL_MIC_BUS) += mic_bus.o diff --git a/drivers/misc/mic/bus/mic_bus.c b/drivers/misc/mic/bus/mic_bus.c new file mode 100644 index 000000000000..961ae90aae47 --- /dev/null +++ b/drivers/misc/mic/bus/mic_bus.c @@ -0,0 +1,218 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2014 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel MIC Bus driver. + * + * This implementation is very similar to the the virtio bus driver + * implementation @ drivers/virtio/virtio.c + */ +#include +#include +#include +#include + +/* Unique numbering for mbus devices. */ +static DEFINE_IDA(mbus_index_ida); + +static ssize_t device_show(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct mbus_device *dev = dev_to_mbus(d); + return sprintf(buf, "0x%04x\n", dev->id.device); +} +static DEVICE_ATTR_RO(device); + +static ssize_t vendor_show(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct mbus_device *dev = dev_to_mbus(d); + return sprintf(buf, "0x%04x\n", dev->id.vendor); +} +static DEVICE_ATTR_RO(vendor); + +static ssize_t modalias_show(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct mbus_device *dev = dev_to_mbus(d); + return sprintf(buf, "mbus:d%08Xv%08X\n", + dev->id.device, dev->id.vendor); +} +static DEVICE_ATTR_RO(modalias); + +static struct attribute *mbus_dev_attrs[] = { + &dev_attr_device.attr, + &dev_attr_vendor.attr, + &dev_attr_modalias.attr, + NULL, +}; +ATTRIBUTE_GROUPS(mbus_dev); + +static inline int mbus_id_match(const struct mbus_device *dev, + const struct mbus_device_id *id) +{ + if (id->device != dev->id.device && id->device != MBUS_DEV_ANY_ID) + return 0; + + return id->vendor == MBUS_DEV_ANY_ID || id->vendor == dev->id.vendor; +} + +/* + * This looks through all the IDs a driver claims to support. If any of them + * match, we return 1 and the kernel will call mbus_dev_probe(). + */ +static int mbus_dev_match(struct device *dv, struct device_driver *dr) +{ + unsigned int i; + struct mbus_device *dev = dev_to_mbus(dv); + const struct mbus_device_id *ids; + + ids = drv_to_mbus(dr)->id_table; + for (i = 0; ids[i].device; i++) + if (mbus_id_match(dev, &ids[i])) + return 1; + return 0; +} + +static int mbus_uevent(struct device *dv, struct kobj_uevent_env *env) +{ + struct mbus_device *dev = dev_to_mbus(dv); + + return add_uevent_var(env, "MODALIAS=mbus:d%08Xv%08X", + dev->id.device, dev->id.vendor); +} + +static int mbus_dev_probe(struct device *d) +{ + int err; + struct mbus_device *dev = dev_to_mbus(d); + struct mbus_driver *drv = drv_to_mbus(dev->dev.driver); + + err = drv->probe(dev); + if (!err) + if (drv->scan) + drv->scan(dev); + return err; +} + +static int mbus_dev_remove(struct device *d) +{ + struct mbus_device *dev = dev_to_mbus(d); + struct mbus_driver *drv = drv_to_mbus(dev->dev.driver); + + drv->remove(dev); + return 0; +} + +static struct bus_type mic_bus = { + .name = "mic_bus", + .match = mbus_dev_match, + .dev_groups = mbus_dev_groups, + .uevent = mbus_uevent, + .probe = mbus_dev_probe, + .remove = mbus_dev_remove, +}; + +int mbus_register_driver(struct mbus_driver *driver) +{ + driver->driver.bus = &mic_bus; + return driver_register(&driver->driver); +} +EXPORT_SYMBOL_GPL(mbus_register_driver); + +void mbus_unregister_driver(struct mbus_driver *driver) +{ + driver_unregister(&driver->driver); +} +EXPORT_SYMBOL_GPL(mbus_unregister_driver); + +static void mbus_release_dev(struct device *d) +{ + struct mbus_device *mbdev = dev_to_mbus(d); + kfree(mbdev); +} + +struct mbus_device * +mbus_register_device(struct device *pdev, int id, struct dma_map_ops *dma_ops, + struct mbus_hw_ops *hw_ops, void __iomem *mmio_va) +{ + int ret; + struct mbus_device *mbdev; + + mbdev = kzalloc(sizeof(*mbdev), GFP_KERNEL); + if (!mbdev) + return ERR_PTR(-ENOMEM); + + mbdev->mmio_va = mmio_va; + mbdev->dev.parent = pdev; + mbdev->id.device = id; + mbdev->id.vendor = MBUS_DEV_ANY_ID; + mbdev->dev.archdata.dma_ops = dma_ops; + mbdev->dev.dma_mask = &mbdev->dev.coherent_dma_mask; + dma_set_mask(&mbdev->dev, DMA_BIT_MASK(64)); + mbdev->dev.release = mbus_release_dev; + mbdev->hw_ops = hw_ops; + mbdev->dev.bus = &mic_bus; + + /* Assign a unique device index and hence name. */ + ret = ida_simple_get(&mbus_index_ida, 0, 0, GFP_KERNEL); + if (ret < 0) + goto free_mbdev; + + mbdev->index = ret; + dev_set_name(&mbdev->dev, "mbus-dev%u", mbdev->index); + /* + * device_register() causes the bus infrastructure to look for a + * matching driver. + */ + ret = device_register(&mbdev->dev); + if (ret) + goto ida_remove; + return mbdev; +ida_remove: + ida_simple_remove(&mbus_index_ida, mbdev->index); +free_mbdev: + kfree(mbdev); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(mbus_register_device); + +void mbus_unregister_device(struct mbus_device *mbdev) +{ + int index = mbdev->index; /* save for after device release */ + + device_unregister(&mbdev->dev); + ida_simple_remove(&mbus_index_ida, index); +} +EXPORT_SYMBOL_GPL(mbus_unregister_device); + +static int __init mbus_init(void) +{ + return bus_register(&mic_bus); +} + +static void __exit mbus_exit(void) +{ + bus_unregister(&mic_bus); + ida_destroy(&mbus_index_ida); +} + +core_initcall(mbus_init); +module_exit(mbus_exit); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_DESCRIPTION("Intel(R) MIC Bus driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mic_bus.h b/include/linux/mic_bus.h new file mode 100644 index 000000000000..d5b5f76d57ef --- /dev/null +++ b/include/linux/mic_bus.h @@ -0,0 +1,110 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2014 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Intel MIC Bus driver. + * + * This implementation is very similar to the the virtio bus driver + * implementation @ include/linux/virtio.h. + */ +#ifndef _MIC_BUS_H_ +#define _MIC_BUS_H_ +/* + * Everything a mbus driver needs to work with any particular mbus + * implementation. + */ +#include +#include + +struct mbus_device_id { + __u32 device; + __u32 vendor; +}; + +#define MBUS_DEV_DMA_HOST 2 +#define MBUS_DEV_DMA_MIC 3 +#define MBUS_DEV_ANY_ID 0xffffffff + +/** + * mbus_device - representation of a device using mbus + * @mmio_va: virtual address of mmio space + * @hw_ops: the hardware ops supported by this device. + * @id: the device type identification (used to match it with a driver). + * @dev: underlying device. + * be used to communicate with. + * @index: unique position on the mbus bus + */ +struct mbus_device { + void __iomem *mmio_va; + struct mbus_hw_ops *hw_ops; + struct mbus_device_id id; + struct device dev; + int index; +}; + +/** + * mbus_driver - operations for a mbus I/O driver + * @driver: underlying device driver (populate name and owner). + * @id_table: the ids serviced by this driver. + * @probe: the function to call when a device is found. Returns 0 or -errno. + * @remove: the function to call when a device is removed. + */ +struct mbus_driver { + struct device_driver driver; + const struct mbus_device_id *id_table; + int (*probe)(struct mbus_device *dev); + void (*scan)(struct mbus_device *dev); + void (*remove)(struct mbus_device *dev); +}; + +/** + * struct mic_irq - opaque pointer used as cookie + */ +struct mic_irq; + +/** + * mbus_hw_ops - Hardware operations for accessing a MIC device on the MIC bus. + */ +struct mbus_hw_ops { + struct mic_irq* (*request_threaded_irq)(struct mbus_device *mbdev, + irq_handler_t handler, + irq_handler_t thread_fn, + const char *name, void *data, + int intr_src); + void (*free_irq)(struct mbus_device *mbdev, + struct mic_irq *cookie, void *data); + void (*ack_interrupt)(struct mbus_device *mbdev, int num); +}; + +struct mbus_device * +mbus_register_device(struct device *pdev, int id, struct dma_map_ops *dma_ops, + struct mbus_hw_ops *hw_ops, void __iomem *mmio_va); +void mbus_unregister_device(struct mbus_device *mbdev); + +int mbus_register_driver(struct mbus_driver *drv); +void mbus_unregister_driver(struct mbus_driver *drv); + +static inline struct mbus_device *dev_to_mbus(struct device *_dev) +{ + return container_of(_dev, struct mbus_device, dev); +} + +static inline struct mbus_driver *drv_to_mbus(struct device_driver *drv) +{ + return container_of(drv, struct mbus_driver, driver); +} + +#endif /* _MIC_BUS_H */ -- cgit v1.2.3 From c14deddec1fbd8c9757c53a49dbfd2dc83265f21 Mon Sep 17 00:00:00 2001 From: "grmoore@altera.com" Date: Tue, 29 Apr 2014 10:29:51 -0500 Subject: mtd: spi-nor: add support for flag status register on Micron chips Some new Micron flash chips require reading the flag status register to determine when operations have completed. Furthermore, chips with multi-die stacks of the 65nm 256Mb QSPI also require reading the status register before reading the flag status register. This patch adds support for the flag status register in the n25q512ax3 and n25q00 Micron QSPI flash chips. Signed-off-by: Graham Moore Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/spi-nor.c | 52 +++++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/spi-nor.h | 4 ++++ 2 files changed, 56 insertions(+) (limited to 'include') diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index c713c8656710..7da3a7067c35 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -47,6 +47,25 @@ static int read_sr(struct spi_nor *nor) return val; } +/* + * Read the flag status register, returning its value in the location + * Return the status register value. + * Returns negative if error occurred. + */ +static int read_fsr(struct spi_nor *nor) +{ + int ret; + u8 val; + + ret = nor->read_reg(nor, SPINOR_OP_RDFSR, &val, 1); + if (ret < 0) { + pr_err("error %d reading FSR\n", ret); + return ret; + } + + return val; +} + /* * Read configuration register, returning its value in the * location. Return the configuration register value. @@ -165,6 +184,32 @@ static int spi_nor_wait_till_ready(struct spi_nor *nor) return -ETIMEDOUT; } +static int spi_nor_wait_till_fsr_ready(struct spi_nor *nor) +{ + unsigned long deadline; + int sr; + int fsr; + + deadline = jiffies + MAX_READY_WAIT_JIFFIES; + + do { + cond_resched(); + + sr = read_sr(nor); + if (sr < 0) { + break; + } else if (!(sr & SR_WIP)) { + fsr = read_fsr(nor); + if (fsr < 0) + break; + if (fsr & FSR_READY) + return 0; + } + } while (!time_after_eq(jiffies, deadline)); + + return -ETIMEDOUT; +} + /* * Service routine to read status register until ready, or timeout occurs. * Returns non-zero if error. @@ -402,6 +447,7 @@ struct flash_info { #define SECT_4K_PMC 0x10 /* SPINOR_OP_BE_4K_PMC works uniformly */ #define SPI_NOR_DUAL_READ 0x20 /* Flash supports Dual Read */ #define SPI_NOR_QUAD_READ 0x40 /* Flash supports Quad Read */ +#define USE_FSR 0x80 /* use flag status register */ }; #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ @@ -488,6 +534,8 @@ const struct spi_device_id spi_nor_ids[] = { { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) }, { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K) }, + { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, USE_FSR) }, + { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, USE_FSR) }, /* PMC */ { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) }, @@ -965,6 +1013,10 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, else mtd->_write = spi_nor_write; + if ((info->flags & USE_FSR) && + nor->wait_till_ready == spi_nor_wait_till_ready) + nor->wait_till_ready = spi_nor_wait_till_fsr_ready; + /* prefer "small sector" erase if possible */ if (info->flags & SECT_4K) { nor->erase_opcode = SPINOR_OP_BE_4K; diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 53241842a7ab..9e6294f32ba8 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -34,6 +34,7 @@ #define SPINOR_OP_SE 0xd8 /* Sector erase (usually 64KiB) */ #define SPINOR_OP_RDID 0x9f /* Read JEDEC ID */ #define SPINOR_OP_RDCR 0x35 /* Read configuration register */ +#define SPINOR_OP_RDFSR 0x70 /* Read flag status register */ /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ #define SPINOR_OP_READ4 0x13 /* Read data bytes (low frequency) */ @@ -66,6 +67,9 @@ #define SR_QUAD_EN_MX 0x40 /* Macronix Quad I/O */ +/* Flag Status Register bits */ +#define FSR_READY 0x80 + /* Configuration Register bits. */ #define CR_QUAD_EN_SPAN 0x2 /* Spansion Quad I/O */ -- cgit v1.2.3 From f86e0add813a3cc0e338089fa6c0928f5f6dc52d Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Wed, 2 Jul 2014 07:53:17 +0900 Subject: video: fbdev: s3c-fb: remove s5pc100 related fimd and fb codes This patch removes fimd and fb codes for s5pc100 SoC. Acked-by: Jingoo Han Cc: Tomi Valkeinen Signed-off-by: Kukjin Kim --- .../devicetree/bindings/video/samsung-fimd.txt | 1 - drivers/video/fbdev/Kconfig | 2 +- drivers/video/fbdev/s3c-fb.c | 35 ---------------------- include/video/samsung_fimd.h | 2 +- 4 files changed, 2 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/video/samsung-fimd.txt b/Documentation/devicetree/bindings/video/samsung-fimd.txt index 741f4a658abe..77942607f77f 100644 --- a/Documentation/devicetree/bindings/video/samsung-fimd.txt +++ b/Documentation/devicetree/bindings/video/samsung-fimd.txt @@ -8,7 +8,6 @@ Required properties: - compatible: value should be one of the following "samsung,s3c2443-fimd"; /* for S3C24XX SoCs */ "samsung,s3c6400-fimd"; /* for S3C64XX SoCs */ - "samsung,s5pc100-fimd"; /* for S5PC100 SoC */ "samsung,s5pv210-fimd"; /* for S5PV210 SoC */ "samsung,exynos4210-fimd"; /* for Exynos4 SoCs */ "samsung,exynos5250-fimd"; /* for Exynos5 SoCs */ diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index 11506e575ad0..79708b2fbfb2 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -2019,7 +2019,7 @@ config FB_TMIO_ACCELL config FB_S3C tristate "Samsung S3C framebuffer support" depends on FB && (CPU_S3C2416 || ARCH_S3C64XX || \ - ARCH_S5PC100 || ARCH_S5PV210 || ARCH_EXYNOS) + ARCH_S5PV210 || ARCH_EXYNOS) select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT diff --git a/drivers/video/fbdev/s3c-fb.c b/drivers/video/fbdev/s3c-fb.c index a0cde41a47d5..b33abb0a433d 100644 --- a/drivers/video/fbdev/s3c-fb.c +++ b/drivers/video/fbdev/s3c-fb.c @@ -1805,38 +1805,6 @@ static struct s3c_fb_driverdata s3c_fb_data_64xx = { .win[4] = &s3c_fb_data_64xx_wins[4], }; -static struct s3c_fb_driverdata s3c_fb_data_s5pc100 = { - .variant = { - .nr_windows = 5, - .vidtcon = VIDTCON0, - .wincon = WINCON(0), - .winmap = WINxMAP(0), - .keycon = WKEYCON, - .osd = VIDOSD_BASE, - .osd_stride = 16, - .buf_start = VIDW_BUF_START(0), - .buf_size = VIDW_BUF_SIZE(0), - .buf_end = VIDW_BUF_END(0), - - .palette = { - [0] = 0x2400, - [1] = 0x2800, - [2] = 0x2c00, - [3] = 0x3000, - [4] = 0x3400, - }, - - .has_prtcon = 1, - .has_blendcon = 1, - .has_clksel = 1, - }, - .win[0] = &s3c_fb_data_s5p_wins[0], - .win[1] = &s3c_fb_data_s5p_wins[1], - .win[2] = &s3c_fb_data_s5p_wins[2], - .win[3] = &s3c_fb_data_s5p_wins[3], - .win[4] = &s3c_fb_data_s5p_wins[4], -}; - static struct s3c_fb_driverdata s3c_fb_data_s5pv210 = { .variant = { .nr_windows = 5, @@ -1974,9 +1942,6 @@ static struct platform_device_id s3c_fb_driver_ids[] = { { .name = "s3c-fb", .driver_data = (unsigned long)&s3c_fb_data_64xx, - }, { - .name = "s5pc100-fb", - .driver_data = (unsigned long)&s3c_fb_data_s5pc100, }, { .name = "s5pv210-fb", .driver_data = (unsigned long)&s3c_fb_data_s5pv210, diff --git a/include/video/samsung_fimd.h b/include/video/samsung_fimd.h index b0393209679b..8af0c6155eed 100644 --- a/include/video/samsung_fimd.h +++ b/include/video/samsung_fimd.h @@ -107,7 +107,7 @@ #define VIDCON2_ORGYCbCr (1 << 8) #define VIDCON2_YUVORDCrCb (1 << 7) -/* PRTCON (S3C6410, S5PC100) +/* PRTCON (S3C6410) * Might not be present in the S3C6410 documentation, * but tests prove it's there almost for sure; shouldn't hurt in any case. */ -- cgit v1.2.3 From 2004c726b9d9a9670b7f837190be9c8dfa7a0e9d Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Sat, 21 Jun 2014 20:52:15 -0400 Subject: auth_gss: fetch the acceptor name out of the downcall If rpc.gssd sends us an acceptor name string trailing the context token, stash it as part of the context. Signed-off-by: Jeff Layton Signed-off-by: Trond Myklebust --- include/linux/sunrpc/auth_gss.h | 1 + net/sunrpc/auth_gss/auth_gss.c | 20 +++++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/auth_gss.h b/include/linux/sunrpc/auth_gss.h index f1cfd4c85cd0..cbc6875fb9cf 100644 --- a/include/linux/sunrpc/auth_gss.h +++ b/include/linux/sunrpc/auth_gss.h @@ -71,6 +71,7 @@ struct gss_cl_ctx { spinlock_t gc_seq_lock; struct gss_ctx __rcu *gc_gss_ctx; struct xdr_netobj gc_wire_ctx; + struct xdr_netobj gc_acceptor; u32 gc_win; unsigned long gc_expiry; struct rcu_head gc_rcu; diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index b6e440baccc3..e34af68603bd 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -262,9 +262,22 @@ gss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct p = ERR_PTR(ret); goto err; } - dprintk("RPC: %s Success. gc_expiry %lu now %lu timeout %u\n", - __func__, ctx->gc_expiry, now, timeout); - return q; + + /* is there any trailing data? */ + if (q == end) { + p = q; + goto done; + } + + /* pull in acceptor name (if there is one) */ + p = simple_get_netobj(q, end, &ctx->gc_acceptor); + if (IS_ERR(p)) + goto err; +done: + dprintk("RPC: %s Success. gc_expiry %lu now %lu timeout %u acceptor %.*s\n", + __func__, ctx->gc_expiry, now, timeout, ctx->gc_acceptor.len, + ctx->gc_acceptor.data); + return p; err: dprintk("RPC: %s returns error %ld\n", __func__, -PTR_ERR(p)); return p; @@ -1225,6 +1238,7 @@ gss_do_free_ctx(struct gss_cl_ctx *ctx) gss_delete_sec_context(&ctx->gc_gss_ctx); kfree(ctx->gc_wire_ctx.data); + kfree(ctx->gc_acceptor.data); kfree(ctx); } -- cgit v1.2.3 From a0337d1ddb5a4bd609e3ff0955551cb240340340 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Sat, 21 Jun 2014 20:52:16 -0400 Subject: sunrpc: add a new "stringify_acceptor" rpc_credop ...and add an new rpc_auth function to call it when it exists. This is only applicable for AUTH_GSS mechanisms, so we only specify this for those sorts of credentials. Signed-off-by: Jeff Layton Signed-off-by: Trond Myklebust --- include/linux/sunrpc/auth.h | 2 ++ net/sunrpc/auth.c | 9 ++++++ net/sunrpc/auth_gss/auth_gss.c | 62 ++++++++++++++++++++++++++++-------------- 3 files changed, 53 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h index 790be1472792..c683b9a06913 100644 --- a/include/linux/sunrpc/auth.h +++ b/include/linux/sunrpc/auth.h @@ -140,6 +140,7 @@ struct rpc_credops { void *, __be32 *, void *); int (*crkey_timeout)(struct rpc_cred *); bool (*crkey_to_expire)(struct rpc_cred *); + char * (*crstringify_acceptor)(struct rpc_cred *); }; extern const struct rpc_authops authunix_ops; @@ -182,6 +183,7 @@ void rpcauth_clear_credcache(struct rpc_cred_cache *); int rpcauth_key_timeout_notify(struct rpc_auth *, struct rpc_cred *); bool rpcauth_cred_key_to_expire(struct rpc_cred *); +char * rpcauth_stringify_acceptor(struct rpc_cred *); static inline struct rpc_cred * get_rpccred(struct rpc_cred *cred) diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index f77366717420..1481efff6aa2 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -363,6 +363,15 @@ rpcauth_cred_key_to_expire(struct rpc_cred *cred) } EXPORT_SYMBOL_GPL(rpcauth_cred_key_to_expire); +char * +rpcauth_stringify_acceptor(struct rpc_cred *cred) +{ + if (!cred->cr_ops->crstringify_acceptor) + return NULL; + return cred->cr_ops->crstringify_acceptor(cred); +} +EXPORT_SYMBOL_GPL(rpcauth_stringify_acceptor); + /* * Destroy a list of credentials */ diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index e34af68603bd..73854314fb85 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -1346,6 +1346,26 @@ gss_cred_init(struct rpc_auth *auth, struct rpc_cred *cred) return err; } +static char * +gss_stringify_acceptor(struct rpc_cred *cred) +{ + char *string; + struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); + struct xdr_netobj *acceptor = &gss_cred->gc_ctx->gc_acceptor; + + /* no point if there's no string */ + if (!acceptor->len) + return NULL; + + string = kmalloc(acceptor->len + 1, GFP_KERNEL); + if (!string) + return string; + + memcpy(string, acceptor->data, acceptor->len); + string[acceptor->len] = '\0'; + return string; +} + /* * Returns -EACCES if GSS context is NULL or will expire within the * timeout (miliseconds) @@ -1923,29 +1943,31 @@ static const struct rpc_authops authgss_ops = { }; static const struct rpc_credops gss_credops = { - .cr_name = "AUTH_GSS", - .crdestroy = gss_destroy_cred, - .cr_init = gss_cred_init, - .crbind = rpcauth_generic_bind_cred, - .crmatch = gss_match, - .crmarshal = gss_marshal, - .crrefresh = gss_refresh, - .crvalidate = gss_validate, - .crwrap_req = gss_wrap_req, - .crunwrap_resp = gss_unwrap_resp, - .crkey_timeout = gss_key_timeout, + .cr_name = "AUTH_GSS", + .crdestroy = gss_destroy_cred, + .cr_init = gss_cred_init, + .crbind = rpcauth_generic_bind_cred, + .crmatch = gss_match, + .crmarshal = gss_marshal, + .crrefresh = gss_refresh, + .crvalidate = gss_validate, + .crwrap_req = gss_wrap_req, + .crunwrap_resp = gss_unwrap_resp, + .crkey_timeout = gss_key_timeout, + .crstringify_acceptor = gss_stringify_acceptor, }; static const struct rpc_credops gss_nullops = { - .cr_name = "AUTH_GSS", - .crdestroy = gss_destroy_nullcred, - .crbind = rpcauth_generic_bind_cred, - .crmatch = gss_match, - .crmarshal = gss_marshal, - .crrefresh = gss_refresh_null, - .crvalidate = gss_validate, - .crwrap_req = gss_wrap_req, - .crunwrap_resp = gss_unwrap_resp, + .cr_name = "AUTH_GSS", + .crdestroy = gss_destroy_nullcred, + .crbind = rpcauth_generic_bind_cred, + .crmatch = gss_match, + .crmarshal = gss_marshal, + .crrefresh = gss_refresh_null, + .crvalidate = gss_validate, + .crwrap_req = gss_wrap_req, + .crunwrap_resp = gss_unwrap_resp, + .crstringify_acceptor = gss_stringify_acceptor, }; static const struct rpc_pipe_ops gss_upcall_ops_v0 = { -- cgit v1.2.3 From f11b2a1cfbf5dd783eb55cb470509d06e20d1c78 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Sat, 21 Jun 2014 20:52:17 -0400 Subject: nfs4: copy acceptor name from context to nfs_client The current CB_COMPOUND handling code tries to compare the principal name of the request with the cl_hostname in the client. This is not guaranteed to ever work, particularly if the client happened to mount a CNAME of the server or a non-fqdn. Fix this by instead comparing the cr_principal string with the acceptor name that we get from gssd. In the event that gssd didn't send one down (i.e. it was too old), then we fall back to trying to use the cl_hostname as we do today. Signed-off-by: Jeff Layton Signed-off-by: Trond Myklebust --- fs/nfs/callback.c | 12 ++++++++++++ fs/nfs/client.c | 1 + fs/nfs/nfs4proc.c | 33 ++++++++++++++++++++++++++++++++- include/linux/nfs_fs_sb.h | 1 + include/linux/nfs_xdr.h | 1 + 5 files changed, 47 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index 073b4cf67ed9..54de482143cc 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -428,6 +428,18 @@ check_gss_callback_principal(struct nfs_client *clp, struct svc_rqst *rqstp) if (p == NULL) return 0; + /* + * Did we get the acceptor from userland during the SETCLIENID + * negotiation? + */ + if (clp->cl_acceptor) + return !strcmp(p, clp->cl_acceptor); + + /* + * Otherwise try to verify it using the cl_hostname. Note that this + * doesn't work if a non-canonical hostname was used in the devname. + */ + /* Expect a GSS_C_NT_HOSTBASED_NAME like "nfs@serverhostname" */ if (memcmp(p, "nfs@", 4) != 0) diff --git a/fs/nfs/client.c b/fs/nfs/client.c index b213ee8fb012..168aa0df2658 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -252,6 +252,7 @@ void nfs_free_client(struct nfs_client *clp) put_net(clp->cl_net); put_nfs_version(clp->cl_nfs_mod); kfree(clp->cl_hostname); + kfree(clp->cl_acceptor); kfree(clp); dprintk("<-- nfs_free_client()\n"); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 0b8490eab486..b7babb3b8a4d 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4936,6 +4936,18 @@ nfs4_init_callback_netid(const struct nfs_client *clp, char *buf, size_t len) return scnprintf(buf, len, "tcp"); } +static void nfs4_setclientid_done(struct rpc_task *task, void *calldata) +{ + struct nfs4_setclientid *sc = calldata; + + if (task->tk_status == 0) + sc->sc_cred = get_rpccred(task->tk_rqstp->rq_cred); +} + +static const struct rpc_call_ops nfs4_setclientid_ops = { + .rpc_call_done = nfs4_setclientid_done, +}; + /** * nfs4_proc_setclientid - Negotiate client ID * @clp: state data structure @@ -4962,6 +4974,14 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, .rpc_resp = res, .rpc_cred = cred, }; + struct rpc_task *task; + struct rpc_task_setup task_setup_data = { + .rpc_client = clp->cl_rpcclient, + .rpc_message = &msg, + .callback_ops = &nfs4_setclientid_ops, + .callback_data = &setclientid, + .flags = RPC_TASK_TIMEOUT, + }; int status; /* nfs_client_id4 */ @@ -4988,7 +5008,18 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, dprintk("NFS call setclientid auth=%s, '%.*s'\n", clp->cl_rpcclient->cl_auth->au_ops->au_name, setclientid.sc_name_len, setclientid.sc_name); - status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT); + task = rpc_run_task(&task_setup_data); + if (IS_ERR(task)) { + status = PTR_ERR(task); + goto out; + } + status = task->tk_status; + if (setclientid.sc_cred) { + clp->cl_acceptor = rpcauth_stringify_acceptor(setclientid.sc_cred); + put_rpccred(setclientid.sc_cred); + } + rpc_put_task(task); +out: trace_nfs4_setclientid(clp, status); dprintk("NFS reply setclientid: %d\n", status); return status; diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 1150ea41b626..922be2e050f5 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -45,6 +45,7 @@ struct nfs_client { struct sockaddr_storage cl_addr; /* server identifier */ size_t cl_addrlen; char * cl_hostname; /* hostname of server */ + char * cl_acceptor; /* GSSAPI acceptor name */ struct list_head cl_share_link; /* link in global client list */ struct list_head cl_superblocks; /* List of nfs_server structs */ diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 81cbbf313272..0040629894df 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -993,6 +993,7 @@ struct nfs4_setclientid { unsigned int sc_uaddr_len; char sc_uaddr[RPCBIND_MAXUADDRLEN + 1]; u32 sc_cb_ident; + struct rpc_cred *sc_cred; }; struct nfs4_setclientid_res { -- cgit v1.2.3 From 0da71f1bf90f259debf50aaaa547e2cc31b5cf67 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 12 Jul 2014 23:36:16 +0200 Subject: Bluetooth: Enable LE encryption events only when supported The support for LE encryption is optional. When encryption is not supported then also do not enable the encryption related events. This moves the event mask setting to the third initialization stage to ensure that the LE features are available. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 1 + net/bluetooth/hci_core.c | 11 +++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 7f754a246ca5..2fee852816ee 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -329,6 +329,7 @@ enum { #define LMP_HOST_SC 0x08 /* LE features */ +#define HCI_LE_ENCRYPTION 0x01 #define HCI_LE_CONN_PARAM_REQ_PROC 0x02 #define HCI_LE_PING 0x10 diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index b29a984f7dd4..ff150e3c7ad1 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1422,14 +1422,17 @@ static void hci_setup_event_mask(struct hci_request *req) /* Use a different default for LE-only devices */ memset(events, 0, sizeof(events)); events[0] |= 0x10; /* Disconnection Complete */ - events[0] |= 0x80; /* Encryption Change */ events[1] |= 0x08; /* Read Remote Version Information Complete */ events[1] |= 0x20; /* Command Complete */ events[1] |= 0x40; /* Command Status */ events[1] |= 0x80; /* Hardware Error */ events[2] |= 0x04; /* Number of Completed Packets */ events[3] |= 0x02; /* Data Buffer Overflow */ - events[5] |= 0x80; /* Encryption Key Refresh Complete */ + + if (hdev->le_features[0] & HCI_LE_ENCRYPTION) { + events[0] |= 0x80; /* Encryption Change */ + events[5] |= 0x80; /* Encryption Key Refresh Complete */ + } } if (lmp_inq_rssi_capable(hdev)) @@ -1482,8 +1485,6 @@ static void hci_init2_req(struct hci_request *req, unsigned long opt) if (lmp_le_capable(hdev)) le_setup(req); - hci_setup_event_mask(req); - /* AVM Berlin (31), aka "BlueFRITZ!", doesn't support the read * local supported commands HCI command. */ @@ -1611,6 +1612,8 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt) struct hci_dev *hdev = req->hdev; u8 p; + hci_setup_event_mask(req); + /* Some Broadcom based Bluetooth controllers do not support the * Delete Stored Link Key command. They are clearly indicating its * absence in the bit mask of supported commands. -- cgit v1.2.3 From 2c14736c75dba10d9da4c8337e1baee11577691c Mon Sep 17 00:00:00 2001 From: Heiko Stübner Date: Thu, 3 Jul 2014 02:01:14 +0200 Subject: clk: rockchip: add clock driver for rk3188 and rk3066 clocks This adds a clock driver that handles the specific muxes, dividers and gates of rk3188 and rk3066 SoCs. The structure of the clock list resembles the arrangement of their counterparts in the clock architecture diagrams found in the SoC documentation. Clocks exported to the clock provider are currently limited to well known or measured ones. So additional clock exports may be necessary in the future. Signed-off-by: Heiko Stuebner Acked-By: Max Schwarz Tested-By: Max Schwarz Signed-off-by: Mike Turquette --- drivers/clk/rockchip/Makefile | 2 + drivers/clk/rockchip/clk-rk3188.c | 672 ++++++++++++++++++++++++++ include/dt-bindings/clock/rk3066a-cru.h | 35 ++ include/dt-bindings/clock/rk3188-cru-common.h | 249 ++++++++++ include/dt-bindings/clock/rk3188-cru.h | 51 ++ 5 files changed, 1009 insertions(+) create mode 100644 drivers/clk/rockchip/clk-rk3188.c create mode 100644 include/dt-bindings/clock/rk3066a-cru.h create mode 100644 include/dt-bindings/clock/rk3188-cru-common.h create mode 100644 include/dt-bindings/clock/rk3188-cru.h (limited to 'include') diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile index 85f8a551fc1a..3eebf72fe91f 100644 --- a/drivers/clk/rockchip/Makefile +++ b/drivers/clk/rockchip/Makefile @@ -6,3 +6,5 @@ obj-y += clk-rockchip.o obj-y += clk.o obj-y += clk-pll.o obj-$(CONFIG_RESET_CONTROLLER) += softrst.o + +obj-y += clk-rk3188.o diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c new file mode 100644 index 000000000000..a83a6d8d0fb6 --- /dev/null +++ b/drivers/clk/rockchip/clk-rk3188.c @@ -0,0 +1,672 @@ +/* + * Copyright (c) 2014 MundoReader S.L. + * Author: Heiko Stuebner + * + * 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 "clk.h" + +#define RK3188_GRF_SOC_STATUS 0xac + +enum rk3188_plls { + apll, cpll, dpll, gpll, +}; + +struct rockchip_pll_rate_table rk3188_pll_rates[] = { + RK3066_PLL_RATE(2208000000, 1, 92, 1), + RK3066_PLL_RATE(2184000000, 1, 91, 1), + RK3066_PLL_RATE(2160000000, 1, 90, 1), + RK3066_PLL_RATE(2136000000, 1, 89, 1), + RK3066_PLL_RATE(2112000000, 1, 88, 1), + RK3066_PLL_RATE(2088000000, 1, 87, 1), + RK3066_PLL_RATE(2064000000, 1, 86, 1), + RK3066_PLL_RATE(2040000000, 1, 85, 1), + RK3066_PLL_RATE(2016000000, 1, 84, 1), + RK3066_PLL_RATE(1992000000, 1, 83, 1), + RK3066_PLL_RATE(1968000000, 1, 82, 1), + RK3066_PLL_RATE(1944000000, 1, 81, 1), + RK3066_PLL_RATE(1920000000, 1, 80, 1), + RK3066_PLL_RATE(1896000000, 1, 79, 1), + RK3066_PLL_RATE(1872000000, 1, 78, 1), + RK3066_PLL_RATE(1848000000, 1, 77, 1), + RK3066_PLL_RATE(1824000000, 1, 76, 1), + RK3066_PLL_RATE(1800000000, 1, 75, 1), + RK3066_PLL_RATE(1776000000, 1, 74, 1), + RK3066_PLL_RATE(1752000000, 1, 73, 1), + RK3066_PLL_RATE(1728000000, 1, 72, 1), + RK3066_PLL_RATE(1704000000, 1, 71, 1), + RK3066_PLL_RATE(1680000000, 1, 70, 1), + RK3066_PLL_RATE(1656000000, 1, 69, 1), + RK3066_PLL_RATE(1632000000, 1, 68, 1), + RK3066_PLL_RATE(1608000000, 1, 67, 1), + RK3066_PLL_RATE(1560000000, 1, 65, 1), + RK3066_PLL_RATE(1512000000, 1, 63, 1), + RK3066_PLL_RATE(1488000000, 1, 62, 1), + RK3066_PLL_RATE(1464000000, 1, 61, 1), + RK3066_PLL_RATE(1440000000, 1, 60, 1), + RK3066_PLL_RATE(1416000000, 1, 59, 1), + RK3066_PLL_RATE(1392000000, 1, 58, 1), + RK3066_PLL_RATE(1368000000, 1, 57, 1), + RK3066_PLL_RATE(1344000000, 1, 56, 1), + RK3066_PLL_RATE(1320000000, 1, 55, 1), + RK3066_PLL_RATE(1296000000, 1, 54, 1), + RK3066_PLL_RATE(1272000000, 1, 53, 1), + RK3066_PLL_RATE(1248000000, 1, 52, 1), + RK3066_PLL_RATE(1224000000, 1, 51, 1), + RK3066_PLL_RATE(1200000000, 1, 50, 1), + RK3066_PLL_RATE(1188000000, 2, 99, 1), + RK3066_PLL_RATE(1176000000, 1, 49, 1), + RK3066_PLL_RATE(1128000000, 1, 47, 1), + RK3066_PLL_RATE(1104000000, 1, 46, 1), + RK3066_PLL_RATE(1008000000, 1, 84, 2), + RK3066_PLL_RATE( 912000000, 1, 76, 2), + RK3066_PLL_RATE( 891000000, 8, 594, 2), + RK3066_PLL_RATE( 888000000, 1, 74, 2), + RK3066_PLL_RATE( 816000000, 1, 68, 2), + RK3066_PLL_RATE( 798000000, 2, 133, 2), + RK3066_PLL_RATE( 792000000, 1, 66, 2), + RK3066_PLL_RATE( 768000000, 1, 64, 2), + RK3066_PLL_RATE( 742500000, 8, 495, 2), + RK3066_PLL_RATE( 696000000, 1, 58, 2), + RK3066_PLL_RATE( 600000000, 1, 50, 2), + RK3066_PLL_RATE( 594000000, 2, 198, 4), + RK3066_PLL_RATE( 552000000, 1, 46, 2), + RK3066_PLL_RATE( 504000000, 1, 84, 4), + RK3066_PLL_RATE( 456000000, 1, 76, 4), + RK3066_PLL_RATE( 408000000, 1, 68, 4), + RK3066_PLL_RATE( 384000000, 2, 128, 4), + RK3066_PLL_RATE( 360000000, 1, 60, 4), + RK3066_PLL_RATE( 312000000, 1, 52, 4), + RK3066_PLL_RATE( 300000000, 1, 50, 4), + RK3066_PLL_RATE( 297000000, 2, 198, 8), + RK3066_PLL_RATE( 252000000, 1, 84, 8), + RK3066_PLL_RATE( 216000000, 1, 72, 8), + RK3066_PLL_RATE( 148500000, 2, 99, 8), + RK3066_PLL_RATE( 126000000, 1, 84, 16), + RK3066_PLL_RATE( 48000000, 1, 64, 32), + { /* sentinel */ }, +}; + +PNAME(mux_pll_p) = { "xin24m", "xin32k" }; +PNAME(mux_armclk_p) = { "apll", "gpll_armclk" }; +PNAME(mux_ddrphy_p) = { "dpll", "gpll_ddr" }; +PNAME(mux_pll_src_gpll_cpll_p) = { "gpll", "cpll" }; +PNAME(mux_pll_src_cpll_gpll_p) = { "cpll", "gpll" }; +PNAME(mux_aclk_cpu_p) = { "apll", "gpll" }; +PNAME(mux_sclk_cif0_p) = { "cif0_pre", "xin24m" }; +PNAME(mux_sclk_i2s0_p) = { "i2s0_pre", "i2s0_frac", "xin12m" }; +PNAME(mux_sclk_spdif_p) = { "spdif_src", "spdif_frac", "xin12m" }; +PNAME(mux_sclk_uart0_p) = { "uart0_pre", "uart0_frac", "xin24m" }; +PNAME(mux_sclk_uart1_p) = { "uart1_pre", "uart1_frac", "xin24m" }; +PNAME(mux_sclk_uart2_p) = { "uart2_pre", "uart2_frac", "xin24m" }; +PNAME(mux_sclk_uart3_p) = { "uart3_pre", "uart3_frac", "xin24m" }; +PNAME(mux_sclk_hsadc_p) = { "hsadc_src", "hsadc_frac", "ext_hsadc" }; +PNAME(mux_mac_p) = { "gpll", "dpll" }; +PNAME(mux_sclk_macref_p) = { "mac_src", "ext_rmii" }; + +static struct rockchip_pll_clock rk3188_pll_clks[] __initdata = { + [apll] = PLL(pll_rk3066, PLL_APLL, "apll", mux_pll_p, 0, RK2928_PLL_CON(0), + RK2928_MODE_CON, 0, 6, rk3188_pll_rates), + [dpll] = PLL(pll_rk3066, PLL_DPLL, "dpll", mux_pll_p, 0, RK2928_PLL_CON(4), + RK2928_MODE_CON, 4, 5, NULL), + [cpll] = PLL(pll_rk3066, PLL_CPLL, "cpll", mux_pll_p, 0, RK2928_PLL_CON(8), + RK2928_MODE_CON, 8, 7, rk3188_pll_rates), + [gpll] = PLL(pll_rk3066, PLL_GPLL, "gpll", mux_pll_p, 0, RK2928_PLL_CON(12), + RK2928_MODE_CON, 12, 8, rk3188_pll_rates), +}; + +#define MFLAGS CLK_MUX_HIWORD_MASK +#define DFLAGS CLK_DIVIDER_HIWORD_MASK +#define GFLAGS (CLK_GATE_HIWORD_MASK | CLK_GATE_SET_TO_DISABLE) + +/* 2 ^ (val + 1) */ +static struct clk_div_table div_core_peri_t[] = { + { .val = 0, .div = 2 }, + { .val = 1, .div = 4 }, + { .val = 2, .div = 8 }, + { .val = 3, .div = 16 }, + { /* sentinel */ }, +}; + +static struct rockchip_clk_branch common_clk_branches[] __initdata = { + /* + * Clock-Architecture Diagram 2 + */ + + GATE(0, "gpll_armclk", "gpll", 0, RK2928_CLKGATE_CON(0), 1, GFLAGS), + + /* these two are set by the cpuclk and should not be changed */ + COMPOSITE_NOMUX_DIVTBL(CORE_PERI, "core_peri", "armclk", 0, + RK2928_CLKSEL_CON(0), 6, 2, DFLAGS | CLK_DIVIDER_READ_ONLY, + div_core_peri_t, RK2928_CLKGATE_CON(0), 0, GFLAGS), + + COMPOSITE(0, "aclk_vepu", mux_pll_src_cpll_gpll_p, 0, + RK2928_CLKSEL_CON(32), 7, 1, MFLAGS, 0, 5, DFLAGS, + RK2928_CLKGATE_CON(3), 9, GFLAGS), + GATE(0, "hclk_vepu", "aclk_vepu", 0, + RK2928_CLKGATE_CON(3), 10, GFLAGS), + COMPOSITE(0, "aclk_vdpu", mux_pll_src_cpll_gpll_p, 0, + RK2928_CLKSEL_CON(32), 15, 1, MFLAGS, 8, 5, DFLAGS, + RK2928_CLKGATE_CON(3), 11, GFLAGS), + GATE(0, "hclk_vdpu", "aclk_vdpu", 0, + RK2928_CLKGATE_CON(3), 12, GFLAGS), + + GATE(0, "gpll_ddr", "gpll", 0, + RK2928_CLKGATE_CON(1), 7, GFLAGS), + COMPOSITE(0, "ddrphy", mux_ddrphy_p, 0, + RK2928_CLKSEL_CON(26), 8, 1, MFLAGS, 0, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO, + RK2928_CLKGATE_CON(0), 2, GFLAGS), + + GATE(0, "aclk_cpu", "aclk_cpu_pre", 0, + RK2928_CLKGATE_CON(0), 3, GFLAGS), + + DIV(0, "pclk_cpu_pre", "aclk_cpu_pre", 0, + RK2928_CLKSEL_CON(1), 12, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO), + GATE(0, "atclk_cpu", "pclk_cpu_pre", 0, + RK2928_CLKGATE_CON(0), 6, GFLAGS), + GATE(0, "pclk_cpu", "pclk_cpu_pre", 0, + RK2928_CLKGATE_CON(0), 5, GFLAGS), + DIV(0, "hclk_cpu_pre", "aclk_cpu_pre", 0, + RK2928_CLKSEL_CON(1), 8, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO), + COMPOSITE_NOMUX(0, "hclk_ahb2apb", "hclk_cpu_pre", 0, + RK2928_CLKSEL_CON(1), 14, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO, + RK2928_CLKGATE_CON(4), 9, GFLAGS), + GATE(0, "hclk_cpu", "hclk_cpu_pre", 0, + RK2928_CLKGATE_CON(0), 4, GFLAGS), + + COMPOSITE(0, "aclk_lcdc0_pre", mux_pll_src_cpll_gpll_p, 0, + RK2928_CLKSEL_CON(31), 7, 1, MFLAGS, 0, 5, DFLAGS, + RK2928_CLKGATE_CON(3), 0, GFLAGS), + COMPOSITE(0, "aclk_lcdc1_pre", mux_pll_src_cpll_gpll_p, 0, + RK2928_CLKSEL_CON(31), 15, 1, MFLAGS, 8, 5, DFLAGS, + RK2928_CLKGATE_CON(1), 4, GFLAGS), + + GATE(0, "aclk_peri", "aclk_peri_pre", 0, + RK2928_CLKGATE_CON(2), 1, GFLAGS), + COMPOSITE_NOMUX(0, "hclk_peri", "aclk_peri_pre", 0, + RK2928_CLKSEL_CON(10), 8, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO, + RK2928_CLKGATE_CON(2), 2, GFLAGS), + COMPOSITE_NOMUX(0, "pclk_peri", "aclk_peri_pre", 0, + RK2928_CLKSEL_CON(10), 12, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO, + RK2928_CLKGATE_CON(2), 3, GFLAGS), + + MUX(0, "cif_src", mux_pll_src_cpll_gpll_p, 0, + RK2928_CLKSEL_CON(29), 0, 1, MFLAGS), + COMPOSITE_NOMUX(0, "cif0_pre", "cif_src", 0, + RK2928_CLKSEL_CON(29), 1, 5, DFLAGS, + RK2928_CLKGATE_CON(3), 7, GFLAGS), + MUX(SCLK_CIF0, "sclk_cif0", mux_sclk_cif0_p, 0, + RK2928_CLKSEL_CON(29), 7, 1, MFLAGS), + + GATE(0, "pclkin_cif0", "ext_cif0", 0, + RK2928_CLKGATE_CON(3), 3, GFLAGS), + + /* + * the 480m are generated inside the usb block from these clocks, + * but they are also a source for the hsicphy clock. + */ + GATE(SCLK_OTGPHY0, "sclk_otgphy0", "usb480m", 0, + RK2928_CLKGATE_CON(1), 5, GFLAGS), + GATE(SCLK_OTGPHY1, "sclk_otgphy1", "usb480m", 0, + RK2928_CLKGATE_CON(1), 6, GFLAGS), + + COMPOSITE(0, "mac_src", mux_mac_p, 0, + RK2928_CLKSEL_CON(21), 0, 1, MFLAGS, 8, 5, DFLAGS, + RK2928_CLKGATE_CON(2), 5, GFLAGS), + MUX(SCLK_MAC, "sclk_macref", mux_sclk_macref_p, CLK_SET_RATE_PARENT, + RK2928_CLKSEL_CON(21), 4, 1, MFLAGS), + GATE(0, "sclk_mac_lbtest", "sclk_macref", + RK2928_CLKGATE_CON(2), 12, 0, GFLAGS), + + COMPOSITE(0, "hsadc_src", mux_pll_src_gpll_cpll_p, 0, + RK2928_CLKSEL_CON(22), 0, 1, MFLAGS, 8, 8, DFLAGS, + RK2928_CLKGATE_CON(2), 6, GFLAGS), + COMPOSITE_FRAC(0, "hsadc_frac", "hsadc_src", + RK2928_CLKSEL_CON(23), 0, + RK2928_CLKGATE_CON(2), 7, 0, GFLAGS), + MUX(SCLK_HSADC, "sclk_hsadc", mux_sclk_hsadc_p, 0, + RK2928_CLKSEL_CON(22), 4, 2, MFLAGS), + + COMPOSITE_NOMUX(SCLK_SARADC, "sclk_saradc", "xin24m", 0, + RK2928_CLKSEL_CON(24), 8, 8, DFLAGS, + RK2928_CLKGATE_CON(2), 8, GFLAGS), + + /* + * Clock-Architecture Diagram 4 + */ + + GATE(SCLK_SMC, "sclk_smc", "hclk_peri", + RK2928_CLKGATE_CON(2), 4, 0, GFLAGS), + + COMPOSITE_NOMUX(SCLK_SPI0, "sclk_spi0", "pclk_peri", 0, + RK2928_CLKSEL_CON(25), 0, 7, DFLAGS, + RK2928_CLKGATE_CON(2), 9, GFLAGS), + COMPOSITE_NOMUX(SCLK_SPI1, "sclk_spi1", "pclk_peri", 0, + RK2928_CLKSEL_CON(25), 8, 7, DFLAGS, + RK2928_CLKGATE_CON(2), 10, GFLAGS), + + COMPOSITE_NOMUX(SCLK_SDMMC, "sclk_sdmmc", "hclk_peri", 0, + RK2928_CLKSEL_CON(11), 0, 6, DFLAGS, + RK2928_CLKGATE_CON(2), 11, GFLAGS), + COMPOSITE_NOMUX(SCLK_SDIO, "sclk_sdio", "hclk_peri", 0, + RK2928_CLKSEL_CON(12), 0, 6, DFLAGS, + RK2928_CLKGATE_CON(2), 13, GFLAGS), + COMPOSITE_NOMUX(SCLK_EMMC, "sclk_emmc", "hclk_peri", 0, + RK2928_CLKSEL_CON(12), 8, 6, DFLAGS, + RK2928_CLKGATE_CON(2), 14, GFLAGS), + + MUX(0, "uart_src", mux_pll_src_gpll_cpll_p, 0, + RK2928_CLKSEL_CON(12), 15, 1, MFLAGS), + COMPOSITE_NOMUX(0, "uart0_pre", "uart_src", 0, + RK2928_CLKSEL_CON(13), 0, 7, DFLAGS, + RK2928_CLKGATE_CON(1), 8, GFLAGS), + COMPOSITE_FRAC(0, "uart0_frac", "uart0_pre", 0, + RK2928_CLKSEL_CON(17), 0, + RK2928_CLKGATE_CON(1), 9, GFLAGS), + MUX(SCLK_UART0, "sclk_uart0", mux_sclk_uart0_p, 0, + RK2928_CLKSEL_CON(13), 8, 2, MFLAGS), + COMPOSITE_NOMUX(0, "uart1_pre", "uart_src", 0, + RK2928_CLKSEL_CON(14), 0, 7, DFLAGS, + RK2928_CLKGATE_CON(1), 10, GFLAGS), + COMPOSITE_FRAC(0, "uart1_frac", "uart1_pre", 0, + RK2928_CLKSEL_CON(18), 0, + RK2928_CLKGATE_CON(1), 11, GFLAGS), + MUX(SCLK_UART1, "sclk_uart1", mux_sclk_uart1_p, 0, + RK2928_CLKSEL_CON(14), 8, 2, MFLAGS), + COMPOSITE_NOMUX(0, "uart2_pre", "uart_src", 0, + RK2928_CLKSEL_CON(15), 0, 7, DFLAGS, + RK2928_CLKGATE_CON(1), 12, GFLAGS), + COMPOSITE_FRAC(0, "uart2_frac", "uart2_pre", 0, + RK2928_CLKSEL_CON(19), 0, + RK2928_CLKGATE_CON(1), 13, GFLAGS), + MUX(SCLK_UART2, "sclk_uart2", mux_sclk_uart2_p, 0, + RK2928_CLKSEL_CON(15), 8, 2, MFLAGS), + COMPOSITE_NOMUX(0, "uart3_pre", "uart_src", 0, + RK2928_CLKSEL_CON(16), 0, 7, DFLAGS, + RK2928_CLKGATE_CON(1), 14, GFLAGS), + COMPOSITE_FRAC(0, "uart3_frac", "uart3_pre", 0, + RK2928_CLKSEL_CON(20), 0, + RK2928_CLKGATE_CON(1), 15, GFLAGS), + MUX(SCLK_UART3, "sclk_uart3", mux_sclk_uart3_p, 0, + RK2928_CLKSEL_CON(16), 8, 2, MFLAGS), + + GATE(SCLK_JTAG, "jtag", "ext_jtag", 0, RK2928_CLKGATE_CON(1), 3, GFLAGS), + + GATE(SCLK_TIMER0, "timer0", "xin24m", 0, RK2928_CLKGATE_CON(1), 0, GFLAGS), + GATE(SCLK_TIMER1, "timer1", "xin24m", 0, RK2928_CLKGATE_CON(1), 1, GFLAGS), + + /* clk_core_pre gates */ + GATE(0, "core_dbg", "armclk", 0, RK2928_CLKGATE_CON(9), 0, GFLAGS), + + /* aclk_cpu gates */ + GATE(ACLK_DMA1, "aclk_dma1", "aclk_cpu", 0, RK2928_CLKGATE_CON(5), 0, GFLAGS), + GATE(0, "aclk_intmem", "aclk_cpu", 0, RK2928_CLKGATE_CON(4), 12, GFLAGS), + GATE(0, "aclk_strc_sys", "aclk_cpu", 0, RK2928_CLKGATE_CON(4), 10, GFLAGS), + + /* hclk_cpu gates */ + GATE(HCLK_ROM, "hclk_rom", "hclk_cpu", 0, RK2928_CLKGATE_CON(5), 6, GFLAGS), + GATE(HCLK_I2S0, "hclk_i2s0", "hclk_cpu", 0, RK2928_CLKGATE_CON(7), 2, GFLAGS), + GATE(HCLK_SPDIF, "hclk_spdif", "hclk_cpu", 0, RK2928_CLKGATE_CON(7), 1, GFLAGS), + GATE(0, "hclk_cpubus", "hclk_cpu", 0, RK2928_CLKGATE_CON(4), 8, GFLAGS), + /* hclk_ahb2apb is part of a clk branch */ + GATE(0, "hclk_vio_bus", "hclk_cpu", 0, RK2928_CLKGATE_CON(6), 12, GFLAGS), + GATE(HCLK_LCDC0, "hclk_lcdc0", "hclk_cpu", 0, RK2928_CLKGATE_CON(6), 1, GFLAGS), + GATE(HCLK_LCDC1, "hclk_lcdc1", "aclk_cpu", 0, RK2928_CLKGATE_CON(6), 2, GFLAGS), + GATE(HCLK_CIF0, "hclk_cif0", "hclk_cpu", 0, RK2928_CLKGATE_CON(6), 4, GFLAGS), + GATE(HCLK_IPP, "hclk_ipp", "hclk_cpu", 0, RK2928_CLKGATE_CON(6), 9, GFLAGS), + GATE(HCLK_RGA, "hclk_rga", "hclk_cpu", 0, RK2928_CLKGATE_CON(6), 10, GFLAGS), + + /* hclk_peri gates */ + GATE(0, "hclk_peri_axi_matrix", "hclk_peri", 0, RK2928_CLKGATE_CON(4), 0, GFLAGS), + GATE(0, "hclk_peri_ahb_arbi", "hclk_peri", 0, RK2928_CLKGATE_CON(4), 6, GFLAGS), + GATE(0, "hclk_emem_peri", "hclk_peri", 0, RK2928_CLKGATE_CON(4), 7, GFLAGS), + GATE(HCLK_EMAC, "hclk_emac", "hclk_peri", 0, RK2928_CLKGATE_CON(7), 0, GFLAGS), + GATE(HCLK_NANDC0, "hclk_nandc0", "hclk_peri", 0, RK2928_CLKGATE_CON(5), 9, GFLAGS), + GATE(0, "hclk_usb_peri", "hclk_peri", 0, RK2928_CLKGATE_CON(4), 5, GFLAGS), + GATE(HCLK_OTG0, "hclk_usbotg0", "hclk_peri", 0, RK2928_CLKGATE_CON(5), 13, GFLAGS), + GATE(HCLK_HSADC, "hclk_hsadc", "hclk_peri", 0, RK2928_CLKGATE_CON(7), 5, GFLAGS), + GATE(HCLK_PIDF, "hclk_pidfilter", "hclk_peri", 0, RK2928_CLKGATE_CON(7), 6, GFLAGS), + GATE(HCLK_SDMMC, "hclk_sdmmc", "hclk_peri", 0, RK2928_CLKGATE_CON(5), 10, GFLAGS), + GATE(HCLK_SDIO, "hclk_sdio", "hclk_peri", 0, RK2928_CLKGATE_CON(5), 11, GFLAGS), + GATE(HCLK_EMMC, "hclk_emmc", "hclk_peri", 0, RK2928_CLKGATE_CON(5), 12, GFLAGS), + + /* aclk_lcdc0_pre gates */ + GATE(0, "aclk_vio0", "aclk_lcdc0_pre", 0, RK2928_CLKGATE_CON(6), 13, GFLAGS), + GATE(ACLK_LCDC0, "aclk_lcdc0", "aclk_vio0", 0, RK2928_CLKGATE_CON(6), 0, GFLAGS), + GATE(ACLK_CIF0, "aclk_cif0", "aclk_vio0", 0, RK2928_CLKGATE_CON(6), 5, GFLAGS), + GATE(ACLK_IPP, "aclk_ipp", "aclk_vio0", 0, RK2928_CLKGATE_CON(6), 8, GFLAGS), + + /* aclk_lcdc1_pre gates */ + GATE(0, "aclk_vio1", "aclk_lcdc1_pre", 0, RK2928_CLKGATE_CON(9), 5, GFLAGS), + GATE(ACLK_LCDC1, "aclk_lcdc1", "aclk_vio1", 0, RK2928_CLKGATE_CON(6), 3, GFLAGS), + GATE(ACLK_RGA, "aclk_rga", "aclk_vio1", 0, RK2928_CLKGATE_CON(6), 11, GFLAGS), + + /* atclk_cpu gates */ + GATE(0, "atclk", "atclk_cpu", 0, RK2928_CLKGATE_CON(9), 3, GFLAGS), + GATE(0, "trace", "atclk_cpu", 0, RK2928_CLKGATE_CON(9), 2, GFLAGS), + + /* pclk_cpu gates */ + GATE(PCLK_PWM01, "pclk_pwm01", "pclk_cpu", 0, RK2928_CLKGATE_CON(7), 10, GFLAGS), + GATE(PCLK_TIMER0, "pclk_timer0", "pclk_cpu", 0, RK2928_CLKGATE_CON(7), 7, GFLAGS), + GATE(PCLK_I2C0, "pclk_i2c0", "pclk_cpu", 0, RK2928_CLKGATE_CON(8), 4, GFLAGS), + GATE(PCLK_I2C1, "pclk_i2c1", "pclk_cpu", 0, RK2928_CLKGATE_CON(8), 5, GFLAGS), + GATE(PCLK_GPIO0, "pclk_gpio0", "pclk_cpu", 0, RK2928_CLKGATE_CON(8), 9, GFLAGS), + GATE(PCLK_GPIO1, "pclk_gpio1", "pclk_cpu", 0, RK2928_CLKGATE_CON(8), 10, GFLAGS), + GATE(PCLK_GPIO2, "pclk_gpio2", "pclk_cpu", 0, RK2928_CLKGATE_CON(8), 11, GFLAGS), + GATE(PCLK_EFUSE, "pclk_efuse", "pclk_cpu", 0, RK2928_CLKGATE_CON(5), 2, GFLAGS), + GATE(PCLK_TZPC, "pclk_tzpc", "pclk_cpu", 0, RK2928_CLKGATE_CON(5), 3, GFLAGS), + GATE(0, "pclk_ddrupctl", "pclk_cpu", 0, RK2928_CLKGATE_CON(5), 7, GFLAGS), + GATE(0, "pclk_ddrpubl", "pclk_cpu", 0, RK2928_CLKGATE_CON(9), 6, GFLAGS), + GATE(0, "pclk_dbg", "pclk_cpu", 0, RK2928_CLKGATE_CON(9), 1, GFLAGS), + GATE(PCLK_GRF, "pclk_grf", "pclk_cpu", 0, RK2928_CLKGATE_CON(5), 4, GFLAGS), + GATE(PCLK_PMU, "pclk_pmu", "pclk_cpu", 0, RK2928_CLKGATE_CON(5), 5, GFLAGS), + + /* aclk_peri */ + GATE(ACLK_DMA2, "aclk_dma2", "aclk_peri", 0, RK2928_CLKGATE_CON(5), 1, GFLAGS), + GATE(ACLK_SMC, "aclk_smc", "aclk_peri", 0, RK2928_CLKGATE_CON(5), 8, GFLAGS), + GATE(0, "aclk_peri_niu", "aclk_peri", 0, RK2928_CLKGATE_CON(4), 4, GFLAGS), + GATE(0, "aclk_cpu_peri", "aclk_peri", 0, RK2928_CLKGATE_CON(4), 2, GFLAGS), + GATE(0, "aclk_peri_axi_matrix", "aclk_peri", 0, RK2928_CLKGATE_CON(4), 3, GFLAGS), + + /* pclk_peri gates */ + GATE(0, "pclk_peri_axi_matrix", "pclk_peri", 0, RK2928_CLKGATE_CON(4), 1, GFLAGS), + GATE(PCLK_PWM23, "pclk_pwm23", "pclk_peri", 0, RK2928_CLKGATE_CON(7), 11, GFLAGS), + GATE(PCLK_WDT, "pclk_wdt", "pclk_peri", 0, RK2928_CLKGATE_CON(7), 15, GFLAGS), + GATE(PCLK_SPI0, "pclk_spi0", "pclk_peri", 0, RK2928_CLKGATE_CON(7), 12, GFLAGS), + GATE(PCLK_SPI1, "pclk_spi1", "pclk_peri", 0, RK2928_CLKGATE_CON(7), 13, GFLAGS), + GATE(PCLK_UART2, "pclk_uart2", "pclk_peri", 0, RK2928_CLKGATE_CON(8), 2, GFLAGS), + GATE(PCLK_UART3, "pclk_uart3", "pclk_peri", 0, RK2928_CLKGATE_CON(8), 3, GFLAGS), + GATE(PCLK_I2C2, "pclk_i2c2", "pclk_peri", 0, RK2928_CLKGATE_CON(8), 6, GFLAGS), + GATE(PCLK_I2C3, "pclk_i2c3", "pclk_peri", 0, RK2928_CLKGATE_CON(8), 7, GFLAGS), + GATE(PCLK_I2C4, "pclk_i2c4", "pclk_peri", 0, RK2928_CLKGATE_CON(8), 8, GFLAGS), + GATE(PCLK_GPIO3, "pclk_gpio3", "pclk_peri", 0, RK2928_CLKGATE_CON(8), 12, GFLAGS), + GATE(PCLK_SARADC, "pclk_saradc", "pclk_peri", 0, RK2928_CLKGATE_CON(7), 14, GFLAGS), +}; + +PNAME(mux_rk3066_lcdc0_p) = { "dclk_lcdc0_src", "xin27m" }; +PNAME(mux_rk3066_lcdc1_p) = { "dclk_lcdc1_src", "xin27m" }; +PNAME(mux_sclk_cif1_p) = { "cif1_pre", "xin24m" }; +PNAME(mux_sclk_i2s1_p) = { "i2s1_pre", "i2s1_frac", "xin12m" }; +PNAME(mux_sclk_i2s2_p) = { "i2s2_pre", "i2s2_frac", "xin12m" }; + +static struct clk_div_table div_aclk_cpu_t[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 3 }, + { .val = 3, .div = 4 }, + { .val = 4, .div = 8 }, + { /* sentinel */ }, +}; + +static struct rockchip_clk_branch rk3066a_clk_branches[] __initdata = { + COMPOSITE_NOGATE(0, "armclk", mux_armclk_p, 0, + RK2928_CLKSEL_CON(0), 8, 1, MFLAGS, 0, 5, DFLAGS), + DIVTBL(0, "aclk_cpu_pre", "armclk", 0, + RK2928_CLKSEL_CON(1), 0, 3, DFLAGS, div_aclk_cpu_t), + + GATE(CORE_L2C, "core_l2c", "aclk_cpu", 0, + RK2928_CLKGATE_CON(9), 4, GFLAGS), + + COMPOSITE(0, "aclk_peri_pre", mux_pll_src_gpll_cpll_p, 0, + RK2928_CLKSEL_CON(10), 15, 1, MFLAGS, 0, 5, DFLAGS, + RK2928_CLKGATE_CON(2), 0, GFLAGS), + + COMPOSITE(0, "dclk_lcdc0_src", mux_pll_src_cpll_gpll_p, 0, + RK2928_CLKSEL_CON(27), 0, 1, MFLAGS, 8, 8, DFLAGS, + RK2928_CLKGATE_CON(3), 1, GFLAGS), + MUX(DCLK_LCDC0, "dclk_lcdc0", mux_rk3066_lcdc0_p, 0, + RK2928_CLKSEL_CON(27), 4, 1, MFLAGS), + COMPOSITE(0, "dclk_lcdc1_src", mux_pll_src_cpll_gpll_p, 0, + RK2928_CLKSEL_CON(28), 0, 1, MFLAGS, 8, 8, DFLAGS, + RK2928_CLKGATE_CON(3), 2, GFLAGS), + MUX(DCLK_LCDC1, "dclk_lcdc1", mux_rk3066_lcdc1_p, 0, + RK2928_CLKSEL_CON(28), 4, 1, MFLAGS), + + COMPOSITE_NOMUX(0, "cif1_pre", "cif_src", 0, + RK2928_CLKSEL_CON(29), 8, 5, DFLAGS, + RK2928_CLKGATE_CON(3), 8, GFLAGS), + MUX(SCLK_CIF1, "sclk_cif1", mux_sclk_cif1_p, 0, + RK2928_CLKSEL_CON(29), 15, 1, MFLAGS), + + GATE(0, "pclkin_cif1", "ext_cif1", 0, + RK2928_CLKGATE_CON(3), 4, GFLAGS), + + COMPOSITE(0, "aclk_gpu_src", mux_pll_src_cpll_gpll_p, 0, + RK2928_CLKSEL_CON(33), 8, 1, MFLAGS, 0, 5, DFLAGS, + RK2928_CLKGATE_CON(3), 13, GFLAGS), + GATE(ACLK_GPU, "aclk_gpu", "aclk_gpu_src", 0, + RK2928_CLKGATE_CON(5), 15, GFLAGS), + + GATE(SCLK_TIMER2, "timer2", "xin24m", 0, + RK2928_CLKGATE_CON(3), 2, GFLAGS), + + COMPOSITE_NOMUX(0, "sclk_tsadc", "xin24m", 0, + RK2928_CLKSEL_CON(34), 0, 16, DFLAGS, + RK2928_CLKGATE_CON(2), 15, GFLAGS), + + MUX(0, "i2s_src", mux_pll_src_gpll_cpll_p, 0, + RK2928_CLKSEL_CON(2), 15, 1, MFLAGS), + COMPOSITE_NOMUX(0, "i2s0_pre", "i2s_src", 0, + RK2928_CLKSEL_CON(2), 0, 7, DFLAGS, + RK2928_CLKGATE_CON(0), 7, GFLAGS), + COMPOSITE_FRAC(0, "i2s0_frac", "i2s0_pre", 0, + RK2928_CLKSEL_CON(6), 0, + RK2928_CLKGATE_CON(0), 8, GFLAGS), + MUX(SCLK_I2S0, "sclk_i2s0", mux_sclk_i2s0_p, 0, + RK2928_CLKSEL_CON(2), 8, 2, MFLAGS), + COMPOSITE_NOMUX(0, "i2s1_pre", "i2s_src", 0, + RK2928_CLKSEL_CON(3), 0, 7, DFLAGS, + RK2928_CLKGATE_CON(0), 9, GFLAGS), + COMPOSITE_FRAC(0, "i2s1_frac", "i2s1_pre", 0, + RK2928_CLKSEL_CON(7), 0, + RK2928_CLKGATE_CON(0), 10, GFLAGS), + MUX(SCLK_I2S1, "sclk_i2s1", mux_sclk_i2s1_p, 0, + RK2928_CLKSEL_CON(3), 8, 2, MFLAGS), + COMPOSITE_NOMUX(0, "i2s2_pre", "i2s_src", 0, + RK2928_CLKSEL_CON(4), 0, 7, DFLAGS, + RK2928_CLKGATE_CON(0), 11, GFLAGS), + COMPOSITE_FRAC(0, "i2s2_frac", "i2s2_pre", 0, + RK2928_CLKSEL_CON(8), 0, + RK2928_CLKGATE_CON(0), 12, GFLAGS), + MUX(SCLK_I2S2, "sclk_i2s2", mux_sclk_i2s2_p, 0, + RK2928_CLKSEL_CON(4), 8, 2, MFLAGS), + COMPOSITE_NOMUX(0, "spdif_pre", "i2s_src", 0, + RK2928_CLKSEL_CON(5), 0, 7, DFLAGS, + RK2928_CLKGATE_CON(0), 13, GFLAGS), + COMPOSITE_FRAC(0, "spdif_frac", "spdif_pll", 0, + RK2928_CLKSEL_CON(9), 0, + RK2928_CLKGATE_CON(0), 14, GFLAGS), + MUX(SCLK_SPDIF, "sclk_spdif", mux_sclk_spdif_p, 0, + RK2928_CLKSEL_CON(5), 8, 2, MFLAGS), + + GATE(HCLK_I2S1, "hclk_i2s1", "hclk_cpu", 0, RK2928_CLKGATE_CON(7), 3, GFLAGS), + GATE(HCLK_I2S2, "hclk_i2s2", "hclk_cpu", 0, RK2928_CLKGATE_CON(7), 4, GFLAGS), + GATE(0, "hclk_cif1", "hclk_cpu", 0, RK2928_CLKGATE_CON(6), 6, GFLAGS), + GATE(0, "hclk_hdmi", "hclk_cpu", 0, RK2928_CLKGATE_CON(4), 14, GFLAGS), + + GATE(HCLK_OTG1, "hclk_usbotg1", "hclk_peri", 0, RK2928_CLKGATE_CON(5), 14, GFLAGS), + + GATE(0, "aclk_cif1", "aclk_vio1", 0, RK2928_CLKGATE_CON(6), 7, GFLAGS), + + GATE(PCLK_TIMER1, "pclk_timer1", "pclk_cpu", 0, RK2928_CLKGATE_CON(7), 8, GFLAGS), + GATE(PCLK_TIMER2, "pclk_timer2", "pclk_cpu", 0, RK2928_CLKGATE_CON(7), 9, GFLAGS), + GATE(PCLK_GPIO6, "pclk_gpio6", "pclk_cpu", 0, RK2928_CLKGATE_CON(8), 15, GFLAGS), + GATE(PCLK_UART0, "pclk_uart0", "pclk_cpu", 0, RK2928_CLKGATE_CON(8), 0, GFLAGS), + GATE(PCLK_UART1, "pclk_uart1", "pclk_cpu", 0, RK2928_CLKGATE_CON(8), 1, GFLAGS), + + GATE(PCLK_GPIO4, "pclk_gpio4", "pclk_peri", 0, RK2928_CLKGATE_CON(8), 13, GFLAGS), + GATE(PCLK_TSADC, "pclk_tsadc", "pclk_peri", 0, RK2928_CLKGATE_CON(4), 13, GFLAGS), +}; + +static struct clk_div_table div_rk3188_aclk_core_t[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 3 }, + { .val = 3, .div = 4 }, + { .val = 4, .div = 8 }, + { /* sentinel */ }, +}; + +PNAME(mux_hsicphy_p) = { "sclk_otgphy0", "sclk_otgphy1", + "gpll", "cpll" }; + +static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = { + COMPOSITE_NOGATE(0, "armclk", mux_armclk_p, 0, + RK2928_CLKSEL_CON(0), 8, 1, MFLAGS, 9, 5, DFLAGS), + COMPOSITE_NOMUX_DIVTBL(0, "aclk_core", "armclk", 0, + RK2928_CLKSEL_CON(1), 3, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, + div_rk3188_aclk_core_t, RK2928_CLKGATE_CON(0), 7, GFLAGS), + + /* do not source aclk_cpu_pre from the apll, to keep complexity down */ + COMPOSITE_NOGATE(0, "aclk_cpu_pre", mux_aclk_cpu_p, CLK_SET_RATE_NO_REPARENT, + RK2928_CLKSEL_CON(0), 5, 1, MFLAGS, 0, 5, DFLAGS), + + GATE(CORE_L2C, "core_l2c", "armclk", 0, + RK2928_CLKGATE_CON(9), 4, GFLAGS), + + COMPOSITE(0, "aclk_peri_pre", mux_pll_src_cpll_gpll_p, 0, + RK2928_CLKSEL_CON(10), 15, 1, MFLAGS, 0, 5, DFLAGS, + RK2928_CLKGATE_CON(2), 0, GFLAGS), + + COMPOSITE(DCLK_LCDC0, "dclk_lcdc0", mux_pll_src_cpll_gpll_p, 0, + RK2928_CLKSEL_CON(27), 0, 1, MFLAGS, 8, 8, DFLAGS, + RK2928_CLKGATE_CON(3), 1, GFLAGS), + COMPOSITE(DCLK_LCDC1, "dclk_lcdc1", mux_pll_src_cpll_gpll_p, 0, + RK2928_CLKSEL_CON(28), 0, 1, MFLAGS, 8, 8, DFLAGS, + RK2928_CLKGATE_CON(3), 2, GFLAGS), + + COMPOSITE(0, "aclk_gpu_src", mux_pll_src_cpll_gpll_p, 0, + RK2928_CLKSEL_CON(34), 7, 1, MFLAGS, 0, 5, DFLAGS, + RK2928_CLKGATE_CON(3), 15, GFLAGS), + GATE(ACLK_GPU, "aclk_gpu", "aclk_gpu_src", 0, + RK2928_CLKGATE_CON(9), 7, GFLAGS), + + GATE(SCLK_TIMER2, "timer2", "xin24m", 0, RK2928_CLKGATE_CON(3), 4, GFLAGS), + GATE(SCLK_TIMER3, "timer3", "xin24m", 0, RK2928_CLKGATE_CON(1), 2, GFLAGS), + GATE(SCLK_TIMER4, "timer4", "xin24m", 0, RK2928_CLKGATE_CON(3), 5, GFLAGS), + GATE(SCLK_TIMER5, "timer5", "xin24m", 0, RK2928_CLKGATE_CON(3), 8, GFLAGS), + GATE(SCLK_TIMER6, "timer6", "xin24m", 0, RK2928_CLKGATE_CON(3), 14, GFLAGS), + + COMPOSITE_NODIV(0, "sclk_hsicphy_480m", mux_hsicphy_p, 0, + RK2928_CLKSEL_CON(30), 0, 2, DFLAGS, + RK2928_CLKGATE_CON(3), 6, GFLAGS), + DIV(0, "sclk_hsicphy_12m", "sclk_hsicphy_480m", 0, + RK2928_CLKGATE_CON(11), 8, 6, DFLAGS), + + MUX(0, "i2s_src", mux_pll_src_gpll_cpll_p, 0, + RK2928_CLKSEL_CON(2), 15, 1, MFLAGS), + COMPOSITE_NOMUX(0, "i2s0_pre", "i2s_src", 0, + RK2928_CLKSEL_CON(3), 0, 7, DFLAGS, + RK2928_CLKGATE_CON(0), 9, GFLAGS), + COMPOSITE_FRAC(0, "i2s0_frac", "i2s0_pre", 0, + RK2928_CLKSEL_CON(7), 0, + RK2928_CLKGATE_CON(0), 10, GFLAGS), + MUX(SCLK_I2S0, "sclk_i2s0", mux_sclk_i2s0_p, 0, + RK2928_CLKSEL_CON(3), 8, 2, MFLAGS), + COMPOSITE_NOMUX(0, "spdif_pre", "i2s_src", 0, + RK2928_CLKSEL_CON(5), 0, 7, DFLAGS, + RK2928_CLKGATE_CON(13), 13, GFLAGS), + COMPOSITE_FRAC(0, "spdif_frac", "spdif_pll", 0, + RK2928_CLKSEL_CON(9), 0, + RK2928_CLKGATE_CON(0), 14, GFLAGS), + MUX(SCLK_SPDIF, "sclk_spdif", mux_sclk_spdif_p, 0, + RK2928_CLKSEL_CON(5), 8, 2, MFLAGS), + + GATE(0, "hclk_imem0", "hclk_cpu", 0, RK2928_CLKGATE_CON(4), 14, GFLAGS), + GATE(0, "hclk_imem1", "hclk_cpu", 0, RK2928_CLKGATE_CON(4), 15, GFLAGS), + + GATE(HCLK_OTG1, "hclk_usbotg1", "hclk_peri", 0, RK2928_CLKGATE_CON(7), 3, GFLAGS), + GATE(HCLK_HSIC, "hclk_hsic", "hclk_peri", 0, RK2928_CLKGATE_CON(7), 4, GFLAGS), + + GATE(PCLK_TIMER3, "pclk_timer3", "pclk_cpu", 0, RK2928_CLKGATE_CON(7), 9, GFLAGS), + + GATE(PCLK_UART0, "pclk_uart0", "hclk_ahb2apb", 0, RK2928_CLKGATE_CON(8), 0, GFLAGS), + GATE(PCLK_UART1, "pclk_uart1", "hclk_ahb2apb", 0, RK2928_CLKGATE_CON(8), 1, GFLAGS), + + GATE(ACLK_GPS, "aclk_gps", "aclk_peri", 0, RK2928_CLKGATE_CON(8), 13, GFLAGS), +}; + +static void __init rk3188_common_clk_init(struct device_node *np) +{ + void __iomem *reg_base; + struct clk *clk; + + reg_base = of_iomap(np, 0); + if (!reg_base) { + pr_err("%s: could not map cru region\n", __func__); + return; + } + + rockchip_clk_init(np, reg_base, CLK_NR_CLKS); + + /* xin12m is created by an cru-internal divider */ + clk = clk_register_fixed_factor(NULL, "xin12m", "xin24m", 0, 1, 2); + if (IS_ERR(clk)) + pr_warn("%s: could not register clock xin12m: %ld\n", + __func__, PTR_ERR(clk)); + + clk = clk_register_fixed_factor(NULL, "usb480m", "xin24m", 0, 20, 1); + if (IS_ERR(clk)) + pr_warn("%s: could not register clock usb480m: %ld\n", + __func__, PTR_ERR(clk)); + + rockchip_clk_register_plls(rk3188_pll_clks, + ARRAY_SIZE(rk3188_pll_clks), + RK3188_GRF_SOC_STATUS); + rockchip_clk_register_branches(common_clk_branches, + ARRAY_SIZE(common_clk_branches)); + + rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0), + ROCKCHIP_SOFTRST_HIWORD_MASK); +} + +static void __init rk3066a_clk_init(struct device_node *np) +{ + rk3188_common_clk_init(np); + rockchip_clk_register_branches(rk3066a_clk_branches, + ARRAY_SIZE(rk3066a_clk_branches)); +} +CLK_OF_DECLARE(rk3066a_cru, "rockchip,rk3066a-cru", rk3066a_clk_init); + +static void __init rk3188a_clk_init(struct device_node *np) +{ + rk3188_common_clk_init(np); + rockchip_clk_register_branches(rk3188_clk_branches, + ARRAY_SIZE(rk3188_clk_branches)); +} +CLK_OF_DECLARE(rk3188a_cru, "rockchip,rk3188a-cru", rk3188a_clk_init); + +static void __init rk3188_clk_init(struct device_node *np) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rk3188_pll_clks); i++) { + struct rockchip_pll_clock *pll = &rk3188_pll_clks[i]; + struct rockchip_pll_rate_table *rate; + + if (!pll->rate_table) + continue; + + rate = pll->rate_table; + while (rate->rate > 0) { + rate->bwadj = 0; + rate++; + } + } + + rk3188a_clk_init(np); +} +CLK_OF_DECLARE(rk3188_cru, "rockchip,rk3188-cru", rk3188_clk_init); diff --git a/include/dt-bindings/clock/rk3066a-cru.h b/include/dt-bindings/clock/rk3066a-cru.h new file mode 100644 index 000000000000..bc1ed1dbd855 --- /dev/null +++ b/include/dt-bindings/clock/rk3066a-cru.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2014 MundoReader S.L. + * Author: Heiko Stuebner + * + * 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 + +/* soft-reset indices */ +#define SRST_SRST1 0 +#define SRST_SRST2 1 + +#define SRST_L2MEM 18 +#define SRST_I2S0 23 +#define SRST_I2S1 24 +#define SRST_I2S2 25 +#define SRST_TIMER2 29 + +#define SRST_GPIO4 36 +#define SRST_GPIO6 38 + +#define SRST_TSADC 92 + +#define SRST_HDMI 96 +#define SRST_HDMI_APB 97 +#define SRST_CIF1 111 diff --git a/include/dt-bindings/clock/rk3188-cru-common.h b/include/dt-bindings/clock/rk3188-cru-common.h new file mode 100644 index 000000000000..750ee60e75fb --- /dev/null +++ b/include/dt-bindings/clock/rk3188-cru-common.h @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2014 MundoReader S.L. + * Author: Heiko Stuebner + * + * 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. + */ + +/* core clocks from */ +#define PLL_APLL 1 +#define PLL_DPLL 2 +#define PLL_CPLL 3 +#define PLL_GPLL 4 +#define CORE_PERI 5 +#define CORE_L2C 6 + +/* sclk gates (special clocks) */ +#define SCLK_UART0 64 +#define SCLK_UART1 65 +#define SCLK_UART2 66 +#define SCLK_UART3 67 +#define SCLK_MAC 68 +#define SCLK_SPI0 69 +#define SCLK_SPI1 70 +#define SCLK_SARADC 71 +#define SCLK_SDMMC 72 +#define SCLK_SDIO 73 +#define SCLK_EMMC 74 +#define SCLK_I2S0 75 +#define SCLK_I2S1 76 +#define SCLK_I2S2 77 +#define SCLK_SPDIF 78 +#define SCLK_CIF0 79 +#define SCLK_CIF1 80 +#define SCLK_OTGPHY0 81 +#define SCLK_OTGPHY1 82 +#define SCLK_HSADC 83 +#define SCLK_TIMER0 84 +#define SCLK_TIMER1 85 +#define SCLK_TIMER2 86 +#define SCLK_TIMER3 87 +#define SCLK_TIMER4 88 +#define SCLK_TIMER5 89 +#define SCLK_TIMER6 90 +#define SCLK_JTAG 91 +#define SCLK_SMC 92 + +#define DCLK_LCDC0 190 +#define DCLK_LCDC1 191 + +/* aclk gates */ +#define ACLK_DMA1 192 +#define ACLK_DMA2 193 +#define ACLK_GPS 194 +#define ACLK_LCDC0 195 +#define ACLK_LCDC1 196 +#define ACLK_GPU 197 +#define ACLK_SMC 198 +#define ACLK_CIF 199 +#define ACLK_IPP 200 +#define ACLK_RGA 201 +#define ACLK_CIF0 202 + +/* pclk gates */ +#define PCLK_GRF 320 +#define PCLK_PMU 321 +#define PCLK_TIMER0 322 +#define PCLK_TIMER1 323 +#define PCLK_TIMER2 324 +#define PCLK_TIMER3 325 +#define PCLK_PWM01 326 +#define PCLK_PWM23 327 +#define PCLK_SPI0 328 +#define PCLK_SPI1 329 +#define PCLK_SARADC 330 +#define PCLK_WDT 331 +#define PCLK_UART0 332 +#define PCLK_UART1 333 +#define PCLK_UART2 334 +#define PCLK_UART3 335 +#define PCLK_I2C0 336 +#define PCLK_I2C1 337 +#define PCLK_I2C2 338 +#define PCLK_I2C3 339 +#define PCLK_I2C4 340 +#define PCLK_GPIO0 341 +#define PCLK_GPIO1 342 +#define PCLK_GPIO2 343 +#define PCLK_GPIO3 344 +#define PCLK_GPIO4 345 +#define PCLK_GPIO6 346 +#define PCLK_EFUSE 347 +#define PCLK_TZPC 348 +#define PCLK_TSADC 349 + +/* hclk gates */ +#define HCLK_SDMMC 448 +#define HCLK_SDIO 449 +#define HCLK_EMMC 450 +#define HCLK_OTG0 451 +#define HCLK_EMAC 452 +#define HCLK_SPDIF 453 +#define HCLK_I2S0 454 +#define HCLK_I2S1 455 +#define HCLK_I2S2 456 +#define HCLK_OTG1 457 +#define HCLK_HSIC 458 +#define HCLK_HSADC 459 +#define HCLK_PIDF 460 +#define HCLK_LCDC0 461 +#define HCLK_LCDC1 462 +#define HCLK_ROM 463 +#define HCLK_CIF0 464 +#define HCLK_IPP 465 +#define HCLK_RGA 466 +#define HCLK_NANDC0 467 + +#define CLK_NR_CLKS (HCLK_NANDC0 + 1) + +/* soft-reset indices */ +#define SRST_MCORE 2 +#define SRST_CORE0 3 +#define SRST_CORE1 4 +#define SRST_MCORE_DBG 7 +#define SRST_CORE0_DBG 8 +#define SRST_CORE1_DBG 9 +#define SRST_CORE0_WDT 12 +#define SRST_CORE1_WDT 13 +#define SRST_STRC_SYS 14 +#define SRST_L2C 15 + +#define SRST_CPU_AHB 17 +#define SRST_AHB2APB 19 +#define SRST_DMA1 20 +#define SRST_INTMEM 21 +#define SRST_ROM 22 +#define SRST_SPDIF 26 +#define SRST_TIMER0 27 +#define SRST_TIMER1 28 +#define SRST_EFUSE 30 + +#define SRST_GPIO0 32 +#define SRST_GPIO1 33 +#define SRST_GPIO2 34 +#define SRST_GPIO3 35 + +#define SRST_UART0 39 +#define SRST_UART1 40 +#define SRST_UART2 41 +#define SRST_UART3 42 +#define SRST_I2C0 43 +#define SRST_I2C1 44 +#define SRST_I2C2 45 +#define SRST_I2C3 46 +#define SRST_I2C4 47 + +#define SRST_PWM0 48 +#define SRST_PWM1 49 +#define SRST_DAP_PO 50 +#define SRST_DAP 51 +#define SRST_DAP_SYS 52 +#define SRST_TPIU_ATB 53 +#define SRST_PMU_APB 54 +#define SRST_GRF 55 +#define SRST_PMU 56 +#define SRST_PERI_AXI 57 +#define SRST_PERI_AHB 58 +#define SRST_PERI_APB 59 +#define SRST_PERI_NIU 60 +#define SRST_CPU_PERI 61 +#define SRST_EMEM_PERI 62 +#define SRST_USB_PERI 63 + +#define SRST_DMA2 64 +#define SRST_SMC 65 +#define SRST_MAC 66 +#define SRST_NANC0 68 +#define SRST_USBOTG0 69 +#define SRST_USBPHY0 70 +#define SRST_OTGC0 71 +#define SRST_USBOTG1 72 +#define SRST_USBPHY1 73 +#define SRST_OTGC1 74 +#define SRST_HSADC 76 +#define SRST_PIDFILTER 77 +#define SRST_DDR_MSCH 79 + +#define SRST_TZPC 80 +#define SRST_SDMMC 81 +#define SRST_SDIO 82 +#define SRST_EMMC 83 +#define SRST_SPI0 84 +#define SRST_SPI1 85 +#define SRST_WDT 86 +#define SRST_SARADC 87 +#define SRST_DDRPHY 88 +#define SRST_DDRPHY_APB 89 +#define SRST_DDRCTL 90 +#define SRST_DDRCTL_APB 91 +#define SRST_DDRPUB 93 + +#define SRST_VIO0_AXI 98 +#define SRST_VIO0_AHB 99 +#define SRST_LCDC0_AXI 100 +#define SRST_LCDC0_AHB 101 +#define SRST_LCDC0_DCLK 102 +#define SRST_LCDC1_AXI 103 +#define SRST_LCDC1_AHB 104 +#define SRST_LCDC1_DCLK 105 +#define SRST_IPP_AXI 106 +#define SRST_IPP_AHB 107 +#define SRST_RGA_AXI 108 +#define SRST_RGA_AHB 109 +#define SRST_CIF0 110 + +#define SRST_VCODEC_AXI 112 +#define SRST_VCODEC_AHB 113 +#define SRST_VIO1_AXI 114 +#define SRST_VCODEC_CPU 115 +#define SRST_VCODEC_NIU 116 +#define SRST_GPU 120 +#define SRST_GPU_NIU 122 +#define SRST_TFUN_ATB 125 +#define SRST_TFUN_APB 126 +#define SRST_CTI4_APB 127 + +#define SRST_TPIU_APB 128 +#define SRST_TRACE 129 +#define SRST_CORE_DBG 130 +#define SRST_DBG_APB 131 +#define SRST_CTI0 132 +#define SRST_CTI0_APB 133 +#define SRST_CTI1 134 +#define SRST_CTI1_APB 135 +#define SRST_PTM_CORE0 136 +#define SRST_PTM_CORE1 137 +#define SRST_PTM0 138 +#define SRST_PTM0_ATB 139 +#define SRST_PTM1 140 +#define SRST_PTM1_ATB 141 +#define SRST_CTM 142 +#define SRST_TS 143 diff --git a/include/dt-bindings/clock/rk3188-cru.h b/include/dt-bindings/clock/rk3188-cru.h new file mode 100644 index 000000000000..9fac8edd3f9d --- /dev/null +++ b/include/dt-bindings/clock/rk3188-cru.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2014 MundoReader S.L. + * Author: Heiko Stuebner + * + * 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 + +/* soft-reset indices */ +#define SRST_PTM_CORE2 0 +#define SRST_PTM_CORE3 1 +#define SRST_CORE2 5 +#define SRST_CORE3 6 +#define SRST_CORE2_DBG 10 +#define SRST_CORE3_DBG 11 + +#define SRST_TIMER2 16 +#define SRST_TIMER4 23 +#define SRST_I2S0 24 +#define SRST_TIMER5 25 +#define SRST_TIMER3 29 +#define SRST_TIMER6 31 + +#define SRST_PTM3 36 +#define SRST_PTM3_ATB 37 + +#define SRST_GPS 67 +#define SRST_HSICPHY 75 +#define SRST_TIMER 78 + +#define SRST_PTM2 92 +#define SRST_CORE2_WDT 94 +#define SRST_CORE3_WDT 95 + +#define SRST_PTM2_ATB 111 + +#define SRST_HSIC 117 +#define SRST_CTI2 118 +#define SRST_CTI2_APB 119 +#define SRST_GPU_BRIDGE 121 +#define SRST_CTI3 123 +#define SRST_CTI3_APB 124 -- cgit v1.2.3 From b9e4ba541607e6ee9c6c8493f51198fe3af03aec Mon Sep 17 00:00:00 2001 From: Heiko Stübner Date: Thu, 3 Jul 2014 02:02:37 +0200 Subject: clk: rockchip: add clock controller for rk3288 Add the clock tree definition for the new rk3288 SoC. Signed-off-by: Heiko Stuebner Acked-By: Max Schwarz Tested-By: Max Schwarz Signed-off-by: Mike Turquette --- drivers/clk/rockchip/Makefile | 1 + drivers/clk/rockchip/clk-rk3288.c | 717 +++++++++++++++++++++++++++++++++ drivers/clk/rockchip/clk.h | 9 + include/dt-bindings/clock/rk3288-cru.h | 278 +++++++++++++ 4 files changed, 1005 insertions(+) create mode 100644 drivers/clk/rockchip/clk-rk3288.c create mode 100644 include/dt-bindings/clock/rk3288-cru.h (limited to 'include') diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile index 3eebf72fe91f..ee6b077381e1 100644 --- a/drivers/clk/rockchip/Makefile +++ b/drivers/clk/rockchip/Makefile @@ -8,3 +8,4 @@ obj-y += clk-pll.o obj-$(CONFIG_RESET_CONTROLLER) += softrst.o obj-y += clk-rk3188.o +obj-y += clk-rk3288.o diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c new file mode 100644 index 000000000000..0d8c6c59a75e --- /dev/null +++ b/drivers/clk/rockchip/clk-rk3288.c @@ -0,0 +1,717 @@ +/* + * Copyright (c) 2014 MundoReader S.L. + * Author: Heiko Stuebner + * + * 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 "clk.h" + +#define RK3288_GRF_SOC_CON(x) (0x244 + x * 4) +#define RK3288_GRF_SOC_STATUS 0x280 + +enum rk3288_plls { + apll, dpll, cpll, gpll, npll, +}; + +struct rockchip_pll_rate_table rk3288_pll_rates[] = { + RK3066_PLL_RATE(2208000000, 1, 92, 1), + RK3066_PLL_RATE(2184000000, 1, 91, 1), + RK3066_PLL_RATE(2160000000, 1, 90, 1), + RK3066_PLL_RATE(2136000000, 1, 89, 1), + RK3066_PLL_RATE(2112000000, 1, 88, 1), + RK3066_PLL_RATE(2088000000, 1, 87, 1), + RK3066_PLL_RATE(2064000000, 1, 86, 1), + RK3066_PLL_RATE(2040000000, 1, 85, 1), + RK3066_PLL_RATE(2016000000, 1, 84, 1), + RK3066_PLL_RATE(1992000000, 1, 83, 1), + RK3066_PLL_RATE(1968000000, 1, 82, 1), + RK3066_PLL_RATE(1944000000, 1, 81, 1), + RK3066_PLL_RATE(1920000000, 1, 80, 1), + RK3066_PLL_RATE(1896000000, 1, 79, 1), + RK3066_PLL_RATE(1872000000, 1, 78, 1), + RK3066_PLL_RATE(1848000000, 1, 77, 1), + RK3066_PLL_RATE(1824000000, 1, 76, 1), + RK3066_PLL_RATE(1800000000, 1, 75, 1), + RK3066_PLL_RATE(1776000000, 1, 74, 1), + RK3066_PLL_RATE(1752000000, 1, 73, 1), + RK3066_PLL_RATE(1728000000, 1, 72, 1), + RK3066_PLL_RATE(1704000000, 1, 71, 1), + RK3066_PLL_RATE(1680000000, 1, 70, 1), + RK3066_PLL_RATE(1656000000, 1, 69, 1), + RK3066_PLL_RATE(1632000000, 1, 68, 1), + RK3066_PLL_RATE(1608000000, 1, 67, 1), + RK3066_PLL_RATE(1560000000, 1, 65, 1), + RK3066_PLL_RATE(1512000000, 1, 63, 1), + RK3066_PLL_RATE(1488000000, 1, 62, 1), + RK3066_PLL_RATE(1464000000, 1, 61, 1), + RK3066_PLL_RATE(1440000000, 1, 60, 1), + RK3066_PLL_RATE(1416000000, 1, 59, 1), + RK3066_PLL_RATE(1392000000, 1, 58, 1), + RK3066_PLL_RATE(1368000000, 1, 57, 1), + RK3066_PLL_RATE(1344000000, 1, 56, 1), + RK3066_PLL_RATE(1320000000, 1, 55, 1), + RK3066_PLL_RATE(1296000000, 1, 54, 1), + RK3066_PLL_RATE(1272000000, 1, 53, 1), + RK3066_PLL_RATE(1248000000, 1, 52, 1), + RK3066_PLL_RATE(1224000000, 1, 51, 1), + RK3066_PLL_RATE(1200000000, 1, 50, 1), + RK3066_PLL_RATE(1188000000, 2, 99, 1), + RK3066_PLL_RATE(1176000000, 1, 49, 1), + RK3066_PLL_RATE(1128000000, 1, 47, 1), + RK3066_PLL_RATE(1104000000, 1, 46, 1), + RK3066_PLL_RATE(1008000000, 1, 84, 2), + RK3066_PLL_RATE( 912000000, 1, 76, 2), + RK3066_PLL_RATE( 891000000, 8, 594, 2), + RK3066_PLL_RATE( 888000000, 1, 74, 2), + RK3066_PLL_RATE( 816000000, 1, 68, 2), + RK3066_PLL_RATE( 798000000, 2, 133, 2), + RK3066_PLL_RATE( 792000000, 1, 66, 2), + RK3066_PLL_RATE( 768000000, 1, 64, 2), + RK3066_PLL_RATE( 742500000, 8, 495, 2), + RK3066_PLL_RATE( 696000000, 1, 58, 2), + RK3066_PLL_RATE( 600000000, 1, 50, 2), + RK3066_PLL_RATE( 594000000, 2, 198, 4), + RK3066_PLL_RATE( 552000000, 1, 46, 2), + RK3066_PLL_RATE( 504000000, 1, 84, 4), + RK3066_PLL_RATE( 456000000, 1, 76, 4), + RK3066_PLL_RATE( 408000000, 1, 68, 4), + RK3066_PLL_RATE( 384000000, 2, 128, 4), + RK3066_PLL_RATE( 360000000, 1, 60, 4), + RK3066_PLL_RATE( 312000000, 1, 52, 4), + RK3066_PLL_RATE( 300000000, 1, 50, 4), + RK3066_PLL_RATE( 297000000, 2, 198, 8), + RK3066_PLL_RATE( 252000000, 1, 84, 8), + RK3066_PLL_RATE( 216000000, 1, 72, 8), + RK3066_PLL_RATE( 148500000, 2, 99, 8), + RK3066_PLL_RATE( 126000000, 1, 84, 16), + RK3066_PLL_RATE( 48000000, 1, 64, 32), + { /* sentinel */ }, +}; + +PNAME(mux_pll_p) = { "xin24m", "xin32k" }; +PNAME(mux_armclk_p) = { "apll_core", "gpll_core" }; +PNAME(mux_ddrphy_p) = { "dpll_ddr", "gpll_ddr" }; +PNAME(mux_aclk_cpu_src_p) = { "cpll_aclk_cpu", "gpll_aclk_cpu" }; + +PNAME(mux_pll_src_cpll_gpll_p) = { "cpll", "gpll" }; +PNAME(mux_pll_src_npll_cpll_gpll_p) = { "npll", "cpll", "gpll" }; +PNAME(mux_pll_src_cpll_gpll_npll_p) = { "cpll", "gpll", "npll" }; +PNAME(mux_pll_src_cpll_gpll_usb480m_p) = { "cpll", "gpll", "usb480m" }; + +PNAME(mux_mmc_src_p) = { "cpll", "gpll", "xin24m", "xin24m" }; +PNAME(mux_i2s_pre_p) = { "i2s_src", "i2s_frac", "ext_i2s", "xin12m" }; +PNAME(mux_i2s_clkout_p) = { "i2s_pre", "xin12m" }; +PNAME(mux_spdif_p) = { "spdif_pre", "spdif_frac", "xin12m" }; +PNAME(mux_spdif_8ch_p) = { "spdif_8ch_pre", "spdif_8ch_frac", "xin12m" }; +PNAME(mux_uart0_pll_p) = { "cpll", "gpll", "usbphy_480m_src", "npll" }; +PNAME(mux_uart0_p) = { "uart0_src", "uart0_frac", "xin24m" }; +PNAME(mux_uart1_p) = { "uart1_src", "uart1_frac", "xin24m" }; +PNAME(mux_uart2_p) = { "uart2_src", "uart2_frac", "xin24m" }; +PNAME(mux_uart3_p) = { "uart3_src", "uart3_frac", "xin24m" }; +PNAME(mux_uart4_p) = { "uart4_src", "uart4_frac", "xin24m" }; +PNAME(mux_cif_out_p) = { "cif_src", "xin24m" }; +PNAME(mux_macref_p) = { "mac_src", "ext_gmac" }; +PNAME(mux_hsadcout_p) = { "hsadc_src", "ext_hsadc" }; +PNAME(mux_edp_24m_p) = { "ext_edp_24m", "xin24m" }; +PNAME(mux_tspout_p) = { "cpll", "gpll", "npll", "xin27m" }; + +PNAME(mux_usbphy480m_p) = { "sclk_otgphy0", "sclk_otgphy1", + "sclk_otgphy2" }; +PNAME(mux_hsicphy480m_p) = { "cpll", "gpll", "usbphy480m_src" }; +PNAME(mux_hsicphy12m_p) = { "hsicphy12m_xin12m", "hsicphy12m_usbphy" }; + +static struct rockchip_pll_clock rk3288_pll_clks[] __initdata = { + [apll] = PLL(pll_rk3066, PLL_APLL, "apll", mux_pll_p, 0, RK3288_PLL_CON(0), + RK3288_MODE_CON, 0, 6, rk3288_pll_rates), + [dpll] = PLL(pll_rk3066, PLL_DPLL, "dpll", mux_pll_p, 0, RK3288_PLL_CON(4), + RK3288_MODE_CON, 4, 5, NULL), + [cpll] = PLL(pll_rk3066, PLL_CPLL, "cpll", mux_pll_p, 0, RK3288_PLL_CON(8), + RK3288_MODE_CON, 8, 7, rk3288_pll_rates), + [gpll] = PLL(pll_rk3066, PLL_GPLL, "gpll", mux_pll_p, 0, RK3288_PLL_CON(12), + RK3288_MODE_CON, 12, 8, rk3288_pll_rates), + [npll] = PLL(pll_rk3066, PLL_NPLL, "npll", mux_pll_p, 0, RK3288_PLL_CON(16), + RK3288_MODE_CON, 14, 9, NULL), +}; + +static struct clk_div_table div_hclk_cpu_t[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 2 }, + { .val = 3, .div = 4 }, + { /* sentinel */}, +}; + +#define MFLAGS CLK_MUX_HIWORD_MASK +#define DFLAGS CLK_DIVIDER_HIWORD_MASK +#define GFLAGS (CLK_GATE_HIWORD_MASK | CLK_GATE_SET_TO_DISABLE) + +static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { + /* + * Clock-Architecture Diagram 1 + */ + + GATE(0, "apll_core", "apll", 0, + RK3288_CLKGATE_CON(0), 1, GFLAGS), + GATE(0, "gpll_core", "gpll", 0, + RK3288_CLKGATE_CON(0), 2, GFLAGS), + COMPOSITE_NOGATE(0, "armclk", mux_armclk_p, 0, + RK3288_CLKSEL_CON(0), 15, 1, MFLAGS, 8, 5, DFLAGS), + + COMPOSITE_NOMUX(0, "armcore0", "armclk", 0, + RK3288_CLKSEL_CON(36), 0, 3, DFLAGS, + RK3288_CLKGATE_CON(12), 0, GFLAGS), + COMPOSITE_NOMUX(0, "armcore1", "armclk", 0, + RK3288_CLKSEL_CON(36), 4, 3, DFLAGS, + RK3288_CLKGATE_CON(12), 1, GFLAGS), + COMPOSITE_NOMUX(0, "armcore2", "armclk", 0, + RK3288_CLKSEL_CON(36), 8, 3, DFLAGS, + RK3288_CLKGATE_CON(12), 2, GFLAGS), + COMPOSITE_NOMUX(0, "armcore3", "armclk", 0, + RK3288_CLKSEL_CON(36), 12, 3, DFLAGS, + RK3288_CLKGATE_CON(12), 3, GFLAGS), + COMPOSITE_NOMUX(0, "l2ram", "armclk", 0, + RK3288_CLKSEL_CON(37), 0, 3, DFLAGS, + RK3288_CLKGATE_CON(12), 4, GFLAGS), + COMPOSITE_NOMUX(0, "aclk_core_m0", "armclk", 0, + RK3288_CLKSEL_CON(0), 0, 4, DFLAGS, + RK3288_CLKGATE_CON(12), 5, GFLAGS), + COMPOSITE_NOMUX(0, "aclk_core_mp", "armclk", 0, + RK3288_CLKSEL_CON(0), 4, 4, DFLAGS, + RK3288_CLKGATE_CON(12), 6, GFLAGS), + COMPOSITE_NOMUX(0, "atclk", "armclk", 0, + RK3288_CLKSEL_CON(37), 4, 5, DFLAGS, + RK3288_CLKGATE_CON(12), 7, GFLAGS), + COMPOSITE_NOMUX(0, "pclk_dbg_pre", "armclk", 0, + RK3288_CLKSEL_CON(37), 9, 5, DFLAGS, + RK3288_CLKGATE_CON(12), 8, GFLAGS), + GATE(0, "pclk_dbg", "pclk_dbg_pre", 0, + RK3288_CLKGATE_CON(12), 9, GFLAGS), + GATE(0, "cs_dbg", "pclk_dbg_pre", 0, + RK3288_CLKGATE_CON(12), 10, GFLAGS), + GATE(0, "pclk_core_niu", "pclk_dbg_pre", 0, + RK3288_CLKGATE_CON(12), 11, GFLAGS), + + GATE(0, "dpll_ddr", "dpll", 0, + RK3288_CLKGATE_CON(0), 8, GFLAGS), + GATE(0, "gpll_ddr", "gpll", 0, + RK3288_CLKGATE_CON(0), 9, GFLAGS), + COMPOSITE_NOGATE(0, "ddrphy", mux_ddrphy_p, 0, + RK3288_CLKSEL_CON(26), 2, 1, MFLAGS, 0, 2, + DFLAGS | CLK_DIVIDER_POWER_OF_TWO), + + GATE(0, "gpll_aclk_cpu", "gpll", 0, + RK3288_CLKGATE_CON(0), 10, GFLAGS), + GATE(0, "cpll_aclk_cpu", "cpll", 0, + RK3288_CLKGATE_CON(0), 11, GFLAGS), + COMPOSITE_NOGATE(0, "aclk_cpu_src", mux_aclk_cpu_src_p, 0, + RK3288_CLKSEL_CON(1), 15, 1, MFLAGS, 3, 5, DFLAGS), + DIV(0, "aclk_cpu_pre", "aclk_cpu_src", 0, + RK3288_CLKSEL_CON(1), 0, 3, DFLAGS), + GATE(0, "aclk_cpu", "aclk_cpu_pre", 0, + RK3288_CLKGATE_CON(0), 3, GFLAGS), + COMPOSITE_NOMUX(0, "pclk_cpu", "aclk_cpu_pre", 0, + RK3288_CLKSEL_CON(1), 12, 3, DFLAGS, + RK3288_CLKGATE_CON(0), 5, GFLAGS), + COMPOSITE_NOMUX_DIVTBL(0, "hclk_cpu", "aclk_cpu_pre", 0, + RK3288_CLKSEL_CON(1), 8, 2, DFLAGS, div_hclk_cpu_t, + RK3288_CLKGATE_CON(0), 4, GFLAGS), + GATE(0, "c2c_host", "aclk_cpu_src", 0, + RK3288_CLKGATE_CON(13), 8, GFLAGS), + COMPOSITE_NOMUX(0, "crypto", "aclk_cpu_pre", 0, + RK3288_CLKSEL_CON(26), 6, 2, DFLAGS, + RK3288_CLKGATE_CON(5), 4, GFLAGS), + GATE(0, "aclk_bus_2pmu", "aclk_cpu_pre", 0, + RK3288_CLKGATE_CON(0), 7, GFLAGS), + + COMPOSITE(0, "i2s_src", mux_pll_src_cpll_gpll_p, 0, + RK3288_CLKSEL_CON(4), 15, 1, MFLAGS, 0, 7, DFLAGS, + RK3288_CLKGATE_CON(4), 1, GFLAGS), + COMPOSITE_FRAC(0, "i2s_frac", "i2s_src", 0, + RK3288_CLKSEL_CON(8), 0, + RK3288_CLKGATE_CON(4), 2, GFLAGS), + MUX(0, "i2s_pre", mux_i2s_pre_p, 0, + RK3288_CLKSEL_CON(4), 8, 2, MFLAGS), + COMPOSITE_NODIV(0, "i2s0_clkout", mux_i2s_clkout_p, 0, + RK3288_CLKSEL_CON(4), 12, 1, MFLAGS, + RK3288_CLKGATE_CON(4), 0, GFLAGS), + GATE(SCLK_I2S0, "sclk_i2s0", "i2s_pre", 0, + RK3288_CLKGATE_CON(4), 3, GFLAGS), + + MUX(0, "spdif_src", mux_pll_src_cpll_gpll_p, 0, + RK3288_CLKSEL_CON(5), 15, 1, MFLAGS), + COMPOSITE_NOMUX(0, "spdif_pre", "spdif_src", 0, + RK3288_CLKSEL_CON(5), 0, 7, DFLAGS, + RK3288_CLKGATE_CON(4), 4, GFLAGS), + COMPOSITE_FRAC(0, "spdif_frac", "spdif_src", 0, + RK3288_CLKSEL_CON(9), 0, + RK3288_CLKGATE_CON(4), 5, GFLAGS), + COMPOSITE_NODIV(SCLK_SPDIF, "sclk_spdif", mux_spdif_p, 0, + RK3288_CLKSEL_CON(5), 8, 2, MFLAGS, + RK3288_CLKGATE_CON(4), 6, GFLAGS), + COMPOSITE_NOMUX(0, "spdif_8ch_pre", "spdif_src", 0, + RK3288_CLKSEL_CON(40), 0, 7, DFLAGS, + RK3288_CLKGATE_CON(4), 7, GFLAGS), + COMPOSITE_FRAC(0, "spdif_8ch_frac", "spdif_8ch_src", 0, + RK3288_CLKSEL_CON(41), 0, + RK3288_CLKGATE_CON(4), 8, GFLAGS), + COMPOSITE_NODIV(SCLK_SPDIF8CH, "sclk_spdif_8ch", mux_spdif_8ch_p, 0, + RK3288_CLKSEL_CON(40), 8, 2, MFLAGS, + RK3288_CLKGATE_CON(4), 9, GFLAGS), + + GATE(0, "sclk_acc_efuse", "xin24m", 0, + RK3288_CLKGATE_CON(0), 12, GFLAGS), + + GATE(SCLK_TIMER0, "sclk_timer0", "xin24m", 0, + RK3288_CLKGATE_CON(1), 0, GFLAGS), + GATE(SCLK_TIMER1, "sclk_timer1", "xin24m", 0, + RK3288_CLKGATE_CON(1), 1, GFLAGS), + GATE(SCLK_TIMER2, "sclk_timer2", "xin24m", 0, + RK3288_CLKGATE_CON(1), 2, GFLAGS), + GATE(SCLK_TIMER3, "sclk_timer3", "xin24m", 0, + RK3288_CLKGATE_CON(1), 3, GFLAGS), + GATE(SCLK_TIMER4, "sclk_timer4", "xin24m", 0, + RK3288_CLKGATE_CON(1), 4, GFLAGS), + GATE(SCLK_TIMER5, "sclk_timer5", "xin24m", 0, + RK3288_CLKGATE_CON(1), 5, GFLAGS), + + /* + * Clock-Architecture Diagram 2 + */ + + COMPOSITE(0, "aclk_vepu", mux_pll_src_cpll_gpll_usb480m_p, 0, + RK3288_CLKSEL_CON(32), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3288_CLKGATE_CON(3), 9, GFLAGS), + COMPOSITE(0, "aclk_vdpu", mux_pll_src_cpll_gpll_usb480m_p, 0, + RK3288_CLKSEL_CON(32), 14, 2, MFLAGS, 8, 5, DFLAGS, + RK3288_CLKGATE_CON(3), 11, GFLAGS), + + COMPOSITE(0, "aclk_vio0", mux_pll_src_cpll_gpll_usb480m_p, 0, + RK3288_CLKSEL_CON(31), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3288_CLKGATE_CON(3), 0, GFLAGS), + DIV(0, "hclk_vio", "aclk_vio0", 0, + RK3288_CLKSEL_CON(28), 8, 5, DFLAGS), + COMPOSITE(0, "aclk_vio1", mux_pll_src_cpll_gpll_usb480m_p, 0, + RK3288_CLKSEL_CON(31), 14, 2, MFLAGS, 8, 5, DFLAGS, + RK3288_CLKGATE_CON(3), 2, GFLAGS), + + COMPOSITE(0, "aclk_rga_pre", mux_pll_src_cpll_gpll_usb480m_p, 0, + RK3288_CLKSEL_CON(30), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3288_CLKGATE_CON(3), 5, GFLAGS), + COMPOSITE(0, "sclk_rga", mux_pll_src_cpll_gpll_usb480m_p, 0, + RK3288_CLKSEL_CON(30), 14, 2, MFLAGS, 8, 5, DFLAGS, + RK3288_CLKGATE_CON(3), 4, GFLAGS), + + COMPOSITE(DCLK_VOP0, "dclk_vop0", mux_pll_src_cpll_gpll_npll_p, 0, + RK3288_CLKSEL_CON(27), 0, 2, MFLAGS, 8, 8, DFLAGS, + RK3288_CLKGATE_CON(3), 1, GFLAGS), + COMPOSITE(DCLK_VOP1, "dclk_vop1", mux_pll_src_cpll_gpll_npll_p, 0, + RK3288_CLKSEL_CON(29), 6, 2, MFLAGS, 8, 8, DFLAGS, + RK3288_CLKGATE_CON(3), 3, GFLAGS), + + COMPOSITE_NODIV(0, "sclk_edp_24m", mux_edp_24m_p, 0, + RK3288_CLKSEL_CON(28), 15, 1, MFLAGS, + RK3288_CLKGATE_CON(3), 12, GFLAGS), + COMPOSITE(0, "sclk_edp", mux_pll_src_cpll_gpll_npll_p, 0, + RK3288_CLKSEL_CON(28), 6, 2, MFLAGS, 0, 6, DFLAGS, + RK3288_CLKGATE_CON(3), 13, GFLAGS), + + COMPOSITE(0, "sclk_isp", mux_pll_src_cpll_gpll_npll_p, 0, + RK3288_CLKSEL_CON(6), 6, 2, MFLAGS, 0, 6, DFLAGS, + RK3288_CLKGATE_CON(3), 14, GFLAGS), + COMPOSITE(0, "sclk_isp_jpe", mux_pll_src_cpll_gpll_npll_p, 0, + RK3288_CLKSEL_CON(6), 14, 2, MFLAGS, 8, 6, DFLAGS, + RK3288_CLKGATE_CON(3), 15, GFLAGS), + + GATE(0, "sclk_hdmi_hdcp", "xin24m", 0, + RK3288_CLKGATE_CON(5), 12, GFLAGS), + GATE(0, "sclk_hdmi_cec", "xin32k", 0, + RK3288_CLKGATE_CON(5), 11, GFLAGS), + + COMPOSITE(0, "aclk_hevc", mux_pll_src_cpll_gpll_npll_p, 0, + RK3288_CLKSEL_CON(39), 14, 2, MFLAGS, 8, 5, DFLAGS, + RK3288_CLKGATE_CON(13), 13, GFLAGS), + DIV(0, "hclk_hevc", "aclk_hevc", 0, + RK3288_CLKSEL_CON(40), 12, 2, DFLAGS), + + COMPOSITE(0, "sclk_hevc_cabac", mux_pll_src_cpll_gpll_npll_p, 0, + RK3288_CLKSEL_CON(42), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3288_CLKGATE_CON(13), 14, GFLAGS), + COMPOSITE(0, "sclk_hevc_core", mux_pll_src_cpll_gpll_npll_p, 0, + RK3288_CLKSEL_CON(42), 14, 2, MFLAGS, 8, 5, DFLAGS, + RK3288_CLKGATE_CON(13), 15, GFLAGS), + + COMPOSITE_NODIV(0, "vip_src", mux_pll_src_cpll_gpll_p, 0, + RK3288_CLKSEL_CON(26), 8, 1, MFLAGS, + RK3288_CLKGATE_CON(3), 7, GFLAGS), + COMPOSITE_NOGATE(0, "sclk_vip_out", mux_cif_out_p, 0, + RK3288_CLKSEL_CON(26), 15, 1, MFLAGS, 9, 5, DFLAGS), + + DIV(0, "pclk_pd_alive", "gpll", 0, + RK3288_CLKSEL_CON(33), 8, 5, DFLAGS), + COMPOSITE_NOMUX(0, "pclk_pd_pmu", "gpll", 0, + RK3288_CLKSEL_CON(33), 0, 5, DFLAGS, + RK3288_CLKGATE_CON(5), 8, GFLAGS), + + COMPOSITE(SCLK_GPU, "sclk_gpu", mux_pll_src_cpll_gpll_usb480m_p, 0, + RK3288_CLKSEL_CON(34), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3288_CLKGATE_CON(5), 7, GFLAGS), + + COMPOSITE(0, "aclk_peri_src", mux_pll_src_cpll_gpll_p, 0, + RK3288_CLKSEL_CON(10), 15, 1, MFLAGS, 0, 5, DFLAGS, + RK3288_CLKGATE_CON(2), 0, GFLAGS), + COMPOSITE_NOMUX(0, "pclk_peri", "aclk_peri_src", 0, + RK3288_CLKSEL_CON(10), 12, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO, + RK3288_CLKGATE_CON(2), 3, GFLAGS), + COMPOSITE_NOMUX(0, "hclk_peri", "aclk_peri_src", 0, + RK3288_CLKSEL_CON(10), 8, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO, + RK3288_CLKGATE_CON(2), 2, GFLAGS), + GATE(0, "aclk_peri", "aclk_peri_src", 0, + RK3288_CLKGATE_CON(2), 1, GFLAGS), + + /* + * Clock-Architecture Diagram 3 + */ + + COMPOSITE(SCLK_SPI0, "sclk_spi0", mux_pll_src_cpll_gpll_p, 0, + RK3288_CLKSEL_CON(25), 7, 1, MFLAGS, 0, 7, DFLAGS, + RK3288_CLKGATE_CON(2), 9, GFLAGS), + COMPOSITE(SCLK_SPI1, "sclk_spi1", mux_pll_src_cpll_gpll_p, 0, + RK3288_CLKSEL_CON(25), 15, 1, MFLAGS, 8, 7, DFLAGS, + RK3288_CLKGATE_CON(2), 10, GFLAGS), + COMPOSITE(SCLK_SPI2, "sclk_spi2", mux_pll_src_cpll_gpll_p, 0, + RK3288_CLKSEL_CON(39), 7, 1, MFLAGS, 0, 7, DFLAGS, + RK3288_CLKGATE_CON(2), 11, GFLAGS), + + COMPOSITE(SCLK_SDMMC, "sclk_sdmmc", mux_mmc_src_p, 0, + RK3288_CLKSEL_CON(11), 6, 2, MFLAGS, 0, 6, DFLAGS, + RK3288_CLKGATE_CON(13), 0, GFLAGS), + COMPOSITE(SCLK_SDIO0, "sclk_sdio0", mux_mmc_src_p, 0, + RK3288_CLKSEL_CON(12), 6, 2, MFLAGS, 0, 6, DFLAGS, + RK3288_CLKGATE_CON(13), 1, GFLAGS), + COMPOSITE(SCLK_SDIO1, "sclk_sdio1", mux_mmc_src_p, 0, + RK3288_CLKSEL_CON(34), 14, 2, MFLAGS, 8, 6, DFLAGS, + RK3288_CLKGATE_CON(13), 2, GFLAGS), + COMPOSITE(SCLK_EMMC, "sclk_emmc", mux_mmc_src_p, 0, + RK3288_CLKSEL_CON(12), 14, 2, MFLAGS, 8, 6, DFLAGS, + RK3288_CLKGATE_CON(13), 3, GFLAGS), + + COMPOSITE(0, "sclk_tspout", mux_tspout_p, 0, + RK3288_CLKSEL_CON(35), 14, 2, MFLAGS, 8, 5, DFLAGS, + RK3288_CLKGATE_CON(4), 11, GFLAGS), + COMPOSITE(0, "sclk_tsp", mux_pll_src_cpll_gpll_npll_p, 0, + RK3288_CLKSEL_CON(35), 6, 2, MFLAGS, 0, 5, DFLAGS, + RK3288_CLKGATE_CON(4), 10, GFLAGS), + + GATE(SCLK_OTGPHY0, "sclk_otgphy0", "usb480m", 0, + RK3288_CLKGATE_CON(13), 4, GFLAGS), + GATE(SCLK_OTGPHY1, "sclk_otgphy1", "usb480m", 0, + RK3288_CLKGATE_CON(13), 5, GFLAGS), + GATE(SCLK_OTGPHY2, "sclk_otgphy2", "usb480m", 0, + RK3288_CLKGATE_CON(13), 6, GFLAGS), + GATE(SCLK_OTG_ADP, "sclk_otg_adp", "xin32k", 0, + RK3288_CLKGATE_CON(13), 7, GFLAGS), + + COMPOSITE_NOMUX(SCLK_TSADC, "sclk_tsadc", "xin32k", 0, + RK3288_CLKSEL_CON(2), 0, 6, DFLAGS, + RK3288_CLKGATE_CON(2), 7, GFLAGS), + + COMPOSITE_NOMUX(SCLK_SARADC, "sclk_saradc", "xin24m", 0, + RK3288_CLKSEL_CON(24), 8, 8, DFLAGS, + RK3288_CLKGATE_CON(2), 8, GFLAGS), + + GATE(SCLK_PS2C, "sclk_ps2c", "xin24m", 0, + RK3288_CLKGATE_CON(5), 13, GFLAGS), + + COMPOSITE(SCLK_NANDC0, "sclk_nandc0", mux_pll_src_cpll_gpll_p, 0, + RK3288_CLKSEL_CON(38), 7, 1, MFLAGS, 0, 5, DFLAGS, + RK3288_CLKGATE_CON(5), 5, GFLAGS), + COMPOSITE(SCLK_NANDC1, "sclk_nandc1", mux_pll_src_cpll_gpll_p, 0, + RK3288_CLKSEL_CON(38), 15, 1, MFLAGS, 8, 5, DFLAGS, + RK3288_CLKGATE_CON(5), 6, GFLAGS), + + COMPOSITE(0, "uart0_src", mux_uart0_pll_p, 0, + RK3288_CLKSEL_CON(13), 13, 2, MFLAGS, 0, 7, DFLAGS, + RK3288_CLKGATE_CON(1), 8, GFLAGS), + COMPOSITE_FRAC(0, "uart0_frac", "uart0_src", 0, + RK3288_CLKSEL_CON(17), 0, + RK3288_CLKGATE_CON(1), 9, GFLAGS), + MUX(SCLK_UART0, "sclk_uart0", mux_uart0_p, 0, + RK3288_CLKSEL_CON(13), 8, 2, MFLAGS), + MUX(0, "uart_src", mux_pll_src_cpll_gpll_p, 0, + RK3288_CLKSEL_CON(13), 15, 1, MFLAGS), + COMPOSITE_NOMUX(0, "uart1_src", "uart_src", 0, + RK3288_CLKSEL_CON(14), 0, 7, DFLAGS, + RK3288_CLKGATE_CON(1), 10, GFLAGS), + COMPOSITE_FRAC(0, "uart1_frac", "uart1_src", 0, + RK3288_CLKSEL_CON(18), 0, + RK3288_CLKGATE_CON(1), 11, GFLAGS), + MUX(SCLK_UART1, "sclk_uart1", mux_uart1_p, 0, + RK3288_CLKSEL_CON(14), 8, 2, MFLAGS), + COMPOSITE_NOMUX(0, "uart2_src", "uart_src", 0, + RK3288_CLKSEL_CON(15), 0, 7, DFLAGS, + RK3288_CLKGATE_CON(1), 12, GFLAGS), + COMPOSITE_FRAC(0, "uart2_frac", "uart2_src", 0, + RK3288_CLKSEL_CON(19), 0, + RK3288_CLKGATE_CON(1), 13, GFLAGS), + MUX(SCLK_UART2, "sclk_uart2", mux_uart2_p, 0, + RK3288_CLKSEL_CON(15), 8, 2, MFLAGS), + COMPOSITE_NOMUX(0, "uart3_src", "uart_src", 0, + RK3288_CLKSEL_CON(16), 0, 7, DFLAGS, + RK3288_CLKGATE_CON(1), 14, GFLAGS), + COMPOSITE_FRAC(0, "uart3_frac", "uart3_src", 0, + RK3288_CLKSEL_CON(20), 0, + RK3288_CLKGATE_CON(1), 15, GFLAGS), + MUX(SCLK_UART3, "sclk_uart3", mux_uart3_p, 0, + RK3288_CLKSEL_CON(16), 8, 2, MFLAGS), + COMPOSITE_NOMUX(0, "uart4_src", "uart_src", 0, + RK3288_CLKSEL_CON(3), 0, 7, DFLAGS, + RK3288_CLKGATE_CON(2), 12, GFLAGS), + COMPOSITE_FRAC(0, "uart4_frac", "uart4_src", 0, + RK3288_CLKSEL_CON(7), 0, + RK3288_CLKGATE_CON(2), 13, GFLAGS), + MUX(SCLK_UART4, "sclk_uart4", mux_uart4_p, 0, + RK3288_CLKSEL_CON(3), 8, 2, MFLAGS), + + COMPOSITE(0, "mac_src", mux_pll_src_npll_cpll_gpll_p, 0, + RK3288_CLKSEL_CON(21), 0, 2, MFLAGS, 8, 5, DFLAGS, + RK3288_CLKGATE_CON(2), 5, GFLAGS), + MUX(0, "macref", mux_macref_p, 0, + RK3288_CLKSEL_CON(21), 4, 1, MFLAGS), + GATE(0, "sclk_macref_out", "macref", 0, + RK3288_CLKGATE_CON(5), 3, GFLAGS), + GATE(SCLK_MACREF, "sclk_macref", "macref", 0, + RK3288_CLKGATE_CON(5), 2, GFLAGS), + GATE(SCLK_MAC_RX, "sclk_mac_rx", "macref", 0, + RK3288_CLKGATE_CON(5), 0, GFLAGS), + GATE(SCLK_MAC_TX, "sclk_mac_tx", "macref", 0, + RK3288_CLKGATE_CON(5), 1, GFLAGS), + + COMPOSITE(0, "hsadc_src", mux_pll_src_cpll_gpll_p, 0, + RK3288_CLKSEL_CON(22), 0, 1, MFLAGS, 8, 8, DFLAGS, + RK3288_CLKGATE_CON(2), 6, GFLAGS), + MUX(SCLK_HSADC, "sclk_hsadc_out", mux_hsadcout_p, 0, + RK3288_CLKSEL_CON(22), 4, 1, MFLAGS), + + GATE(0, "jtag", "ext_jtag", 0, + RK3288_CLKGATE_CON(4), 14, GFLAGS), + + COMPOSITE_NODIV(0, "usbphy480m_src", mux_usbphy480m_p, 0, + RK3288_CLKSEL_CON(13), 11, 2, MFLAGS, + RK3288_CLKGATE_CON(5), 15, GFLAGS), + COMPOSITE_NODIV(SCLK_HSICPHY480M, "sclk_hsicphy480m", mux_hsicphy480m_p, 0, + RK3288_CLKSEL_CON(29), 0, 2, MFLAGS, + RK3288_CLKGATE_CON(3), 6, GFLAGS), + GATE(0, "hsicphy12m_xin12m", "xin12m", 0, + RK3288_CLKGATE_CON(13), 9, GFLAGS), + DIV(0, "hsicphy12m_usbphy", "sclk_hsicphy480m", 0, + RK3288_CLKSEL_CON(11), 8, 6, DFLAGS), + MUX(SCLK_HSICPHY12M, "sclk_hsicphy12m", mux_hsicphy12m_p, 0, + RK3288_CLKSEL_CON(22), 4, 1, MFLAGS), + + /* + * Clock-Architecture Diagram 4 + */ + + /* aclk_cpu gates */ + GATE(0, "sclk_intmem0", "aclk_cpu", 0, RK3288_CLKGATE_CON(10), 5, GFLAGS), + GATE(0, "sclk_intmem1", "aclk_cpu", 0, RK3288_CLKGATE_CON(10), 6, GFLAGS), + GATE(0, "sclk_intmem2", "aclk_cpu", 0, RK3288_CLKGATE_CON(10), 7, GFLAGS), + GATE(ACLK_DMAC1, "aclk_dmac1", "aclk_cpu", 0, RK3288_CLKGATE_CON(10), 12, GFLAGS), + GATE(0, "aclk_strc_sys", "aclk_cpu", 0, RK3288_CLKGATE_CON(10), 13, GFLAGS), + GATE(0, "aclk_intmem", "aclk_cpu", 0, RK3288_CLKGATE_CON(10), 4, GFLAGS), + GATE(ACLK_CRYPTO, "aclk_crypto", "aclk_cpu", 0, RK3288_CLKGATE_CON(11), 6, GFLAGS), + GATE(0, "aclk_ccp", "aclk_cpu", 0, RK3288_CLKGATE_CON(11), 8, GFLAGS), + + /* hclk_cpu gates */ + GATE(HCLK_CRYPTO, "hclk_crypto", "hclk_cpu", 0, RK3288_CLKGATE_CON(11), 7, GFLAGS), + GATE(HCLK_I2S0, "hclk_i2s0", "hclk_cpu", 0, RK3288_CLKGATE_CON(10), 8, GFLAGS), + GATE(HCLK_ROM, "hclk_rom", "hclk_cpu", 0, RK3288_CLKGATE_CON(10), 9, GFLAGS), + GATE(HCLK_SPDIF, "hclk_spdif", "hclk_cpu", 0, RK3288_CLKGATE_CON(10), 10, GFLAGS), + GATE(HCLK_SPDIF8CH, "hclk_spdif_8ch", "hclk_cpu", 0, RK3288_CLKGATE_CON(10), 11, GFLAGS), + + /* pclk_cpu gates */ + GATE(PCLK_PWM, "pclk_pwm", "pclk_cpu", 0, RK3288_CLKGATE_CON(10), 0, GFLAGS), + GATE(PCLK_TIMER, "pclk_timer", "pclk_cpu", 0, RK3288_CLKGATE_CON(10), 1, GFLAGS), + GATE(PCLK_I2C0, "pclk_i2c0", "pclk_cpu", 0, RK3288_CLKGATE_CON(10), 2, GFLAGS), + GATE(PCLK_I2C1, "pclk_i2c1", "pclk_cpu", 0, RK3288_CLKGATE_CON(10), 3, GFLAGS), + GATE(0, "pclk_ddrupctl0", "pclk_cpu", 0, RK3288_CLKGATE_CON(10), 14, GFLAGS), + GATE(0, "pclk_publ0", "pclk_cpu", 0, RK3288_CLKGATE_CON(10), 15, GFLAGS), + GATE(0, "pclk_ddrupctl1", "pclk_cpu", 0, RK3288_CLKGATE_CON(11), 0, GFLAGS), + GATE(0, "pclk_publ1", "pclk_cpu", 0, RK3288_CLKGATE_CON(11), 1, GFLAGS), + GATE(0, "pclk_efuse_1024", "pclk_cpu", 0, RK3288_CLKGATE_CON(11), 2, GFLAGS), + GATE(PCLK_TZPC, "pclk_tzpc", "pclk_cpu", 0, RK3288_CLKGATE_CON(11), 3, GFLAGS), + GATE(PCLK_UART2, "pclk_uart2", "pclk_cpu", 0, RK3288_CLKGATE_CON(11), 9, GFLAGS), + GATE(0, "pclk_efuse_256", "pclk_cpu", 0, RK3288_CLKGATE_CON(11), 10, GFLAGS), + GATE(PCLK_RKPWM, "pclk_rkpwm", "pclk_cpu", 0, RK3288_CLKGATE_CON(11), 11, GFLAGS), + + /* ddrctrl [DDR Controller PHY clock] gates */ + GATE(0, "nclk_ddrupctl0", "ddrphy", 0, RK3288_CLKGATE_CON(11), 4, GFLAGS), + GATE(0, "nclk_ddrupctl1", "ddrphy", 0, RK3288_CLKGATE_CON(11), 5, GFLAGS), + + /* ddrphy gates */ + GATE(0, "sclk_ddrphy0", "ddrphy", 0, RK3288_CLKGATE_CON(4), 12, GFLAGS), + GATE(0, "sclk_ddrphy1", "ddrphy", 0, RK3288_CLKGATE_CON(4), 13, GFLAGS), + + /* aclk_peri gates */ + GATE(0, "aclk_peri_axi_matrix", "aclk_peri", 0, RK3288_CLKGATE_CON(6), 2, GFLAGS), + GATE(ACLK_DMAC2, "aclk_dmac2", "aclk_peri", 0, RK3288_CLKGATE_CON(6), 3, GFLAGS), + GATE(0, "aclk_peri_niu", "aclk_peri", 0, RK3288_CLKGATE_CON(7), 11, GFLAGS), + GATE(ACLK_MMU, "aclk_mmu", "aclk_peri", 0, RK3288_CLKGATE_CON(8), 12, GFLAGS), + GATE(ACLK_GMAC, "aclk_gmac", "aclk_peri", 0, RK3288_CLKGATE_CON(8), 0, GFLAGS), + GATE(HCLK_GPS, "hclk_gps", "aclk_peri", 0, RK3288_CLKGATE_CON(8), 2, GFLAGS), + + /* hclk_peri gates */ + GATE(0, "hclk_peri_matrix", "hclk_peri", 0, RK3288_CLKGATE_CON(6), 0, GFLAGS), + GATE(HCLK_OTG0, "hclk_otg0", "hclk_peri", 0, RK3288_CLKGATE_CON(7), 4, GFLAGS), + GATE(HCLK_USBHOST0, "hclk_host0", "hclk_peri", 0, RK3288_CLKGATE_CON(7), 6, GFLAGS), + GATE(HCLK_USBHOST1, "hclk_host1", "hclk_peri", 0, RK3288_CLKGATE_CON(7), 7, GFLAGS), + GATE(HCLK_HSIC, "hclk_hsic", "hclk_peri", 0, RK3288_CLKGATE_CON(7), 8, GFLAGS), + GATE(0, "hclk_usb_peri", "hclk_peri", 0, RK3288_CLKGATE_CON(7), 9, GFLAGS), + GATE(0, "hclk_peri_ahb_arbi", "hclk_peri", 0, RK3288_CLKGATE_CON(7), 10, GFLAGS), + GATE(0, "hclk_emem", "hclk_peri", 0, RK3288_CLKGATE_CON(7), 12, GFLAGS), + GATE(0, "hclk_mem", "hclk_peri", 0, RK3288_CLKGATE_CON(7), 13, GFLAGS), + GATE(HCLK_NANDC0, "hclk_nandc0", "hclk_peri", 0, RK3288_CLKGATE_CON(7), 14, GFLAGS), + GATE(HCLK_NANDC1, "hclk_nandc1", "hclk_peri", 0, RK3288_CLKGATE_CON(7), 15, GFLAGS), + GATE(HCLK_TSP, "hclk_tsp", "hclk_peri", 0, RK3288_CLKGATE_CON(8), 8, GFLAGS), + GATE(HCLK_SDMMC, "hclk_sdmmc", "hclk_peri", 0, RK3288_CLKGATE_CON(8), 3, GFLAGS), + GATE(HCLK_SDIO0, "hclk_sdio0", "hclk_peri", 0, RK3288_CLKGATE_CON(8), 4, GFLAGS), + GATE(HCLK_SDIO1, "hclk_sdio1", "hclk_peri", 0, RK3288_CLKGATE_CON(8), 5, GFLAGS), + GATE(HCLK_EMMC, "hclk_emmc", "hclk_peri", 0, RK3288_CLKGATE_CON(8), 6, GFLAGS), + GATE(HCLK_HSADC, "hclk_hsadc", "hclk_peri", 0, RK3288_CLKGATE_CON(8), 7, GFLAGS), + GATE(0, "pmu_hclk_otg0", "hclk_peri", 0, RK3288_CLKGATE_CON(7), 5, GFLAGS), + + /* pclk_peri gates */ + GATE(0, "pclk_peri_matrix", "pclk_peri", 0, RK3288_CLKGATE_CON(6), 1, GFLAGS), + GATE(PCLK_SPI0, "pclk_spi0", "pclk_peri", 0, RK3288_CLKGATE_CON(6), 4, GFLAGS), + GATE(PCLK_SPI1, "pclk_spi1", "pclk_peri", 0, RK3288_CLKGATE_CON(6), 5, GFLAGS), + GATE(PCLK_SPI2, "pclk_spi2", "pclk_peri", 0, RK3288_CLKGATE_CON(6), 6, GFLAGS), + GATE(PCLK_PS2C, "pclk_ps2c", "pclk_peri", 0, RK3288_CLKGATE_CON(6), 7, GFLAGS), + GATE(PCLK_UART0, "pclk_uart0", "pclk_peri", 0, RK3288_CLKGATE_CON(6), 8, GFLAGS), + GATE(PCLK_UART1, "pclk_uart1", "pclk_peri", 0, RK3288_CLKGATE_CON(6), 9, GFLAGS), + GATE(PCLK_I2C4, "pclk_i2c4", "pclk_peri", 0, RK3288_CLKGATE_CON(6), 15, GFLAGS), + GATE(PCLK_UART3, "pclk_uart3", "pclk_peri", 0, RK3288_CLKGATE_CON(6), 11, GFLAGS), + GATE(PCLK_UART4, "pclk_uart4", "pclk_peri", 0, RK3288_CLKGATE_CON(6), 12, GFLAGS), + GATE(PCLK_I2C2, "pclk_i2c2", "pclk_peri", 0, RK3288_CLKGATE_CON(6), 13, GFLAGS), + GATE(PCLK_I2C3, "pclk_i2c3", "pclk_peri", 0, RK3288_CLKGATE_CON(6), 14, GFLAGS), + GATE(PCLK_SARADC, "pclk_saradc", "pclk_peri", 0, RK3288_CLKGATE_CON(7), 1, GFLAGS), + GATE(PCLK_TSADC, "pclk_tsadc", "pclk_peri", 0, RK3288_CLKGATE_CON(7), 2, GFLAGS), + GATE(PCLK_SIM, "pclk_sim", "pclk_peri", 0, RK3288_CLKGATE_CON(7), 3, GFLAGS), + GATE(PCLK_I2C5, "pclk_i2c5", "pclk_peri", 0, RK3288_CLKGATE_CON(7), 0, GFLAGS), + GATE(PCLK_GMAC, "pclk_gmac", "pclk_peri", 0, RK3288_CLKGATE_CON(8), 1, GFLAGS), + + GATE(SCLK_LCDC_PWM0, "sclk_lcdc_pwm0", "xin24m", 0, RK3288_CLKGATE_CON(13), 10, GFLAGS), + GATE(SCLK_LCDC_PWM1, "sclk_lcdc_pwm1", "xin24m", 0, RK3288_CLKGATE_CON(13), 11, GFLAGS), + GATE(0, "sclk_pvtm_core", "xin24m", 0, RK3288_CLKGATE_CON(5), 9, GFLAGS), + GATE(0, "sclk_pvtm_gpu", "xin24m", 0, RK3288_CLKGATE_CON(5), 10, GFLAGS), + GATE(0, "sclk_mipidsi_24m", "xin24m", 0, RK3288_CLKGATE_CON(5), 15, GFLAGS), + + /* sclk_gpu gates */ + GATE(ACLK_GPU, "aclk_gpu", "sclk_gpu", 0, RK3288_CLKGATE_CON(18), 0, GFLAGS), + + /* pclk_pd_alive gates */ + GATE(PCLK_GPIO8, "pclk_gpio8", "pclk_pd_alive", 0, RK3288_CLKGATE_CON(14), 8, GFLAGS), + GATE(PCLK_GPIO7, "pclk_gpio7", "pclk_pd_alive", 0, RK3288_CLKGATE_CON(14), 7, GFLAGS), + GATE(PCLK_GPIO1, "pclk_gpio1", "pclk_pd_alive", 0, RK3288_CLKGATE_CON(14), 1, GFLAGS), + GATE(PCLK_GPIO2, "pclk_gpio2", "pclk_pd_alive", 0, RK3288_CLKGATE_CON(14), 2, GFLAGS), + GATE(PCLK_GPIO3, "pclk_gpio3", "pclk_pd_alive", 0, RK3288_CLKGATE_CON(14), 3, GFLAGS), + GATE(PCLK_GPIO4, "pclk_gpio4", "pclk_pd_alive", 0, RK3288_CLKGATE_CON(14), 4, GFLAGS), + GATE(PCLK_GPIO5, "pclk_gpio5", "pclk_pd_alive", 0, RK3288_CLKGATE_CON(14), 5, GFLAGS), + GATE(PCLK_GPIO6, "pclk_gpio6", "pclk_pd_alive", 0, RK3288_CLKGATE_CON(14), 6, GFLAGS), + GATE(PCLK_GRF, "pclk_grf", "pclk_pd_alive", 0, RK3288_CLKGATE_CON(14), 11, GFLAGS), + GATE(0, "pclk_alive_niu", "pclk_pd_alive", 0, RK3288_CLKGATE_CON(14), 12, GFLAGS), + + /* pclk_pd_pmu gates */ + GATE(PCLK_PMU, "pclk_pmu", "pclk_pd_pmu", 0, RK3288_CLKGATE_CON(17), 0, GFLAGS), + GATE(0, "pclk_intmem1", "pclk_pd_pmu", 0, RK3288_CLKGATE_CON(17), 1, GFLAGS), + GATE(0, "pclk_pmu_niu", "pclk_pd_pmu", 0, RK3288_CLKGATE_CON(17), 2, GFLAGS), + GATE(PCLK_SGRF, "pclk_sgrf", "pclk_pd_pmu", 0, RK3288_CLKGATE_CON(17), 3, GFLAGS), + GATE(PCLK_GPIO0, "pclk_gpio0", "pclk_pd_pmu", 0, RK3288_CLKGATE_CON(17), 4, GFLAGS), + + /* hclk_vio gates */ + GATE(HCLK_RGA, "hclk_rga", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 1, GFLAGS), + GATE(HCLK_VOP0, "hclk_vop0", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 6, GFLAGS), + GATE(HCLK_VOP1, "hclk_vop1", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 8, GFLAGS), + GATE(0, "hclk_vio_ahb_arbi", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 9, GFLAGS), + GATE(0, "hclk_vio_niu", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 10, GFLAGS), + GATE(0, "hclk_vip", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 15, GFLAGS), + GATE(HCLK_IEP, "hclk_iep", "hclk_vio", 0, RK3288_CLKGATE_CON(15), 3, GFLAGS), + GATE(HCLK_ISP, "hclk_isp", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 1, GFLAGS), + GATE(0, "hclk_vio2_h2p", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 10, GFLAGS), + GATE(0, "pclk_mipi_dsi0", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 4, GFLAGS), + GATE(0, "pclk_mipi_dsi1", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 5, GFLAGS), + GATE(0, "pclk_mipi_csi", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 6, GFLAGS), + GATE(0, "pclk_lvds_phy", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 7, GFLAGS), + GATE(0, "pclk_edp_ctrl", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 8, GFLAGS), + GATE(0, "pclk_hdmi_ctrl", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 9, GFLAGS), + GATE(0, "pclk_vio2_h2p", "hclk_vio", 0, RK3288_CLKGATE_CON(16), 11, GFLAGS), + + /* aclk_vio0 gates */ + GATE(ACLK_VOP0, "aclk_vop0", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 5, GFLAGS), + GATE(0, "aclk_iep", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 2, GFLAGS), + GATE(0, "aclk_vio0_niu", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 11, GFLAGS), + GATE(0, "aclk_vip", "aclk_vio0", 0, RK3288_CLKGATE_CON(15), 14, GFLAGS), + + /* aclk_vio1 gates */ + GATE(ACLK_VOP1, "aclk_vop1", "aclk_vio1", 0, RK3288_CLKGATE_CON(15), 7, GFLAGS), + GATE(0, "aclk_isp", "aclk_vio1", 0, RK3288_CLKGATE_CON(16), 2, GFLAGS), + GATE(0, "aclk_vio1_niu", "aclk_vio1", 0, RK3288_CLKGATE_CON(15), 12, GFLAGS), + + /* aclk_rga_pre gates */ + GATE(ACLK_RGA, "aclk_rga", "aclk_rga_pre", 0, RK3288_CLKGATE_CON(15), 0, GFLAGS), + GATE(0, "aclk_rga_niu", "aclk_rga_pre", 0, RK3288_CLKGATE_CON(15), 13, GFLAGS), + + /* + * Other ungrouped clocks. + */ + + GATE(0, "pclk_vip_in", "ext_vip", 0, RK3288_CLKGATE_CON(16), 0, GFLAGS), + GATE(0, "pclk_isp_in", "ext_isp", 0, RK3288_CLKGATE_CON(16), 3, GFLAGS), +}; + +static void __init rk3288_clk_init(struct device_node *np) +{ + void __iomem *reg_base; + struct clk *clk; + + reg_base = of_iomap(np, 0); + if (!reg_base) { + pr_err("%s: could not map cru region\n", __func__); + return; + } + + rockchip_clk_init(np, reg_base, CLK_NR_CLKS); + + /* xin12m is created by an cru-internal divider */ + clk = clk_register_fixed_factor(NULL, "xin12m", "xin24m", 0, 1, 2); + if (IS_ERR(clk)) + pr_warn("%s: could not register clock xin12m: %ld\n", + __func__, PTR_ERR(clk)); + + + clk = clk_register_fixed_factor(NULL, "usb480m", "xin24m", 0, 20, 1); + if (IS_ERR(clk)) + pr_warn("%s: could not register clock usb480m: %ld\n", + __func__, PTR_ERR(clk)); + + rockchip_clk_register_plls(rk3288_pll_clks, + ARRAY_SIZE(rk3288_pll_clks), + RK3288_GRF_SOC_STATUS); + rockchip_clk_register_branches(rk3288_clk_branches, + ARRAY_SIZE(rk3288_clk_branches)); + + rockchip_register_softrst(np, 9, reg_base + RK3288_SOFTRST_CON(0), + ROCKCHIP_SOFTRST_HIWORD_MASK); +} +CLK_OF_DECLARE(rk3288_cru, "rockchip,rk3288-cru", rk3288_clk_init); diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index 32c334d7fc87..887cbdeca2aa 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h @@ -40,6 +40,15 @@ #define RK2928_SOFTRST_CON(x) (x * 0x4 + 0x110) #define RK2928_MISC_CON 0x134 +#define RK3288_PLL_CON(x) RK2928_PLL_CON(x) +#define RK3288_MODE_CON 0x50 +#define RK3288_CLKSEL_CON(x) (x * 0x4 + 0x60) +#define RK3288_CLKGATE_CON(x) (x * 0x4 + 0x160) +#define RK3288_GLB_SRST_FST 0x1b0 +#define RK3288_GLB_SRST_SND 0x1b4 +#define RK3288_SOFTRST_CON(x) (x * 0x4 + 0x1b8) +#define RK3288_MISC_CON 0x1e8 + enum rockchip_pll_type { pll_rk3066, }; diff --git a/include/dt-bindings/clock/rk3288-cru.h b/include/dt-bindings/clock/rk3288-cru.h new file mode 100644 index 000000000000..ebcb460ea4ad --- /dev/null +++ b/include/dt-bindings/clock/rk3288-cru.h @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2014 MundoReader S.L. + * Author: Heiko Stuebner + * + * 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. + */ + +/* core clocks */ +#define PLL_APLL 1 +#define PLL_DPLL 2 +#define PLL_CPLL 3 +#define PLL_GPLL 4 +#define PLL_NPLL 5 + +/* sclk gates (special clocks) */ +#define SCLK_GPU 64 +#define SCLK_SPI0 65 +#define SCLK_SPI1 66 +#define SCLK_SPI2 67 +#define SCLK_SDMMC 68 +#define SCLK_SDIO0 69 +#define SCLK_SDIO1 70 +#define SCLK_EMMC 71 +#define SCLK_TSADC 72 +#define SCLK_SARADC 73 +#define SCLK_PS2C 74 +#define SCLK_NANDC0 75 +#define SCLK_NANDC1 76 +#define SCLK_UART0 77 +#define SCLK_UART1 78 +#define SCLK_UART2 79 +#define SCLK_UART3 80 +#define SCLK_UART4 81 +#define SCLK_I2S0 82 +#define SCLK_SPDIF 83 +#define SCLK_SPDIF8CH 84 +#define SCLK_TIMER0 85 +#define SCLK_TIMER1 86 +#define SCLK_TIMER2 87 +#define SCLK_TIMER3 88 +#define SCLK_TIMER4 89 +#define SCLK_TIMER5 90 +#define SCLK_TIMER6 91 +#define SCLK_HSADC 92 +#define SCLK_OTGPHY0 93 +#define SCLK_OTGPHY1 94 +#define SCLK_OTGPHY2 95 +#define SCLK_OTG_ADP 96 +#define SCLK_HSICPHY480M 97 +#define SCLK_HSICPHY12M 98 +#define SCLK_MACREF 99 +#define SCLK_LCDC_PWM0 100 +#define SCLK_LCDC_PWM1 101 +#define SCLK_MAC_RX 102 +#define SCLK_MAC_TX 103 + +#define DCLK_VOP0 190 +#define DCLK_VOP1 191 + +/* aclk gates */ +#define ACLK_GPU 192 +#define ACLK_DMAC1 193 +#define ACLK_DMAC2 194 +#define ACLK_MMU 195 +#define ACLK_GMAC 196 +#define ACLK_VOP0 197 +#define ACLK_VOP1 198 +#define ACLK_CRYPTO 199 +#define ACLK_RGA 200 + +/* pclk gates */ +#define PCLK_GPIO0 320 +#define PCLK_GPIO1 321 +#define PCLK_GPIO2 322 +#define PCLK_GPIO3 323 +#define PCLK_GPIO4 324 +#define PCLK_GPIO5 325 +#define PCLK_GPIO6 326 +#define PCLK_GPIO7 327 +#define PCLK_GPIO8 328 +#define PCLK_GRF 329 +#define PCLK_SGRF 330 +#define PCLK_PMU 331 +#define PCLK_I2C0 332 +#define PCLK_I2C1 333 +#define PCLK_I2C2 334 +#define PCLK_I2C3 335 +#define PCLK_I2C4 336 +#define PCLK_I2C5 337 +#define PCLK_SPI0 338 +#define PCLK_SPI1 339 +#define PCLK_SPI2 340 +#define PCLK_UART0 341 +#define PCLK_UART1 342 +#define PCLK_UART2 343 +#define PCLK_UART3 344 +#define PCLK_UART4 345 +#define PCLK_TSADC 346 +#define PCLK_SARADC 347 +#define PCLK_SIM 348 +#define PCLK_GMAC 349 +#define PCLK_PWM 350 +#define PCLK_RKPWM 351 +#define PCLK_PS2C 352 +#define PCLK_TIMER 353 +#define PCLK_TZPC 354 + +/* hclk gates */ +#define HCLK_GPS 448 +#define HCLK_OTG0 449 +#define HCLK_USBHOST0 450 +#define HCLK_USBHOST1 451 +#define HCLK_HSIC 452 +#define HCLK_NANDC0 453 +#define HCLK_NANDC1 454 +#define HCLK_TSP 455 +#define HCLK_SDMMC 456 +#define HCLK_SDIO0 457 +#define HCLK_SDIO1 458 +#define HCLK_EMMC 459 +#define HCLK_HSADC 460 +#define HCLK_CRYPTO 461 +#define HCLK_I2S0 462 +#define HCLK_SPDIF 463 +#define HCLK_SPDIF8CH 464 +#define HCLK_VOP0 465 +#define HCLK_VOP1 466 +#define HCLK_ROM 467 +#define HCLK_IEP 468 +#define HCLK_ISP 469 +#define HCLK_RGA 470 + +#define CLK_NR_CLKS (HCLK_RGA + 1) + +/* soft-reset indices */ +#define SRST_CORE0 0 +#define SRST_CORE1 1 +#define SRST_CORE2 2 +#define SRST_CORE3 3 +#define SRST_CORE0_PO 4 +#define SRST_CORE1_PO 5 +#define SRST_CORE2_PO 6 +#define SRST_CORE3_PO 7 +#define SRST_PDCORE_STRSYS 8 +#define SRST_PDBUS_STRSYS 9 +#define SRST_L2C 10 +#define SRST_TOPDBG 11 +#define SRST_CORE0_DBG 12 +#define SRST_CORE1_DBG 13 +#define SRST_CORE2_DBG 14 +#define SRST_CORE3_DBG 15 + +#define SRST_PDBUG_AHB_ARBITOR 16 +#define SRST_EFUSE256 17 +#define SRST_DMAC1 18 +#define SRST_INTMEM 19 +#define SRST_ROM 20 +#define SRST_SPDIF8CH 21 +#define SRST_TIMER 22 +#define SRST_I2S0 23 +#define SRST_SPDIF 24 +#define SRST_TIMER0 25 +#define SRST_TIMER1 26 +#define SRST_TIMER2 27 +#define SRST_TIMER3 28 +#define SRST_TIMER4 29 +#define SRST_TIMER5 30 +#define SRST_EFUSE 31 + +#define SRST_GPIO0 32 +#define SRST_GPIO1 33 +#define SRST_GPIO2 34 +#define SRST_GPIO3 35 +#define SRST_GPIO4 36 +#define SRST_GPIO5 37 +#define SRST_GPIO6 38 +#define SRST_GPIO7 39 +#define SRST_GPIO8 40 +#define SRST_I2C0 42 +#define SRST_I2C1 43 +#define SRST_I2C2 44 +#define SRST_I2C3 45 +#define SRST_I2C4 46 +#define SRST_I2C5 47 + +#define SRST_DWPWM 48 +#define SRST_MMC_PERI 49 +#define SRST_PERIPH_MMU 50 +#define SRST_DAP 51 +#define SRST_DAP_SYS 52 +#define SRST_TPIU 53 +#define SRST_PMU_APB 54 +#define SRST_GRF 55 +#define SRST_PMU 56 +#define SRST_PERIPH_AXI 57 +#define SRST_PERIPH_AHB 58 +#define SRST_PERIPH_APB 59 +#define SRST_PERIPH_NIU 60 +#define SRST_PDPERI_AHB_ARBI 61 +#define SRST_EMEM 62 +#define SRST_USB_PERI 63 + +#define SRST_DMAC2 64 +#define SRST_MAC 66 +#define SRST_GPS 67 +#define SRST_RKPWM 69 +#define SRST_CCP 71 +#define SRST_USBHOST0 72 +#define SRST_HSIC 73 +#define SRST_HSIC_AUX 74 +#define SRST_HSIC_PHY 75 +#define SRST_HSADC 76 +#define SRST_NANDC0 77 +#define SRST_NANDC1 78 + +#define SRST_TZPC 80 +#define SRST_SPI0 83 +#define SRST_SPI1 84 +#define SRST_SPI2 85 +#define SRST_SARADC 87 +#define SRST_PDALIVE_NIU 88 +#define SRST_PDPMU_INTMEM 89 +#define SRST_PDPMU_NIU 90 +#define SRST_SGRF 91 + +#define SRST_VIO_ARBI 96 +#define SRST_RGA_NIU 97 +#define SRST_VIO0_NIU_AXI 98 +#define SRST_VIO_NIU_AHB 99 +#define SRST_LCDC0_AXI 100 +#define SRST_LCDC0_AHB 101 +#define SRST_LCDC0_DCLK 102 +#define SRST_VIO1_NIU_AXI 103 +#define SRST_VIP 104 +#define SRST_RGA_CORE 105 +#define SRST_IEP_AXI 106 +#define SRST_IEP_AHB 107 +#define SRST_RGA_AXI 108 +#define SRST_RGA_AHB 109 +#define SRST_ISP 110 +#define SRST_EDP 111 + +#define SRST_VCODEC_AXI 112 +#define SRST_VCODEC_AHB 113 +#define SRST_VIO_H2P 114 +#define SRST_MIPIDSI0 115 +#define SRST_MIPIDSI1 116 +#define SRST_MIPICSI 117 +#define SRST_LVDS_PHY 118 +#define SRST_LVDS_CON 119 +#define SRST_GPU 120 +#define SRST_HDMI 121 +#define SRST_CORE_PVTM 124 +#define SRST_GPU_PVTM 125 + +#define SRST_MMC0 128 +#define SRST_SDIO0 129 +#define SRST_SDIO1 130 +#define SRST_EMMC 131 +#define SRST_USBOTG_AHB 132 +#define SRST_USBOTG_PHY 133 +#define SRST_USBOTG_CON 134 +#define SRST_USBHOST0_AHB 135 +#define SRST_USBHOST0_PHY 136 +#define SRST_USBHOST0_CON 137 +#define SRST_USBHOST1_AHB 138 +#define SRST_USBHOST1_PHY 139 +#define SRST_USBHOST1_CON 140 +#define SRST_USB_ADP 141 +#define SRST_ACC_EFUSE 142 -- cgit v1.2.3 From 5a54e7c85b8303b84a9b2fbf978a74d625f85a99 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 13 Jul 2014 20:50:15 +0200 Subject: Bluetooth: Convert L2CAP ident spinlock into a mutex The spinlock protecting the L2CAP ident number can be converted into a mutex since the whole processing is run in a workqueue. So instead of using a spinlock, just use a mutex here. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/l2cap.h | 3 +-- net/bluetooth/l2cap_core.c | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index d8e7b9330ab8..1fffd92808b0 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -625,11 +625,10 @@ struct l2cap_conn { struct delayed_work info_timer; - spinlock_t lock; - struct sk_buff *rx_skb; __u32 rx_len; __u8 tx_ident; + struct mutex ident_lock; struct sk_buff_head pending_rx; struct work_struct pending_rx_work; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 0c3c4935f635..8538cb07b0c0 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -798,14 +798,14 @@ static u8 l2cap_get_ident(struct l2cap_conn *conn) * 200 - 254 are used by utilities like l2ping, etc. */ - spin_lock(&conn->lock); + mutex_lock(&conn->ident_lock); if (++conn->tx_ident > 128) conn->tx_ident = 1; id = conn->tx_ident; - spin_unlock(&conn->lock); + mutex_unlock(&conn->ident_lock); return id; } @@ -7016,7 +7016,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon) conn->hs_enabled = test_bit(HCI_HS_ENABLED, &hcon->hdev->dev_flags); - spin_lock_init(&conn->lock); + mutex_init(&conn->ident_lock); mutex_init(&conn->chan_lock); INIT_LIST_HEAD(&conn->chan_l); -- cgit v1.2.3 From 17fa388ddceb89e9673d83d82cf28ef79e8225d5 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 13 Jul 2014 17:00:37 +0200 Subject: locks: typedef fl_owner_t to void * fl_owner_t is a cookie that can store all kinds of different pointers, so don't pretends it points to a file structure. For now just change the typedef, but as a follow on this will allow to get rids of lots of casts and eventually the typedef itself. Signed-off-by: Christoph Hellwig Signed-off-by: Jeff Layton --- include/linux/fs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/fs.h b/include/linux/fs.h index e11d60cc867b..2daccaf4b547 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -833,7 +833,7 @@ static inline struct file *get_file(struct file *f) * * Lockd stuffs a "host" pointer into this. */ -typedef struct files_struct *fl_owner_t; +typedef void *fl_owner_t; struct file_lock_operations { void (*fl_copy_lock)(struct file_lock *, struct file_lock *); -- cgit v1.2.3 From ec31a05c4dfa95149b1754d9de92831a5a95c636 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 12 Jul 2014 15:49:16 +0200 Subject: net: filter: sk_chk_filter() no longer mangles filter Add const attribute to filter argument to make clear it is no longer modified. Signed-off-by: Eric Dumazet Acked-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/filter.h | 2 +- net/core/filter.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/filter.h b/include/linux/filter.h index b885dcb7eaca..c43c8258e682 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -362,7 +362,7 @@ void sk_unattached_filter_destroy(struct sk_filter *fp); int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk); int sk_detach_filter(struct sock *sk); -int sk_chk_filter(struct sock_filter *filter, unsigned int flen); +int sk_chk_filter(const struct sock_filter *filter, unsigned int flen); int sk_get_filter(struct sock *sk, struct sock_filter __user *filter, unsigned int len); diff --git a/net/core/filter.c b/net/core/filter.c index 87af1e3e56c0..b90ae7fb3b89 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1085,7 +1085,7 @@ err: * a cell if not previously written, and we check all branches to be sure * a malicious user doesn't try to abuse us. */ -static int check_load_and_stores(struct sock_filter *filter, int flen) +static int check_load_and_stores(const struct sock_filter *filter, int flen) { u16 *masks, memvalid = 0; /* One bit per cell, 16 cells */ int pc, ret = 0; @@ -1218,7 +1218,7 @@ static bool chk_code_allowed(u16 code_to_probe) * * Returns 0 if the rule set is legal or -EINVAL if not. */ -int sk_chk_filter(struct sock_filter *filter, unsigned int flen) +int sk_chk_filter(const struct sock_filter *filter, unsigned int flen) { bool anc_found; int pc; @@ -1228,7 +1228,7 @@ int sk_chk_filter(struct sock_filter *filter, unsigned int flen) /* Check the filter code now */ for (pc = 0; pc < flen; pc++) { - struct sock_filter *ftest = &filter[pc]; + const struct sock_filter *ftest = &filter[pc]; /* May we actually operate on this code? */ if (!chk_code_allowed(ftest->code)) -- cgit v1.2.3 From 884d6a0b558c9c31fdf0d950bd07a2c804876445 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Tue, 8 Jul 2014 21:32:11 +0900 Subject: drm/dsi: Flag for non-continuous clock behavior As per section 5.6.1 of the DSI specification, all DSI transmitters must support continuous clock behavior on the clock lane, while non-continuous mode support is only optional. Add a flag that allows devices to indicate that they support non-continuous clock mode so host drivers can adapt their behavior accordingly. Signed-off-by: Alexandre Courbot Signed-off-by: Thierry Reding --- include/drm/drm_mipi_dsi.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h index 944f33f8ba38..efa1b552adc5 100644 --- a/include/drm/drm_mipi_dsi.h +++ b/include/drm/drm_mipi_dsi.h @@ -94,6 +94,8 @@ void mipi_dsi_host_unregister(struct mipi_dsi_host *host); #define MIPI_DSI_MODE_VSYNC_FLUSH BIT(8) /* disable EoT packets in HS mode */ #define MIPI_DSI_MODE_EOT_PACKET BIT(9) +/* device supports non-continuous clock behavior (DSI spec 5.6.1) */ +#define MIPI_DSI_CLOCK_NON_CONTINUOUS BIT(10) enum mipi_dsi_pixel_format { MIPI_DSI_FMT_RGB888, -- cgit v1.2.3 From 38e029f14a9702f71d5953246df9f722bca49017 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 1 Jul 2014 12:23:12 +0200 Subject: netfilter: nf_tables: set NLM_F_DUMP_INTR if netlink dumping is stale An updater may interfer with the dumping of any of the object lists. Fix this by using a per-net generation counter and use the nl_dump_check_consistent() interface so the NLM_F_DUMP_INTR flag is set to notify userspace that it has to restart the dump since an updater has interfered. This patch also replaces the existing consistency checking code in the rule dumping path since it is broken. Basically, the value that the dump callback returns is not propagated to userspace via netlink_dump_start(). Signed-off-by: Pablo Neira Ayuso --- include/net/netns/nftables.h | 2 +- net/netfilter/nf_tables_api.c | 30 +++++++++++++++++++++++------- 2 files changed, 24 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/netns/nftables.h b/include/net/netns/nftables.h index 26a394cb91a8..eee608b12cc9 100644 --- a/include/net/netns/nftables.h +++ b/include/net/netns/nftables.h @@ -13,8 +13,8 @@ struct netns_nftables { struct nft_af_info *inet; struct nft_af_info *arp; struct nft_af_info *bridge; + unsigned int base_seq; u8 gencursor; - u8 genctr; }; #endif diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index a27a7c56e7c3..ac03d748360e 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -278,6 +278,8 @@ static int nf_tables_dump_tables(struct sk_buff *skb, int family = nfmsg->nfgen_family; rcu_read_lock(); + cb->seq = net->nft.base_seq; + list_for_each_entry_rcu(afi, &net->nft.af_info, list) { if (family != NFPROTO_UNSPEC && family != afi->family) continue; @@ -295,6 +297,8 @@ static int nf_tables_dump_tables(struct sk_buff *skb, NLM_F_MULTI, afi->family, table) < 0) goto done; + + nl_dump_check_consistent(cb, nlmsg_hdr(skb)); cont: idx++; } @@ -767,6 +771,8 @@ static int nf_tables_dump_chains(struct sk_buff *skb, int family = nfmsg->nfgen_family; rcu_read_lock(); + cb->seq = net->nft.base_seq; + list_for_each_entry_rcu(afi, &net->nft.af_info, list) { if (family != NFPROTO_UNSPEC && family != afi->family) continue; @@ -784,6 +790,8 @@ static int nf_tables_dump_chains(struct sk_buff *skb, NLM_F_MULTI, afi->family, table, chain) < 0) goto done; + + nl_dump_check_consistent(cb, nlmsg_hdr(skb)); cont: idx++; } @@ -1555,10 +1563,10 @@ static int nf_tables_dump_rules(struct sk_buff *skb, unsigned int idx = 0, s_idx = cb->args[0]; struct net *net = sock_net(skb->sk); int family = nfmsg->nfgen_family; - u8 genctr = ACCESS_ONCE(net->nft.genctr); - u8 gencursor = ACCESS_ONCE(net->nft.gencursor); rcu_read_lock(); + cb->seq = net->nft.base_seq; + list_for_each_entry_rcu(afi, &net->nft.af_info, list) { if (family != NFPROTO_UNSPEC && family != afi->family) continue; @@ -1579,6 +1587,8 @@ static int nf_tables_dump_rules(struct sk_buff *skb, NLM_F_MULTI | NLM_F_APPEND, afi->family, table, chain, rule) < 0) goto done; + + nl_dump_check_consistent(cb, nlmsg_hdr(skb)); cont: idx++; } @@ -1588,10 +1598,6 @@ cont: done: rcu_read_unlock(); - /* Invalidate this dump, a transition to the new generation happened */ - if (gencursor != net->nft.gencursor || genctr != net->nft.genctr) - return -EBUSY; - cb->args[0] = idx; return skb->len; } @@ -2244,6 +2250,8 @@ static int nf_tables_dump_sets_table(struct nft_ctx *ctx, struct sk_buff *skb, return skb->len; rcu_read_lock(); + cb->seq = ctx->net->nft.base_seq; + list_for_each_entry_rcu(set, &ctx->table->sets, list) { if (idx < s_idx) goto cont; @@ -2252,6 +2260,7 @@ static int nf_tables_dump_sets_table(struct nft_ctx *ctx, struct sk_buff *skb, cb->args[0] = idx; goto done; } + nl_dump_check_consistent(cb, nlmsg_hdr(skb)); cont: idx++; } @@ -2272,6 +2281,8 @@ static int nf_tables_dump_sets_family(struct nft_ctx *ctx, struct sk_buff *skb, return skb->len; rcu_read_lock(); + cb->seq = ctx->net->nft.base_seq; + list_for_each_entry_rcu(table, &ctx->afi->tables, list) { if (cur_table) { if (cur_table != table) @@ -2290,6 +2301,7 @@ static int nf_tables_dump_sets_family(struct nft_ctx *ctx, struct sk_buff *skb, cb->args[2] = (unsigned long) table; goto done; } + nl_dump_check_consistent(cb, nlmsg_hdr(skb)); cont: idx++; } @@ -2314,6 +2326,8 @@ static int nf_tables_dump_sets_all(struct nft_ctx *ctx, struct sk_buff *skb, return skb->len; rcu_read_lock(); + cb->seq = net->nft.base_seq; + list_for_each_entry_rcu(afi, &net->nft.af_info, list) { if (cur_family) { if (afi->family != cur_family) @@ -2344,6 +2358,7 @@ static int nf_tables_dump_sets_all(struct nft_ctx *ctx, struct sk_buff *skb, cb->args[3] = afi->family; goto done; } + nl_dump_check_consistent(cb, nlmsg_hdr(skb)); cont: idx++; } @@ -3361,7 +3376,7 @@ static int nf_tables_commit(struct sk_buff *skb) struct nft_set *set; /* Bump generation counter, invalidate any dump in progress */ - net->nft.genctr++; + while (++net->nft.base_seq == 0); /* A new generation has just started */ net->nft.gencursor = gencursor_next(net); @@ -3966,6 +3981,7 @@ static int nf_tables_init_net(struct net *net) { INIT_LIST_HEAD(&net->nft.af_info); INIT_LIST_HEAD(&net->nft.commit_list); + net->nft.base_seq = 1; return 0; } -- cgit v1.2.3 From ce355e209feb030945dae4c358c02f29a84f3f8b Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 9 Jul 2014 15:14:06 +0200 Subject: netfilter: nf_tables: 64bit stats need some extra synchronization Use generic u64_stats_sync infrastructure to get proper 64bit stats, even on 32bit arches, at no extra cost for 64bit arches. Without this fix, 32bit arches can have some wrong counters at the time the carry is propagated into upper word. Signed-off-by: Eric Dumazet Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 6 ++++-- net/netfilter/nf_tables_api.c | 15 +++++++++++---- net/netfilter/nf_tables_core.c | 10 ++++++---- 3 files changed, 21 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 713b0b88bd5a..c4d86198d3d6 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #define NFT_JUMP_STACK_SIZE 16 @@ -528,8 +529,9 @@ enum nft_chain_type { }; struct nft_stats { - u64 bytes; - u64 pkts; + u64 bytes; + u64 pkts; + struct u64_stats_sync syncp; }; #define NFT_HOOK_OPS_MAX 2 diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index ac03d748360e..8746ff9a8357 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -644,13 +644,20 @@ static int nft_dump_stats(struct sk_buff *skb, struct nft_stats __percpu *stats) { struct nft_stats *cpu_stats, total; struct nlattr *nest; + unsigned int seq; + u64 pkts, bytes; int cpu; memset(&total, 0, sizeof(total)); for_each_possible_cpu(cpu) { cpu_stats = per_cpu_ptr(stats, cpu); - total.pkts += cpu_stats->pkts; - total.bytes += cpu_stats->bytes; + do { + seq = u64_stats_fetch_begin_irq(&cpu_stats->syncp); + pkts = cpu_stats->pkts; + bytes = cpu_stats->bytes; + } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, seq)); + total.pkts += pkts; + total.bytes += bytes; } nest = nla_nest_start(skb, NFTA_CHAIN_COUNTERS); if (nest == NULL) @@ -875,7 +882,7 @@ static struct nft_stats __percpu *nft_stats_alloc(const struct nlattr *attr) if (!tb[NFTA_COUNTER_BYTES] || !tb[NFTA_COUNTER_PACKETS]) return ERR_PTR(-EINVAL); - newstats = alloc_percpu(struct nft_stats); + newstats = netdev_alloc_pcpu_stats(struct nft_stats); if (newstats == NULL) return ERR_PTR(-ENOMEM); @@ -1091,7 +1098,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, } basechain->stats = stats; } else { - stats = alloc_percpu(struct nft_stats); + stats = netdev_alloc_pcpu_stats(struct nft_stats); if (IS_ERR(stats)) { module_put(type->owner); kfree(basechain); diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c index 345acfb1720b..3b90eb2b2c55 100644 --- a/net/netfilter/nf_tables_core.c +++ b/net/netfilter/nf_tables_core.c @@ -109,7 +109,7 @@ nft_do_chain(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops) struct nft_data data[NFT_REG_MAX + 1]; unsigned int stackptr = 0; struct nft_jumpstack jumpstack[NFT_JUMP_STACK_SIZE]; - struct nft_stats __percpu *stats; + struct nft_stats *stats; int rulenum; /* * Cache cursor to avoid problems in case that the cursor is updated @@ -205,9 +205,11 @@ next_rule: nft_trace_packet(pkt, basechain, -1, NFT_TRACE_POLICY); rcu_read_lock_bh(); - stats = rcu_dereference(nft_base_chain(basechain)->stats); - __this_cpu_inc(stats->pkts); - __this_cpu_add(stats->bytes, pkt->skb->len); + stats = this_cpu_ptr(rcu_dereference(nft_base_chain(basechain)->stats)); + u64_stats_update_begin(&stats->syncp); + stats->pkts++; + stats->bytes += pkt->skb->len; + u64_stats_update_end(&stats->syncp); rcu_read_unlock_bh(); return nft_base_chain(basechain)->policy; -- cgit v1.2.3 From d788cbd3f9065d829351746f94417d469f14eaaf Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Tue, 1 Jul 2014 06:32:27 +0900 Subject: ASoC: samsung: remove s5pc100 related codes This patch removes s5pc100 related codes in . Signed-off-by: Kukjin Kim Signed-off-by: Mark Brown --- include/linux/platform_data/asoc-s3c.h | 9 --------- 1 file changed, 9 deletions(-) (limited to 'include') diff --git a/include/linux/platform_data/asoc-s3c.h b/include/linux/platform_data/asoc-s3c.h index 709c6f7e2f8c..a6591c693ebb 100644 --- a/include/linux/platform_data/asoc-s3c.h +++ b/include/linux/platform_data/asoc-s3c.h @@ -15,15 +15,6 @@ #define S3C64XX_AC97_GPE 1 extern void s3c64xx_ac97_setup_gpio(int); -/* - * The machine init code calls s5p*_spdif_setup_gpio with - * one of these defines in order to select appropriate bank - * of GPIO for S/PDIF pins - */ -#define S5PC100_SPDIF_GPD 0 -#define S5PC100_SPDIF_GPG3 1 -extern void s5pc100_spdif_setup_gpio(int); - struct samsung_i2s { /* If the Primary DAI has 5.1 Channels */ #define QUIRK_PRI_6CHAN (1 << 0) -- cgit v1.2.3 From 1028a37daa148cc6cf85aa2aecb4390ddf1e1e56 Mon Sep 17 00:00:00 2001 From: James Ban Date: Mon, 14 Jul 2014 13:48:45 +0900 Subject: regulator: da9211: new regulator driver This is the driver for the Dialog DA9211 Multi-phase 12A DC-DC Buck Converter regulator. It communicates via an I2C bus to the device. Signed-off-by: James Ban Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 10 + drivers/regulator/Makefile | 1 + drivers/regulator/da9211-regulator.c | 388 +++++++++++++++++++++++++++++++++++ drivers/regulator/da9211-regulator.h | 271 ++++++++++++++++++++++++ include/linux/regulator/da9211.h | 32 +++ 5 files changed, 702 insertions(+) create mode 100644 drivers/regulator/da9211-regulator.c create mode 100644 drivers/regulator/da9211-regulator.h create mode 100644 include/linux/regulator/da9211.h (limited to 'include') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 789eb46090e3..f5040fc49a3a 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -198,6 +198,16 @@ config REGULATOR_DA9210 converter 12A DC-DC Buck controlled through an I2C interface. +config REGULATOR_DA9211 + tristate "Dialog Semiconductor DA9211/DA9212 regulator" + depends on I2C + select REGMAP_I2C + help + Say y here to support for the Dialog Semiconductor DA9211/DA9212. + The DA9211/DA9212 is a multi-phase synchronous step down + converter 12A DC-DC Buck controlled through an I2C + interface. + config REGULATOR_DBX500_PRCMU bool diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index d461110f4463..aa4a6aa7b558 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o obj-$(CONFIG_REGULATOR_DA9055) += da9055-regulator.o obj-$(CONFIG_REGULATOR_DA9063) += da9063-regulator.o obj-$(CONFIG_REGULATOR_DA9210) += da9210-regulator.o +obj-$(CONFIG_REGULATOR_DA9211) += da9211-regulator.o obj-$(CONFIG_REGULATOR_DBX500_PRCMU) += dbx500-prcmu.o obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o obj-$(CONFIG_REGULATOR_FAN53555) += fan53555.o diff --git a/drivers/regulator/da9211-regulator.c b/drivers/regulator/da9211-regulator.c new file mode 100644 index 000000000000..bd1850658a3f --- /dev/null +++ b/drivers/regulator/da9211-regulator.c @@ -0,0 +1,388 @@ +/* + * da9211-regulator.c - Regulator device driver for DA9211 + * Copyright (C) 2014 Dialog Semiconductor Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "da9211-regulator.h" + +#define DA9211_BUCK_MODE_SLEEP 1 +#define DA9211_BUCK_MODE_SYNC 2 +#define DA9211_BUCK_MODE_AUTO 3 + +/* DA9211 REGULATOR IDs */ +#define DA9211_ID_BUCKA 0 +#define DA9211_ID_BUCKB 1 + +struct da9211 { + struct device *dev; + struct regmap *regmap; + struct da9211_pdata *pdata; + struct regulator_dev *rdev[DA9211_MAX_REGULATORS]; + int num_regulator; + int chip_irq; +}; + +static const struct regmap_range_cfg da9211_regmap_range[] = { + { + .selector_reg = DA9211_REG_PAGE_CON, + .selector_mask = DA9211_REG_PAGE_MASK, + .selector_shift = DA9211_REG_PAGE_SHIFT, + .window_start = 0, + .window_len = 256, + .range_min = 0, + .range_max = 2*256, + }, +}; + +static const struct regmap_config da9211_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 2 * 256, + .ranges = da9211_regmap_range, + .num_ranges = ARRAY_SIZE(da9211_regmap_range), +}; + +/* Default limits measured in millivolts and milliamps */ +#define DA9211_MIN_MV 300 +#define DA9211_MAX_MV 1570 +#define DA9211_STEP_MV 10 + +/* Current limits for buck (uA) indices corresponds with register values */ +static const int da9211_current_limits[] = { + 2000000, 2200000, 2400000, 2600000, 2800000, 3000000, 3200000, 3400000, + 3600000, 3800000, 4000000, 4200000, 4400000, 4600000, 4800000, 5000000 +}; + +static unsigned int da9211_buck_get_mode(struct regulator_dev *rdev) +{ + int id = rdev_get_id(rdev); + struct da9211 *chip = rdev_get_drvdata(rdev); + unsigned int data; + int ret, mode = 0; + + ret = regmap_read(chip->regmap, DA9211_REG_BUCKA_CONF+id, &data); + if (ret < 0) + return ret; + + switch (data & 0x03) { + case DA9211_BUCK_MODE_SYNC: + mode = REGULATOR_MODE_FAST; + break; + case DA9211_BUCK_MODE_AUTO: + mode = REGULATOR_MODE_NORMAL; + break; + case DA9211_BUCK_MODE_SLEEP: + mode = REGULATOR_MODE_STANDBY; + break; + } + + return mode; +} + +static int da9211_buck_set_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + int id = rdev_get_id(rdev); + struct da9211 *chip = rdev_get_drvdata(rdev); + int val = 0; + + switch (mode) { + case REGULATOR_MODE_FAST: + val = DA9211_BUCK_MODE_SYNC; + break; + case REGULATOR_MODE_NORMAL: + val = DA9211_BUCK_MODE_AUTO; + break; + case REGULATOR_MODE_STANDBY: + val = DA9211_BUCK_MODE_SLEEP; + break; + } + + return regmap_update_bits(chip->regmap, DA9211_REG_BUCKA_CONF+id, + 0x03, val); +} + +static int da9211_set_current_limit(struct regulator_dev *rdev, int min, + int max) +{ + int id = rdev_get_id(rdev); + struct da9211 *chip = rdev_get_drvdata(rdev); + int i; + + /* search for closest to maximum */ + for (i = ARRAY_SIZE(da9211_current_limits)-1; i >= 0; i--) { + if (min <= da9211_current_limits[i] && + max >= da9211_current_limits[i]) { + return regmap_update_bits(chip->regmap, + DA9211_REG_BUCK_ILIM, + (0x0F << id*4), (i << id*4)); + } + } + + return -EINVAL; +} + +static int da9211_get_current_limit(struct regulator_dev *rdev) +{ + int id = rdev_get_id(rdev); + struct da9211 *chip = rdev_get_drvdata(rdev); + unsigned int data; + int ret; + + ret = regmap_read(chip->regmap, DA9211_REG_BUCK_ILIM, &data); + if (ret < 0) + return ret; + + /* select one of 16 values: 0000 (2000mA) to 1111 (5000mA) */ + data = (data >> id*4) & 0x0F; + return da9211_current_limits[data]; +} + +static struct regulator_ops da9211_buck_ops = { + .get_mode = da9211_buck_get_mode, + .set_mode = da9211_buck_set_mode, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .set_current_limit = da9211_set_current_limit, + .get_current_limit = da9211_get_current_limit, +}; + +#define DA9211_BUCK(_id) \ +{\ + .name = #_id,\ + .ops = &da9211_buck_ops,\ + .type = REGULATOR_VOLTAGE,\ + .id = DA9211_ID_##_id,\ + .n_voltages = (DA9211_MAX_MV - DA9211_MIN_MV) / DA9211_STEP_MV + 1,\ + .min_uV = (DA9211_MIN_MV * 1000),\ + .uV_step = (DA9211_STEP_MV * 1000),\ + .enable_reg = DA9211_REG_BUCKA_CONT + DA9211_ID_##_id,\ + .enable_mask = DA9211_BUCKA_EN,\ + .vsel_reg = DA9211_REG_VBUCKA_A + DA9211_ID_##_id * 2,\ + .vsel_mask = DA9211_VBUCK_MASK,\ + .owner = THIS_MODULE,\ +} + +static struct regulator_desc da9211_regulators[] = { + DA9211_BUCK(BUCKA), + DA9211_BUCK(BUCKB), +}; + +static irqreturn_t da9211_irq_handler(int irq, void *data) +{ + struct da9211 *chip = data; + int reg_val, err, ret = IRQ_NONE; + + err = regmap_read(chip->regmap, DA9211_REG_EVENT_B, ®_val); + if (err < 0) + goto error_i2c; + + if (reg_val & DA9211_E_OV_CURR_A) { + regulator_notifier_call_chain(chip->rdev[0], + REGULATOR_EVENT_OVER_CURRENT, + rdev_get_drvdata(chip->rdev[0])); + + err = regmap_write(chip->regmap, DA9211_REG_EVENT_B, + DA9211_E_OV_CURR_A); + if (err < 0) + goto error_i2c; + + ret = IRQ_HANDLED; + } + + if (reg_val & DA9211_E_OV_CURR_B) { + regulator_notifier_call_chain(chip->rdev[1], + REGULATOR_EVENT_OVER_CURRENT, + rdev_get_drvdata(chip->rdev[1])); + + err = regmap_write(chip->regmap, DA9211_REG_EVENT_B, + DA9211_E_OV_CURR_B); + if (err < 0) + goto error_i2c; + + ret = IRQ_HANDLED; + } + + return ret; + +error_i2c: + dev_err(chip->dev, "I2C error : %d\n", err); + return IRQ_NONE; +} + +static int da9211_regulator_init(struct da9211 *chip) +{ + struct regulator_config config = { }; + int i, ret; + unsigned int data; + + ret = regmap_read(chip->regmap, DA9211_REG_CONFIG_E, &data); + if (ret < 0) { + dev_err(chip->dev, "Failed to read CONTROL_E reg: %d\n", ret); + goto err; + } + + data &= DA9211_SLAVE_SEL; + /* If configuration for 1/2 bucks is different between platform data + * and the register, driver should exit. + */ + if ((chip->pdata->num_buck == 2 && data == 0x40) + || (chip->pdata->num_buck == 1 && data == 0x00)) { + if (data == 0) + chip->num_regulator = 1; + else + chip->num_regulator = 2; + } else { + ret = -EINVAL; + dev_err(chip->dev, "Configuration is mismatched\n"); + goto err; + } + + for (i = 0; i < chip->num_regulator; i++) { + if (chip->pdata) + config.init_data = + &(chip->pdata->init_data[i]); + + config.dev = chip->dev; + config.driver_data = chip; + config.regmap = chip->regmap; + + chip->rdev[i] = devm_regulator_register(chip->dev, + &da9211_regulators[i], &config); + if (IS_ERR(chip->rdev[i])) { + dev_err(chip->dev, + "Failed to register DA9211 regulator\n"); + ret = PTR_ERR(chip->rdev[i]); + goto err_regulator; + } + + if (chip->chip_irq != 0) { + ret = regmap_update_bits(chip->regmap, + DA9211_REG_MASK_B, DA9211_M_OV_CURR_A << i, 1); + if (ret < 0) { + dev_err(chip->dev, + "Failed to update mask reg: %d\n", ret); + goto err_regulator; + } + } + } + + return 0; + +err_regulator: + while (--i >= 0) + devm_regulator_unregister(chip->dev, chip->rdev[i]); +err: + return ret; +} +/* + * I2C driver interface functions + */ +static int da9211_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct da9211 *chip; + int error, ret; + + chip = devm_kzalloc(&i2c->dev, sizeof(struct da9211), GFP_KERNEL); + + chip->dev = &i2c->dev; + chip->regmap = devm_regmap_init_i2c(i2c, &da9211_regmap_config); + if (IS_ERR(chip->regmap)) { + error = PTR_ERR(chip->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + error); + return error; + } + + i2c_set_clientdata(i2c, chip); + + chip->pdata = i2c->dev.platform_data; + if (!chip->pdata) { + dev_err(&i2c->dev, "No platform init data supplied\n"); + return -ENODEV; + } + + chip->chip_irq = i2c->irq; + + if (chip->chip_irq != 0) { + ret = devm_request_threaded_irq(chip->dev, chip->chip_irq, NULL, + da9211_irq_handler, + IRQF_TRIGGER_LOW|IRQF_ONESHOT, + "da9211", chip); + if (ret != 0) { + dev_err(chip->dev, "Failed to request IRQ: %d\n", + chip->chip_irq); + return ret; + } + } else { + dev_warn(chip->dev, "No IRQ configured\n"); + } + + ret = da9211_regulator_init(chip); + + if (ret < 0) + dev_err(&i2c->dev, "Failed to initialize regulator: %d\n", ret); + + return ret; +} + +static int da9211_i2c_remove(struct i2c_client *i2c) +{ + struct da9211 *chip = i2c_get_clientdata(i2c); + int i; + + for (i = 0; i < chip->num_regulator; i++) + devm_regulator_unregister(chip->dev, chip->rdev[i]); + + return 0; +} + +static const struct i2c_device_id da9211_i2c_id[] = { + {"da9211", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, da9211_i2c_id); + +static struct i2c_driver da9211_regulator_driver = { + .driver = { + .name = "da9211", + .owner = THIS_MODULE, + }, + .probe = da9211_i2c_probe, + .remove = da9211_i2c_remove, + .id_table = da9211_i2c_id, +}; + +module_i2c_driver(da9211_regulator_driver); + +MODULE_AUTHOR("James Ban "); +MODULE_DESCRIPTION("Regulator device driver for Dialog DA9211"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/da9211-regulator.h b/drivers/regulator/da9211-regulator.h new file mode 100644 index 000000000000..88b1769e8058 --- /dev/null +++ b/drivers/regulator/da9211-regulator.h @@ -0,0 +1,271 @@ +/* + * da9211-regulator.h - Regulator definitions for DA9211 + * Copyright (C) 2014 Dialog Semiconductor Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + */ + +#ifndef __DA9211_REGISTERS_H__ +#define __DA9211_REGISTERS_H__ + +/* Page selection */ +#define DA9211_REG_PAGE_CON 0x00 + +/* System Control and Event Registers */ +#define DA9211_REG_STATUS_A 0x50 +#define DA9211_REG_STATUS_B 0x51 +#define DA9211_REG_EVENT_A 0x52 +#define DA9211_REG_EVENT_B 0x53 +#define DA9211_REG_MASK_A 0x54 +#define DA9211_REG_MASK_B 0x55 +#define DA9211_REG_CONTROL_A 0x56 + +/* GPIO Control Registers */ +#define DA9211_REG_GPIO_0_1 0x58 +#define DA9211_REG_GPIO_2_3 0x59 +#define DA9211_REG_GPIO_4 0x5A + +/* Regulator Registers */ +#define DA9211_REG_BUCKA_CONT 0x5D +#define DA9211_REG_BUCKB_CONT 0x5E +#define DA9211_REG_BUCK_ILIM 0xD0 +#define DA9211_REG_BUCKA_CONF 0xD1 +#define DA9211_REG_BUCKB_CONF 0xD2 +#define DA9211_REG_BUCK_CONF 0xD3 +#define DA9211_REG_VBACKA_MAX 0xD5 +#define DA9211_REG_VBACKB_MAX 0xD6 +#define DA9211_REG_VBUCKA_A 0xD7 +#define DA9211_REG_VBUCKA_B 0xD8 +#define DA9211_REG_VBUCKB_A 0xD9 +#define DA9211_REG_VBUCKB_B 0xDA + +/* I2C Interface Settings */ +#define DA9211_REG_INTERFACE 0x105 + +/* BUCK Phase Selection*/ +#define DA9211_REG_CONFIG_E 0x147 + +/* + * Registers bits + */ +/* DA9211_REG_PAGE_CON (addr=0x00) */ +#define DA9211_REG_PAGE_SHIFT 1 +#define DA9211_REG_PAGE_MASK 0x02 +/* On I2C registers 0x00 - 0xFF */ +#define DA9211_REG_PAGE0 0 +/* On I2C registers 0x100 - 0x1FF */ +#define DA9211_REG_PAGE2 2 +#define DA9211_PAGE_WRITE_MODE 0x00 +#define DA9211_REPEAT_WRITE_MODE 0x40 +#define DA9211_PAGE_REVERT 0x80 + +/* DA9211_REG_STATUS_A (addr=0x50) */ +#define DA9211_GPI0 0x01 +#define DA9211_GPI1 0x02 +#define DA9211_GPI2 0x04 +#define DA9211_GPI3 0x08 +#define DA9211_GPI4 0x10 + +/* DA9211_REG_EVENT_A (addr=0x52) */ +#define DA9211_E_GPI0 0x01 +#define DA9211_E_GPI1 0x02 +#define DA9211_E_GPI2 0x04 +#define DA9211_E_GPI3 0x08 +#define DA9211_E_GPI4 0x10 +#define DA9211_E_UVLO_IO 0x40 + +/* DA9211_REG_EVENT_B (addr=0x53) */ +#define DA9211_E_PWRGOOD_A 0x01 +#define DA9211_E_PWRGOOD_B 0x02 +#define DA9211_E_TEMP_WARN 0x04 +#define DA9211_E_TEMP_CRIT 0x08 +#define DA9211_E_OV_CURR_A 0x10 +#define DA9211_E_OV_CURR_B 0x20 + +/* DA9211_REG_MASK_A (addr=0x54) */ +#define DA9211_M_GPI0 0x01 +#define DA9211_M_GPI1 0x02 +#define DA9211_M_GPI2 0x04 +#define DA9211_M_GPI3 0x08 +#define DA9211_M_GPI4 0x10 +#define DA9211_M_UVLO_IO 0x40 + +/* DA9211_REG_MASK_B (addr=0x55) */ +#define DA9211_M_PWRGOOD_A 0x01 +#define DA9211_M_PWRGOOD_B 0x02 +#define DA9211_M_TEMP_WARN 0x04 +#define DA9211_M_TEMP_CRIT 0x08 +#define DA9211_M_OV_CURR_A 0x10 +#define DA9211_M_OV_CURR_B 0x20 + +/* DA9211_REG_CONTROL_A (addr=0x56) */ +#define DA9211_DEBOUNCING_SHIFT 0 +#define DA9211_DEBOUNCING_MASK 0x07 +#define DA9211_SLEW_RATE_SHIFT 3 +#define DA9211_SLEW_RATE_A_MASK 0x18 +#define DA9211_SLEW_RATE_B_SHIFT 5 +#define DA9211_SLEW_RATE_B_MASK 0x60 +#define DA9211_V_LOCK 0x80 + +/* DA9211_REG_GPIO_0_1 (addr=0x58) */ +#define DA9211_GPIO0_PIN_SHIFT 0 +#define DA9211_GPIO0_PIN_MASK 0x03 +#define DA9211_GPIO0_PIN_GPI 0x00 +#define DA9211_GPIO0_PIN_GPO_OD 0x02 +#define DA9211_GPIO0_PIN_GPO 0x03 +#define DA9211_GPIO0_TYPE 0x04 +#define DA9211_GPIO0_TYPE_GPI 0x00 +#define DA9211_GPIO0_TYPE_GPO 0x04 +#define DA9211_GPIO0_MODE 0x08 +#define DA9211_GPIO1_PIN_SHIFT 4 +#define DA9211_GPIO1_PIN_MASK 0x30 +#define DA9211_GPIO1_PIN_GPI 0x00 +#define DA9211_GPIO1_PIN_VERROR 0x10 +#define DA9211_GPIO1_PIN_GPO_OD 0x20 +#define DA9211_GPIO1_PIN_GPO 0x30 +#define DA9211_GPIO1_TYPE_SHIFT 0x40 +#define DA9211_GPIO1_TYPE_GPI 0x00 +#define DA9211_GPIO1_TYPE_GPO 0x40 +#define DA9211_GPIO1_MODE 0x80 + +/* DA9211_REG_GPIO_2_3 (addr=0x59) */ +#define DA9211_GPIO2_PIN_SHIFT 0 +#define DA9211_GPIO2_PIN_MASK 0x03 +#define DA9211_GPIO2_PIN_GPI 0x00 +#define DA9211_GPIO5_PIN_BUCK_CLK 0x10 +#define DA9211_GPIO2_PIN_GPO_OD 0x02 +#define DA9211_GPIO2_PIN_GPO 0x03 +#define DA9211_GPIO2_TYPE 0x04 +#define DA9211_GPIO2_TYPE_GPI 0x00 +#define DA9211_GPIO2_TYPE_GPO 0x04 +#define DA9211_GPIO2_MODE 0x08 +#define DA9211_GPIO3_PIN_SHIFT 4 +#define DA9211_GPIO3_PIN_MASK 0x30 +#define DA9211_GPIO3_PIN_GPI 0x00 +#define DA9211_GPIO3_PIN_IERROR 0x10 +#define DA9211_GPIO3_PIN_GPO_OD 0x20 +#define DA9211_GPIO3_PIN_GPO 0x30 +#define DA9211_GPIO3_TYPE_SHIFT 0x40 +#define DA9211_GPIO3_TYPE_GPI 0x00 +#define DA9211_GPIO3_TYPE_GPO 0x40 +#define DA9211_GPIO3_MODE 0x80 + +/* DA9211_REG_GPIO_4 (addr=0x5A) */ +#define DA9211_GPIO4_PIN_SHIFT 0 +#define DA9211_GPIO4_PIN_MASK 0x03 +#define DA9211_GPIO4_PIN_GPI 0x00 +#define DA9211_GPIO4_PIN_GPO_OD 0x02 +#define DA9211_GPIO4_PIN_GPO 0x03 +#define DA9211_GPIO4_TYPE 0x04 +#define DA9211_GPIO4_TYPE_GPI 0x00 +#define DA9211_GPIO4_TYPE_GPO 0x04 +#define DA9211_GPIO4_MODE 0x08 + +/* DA9211_REG_BUCKA_CONT (addr=0x5D) */ +#define DA9211_BUCKA_EN 0x01 +#define DA9211_BUCKA_GPI_SHIFT 1 +#define DA9211_BUCKA_GPI_MASK 0x06 +#define DA9211_BUCKA_GPI_OFF 0x00 +#define DA9211_BUCKA_GPI_GPIO0 0x02 +#define DA9211_BUCKA_GPI_GPIO1 0x04 +#define DA9211_BUCKA_GPI_GPIO3 0x06 +#define DA9211_BUCKA_PD_DIS 0x08 +#define DA9211_VBUCKA_SEL 0x10 +#define DA9211_VBUCKA_SEL_A 0x00 +#define DA9211_VBUCKA_SEL_B 0x10 +#define DA9211_VBUCKA_GPI_SHIFT 5 +#define DA9211_VBUCKA_GPI_MASK 0x60 +#define DA9211_VBUCKA_GPI_OFF 0x00 +#define DA9211_VBUCKA_GPI_GPIO1 0x20 +#define DA9211_VBUCKA_GPI_GPIO2 0x40 +#define DA9211_VBUCKA_GPI_GPIO4 0x60 + +/* DA9211_REG_BUCKB_CONT (addr=0x5E) */ +#define DA9211_BUCKB_EN 0x01 +#define DA9211_BUCKB_GPI_SHIFT 1 +#define DA9211_BUCKB_GPI_MASK 0x06 +#define DA9211_BUCKB_GPI_OFF 0x00 +#define DA9211_BUCKB_GPI_GPIO0 0x02 +#define DA9211_BUCKB_GPI_GPIO1 0x04 +#define DA9211_BUCKB_GPI_GPIO3 0x06 +#define DA9211_BUCKB_PD_DIS 0x08 +#define DA9211_VBUCKB_SEL 0x10 +#define DA9211_VBUCKB_SEL_A 0x00 +#define DA9211_VBUCKB_SEL_B 0x10 +#define DA9211_VBUCKB_GPI_SHIFT 5 +#define DA9211_VBUCKB_GPI_MASK 0x60 +#define DA9211_VBUCKB_GPI_OFF 0x00 +#define DA9211_VBUCKB_GPI_GPIO1 0x20 +#define DA9211_VBUCKB_GPI_GPIO2 0x40 +#define DA9211_VBUCKB_GPI_GPIO4 0x60 + +/* DA9211_REG_BUCK_ILIM (addr=0xD0) */ +#define DA9211_BUCKA_ILIM_SHIFT 0 +#define DA9211_BUCKA_ILIM_MASK 0x0F +#define DA9211_BUCKB_ILIM_SHIFT 4 +#define DA9211_BUCKB_ILIM_MASK 0xF0 + +/* DA9211_REG_BUCKA_CONF (addr=0xD1) */ +#define DA9211_BUCKA_MODE_SHIFT 0 +#define DA9211_BUCKA_MODE_MASK 0x03 +#define DA9211_BUCKA_MODE_MANUAL 0x00 +#define DA9211_BUCKA_MODE_SLEEP 0x01 +#define DA9211_BUCKA_MODE_SYNC 0x02 +#define DA9211_BUCKA_MODE_AUTO 0x03 +#define DA9211_BUCKA_UP_CTRL_SHIFT 2 +#define DA9211_BUCKA_UP_CTRL_MASK 0x1C +#define DA9211_BUCKA_DOWN_CTRL_SHIFT 5 +#define DA9211_BUCKA_DOWN_CTRL_MASK 0xE0 + +/* DA9211_REG_BUCKB_CONF (addr=0xD2) */ +#define DA9211_BUCKB_MODE_SHIFT 0 +#define DA9211_BUCKB_MODE_MASK 0x03 +#define DA9211_BUCKB_MODE_MANUAL 0x00 +#define DA9211_BUCKB_MODE_SLEEP 0x01 +#define DA9211_BUCKB_MODE_SYNC 0x02 +#define DA9211_BUCKB_MODE_AUTO 0x03 +#define DA9211_BUCKB_UP_CTRL_SHIFT 2 +#define DA9211_BUCKB_UP_CTRL_MASK 0x1C +#define DA9211_BUCKB_DOWN_CTRL_SHIFT 5 +#define DA9211_BUCKB_DOWN_CTRL_MASK 0xE0 + +/* DA9211_REG_BUCK_CONF (addr=0xD3) */ +#define DA9211_PHASE_SEL_A_SHIFT 0 +#define DA9211_PHASE_SEL_A_MASK 0x03 +#define DA9211_PHASE_SEL_B_SHIFT 2 +#define DA9211_PHASE_SEL_B_MASK 0x04 +#define DA9211_PH_SH_EN_A_SHIFT 3 +#define DA9211_PH_SH_EN_A_MASK 0x08 +#define DA9211_PH_SH_EN_B_SHIFT 4 +#define DA9211_PH_SH_EN_B_MASK 0x10 + +/* DA9211_REG_VBUCKA_MAX (addr=0xD5) */ +#define DA9211_VBUCKA_BASE_SHIFT 0 +#define DA9211_VBUCKA_BASE_MASK 0x7F + +/* DA9211_REG_VBUCKB_MAX (addr=0xD6) */ +#define DA9211_VBUCKB_BASE_SHIFT 0 +#define DA9211_VBUCKB_BASE_MASK 0x7F + +/* DA9211_REG_VBUCKA/B_A/B (addr=0xD7/0xD8/0xD9/0xDA) */ +#define DA9211_VBUCK_SHIFT 0 +#define DA9211_VBUCK_MASK 0x7F +#define DA9211_VBUCK_BIAS 0 +#define DA9211_BUCK_SL 0x80 + +/* DA9211_REG_INTERFACE (addr=0x105) */ +#define DA9211_IF_BASE_ADDR_SHIFT 4 +#define DA9211_IF_BASE_ADDR_MASK 0xF0 + +/* DA9211_REG_CONFIG_E (addr=0x147) */ +#define DA9211_SLAVE_SEL 0x40 + +#endif /* __DA9211_REGISTERS_H__ */ diff --git a/include/linux/regulator/da9211.h b/include/linux/regulator/da9211.h new file mode 100644 index 000000000000..0981ce0e72cc --- /dev/null +++ b/include/linux/regulator/da9211.h @@ -0,0 +1,32 @@ +/* + * da9211.h - Regulator device driver for DA9211 + * Copyright (C) 2014 Dialog Semiconductor Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + */ + +#ifndef __LINUX_REGULATOR_DA9211_H +#define __LINUX_REGULATOR_DA9211_H + +#include + +#define DA9211_MAX_REGULATORS 2 + +struct da9211_pdata { + /* + * Number of buck + * 1 : 4 phase 1 buck + * 2 : 2 phase 2 buck + */ + int num_buck; + struct regulator_init_data *init_data; +}; +#endif -- cgit v1.2.3 From 162e371712768248a38646eefa71af38b6e0f8ce Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Fri, 11 Jul 2014 16:42:34 +0100 Subject: x86/xen: safely map and unmap grant frames when in atomic context arch_gnttab_map_frames() and arch_gnttab_unmap_frames() are called in atomic context but were calling alloc_vm_area() which might sleep. Also, if a driver attempts to allocate a grant ref from an interrupt and the table needs expanding, then the CPU may already by in lazy MMU mode and apply_to_page_range() will BUG when it tries to re-enable lazy MMU mode. These two functions are only used in PV guests. Introduce arch_gnttab_init() to allocates the virtual address space in advance. Avoid the use of apply_to_page_range() by using saving and using the array of PTE addresses from the alloc_vm_area() call. N.B. 'alloc_vm_area' pre-allocates the pagetable so there is no need to worry about having to do a PGD/PUD/PMD walk (like apply_to_page_range does) and we can instead do set_pte. Signed-off-by: David Vrabel Signed-off-by: Konrad Rzeszutek Wilk ---- [v2: Add comment about alloc_vm_area] [v3: Fix compile error found by 0-day bot] --- arch/arm/xen/grant-table.c | 5 ++ arch/x86/xen/grant-table.c | 148 ++++++++++++++++++++++++++++----------------- drivers/xen/grant-table.c | 9 ++- include/xen/grant_table.h | 1 + 4 files changed, 105 insertions(+), 58 deletions(-) (limited to 'include') diff --git a/arch/arm/xen/grant-table.c b/arch/arm/xen/grant-table.c index 859a9bb002d5..91cf08ba1e95 100644 --- a/arch/arm/xen/grant-table.c +++ b/arch/arm/xen/grant-table.c @@ -51,3 +51,8 @@ int arch_gnttab_map_status(uint64_t *frames, unsigned long nr_gframes, { return -ENOSYS; } + +int arch_gnttab_init(unsigned long nr_shared, unsigned long nr_status) +{ + return 0; +} diff --git a/arch/x86/xen/grant-table.c b/arch/x86/xen/grant-table.c index c98583588580..ebfa9b2c871d 100644 --- a/arch/x86/xen/grant-table.c +++ b/arch/x86/xen/grant-table.c @@ -36,99 +36,133 @@ #include #include +#include #include #include #include #include +#include #include -static int map_pte_fn(pte_t *pte, struct page *pmd_page, - unsigned long addr, void *data) +static struct gnttab_vm_area { + struct vm_struct *area; + pte_t **ptes; +} gnttab_shared_vm_area, gnttab_status_vm_area; + +int arch_gnttab_map_shared(unsigned long *frames, unsigned long nr_gframes, + unsigned long max_nr_gframes, + void **__shared) { - unsigned long **frames = (unsigned long **)data; + void *shared = *__shared; + unsigned long addr; + unsigned long i; - set_pte_at(&init_mm, addr, pte, mfn_pte((*frames)[0], PAGE_KERNEL)); - (*frames)++; - return 0; -} + if (shared == NULL) + *__shared = shared = gnttab_shared_vm_area.area->addr; -/* - * This function is used to map shared frames to store grant status. It is - * different from map_pte_fn above, the frames type here is uint64_t. - */ -static int map_pte_fn_status(pte_t *pte, struct page *pmd_page, - unsigned long addr, void *data) -{ - uint64_t **frames = (uint64_t **)data; + addr = (unsigned long)shared; + + for (i = 0; i < nr_gframes; i++) { + set_pte_at(&init_mm, addr, gnttab_shared_vm_area.ptes[i], + mfn_pte(frames[i], PAGE_KERNEL)); + addr += PAGE_SIZE; + } - set_pte_at(&init_mm, addr, pte, mfn_pte((*frames)[0], PAGE_KERNEL)); - (*frames)++; return 0; } -static int unmap_pte_fn(pte_t *pte, struct page *pmd_page, - unsigned long addr, void *data) +int arch_gnttab_map_status(uint64_t *frames, unsigned long nr_gframes, + unsigned long max_nr_gframes, + grant_status_t **__shared) { + grant_status_t *shared = *__shared; + unsigned long addr; + unsigned long i; + + if (shared == NULL) + *__shared = shared = gnttab_status_vm_area.area->addr; + + addr = (unsigned long)shared; + + for (i = 0; i < nr_gframes; i++) { + set_pte_at(&init_mm, addr, gnttab_status_vm_area.ptes[i], + mfn_pte(frames[i], PAGE_KERNEL)); + addr += PAGE_SIZE; + } - set_pte_at(&init_mm, addr, pte, __pte(0)); return 0; } -int arch_gnttab_map_shared(unsigned long *frames, unsigned long nr_gframes, - unsigned long max_nr_gframes, - void **__shared) +void arch_gnttab_unmap(void *shared, unsigned long nr_gframes) { - int rc; - void *shared = *__shared; + pte_t **ptes; + unsigned long addr; + unsigned long i; - if (shared == NULL) { - struct vm_struct *area = - alloc_vm_area(PAGE_SIZE * max_nr_gframes, NULL); - BUG_ON(area == NULL); - shared = area->addr; - *__shared = shared; - } + if (shared == gnttab_status_vm_area.area->addr) + ptes = gnttab_status_vm_area.ptes; + else + ptes = gnttab_shared_vm_area.ptes; - rc = apply_to_page_range(&init_mm, (unsigned long)shared, - PAGE_SIZE * nr_gframes, - map_pte_fn, &frames); - return rc; + addr = (unsigned long)shared; + + for (i = 0; i < nr_gframes; i++) { + set_pte_at(&init_mm, addr, ptes[i], __pte(0)); + addr += PAGE_SIZE; + } } -int arch_gnttab_map_status(uint64_t *frames, unsigned long nr_gframes, - unsigned long max_nr_gframes, - grant_status_t **__shared) +static int arch_gnttab_valloc(struct gnttab_vm_area *area, unsigned nr_frames) { - int rc; - grant_status_t *shared = *__shared; + area->ptes = kmalloc(sizeof(pte_t *) * nr_frames, GFP_KERNEL); + if (area->ptes == NULL) + return -ENOMEM; - if (shared == NULL) { - /* No need to pass in PTE as we are going to do it - * in apply_to_page_range anyhow. */ - struct vm_struct *area = - alloc_vm_area(PAGE_SIZE * max_nr_gframes, NULL); - BUG_ON(area == NULL); - shared = area->addr; - *__shared = shared; + area->area = alloc_vm_area(PAGE_SIZE * nr_frames, area->ptes); + if (area->area == NULL) { + kfree(area->ptes); + return -ENOMEM; } - rc = apply_to_page_range(&init_mm, (unsigned long)shared, - PAGE_SIZE * nr_gframes, - map_pte_fn_status, &frames); - return rc; + return 0; } -void arch_gnttab_unmap(void *shared, unsigned long nr_gframes) +static void arch_gnttab_vfree(struct gnttab_vm_area *area) +{ + free_vm_area(area->area); + kfree(area->ptes); +} + +int arch_gnttab_init(unsigned long nr_shared, unsigned long nr_status) { - apply_to_page_range(&init_mm, (unsigned long)shared, - PAGE_SIZE * nr_gframes, unmap_pte_fn, NULL); + int ret; + + if (!xen_pv_domain()) + return 0; + + ret = arch_gnttab_valloc(&gnttab_shared_vm_area, nr_shared); + if (ret < 0) + return ret; + + /* + * Always allocate the space for the status frames in case + * we're migrated to a host with V2 support. + */ + ret = arch_gnttab_valloc(&gnttab_status_vm_area, nr_status); + if (ret < 0) + goto err; + + return 0; + err: + arch_gnttab_vfree(&gnttab_shared_vm_area); + return -ENOMEM; } + #ifdef CONFIG_XEN_PVH #include #include -#include #include static int __init xlated_setup_gnttab_pages(void) { diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index 5d4de88fe5b8..eeba7544f0cd 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c @@ -1195,18 +1195,20 @@ static int gnttab_expand(unsigned int req_entries) int gnttab_init(void) { int i; + unsigned long max_nr_grant_frames; unsigned int max_nr_glist_frames, nr_glist_frames; unsigned int nr_init_grefs; int ret; gnttab_request_version(); + max_nr_grant_frames = gnttab_max_grant_frames(); nr_grant_frames = 1; /* Determine the maximum number of frames required for the * grant reference free list on the current hypervisor. */ BUG_ON(grefs_per_grant_frame == 0); - max_nr_glist_frames = (gnttab_max_grant_frames() * + max_nr_glist_frames = (max_nr_grant_frames * grefs_per_grant_frame / RPP); gnttab_list = kmalloc(max_nr_glist_frames * sizeof(grant_ref_t *), @@ -1223,6 +1225,11 @@ int gnttab_init(void) } } + ret = arch_gnttab_init(max_nr_grant_frames, + nr_status_frames(max_nr_grant_frames)); + if (ret < 0) + goto ini_nomem; + if (gnttab_setup() < 0) { ret = -ENODEV; goto ini_nomem; diff --git a/include/xen/grant_table.h b/include/xen/grant_table.h index a5af2a26d94f..5c1aba154b64 100644 --- a/include/xen/grant_table.h +++ b/include/xen/grant_table.h @@ -170,6 +170,7 @@ gnttab_set_unmap_op(struct gnttab_unmap_grant_ref *unmap, phys_addr_t addr, unmap->dev_bus_addr = 0; } +int arch_gnttab_init(unsigned long nr_shared, unsigned long nr_status); int arch_gnttab_map_shared(xen_pfn_t *frames, unsigned long nr_gframes, unsigned long max_nr_gframes, void **__shared); -- cgit v1.2.3 From 438b33c7145ca8a5131a30c36d8f59bce119a19a Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Wed, 2 Jul 2014 11:25:29 +0100 Subject: xen/grant-table: remove support for V2 tables Since 11c7ff17c9b6dbf3a4e4f36be30ad531a6cf0ec9 (xen/grant-table: Force to use v1 of grants.) the code for V2 grant tables is not used. Signed-off-by: David Vrabel Signed-off-by: Konrad Rzeszutek Wilk --- arch/arm/xen/grant-table.c | 9 +- arch/x86/xen/grant-table.c | 60 +-------- drivers/xen/grant-table.c | 309 +-------------------------------------------- include/xen/grant_table.h | 30 +---- 4 files changed, 13 insertions(+), 395 deletions(-) (limited to 'include') diff --git a/arch/arm/xen/grant-table.c b/arch/arm/xen/grant-table.c index 91cf08ba1e95..e43791829ace 100644 --- a/arch/arm/xen/grant-table.c +++ b/arch/arm/xen/grant-table.c @@ -45,14 +45,7 @@ void arch_gnttab_unmap(void *shared, unsigned long nr_gframes) return; } -int arch_gnttab_map_status(uint64_t *frames, unsigned long nr_gframes, - unsigned long max_nr_gframes, - grant_status_t **__shared) -{ - return -ENOSYS; -} - -int arch_gnttab_init(unsigned long nr_shared, unsigned long nr_status) +int arch_gnttab_init(unsigned long nr_shared) { return 0; } diff --git a/arch/x86/xen/grant-table.c b/arch/x86/xen/grant-table.c index ebfa9b2c871d..c0413046483a 100644 --- a/arch/x86/xen/grant-table.c +++ b/arch/x86/xen/grant-table.c @@ -49,7 +49,7 @@ static struct gnttab_vm_area { struct vm_struct *area; pte_t **ptes; -} gnttab_shared_vm_area, gnttab_status_vm_area; +} gnttab_shared_vm_area; int arch_gnttab_map_shared(unsigned long *frames, unsigned long nr_gframes, unsigned long max_nr_gframes, @@ -73,43 +73,16 @@ int arch_gnttab_map_shared(unsigned long *frames, unsigned long nr_gframes, return 0; } -int arch_gnttab_map_status(uint64_t *frames, unsigned long nr_gframes, - unsigned long max_nr_gframes, - grant_status_t **__shared) -{ - grant_status_t *shared = *__shared; - unsigned long addr; - unsigned long i; - - if (shared == NULL) - *__shared = shared = gnttab_status_vm_area.area->addr; - - addr = (unsigned long)shared; - - for (i = 0; i < nr_gframes; i++) { - set_pte_at(&init_mm, addr, gnttab_status_vm_area.ptes[i], - mfn_pte(frames[i], PAGE_KERNEL)); - addr += PAGE_SIZE; - } - - return 0; -} - void arch_gnttab_unmap(void *shared, unsigned long nr_gframes) { - pte_t **ptes; unsigned long addr; unsigned long i; - if (shared == gnttab_status_vm_area.area->addr) - ptes = gnttab_status_vm_area.ptes; - else - ptes = gnttab_shared_vm_area.ptes; - addr = (unsigned long)shared; for (i = 0; i < nr_gframes; i++) { - set_pte_at(&init_mm, addr, ptes[i], __pte(0)); + set_pte_at(&init_mm, addr, gnttab_shared_vm_area.ptes[i], + __pte(0)); addr += PAGE_SIZE; } } @@ -129,35 +102,12 @@ static int arch_gnttab_valloc(struct gnttab_vm_area *area, unsigned nr_frames) return 0; } -static void arch_gnttab_vfree(struct gnttab_vm_area *area) +int arch_gnttab_init(unsigned long nr_shared) { - free_vm_area(area->area); - kfree(area->ptes); -} - -int arch_gnttab_init(unsigned long nr_shared, unsigned long nr_status) -{ - int ret; - if (!xen_pv_domain()) return 0; - ret = arch_gnttab_valloc(&gnttab_shared_vm_area, nr_shared); - if (ret < 0) - return ret; - - /* - * Always allocate the space for the status frames in case - * we're migrated to a host with V2 support. - */ - ret = arch_gnttab_valloc(&gnttab_status_vm_area, nr_status); - if (ret < 0) - goto err; - - return 0; - err: - arch_gnttab_vfree(&gnttab_shared_vm_area); - return -ENOMEM; + return arch_gnttab_valloc(&gnttab_shared_vm_area, nr_shared); } #ifdef CONFIG_XEN_PVH diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index eeba7544f0cd..c254ae036f18 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c @@ -69,7 +69,6 @@ struct grant_frames xen_auto_xlat_grant_frames; static union { struct grant_entry_v1 *v1; - union grant_entry_v2 *v2; void *addr; } gnttab_shared; @@ -120,36 +119,10 @@ struct gnttab_ops { * by bit operations. */ int (*query_foreign_access)(grant_ref_t ref); - /* - * Grant a domain to access a range of bytes within the page referred by - * an available grant entry. Ref parameter is reference of a grant entry - * which will be sub-page accessed, domid is id of grantee domain, frame - * is frame address of subpage grant, flags is grant type and flag - * information, page_off is offset of the range of bytes, and length is - * length of bytes to be accessed. - */ - void (*update_subpage_entry)(grant_ref_t ref, domid_t domid, - unsigned long frame, int flags, - unsigned page_off, unsigned length); - /* - * Redirect an available grant entry on domain A to another grant - * reference of domain B, then allow domain C to use grant reference - * of domain B transitively. Ref parameter is an available grant entry - * reference on domain A, domid is id of domain C which accesses grant - * entry transitively, flags is grant type and flag information, - * trans_domid is id of domain B whose grant entry is finally accessed - * transitively, trans_gref is grant entry transitive reference of - * domain B. - */ - void (*update_trans_entry)(grant_ref_t ref, domid_t domid, int flags, - domid_t trans_domid, grant_ref_t trans_gref); }; static struct gnttab_ops *gnttab_interface; -/*This reflects status of grant entries, so act as a global value*/ -static grant_status_t *grstatus; - static int grant_table_version; static int grefs_per_grant_frame; @@ -231,7 +204,7 @@ static void put_free_entry(grant_ref_t ref) } /* - * Following applies to gnttab_update_entry_v1 and gnttab_update_entry_v2. + * Following applies to gnttab_update_entry_v1. * Introducing a valid entry into the grant table: * 1. Write ent->domid. * 2. Write ent->frame: @@ -250,15 +223,6 @@ static void gnttab_update_entry_v1(grant_ref_t ref, domid_t domid, gnttab_shared.v1[ref].flags = flags; } -static void gnttab_update_entry_v2(grant_ref_t ref, domid_t domid, - unsigned long frame, unsigned flags) -{ - gnttab_shared.v2[ref].hdr.domid = domid; - gnttab_shared.v2[ref].full_page.frame = frame; - wmb(); - gnttab_shared.v2[ref].hdr.flags = GTF_permit_access | flags; -} - /* * Public grant-issuing interface functions */ @@ -285,132 +249,11 @@ int gnttab_grant_foreign_access(domid_t domid, unsigned long frame, } EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access); -static void gnttab_update_subpage_entry_v2(grant_ref_t ref, domid_t domid, - unsigned long frame, int flags, - unsigned page_off, unsigned length) -{ - gnttab_shared.v2[ref].sub_page.frame = frame; - gnttab_shared.v2[ref].sub_page.page_off = page_off; - gnttab_shared.v2[ref].sub_page.length = length; - gnttab_shared.v2[ref].hdr.domid = domid; - wmb(); - gnttab_shared.v2[ref].hdr.flags = - GTF_permit_access | GTF_sub_page | flags; -} - -int gnttab_grant_foreign_access_subpage_ref(grant_ref_t ref, domid_t domid, - unsigned long frame, int flags, - unsigned page_off, - unsigned length) -{ - if (flags & (GTF_accept_transfer | GTF_reading | - GTF_writing | GTF_transitive)) - return -EPERM; - - if (gnttab_interface->update_subpage_entry == NULL) - return -ENOSYS; - - gnttab_interface->update_subpage_entry(ref, domid, frame, flags, - page_off, length); - - return 0; -} -EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_subpage_ref); - -int gnttab_grant_foreign_access_subpage(domid_t domid, unsigned long frame, - int flags, unsigned page_off, - unsigned length) -{ - int ref, rc; - - ref = get_free_entries(1); - if (unlikely(ref < 0)) - return -ENOSPC; - - rc = gnttab_grant_foreign_access_subpage_ref(ref, domid, frame, flags, - page_off, length); - if (rc < 0) { - put_free_entry(ref); - return rc; - } - - return ref; -} -EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_subpage); - -bool gnttab_subpage_grants_available(void) -{ - return gnttab_interface->update_subpage_entry != NULL; -} -EXPORT_SYMBOL_GPL(gnttab_subpage_grants_available); - -static void gnttab_update_trans_entry_v2(grant_ref_t ref, domid_t domid, - int flags, domid_t trans_domid, - grant_ref_t trans_gref) -{ - gnttab_shared.v2[ref].transitive.trans_domid = trans_domid; - gnttab_shared.v2[ref].transitive.gref = trans_gref; - gnttab_shared.v2[ref].hdr.domid = domid; - wmb(); - gnttab_shared.v2[ref].hdr.flags = - GTF_permit_access | GTF_transitive | flags; -} - -int gnttab_grant_foreign_access_trans_ref(grant_ref_t ref, domid_t domid, - int flags, domid_t trans_domid, - grant_ref_t trans_gref) -{ - if (flags & (GTF_accept_transfer | GTF_reading | - GTF_writing | GTF_sub_page)) - return -EPERM; - - if (gnttab_interface->update_trans_entry == NULL) - return -ENOSYS; - - gnttab_interface->update_trans_entry(ref, domid, flags, trans_domid, - trans_gref); - - return 0; -} -EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_trans_ref); - -int gnttab_grant_foreign_access_trans(domid_t domid, int flags, - domid_t trans_domid, - grant_ref_t trans_gref) -{ - int ref, rc; - - ref = get_free_entries(1); - if (unlikely(ref < 0)) - return -ENOSPC; - - rc = gnttab_grant_foreign_access_trans_ref(ref, domid, flags, - trans_domid, trans_gref); - if (rc < 0) { - put_free_entry(ref); - return rc; - } - - return ref; -} -EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_trans); - -bool gnttab_trans_grants_available(void) -{ - return gnttab_interface->update_trans_entry != NULL; -} -EXPORT_SYMBOL_GPL(gnttab_trans_grants_available); - static int gnttab_query_foreign_access_v1(grant_ref_t ref) { return gnttab_shared.v1[ref].flags & (GTF_reading|GTF_writing); } -static int gnttab_query_foreign_access_v2(grant_ref_t ref) -{ - return grstatus[ref] & (GTF_reading|GTF_writing); -} - int gnttab_query_foreign_access(grant_ref_t ref) { return gnttab_interface->query_foreign_access(ref); @@ -433,29 +276,6 @@ static int gnttab_end_foreign_access_ref_v1(grant_ref_t ref, int readonly) return 1; } -static int gnttab_end_foreign_access_ref_v2(grant_ref_t ref, int readonly) -{ - gnttab_shared.v2[ref].hdr.flags = 0; - mb(); - if (grstatus[ref] & (GTF_reading|GTF_writing)) { - return 0; - } else { - /* The read of grstatus needs to have acquire - semantics. On x86, reads already have - that, and we just need to protect against - compiler reorderings. On other - architectures we may need a full - barrier. */ -#ifdef CONFIG_X86 - barrier(); -#else - mb(); -#endif - } - - return 1; -} - static inline int _gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly) { return gnttab_interface->end_foreign_access_ref(ref, readonly); @@ -616,37 +436,6 @@ static unsigned long gnttab_end_foreign_transfer_ref_v1(grant_ref_t ref) return frame; } -static unsigned long gnttab_end_foreign_transfer_ref_v2(grant_ref_t ref) -{ - unsigned long frame; - u16 flags; - u16 *pflags; - - pflags = &gnttab_shared.v2[ref].hdr.flags; - - /* - * If a transfer is not even yet started, try to reclaim the grant - * reference and return failure (== 0). - */ - while (!((flags = *pflags) & GTF_transfer_committed)) { - if (sync_cmpxchg(pflags, flags, 0) == flags) - return 0; - cpu_relax(); - } - - /* If a transfer is in progress then wait until it is completed. */ - while (!(flags & GTF_transfer_completed)) { - flags = *pflags; - cpu_relax(); - } - - rmb(); /* Read the frame number /after/ reading completion status. */ - frame = gnttab_shared.v2[ref].full_page.frame; - BUG_ON(frame == 0); - - return frame; -} - unsigned long gnttab_end_foreign_transfer_ref(grant_ref_t ref) { return gnttab_interface->end_foreign_transfer_ref(ref); @@ -962,12 +751,6 @@ int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops, } EXPORT_SYMBOL_GPL(gnttab_unmap_refs); -static unsigned nr_status_frames(unsigned nr_grant_frames) -{ - BUG_ON(grefs_per_grant_frame == 0); - return (nr_grant_frames * grefs_per_grant_frame + SPP - 1) / SPP; -} - static int gnttab_map_frames_v1(xen_pfn_t *frames, unsigned int nr_gframes) { int rc; @@ -985,55 +768,6 @@ static void gnttab_unmap_frames_v1(void) arch_gnttab_unmap(gnttab_shared.addr, nr_grant_frames); } -static int gnttab_map_frames_v2(xen_pfn_t *frames, unsigned int nr_gframes) -{ - uint64_t *sframes; - unsigned int nr_sframes; - struct gnttab_get_status_frames getframes; - int rc; - - nr_sframes = nr_status_frames(nr_gframes); - - /* No need for kzalloc as it is initialized in following hypercall - * GNTTABOP_get_status_frames. - */ - sframes = kmalloc(nr_sframes * sizeof(uint64_t), GFP_ATOMIC); - if (!sframes) - return -ENOMEM; - - getframes.dom = DOMID_SELF; - getframes.nr_frames = nr_sframes; - set_xen_guest_handle(getframes.frame_list, sframes); - - rc = HYPERVISOR_grant_table_op(GNTTABOP_get_status_frames, - &getframes, 1); - if (rc == -ENOSYS) { - kfree(sframes); - return -ENOSYS; - } - - BUG_ON(rc || getframes.status); - - rc = arch_gnttab_map_status(sframes, nr_sframes, - nr_status_frames(gnttab_max_grant_frames()), - &grstatus); - BUG_ON(rc); - kfree(sframes); - - rc = arch_gnttab_map_shared(frames, nr_gframes, - gnttab_max_grant_frames(), - &gnttab_shared.addr); - BUG_ON(rc); - - return 0; -} - -static void gnttab_unmap_frames_v2(void) -{ - arch_gnttab_unmap(gnttab_shared.addr, nr_grant_frames); - arch_gnttab_unmap(grstatus, nr_status_frames(nr_grant_frames)); -} - static int gnttab_map(unsigned int start_idx, unsigned int end_idx) { struct gnttab_setup_table setup; @@ -1101,43 +835,13 @@ static struct gnttab_ops gnttab_v1_ops = { .query_foreign_access = gnttab_query_foreign_access_v1, }; -static struct gnttab_ops gnttab_v2_ops = { - .map_frames = gnttab_map_frames_v2, - .unmap_frames = gnttab_unmap_frames_v2, - .update_entry = gnttab_update_entry_v2, - .end_foreign_access_ref = gnttab_end_foreign_access_ref_v2, - .end_foreign_transfer_ref = gnttab_end_foreign_transfer_ref_v2, - .query_foreign_access = gnttab_query_foreign_access_v2, - .update_subpage_entry = gnttab_update_subpage_entry_v2, - .update_trans_entry = gnttab_update_trans_entry_v2, -}; - static void gnttab_request_version(void) { - int rc; - struct gnttab_set_version gsv; + /* Only version 1 is used, which will always be available. */ + grant_table_version = 1; + grefs_per_grant_frame = PAGE_SIZE / sizeof(struct grant_entry_v1); + gnttab_interface = &gnttab_v1_ops; - gsv.version = 1; - - rc = HYPERVISOR_grant_table_op(GNTTABOP_set_version, &gsv, 1); - if (rc == 0 && gsv.version == 2) { - grant_table_version = 2; - grefs_per_grant_frame = PAGE_SIZE / sizeof(union grant_entry_v2); - gnttab_interface = &gnttab_v2_ops; - } else if (grant_table_version == 2) { - /* - * If we've already used version 2 features, - * but then suddenly discover that they're not - * available (e.g. migrating to an older - * version of Xen), almost unbounded badness - * can happen. - */ - panic("we need grant tables version 2, but only version 1 is available"); - } else { - grant_table_version = 1; - grefs_per_grant_frame = PAGE_SIZE / sizeof(struct grant_entry_v1); - gnttab_interface = &gnttab_v1_ops; - } pr_info("Grant tables using version %d layout\n", grant_table_version); } @@ -1225,8 +929,7 @@ int gnttab_init(void) } } - ret = arch_gnttab_init(max_nr_grant_frames, - nr_status_frames(max_nr_grant_frames)); + ret = arch_gnttab_init(max_nr_grant_frames); if (ret < 0) goto ini_nomem; diff --git a/include/xen/grant_table.h b/include/xen/grant_table.h index 5c1aba154b64..3387465b9caa 100644 --- a/include/xen/grant_table.h +++ b/include/xen/grant_table.h @@ -64,24 +64,6 @@ int gnttab_resume(void); int gnttab_grant_foreign_access(domid_t domid, unsigned long frame, int readonly); -int gnttab_grant_foreign_access_subpage(domid_t domid, unsigned long frame, - int flags, unsigned page_off, - unsigned length); -int gnttab_grant_foreign_access_trans(domid_t domid, int flags, - domid_t trans_domid, - grant_ref_t trans_gref); - -/* - * Are sub-page grants available on this version of Xen? Returns true if they - * are, and false if they're not. - */ -bool gnttab_subpage_grants_available(void); - -/* - * Are transitive grants available on this version of Xen? Returns true if they - * are, and false if they're not. - */ -bool gnttab_trans_grants_available(void); /* * End access through the given grant reference, iff the grant entry is no @@ -128,13 +110,6 @@ void gnttab_cancel_free_callback(struct gnttab_free_callback *callback); void gnttab_grant_foreign_access_ref(grant_ref_t ref, domid_t domid, unsigned long frame, int readonly); -int gnttab_grant_foreign_access_subpage_ref(grant_ref_t ref, domid_t domid, - unsigned long frame, int flags, - unsigned page_off, - unsigned length); -int gnttab_grant_foreign_access_trans_ref(grant_ref_t ref, domid_t domid, - int flags, domid_t trans_domid, - grant_ref_t trans_gref); void gnttab_grant_foreign_transfer_ref(grant_ref_t, domid_t domid, unsigned long pfn); @@ -170,13 +145,10 @@ gnttab_set_unmap_op(struct gnttab_unmap_grant_ref *unmap, phys_addr_t addr, unmap->dev_bus_addr = 0; } -int arch_gnttab_init(unsigned long nr_shared, unsigned long nr_status); +int arch_gnttab_init(unsigned long nr_shared); int arch_gnttab_map_shared(xen_pfn_t *frames, unsigned long nr_gframes, unsigned long max_nr_gframes, void **__shared); -int arch_gnttab_map_status(uint64_t *frames, unsigned long nr_gframes, - unsigned long max_nr_gframes, - grant_status_t **__shared); void arch_gnttab_unmap(void *shared, unsigned long nr_gframes); struct grant_frames { -- cgit v1.2.3 From 9ecf07a1d8f70f72ec99a0f102c8aa24609d84f4 Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Sat, 12 Jul 2014 22:36:44 +0200 Subject: neigh: sysctl - simplify address calculation of gc_* variables The code in neigh_sysctl_register() relies on a specific layout of struct neigh_table, namely that the 'gc_*' variables are directly following the 'parms' member in a specific order. The code, though, expresses this in the most ugly way. Get rid of the ugly casts and use the 'tbl' pointer to get a handle to the table. This way we can refer to the 'gc_*' variables directly. Similarly seen in the grsecurity patch, written by Brad Spengler. Signed-off-by: Mathias Krause Cc: Brad Spengler Signed-off-by: David S. Miller --- include/net/neighbour.h | 1 - net/core/neighbour.c | 9 +++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 7277caf3743d..47f425464f84 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -203,7 +203,6 @@ struct neigh_table { void (*proxy_redo)(struct sk_buff *skb); char *id; struct neigh_parms parms; - /* HACK. gc_* should follow parms without a gap! */ int gc_interval; int gc_thresh1; int gc_thresh2; diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 32d872eec7f5..559890b0f0a2 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -3059,11 +3059,12 @@ int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p, memset(&t->neigh_vars[NEIGH_VAR_GC_INTERVAL], 0, sizeof(t->neigh_vars[NEIGH_VAR_GC_INTERVAL])); } else { + struct neigh_table *tbl = p->tbl; dev_name_source = "default"; - t->neigh_vars[NEIGH_VAR_GC_INTERVAL].data = (int *)(p + 1); - t->neigh_vars[NEIGH_VAR_GC_THRESH1].data = (int *)(p + 1) + 1; - t->neigh_vars[NEIGH_VAR_GC_THRESH2].data = (int *)(p + 1) + 2; - t->neigh_vars[NEIGH_VAR_GC_THRESH3].data = (int *)(p + 1) + 3; + t->neigh_vars[NEIGH_VAR_GC_INTERVAL].data = &tbl->gc_interval; + t->neigh_vars[NEIGH_VAR_GC_THRESH1].data = &tbl->gc_thresh1; + t->neigh_vars[NEIGH_VAR_GC_THRESH2].data = &tbl->gc_thresh2; + t->neigh_vars[NEIGH_VAR_GC_THRESH3].data = &tbl->gc_thresh3; } if (handler) { -- cgit v1.2.3 From 8024e02879ddd5042be02c70557f74cdc70b44b4 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Sun, 13 Jul 2014 19:49:37 -0700 Subject: udp: Add udp_sock_create for UDP tunnels to open listener socket Added udp_tunnel.c which can contain some common functions for UDP tunnels. The first function in this is udp_sock_create which is used to open the listener port for a UDP tunnel. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- include/net/udp_tunnel.h | 32 +++++++++++++++ net/ipv4/Kconfig | 4 ++ net/ipv4/Makefile | 1 + net/ipv4/udp_tunnel.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 137 insertions(+) create mode 100644 include/net/udp_tunnel.h create mode 100644 net/ipv4/udp_tunnel.c (limited to 'include') diff --git a/include/net/udp_tunnel.h b/include/net/udp_tunnel.h new file mode 100644 index 000000000000..ffd69cbded35 --- /dev/null +++ b/include/net/udp_tunnel.h @@ -0,0 +1,32 @@ +#ifndef __NET_UDP_TUNNEL_H +#define __NET_UDP_TUNNEL_H + +struct udp_port_cfg { + u8 family; + + /* Used only for kernel-created sockets */ + union { + struct in_addr local_ip; +#if IS_ENABLED(CONFIG_IPV6) + struct in6_addr local_ip6; +#endif + }; + + union { + struct in_addr peer_ip; +#if IS_ENABLED(CONFIG_IPV6) + struct in6_addr peer_ip6; +#endif + }; + + __be16 local_udp_port; + __be16 peer_udp_port; + unsigned int use_udp_checksums:1, + use_udp6_tx_checksums:1, + use_udp6_rx_checksums:1; +}; + +int udp_sock_create(struct net *net, struct udp_port_cfg *cfg, + struct socket **sockp); + +#endif diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig index 05c57f0fcabe..dbc10d84161f 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig @@ -307,6 +307,10 @@ config NET_IPVTI the notion of a secure tunnel for IPSEC and then use routing protocol on top. +config NET_UDP_TUNNEL + tristate + default n + config INET_AH tristate "IP: AH transformation" select XFRM_ALGO diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index f032688d20d3..8ee1cd4053ee 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_NET_IPIP) += ipip.o gre-y := gre_demux.o obj-$(CONFIG_NET_IPGRE_DEMUX) += gre.o obj-$(CONFIG_NET_IPGRE) += ip_gre.o +obj-$(CONFIG_NET_UDP_TUNNEL) += udp_tunnel.o obj-$(CONFIG_NET_IPVTI) += ip_vti.o obj-$(CONFIG_SYN_COOKIES) += syncookies.o obj-$(CONFIG_INET_AH) += ah4.o diff --git a/net/ipv4/udp_tunnel.c b/net/ipv4/udp_tunnel.c new file mode 100644 index 000000000000..61ec1a65207e --- /dev/null +++ b/net/ipv4/udp_tunnel.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int udp_sock_create(struct net *net, struct udp_port_cfg *cfg, + struct socket **sockp) +{ + int err = -EINVAL; + struct socket *sock = NULL; + +#if IS_ENABLED(CONFIG_IPV6) + if (cfg->family == AF_INET6) { + struct sockaddr_in6 udp6_addr; + + err = sock_create_kern(AF_INET6, SOCK_DGRAM, 0, &sock); + if (err < 0) + goto error; + + sk_change_net(sock->sk, net); + + udp6_addr.sin6_family = AF_INET6; + memcpy(&udp6_addr.sin6_addr, &cfg->local_ip6, + sizeof(udp6_addr.sin6_addr)); + udp6_addr.sin6_port = cfg->local_udp_port; + err = kernel_bind(sock, (struct sockaddr *)&udp6_addr, + sizeof(udp6_addr)); + if (err < 0) + goto error; + + if (cfg->peer_udp_port) { + udp6_addr.sin6_family = AF_INET6; + memcpy(&udp6_addr.sin6_addr, &cfg->peer_ip6, + sizeof(udp6_addr.sin6_addr)); + udp6_addr.sin6_port = cfg->peer_udp_port; + err = kernel_connect(sock, + (struct sockaddr *)&udp6_addr, + sizeof(udp6_addr), 0); + } + if (err < 0) + goto error; + + udp_set_no_check6_tx(sock->sk, !cfg->use_udp6_tx_checksums); + udp_set_no_check6_rx(sock->sk, !cfg->use_udp6_rx_checksums); + } else +#endif + if (cfg->family == AF_INET) { + struct sockaddr_in udp_addr; + + err = sock_create_kern(AF_INET, SOCK_DGRAM, 0, &sock); + if (err < 0) + goto error; + + sk_change_net(sock->sk, net); + + udp_addr.sin_family = AF_INET; + udp_addr.sin_addr = cfg->local_ip; + udp_addr.sin_port = cfg->local_udp_port; + err = kernel_bind(sock, (struct sockaddr *)&udp_addr, + sizeof(udp_addr)); + if (err < 0) + goto error; + + if (cfg->peer_udp_port) { + udp_addr.sin_family = AF_INET; + udp_addr.sin_addr = cfg->peer_ip; + udp_addr.sin_port = cfg->peer_udp_port; + err = kernel_connect(sock, + (struct sockaddr *)&udp_addr, + sizeof(udp_addr), 0); + if (err < 0) + goto error; + } + + sock->sk->sk_no_check_tx = !cfg->use_udp_checksums; + } else { + return -EPFNOSUPPORT; + } + + + *sockp = sock; + + return 0; + +error: + if (sock) { + kernel_sock_shutdown(sock, SHUT_RDWR); + sk_release_kernel(sock->sk); + } + *sockp = NULL; + return err; +} +EXPORT_SYMBOL(udp_sock_create); + +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From f736d9985e69e72d9a2ebfd131a72ee8f870c6f3 Mon Sep 17 00:00:00 2001 From: Nikita Edward Baruzdin Date: Fri, 11 Jul 2014 16:13:21 +0400 Subject: can: netlink: Remove space before tab Fixes the corresponing checkpatch.pl warning. Signed-off-by: Nikita Edward Baruzdin Acked-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- include/uapi/linux/can/netlink.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/can/netlink.h b/include/uapi/linux/can/netlink.h index 813d11f54977..3bbf5c7e1500 100644 --- a/include/uapi/linux/can/netlink.h +++ b/include/uapi/linux/can/netlink.h @@ -92,7 +92,7 @@ struct can_ctrlmode { }; #define CAN_CTRLMODE_LOOPBACK 0x01 /* Loopback mode */ -#define CAN_CTRLMODE_LISTENONLY 0x02 /* Listen-only mode */ +#define CAN_CTRLMODE_LISTENONLY 0x02 /* Listen-only mode */ #define CAN_CTRLMODE_3_SAMPLES 0x04 /* Triple sampling mode */ #define CAN_CTRLMODE_ONE_SHOT 0x08 /* One-Shot mode */ #define CAN_CTRLMODE_BERR_REPORTING 0x10 /* Bus-error reporting */ -- cgit v1.2.3 From 4b9e1bab12c9b6de965268c2fbe6ebbb35dddd89 Mon Sep 17 00:00:00 2001 From: Nikita Edward Baruzdin Date: Fri, 11 Jul 2014 16:13:22 +0400 Subject: can: netlink: Add CAN_CTRLMODE_PRESUME_ACK flag Most CAN controllers have a support for ignoring ACK absence. Some of them refer to this feature as a self test mode (e. g. SJA1000) and some include it as a part of a loopback mode (e. g. MCP2510). Setting the introduced flag via netlink should make CAN controller perform a successful transmission, even if there is no acknowledgement (dominant ACK bit) received. Signed-off-by: Nikita Edward Baruzdin Acked-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- include/uapi/linux/can/netlink.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/uapi/linux/can/netlink.h b/include/uapi/linux/can/netlink.h index 3bbf5c7e1500..3e4323a3918d 100644 --- a/include/uapi/linux/can/netlink.h +++ b/include/uapi/linux/can/netlink.h @@ -97,6 +97,7 @@ struct can_ctrlmode { #define CAN_CTRLMODE_ONE_SHOT 0x08 /* One-Shot mode */ #define CAN_CTRLMODE_BERR_REPORTING 0x10 /* Bus-error reporting */ #define CAN_CTRLMODE_FD 0x20 /* CAN FD mode */ +#define CAN_CTRLMODE_PRESUME_ACK 0x40 /* Ignore missing CAN ACKs */ /* * CAN device statistics -- cgit v1.2.3 From c84dbf61a7b322188d2a7fddc0cc6317ac6713e2 Mon Sep 17 00:00:00 2001 From: Torsten Duwe Date: Sat, 14 Jun 2014 23:38:36 -0400 Subject: random: add_hwgenerator_randomness() for feeding entropy from devices This patch adds an interface to the random pool for feeding entropy in-kernel. Signed-off-by: Torsten Duwe Signed-off-by: Theodore Ts'o Acked-by: H. Peter Anvin --- drivers/char/random.c | 21 +++++++++++++++++++++ include/linux/hw_random.h | 2 ++ 2 files changed, 23 insertions(+) (limited to 'include') diff --git a/drivers/char/random.c b/drivers/char/random.c index d3bb7927fb49..914b1575df8f 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -250,6 +250,7 @@ #include #include #include +#include #include #include #include @@ -1750,3 +1751,23 @@ randomize_range(unsigned long start, unsigned long end, unsigned long len) return 0; return PAGE_ALIGN(get_random_int() % range + start); } + +/* Interface for in-kernel drivers of true hardware RNGs. + * Those devices may produce endless random bits and will be throttled + * when our pool is full. + */ +void add_hwgenerator_randomness(const char *buffer, size_t count, + size_t entropy) +{ + struct entropy_store *poolp = &input_pool; + + /* Suspend writing if we're above the trickle threshold. + * We'll be woken up again once below random_write_wakeup_thresh, + * or when the calling thread is about to terminate. + */ + wait_event_interruptible(random_write_wait, kthread_should_stop() || + ENTROPY_BITS(&input_pool) <= random_write_wakeup_bits); + mix_pool_bytes(poolp, buffer, count); + credit_entropy_bits(poolp, entropy); +} +EXPORT_SYMBOL_GPL(add_hwgenerator_randomness); diff --git a/include/linux/hw_random.h b/include/linux/hw_random.h index b4b0eef5fddf..3f075ff00411 100644 --- a/include/linux/hw_random.h +++ b/include/linux/hw_random.h @@ -47,5 +47,7 @@ struct hwrng { extern int hwrng_register(struct hwrng *rng); /** Unregister a Hardware Random Number Generator driver. */ extern void hwrng_unregister(struct hwrng *rng); +/** Feed random bits into the pool. */ +extern void add_hwgenerator_randomness(const char *buffer, size_t count, size_t entropy); #endif /* LINUX_HWRANDOM_H_ */ -- cgit v1.2.3 From 0f734e6e768b4b66737b3d3e13f1769a12ecff86 Mon Sep 17 00:00:00 2001 From: Torsten Duwe Date: Sat, 14 Jun 2014 23:48:41 -0400 Subject: hwrng: add per-device entropy derating This patch introduces a derating factor to struct hwrng for the random bits going into the kernel input pool, and a common default derating for drivers which do not specify one. Signed-off-by: Torsten Duwe Signed-off-by: Theodore Ts'o Acked-by: H. Peter Anvin --- drivers/char/hw_random/core.c | 11 ++++++++++- include/linux/hw_random.h | 3 +++ 2 files changed, 13 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index 48b3c812b9ec..3aaf97c774f1 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -56,11 +56,15 @@ static LIST_HEAD(rng_list); static DEFINE_MUTEX(rng_mutex); static int data_avail; static u8 *rng_buffer, *rng_fillbuf; -static unsigned short current_quality = 700; /* an arbitrary 70% */ +static unsigned short current_quality; +static unsigned short default_quality; /* = 0; default to "off" */ module_param(current_quality, ushort, 0644); MODULE_PARM_DESC(current_quality, "current hwrng entropy estimation per mill"); +module_param(default_quality, ushort, 0644); +MODULE_PARM_DESC(default_quality, + "default entropy content of hwrng per mill"); static void start_khwrngd(void); @@ -79,6 +83,11 @@ static inline int hwrng_init(struct hwrng *rng) return err; } + current_quality = rng->quality ? : default_quality; + current_quality &= 1023; + + if (current_quality == 0 && hwrng_fill) + kthread_stop(hwrng_fill); if (current_quality > 0 && !hwrng_fill) start_khwrngd(); diff --git a/include/linux/hw_random.h b/include/linux/hw_random.h index 3f075ff00411..914bb08cd738 100644 --- a/include/linux/hw_random.h +++ b/include/linux/hw_random.h @@ -29,6 +29,8 @@ * @read: New API. drivers can fill up to max bytes of data * into the buffer. The buffer is aligned for any type. * @priv: Private data, for use by the RNG driver. + * @quality: Estimation of true entropy in RNG's bitstream + * (per mill). */ struct hwrng { const char *name; @@ -38,6 +40,7 @@ struct hwrng { int (*data_read)(struct hwrng *rng, u32 *data); int (*read)(struct hwrng *rng, void *data, size_t max, bool wait); unsigned long priv; + unsigned short quality; /* internal. */ struct list_head list; -- cgit v1.2.3 From 0cea76f3393782d67ccea8f07e9abf341bc4f60e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Jul 2014 16:31:01 +0200 Subject: ALSA: control: Define SNDRV_CTL_TLV_OP_* constants Instead of hard-coded magic numbers, define constants for op_flag to tlv callbacks. Signed-off-by: Takashi Iwai --- include/sound/control.h | 7 ++++++- sound/core/control.c | 6 +++--- 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/sound/control.h b/include/sound/control.h index 5358892b1b39..042613938a1d 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -31,10 +31,15 @@ typedef int (snd_kcontrol_info_t) (struct snd_kcontrol * kcontrol, struct snd_ct typedef int (snd_kcontrol_get_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol); typedef int (snd_kcontrol_put_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol); typedef int (snd_kcontrol_tlv_rw_t)(struct snd_kcontrol *kcontrol, - int op_flag, /* 0=read,1=write,-1=command */ + int op_flag, /* SNDRV_CTL_TLV_OP_XXX */ unsigned int size, unsigned int __user *tlv); +enum { + SNDRV_CTL_TLV_OP_READ = 0, + SNDRV_CTL_TLV_OP_WRITE = 1, + SNDRV_CTL_TLV_OP_CMD = -1, +}; struct snd_kcontrol_new { snd_ctl_elem_iface_t iface; /* interface identifier */ diff --git a/sound/core/control.c b/sound/core/control.c index f0b0e14497a5..b9611344ff9e 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -1406,11 +1406,11 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS: return snd_ctl_subscribe_events(ctl, ip); case SNDRV_CTL_IOCTL_TLV_READ: - return snd_ctl_tlv_ioctl(ctl, argp, 0); + return snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_READ); case SNDRV_CTL_IOCTL_TLV_WRITE: - return snd_ctl_tlv_ioctl(ctl, argp, 1); + return snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_WRITE); case SNDRV_CTL_IOCTL_TLV_COMMAND: - return snd_ctl_tlv_ioctl(ctl, argp, -1); + return snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_CMD); case SNDRV_CTL_IOCTL_POWER: return -ENOPROTOOPT; case SNDRV_CTL_IOCTL_POWER_STATE: -- cgit v1.2.3 From 5577964e64692e17cc498854b7e0833e6532cd64 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 15 Jul 2014 11:05:09 -0400 Subject: cgroup: rename cgroup_subsys->base_cftypes to ->legacy_cftypes Currently, cgroup_subsys->base_cftypes is used for both the unified default hierarchy and legacy ones and subsystems can mark each file with either CFTYPE_ONLY_ON_DFL or CFTYPE_INSANE if it has to appear only on one of them. This is quite hairy and error-prone. Also, we may end up exposing interface files to the default hierarchy without thinking it through. cgroup_subsys will grow two separate cftype arrays and apply each only on the hierarchies of the matching type. This will allow organizing cftypes in a lot clearer way and encourage subsystems to scrutinize the interface which is being exposed in the new default hierarchy. In preparation, this patch renames cgroup_subsys->base_cftypes to cgroup_subsys->legacy_cftypes. This patch is pure rename. Signed-off-by: Tejun Heo Acked-by: Neil Horman Acked-by: Li Zefan Cc: Johannes Weiner Cc: Michal Hocko Cc: Vivek Goyal Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Ingo Molnar Cc: Arnaldo Carvalho de Melo Cc: Aristeu Rozanski Cc: Aneesh Kumar K.V --- block/blk-cgroup.c | 2 +- include/linux/cgroup.h | 2 +- kernel/cgroup.c | 4 ++-- kernel/cgroup_freezer.c | 2 +- kernel/cpuset.c | 2 +- kernel/sched/core.c | 2 +- kernel/sched/cpuacct.c | 2 +- mm/memcontrol.c | 2 +- net/core/netclassid_cgroup.c | 2 +- net/core/netprio_cgroup.c | 2 +- security/device_cgroup.c | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 63c3cd454d1e..5cfbc723041c 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -924,7 +924,7 @@ struct cgroup_subsys blkio_cgrp_subsys = { .css_offline = blkcg_css_offline, .css_free = blkcg_css_free, .can_attach = blkcg_can_attach, - .base_cftypes = blkcg_files, + .legacy_cftypes = blkcg_files, #ifdef CONFIG_MEMCG /* * This ensures that, if available, memcg is automatically enabled diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 7bb274487c89..a6e9c2eeab89 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -672,7 +672,7 @@ struct cgroup_subsys { struct list_head cfts; /* base cftypes, automatically registered with subsys itself */ - struct cftype *base_cftypes; + struct cftype *legacy_cftypes; /* used on the legacy hierarchies */ /* * A subsystem may depend on other subsystems. When such subsystem diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 7e5fee5d6422..6496a83b0314 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -4890,7 +4890,7 @@ int __init cgroup_init(void) */ if (!ss->disabled) { cgrp_dfl_root.subsys_mask |= 1 << ss->id; - WARN_ON(cgroup_add_cftypes(ss, ss->base_cftypes)); + WARN_ON(cgroup_add_cftypes(ss, ss->legacy_cftypes)); } } @@ -5480,6 +5480,6 @@ static struct cftype debug_files[] = { struct cgroup_subsys debug_cgrp_subsys = { .css_alloc = debug_css_alloc, .css_free = debug_css_free, - .base_cftypes = debug_files, + .legacy_cftypes = debug_files, }; #endif /* CONFIG_CGROUP_DEBUG */ diff --git a/kernel/cgroup_freezer.c b/kernel/cgroup_freezer.c index a79e40f9d700..92b98cc0ee76 100644 --- a/kernel/cgroup_freezer.c +++ b/kernel/cgroup_freezer.c @@ -480,5 +480,5 @@ struct cgroup_subsys freezer_cgrp_subsys = { .css_free = freezer_css_free, .attach = freezer_attach, .fork = freezer_fork, - .base_cftypes = files, + .legacy_cftypes = files, }; diff --git a/kernel/cpuset.c b/kernel/cpuset.c index 53a9bbf16391..f337f42a07ac 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -2036,7 +2036,7 @@ struct cgroup_subsys cpuset_cgrp_subsys = { .cancel_attach = cpuset_cancel_attach, .attach = cpuset_attach, .bind = cpuset_bind, - .base_cftypes = files, + .legacy_cftypes = files, .early_init = 1, }; diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 3bdf01b494fe..6628e8014824 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -8088,7 +8088,7 @@ struct cgroup_subsys cpu_cgrp_subsys = { .can_attach = cpu_cgroup_can_attach, .attach = cpu_cgroup_attach, .exit = cpu_cgroup_exit, - .base_cftypes = cpu_files, + .legacy_cftypes = cpu_files, .early_init = 1, }; diff --git a/kernel/sched/cpuacct.c b/kernel/sched/cpuacct.c index 9cf350c94ec4..dd7cbb55bbf2 100644 --- a/kernel/sched/cpuacct.c +++ b/kernel/sched/cpuacct.c @@ -278,6 +278,6 @@ void cpuacct_account_field(struct task_struct *p, int index, u64 val) struct cgroup_subsys cpuacct_cgrp_subsys = { .css_alloc = cpuacct_css_alloc, .css_free = cpuacct_css_free, - .base_cftypes = files, + .legacy_cftypes = files, .early_init = 1, }; diff --git a/mm/memcontrol.c b/mm/memcontrol.c index a2a4bd69a7ae..8331f2739e2c 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -7048,7 +7048,7 @@ struct cgroup_subsys memory_cgrp_subsys = { .cancel_attach = mem_cgroup_cancel_attach, .attach = mem_cgroup_move_task, .bind = mem_cgroup_bind, - .base_cftypes = mem_cgroup_files, + .legacy_cftypes = mem_cgroup_files, .early_init = 0, }; diff --git a/net/core/netclassid_cgroup.c b/net/core/netclassid_cgroup.c index 30d903b19c62..1f2a126f4ffa 100644 --- a/net/core/netclassid_cgroup.c +++ b/net/core/netclassid_cgroup.c @@ -107,5 +107,5 @@ struct cgroup_subsys net_cls_cgrp_subsys = { .css_online = cgrp_css_online, .css_free = cgrp_css_free, .attach = cgrp_attach, - .base_cftypes = ss_files, + .legacy_cftypes = ss_files, }; diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c index 2f385b9bccc0..cbd0a199bf52 100644 --- a/net/core/netprio_cgroup.c +++ b/net/core/netprio_cgroup.c @@ -249,7 +249,7 @@ struct cgroup_subsys net_prio_cgrp_subsys = { .css_online = cgrp_css_online, .css_free = cgrp_css_free, .attach = net_prio_attach, - .base_cftypes = ss_files, + .legacy_cftypes = ss_files, }; static int netprio_device_event(struct notifier_block *unused, diff --git a/security/device_cgroup.c b/security/device_cgroup.c index d9d69e6930ed..188c1d26393b 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -796,7 +796,7 @@ struct cgroup_subsys devices_cgrp_subsys = { .css_free = devcgroup_css_free, .css_online = devcgroup_online, .css_offline = devcgroup_offline, - .base_cftypes = dev_cgroup_files, + .legacy_cftypes = dev_cgroup_files, }; /** -- cgit v1.2.3 From 2cf669a58dc08fa065a8bd0dca866c0e6cb358cc Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 15 Jul 2014 11:05:09 -0400 Subject: cgroup: replace cgroup_add_cftypes() with cgroup_add_legacy_cftypes() Currently, cftypes added by cgroup_add_cftypes() are used for both the unified default hierarchy and legacy ones and subsystems can mark each file with either CFTYPE_ONLY_ON_DFL or CFTYPE_INSANE if it has to appear only on one of them. This is quite hairy and error-prone. Also, we may end up exposing interface files to the default hierarchy without thinking it through. cgroup_subsys will grow two separate cftype addition functions and apply each only on the hierarchies of the matching type. This will allow organizing cftypes in a lot clearer way and encourage subsystems to scrutinize the interface which is being exposed in the new default hierarchy. In preparation, this patch adds cgroup_add_legacy_cftypes() which currently is a simple wrapper around cgroup_add_cftypes() and replaces all cgroup_add_cftypes() usages with it. While at it, this patch drops a completely spurious return from __hugetlb_cgroup_file_init(). This patch doesn't introduce any functional differences. Signed-off-by: Tejun Heo Acked-by: Neil Horman Acked-by: Li Zefan Cc: Johannes Weiner Cc: Michal Hocko Cc: Aneesh Kumar K.V --- block/blk-cgroup.c | 3 ++- include/linux/cgroup.h | 2 +- kernel/cgroup.c | 7 ++++++- mm/hugetlb_cgroup.c | 5 ++--- mm/memcontrol.c | 3 ++- net/ipv4/tcp_memcontrol.c | 2 +- 6 files changed, 14 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 5cfbc723041c..2541cf043ba8 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -1124,7 +1124,8 @@ int __init blkcg_policy_register(struct blkcg_policy *pol) /* everything is in place, add intf files for the new policy */ if (pol->cftypes) - WARN_ON(cgroup_add_cftypes(&blkio_cgrp_subsys, pol->cftypes)); + WARN_ON(cgroup_add_legacy_cftypes(&blkio_cgrp_subsys, + pol->cftypes)); ret = 0; out_unlock: mutex_unlock(&blkcg_pol_mutex); diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index a6e9c2eeab89..f5f0feef2701 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -590,7 +590,7 @@ static inline void pr_cont_cgroup_path(struct cgroup *cgrp) char *task_cgroup_path(struct task_struct *task, char *buf, size_t buflen); -int cgroup_add_cftypes(struct cgroup_subsys *ss, struct cftype *cfts); +int cgroup_add_legacy_cftypes(struct cgroup_subsys *ss, struct cftype *cfts); int cgroup_rm_cftypes(struct cftype *cfts); bool cgroup_is_descendant(struct cgroup *cgrp, struct cgroup *ancestor); diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 6496a83b0314..c275aa439a6f 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -3170,7 +3170,7 @@ int cgroup_rm_cftypes(struct cftype *cfts) * function currently returns 0 as long as @cfts registration is successful * even if some file creation attempts on existing cgroups fail. */ -int cgroup_add_cftypes(struct cgroup_subsys *ss, struct cftype *cfts) +static int cgroup_add_cftypes(struct cgroup_subsys *ss, struct cftype *cfts) { int ret; @@ -3195,6 +3195,11 @@ int cgroup_add_cftypes(struct cgroup_subsys *ss, struct cftype *cfts) return ret; } +int cgroup_add_legacy_cftypes(struct cgroup_subsys *ss, struct cftype *cfts) +{ + return cgroup_add_cftypes(ss, cfts); +} + /** * cgroup_task_count - count the number of tasks in a cgroup. * @cgrp: the cgroup in question diff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c index 493f758445e7..9aae6f47433f 100644 --- a/mm/hugetlb_cgroup.c +++ b/mm/hugetlb_cgroup.c @@ -358,9 +358,8 @@ static void __init __hugetlb_cgroup_file_init(int idx) cft = &h->cgroup_files[4]; memset(cft, 0, sizeof(*cft)); - WARN_ON(cgroup_add_cftypes(&hugetlb_cgrp_subsys, h->cgroup_files)); - - return; + WARN_ON(cgroup_add_legacy_cftypes(&hugetlb_cgrp_subsys, + h->cgroup_files)); } void __init hugetlb_cgroup_file_init(void) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 8331f2739e2c..b6b3c6fea509 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -7065,7 +7065,8 @@ __setup("swapaccount=", enable_swap_account); static void __init memsw_file_init(void) { - WARN_ON(cgroup_add_cftypes(&memory_cgrp_subsys, memsw_cgroup_files)); + WARN_ON(cgroup_add_legacy_cftypes(&memory_cgrp_subsys, + memsw_cgroup_files)); } static void __init enable_swap_cgroup(void) diff --git a/net/ipv4/tcp_memcontrol.c b/net/ipv4/tcp_memcontrol.c index f7a2ec3ac584..3af522622fad 100644 --- a/net/ipv4/tcp_memcontrol.c +++ b/net/ipv4/tcp_memcontrol.c @@ -222,7 +222,7 @@ static struct cftype tcp_files[] = { static int __init tcp_memcontrol_init(void) { - WARN_ON(cgroup_add_cftypes(&memory_cgrp_subsys, tcp_files)); + WARN_ON(cgroup_add_legacy_cftypes(&memory_cgrp_subsys, tcp_files)); return 0; } __initcall(tcp_memcontrol_init); -- cgit v1.2.3 From a8ddc8215e1a4cd9dc5d6210811cfc381a489ec2 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 15 Jul 2014 11:05:10 -0400 Subject: cgroup: distinguish the default and legacy hierarchies when handling cftypes Until now, cftype arrays carried files for both the default and legacy hierarchies and the files which needed to be used on only one of them were flagged with either CFTYPE_ONLY_ON_DFL or CFTYPE_INSANE. This gets confusing very quickly and we may end up exposing interface files to the default hierarchy without thinking it through. This patch makes cgroup core provide separate sets of interfaces for cftype handling so that the cftypes for the default and legacy hierarchies are clearly distinguished. The previous two patches renamed the existing ones so that they clearly indicate that they're for the legacy hierarchies. This patch adds the interface for the default hierarchy and apply them selectively depending on the hierarchy type. * cftypes added through cgroup_subsys->dfl_cftypes and cgroup_add_dfl_cftypes() only show up on the default hierarchy. * cftypes added through cgroup_subsys->legacy_cftypes and cgroup_add_legacy_cftypes() only show up on the legacy hierarchies. * cgroup_subsys->dfl_cftypes and ->legacy_cftypes can point to the same array for the cases where the interface files are identical on both types of hierarchies. * This makes all the existing subsystem interface files legacy-only by default and all subsystems will have no interface file created when enabled on the default hierarchy. Each subsystem should explicitly review and compose the interface for the default hierarchy. * A boot param "cgroup__DEVEL__legacy_files_on_dfl" is added which makes subsystems which haven't decided the interface files for the default hierarchy to present the legacy files on the default hierarchy so that its behavior on the default hierarchy can be tested. As the awkward name suggests, this is for development only. * memcg's CFTYPE_INSANE on "use_hierarchy" is noop now as the whole array isn't used on the default hierarchy. The flag is removed. v2: Updated documentation for cgroup__DEVEL__legacy_files_on_dfl. v3: Clear CFTYPE_ONLY_ON_DFL and CFTYPE_INSANE when cfts are removed as suggested by Li. Signed-off-by: Tejun Heo Acked-by: Neil Horman Acked-by: Li Zefan Cc: Johannes Weiner Cc: Michal Hocko Cc: Vivek Goyal Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Ingo Molnar Cc: Arnaldo Carvalho de Melo Cc: Aristeu Rozanski Cc: Aneesh Kumar K.V --- Documentation/cgroups/unified-hierarchy.txt | 18 ++++++--- include/linux/cgroup.h | 9 ++++- kernel/cgroup.c | 62 +++++++++++++++++++++++++++-- mm/memcontrol.c | 1 - 4 files changed, 78 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/Documentation/cgroups/unified-hierarchy.txt b/Documentation/cgroups/unified-hierarchy.txt index a7a2205539a7..4f4563277864 100644 --- a/Documentation/cgroups/unified-hierarchy.txt +++ b/Documentation/cgroups/unified-hierarchy.txt @@ -94,12 +94,18 @@ change soon. mount -t cgroup -o __DEVEL__sane_behavior cgroup $MOUNT_POINT -All controllers which are not bound to other hierarchies are -automatically bound to unified hierarchy and show up at the root of -it. Controllers which are enabled only in the root of unified -hierarchy can be bound to other hierarchies. This allows mixing -unified hierarchy with the traditional multiple hierarchies in a fully -backward compatible way. +All controllers which support the unified hierarchy and are not bound +to other hierarchies are automatically bound to unified hierarchy and +show up at the root of it. Controllers which are enabled only in the +root of unified hierarchy can be bound to other hierarchies. This +allows mixing unified hierarchy with the traditional multiple +hierarchies in a fully backward compatible way. + +For development purposes, the following boot parameter makes all +controllers to appear on the unified hierarchy whether supported or +not. + + cgroup__DEVEL__legacy_files_on_dfl A controller can be moved across hierarchies only after the controller is no longer referenced in its current hierarchy. Because per-cgroup diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index f5f0feef2701..9f76236ac158 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -590,6 +590,7 @@ static inline void pr_cont_cgroup_path(struct cgroup *cgrp) char *task_cgroup_path(struct task_struct *task, char *buf, size_t buflen); +int cgroup_add_dfl_cftypes(struct cgroup_subsys *ss, struct cftype *cfts); int cgroup_add_legacy_cftypes(struct cgroup_subsys *ss, struct cftype *cfts); int cgroup_rm_cftypes(struct cftype *cfts); @@ -671,8 +672,12 @@ struct cgroup_subsys { */ struct list_head cfts; - /* base cftypes, automatically registered with subsys itself */ - struct cftype *legacy_cftypes; /* used on the legacy hierarchies */ + /* + * Base cftypes which are automatically registered. The two can + * point to the same array. + */ + struct cftype *dfl_cftypes; /* for the default hierarchy */ + struct cftype *legacy_cftypes; /* for the legacy hierarchies */ /* * A subsystem may depend on other subsystems. When such subsystem diff --git a/kernel/cgroup.c b/kernel/cgroup.c index c275aa439a6f..374ebdf74f35 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -149,6 +149,12 @@ struct cgroup_root cgrp_dfl_root; */ static bool cgrp_dfl_root_visible; +/* + * Set by the boot param of the same name and makes subsystems with NULL + * ->dfl_files to use ->legacy_files on the default hierarchy. + */ +static bool cgroup_legacy_files_on_dfl; + /* some controllers are not supported in the default hierarchy */ static const unsigned int cgrp_dfl_root_inhibit_ss_mask = 0 #ifdef CONFIG_CGROUP_DEBUG @@ -3085,6 +3091,9 @@ static void cgroup_exit_cftypes(struct cftype *cfts) kfree(cft->kf_ops); cft->kf_ops = NULL; cft->ss = NULL; + + /* revert flags set by cgroup core while adding @cfts */ + cft->flags &= ~(CFTYPE_ONLY_ON_DFL | CFTYPE_INSANE); } } @@ -3195,8 +3204,37 @@ static int cgroup_add_cftypes(struct cgroup_subsys *ss, struct cftype *cfts) return ret; } +/** + * cgroup_add_dfl_cftypes - add an array of cftypes for default hierarchy + * @ss: target cgroup subsystem + * @cfts: zero-length name terminated array of cftypes + * + * Similar to cgroup_add_cftypes() but the added files are only used for + * the default hierarchy. + */ +int cgroup_add_dfl_cftypes(struct cgroup_subsys *ss, struct cftype *cfts) +{ + struct cftype *cft; + + for (cft = cfts; cft && cft->name[0] != '\0'; cft++) + cft->flags |= CFTYPE_ONLY_ON_DFL; + return cgroup_add_cftypes(ss, cfts); +} + +/** + * cgroup_add_legacy_cftypes - add an array of cftypes for legacy hierarchies + * @ss: target cgroup subsystem + * @cfts: zero-length name terminated array of cftypes + * + * Similar to cgroup_add_cftypes() but the added files are only used for + * the legacy hierarchies. + */ int cgroup_add_legacy_cftypes(struct cgroup_subsys *ss, struct cftype *cfts) { + struct cftype *cft; + + for (cft = cfts; cft && cft->name[0] != '\0'; cft++) + cft->flags |= CFTYPE_INSANE; return cgroup_add_cftypes(ss, cfts); } @@ -4893,9 +4931,19 @@ int __init cgroup_init(void) * disabled flag and cftype registration needs kmalloc, * both of which aren't available during early_init. */ - if (!ss->disabled) { - cgrp_dfl_root.subsys_mask |= 1 << ss->id; - WARN_ON(cgroup_add_cftypes(ss, ss->legacy_cftypes)); + if (ss->disabled) + continue; + + cgrp_dfl_root.subsys_mask |= 1 << ss->id; + + if (cgroup_legacy_files_on_dfl && !ss->dfl_cftypes) + ss->dfl_cftypes = ss->legacy_cftypes; + + if (ss->dfl_cftypes == ss->legacy_cftypes) { + WARN_ON(cgroup_add_cftypes(ss, ss->dfl_cftypes)); + } else { + WARN_ON(cgroup_add_dfl_cftypes(ss, ss->dfl_cftypes)); + WARN_ON(cgroup_add_legacy_cftypes(ss, ss->legacy_cftypes)); } } @@ -5291,6 +5339,14 @@ static int __init cgroup_disable(char *str) } __setup("cgroup_disable=", cgroup_disable); +static int __init cgroup_set_legacy_files_on_dfl(char *str) +{ + printk("cgroup: using legacy files on the default hierarchy\n"); + cgroup_legacy_files_on_dfl = true; + return 0; +} +__setup("cgroup__DEVEL__legacy_files_on_dfl", cgroup_set_legacy_files_on_dfl); + /** * css_tryget_online_from_dir - get corresponding css from a cgroup dentry * @dentry: directory dentry of interest diff --git a/mm/memcontrol.c b/mm/memcontrol.c index b6b3c6fea509..45c10c6fc3ce 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -6003,7 +6003,6 @@ static struct cftype mem_cgroup_files[] = { }, { .name = "use_hierarchy", - .flags = CFTYPE_INSANE, .write_u64 = mem_cgroup_hierarchy_write, .read_u64 = mem_cgroup_hierarchy_read, }, -- cgit v1.2.3 From 05ebb6e60f044a9cef2549b6204559276500f363 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 15 Jul 2014 11:05:10 -0400 Subject: cgroup: make CFTYPE_ONLY_ON_DFL and CFTYPE_NO_ internal to cgroup core cgroup now distinguishes cftypes for the default and legacy hierarchies more explicitly by using separate arrays and CFTYPE_ONLY_ON_DFL and CFTYPE_INSANE should be and are used only inside cgroup core proper. Let's make it clear that the flags are internal by prefixing them with double underscores. CFTYPE_INSANE is renamed to __CFTYPE_NOT_ON_DFL for consistency. The two flags are also collected and assigned bits >= 16 so that they aren't mixed with the published flags. v2: Convert the extra ones in cgroup_exit_cftypes() which are added by revision to the previous patch. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 6 ++++-- kernel/cgroup.c | 10 +++++----- 2 files changed, 9 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 9f76236ac158..b5223c570eba 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -384,9 +384,11 @@ struct css_set { enum { CFTYPE_ONLY_ON_ROOT = (1 << 0), /* only create on root cgrp */ CFTYPE_NOT_ON_ROOT = (1 << 1), /* don't create on root cgrp */ - CFTYPE_INSANE = (1 << 2), /* don't create if sane_behavior */ CFTYPE_NO_PREFIX = (1 << 3), /* (DON'T USE FOR NEW FILES) no subsys prefix */ - CFTYPE_ONLY_ON_DFL = (1 << 4), /* only on default hierarchy */ + + /* internal flags, do not use outside cgroup core proper */ + __CFTYPE_ONLY_ON_DFL = (1 << 16), /* only on default hierarchy */ + __CFTYPE_NOT_ON_DFL = (1 << 17), /* not on default hierarchy */ }; #define MAX_CFTYPE_NAME 64 diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 374ebdf74f35..f41d164a3d54 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -3031,9 +3031,9 @@ static int cgroup_addrm_files(struct cgroup *cgrp, struct cftype cfts[], for (cft = cfts; cft->name[0] != '\0'; cft++) { /* does cft->flags tell us to skip this file on @cgrp? */ - if ((cft->flags & CFTYPE_ONLY_ON_DFL) && !cgroup_on_dfl(cgrp)) + if ((cft->flags & __CFTYPE_ONLY_ON_DFL) && !cgroup_on_dfl(cgrp)) continue; - if ((cft->flags & CFTYPE_INSANE) && cgroup_on_dfl(cgrp)) + if ((cft->flags & __CFTYPE_NOT_ON_DFL) && cgroup_on_dfl(cgrp)) continue; if ((cft->flags & CFTYPE_NOT_ON_ROOT) && !cgroup_parent(cgrp)) continue; @@ -3093,7 +3093,7 @@ static void cgroup_exit_cftypes(struct cftype *cfts) cft->ss = NULL; /* revert flags set by cgroup core while adding @cfts */ - cft->flags &= ~(CFTYPE_ONLY_ON_DFL | CFTYPE_INSANE); + cft->flags &= ~(__CFTYPE_ONLY_ON_DFL | __CFTYPE_NOT_ON_DFL); } } @@ -3217,7 +3217,7 @@ int cgroup_add_dfl_cftypes(struct cgroup_subsys *ss, struct cftype *cfts) struct cftype *cft; for (cft = cfts; cft && cft->name[0] != '\0'; cft++) - cft->flags |= CFTYPE_ONLY_ON_DFL; + cft->flags |= __CFTYPE_ONLY_ON_DFL; return cgroup_add_cftypes(ss, cfts); } @@ -3234,7 +3234,7 @@ int cgroup_add_legacy_cftypes(struct cgroup_subsys *ss, struct cftype *cfts) struct cftype *cft; for (cft = cfts; cft && cft->name[0] != '\0'; cft++) - cft->flags |= CFTYPE_INSANE; + cft->flags |= __CFTYPE_NOT_ON_DFL; return cgroup_add_cftypes(ss, cfts); } -- cgit v1.2.3 From b9ba6f94b2382ef832f97122976b73004f714714 Mon Sep 17 00:00:00 2001 From: Niu Yawei Date: Wed, 4 Jun 2014 12:23:19 +0800 Subject: quota: remove dqptr_sem Remove dqptr_sem to make quota code scalable: Remove the dqptr_sem, accessing inode->i_dquot now protected by dquot_srcu, and changing inode->i_dquot is now serialized by dq_data_lock. Signed-off-by: Lai Siyao Signed-off-by: Niu Yawei Signed-off-by: Jan Kara --- fs/quota/dquot.c | 114 ++++++++++++++++++++++---------------------------- fs/super.c | 1 - include/linux/quota.h | 1 - 3 files changed, 49 insertions(+), 67 deletions(-) (limited to 'include') diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index fb2d2e2a89e7..f2d0eee9d1f1 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -96,13 +96,16 @@ * Note that some things (eg. sb pointer, type, id) doesn't change during * the life of the dquot structure and so needn't to be protected by a lock * - * Any operation working on dquots via inode pointers must hold dqptr_sem. If - * operation is just reading pointers from inode (or not using them at all) the - * read lock is enough. If pointers are altered function must hold write lock. + * Operation accessing dquots via inode pointers are protected by dquot_srcu. + * Operation of reading pointer needs srcu_read_lock(&dquot_srcu), and + * synchronize_srcu(&dquot_srcu) is called after clearing pointers from + * inode and before dropping dquot references to avoid use of dquots after + * they are freed. dq_data_lock is used to serialize the pointer setting and + * clearing operations. * Special care needs to be taken about S_NOQUOTA inode flag (marking that * inode is a quota file). Functions adding pointers from inode to dquots have - * to check this flag under dqptr_sem and then (if S_NOQUOTA is not set) they - * have to do all pointer modifications before dropping dqptr_sem. This makes + * to check this flag under dq_data_lock and then (if S_NOQUOTA is not set) they + * have to do all pointer modifications before dropping dq_data_lock. This makes * sure they cannot race with quotaon which first sets S_NOQUOTA flag and * then drops all pointers to dquots from an inode. * @@ -116,21 +119,15 @@ * spinlock to internal buffers before writing. * * Lock ordering (including related VFS locks) is the following: - * dqonoff_mutex > i_mutex > journal_lock > dqptr_sem > dquot->dq_lock > - * dqio_mutex + * dqonoff_mutex > i_mutex > journal_lock > dquot->dq_lock > dqio_mutex * dqonoff_mutex > i_mutex comes from dquot_quota_sync, dquot_enable, etc. - * The lock ordering of dqptr_sem imposed by quota code is only dqonoff_sem > - * dqptr_sem. But filesystem has to count with the fact that functions such as - * dquot_alloc_space() acquire dqptr_sem and they usually have to be called - * from inside a transaction to keep filesystem consistency after a crash. Also - * filesystems usually want to do some IO on dquot from ->mark_dirty which is - * called with dqptr_sem held. */ static __cacheline_aligned_in_smp DEFINE_SPINLOCK(dq_list_lock); static __cacheline_aligned_in_smp DEFINE_SPINLOCK(dq_state_lock); __cacheline_aligned_in_smp DEFINE_SPINLOCK(dq_data_lock); EXPORT_SYMBOL(dq_data_lock); +DEFINE_STATIC_SRCU(dquot_srcu); void __quota_error(struct super_block *sb, const char *func, const char *fmt, ...) @@ -964,7 +961,6 @@ static void add_dquot_ref(struct super_block *sb, int type) /* * Remove references to dquots from inode and add dquot to list for freeing * if we have the last reference to dquot - * We can't race with anybody because we hold dqptr_sem for writing... */ static void remove_inode_dquot_ref(struct inode *inode, int type, struct list_head *tofree_head) @@ -1024,13 +1020,15 @@ static void remove_dquot_ref(struct super_block *sb, int type, * We have to scan also I_NEW inodes because they can already * have quota pointer initialized. Luckily, we need to touch * only quota pointers and these have separate locking - * (dqptr_sem). + * (dq_data_lock). */ + spin_lock(&dq_data_lock); if (!IS_NOQUOTA(inode)) { if (unlikely(inode_get_rsv_space(inode) > 0)) reserved = 1; remove_inode_dquot_ref(inode, type, tofree_head); } + spin_unlock(&dq_data_lock); } spin_unlock(&inode_sb_list_lock); #ifdef CONFIG_QUOTA_DEBUG @@ -1048,9 +1046,8 @@ static void drop_dquot_ref(struct super_block *sb, int type) LIST_HEAD(tofree_head); if (sb->dq_op) { - down_write(&sb_dqopt(sb)->dqptr_sem); remove_dquot_ref(sb, type, &tofree_head); - up_write(&sb_dqopt(sb)->dqptr_sem); + synchronize_srcu(&dquot_srcu); put_dquot_list(&tofree_head); } } @@ -1381,9 +1378,6 @@ static int dquot_active(const struct inode *inode) /* * Initialize quota pointers in inode * - * We do things in a bit complicated way but by that we avoid calling - * dqget() and thus filesystem callbacks under dqptr_sem. - * * It is better to call this function outside of any transaction as it * might need a lot of space in journal for dquot structure allocation. */ @@ -1394,8 +1388,6 @@ static void __dquot_initialize(struct inode *inode, int type) struct super_block *sb = inode->i_sb; qsize_t rsv; - /* First test before acquiring mutex - solves deadlocks when we - * re-enter the quota code and are already holding the mutex */ if (!dquot_active(inode)) return; @@ -1429,7 +1421,7 @@ static void __dquot_initialize(struct inode *inode, int type) if (!init_needed) return; - down_write(&sb_dqopt(sb)->dqptr_sem); + spin_lock(&dq_data_lock); if (IS_NOQUOTA(inode)) goto out_err; for (cnt = 0; cnt < MAXQUOTAS; cnt++) { @@ -1449,15 +1441,12 @@ static void __dquot_initialize(struct inode *inode, int type) * did a write before quota was turned on */ rsv = inode_get_rsv_space(inode); - if (unlikely(rsv)) { - spin_lock(&dq_data_lock); + if (unlikely(rsv)) dquot_resv_space(inode->i_dquot[cnt], rsv); - spin_unlock(&dq_data_lock); - } } } out_err: - up_write(&sb_dqopt(sb)->dqptr_sem); + spin_unlock(&dq_data_lock); /* Drop unused references */ dqput_all(got); } @@ -1469,19 +1458,24 @@ void dquot_initialize(struct inode *inode) EXPORT_SYMBOL(dquot_initialize); /* - * Release all quotas referenced by inode + * Release all quotas referenced by inode. + * + * This function only be called on inode free or converting + * a file to quota file, no other users for the i_dquot in + * both cases, so we needn't call synchronize_srcu() after + * clearing i_dquot. */ static void __dquot_drop(struct inode *inode) { int cnt; struct dquot *put[MAXQUOTAS]; - down_write(&sb_dqopt(inode->i_sb)->dqptr_sem); + spin_lock(&dq_data_lock); for (cnt = 0; cnt < MAXQUOTAS; cnt++) { put[cnt] = inode->i_dquot[cnt]; inode->i_dquot[cnt] = NULL; } - up_write(&sb_dqopt(inode->i_sb)->dqptr_sem); + spin_unlock(&dq_data_lock); dqput_all(put); } @@ -1599,15 +1593,11 @@ static void inode_decr_space(struct inode *inode, qsize_t number, int reserve) */ int __dquot_alloc_space(struct inode *inode, qsize_t number, int flags) { - int cnt, ret = 0; + int cnt, ret = 0, index; struct dquot_warn warn[MAXQUOTAS]; struct dquot **dquots = inode->i_dquot; int reserve = flags & DQUOT_SPACE_RESERVE; - /* - * First test before acquiring mutex - solves deadlocks when we - * re-enter the quota code and are already holding the mutex - */ if (!dquot_active(inode)) { inode_incr_space(inode, number, reserve); goto out; @@ -1616,7 +1606,7 @@ int __dquot_alloc_space(struct inode *inode, qsize_t number, int flags) for (cnt = 0; cnt < MAXQUOTAS; cnt++) warn[cnt].w_type = QUOTA_NL_NOWARN; - down_read(&sb_dqopt(inode->i_sb)->dqptr_sem); + index = srcu_read_lock(&dquot_srcu); spin_lock(&dq_data_lock); for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (!dquots[cnt]) @@ -1643,7 +1633,7 @@ int __dquot_alloc_space(struct inode *inode, qsize_t number, int flags) goto out_flush_warn; mark_all_dquot_dirty(dquots); out_flush_warn: - up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); + srcu_read_unlock(&dquot_srcu, index); flush_warnings(warn); out: return ret; @@ -1655,17 +1645,16 @@ EXPORT_SYMBOL(__dquot_alloc_space); */ int dquot_alloc_inode(const struct inode *inode) { - int cnt, ret = 0; + int cnt, ret = 0, index; struct dquot_warn warn[MAXQUOTAS]; struct dquot * const *dquots = inode->i_dquot; - /* First test before acquiring mutex - solves deadlocks when we - * re-enter the quota code and are already holding the mutex */ if (!dquot_active(inode)) return 0; for (cnt = 0; cnt < MAXQUOTAS; cnt++) warn[cnt].w_type = QUOTA_NL_NOWARN; - down_read(&sb_dqopt(inode->i_sb)->dqptr_sem); + + index = srcu_read_lock(&dquot_srcu); spin_lock(&dq_data_lock); for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (!dquots[cnt]) @@ -1685,7 +1674,7 @@ warn_put_all: spin_unlock(&dq_data_lock); if (ret == 0) mark_all_dquot_dirty(dquots); - up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); + srcu_read_unlock(&dquot_srcu, index); flush_warnings(warn); return ret; } @@ -1696,14 +1685,14 @@ EXPORT_SYMBOL(dquot_alloc_inode); */ int dquot_claim_space_nodirty(struct inode *inode, qsize_t number) { - int cnt; + int cnt, index; if (!dquot_active(inode)) { inode_claim_rsv_space(inode, number); return 0; } - down_read(&sb_dqopt(inode->i_sb)->dqptr_sem); + index = srcu_read_lock(&dquot_srcu); spin_lock(&dq_data_lock); /* Claim reserved quotas to allocated quotas */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { @@ -1715,7 +1704,7 @@ int dquot_claim_space_nodirty(struct inode *inode, qsize_t number) inode_claim_rsv_space(inode, number); spin_unlock(&dq_data_lock); mark_all_dquot_dirty(inode->i_dquot); - up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); + srcu_read_unlock(&dquot_srcu, index); return 0; } EXPORT_SYMBOL(dquot_claim_space_nodirty); @@ -1725,14 +1714,14 @@ EXPORT_SYMBOL(dquot_claim_space_nodirty); */ void dquot_reclaim_space_nodirty(struct inode *inode, qsize_t number) { - int cnt; + int cnt, index; if (!dquot_active(inode)) { inode_reclaim_rsv_space(inode, number); return; } - down_read(&sb_dqopt(inode->i_sb)->dqptr_sem); + index = srcu_read_lock(&dquot_srcu); spin_lock(&dq_data_lock); /* Claim reserved quotas to allocated quotas */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { @@ -1744,7 +1733,7 @@ void dquot_reclaim_space_nodirty(struct inode *inode, qsize_t number) inode_reclaim_rsv_space(inode, number); spin_unlock(&dq_data_lock); mark_all_dquot_dirty(inode->i_dquot); - up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); + srcu_read_unlock(&dquot_srcu, index); return; } EXPORT_SYMBOL(dquot_reclaim_space_nodirty); @@ -1757,16 +1746,14 @@ void __dquot_free_space(struct inode *inode, qsize_t number, int flags) unsigned int cnt; struct dquot_warn warn[MAXQUOTAS]; struct dquot **dquots = inode->i_dquot; - int reserve = flags & DQUOT_SPACE_RESERVE; + int reserve = flags & DQUOT_SPACE_RESERVE, index; - /* First test before acquiring mutex - solves deadlocks when we - * re-enter the quota code and are already holding the mutex */ if (!dquot_active(inode)) { inode_decr_space(inode, number, reserve); return; } - down_read(&sb_dqopt(inode->i_sb)->dqptr_sem); + index = srcu_read_lock(&dquot_srcu); spin_lock(&dq_data_lock); for (cnt = 0; cnt < MAXQUOTAS; cnt++) { int wtype; @@ -1789,7 +1776,7 @@ void __dquot_free_space(struct inode *inode, qsize_t number, int flags) goto out_unlock; mark_all_dquot_dirty(dquots); out_unlock: - up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); + srcu_read_unlock(&dquot_srcu, index); flush_warnings(warn); } EXPORT_SYMBOL(__dquot_free_space); @@ -1802,13 +1789,12 @@ void dquot_free_inode(const struct inode *inode) unsigned int cnt; struct dquot_warn warn[MAXQUOTAS]; struct dquot * const *dquots = inode->i_dquot; + int index; - /* First test before acquiring mutex - solves deadlocks when we - * re-enter the quota code and are already holding the mutex */ if (!dquot_active(inode)) return; - down_read(&sb_dqopt(inode->i_sb)->dqptr_sem); + index = srcu_read_lock(&dquot_srcu); spin_lock(&dq_data_lock); for (cnt = 0; cnt < MAXQUOTAS; cnt++) { int wtype; @@ -1823,7 +1809,7 @@ void dquot_free_inode(const struct inode *inode) } spin_unlock(&dq_data_lock); mark_all_dquot_dirty(dquots); - up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); + srcu_read_unlock(&dquot_srcu, index); flush_warnings(warn); } EXPORT_SYMBOL(dquot_free_inode); @@ -1837,6 +1823,8 @@ EXPORT_SYMBOL(dquot_free_inode); * This operation can block, but only after everything is updated * A transaction must be started when entering this function. * + * We are holding reference on transfer_from & transfer_to, no need to + * protect them by srcu_read_lock(). */ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to) { @@ -1849,8 +1837,6 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to) struct dquot_warn warn_from_inodes[MAXQUOTAS]; struct dquot_warn warn_from_space[MAXQUOTAS]; - /* First test before acquiring mutex - solves deadlocks when we - * re-enter the quota code and are already holding the mutex */ if (IS_NOQUOTA(inode)) return 0; /* Initialize the arrays */ @@ -1859,12 +1845,12 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to) warn_from_inodes[cnt].w_type = QUOTA_NL_NOWARN; warn_from_space[cnt].w_type = QUOTA_NL_NOWARN; } - down_write(&sb_dqopt(inode->i_sb)->dqptr_sem); + + spin_lock(&dq_data_lock); if (IS_NOQUOTA(inode)) { /* File without quota accounting? */ - up_write(&sb_dqopt(inode->i_sb)->dqptr_sem); + spin_unlock(&dq_data_lock); return 0; } - spin_lock(&dq_data_lock); cur_space = inode_get_bytes(inode); rsv_space = inode_get_rsv_space(inode); space = cur_space + rsv_space; @@ -1918,7 +1904,6 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to) inode->i_dquot[cnt] = transfer_to[cnt]; } spin_unlock(&dq_data_lock); - up_write(&sb_dqopt(inode->i_sb)->dqptr_sem); mark_all_dquot_dirty(transfer_from); mark_all_dquot_dirty(transfer_to); @@ -1932,7 +1917,6 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to) return 0; over_quota: spin_unlock(&dq_data_lock); - up_write(&sb_dqopt(inode->i_sb)->dqptr_sem); flush_warnings(warn_to); return ret; } diff --git a/fs/super.c b/fs/super.c index d20d5b11dedf..872b26bf06dd 100644 --- a/fs/super.c +++ b/fs/super.c @@ -218,7 +218,6 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags) lockdep_set_class(&s->s_vfs_rename_mutex, &type->s_vfs_rename_key); mutex_init(&s->s_dquot.dqio_mutex); mutex_init(&s->s_dquot.dqonoff_mutex); - init_rwsem(&s->s_dquot.dqptr_sem); s->s_maxbytes = MAX_NON_LFS; s->s_op = &default_op; s->s_time_gran = 1000000000; diff --git a/include/linux/quota.h b/include/linux/quota.h index 0f3c5d38da1f..80d345a3524c 100644 --- a/include/linux/quota.h +++ b/include/linux/quota.h @@ -390,7 +390,6 @@ struct quota_info { unsigned int flags; /* Flags for diskquotas on this device */ struct mutex dqio_mutex; /* lock device while I/O in progress */ struct mutex dqonoff_mutex; /* Serialize quotaon & quotaoff */ - struct rw_semaphore dqptr_sem; /* serialize ops using quota_info struct, pointers from inode to dquots */ struct inode *files[MAXQUOTAS]; /* inodes of quotafiles */ struct mem_dqinfo info[MAXQUOTAS]; /* Information for each quota type */ const struct quota_format_ops *ops[MAXQUOTAS]; /* Operations for each type */ -- cgit v1.2.3 From 685343fc3ba61a1f6eef361b786601123db16c28 Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Mon, 14 Jul 2014 16:37:22 +0200 Subject: net: add name_assign_type netdev attribute Based on a patch by David Herrmann. The name_assign_type attribute gives hints where the interface name of a given net-device comes from. These values are currently defined: NET_NAME_ENUM: The ifname is provided by the kernel with an enumerated suffix, typically based on order of discovery. Names may be reused and unpredictable. NET_NAME_PREDICTABLE: The ifname has been assigned by the kernel in a predictable way that is guaranteed to avoid reuse and always be the same for a given device. Examples include statically created devices like the loopback device and names deduced from hardware properties (including being given explicitly by the firmware). Names depending on the order of discovery, or in any other way on the existence of other devices, must not be marked as PREDICTABLE. NET_NAME_USER: The ifname was provided by user-space during net-device setup. NET_NAME_RENAMED: The net-device has been renamed from userspace. Once this type is set, it cannot change again. NET_NAME_UNKNOWN: This is an internal placeholder to indicate that we yet haven't yet categorized the name. It will not be exposed to userspace, rather -EINVAL is returned. The aim of these patches is to improve user-space renaming of interfaces. As a general rule, userspace must rename interfaces to guarantee that names stay the same every time a given piece of hardware appears (at boot, or when attaching it). However, there are several situations where userspace should not perform the renaming, and that depends on both the policy of the local admin, but crucially also on the nature of the current interface name. If an interface was created in repsonse to a userspace request, and userspace already provided a name, we most probably want to leave that name alone. The main instance of this is wifi-P2P devices created over nl80211, which currently have a long-standing bug where they are getting renamed by udev. We label such names NET_NAME_USER. If an interface, unbeknown to us, has already been renamed from userspace, we most probably want to leave also that alone. This will typically happen when third-party plugins (for instance to udev, but the interface is generic so could be from anywhere) renames the interface without informing udev about it. A typical situation is when you switch root from an installer or an initrd to the real system and the new instance of udev does not know what happened before the switch. These types of problems have caused repeated issues in the past. To solve this, once an interface has been renamed, its name is labelled NET_NAME_RENAMED. In many cases, the kernel is actually able to name interfaces in such a way that there is no need for userspace to rename them. This is the case when the enumeration order of devices, or in fact any other (non-parent) device on the system, can not influence the name of the interface. Examples include statically created devices, or any naming schemes based on hardware properties of the interface. In this case the admin may prefer to use the kernel-provided names, and to make that possible we label such names NET_NAME_PREDICTABLE. We want the kernel to have tho possibilty of performing predictable interface naming itself (and exposing to userspace that it has), as the information necessary for a proper naming scheme for a certain class of devices may not be exposed to userspace. The case where renaming is almost certainly desired, is when the kernel has given the interface a name using global device enumeration based on order of discovery (ethX, wlanY, etc). These naming schemes are labelled NET_NAME_ENUM. Lastly, a fallback is left as NET_NAME_UNKNOWN, to indicate that a driver has not yet been ported. This is mostly useful as a transitionary measure, allowing us to label the various naming schemes bit by bit. v8: minor documentation fixes v9: move comment to the right commit Signed-off-by: Tom Gundersen Reviewed-by: David Herrmann Reviewed-by: Kay Sievers Signed-off-by: David S. Miller --- Documentation/ABI/testing/sysfs-class-net | 11 +++++++++++ include/linux/netdevice.h | 2 ++ include/uapi/linux/netdevice.h | 6 ++++++ net/core/net-sysfs.c | 20 ++++++++++++++++++++ 4 files changed, 39 insertions(+) (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-class-net b/Documentation/ABI/testing/sysfs-class-net index 416c5d59f52e..d322b0581194 100644 --- a/Documentation/ABI/testing/sysfs-class-net +++ b/Documentation/ABI/testing/sysfs-class-net @@ -1,3 +1,14 @@ +What: /sys/class/net//name_assign_type +Date: July 2014 +KernelVersion: 3.17 +Contact: netdev@vger.kernel.org +Description: + Indicates the name assignment type. Possible values are: + 1: enumerated by the kernel, possibly in an unpredictable way + 2: predictably named by the kernel + 3: named by userspace + 4: renamed + What: /sys/class/net//addr_assign_type Date: July 2010 KernelVersion: 3.2 diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 3a320db96180..9be34732142f 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1381,6 +1381,8 @@ struct net_device { struct kset *queues_kset; #endif + unsigned char name_assign_type; + bool uc_promisc; unsigned int promiscuity; unsigned int allmulti; diff --git a/include/uapi/linux/netdevice.h b/include/uapi/linux/netdevice.h index fdfbd1c17065..55818543342d 100644 --- a/include/uapi/linux/netdevice.h +++ b/include/uapi/linux/netdevice.h @@ -37,6 +37,12 @@ #define INIT_NETDEV_GROUP 0 +/* interface name assignment types (sysfs name_assign_type attribute) */ +#define NET_NAME_UNKNOWN 0 /* unknown origin (not exposed to userspace) */ +#define NET_NAME_ENUM 1 /* enumerated by kernel */ +#define NET_NAME_PREDICTABLE 2 /* predictably named by the kernel */ +#define NET_NAME_USER 3 /* provided by user-space */ +#define NET_NAME_RENAMED 4 /* renamed by user-space */ /* Media selection options. */ enum { diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 1cac29ebb05b..7752f2ad49a5 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -112,6 +112,25 @@ NETDEVICE_SHOW_RO(ifindex, fmt_dec); NETDEVICE_SHOW_RO(type, fmt_dec); NETDEVICE_SHOW_RO(link_mode, fmt_dec); +static ssize_t format_name_assign_type(const struct net_device *net, char *buf) +{ + return sprintf(buf, fmt_dec, net->name_assign_type); +} + +static ssize_t name_assign_type_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct net_device *net = to_net_dev(dev); + ssize_t ret = -EINVAL; + + if (net->name_assign_type != NET_NAME_UNKNOWN) + ret = netdev_show(dev, attr, buf, format_name_assign_type); + + return ret; +} +static DEVICE_ATTR_RO(name_assign_type); + /* use same locking rules as GIFHWADDR ioctl's */ static ssize_t address_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -387,6 +406,7 @@ static struct attribute *net_class_attrs[] = { &dev_attr_dev_port.attr, &dev_attr_iflink.attr, &dev_attr_ifindex.attr, + &dev_attr_name_assign_type.attr, &dev_attr_addr_assign_type.attr, &dev_attr_addr_len.attr, &dev_attr_link_mode.attr, -- cgit v1.2.3 From c835a677331495cf137a7f8a023463afd9f032f8 Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Mon, 14 Jul 2014 16:37:24 +0200 Subject: net: set name_assign_type in alloc_netdev() Extend alloc_netdev{,_mq{,s}}() to take name_assign_type as argument, and convert all users to pass NET_NAME_UNKNOWN. Coccinelle patch: @@ expression sizeof_priv, name, setup, txqs, rxqs, count; @@ ( -alloc_netdev_mqs(sizeof_priv, name, setup, txqs, rxqs) +alloc_netdev_mqs(sizeof_priv, name, NET_NAME_UNKNOWN, setup, txqs, rxqs) | -alloc_netdev_mq(sizeof_priv, name, setup, count) +alloc_netdev_mq(sizeof_priv, name, NET_NAME_UNKNOWN, setup, count) | -alloc_netdev(sizeof_priv, name, setup) +alloc_netdev(sizeof_priv, name, NET_NAME_UNKNOWN, setup) ) v9: move comments here from the wrong commit Signed-off-by: Tom Gundersen Reviewed-by: David Herrmann Signed-off-by: David S. Miller --- drivers/firewire/net.c | 3 ++- drivers/hsi/clients/ssi_protocol.c | 2 +- drivers/infiniband/hw/amso1100/c2_provider.c | 2 +- drivers/infiniband/ulp/ipoib/ipoib_main.c | 4 ++-- drivers/isdn/i4l/isdn_net.c | 3 ++- drivers/media/dvb-core/dvb_net.c | 3 ++- drivers/misc/sgi-xp/xpnet.c | 3 ++- drivers/net/arcnet/arcnet.c | 3 ++- drivers/net/bonding/bond_main.c | 2 +- drivers/net/caif/caif_serial.c | 3 ++- drivers/net/caif/caif_spi.c | 4 ++-- drivers/net/caif/caif_virtio.c | 2 +- drivers/net/can/dev.c | 2 +- drivers/net/can/slcan.c | 2 +- drivers/net/dummy.c | 2 +- drivers/net/eql.c | 3 ++- drivers/net/ethernet/8390/lib8390.c | 2 +- drivers/net/ethernet/tile/tilegx.c | 4 ++-- drivers/net/ethernet/tile/tilepro.c | 3 ++- drivers/net/hamradio/6pack.c | 3 ++- drivers/net/hamradio/baycom_epp.c | 2 +- drivers/net/hamradio/bpqether.c | 4 ++-- drivers/net/hamradio/dmascc.c | 4 ++-- drivers/net/hamradio/hdlcdrv.c | 2 +- drivers/net/hamradio/mkiss.c | 3 ++- drivers/net/hamradio/scc.c | 2 +- drivers/net/hamradio/yam.c | 2 +- drivers/net/ieee802154/fakehard.c | 3 ++- drivers/net/ifb.c | 4 ++-- drivers/net/loopback.c | 2 +- drivers/net/ppp/ppp_generic.c | 3 ++- drivers/net/slip/slip.c | 2 +- drivers/net/tun.c | 3 ++- drivers/net/usb/cdc-phonet.c | 2 +- drivers/net/usb/hso.c | 3 ++- drivers/net/wan/dlci.c | 4 ++-- drivers/net/wan/hdlc.c | 3 ++- drivers/net/wan/hdlc_fr.c | 5 +++-- drivers/net/wan/lapbether.c | 4 ++-- drivers/net/wan/sbni.c | 7 ++++--- drivers/net/wan/sdla.c | 3 ++- drivers/net/wan/x25_asy.c | 4 ++-- drivers/net/wimax/i2400m/usb.c | 2 +- drivers/net/wireless/airo.c | 5 +++-- drivers/net/wireless/ath/ath6kl/cfg80211.c | 2 +- drivers/net/wireless/ath/wil6210/netdev.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c | 3 ++- drivers/net/wireless/libertas/main.c | 2 +- drivers/net/wireless/libertas/mesh.c | 2 +- drivers/net/wireless/mac80211_hwsim.c | 3 ++- drivers/net/wireless/mwifiex/cfg80211.c | 3 ++- drivers/net/xen-netback/interface.c | 4 ++-- drivers/s390/net/claw.c | 2 +- drivers/s390/net/ctcm_main.c | 6 ++++-- drivers/s390/net/netiucv.c | 2 +- drivers/s390/net/qeth_l2_main.c | 6 ++++-- drivers/s390/net/qeth_l3_main.c | 3 ++- drivers/staging/cxt1e1/linux.c | 3 ++- drivers/staging/gdm724x/gdm_lte.c | 2 +- drivers/staging/gdm72xx/gdm_wimax.c | 3 ++- drivers/staging/vt6655/wpactl.c | 3 ++- drivers/staging/wlan-ng/p80211netdev.c | 2 +- drivers/tty/n_gsm.c | 5 ++--- drivers/usb/gadget/f_phonet.c | 3 ++- include/linux/netdevice.h | 10 ++++++---- net/802/fc.c | 2 +- net/802/fddi.c | 3 ++- net/802/hippi.c | 3 ++- net/8021q/vlan.c | 3 ++- net/appletalk/dev.c | 3 ++- net/atm/br2684.c | 4 ++-- net/atm/clip.c | 3 ++- net/batman-adv/soft-interface.c | 2 +- net/bluetooth/6lowpan.c | 2 +- net/bluetooth/bnep/core.c | 5 +++-- net/bridge/br_if.c | 2 +- net/core/dev.c | 13 ++++++++----- net/core/rtnetlink.c | 4 ++-- net/dsa/slave.c | 4 ++-- net/ethernet/eth.c | 3 ++- net/ipv4/ip_tunnel.c | 2 +- net/ipv4/ipmr.c | 2 +- net/ipv6/ip6_gre.c | 6 ++++-- net/ipv6/ip6_tunnel.c | 5 +++-- net/ipv6/ip6_vti.c | 4 ++-- net/ipv6/ip6mr.c | 2 +- net/ipv6/sit.c | 4 +++- net/irda/irda_device.c | 3 ++- net/irda/irlan/irlan_eth.c | 2 +- net/l2tp/l2tp_eth.c | 3 ++- net/mac80211/iface.c | 6 +++--- net/mac802154/ieee802154_dev.c | 6 ++++-- net/netrom/af_netrom.c | 2 +- net/openvswitch/vport-internal_dev.c | 3 ++- net/phonet/pep-gprs.c | 2 +- net/rose/af_rose.c | 2 +- net/sched/sch_teql.c | 4 ++-- 97 files changed, 185 insertions(+), 133 deletions(-) (limited to 'include') diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c index c3986452194d..2c68da1ceeee 100644 --- a/drivers/firewire/net.c +++ b/drivers/firewire/net.c @@ -1460,7 +1460,8 @@ static int fwnet_probe(struct fw_unit *unit, goto have_dev; } - net = alloc_netdev(sizeof(*dev), "firewire%d", fwnet_init_dev); + net = alloc_netdev(sizeof(*dev), "firewire%d", NET_NAME_UNKNOWN, + fwnet_init_dev); if (net == NULL) { mutex_unlock(&fwnet_device_mutex); return -ENOMEM; diff --git a/drivers/hsi/clients/ssi_protocol.c b/drivers/hsi/clients/ssi_protocol.c index ce4be3738d46..737fa2e0e782 100644 --- a/drivers/hsi/clients/ssi_protocol.c +++ b/drivers/hsi/clients/ssi_protocol.c @@ -1115,7 +1115,7 @@ static int ssi_protocol_probe(struct device *dev) goto out; } - ssi->netdev = alloc_netdev(0, ifname, ssip_pn_setup); + ssi->netdev = alloc_netdev(0, ifname, NET_NAME_UNKNOWN, ssip_pn_setup); if (!ssi->netdev) { dev_err(dev, "No memory for netdev\n"); err = -ENOMEM; diff --git a/drivers/infiniband/hw/amso1100/c2_provider.c b/drivers/infiniband/hw/amso1100/c2_provider.c index 8af33cf1fc4e..2d5cbf4363e4 100644 --- a/drivers/infiniband/hw/amso1100/c2_provider.c +++ b/drivers/infiniband/hw/amso1100/c2_provider.c @@ -734,7 +734,7 @@ static struct net_device *c2_pseudo_netdev_init(struct c2_dev *c2dev) /* change ethxxx to iwxxx */ strcpy(name, "iw"); strcat(name, &c2dev->netdev->name[3]); - netdev = alloc_netdev(0, name, setup); + netdev = alloc_netdev(0, name, NET_NAME_UNKNOWN, setup); if (!netdev) { printk(KERN_ERR PFX "%s - etherdev alloc failed", __func__); diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 5786a78ff8bc..4e675f4fecc9 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -1394,8 +1394,8 @@ struct ipoib_dev_priv *ipoib_intf_alloc(const char *name) { struct net_device *dev; - dev = alloc_netdev((int) sizeof (struct ipoib_dev_priv), name, - ipoib_setup); + dev = alloc_netdev((int)sizeof(struct ipoib_dev_priv), name, + NET_NAME_UNKNOWN, ipoib_setup); if (!dev) return NULL; diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c index d9aebbc510cc..c2ed6246a389 100644 --- a/drivers/isdn/i4l/isdn_net.c +++ b/drivers/isdn/i4l/isdn_net.c @@ -2588,7 +2588,8 @@ isdn_net_new(char *name, struct net_device *master) printk(KERN_WARNING "isdn_net: Could not allocate net-device\n"); return NULL; } - netdev->dev = alloc_netdev(sizeof(isdn_net_local), name, _isdn_setup); + netdev->dev = alloc_netdev(sizeof(isdn_net_local), name, + NET_NAME_UNKNOWN, _isdn_setup); if (!netdev->dev) { printk(KERN_WARNING "isdn_net: Could not allocate network device\n"); kfree(netdev); diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c index 8a86b3025637..059e6117f22b 100644 --- a/drivers/media/dvb-core/dvb_net.c +++ b/drivers/media/dvb-core/dvb_net.c @@ -1276,7 +1276,8 @@ static int dvb_net_add_if(struct dvb_net *dvbnet, u16 pid, u8 feedtype) if ((if_num = get_if(dvbnet)) < 0) return -EINVAL; - net = alloc_netdev(sizeof(struct dvb_net_priv), "dvb", dvb_net_setup); + net = alloc_netdev(sizeof(struct dvb_net_priv), "dvb", + NET_NAME_UNKNOWN, dvb_net_setup); if (!net) return -ENOMEM; diff --git a/drivers/misc/sgi-xp/xpnet.c b/drivers/misc/sgi-xp/xpnet.c index 3fac67a5204c..557f9782c53c 100644 --- a/drivers/misc/sgi-xp/xpnet.c +++ b/drivers/misc/sgi-xp/xpnet.c @@ -544,7 +544,8 @@ xpnet_init(void) * use ether_setup() to init the majority of our device * structure and then override the necessary pieces. */ - xpnet_device = alloc_netdev(0, XPNET_DEVICE_NAME, ether_setup); + xpnet_device = alloc_netdev(0, XPNET_DEVICE_NAME, NET_NAME_UNKNOWN, + ether_setup); if (xpnet_device == NULL) { kfree(xpnet_broadcast_partitions); return -ENOMEM; diff --git a/drivers/net/arcnet/arcnet.c b/drivers/net/arcnet/arcnet.c index a956053608f9..3b790de6c976 100644 --- a/drivers/net/arcnet/arcnet.c +++ b/drivers/net/arcnet/arcnet.c @@ -346,7 +346,8 @@ struct net_device *alloc_arcdev(const char *name) struct net_device *dev; dev = alloc_netdev(sizeof(struct arcnet_local), - name && *name ? name : "arc%d", arcdev_setup); + name && *name ? name : "arc%d", NET_NAME_UNKNOWN, + arcdev_setup); if(dev) { struct arcnet_local *lp = netdev_priv(dev); spin_lock_init(&lp->lock); diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 09dc3ef771a7..46dcb7b6216f 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -4420,7 +4420,7 @@ int bond_create(struct net *net, const char *name) rtnl_lock(); bond_dev = alloc_netdev_mq(sizeof(struct bonding), - name ? name : "bond%d", + name ? name : "bond%d", NET_NAME_UNKNOWN, bond_setup, tx_queues); if (!bond_dev) { pr_err("%s: eek! can't alloc netdev!\n", name); diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c index fc73865bb83a..27bbc56de15f 100644 --- a/drivers/net/caif/caif_serial.c +++ b/drivers/net/caif/caif_serial.c @@ -349,7 +349,8 @@ static int ldisc_open(struct tty_struct *tty) result = snprintf(name, sizeof(name), "cf%s", tty->name); if (result >= IFNAMSIZ) return -EINVAL; - dev = alloc_netdev(sizeof(*ser), name, caifdev_setup); + dev = alloc_netdev(sizeof(*ser), name, NET_NAME_UNKNOWN, + caifdev_setup); if (!dev) return -ENOMEM; diff --git a/drivers/net/caif/caif_spi.c b/drivers/net/caif/caif_spi.c index ff54c0eb2052..72ea9ff9bb9c 100644 --- a/drivers/net/caif/caif_spi.c +++ b/drivers/net/caif/caif_spi.c @@ -730,8 +730,8 @@ int cfspi_spi_probe(struct platform_device *pdev) int res; dev = (struct cfspi_dev *)pdev->dev.platform_data; - ndev = alloc_netdev(sizeof(struct cfspi), - "cfspi%d", cfspi_setup); + ndev = alloc_netdev(sizeof(struct cfspi), "cfspi%d", + NET_NAME_UNKNOWN, cfspi_setup); if (!dev) return -ENODEV; diff --git a/drivers/net/caif/caif_virtio.c b/drivers/net/caif/caif_virtio.c index 985608634f8c..a5fefb9059c5 100644 --- a/drivers/net/caif/caif_virtio.c +++ b/drivers/net/caif/caif_virtio.c @@ -661,7 +661,7 @@ static int cfv_probe(struct virtio_device *vdev) int err = -EINVAL; netdev = alloc_netdev(sizeof(struct cfv_info), cfv_netdev_name, - cfv_netdev_setup); + NET_NAME_UNKNOWN, cfv_netdev_setup); if (!netdev) return -ENOMEM; diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c index e318e87e2bfc..9f91fcba43f8 100644 --- a/drivers/net/can/dev.c +++ b/drivers/net/can/dev.c @@ -565,7 +565,7 @@ struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max) else size = sizeof_priv; - dev = alloc_netdev(size, "can%d", can_setup); + dev = alloc_netdev(size, "can%d", NET_NAME_UNKNOWN, can_setup); if (!dev) return NULL; diff --git a/drivers/net/can/slcan.c b/drivers/net/can/slcan.c index ea4d4f1a6411..acb5b92ace92 100644 --- a/drivers/net/can/slcan.c +++ b/drivers/net/can/slcan.c @@ -529,7 +529,7 @@ static struct slcan *slc_alloc(dev_t line) return NULL; sprintf(name, "slcan%d", i); - dev = alloc_netdev(sizeof(*sl), name, slc_setup); + dev = alloc_netdev(sizeof(*sl), name, NET_NAME_UNKNOWN, slc_setup); if (!dev) return NULL; diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c index 0932ffbf381b..ff435fbd1ad0 100644 --- a/drivers/net/dummy.c +++ b/drivers/net/dummy.c @@ -164,7 +164,7 @@ static int __init dummy_init_one(void) struct net_device *dev_dummy; int err; - dev_dummy = alloc_netdev(0, "dummy%d", dummy_setup); + dev_dummy = alloc_netdev(0, "dummy%d", NET_NAME_UNKNOWN, dummy_setup); if (!dev_dummy) return -ENOMEM; diff --git a/drivers/net/eql.c b/drivers/net/eql.c index 7a79b6046879..957e5c0cede3 100644 --- a/drivers/net/eql.c +++ b/drivers/net/eql.c @@ -585,7 +585,8 @@ static int __init eql_init_module(void) pr_info("%s\n", version); - dev_eql = alloc_netdev(sizeof(equalizer_t), "eql", eql_setup); + dev_eql = alloc_netdev(sizeof(equalizer_t), "eql", NET_NAME_UNKNOWN, + eql_setup); if (!dev_eql) return -ENOMEM; diff --git a/drivers/net/ethernet/8390/lib8390.c b/drivers/net/ethernet/8390/lib8390.c index 599311f0e05c..b96e8852b2d1 100644 --- a/drivers/net/ethernet/8390/lib8390.c +++ b/drivers/net/ethernet/8390/lib8390.c @@ -986,7 +986,7 @@ static void ethdev_setup(struct net_device *dev) static struct net_device *____alloc_ei_netdev(int size) { return alloc_netdev(sizeof(struct ei_device) + size, "eth%d", - ethdev_setup); + NET_NAME_UNKNOWN, ethdev_setup); } diff --git a/drivers/net/ethernet/tile/tilegx.c b/drivers/net/ethernet/tile/tilegx.c index 4c70360967c2..69557a26f749 100644 --- a/drivers/net/ethernet/tile/tilegx.c +++ b/drivers/net/ethernet/tile/tilegx.c @@ -2201,8 +2201,8 @@ static void tile_net_dev_init(const char *name, const uint8_t *mac) /* Allocate the device structure. Normally, "name" is a * template, instantiated by register_netdev(), but not for us. */ - dev = alloc_netdev_mqs(sizeof(*priv), name, tile_net_setup, - NR_CPUS, 1); + dev = alloc_netdev_mqs(sizeof(*priv), name, NET_NAME_UNKNOWN, + tile_net_setup, NR_CPUS, 1); if (!dev) { pr_err("alloc_netdev_mqs(%s) failed\n", name); return; diff --git a/drivers/net/ethernet/tile/tilepro.c b/drivers/net/ethernet/tile/tilepro.c index e5a5c5d4ce0c..88c712126692 100644 --- a/drivers/net/ethernet/tile/tilepro.c +++ b/drivers/net/ethernet/tile/tilepro.c @@ -2292,7 +2292,8 @@ static struct net_device *tile_net_dev_init(const char *name) * tile_net_setup(), and saves "name". Normally, "name" is a * template, instantiated by register_netdev(), but not for us. */ - dev = alloc_netdev(sizeof(*priv), name, tile_net_setup); + dev = alloc_netdev(sizeof(*priv), name, NET_NAME_UNKNOWN, + tile_net_setup); if (!dev) { pr_err("alloc_netdev(%s) failed\n", name); return NULL; diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c index 66e2b19ef709..c3c4051a089d 100644 --- a/drivers/net/hamradio/6pack.c +++ b/drivers/net/hamradio/6pack.c @@ -596,7 +596,8 @@ static int sixpack_open(struct tty_struct *tty) if (tty->ops->write == NULL) return -EOPNOTSUPP; - dev = alloc_netdev(sizeof(struct sixpack), "sp%d", sp_setup); + dev = alloc_netdev(sizeof(struct sixpack), "sp%d", NET_NAME_UNKNOWN, + sp_setup); if (!dev) { err = -ENOMEM; goto out; diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c index 484f77ec2ce1..a98c153f371e 100644 --- a/drivers/net/hamradio/baycom_epp.c +++ b/drivers/net/hamradio/baycom_epp.c @@ -1206,7 +1206,7 @@ static int __init init_baycomepp(void) struct net_device *dev; dev = alloc_netdev(sizeof(struct baycom_state), "bce%d", - baycom_epp_dev_setup); + NET_NAME_UNKNOWN, baycom_epp_dev_setup); if (!dev) { printk(KERN_WARNING "bce%d : out of memory\n", i); diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c index d50b23cf9ea9..c2894e43840e 100644 --- a/drivers/net/hamradio/bpqether.c +++ b/drivers/net/hamradio/bpqether.c @@ -501,8 +501,8 @@ static int bpq_new_device(struct net_device *edev) struct net_device *ndev; struct bpqdev *bpq; - ndev = alloc_netdev(sizeof(struct bpqdev), "bpq%d", - bpq_setup); + ndev = alloc_netdev(sizeof(struct bpqdev), "bpq%d", NET_NAME_UNKNOWN, + bpq_setup); if (!ndev) return -ENOMEM; diff --git a/drivers/net/hamradio/dmascc.c b/drivers/net/hamradio/dmascc.c index 6636022a1027..0fad408f24aa 100644 --- a/drivers/net/hamradio/dmascc.c +++ b/drivers/net/hamradio/dmascc.c @@ -466,7 +466,7 @@ static int __init setup_adapter(int card_base, int type, int n) if (!info) goto out; - info->dev[0] = alloc_netdev(0, "", dev_setup); + info->dev[0] = alloc_netdev(0, "", NET_NAME_UNKNOWN, dev_setup); if (!info->dev[0]) { printk(KERN_ERR "dmascc: " "could not allocate memory for %s at %#3x\n", @@ -474,7 +474,7 @@ static int __init setup_adapter(int card_base, int type, int n) goto out1; } - info->dev[1] = alloc_netdev(0, "", dev_setup); + info->dev[1] = alloc_netdev(0, "", NET_NAME_UNKNOWN, dev_setup); if (!info->dev[1]) { printk(KERN_ERR "dmascc: " "could not allocate memory for %s at %#3x\n", diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c index 5d78c1d08abd..c67a27245072 100644 --- a/drivers/net/hamradio/hdlcdrv.c +++ b/drivers/net/hamradio/hdlcdrv.c @@ -699,7 +699,7 @@ struct net_device *hdlcdrv_register(const struct hdlcdrv_ops *ops, if (privsize < sizeof(struct hdlcdrv_state)) privsize = sizeof(struct hdlcdrv_state); - dev = alloc_netdev(privsize, ifname, hdlcdrv_setup); + dev = alloc_netdev(privsize, ifname, NET_NAME_UNKNOWN, hdlcdrv_setup); if (!dev) return ERR_PTR(-ENOMEM); diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c index 8a6c720a4cc9..f990bb1c3e02 100644 --- a/drivers/net/hamradio/mkiss.c +++ b/drivers/net/hamradio/mkiss.c @@ -734,7 +734,8 @@ static int mkiss_open(struct tty_struct *tty) if (tty->ops->write == NULL) return -EOPNOTSUPP; - dev = alloc_netdev(sizeof(struct mkiss), "ax%d", ax_setup); + dev = alloc_netdev(sizeof(struct mkiss), "ax%d", NET_NAME_UNKNOWN, + ax_setup); if (!dev) { err = -ENOMEM; goto out; diff --git a/drivers/net/hamradio/scc.c b/drivers/net/hamradio/scc.c index 4bc6ee8e7987..57be9e0e98a6 100644 --- a/drivers/net/hamradio/scc.c +++ b/drivers/net/hamradio/scc.c @@ -1515,7 +1515,7 @@ static int scc_net_alloc(const char *name, struct scc_channel *scc) int err; struct net_device *dev; - dev = alloc_netdev(0, name, scc_net_setup); + dev = alloc_netdev(0, name, NET_NAME_UNKNOWN, scc_net_setup); if (!dev) return -ENOMEM; diff --git a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c index 81901659cc9e..717433cfb81d 100644 --- a/drivers/net/hamradio/yam.c +++ b/drivers/net/hamradio/yam.c @@ -1147,7 +1147,7 @@ static int __init yam_init_driver(void) sprintf(name, "yam%d", i); dev = alloc_netdev(sizeof(struct yam_port), name, - yam_setup); + NET_NAME_UNKNOWN, yam_setup); if (!dev) { pr_err("yam: cannot allocate net device\n"); err = -ENOMEM; diff --git a/drivers/net/ieee802154/fakehard.c b/drivers/net/ieee802154/fakehard.c index 78f18be3bbf2..9ce854f43917 100644 --- a/drivers/net/ieee802154/fakehard.c +++ b/drivers/net/ieee802154/fakehard.c @@ -343,7 +343,8 @@ static int ieee802154fake_probe(struct platform_device *pdev) if (!phy) return -ENOMEM; - dev = alloc_netdev(sizeof(struct fakehard_priv), "hardwpan%d", ieee802154_fake_setup); + dev = alloc_netdev(sizeof(struct fakehard_priv), "hardwpan%d", + NET_NAME_UNKNOWN, ieee802154_fake_setup); if (!dev) { wpan_phy_free(phy); return -ENOMEM; diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c index 46a7790be004..d2d4a3d2237f 100644 --- a/drivers/net/ifb.c +++ b/drivers/net/ifb.c @@ -269,8 +269,8 @@ static int __init ifb_init_one(int index) struct ifb_private *dp; int err; - dev_ifb = alloc_netdev(sizeof(struct ifb_private), - "ifb%d", ifb_setup); + dev_ifb = alloc_netdev(sizeof(struct ifb_private), "ifb%d", + NET_NAME_UNKNOWN, ifb_setup); if (!dev_ifb) return -ENOMEM; diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index bb96409f8c05..8f2262540561 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -195,7 +195,7 @@ static __net_init int loopback_net_init(struct net *net) int err; err = -ENOMEM; - dev = alloc_netdev(0, "lo", loopback_setup); + dev = alloc_netdev(0, "lo", NET_NAME_UNKNOWN, loopback_setup); if (!dev) goto out; diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 91d6c1272fcf..5c002b1ef169 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -2665,7 +2665,8 @@ ppp_create_interface(struct net *net, int unit, int *retp) int ret = -ENOMEM; int i; - dev = alloc_netdev(sizeof(struct ppp), "", ppp_setup); + dev = alloc_netdev(sizeof(struct ppp), "", NET_NAME_UNKNOWN, + ppp_setup); if (!dev) goto out1; diff --git a/drivers/net/slip/slip.c b/drivers/net/slip/slip.c index 87526443841f..05387b1e2e95 100644 --- a/drivers/net/slip/slip.c +++ b/drivers/net/slip/slip.c @@ -749,7 +749,7 @@ static struct slip *sl_alloc(dev_t line) return NULL; sprintf(name, "sl%d", i); - dev = alloc_netdev(sizeof(*sl), name, sl_setup); + dev = alloc_netdev(sizeof(*sl), name, NET_NAME_UNKNOWN, sl_setup); if (!dev) return NULL; diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 98bad1fb1bfb..acaaf6784179 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1633,7 +1633,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) name = ifr->ifr_name; dev = alloc_netdev_mqs(sizeof(struct tun_struct), name, - tun_setup, queues, queues); + NET_NAME_UNKNOWN, tun_setup, queues, + queues); if (!dev) return -ENOMEM; diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c index 6358d420e185..2ec1500d0077 100644 --- a/drivers/net/usb/cdc-phonet.c +++ b/drivers/net/usb/cdc-phonet.c @@ -387,7 +387,7 @@ static int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *i return -EINVAL; dev = alloc_netdev(sizeof(*pnd) + sizeof(pnd->urbs[0]) * rxq_size, - ifname, usbpn_setup); + ifname, NET_NAME_UNKNOWN, usbpn_setup); if (!dev) return -ENOMEM; diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index a3a05869309d..50b36b299946 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -2520,7 +2520,8 @@ static struct hso_device *hso_create_net_device(struct usb_interface *interface, /* allocate our network device, then we can put in our private data */ /* call hso_net_init to do the basic initialization */ - net = alloc_netdev(sizeof(struct hso_net), "hso%d", hso_net_init); + net = alloc_netdev(sizeof(struct hso_net), "hso%d", NET_NAME_UNKNOWN, + hso_net_init); if (!net) { dev_err(&interface->dev, "Unable to create ethernet device\n"); goto exit; diff --git a/drivers/net/wan/dlci.c b/drivers/net/wan/dlci.c index 19f7cb2cdef3..a463613a0719 100644 --- a/drivers/net/wan/dlci.c +++ b/drivers/net/wan/dlci.c @@ -327,8 +327,8 @@ static int dlci_add(struct dlci_add *dlci) goto err1; /* create device name */ - master = alloc_netdev( sizeof(struct dlci_local), "dlci%d", - dlci_setup); + master = alloc_netdev(sizeof(struct dlci_local), "dlci%d", + NET_NAME_UNKNOWN, dlci_setup); if (!master) { err = -ENOMEM; goto err1; diff --git a/drivers/net/wan/hdlc.c b/drivers/net/wan/hdlc.c index 9c33ca918e19..51f6cee8aab2 100644 --- a/drivers/net/wan/hdlc.c +++ b/drivers/net/wan/hdlc.c @@ -256,7 +256,8 @@ static void hdlc_setup(struct net_device *dev) struct net_device *alloc_hdlcdev(void *priv) { struct net_device *dev; - dev = alloc_netdev(sizeof(struct hdlc_device), "hdlc%d", hdlc_setup); + dev = alloc_netdev(sizeof(struct hdlc_device), "hdlc%d", + NET_NAME_UNKNOWN, hdlc_setup); if (dev) dev_to_hdlc(dev)->priv = priv; return dev; diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c index 7c6cb4f31798..7cc64eac0fa3 100644 --- a/drivers/net/wan/hdlc_fr.c +++ b/drivers/net/wan/hdlc_fr.c @@ -1075,10 +1075,11 @@ static int fr_add_pvc(struct net_device *frad, unsigned int dlci, int type) used = pvc_is_used(pvc); if (type == ARPHRD_ETHER) { - dev = alloc_netdev(0, "pvceth%d", ether_setup); + dev = alloc_netdev(0, "pvceth%d", NET_NAME_UNKNOWN, + ether_setup); dev->priv_flags &= ~IFF_TX_SKB_SHARING; } else - dev = alloc_netdev(0, "pvc%d", pvc_setup); + dev = alloc_netdev(0, "pvc%d", NET_NAME_UNKNOWN, pvc_setup); if (!dev) { netdev_warn(frad, "Memory squeeze on fr_pvc()\n"); diff --git a/drivers/net/wan/lapbether.c b/drivers/net/wan/lapbether.c index a33a46fa88dd..2f5eda8a7227 100644 --- a/drivers/net/wan/lapbether.c +++ b/drivers/net/wan/lapbether.c @@ -325,8 +325,8 @@ static int lapbeth_new_device(struct net_device *dev) ASSERT_RTNL(); - ndev = alloc_netdev(sizeof(*lapbeth), "lapb%d", - lapbeth_setup); + ndev = alloc_netdev(sizeof(*lapbeth), "lapb%d", NET_NAME_UNKNOWN, + lapbeth_setup); if (!ndev) goto out; diff --git a/drivers/net/wan/sbni.c b/drivers/net/wan/sbni.c index 1b89ecf0959e..758c4ba1e97c 100644 --- a/drivers/net/wan/sbni.c +++ b/drivers/net/wan/sbni.c @@ -227,7 +227,8 @@ int __init sbni_probe(int unit) struct net_device *dev; int err; - dev = alloc_netdev(sizeof(struct net_local), "sbni", sbni_devsetup); + dev = alloc_netdev(sizeof(struct net_local), "sbni", + NET_NAME_UNKNOWN, sbni_devsetup); if (!dev) return -ENOMEM; @@ -1477,8 +1478,8 @@ int __init init_module( void ) int err; while( num < SBNI_MAX_NUM_CARDS ) { - dev = alloc_netdev(sizeof(struct net_local), - "sbni%d", sbni_devsetup); + dev = alloc_netdev(sizeof(struct net_local), "sbni%d", + NET_NAME_UNKNOWN, sbni_devsetup); if( !dev) break; diff --git a/drivers/net/wan/sdla.c b/drivers/net/wan/sdla.c index cdd45fb8a1f6..421ac5f85699 100644 --- a/drivers/net/wan/sdla.c +++ b/drivers/net/wan/sdla.c @@ -1631,7 +1631,8 @@ static int __init init_sdla(void) printk("%s.\n", version); - sdla = alloc_netdev(sizeof(struct frad_local), "sdla0", setup_sdla); + sdla = alloc_netdev(sizeof(struct frad_local), "sdla0", + NET_NAME_UNKNOWN, setup_sdla); if (!sdla) return -ENOMEM; diff --git a/drivers/net/wan/x25_asy.c b/drivers/net/wan/x25_asy.c index 5895f1978691..df6c07357556 100644 --- a/drivers/net/wan/x25_asy.c +++ b/drivers/net/wan/x25_asy.c @@ -81,8 +81,8 @@ static struct x25_asy *x25_asy_alloc(void) char name[IFNAMSIZ]; sprintf(name, "x25asy%d", i); - dev = alloc_netdev(sizeof(struct x25_asy), - name, x25_asy_setup); + dev = alloc_netdev(sizeof(struct x25_asy), name, + NET_NAME_UNKNOWN, x25_asy_setup); if (!dev) return NULL; diff --git a/drivers/net/wimax/i2400m/usb.c b/drivers/net/wimax/i2400m/usb.c index cd15a93d9084..e7f5910a6519 100644 --- a/drivers/net/wimax/i2400m/usb.c +++ b/drivers/net/wimax/i2400m/usb.c @@ -472,7 +472,7 @@ int i2400mu_probe(struct usb_interface *iface, /* Allocate instance [calls i2400m_netdev_setup() on it]. */ result = -ENOMEM; - net_dev = alloc_netdev(sizeof(*i2400mu), "wmx%d", + net_dev = alloc_netdev(sizeof(*i2400mu), "wmx%d", NET_NAME_UNKNOWN, i2400mu_netdev_setup); if (net_dev == NULL) { dev_err(dev, "no memory for network device instance\n"); diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index 64747d457bb3..29d88739454b 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -2685,7 +2685,8 @@ static struct net_device *init_wifidev(struct airo_info *ai, struct net_device *ethdev) { int err; - struct net_device *dev = alloc_netdev(0, "wifi%d", wifi_setup); + struct net_device *dev = alloc_netdev(0, "wifi%d", NET_NAME_UNKNOWN, + wifi_setup); if (!dev) return NULL; dev->ml_priv = ethdev->ml_priv; @@ -2785,7 +2786,7 @@ static struct net_device *_init_airo_card( unsigned short irq, int port, CapabilityRid cap_rid; /* Create the network device object. */ - dev = alloc_netdev(sizeof(*ai), "", ether_setup); + dev = alloc_netdev(sizeof(*ai), "", NET_NAME_UNKNOWN, ether_setup); if (!dev) { airo_print_err("", "Couldn't alloc_etherdev"); return NULL; diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 0e26f4a34fda..1c4ce8e3eebe 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -3636,7 +3636,7 @@ struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name, struct net_device *ndev; struct ath6kl_vif *vif; - ndev = alloc_netdev(sizeof(*vif), name, ether_setup); + ndev = alloc_netdev(sizeof(*vif), name, NET_NAME_UNKNOWN, ether_setup); if (!ndev) return NULL; diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index 106b6dcb773a..7afce6e8c507 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -132,7 +132,7 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr) ch = wdev->wiphy->bands[IEEE80211_BAND_60GHZ]->channels; cfg80211_chandef_create(&wdev->preset_chandef, ch, NL80211_CHAN_NO_HT); - ndev = alloc_netdev(0, "wlan%d", ether_setup); + ndev = alloc_netdev(0, "wlan%d", NET_NAME_UNKNOWN, ether_setup); if (!ndev) { dev_err(dev, "alloc_netdev_mqs failed\n"); rc = -ENOMEM; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 09dd8c13d844..2699441d4f41 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -808,7 +808,8 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, } else { brcmf_dbg(INFO, "allocate netdev interface\n"); /* Allocate netdev, including space for private structure */ - ndev = alloc_netdev(sizeof(*ifp), name, ether_setup); + ndev = alloc_netdev(sizeof(*ifp), name, NET_NAME_UNKNOWN, + ether_setup); if (!ndev) return ERR_PTR(-ENOMEM); diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c index 0c02f0483d1f..569b64ecc607 100644 --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c @@ -981,7 +981,7 @@ struct lbs_private *lbs_add_card(void *card, struct device *dmdev) goto err_wdev; } - dev = alloc_netdev(0, "wlan%d", ether_setup); + dev = alloc_netdev(0, "wlan%d", NET_NAME_UNKNOWN, ether_setup); if (!dev) { dev_err(dmdev, "no memory for network device instance\n"); goto err_adapter; diff --git a/drivers/net/wireless/libertas/mesh.c b/drivers/net/wireless/libertas/mesh.c index 6fef746345bc..01a67f62696f 100644 --- a/drivers/net/wireless/libertas/mesh.c +++ b/drivers/net/wireless/libertas/mesh.c @@ -1000,7 +1000,7 @@ static int lbs_add_mesh(struct lbs_private *priv) goto done; } - mesh_dev = alloc_netdev(0, "msh%d", ether_setup); + mesh_dev = alloc_netdev(0, "msh%d", NET_NAME_UNKNOWN, ether_setup); if (!mesh_dev) { lbs_deb_mesh("init mshX device failed\n"); ret = -ENOMEM; diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index eba51460a5de..5ea65fce0b83 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -2676,7 +2676,8 @@ static int __init init_mac80211_hwsim(void) goto out_free_radios; } - hwsim_mon = alloc_netdev(0, "hwsim%d", hwsim_mon_setup); + hwsim_mon = alloc_netdev(0, "hwsim%d", NET_NAME_UNKNOWN, + hwsim_mon_setup); if (hwsim_mon == NULL) { err = -ENOMEM; goto out_free_radios; diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 6af135fa99f7..ca87f923c61e 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -2232,7 +2232,8 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, } dev = alloc_netdev_mqs(sizeof(struct mwifiex_private *), name, - ether_setup, IEEE80211_NUM_ACS, 1); + NET_NAME_UNKNOWN, ether_setup, + IEEE80211_NUM_ACS, 1); if (!dev) { wiphy_err(wiphy, "no memory available for netdevice\n"); priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c index ef75b45e5085..bd59d9dbf27b 100644 --- a/drivers/net/xen-netback/interface.c +++ b/drivers/net/xen-netback/interface.c @@ -418,8 +418,8 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid, * When the guest selects the desired number, it will be updated * via netif_set_real_num_*_queues(). */ - dev = alloc_netdev_mq(sizeof(struct xenvif), name, ether_setup, - xenvif_max_queues); + dev = alloc_netdev_mq(sizeof(struct xenvif), name, NET_NAME_UNKNOWN, + ether_setup, xenvif_max_queues); if (dev == NULL) { pr_warn("Could not allocate netdev for %s\n", name); return ERR_PTR(-ENOMEM); diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c index d837c3c5330f..fbc6701bef30 100644 --- a/drivers/s390/net/claw.c +++ b/drivers/s390/net/claw.c @@ -2915,7 +2915,7 @@ claw_new_device(struct ccwgroup_device *cgdev) "failed with error code %d\n", ret); goto out; } - dev = alloc_netdev(0,"claw%d",claw_init_netdevice); + dev = alloc_netdev(0, "claw%d", NET_NAME_UNKNOWN, claw_init_netdevice); if (!dev) { dev_warn(&cgdev->dev, "Activating the CLAW device failed\n"); diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c index 03b6ad035577..e056dd4fe44d 100644 --- a/drivers/s390/net/ctcm_main.c +++ b/drivers/s390/net/ctcm_main.c @@ -1137,9 +1137,11 @@ static struct net_device *ctcm_init_netdevice(struct ctcm_priv *priv) return NULL; if (IS_MPC(priv)) - dev = alloc_netdev(0, MPC_DEVICE_GENE, ctcm_dev_setup); + dev = alloc_netdev(0, MPC_DEVICE_GENE, NET_NAME_UNKNOWN, + ctcm_dev_setup); else - dev = alloc_netdev(0, CTC_DEVICE_GENE, ctcm_dev_setup); + dev = alloc_netdev(0, CTC_DEVICE_GENE, NET_NAME_UNKNOWN, + ctcm_dev_setup); if (!dev) { CTCM_DBF_TEXT_(ERROR, CTC_DBF_CRIT, diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index ce16d1bdb20a..0a87809c8af7 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -2015,7 +2015,7 @@ static struct net_device *netiucv_init_netdevice(char *username, char *userdata) struct net_device *dev; dev = alloc_netdev(sizeof(struct netiucv_priv), "iucv%d", - netiucv_setup_netdevice); + NET_NAME_UNKNOWN, netiucv_setup_netdevice); if (!dev) return NULL; rtnl_lock(); diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 5ef5b4f45758..c2679bfe7f66 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -952,10 +952,12 @@ static int qeth_l2_setup_netdev(struct qeth_card *card) { switch (card->info.type) { case QETH_CARD_TYPE_IQD: - card->dev = alloc_netdev(0, "hsi%d", ether_setup); + card->dev = alloc_netdev(0, "hsi%d", NET_NAME_UNKNOWN, + ether_setup); break; case QETH_CARD_TYPE_OSN: - card->dev = alloc_netdev(0, "osn%d", ether_setup); + card->dev = alloc_netdev(0, "osn%d", NET_NAME_UNKNOWN, + ether_setup); card->dev->flags |= IFF_NOARP; break; default: diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 14e0b5810e8c..f8427a2c4840 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -3287,7 +3287,8 @@ static int qeth_l3_setup_netdev(struct qeth_card *card) } } } else if (card->info.type == QETH_CARD_TYPE_IQD) { - card->dev = alloc_netdev(0, "hsi%d", ether_setup); + card->dev = alloc_netdev(0, "hsi%d", NET_NAME_UNKNOWN, + ether_setup); if (!card->dev) return -ENODEV; card->dev->flags |= IFF_NOARP; diff --git a/drivers/staging/cxt1e1/linux.c b/drivers/staging/cxt1e1/linux.c index 09f3d5ca75ac..85d776bbfb15 100644 --- a/drivers/staging/cxt1e1/linux.c +++ b/drivers/staging/cxt1e1/linux.c @@ -917,7 +917,8 @@ c4_add_dev(hdw_info_t *hi, int brdno, unsigned long f0, unsigned long f1, struct net_device *ndev; ci_t *ci; - ndev = alloc_netdev(sizeof(ci_t), SBE_IFACETMPL, c4_setup); + ndev = alloc_netdev(sizeof(ci_t), SBE_IFACETMPL, NET_NAME_UNKNOWN, + c4_setup); if (!ndev) { pr_warning("%s: no memory for struct net_device !\n", hi->devname); diff --git a/drivers/staging/gdm724x/gdm_lte.c b/drivers/staging/gdm724x/gdm_lte.c index 64c55b99fda4..c2268527422f 100644 --- a/drivers/staging/gdm724x/gdm_lte.c +++ b/drivers/staging/gdm724x/gdm_lte.c @@ -885,7 +885,7 @@ int register_lte_device(struct phy_dev *phy_dev, /* Allocate netdev */ net = alloc_netdev(sizeof(struct nic), pdn_dev_name, - ether_setup); + NET_NAME_UNKNOWN, ether_setup); if (net == NULL) { pr_err("alloc_netdev failed\n"); ret = -ENOMEM; diff --git a/drivers/staging/gdm72xx/gdm_wimax.c b/drivers/staging/gdm72xx/gdm_wimax.c index e5e511585122..a9a6fc51024b 100644 --- a/drivers/staging/gdm72xx/gdm_wimax.c +++ b/drivers/staging/gdm72xx/gdm_wimax.c @@ -886,7 +886,8 @@ int register_wimax_device(struct phy_dev *phy_dev, struct device *pdev) struct net_device *dev; int ret; - dev = alloc_netdev(sizeof(*nic), "wm%d", ether_setup); + dev = alloc_netdev(sizeof(*nic), "wm%d", NET_NAME_UNKNOWN, + ether_setup); if (dev == NULL) { pr_err("alloc_etherdev failed\n"); diff --git a/drivers/staging/vt6655/wpactl.c b/drivers/staging/vt6655/wpactl.c index 8392d4d1d5ed..0814bfd68b2e 100644 --- a/drivers/staging/vt6655/wpactl.c +++ b/drivers/staging/vt6655/wpactl.c @@ -89,7 +89,8 @@ static int wpa_init_wpadev(PSDevice pDevice) struct net_device *dev = pDevice->dev; int ret = 0; - pDevice->wpadev = alloc_netdev(sizeof(PSDevice), "vntwpa", wpadev_setup); + pDevice->wpadev = alloc_netdev(sizeof(PSDevice), "vntwpa", + NET_NAME_UNKNOWN, wpadev_setup); if (pDevice->wpadev == NULL) return -ENOMEM; diff --git a/drivers/staging/wlan-ng/p80211netdev.c b/drivers/staging/wlan-ng/p80211netdev.c index 00b186c59725..6c78f917e24a 100644 --- a/drivers/staging/wlan-ng/p80211netdev.c +++ b/drivers/staging/wlan-ng/p80211netdev.c @@ -769,7 +769,7 @@ int wlan_setup(wlandevice_t *wlandev, struct device *physdev) /* Allocate and initialize the struct device */ netdev = alloc_netdev(sizeof(struct wireless_dev), "wlan%d", - ether_setup); + NET_NAME_UNKNOWN, ether_setup); if (netdev == NULL) { dev_err(physdev, "Failed to alloc netdev.\n"); wlan_free_wiphy(wiphy); diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 2ebe47b78a3e..cde3ab97900f 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -2789,9 +2789,8 @@ static int gsm_create_network(struct gsm_dlci *dlci, struct gsm_netconfig *nc) netname = "gsm%d"; if (nc->if_name[0] != '\0') netname = nc->if_name; - net = alloc_netdev(sizeof(struct gsm_mux_net), - netname, - gsm_mux_net_init); + net = alloc_netdev(sizeof(struct gsm_mux_net), netname, + NET_NAME_UNKNOWN, gsm_mux_net_init); if (!net) { pr_err("alloc_netdev failed"); return -ENOMEM; diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c index f2b781773eed..b9cfc1571d71 100644 --- a/drivers/usb/gadget/f_phonet.c +++ b/drivers/usb/gadget/f_phonet.c @@ -721,7 +721,8 @@ struct net_device *gphonet_setup_default(void) struct phonet_port *port; /* Create net device */ - dev = alloc_netdev(sizeof(*port), "upnlink%d", pn_net_setup); + dev = alloc_netdev(sizeof(*port), "upnlink%d", NET_NAME_UNKNOWN, + pn_net_setup); if (!dev) return ERR_PTR(-ENOMEM); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 9be34732142f..15ed750458ad 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2991,13 +2991,15 @@ void ether_setup(struct net_device *dev); /* Support for loadable net-drivers */ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, + unsigned char name_assign_type, void (*setup)(struct net_device *), unsigned int txqs, unsigned int rxqs); -#define alloc_netdev(sizeof_priv, name, setup) \ - alloc_netdev_mqs(sizeof_priv, name, setup, 1, 1) +#define alloc_netdev(sizeof_priv, name, name_assign_type, setup) \ + alloc_netdev_mqs(sizeof_priv, name, name_assign_type, setup, 1, 1) -#define alloc_netdev_mq(sizeof_priv, name, setup, count) \ - alloc_netdev_mqs(sizeof_priv, name, setup, count, count) +#define alloc_netdev_mq(sizeof_priv, name, name_assign_type, setup, count) \ + alloc_netdev_mqs(sizeof_priv, name, name_assign_type, setup, count, \ + count) int register_netdev(struct net_device *dev); void unregister_netdev(struct net_device *dev); diff --git a/net/802/fc.c b/net/802/fc.c index 05eea6b98bb8..7c174b6750cd 100644 --- a/net/802/fc.c +++ b/net/802/fc.c @@ -126,6 +126,6 @@ static void fc_setup(struct net_device *dev) */ struct net_device *alloc_fcdev(int sizeof_priv) { - return alloc_netdev(sizeof_priv, "fc%d", fc_setup); + return alloc_netdev(sizeof_priv, "fc%d", NET_NAME_UNKNOWN, fc_setup); } EXPORT_SYMBOL(alloc_fcdev); diff --git a/net/802/fddi.c b/net/802/fddi.c index 9cda40661e0d..59e7346f1193 100644 --- a/net/802/fddi.c +++ b/net/802/fddi.c @@ -207,7 +207,8 @@ static void fddi_setup(struct net_device *dev) */ struct net_device *alloc_fddidev(int sizeof_priv) { - return alloc_netdev(sizeof_priv, "fddi%d", fddi_setup); + return alloc_netdev(sizeof_priv, "fddi%d", NET_NAME_UNKNOWN, + fddi_setup); } EXPORT_SYMBOL(alloc_fddidev); diff --git a/net/802/hippi.c b/net/802/hippi.c index 5ff2a718ddca..2e03f8259dd5 100644 --- a/net/802/hippi.c +++ b/net/802/hippi.c @@ -228,7 +228,8 @@ static void hippi_setup(struct net_device *dev) struct net_device *alloc_hippi_dev(int sizeof_priv) { - return alloc_netdev(sizeof_priv, "hip%d", hippi_setup); + return alloc_netdev(sizeof_priv, "hip%d", NET_NAME_UNKNOWN, + hippi_setup); } EXPORT_SYMBOL(alloc_hippi_dev); diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 44ebd5c2cd4a..cba9c212a730 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -250,7 +250,8 @@ static int register_vlan_device(struct net_device *real_dev, u16 vlan_id) snprintf(name, IFNAMSIZ, "vlan%.4i", vlan_id); } - new_dev = alloc_netdev(sizeof(struct vlan_dev_priv), name, vlan_setup); + new_dev = alloc_netdev(sizeof(struct vlan_dev_priv), name, + NET_NAME_UNKNOWN, vlan_setup); if (new_dev == NULL) return -ENOBUFS; diff --git a/net/appletalk/dev.c b/net/appletalk/dev.c index 6c8016f61866..e4158b8b926d 100644 --- a/net/appletalk/dev.c +++ b/net/appletalk/dev.c @@ -39,6 +39,7 @@ static void ltalk_setup(struct net_device *dev) struct net_device *alloc_ltalkdev(int sizeof_priv) { - return alloc_netdev(sizeof_priv, "lt%d", ltalk_setup); + return alloc_netdev(sizeof_priv, "lt%d", NET_NAME_UNKNOWN, + ltalk_setup); } EXPORT_SYMBOL(alloc_ltalkdev); diff --git a/net/atm/br2684.c b/net/atm/br2684.c index 403e71fa88fe..cc78538d163b 100644 --- a/net/atm/br2684.c +++ b/net/atm/br2684.c @@ -682,8 +682,8 @@ static int br2684_create(void __user *arg) netdev = alloc_netdev(sizeof(struct br2684_dev), ni.ifname[0] ? ni.ifname : "nas%d", - (payload == p_routed) ? - br2684_setup_routed : br2684_setup); + NET_NAME_UNKNOWN, + (payload == p_routed) ? br2684_setup_routed : br2684_setup); if (!netdev) return -ENOMEM; diff --git a/net/atm/clip.c b/net/atm/clip.c index ba291ce4bdff..46339040fef0 100644 --- a/net/atm/clip.c +++ b/net/atm/clip.c @@ -520,7 +520,8 @@ static int clip_create(int number) if (PRIV(dev)->number >= number) number = PRIV(dev)->number + 1; } - dev = alloc_netdev(sizeof(struct clip_priv), "", clip_setup); + dev = alloc_netdev(sizeof(struct clip_priv), "", NET_NAME_UNKNOWN, + clip_setup); if (!dev) return -ENOMEM; clip_priv = PRIV(dev); diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index e7ee65dc20bf..d551e6302cf3 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -895,7 +895,7 @@ struct net_device *batadv_softif_create(const char *name) int ret; soft_iface = alloc_netdev(sizeof(struct batadv_priv), name, - batadv_softif_init_early); + NET_NAME_UNKNOWN, batadv_softif_init_early); if (!soft_iface) return NULL; diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 5a7f81df603c..206b65ccd5b8 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -712,7 +712,7 @@ static int setup_netdev(struct l2cap_chan *chan, struct lowpan_dev **dev) unsigned long flags; netdev = alloc_netdev(sizeof(struct lowpan_dev), IFACE_NAME_TEMPLATE, - netdev_setup); + NET_NAME_UNKNOWN, netdev_setup); if (!netdev) return -ENOMEM; diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index a841d3e776c5..85bcc21e84d2 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -538,8 +538,9 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock) /* session struct allocated as private part of net_device */ dev = alloc_netdev(sizeof(struct bnep_session), - (*req->device) ? req->device : "bnep%d", - bnep_net_setup); + (*req->device) ? req->device : "bnep%d", + NET_NAME_UNKNOWN, + bnep_net_setup); if (!dev) return -ENOMEM; diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 3eca3fdf8fe1..078d336a1f37 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -344,7 +344,7 @@ int br_add_bridge(struct net *net, const char *name) struct net_device *dev; int res; - dev = alloc_netdev(sizeof(struct net_bridge), name, + dev = alloc_netdev(sizeof(struct net_bridge), name, NET_NAME_UNKNOWN, br_dev_setup); if (!dev) diff --git a/net/core/dev.c b/net/core/dev.c index 38793fb84a35..2c98f10ee62a 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -6441,17 +6441,19 @@ void netdev_freemem(struct net_device *dev) /** * alloc_netdev_mqs - allocate network device - * @sizeof_priv: size of private data to allocate space for - * @name: device name format string - * @setup: callback to initialize device - * @txqs: the number of TX subqueues to allocate - * @rxqs: the number of RX subqueues to allocate + * @sizeof_priv: size of private data to allocate space for + * @name: device name format string + * @name_assign_type: origin of device name + * @setup: callback to initialize device + * @txqs: the number of TX subqueues to allocate + * @rxqs: the number of RX subqueues to allocate * * Allocates a struct net_device with private data area for driver use * and performs basic initialization. Also allocates subqueue structs * for each queue on the device. */ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, + unsigned char name_assign_type, void (*setup)(struct net_device *), unsigned int txqs, unsigned int rxqs) { @@ -6530,6 +6532,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, #endif strcpy(dev->name, name); + dev->name_assign_type = name_assign_type; dev->group = INIT_NETDEV_GROUP; if (!dev->ethtool_ops) dev->ethtool_ops = &default_ethtool_ops; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 1f8a59e02c48..599864322de8 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1828,8 +1828,8 @@ struct net_device *rtnl_create_link(struct net *net, num_rx_queues = ops->get_num_rx_queues(); err = -ENOMEM; - dev = alloc_netdev_mqs(ops->priv_size, ifname, ops->setup, - num_tx_queues, num_rx_queues); + dev = alloc_netdev_mqs(ops->priv_size, ifname, NET_NAME_UNKNOWN, + ops->setup, num_tx_queues, num_rx_queues); if (!dev) goto err; diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 64c5af0a10dd..45a1e34c89e0 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -340,8 +340,8 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent, struct dsa_slave_priv *p; int ret; - slave_dev = alloc_netdev(sizeof(struct dsa_slave_priv), - name, ether_setup); + slave_dev = alloc_netdev(sizeof(struct dsa_slave_priv), name, + NET_NAME_UNKNOWN, ether_setup); if (slave_dev == NULL) return slave_dev; diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c index 5dc638cad2e1..f405e0592407 100644 --- a/net/ethernet/eth.c +++ b/net/ethernet/eth.c @@ -390,7 +390,8 @@ EXPORT_SYMBOL(ether_setup); struct net_device *alloc_etherdev_mqs(int sizeof_priv, unsigned int txqs, unsigned int rxqs) { - return alloc_netdev_mqs(sizeof_priv, "eth%d", ether_setup, txqs, rxqs); + return alloc_netdev_mqs(sizeof_priv, "eth%d", NET_NAME_UNKNOWN, + ether_setup, txqs, rxqs); } EXPORT_SYMBOL(alloc_etherdev_mqs); diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 54b6731dab55..0157a7af20a8 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -301,7 +301,7 @@ static struct net_device *__ip_tunnel_create(struct net *net, } ASSERT_RTNL(); - dev = alloc_netdev(ops->priv_size, name, ops->setup); + dev = alloc_netdev(ops->priv_size, name, NET_NAME_UNKNOWN, ops->setup); if (!dev) { err = -ENOMEM; goto failed; diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 65bcaa789043..c8034587859d 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -500,7 +500,7 @@ static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt) else sprintf(name, "pimreg%u", mrt->id); - dev = alloc_netdev(0, name, reg_vif_setup); + dev = alloc_netdev(0, name, NET_NAME_UNKNOWN, reg_vif_setup); if (dev == NULL) return NULL; diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 365b2b6f3942..5f19dfbc4c6a 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -322,7 +322,8 @@ static struct ip6_tnl *ip6gre_tunnel_locate(struct net *net, else strcpy(name, "ip6gre%d"); - dev = alloc_netdev(sizeof(*t), name, ip6gre_tunnel_setup); + dev = alloc_netdev(sizeof(*t), name, NET_NAME_UNKNOWN, + ip6gre_tunnel_setup); if (!dev) return NULL; @@ -1326,7 +1327,8 @@ static int __net_init ip6gre_init_net(struct net *net) int err; ign->fb_tunnel_dev = alloc_netdev(sizeof(struct ip6_tnl), "ip6gre0", - ip6gre_tunnel_setup); + NET_NAME_UNKNOWN, + ip6gre_tunnel_setup); if (!ign->fb_tunnel_dev) { err = -ENOMEM; goto err_alloc_dev; diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 51a1eb185ea7..f9de5a695072 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -315,7 +315,8 @@ static struct ip6_tnl *ip6_tnl_create(struct net *net, struct __ip6_tnl_parm *p) else sprintf(name, "ip6tnl%%d"); - dev = alloc_netdev(sizeof (*t), name, ip6_tnl_dev_setup); + dev = alloc_netdev(sizeof(*t), name, NET_NAME_UNKNOWN, + ip6_tnl_dev_setup); if (dev == NULL) goto failed; @@ -1773,7 +1774,7 @@ static int __net_init ip6_tnl_init_net(struct net *net) err = -ENOMEM; ip6n->fb_tnl_dev = alloc_netdev(sizeof(struct ip6_tnl), "ip6tnl0", - ip6_tnl_dev_setup); + NET_NAME_UNKNOWN, ip6_tnl_dev_setup); if (!ip6n->fb_tnl_dev) goto err_alloc_dev; diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index 9aaa6bb229e4..17ee4fc32dfe 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -204,7 +204,7 @@ static struct ip6_tnl *vti6_tnl_create(struct net *net, struct __ip6_tnl_parm *p else sprintf(name, "ip6_vti%%d"); - dev = alloc_netdev(sizeof(*t), name, vti6_dev_setup); + dev = alloc_netdev(sizeof(*t), name, NET_NAME_UNKNOWN, vti6_dev_setup); if (dev == NULL) goto failed; @@ -1020,7 +1020,7 @@ static int __net_init vti6_init_net(struct net *net) err = -ENOMEM; ip6n->fb_tnl_dev = alloc_netdev(sizeof(struct ip6_tnl), "ip6_vti0", - vti6_dev_setup); + NET_NAME_UNKNOWN, vti6_dev_setup); if (!ip6n->fb_tnl_dev) goto err_alloc_dev; diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 8250474ab7dc..f9a3fd320d1d 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -744,7 +744,7 @@ static struct net_device *ip6mr_reg_vif(struct net *net, struct mr6_table *mrt) else sprintf(name, "pim6reg%u", mrt->id); - dev = alloc_netdev(0, name, reg_vif_setup); + dev = alloc_netdev(0, name, NET_NAME_UNKNOWN, reg_vif_setup); if (dev == NULL) return NULL; diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 4f408176dc64..2e9ba035fb5f 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -250,7 +250,8 @@ static struct ip_tunnel *ipip6_tunnel_locate(struct net *net, else strcpy(name, "sit%d"); - dev = alloc_netdev(sizeof(*t), name, ipip6_tunnel_setup); + dev = alloc_netdev(sizeof(*t), name, NET_NAME_UNKNOWN, + ipip6_tunnel_setup); if (dev == NULL) return NULL; @@ -1729,6 +1730,7 @@ static int __net_init sit_init_net(struct net *net) sitn->tunnels[3] = sitn->tunnels_r_l; sitn->fb_tunnel_dev = alloc_netdev(sizeof(struct ip_tunnel), "sit0", + NET_NAME_UNKNOWN, ipip6_tunnel_setup); if (!sitn->fb_tunnel_dev) { err = -ENOMEM; diff --git a/net/irda/irda_device.c b/net/irda/irda_device.c index 365b895da84b..9e0d909390fd 100644 --- a/net/irda/irda_device.c +++ b/net/irda/irda_device.c @@ -293,7 +293,8 @@ static void irda_device_setup(struct net_device *dev) */ struct net_device *alloc_irdadev(int sizeof_priv) { - return alloc_netdev(sizeof_priv, "irda%d", irda_device_setup); + return alloc_netdev(sizeof_priv, "irda%d", NET_NAME_UNKNOWN, + irda_device_setup); } EXPORT_SYMBOL(alloc_irdadev); diff --git a/net/irda/irlan/irlan_eth.c b/net/irda/irlan/irlan_eth.c index ffcec225b5d9..dc13f1a45f2f 100644 --- a/net/irda/irlan/irlan_eth.c +++ b/net/irda/irlan/irlan_eth.c @@ -96,7 +96,7 @@ static void irlan_eth_setup(struct net_device *dev) */ struct net_device *alloc_irlandev(const char *name) { - return alloc_netdev(sizeof(struct irlan_cb), name, + return alloc_netdev(sizeof(struct irlan_cb), name, NET_NAME_UNKNOWN, irlan_eth_setup); } diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c index 76125c57ee6d..edb78e69efe4 100644 --- a/net/l2tp/l2tp_eth.c +++ b/net/l2tp/l2tp_eth.c @@ -246,7 +246,8 @@ static int l2tp_eth_create(struct net *net, u32 tunnel_id, u32 session_id, u32 p goto out; } - dev = alloc_netdev(sizeof(*priv), name, l2tp_eth_dev_setup); + dev = alloc_netdev(sizeof(*priv), name, NET_NAME_UNKNOWN, + l2tp_eth_dev_setup); if (!dev) { rc = -ENOMEM; goto out_del_session; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index bbf51b2f0651..4edfc7c1524f 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1624,9 +1624,9 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, if (local->hw.queues >= IEEE80211_NUM_ACS) txqs = IEEE80211_NUM_ACS; - ndev = alloc_netdev_mqs(sizeof(*sdata) + - local->hw.vif_data_size, - name, ieee80211_if_setup, txqs, 1); + ndev = alloc_netdev_mqs(sizeof(*sdata) + local->hw.vif_data_size, + name, NET_NAME_UNKNOWN, + ieee80211_if_setup, txqs, 1); if (!ndev) return -ENOMEM; dev_net_set(ndev, wiphy_net(local->hw.wiphy)); diff --git a/net/mac802154/ieee802154_dev.c b/net/mac802154/ieee802154_dev.c index 9b54370f5e87..b36b2b996578 100644 --- a/net/mac802154/ieee802154_dev.c +++ b/net/mac802154/ieee802154_dev.c @@ -167,11 +167,13 @@ mac802154_add_iface(struct wpan_phy *phy, const char *name, int type) switch (type) { case IEEE802154_DEV_MONITOR: dev = alloc_netdev(sizeof(struct mac802154_sub_if_data), - name, mac802154_monitor_setup); + name, NET_NAME_UNKNOWN, + mac802154_monitor_setup); break; case IEEE802154_DEV_WPAN: dev = alloc_netdev(sizeof(struct mac802154_sub_if_data), - name, mac802154_wpan_setup); + name, NET_NAME_UNKNOWN, + mac802154_wpan_setup); break; default: dev = NULL; diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index ede50d197e10..71cf1bffea06 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -1418,7 +1418,7 @@ static int __init nr_proto_init(void) struct net_device *dev; sprintf(name, "nr%d", i); - dev = alloc_netdev(0, name, nr_setup); + dev = alloc_netdev(0, name, NET_NAME_UNKNOWN, nr_setup); if (!dev) { printk(KERN_ERR "NET/ROM: nr_proto_init - unable to allocate device structure\n"); goto fail; diff --git a/net/openvswitch/vport-internal_dev.c b/net/openvswitch/vport-internal_dev.c index 295471a66c78..bd658555afdf 100644 --- a/net/openvswitch/vport-internal_dev.c +++ b/net/openvswitch/vport-internal_dev.c @@ -165,7 +165,8 @@ static struct vport *internal_dev_create(const struct vport_parms *parms) netdev_vport = netdev_vport_priv(vport); netdev_vport->dev = alloc_netdev(sizeof(struct internal_dev), - parms->name, do_setup); + parms->name, NET_NAME_UNKNOWN, + do_setup); if (!netdev_vport->dev) { err = -ENOMEM; goto error_free_vport; diff --git a/net/phonet/pep-gprs.c b/net/phonet/pep-gprs.c index 66dc65e7c6a1..e9a83a637185 100644 --- a/net/phonet/pep-gprs.c +++ b/net/phonet/pep-gprs.c @@ -267,7 +267,7 @@ int gprs_attach(struct sock *sk) return -EINVAL; /* need packet boundaries */ /* Create net device */ - dev = alloc_netdev(sizeof(*gp), ifname, gprs_setup); + dev = alloc_netdev(sizeof(*gp), ifname, NET_NAME_UNKNOWN, gprs_setup); if (!dev) return -ENOMEM; gp = netdev_priv(dev); diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index 8451c8cdc9de..a85c1a086ae4 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -1538,7 +1538,7 @@ static int __init rose_proto_init(void) char name[IFNAMSIZ]; sprintf(name, "rose%d", i); - dev = alloc_netdev(0, name, rose_setup); + dev = alloc_netdev(0, name, NET_NAME_UNKNOWN, rose_setup); if (!dev) { printk(KERN_ERR "ROSE: rose_proto_init - unable to allocate memory\n"); rc = -ENOMEM; diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index 474167162947..bd33793b527e 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -485,8 +485,8 @@ static int __init teql_init(void) struct net_device *dev; struct teql_master *master; - dev = alloc_netdev(sizeof(struct teql_master), - "teql%d", teql_master_setup); + dev = alloc_netdev(sizeof(struct teql_master), "teql%d", + NET_NAME_UNKNOWN, teql_master_setup); if (!dev) { err = -ENOMEM; break; -- cgit v1.2.3 From 5517750f058edd111bcabe5e116056cc63b1f39c Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Mon, 14 Jul 2014 16:37:25 +0200 Subject: net: rtnetlink - make create_link take name_assign_type This passes down NET_NAME_USER (or NET_NAME_ENUM) to alloc_netdev(), for any device created over rtnetlink. v9: restore reverse-christmas-tree order of local variables Signed-off-by: Tom Gundersen Signed-off-by: David S. Miller --- drivers/net/veth.c | 11 ++++++++--- include/net/rtnetlink.h | 1 + net/core/rtnetlink.c | 12 ++++++++---- 3 files changed, 17 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 9b945e60530e..8ad596573d17 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -335,6 +335,7 @@ static int veth_newlink(struct net *src_net, struct net_device *dev, struct veth_priv *priv; char ifname[IFNAMSIZ]; struct nlattr *peer_tb[IFLA_MAX + 1], **tbp; + unsigned char name_assign_type; struct ifinfomsg *ifmp; struct net *net; @@ -362,16 +363,20 @@ static int veth_newlink(struct net *src_net, struct net_device *dev, tbp = tb; } - if (tbp[IFLA_IFNAME]) + if (tbp[IFLA_IFNAME]) { nla_strlcpy(ifname, tbp[IFLA_IFNAME], IFNAMSIZ); - else + name_assign_type = NET_NAME_USER; + } else { snprintf(ifname, IFNAMSIZ, DRV_NAME "%%d"); + name_assign_type = NET_NAME_ENUM; + } net = rtnl_link_get_net(src_net, tbp); if (IS_ERR(net)) return PTR_ERR(net); - peer = rtnl_create_link(net, ifname, &veth_link_ops, tbp); + peer = rtnl_create_link(net, ifname, name_assign_type, + &veth_link_ops, tbp); if (IS_ERR(peer)) { put_net(net); return PTR_ERR(peer); diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h index 72240e5ac2c4..e21b9f9653c0 100644 --- a/include/net/rtnetlink.h +++ b/include/net/rtnetlink.h @@ -136,6 +136,7 @@ void rtnl_af_unregister(struct rtnl_af_ops *ops); struct net *rtnl_link_get_net(struct net *src_net, struct nlattr *tb[]); struct net_device *rtnl_create_link(struct net *net, char *ifname, + unsigned char name_assign_type, const struct rtnl_link_ops *ops, struct nlattr *tb[]); int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 599864322de8..e9918020dbc9 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1810,7 +1810,8 @@ int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm) EXPORT_SYMBOL(rtnl_configure_link); struct net_device *rtnl_create_link(struct net *net, - char *ifname, const struct rtnl_link_ops *ops, struct nlattr *tb[]) + char *ifname, unsigned char name_assign_type, + const struct rtnl_link_ops *ops, struct nlattr *tb[]) { int err; struct net_device *dev; @@ -1828,7 +1829,7 @@ struct net_device *rtnl_create_link(struct net *net, num_rx_queues = ops->get_num_rx_queues(); err = -ENOMEM; - dev = alloc_netdev_mqs(ops->priv_size, ifname, NET_NAME_UNKNOWN, + dev = alloc_netdev_mqs(ops->priv_size, ifname, name_assign_type, ops->setup, num_tx_queues, num_rx_queues); if (!dev) goto err; @@ -1894,6 +1895,7 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh) char ifname[IFNAMSIZ]; struct nlattr *tb[IFLA_MAX+1]; struct nlattr *linkinfo[IFLA_INFO_MAX+1]; + unsigned char name_assign_type = NET_NAME_USER; int err; #ifdef CONFIG_MODULES @@ -2046,14 +2048,16 @@ replay: if (!ops->setup) return -EOPNOTSUPP; - if (!ifname[0]) + if (!ifname[0]) { snprintf(ifname, IFNAMSIZ, "%s%%d", ops->kind); + name_assign_type = NET_NAME_ENUM; + } dest_net = rtnl_link_get_net(net, tb); if (IS_ERR(dest_net)) return PTR_ERR(dest_net); - dev = rtnl_create_link(dest_net, ifname, ops, tb); + dev = rtnl_create_link(dest_net, ifname, name_assign_type, ops, tb); if (IS_ERR(dev)) { err = PTR_ERR(dev); goto out; -- cgit v1.2.3 From 5ee2c941b5969eb1b5592f9731b3ee76a784641f Mon Sep 17 00:00:00 2001 From: Christoph Paasch Date: Mon, 14 Jul 2014 16:58:32 +0200 Subject: tcp: Remove unnecessary arg from tcp_enter_cwr and tcp_init_cwnd_reduction Since Yuchung's 9b44190dc11 (tcp: refactor F-RTO), tcp_enter_cwr is always called with set_ssthresh = 1. Thus, we can remove this argument from tcp_enter_cwr. Further, as we remove this one, tcp_init_cwnd_reduction is then always called with set_ssthresh = true, and so we can get rid of this argument as well. Cc: Yuchung Cheng Signed-off-by: Christoph Paasch Acked-by: Yuchung Cheng Signed-off-by: David S. Miller --- include/net/tcp.h | 2 +- net/ipv4/tcp_input.c | 15 +++++++-------- net/ipv4/tcp_output.c | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index c9a75dbba0c7..0aeb2eb749dc 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -928,7 +928,7 @@ static inline __u32 tcp_current_ssthresh(const struct sock *sk) /* Use define here intentionally to get WARN_ON location shown at the caller */ #define tcp_verify_left_out(tp) WARN_ON(tcp_left_out(tp) > tp->packets_out) -void tcp_enter_cwr(struct sock *sk, const int set_ssthresh); +void tcp_enter_cwr(struct sock *sk); __u32 tcp_init_cwnd(const struct tcp_sock *tp, const struct dst_entry *dst); /* The maximum number of MSS of available cwnd for which TSO defers diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index bb684967f7a7..2e16afba182c 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -2475,7 +2475,7 @@ static bool tcp_try_undo_loss(struct sock *sk, bool frto_undo) * losses and/or application stalls), do not perform any further cwnd * reductions, but instead slow start up to ssthresh. */ -static void tcp_init_cwnd_reduction(struct sock *sk, const bool set_ssthresh) +static void tcp_init_cwnd_reduction(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); @@ -2485,8 +2485,7 @@ static void tcp_init_cwnd_reduction(struct sock *sk, const bool set_ssthresh) tp->prior_cwnd = tp->snd_cwnd; tp->prr_delivered = 0; tp->prr_out = 0; - if (set_ssthresh) - tp->snd_ssthresh = inet_csk(sk)->icsk_ca_ops->ssthresh(sk); + tp->snd_ssthresh = inet_csk(sk)->icsk_ca_ops->ssthresh(sk); TCP_ECN_queue_cwr(tp); } @@ -2528,14 +2527,14 @@ static inline void tcp_end_cwnd_reduction(struct sock *sk) } /* Enter CWR state. Disable cwnd undo since congestion is proven with ECN */ -void tcp_enter_cwr(struct sock *sk, const int set_ssthresh) +void tcp_enter_cwr(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); tp->prior_ssthresh = 0; if (inet_csk(sk)->icsk_ca_state < TCP_CA_CWR) { tp->undo_marker = 0; - tcp_init_cwnd_reduction(sk, set_ssthresh); + tcp_init_cwnd_reduction(sk); tcp_set_ca_state(sk, TCP_CA_CWR); } } @@ -2564,7 +2563,7 @@ static void tcp_try_to_open(struct sock *sk, int flag, const int prior_unsacked) tp->retrans_stamp = 0; if (flag & FLAG_ECE) - tcp_enter_cwr(sk, 1); + tcp_enter_cwr(sk); if (inet_csk(sk)->icsk_ca_state != TCP_CA_CWR) { tcp_try_keep_open(sk); @@ -2670,7 +2669,7 @@ static void tcp_enter_recovery(struct sock *sk, bool ece_ack) if (inet_csk(sk)->icsk_ca_state < TCP_CA_CWR) { if (!ece_ack) tp->prior_ssthresh = tcp_current_ssthresh(sk); - tcp_init_cwnd_reduction(sk, true); + tcp_init_cwnd_reduction(sk); } tcp_set_ca_state(sk, TCP_CA_Recovery); } @@ -3346,7 +3345,7 @@ static void tcp_process_tlp_ack(struct sock *sk, u32 ack, int flag) tp->tlp_high_seq = 0; /* Don't reduce cwnd if DSACK arrives for TLP retrans. */ if (!(flag & FLAG_DSACKING_ACK)) { - tcp_init_cwnd_reduction(sk, true); + tcp_init_cwnd_reduction(sk); tcp_set_ca_state(sk, TCP_CA_CWR); tcp_end_cwnd_reduction(sk); tcp_try_keep_open(sk); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index bcee13c4627c..306dd5dead7d 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -979,7 +979,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, if (likely(err <= 0)) return err; - tcp_enter_cwr(sk, 1); + tcp_enter_cwr(sk); return net_xmit_eval(err); } -- cgit v1.2.3 From 26c4fdb0528ae7c4be9fbc8a8210f3b410e6b5aa Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Mon, 14 Jul 2014 17:55:30 -0400 Subject: net-timestamp: document deprecated syststamp The SO_TIMESTAMPING API defines option SOF_TIMESTAMPING_SYS_HW. This feature is deprecated. It should not be implemented by new device drivers. Existing drivers do not implement it, either -- with one exception. Driver developers are encouraged to expose the NIC hw clock as a PTP HW clock source, instead, and synchronize system time to the HW source. The control flag cannot be removed due to being part of the ABI, nor can the structure scm_timestamping that is returned. Due to the one legacy driver, the internal datapath and structure are not removed. This patch only clearly marks the interface as deprecated. Device drivers should always return a syststamp value of zero. Signed-off-by: Willem de Bruijn ---- We can consider adding a WARN_ON_ONCE in__sock_recv_timestamp if non-zero syststamp is encountered Acked-by: Richard Cochran Signed-off-by: David S. Miller --- Documentation/networking/timestamping.txt | 10 ++++++++-- include/linux/skbuff.h | 6 ++++-- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/Documentation/networking/timestamping.txt b/Documentation/networking/timestamping.txt index bc3554124903..8b4ad809df27 100644 --- a/Documentation/networking/timestamping.txt +++ b/Documentation/networking/timestamping.txt @@ -40,7 +40,7 @@ the set bits correspond to data that is available, then the control message will not be generated: SOF_TIMESTAMPING_SOFTWARE: report systime if available -SOF_TIMESTAMPING_SYS_HARDWARE: report hwtimetrans if available +SOF_TIMESTAMPING_SYS_HARDWARE: report hwtimetrans if available (deprecated) SOF_TIMESTAMPING_RAW_HARDWARE: report hwtimeraw if available It is worth noting that timestamps may be collected for reasons other @@ -94,7 +94,13 @@ not perfect; as a consequence, sorting packets received via different NICs by their hwtimetrans may differ from the order in which they were received. hwtimetrans may be non-monotonic even for the same NIC. Filled in if SOF_TIMESTAMPING_SYS_HARDWARE is set. Requires support -by the network device and will be empty without that support. +by the network device and will be empty without that support. This +field is DEPRECATED. Only one driver computes this value. New device +drivers must leave this zero. Instead, they can expose the hardware +clock device on the NIC directly as a HW PTP clock source, to allow +time conversion in userspace and optionally synchronize system time +with a userspace PTP stack such as linuxptp. For the PTP clock API, +see Documentation/ptp/ptp.txt. SIOCSHWTSTAMP, SIOCGHWTSTAMP: diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 890fb3307dd6..369430340ed9 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -211,7 +211,7 @@ static inline void skb_frag_size_sub(skb_frag_t *frag, int delta) * struct skb_shared_hwtstamps - hardware time stamps * @hwtstamp: hardware time stamp transformed into duration * since arbitrary point in time - * @syststamp: hwtstamp transformed to system time base + * @syststamp: hwtstamp transformed to system time base (deprecated) * * Software time stamps generated by ktime_get_real() are stored in * skb->tstamp. The relation between the different kinds of time @@ -222,7 +222,9 @@ static inline void skb_frag_size_sub(skb_frag_t *frag, int delta) * syststamp/tstamp/"syststamp from other device" comparison is * limited by the accuracy of the transformation into system time * base. This depends on the device driver and its underlying - * hardware. + * hardware. The syststamp implementation is deprecated in favor + * of hwtstamps and hw PTP clock sources exposed directly to + * userspace. * * hwtstamps can only be compared against other hwtstamps from * the same device. -- cgit v1.2.3 From 2b46cd23a5a2cf0b8d3583338b63409f5e78e7cd Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Thu, 12 Jun 2014 19:41:42 +0300 Subject: clk: qcom: Add APQ8084 Multimedia Clock Controller (MMCC) support Add support for the multimedia clock controller found on the APQ8084 based platforms. This will allow the multimedia device drivers to control their clocks. Signed-off-by: Georgi Djakov [sboyd: Rework parent mapping to avoid conflicts] Signed-off-by: Stephen Boyd --- .../devicetree/bindings/clock/qcom,mmcc.txt | 1 + drivers/clk/qcom/Kconfig | 9 + drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/mmcc-apq8084.c | 3352 ++++++++++++++++++++ include/dt-bindings/clock/qcom,mmcc-apq8084.h | 183 ++ include/dt-bindings/reset/qcom,mmcc-apq8084.h | 64 + 6 files changed, 3610 insertions(+) create mode 100644 drivers/clk/qcom/mmcc-apq8084.c create mode 100644 include/dt-bindings/clock/qcom,mmcc-apq8084.h create mode 100644 include/dt-bindings/reset/qcom,mmcc-apq8084.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/qcom,mmcc.txt b/Documentation/devicetree/bindings/clock/qcom,mmcc.txt index d572e9964c54..4f1f6be9e66d 100644 --- a/Documentation/devicetree/bindings/clock/qcom,mmcc.txt +++ b/Documentation/devicetree/bindings/clock/qcom,mmcc.txt @@ -4,6 +4,7 @@ Qualcomm Multimedia Clock & Reset Controller Binding Required properties : - compatible : shall contain only one of the following: + "qcom,mmcc-apq8084" "qcom,mmcc-msm8660" "qcom,mmcc-msm8960" "qcom,mmcc-msm8974" diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index e7516c2a2502..e5f95161d747 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -12,6 +12,15 @@ config APQ_GCC_8084 Say Y if you want to use peripheral devices such as UART, SPI, i2c, USB, SD/eMMC, SATA, PCIe, etc. +config APQ_MMCC_8084 + tristate "APQ8084 Multimedia Clock Controller" + select APQ_GCC_8084 + depends on COMMON_CLK_QCOM + help + Support for the multimedia clock controller on apq8084 devices. + Say Y if you want to support multimedia devices such as display, + graphics, video encode/decode, camera, etc. + config MSM_GCC_8660 tristate "MSM8660 Global Clock Controller" depends on COMMON_CLK_QCOM diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 196a5df9125a..f9366261f836 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -9,6 +9,7 @@ clk-qcom-y += clk-branch.o clk-qcom-y += reset.o obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o +obj-$(CONFIG_APQ_MMCC_8084) += mmcc-apq8084.o obj-$(CONFIG_MSM_GCC_8660) += gcc-msm8660.o obj-$(CONFIG_MSM_GCC_8960) += gcc-msm8960.o obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o diff --git a/drivers/clk/qcom/mmcc-apq8084.c b/drivers/clk/qcom/mmcc-apq8084.c new file mode 100644 index 000000000000..751eea376a2b --- /dev/null +++ b/drivers/clk/qcom/mmcc-apq8084.c @@ -0,0 +1,3352 @@ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * 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. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "common.h" +#include "clk-regmap.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-branch.h" +#include "reset.h" + +#define P_XO 0 +#define P_MMPLL0 1 +#define P_EDPLINK 1 +#define P_MMPLL1 2 +#define P_HDMIPLL 2 +#define P_GPLL0 3 +#define P_EDPVCO 3 +#define P_MMPLL4 4 +#define P_DSI0PLL 4 +#define P_DSI0PLL_BYTE 4 +#define P_MMPLL2 4 +#define P_MMPLL3 4 +#define P_GPLL1 5 +#define P_DSI1PLL 5 +#define P_DSI1PLL_BYTE 5 +#define P_MMSLEEP 6 + +static const u8 mmcc_xo_mmpll0_mmpll1_gpll0_map[] = { + [P_XO] = 0, + [P_MMPLL0] = 1, + [P_MMPLL1] = 2, + [P_GPLL0] = 5, +}; + +static const char *mmcc_xo_mmpll0_mmpll1_gpll0[] = { + "xo", + "mmpll0_vote", + "mmpll1_vote", + "mmss_gpll0_vote", +}; + +static const u8 mmcc_xo_mmpll0_dsi_hdmi_gpll0_map[] = { + [P_XO] = 0, + [P_MMPLL0] = 1, + [P_HDMIPLL] = 4, + [P_GPLL0] = 5, + [P_DSI0PLL] = 2, + [P_DSI1PLL] = 3, +}; + +static const char *mmcc_xo_mmpll0_dsi_hdmi_gpll0[] = { + "xo", + "mmpll0_vote", + "hdmipll", + "mmss_gpll0_vote", + "dsi0pll", + "dsi1pll", +}; + +static const u8 mmcc_xo_mmpll0_1_2_gpll0_map[] = { + [P_XO] = 0, + [P_MMPLL0] = 1, + [P_MMPLL1] = 2, + [P_GPLL0] = 5, + [P_MMPLL2] = 3, +}; + +static const char *mmcc_xo_mmpll0_1_2_gpll0[] = { + "xo", + "mmpll0_vote", + "mmpll1_vote", + "mmss_gpll0_vote", + "mmpll2", +}; + +static const u8 mmcc_xo_mmpll0_1_3_gpll0_map[] = { + [P_XO] = 0, + [P_MMPLL0] = 1, + [P_MMPLL1] = 2, + [P_GPLL0] = 5, + [P_MMPLL3] = 3, +}; + +static const char *mmcc_xo_mmpll0_1_3_gpll0[] = { + "xo", + "mmpll0_vote", + "mmpll1_vote", + "mmss_gpll0_vote", + "mmpll3", +}; + +static const u8 mmcc_xo_dsi_hdmi_edp_map[] = { + [P_XO] = 0, + [P_EDPLINK] = 4, + [P_HDMIPLL] = 3, + [P_EDPVCO] = 5, + [P_DSI0PLL] = 1, + [P_DSI1PLL] = 2, +}; + +static const char *mmcc_xo_dsi_hdmi_edp[] = { + "xo", + "edp_link_clk", + "hdmipll", + "edp_vco_div", + "dsi0pll", + "dsi1pll", +}; + +static const u8 mmcc_xo_dsi_hdmi_edp_gpll0_map[] = { + [P_XO] = 0, + [P_EDPLINK] = 4, + [P_HDMIPLL] = 3, + [P_GPLL0] = 5, + [P_DSI0PLL] = 1, + [P_DSI1PLL] = 2, +}; + +static const char *mmcc_xo_dsi_hdmi_edp_gpll0[] = { + "xo", + "edp_link_clk", + "hdmipll", + "gpll0_vote", + "dsi0pll", + "dsi1pll", +}; + +static const u8 mmcc_xo_dsibyte_hdmi_edp_gpll0_map[] = { + [P_XO] = 0, + [P_EDPLINK] = 4, + [P_HDMIPLL] = 3, + [P_GPLL0] = 5, + [P_DSI0PLL_BYTE] = 1, + [P_DSI1PLL_BYTE] = 2, +}; + +static const char *mmcc_xo_dsibyte_hdmi_edp_gpll0[] = { + "xo", + "edp_link_clk", + "hdmipll", + "gpll0_vote", + "dsi0pllbyte", + "dsi1pllbyte", +}; + +static const u8 mmcc_xo_mmpll0_1_4_gpll0_map[] = { + [P_XO] = 0, + [P_MMPLL0] = 1, + [P_MMPLL1] = 2, + [P_GPLL0] = 5, + [P_MMPLL4] = 3, +}; + +static const char *mmcc_xo_mmpll0_1_4_gpll0[] = { + "xo", + "mmpll0", + "mmpll1", + "mmpll4", + "gpll0", +}; + +static const u8 mmcc_xo_mmpll0_1_4_gpll1_0_map[] = { + [P_XO] = 0, + [P_MMPLL0] = 1, + [P_MMPLL1] = 2, + [P_MMPLL4] = 3, + [P_GPLL0] = 5, + [P_GPLL1] = 4, +}; + +static const char *mmcc_xo_mmpll0_1_4_gpll1_0[] = { + "xo", + "mmpll0", + "mmpll1", + "mmpll4", + "gpll1", + "gpll0", +}; + +static const u8 mmcc_xo_mmpll0_1_4_gpll1_0_sleep_map[] = { + [P_XO] = 0, + [P_MMPLL0] = 1, + [P_MMPLL1] = 2, + [P_MMPLL4] = 3, + [P_GPLL0] = 5, + [P_GPLL1] = 4, + [P_MMSLEEP] = 6, +}; + +static const char *mmcc_xo_mmpll0_1_4_gpll1_0_sleep[] = { + "xo", + "mmpll0", + "mmpll1", + "mmpll4", + "gpll1", + "gpll0", + "sleep_clk_src", +}; + +#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) } + +static struct clk_pll mmpll0 = { + .l_reg = 0x0004, + .m_reg = 0x0008, + .n_reg = 0x000c, + .config_reg = 0x0014, + .mode_reg = 0x0000, + .status_reg = 0x001c, + .status_bit = 17, + .clkr.hw.init = &(struct clk_init_data){ + .name = "mmpll0", + .parent_names = (const char *[]){ "xo" }, + .num_parents = 1, + .ops = &clk_pll_ops, + }, +}; + +static struct clk_regmap mmpll0_vote = { + .enable_reg = 0x0100, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mmpll0_vote", + .parent_names = (const char *[]){ "mmpll0" }, + .num_parents = 1, + .ops = &clk_pll_vote_ops, + }, +}; + +static struct clk_pll mmpll1 = { + .l_reg = 0x0044, + .m_reg = 0x0048, + .n_reg = 0x004c, + .config_reg = 0x0050, + .mode_reg = 0x0040, + .status_reg = 0x005c, + .status_bit = 17, + .clkr.hw.init = &(struct clk_init_data){ + .name = "mmpll1", + .parent_names = (const char *[]){ "xo" }, + .num_parents = 1, + .ops = &clk_pll_ops, + }, +}; + +static struct clk_regmap mmpll1_vote = { + .enable_reg = 0x0100, + .enable_mask = BIT(1), + .hw.init = &(struct clk_init_data){ + .name = "mmpll1_vote", + .parent_names = (const char *[]){ "mmpll1" }, + .num_parents = 1, + .ops = &clk_pll_vote_ops, + }, +}; + +static struct clk_pll mmpll2 = { + .l_reg = 0x4104, + .m_reg = 0x4108, + .n_reg = 0x410c, + .config_reg = 0x4110, + .mode_reg = 0x4100, + .status_reg = 0x411c, + .clkr.hw.init = &(struct clk_init_data){ + .name = "mmpll2", + .parent_names = (const char *[]){ "xo" }, + .num_parents = 1, + .ops = &clk_pll_ops, + }, +}; + +static struct clk_pll mmpll3 = { + .l_reg = 0x0084, + .m_reg = 0x0088, + .n_reg = 0x008c, + .config_reg = 0x0090, + .mode_reg = 0x0080, + .status_reg = 0x009c, + .status_bit = 17, + .clkr.hw.init = &(struct clk_init_data){ + .name = "mmpll3", + .parent_names = (const char *[]){ "xo" }, + .num_parents = 1, + .ops = &clk_pll_ops, + }, +}; + +static struct clk_pll mmpll4 = { + .l_reg = 0x00a4, + .m_reg = 0x00a8, + .n_reg = 0x00ac, + .config_reg = 0x00b0, + .mode_reg = 0x0080, + .status_reg = 0x00bc, + .clkr.hw.init = &(struct clk_init_data){ + .name = "mmpll4", + .parent_names = (const char *[]){ "xo" }, + .num_parents = 1, + .ops = &clk_pll_ops, + }, +}; + +static struct clk_rcg2 mmss_ahb_clk_src = { + .cmd_rcgr = 0x5000, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_mmpll1_gpll0_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "mmss_ahb_clk_src", + .parent_names = mmcc_xo_mmpll0_mmpll1_gpll0, + .num_parents = 4, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_mmss_axi_clk[] = { + F(19200000, P_XO, 1, 0, 0), + F(37500000, P_GPLL0, 16, 0, 0), + F(50000000, P_GPLL0, 12, 0, 0), + F(75000000, P_GPLL0, 8, 0, 0), + F(100000000, P_GPLL0, 6, 0, 0), + F(150000000, P_GPLL0, 4, 0, 0), + F(333430000, P_MMPLL1, 3.5, 0, 0), + F(400000000, P_MMPLL0, 2, 0, 0), + F(466800000, P_MMPLL1, 2.5, 0, 0), +}; + +static struct clk_rcg2 mmss_axi_clk_src = { + .cmd_rcgr = 0x5040, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_mmpll1_gpll0_map, + .freq_tbl = ftbl_mmss_axi_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "mmss_axi_clk_src", + .parent_names = mmcc_xo_mmpll0_mmpll1_gpll0, + .num_parents = 4, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_ocmemnoc_clk[] = { + F(19200000, P_XO, 1, 0, 0), + F(37500000, P_GPLL0, 16, 0, 0), + F(50000000, P_GPLL0, 12, 0, 0), + F(75000000, P_GPLL0, 8, 0, 0), + F(109090000, P_GPLL0, 5.5, 0, 0), + F(150000000, P_GPLL0, 4, 0, 0), + F(228570000, P_MMPLL0, 3.5, 0, 0), + F(320000000, P_MMPLL0, 2.5, 0, 0), +}; + +static struct clk_rcg2 ocmemnoc_clk_src = { + .cmd_rcgr = 0x5090, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_mmpll1_gpll0_map, + .freq_tbl = ftbl_ocmemnoc_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "ocmemnoc_clk_src", + .parent_names = mmcc_xo_mmpll0_mmpll1_gpll0, + .num_parents = 4, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_camss_csi0_3_clk[] = { + F(100000000, P_GPLL0, 6, 0, 0), + F(200000000, P_MMPLL0, 4, 0, 0), + { } +}; + +static struct clk_rcg2 csi0_clk_src = { + .cmd_rcgr = 0x3090, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_1_4_gpll0_map, + .freq_tbl = ftbl_camss_csi0_3_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "csi0_clk_src", + .parent_names = mmcc_xo_mmpll0_1_4_gpll0, + .num_parents = 5, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 csi1_clk_src = { + .cmd_rcgr = 0x3100, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_1_4_gpll0_map, + .freq_tbl = ftbl_camss_csi0_3_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "csi1_clk_src", + .parent_names = mmcc_xo_mmpll0_1_4_gpll0, + .num_parents = 5, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 csi2_clk_src = { + .cmd_rcgr = 0x3160, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_1_4_gpll0_map, + .freq_tbl = ftbl_camss_csi0_3_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "csi2_clk_src", + .parent_names = mmcc_xo_mmpll0_1_4_gpll0, + .num_parents = 5, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 csi3_clk_src = { + .cmd_rcgr = 0x31c0, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_1_4_gpll0_map, + .freq_tbl = ftbl_camss_csi0_3_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "csi3_clk_src", + .parent_names = mmcc_xo_mmpll0_1_4_gpll0, + .num_parents = 5, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_camss_vfe_vfe0_1_clk[] = { + F(37500000, P_GPLL0, 16, 0, 0), + F(50000000, P_GPLL0, 12, 0, 0), + F(60000000, P_GPLL0, 10, 0, 0), + F(80000000, P_GPLL0, 7.5, 0, 0), + F(100000000, P_GPLL0, 6, 0, 0), + F(109090000, P_GPLL0, 5.5, 0, 0), + F(133330000, P_GPLL0, 4.5, 0, 0), + F(200000000, P_GPLL0, 3, 0, 0), + F(228570000, P_MMPLL0, 3.5, 0, 0), + F(266670000, P_MMPLL0, 3, 0, 0), + F(320000000, P_MMPLL0, 2.5, 0, 0), + F(465000000, P_MMPLL4, 2, 0, 0), + F(600000000, P_GPLL0, 1, 0, 0), + { } +}; + +static struct clk_rcg2 vfe0_clk_src = { + .cmd_rcgr = 0x3600, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_1_4_gpll0_map, + .freq_tbl = ftbl_camss_vfe_vfe0_1_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "vfe0_clk_src", + .parent_names = mmcc_xo_mmpll0_1_4_gpll0, + .num_parents = 5, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 vfe1_clk_src = { + .cmd_rcgr = 0x3620, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_1_4_gpll0_map, + .freq_tbl = ftbl_camss_vfe_vfe0_1_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "vfe1_clk_src", + .parent_names = mmcc_xo_mmpll0_1_4_gpll0, + .num_parents = 5, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_mdss_mdp_clk[] = { + F(37500000, P_GPLL0, 16, 0, 0), + F(60000000, P_GPLL0, 10, 0, 0), + F(75000000, P_GPLL0, 8, 0, 0), + F(85710000, P_GPLL0, 7, 0, 0), + F(100000000, P_GPLL0, 6, 0, 0), + F(150000000, P_GPLL0, 4, 0, 0), + F(160000000, P_MMPLL0, 5, 0, 0), + F(200000000, P_MMPLL0, 4, 0, 0), + F(228570000, P_MMPLL0, 3.5, 0, 0), + F(300000000, P_GPLL0, 2, 0, 0), + F(320000000, P_MMPLL0, 2.5, 0, 0), + { } +}; + +static struct clk_rcg2 mdp_clk_src = { + .cmd_rcgr = 0x2040, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_dsi_hdmi_gpll0_map, + .freq_tbl = ftbl_mdss_mdp_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "mdp_clk_src", + .parent_names = mmcc_xo_mmpll0_dsi_hdmi_gpll0, + .num_parents = 6, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gfx3d_clk_src = { + .cmd_rcgr = 0x4000, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_1_2_gpll0_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gfx3d_clk_src", + .parent_names = mmcc_xo_mmpll0_1_2_gpll0, + .num_parents = 5, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_camss_jpeg_jpeg0_2_clk[] = { + F(75000000, P_GPLL0, 8, 0, 0), + F(133330000, P_GPLL0, 4.5, 0, 0), + F(200000000, P_GPLL0, 3, 0, 0), + F(228570000, P_MMPLL0, 3.5, 0, 0), + F(266670000, P_MMPLL0, 3, 0, 0), + F(320000000, P_MMPLL0, 2.5, 0, 0), + { } +}; + +static struct clk_rcg2 jpeg0_clk_src = { + .cmd_rcgr = 0x3500, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_1_4_gpll0_map, + .freq_tbl = ftbl_camss_jpeg_jpeg0_2_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "jpeg0_clk_src", + .parent_names = mmcc_xo_mmpll0_1_4_gpll0, + .num_parents = 5, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 jpeg1_clk_src = { + .cmd_rcgr = 0x3520, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_1_4_gpll0_map, + .freq_tbl = ftbl_camss_jpeg_jpeg0_2_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "jpeg1_clk_src", + .parent_names = mmcc_xo_mmpll0_1_4_gpll0, + .num_parents = 5, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 jpeg2_clk_src = { + .cmd_rcgr = 0x3540, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_1_4_gpll0_map, + .freq_tbl = ftbl_camss_jpeg_jpeg0_2_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "jpeg2_clk_src", + .parent_names = mmcc_xo_mmpll0_1_4_gpll0, + .num_parents = 5, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl pixel_freq_tbl[] = { + { .src = P_DSI0PLL }, + { } +}; + +static struct clk_rcg2 pclk0_clk_src = { + .cmd_rcgr = 0x2000, + .mnd_width = 8, + .hid_width = 5, + .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map, + .freq_tbl = pixel_freq_tbl, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pclk0_clk_src", + .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0, + .num_parents = 6, + .ops = &clk_pixel_ops, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_rcg2 pclk1_clk_src = { + .cmd_rcgr = 0x2020, + .mnd_width = 8, + .hid_width = 5, + .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map, + .freq_tbl = pixel_freq_tbl, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pclk1_clk_src", + .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0, + .num_parents = 6, + .ops = &clk_pixel_ops, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct freq_tbl ftbl_venus0_vcodec0_clk[] = { + F(50000000, P_GPLL0, 12, 0, 0), + F(100000000, P_GPLL0, 6, 0, 0), + F(133330000, P_GPLL0, 4.5, 0, 0), + F(200000000, P_MMPLL0, 4, 0, 0), + F(266670000, P_MMPLL0, 3, 0, 0), + F(465000000, P_MMPLL3, 2, 0, 0), + { } +}; + +static struct clk_rcg2 vcodec0_clk_src = { + .cmd_rcgr = 0x1000, + .mnd_width = 8, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_1_3_gpll0_map, + .freq_tbl = ftbl_venus0_vcodec0_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "vcodec0_clk_src", + .parent_names = mmcc_xo_mmpll0_1_3_gpll0, + .num_parents = 5, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_avsync_vp_clk[] = { + F(150000000, P_GPLL0, 4, 0, 0), + F(320000000, P_MMPLL0, 2.5, 0, 0), + { } +}; + +static struct clk_rcg2 vp_clk_src = { + .cmd_rcgr = 0x2430, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_mmpll1_gpll0_map, + .freq_tbl = ftbl_avsync_vp_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "vp_clk_src", + .parent_names = mmcc_xo_mmpll0_mmpll1_gpll0, + .num_parents = 4, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_camss_cci_cci_clk[] = { + F(19200000, P_XO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cci_clk_src = { + .cmd_rcgr = 0x3300, + .mnd_width = 8, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_1_4_gpll1_0_map, + .freq_tbl = ftbl_camss_cci_cci_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cci_clk_src", + .parent_names = mmcc_xo_mmpll0_1_4_gpll1_0, + .num_parents = 6, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_camss_gp0_1_clk[] = { + F(10000, P_XO, 16, 1, 120), + F(24000, P_XO, 16, 1, 50), + F(6000000, P_GPLL0, 10, 1, 10), + F(12000000, P_GPLL0, 10, 1, 5), + F(13000000, P_GPLL0, 4, 13, 150), + F(24000000, P_GPLL0, 5, 1, 5), + { } +}; + +static struct clk_rcg2 camss_gp0_clk_src = { + .cmd_rcgr = 0x3420, + .mnd_width = 8, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_1_4_gpll1_0_sleep_map, + .freq_tbl = ftbl_camss_gp0_1_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "camss_gp0_clk_src", + .parent_names = mmcc_xo_mmpll0_1_4_gpll1_0_sleep, + .num_parents = 7, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 camss_gp1_clk_src = { + .cmd_rcgr = 0x3450, + .mnd_width = 8, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_1_4_gpll1_0_sleep_map, + .freq_tbl = ftbl_camss_gp0_1_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "camss_gp1_clk_src", + .parent_names = mmcc_xo_mmpll0_1_4_gpll1_0_sleep, + .num_parents = 7, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_camss_mclk0_3_clk[] = { + F(4800000, P_XO, 4, 0, 0), + F(6000000, P_GPLL0, 10, 1, 10), + F(8000000, P_GPLL0, 15, 1, 5), + F(9600000, P_XO, 2, 0, 0), + F(16000000, P_MMPLL0, 10, 1, 5), + F(19200000, P_XO, 1, 0, 0), + F(24000000, P_GPLL0, 5, 1, 5), + F(32000000, P_MMPLL0, 5, 1, 5), + F(48000000, P_GPLL0, 12.5, 0, 0), + F(64000000, P_MMPLL0, 12.5, 0, 0), + { } +}; + +static struct clk_rcg2 mclk0_clk_src = { + .cmd_rcgr = 0x3360, + .mnd_width = 8, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_1_4_gpll1_0_map, + .freq_tbl = ftbl_camss_mclk0_3_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "mclk0_clk_src", + .parent_names = mmcc_xo_mmpll0_1_4_gpll1_0, + .num_parents = 6, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 mclk1_clk_src = { + .cmd_rcgr = 0x3390, + .mnd_width = 8, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_1_4_gpll1_0_map, + .freq_tbl = ftbl_camss_mclk0_3_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "mclk1_clk_src", + .parent_names = mmcc_xo_mmpll0_1_4_gpll1_0, + .num_parents = 6, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 mclk2_clk_src = { + .cmd_rcgr = 0x33c0, + .mnd_width = 8, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_1_4_gpll1_0_map, + .freq_tbl = ftbl_camss_mclk0_3_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "mclk2_clk_src", + .parent_names = mmcc_xo_mmpll0_1_4_gpll1_0, + .num_parents = 6, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 mclk3_clk_src = { + .cmd_rcgr = 0x33f0, + .mnd_width = 8, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_1_4_gpll1_0_map, + .freq_tbl = ftbl_camss_mclk0_3_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "mclk3_clk_src", + .parent_names = mmcc_xo_mmpll0_1_4_gpll1_0, + .num_parents = 6, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_camss_phy0_2_csi0_2phytimer_clk[] = { + F(100000000, P_GPLL0, 6, 0, 0), + F(200000000, P_MMPLL0, 4, 0, 0), + { } +}; + +static struct clk_rcg2 csi0phytimer_clk_src = { + .cmd_rcgr = 0x3000, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_1_4_gpll0_map, + .freq_tbl = ftbl_camss_phy0_2_csi0_2phytimer_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "csi0phytimer_clk_src", + .parent_names = mmcc_xo_mmpll0_1_4_gpll0, + .num_parents = 5, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 csi1phytimer_clk_src = { + .cmd_rcgr = 0x3030, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_1_4_gpll0_map, + .freq_tbl = ftbl_camss_phy0_2_csi0_2phytimer_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "csi1phytimer_clk_src", + .parent_names = mmcc_xo_mmpll0_mmpll1_gpll0, + .num_parents = 5, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 csi2phytimer_clk_src = { + .cmd_rcgr = 0x3060, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_1_4_gpll0_map, + .freq_tbl = ftbl_camss_phy0_2_csi0_2phytimer_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "csi2phytimer_clk_src", + .parent_names = mmcc_xo_mmpll0_1_4_gpll0, + .num_parents = 5, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_camss_vfe_cpp_clk[] = { + F(133330000, P_GPLL0, 4.5, 0, 0), + F(266670000, P_MMPLL0, 3, 0, 0), + F(320000000, P_MMPLL0, 2.5, 0, 0), + F(372000000, P_MMPLL4, 2.5, 0, 0), + F(465000000, P_MMPLL4, 2, 0, 0), + F(600000000, P_GPLL0, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cpp_clk_src = { + .cmd_rcgr = 0x3640, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_1_4_gpll0_map, + .freq_tbl = ftbl_camss_vfe_cpp_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "cpp_clk_src", + .parent_names = mmcc_xo_mmpll0_1_4_gpll0, + .num_parents = 5, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl byte_freq_tbl[] = { + { .src = P_DSI0PLL_BYTE }, + { } +}; + +static struct clk_rcg2 byte0_clk_src = { + .cmd_rcgr = 0x2120, + .hid_width = 5, + .parent_map = mmcc_xo_dsibyte_hdmi_edp_gpll0_map, + .freq_tbl = byte_freq_tbl, + .clkr.hw.init = &(struct clk_init_data){ + .name = "byte0_clk_src", + .parent_names = mmcc_xo_dsibyte_hdmi_edp_gpll0, + .num_parents = 6, + .ops = &clk_byte_ops, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_rcg2 byte1_clk_src = { + .cmd_rcgr = 0x2140, + .hid_width = 5, + .parent_map = mmcc_xo_dsibyte_hdmi_edp_gpll0_map, + .freq_tbl = byte_freq_tbl, + .clkr.hw.init = &(struct clk_init_data){ + .name = "byte1_clk_src", + .parent_names = mmcc_xo_dsibyte_hdmi_edp_gpll0, + .num_parents = 6, + .ops = &clk_byte_ops, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct freq_tbl ftbl_mdss_edpaux_clk[] = { + F(19200000, P_XO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 edpaux_clk_src = { + .cmd_rcgr = 0x20e0, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_mmpll1_gpll0_map, + .freq_tbl = ftbl_mdss_edpaux_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "edpaux_clk_src", + .parent_names = mmcc_xo_mmpll0_mmpll1_gpll0, + .num_parents = 4, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_mdss_edplink_clk[] = { + F(135000000, P_EDPLINK, 2, 0, 0), + F(270000000, P_EDPLINK, 11, 0, 0), + { } +}; + +static struct clk_rcg2 edplink_clk_src = { + .cmd_rcgr = 0x20c0, + .hid_width = 5, + .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map, + .freq_tbl = ftbl_mdss_edplink_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "edplink_clk_src", + .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0, + .num_parents = 6, + .ops = &clk_rcg2_ops, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct freq_tbl edp_pixel_freq_tbl[] = { + { .src = P_EDPVCO }, + { } +}; + +static struct clk_rcg2 edppixel_clk_src = { + .cmd_rcgr = 0x20a0, + .mnd_width = 8, + .hid_width = 5, + .parent_map = mmcc_xo_dsi_hdmi_edp_map, + .freq_tbl = edp_pixel_freq_tbl, + .clkr.hw.init = &(struct clk_init_data){ + .name = "edppixel_clk_src", + .parent_names = mmcc_xo_dsi_hdmi_edp, + .num_parents = 6, + .ops = &clk_edp_pixel_ops, + }, +}; + +static struct freq_tbl ftbl_mdss_esc0_1_clk[] = { + F(19200000, P_XO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 esc0_clk_src = { + .cmd_rcgr = 0x2160, + .hid_width = 5, + .parent_map = mmcc_xo_dsibyte_hdmi_edp_gpll0_map, + .freq_tbl = ftbl_mdss_esc0_1_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "esc0_clk_src", + .parent_names = mmcc_xo_dsibyte_hdmi_edp_gpll0, + .num_parents = 6, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 esc1_clk_src = { + .cmd_rcgr = 0x2180, + .hid_width = 5, + .parent_map = mmcc_xo_dsibyte_hdmi_edp_gpll0_map, + .freq_tbl = ftbl_mdss_esc0_1_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "esc1_clk_src", + .parent_names = mmcc_xo_dsibyte_hdmi_edp_gpll0, + .num_parents = 6, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl extpclk_freq_tbl[] = { + { .src = P_HDMIPLL }, + { } +}; + +static struct clk_rcg2 extpclk_clk_src = { + .cmd_rcgr = 0x2060, + .hid_width = 5, + .parent_map = mmcc_xo_dsi_hdmi_edp_gpll0_map, + .freq_tbl = extpclk_freq_tbl, + .clkr.hw.init = &(struct clk_init_data){ + .name = "extpclk_clk_src", + .parent_names = mmcc_xo_dsi_hdmi_edp_gpll0, + .num_parents = 6, + .ops = &clk_byte_ops, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct freq_tbl ftbl_mdss_hdmi_clk[] = { + F(19200000, P_XO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 hdmi_clk_src = { + .cmd_rcgr = 0x2100, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_mmpll1_gpll0_map, + .freq_tbl = ftbl_mdss_hdmi_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "hdmi_clk_src", + .parent_names = mmcc_xo_mmpll0_mmpll1_gpll0, + .num_parents = 4, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_mdss_vsync_clk[] = { + F(19200000, P_XO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 vsync_clk_src = { + .cmd_rcgr = 0x2080, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_mmpll1_gpll0_map, + .freq_tbl = ftbl_mdss_vsync_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "vsync_clk_src", + .parent_names = mmcc_xo_mmpll0_mmpll1_gpll0, + .num_parents = 4, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_mmss_rbcpr_clk[] = { + F(50000000, P_GPLL0, 12, 0, 0), + { } +}; + +static struct clk_rcg2 rbcpr_clk_src = { + .cmd_rcgr = 0x4060, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_mmpll1_gpll0_map, + .freq_tbl = ftbl_mmss_rbcpr_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "rbcpr_clk_src", + .parent_names = mmcc_xo_mmpll0_mmpll1_gpll0, + .num_parents = 4, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_oxili_rbbmtimer_clk[] = { + F(19200000, P_XO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 rbbmtimer_clk_src = { + .cmd_rcgr = 0x4090, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_mmpll1_gpll0_map, + .freq_tbl = ftbl_oxili_rbbmtimer_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "rbbmtimer_clk_src", + .parent_names = mmcc_xo_mmpll0_mmpll1_gpll0, + .num_parents = 4, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_vpu_maple_clk[] = { + F(50000000, P_GPLL0, 12, 0, 0), + F(100000000, P_GPLL0, 6, 0, 0), + F(133330000, P_GPLL0, 4.5, 0, 0), + F(200000000, P_MMPLL0, 4, 0, 0), + F(266670000, P_MMPLL0, 3, 0, 0), + F(465000000, P_MMPLL3, 2, 0, 0), + { } +}; + +static struct clk_rcg2 maple_clk_src = { + .cmd_rcgr = 0x1320, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_mmpll1_gpll0_map, + .freq_tbl = ftbl_vpu_maple_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "maple_clk_src", + .parent_names = mmcc_xo_mmpll0_mmpll1_gpll0, + .num_parents = 4, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_vpu_vdp_clk[] = { + F(50000000, P_GPLL0, 12, 0, 0), + F(100000000, P_GPLL0, 6, 0, 0), + F(200000000, P_MMPLL0, 4, 0, 0), + F(320000000, P_MMPLL0, 2.5, 0, 0), + F(400000000, P_MMPLL0, 2, 0, 0), + { } +}; + +static struct clk_rcg2 vdp_clk_src = { + .cmd_rcgr = 0x1300, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_mmpll1_gpll0_map, + .freq_tbl = ftbl_vpu_vdp_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "vdp_clk_src", + .parent_names = mmcc_xo_mmpll0_mmpll1_gpll0, + .num_parents = 4, + .ops = &clk_rcg2_ops, + }, +}; + +static struct freq_tbl ftbl_vpu_bus_clk[] = { + F(40000000, P_GPLL0, 15, 0, 0), + F(80000000, P_MMPLL0, 10, 0, 0), + { } +}; + +static struct clk_rcg2 vpu_bus_clk_src = { + .cmd_rcgr = 0x1340, + .hid_width = 5, + .parent_map = mmcc_xo_mmpll0_mmpll1_gpll0_map, + .freq_tbl = ftbl_vpu_bus_clk, + .clkr.hw.init = &(struct clk_init_data){ + .name = "vpu_bus_clk_src", + .parent_names = mmcc_xo_mmpll0_mmpll1_gpll0, + .num_parents = 4, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_branch mmss_cxo_clk = { + .halt_reg = 0x5104, + .clkr = { + .enable_reg = 0x5104, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mmss_cxo_clk", + .parent_names = (const char *[]){ "xo" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mmss_sleepclk_clk = { + .halt_reg = 0x5100, + .clkr = { + .enable_reg = 0x5100, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mmss_sleepclk_clk", + .parent_names = (const char *[]){ + "sleep_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch avsync_ahb_clk = { + .halt_reg = 0x2414, + .clkr = { + .enable_reg = 0x2414, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "avsync_ahb_clk", + .parent_names = (const char *[]){ + "mmss_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch avsync_edppixel_clk = { + .halt_reg = 0x2418, + .clkr = { + .enable_reg = 0x2418, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "avsync_edppixel_clk", + .parent_names = (const char *[]){ + "edppixel_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch avsync_extpclk_clk = { + .halt_reg = 0x2410, + .clkr = { + .enable_reg = 0x2410, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "avsync_extpclk_clk", + .parent_names = (const char *[]){ + "extpclk_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch avsync_pclk0_clk = { + .halt_reg = 0x241c, + .clkr = { + .enable_reg = 0x241c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "avsync_pclk0_clk", + .parent_names = (const char *[]){ + "pclk0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch avsync_pclk1_clk = { + .halt_reg = 0x2420, + .clkr = { + .enable_reg = 0x2420, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "avsync_pclk1_clk", + .parent_names = (const char *[]){ + "pclk1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch avsync_vp_clk = { + .halt_reg = 0x2404, + .clkr = { + .enable_reg = 0x2404, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "avsync_vp_clk", + .parent_names = (const char *[]){ + "vp_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_ahb_clk = { + .halt_reg = 0x348c, + .clkr = { + .enable_reg = 0x348c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_ahb_clk", + .parent_names = (const char *[]){ + "mmss_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_cci_cci_ahb_clk = { + .halt_reg = 0x3348, + .clkr = { + .enable_reg = 0x3348, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_cci_cci_ahb_clk", + .parent_names = (const char *[]){ + "mmss_ahb_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_cci_cci_clk = { + .halt_reg = 0x3344, + .clkr = { + .enable_reg = 0x3344, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_cci_cci_clk", + .parent_names = (const char *[]){ + "cci_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_csi0_ahb_clk = { + .halt_reg = 0x30bc, + .clkr = { + .enable_reg = 0x30bc, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_csi0_ahb_clk", + .parent_names = (const char *[]){ + "mmss_ahb_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_csi0_clk = { + .halt_reg = 0x30b4, + .clkr = { + .enable_reg = 0x30b4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_csi0_clk", + .parent_names = (const char *[]){ + "csi0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_csi0phy_clk = { + .halt_reg = 0x30c4, + .clkr = { + .enable_reg = 0x30c4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_csi0phy_clk", + .parent_names = (const char *[]){ + "csi0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_csi0pix_clk = { + .halt_reg = 0x30e4, + .clkr = { + .enable_reg = 0x30e4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_csi0pix_clk", + .parent_names = (const char *[]){ + "csi0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_csi0rdi_clk = { + .halt_reg = 0x30d4, + .clkr = { + .enable_reg = 0x30d4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_csi0rdi_clk", + .parent_names = (const char *[]){ + "csi0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_csi1_ahb_clk = { + .halt_reg = 0x3128, + .clkr = { + .enable_reg = 0x3128, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_csi1_ahb_clk", + .parent_names = (const char *[]){ + "mmss_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_csi1_clk = { + .halt_reg = 0x3124, + .clkr = { + .enable_reg = 0x3124, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_csi1_clk", + .parent_names = (const char *[]){ + "csi1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_csi1phy_clk = { + .halt_reg = 0x3134, + .clkr = { + .enable_reg = 0x3134, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_csi1phy_clk", + .parent_names = (const char *[]){ + "csi1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_csi1pix_clk = { + .halt_reg = 0x3154, + .clkr = { + .enable_reg = 0x3154, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_csi1pix_clk", + .parent_names = (const char *[]){ + "csi1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_csi1rdi_clk = { + .halt_reg = 0x3144, + .clkr = { + .enable_reg = 0x3144, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_csi1rdi_clk", + .parent_names = (const char *[]){ + "csi1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_csi2_ahb_clk = { + .halt_reg = 0x3188, + .clkr = { + .enable_reg = 0x3188, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_csi2_ahb_clk", + .parent_names = (const char *[]){ + "mmss_ahb_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_csi2_clk = { + .halt_reg = 0x3184, + .clkr = { + .enable_reg = 0x3184, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_csi2_clk", + .parent_names = (const char *[]){ + "csi2_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_csi2phy_clk = { + .halt_reg = 0x3194, + .clkr = { + .enable_reg = 0x3194, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_csi2phy_clk", + .parent_names = (const char *[]){ + "csi2_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_csi2pix_clk = { + .halt_reg = 0x31b4, + .clkr = { + .enable_reg = 0x31b4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_csi2pix_clk", + .parent_names = (const char *[]){ + "csi2_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_csi2rdi_clk = { + .halt_reg = 0x31a4, + .clkr = { + .enable_reg = 0x31a4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_csi2rdi_clk", + .parent_names = (const char *[]){ + "csi2_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_csi3_ahb_clk = { + .halt_reg = 0x31e8, + .clkr = { + .enable_reg = 0x31e8, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_csi3_ahb_clk", + .parent_names = (const char *[]){ + "mmss_ahb_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_csi3_clk = { + .halt_reg = 0x31e4, + .clkr = { + .enable_reg = 0x31e4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_csi3_clk", + .parent_names = (const char *[]){ + "csi3_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_csi3phy_clk = { + .halt_reg = 0x31f4, + .clkr = { + .enable_reg = 0x31f4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_csi3phy_clk", + .parent_names = (const char *[]){ + "csi3_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_csi3pix_clk = { + .halt_reg = 0x3214, + .clkr = { + .enable_reg = 0x3214, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_csi3pix_clk", + .parent_names = (const char *[]){ + "csi3_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_csi3rdi_clk = { + .halt_reg = 0x3204, + .clkr = { + .enable_reg = 0x3204, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_csi3rdi_clk", + .parent_names = (const char *[]){ + "csi3_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_csi_vfe0_clk = { + .halt_reg = 0x3704, + .clkr = { + .enable_reg = 0x3704, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_csi_vfe0_clk", + .parent_names = (const char *[]){ + "vfe0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_csi_vfe1_clk = { + .halt_reg = 0x3714, + .clkr = { + .enable_reg = 0x3714, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_csi_vfe1_clk", + .parent_names = (const char *[]){ + "vfe1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_gp0_clk = { + .halt_reg = 0x3444, + .clkr = { + .enable_reg = 0x3444, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_gp0_clk", + .parent_names = (const char *[]){ + "camss_gp0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_gp1_clk = { + .halt_reg = 0x3474, + .clkr = { + .enable_reg = 0x3474, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_gp1_clk", + .parent_names = (const char *[]){ + "camss_gp1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_ispif_ahb_clk = { + .halt_reg = 0x3224, + .clkr = { + .enable_reg = 0x3224, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_ispif_ahb_clk", + .parent_names = (const char *[]){ + "mmss_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_jpeg_jpeg0_clk = { + .halt_reg = 0x35a8, + .clkr = { + .enable_reg = 0x35a8, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_jpeg_jpeg0_clk", + .parent_names = (const char *[]){ + "jpeg0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_jpeg_jpeg1_clk = { + .halt_reg = 0x35ac, + .clkr = { + .enable_reg = 0x35ac, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_jpeg_jpeg1_clk", + .parent_names = (const char *[]){ + "jpeg1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_jpeg_jpeg2_clk = { + .halt_reg = 0x35b0, + .clkr = { + .enable_reg = 0x35b0, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_jpeg_jpeg2_clk", + .parent_names = (const char *[]){ + "jpeg2_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_jpeg_jpeg_ahb_clk = { + .halt_reg = 0x35b4, + .clkr = { + .enable_reg = 0x35b4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_jpeg_jpeg_ahb_clk", + .parent_names = (const char *[]){ + "mmss_ahb_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_jpeg_jpeg_axi_clk = { + .halt_reg = 0x35b8, + .clkr = { + .enable_reg = 0x35b8, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_jpeg_jpeg_axi_clk", + .parent_names = (const char *[]){ + "mmss_axi_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_mclk0_clk = { + .halt_reg = 0x3384, + .clkr = { + .enable_reg = 0x3384, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_mclk0_clk", + .parent_names = (const char *[]){ + "mclk0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_mclk1_clk = { + .halt_reg = 0x33b4, + .clkr = { + .enable_reg = 0x33b4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_mclk1_clk", + .parent_names = (const char *[]){ + "mclk1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_mclk2_clk = { + .halt_reg = 0x33e4, + .clkr = { + .enable_reg = 0x33e4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_mclk2_clk", + .parent_names = (const char *[]){ + "mclk2_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_mclk3_clk = { + .halt_reg = 0x3414, + .clkr = { + .enable_reg = 0x3414, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_mclk3_clk", + .parent_names = (const char *[]){ + "mclk3_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_micro_ahb_clk = { + .halt_reg = 0x3494, + .clkr = { + .enable_reg = 0x3494, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_micro_ahb_clk", + .parent_names = (const char *[]){ + "mmss_ahb_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_phy0_csi0phytimer_clk = { + .halt_reg = 0x3024, + .clkr = { + .enable_reg = 0x3024, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_phy0_csi0phytimer_clk", + .parent_names = (const char *[]){ + "csi0phytimer_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_phy1_csi1phytimer_clk = { + .halt_reg = 0x3054, + .clkr = { + .enable_reg = 0x3054, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_phy1_csi1phytimer_clk", + .parent_names = (const char *[]){ + "csi1phytimer_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_phy2_csi2phytimer_clk = { + .halt_reg = 0x3084, + .clkr = { + .enable_reg = 0x3084, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_phy2_csi2phytimer_clk", + .parent_names = (const char *[]){ + "csi2phytimer_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_top_ahb_clk = { + .halt_reg = 0x3484, + .clkr = { + .enable_reg = 0x3484, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_top_ahb_clk", + .parent_names = (const char *[]){ + "mmss_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_vfe_cpp_ahb_clk = { + .halt_reg = 0x36b4, + .clkr = { + .enable_reg = 0x36b4, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_vfe_cpp_ahb_clk", + .parent_names = (const char *[]){ + "mmss_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_vfe_cpp_clk = { + .halt_reg = 0x36b0, + .clkr = { + .enable_reg = 0x36b0, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_vfe_cpp_clk", + .parent_names = (const char *[]){ + "cpp_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_vfe_vfe0_clk = { + .halt_reg = 0x36a8, + .clkr = { + .enable_reg = 0x36a8, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_vfe_vfe0_clk", + .parent_names = (const char *[]){ + "vfe0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_vfe_vfe1_clk = { + .halt_reg = 0x36ac, + .clkr = { + .enable_reg = 0x36ac, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_vfe_vfe1_clk", + .parent_names = (const char *[]){ + "vfe1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_vfe_vfe_ahb_clk = { + .halt_reg = 0x36b8, + .clkr = { + .enable_reg = 0x36b8, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_vfe_vfe_ahb_clk", + .parent_names = (const char *[]){ + "mmss_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch camss_vfe_vfe_axi_clk = { + .halt_reg = 0x36bc, + .clkr = { + .enable_reg = 0x36bc, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "camss_vfe_vfe_axi_clk", + .parent_names = (const char *[]){ + "mmss_axi_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mdss_ahb_clk = { + .halt_reg = 0x2308, + .clkr = { + .enable_reg = 0x2308, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mdss_ahb_clk", + .parent_names = (const char *[]){ + "mmss_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mdss_axi_clk = { + .halt_reg = 0x2310, + .clkr = { + .enable_reg = 0x2310, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mdss_axi_clk", + .parent_names = (const char *[]){ + "mmss_axi_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mdss_byte0_clk = { + .halt_reg = 0x233c, + .clkr = { + .enable_reg = 0x233c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mdss_byte0_clk", + .parent_names = (const char *[]){ + "byte0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mdss_byte1_clk = { + .halt_reg = 0x2340, + .clkr = { + .enable_reg = 0x2340, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mdss_byte1_clk", + .parent_names = (const char *[]){ + "byte1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mdss_edpaux_clk = { + .halt_reg = 0x2334, + .clkr = { + .enable_reg = 0x2334, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mdss_edpaux_clk", + .parent_names = (const char *[]){ + "edpaux_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mdss_edplink_clk = { + .halt_reg = 0x2330, + .clkr = { + .enable_reg = 0x2330, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mdss_edplink_clk", + .parent_names = (const char *[]){ + "edplink_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mdss_edppixel_clk = { + .halt_reg = 0x232c, + .clkr = { + .enable_reg = 0x232c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mdss_edppixel_clk", + .parent_names = (const char *[]){ + "edppixel_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mdss_esc0_clk = { + .halt_reg = 0x2344, + .clkr = { + .enable_reg = 0x2344, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mdss_esc0_clk", + .parent_names = (const char *[]){ + "esc0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mdss_esc1_clk = { + .halt_reg = 0x2348, + .clkr = { + .enable_reg = 0x2348, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mdss_esc1_clk", + .parent_names = (const char *[]){ + "esc1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mdss_extpclk_clk = { + .halt_reg = 0x2324, + .clkr = { + .enable_reg = 0x2324, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mdss_extpclk_clk", + .parent_names = (const char *[]){ + "extpclk_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mdss_hdmi_ahb_clk = { + .halt_reg = 0x230c, + .clkr = { + .enable_reg = 0x230c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mdss_hdmi_ahb_clk", + .parent_names = (const char *[]){ + "mmss_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mdss_hdmi_clk = { + .halt_reg = 0x2338, + .clkr = { + .enable_reg = 0x2338, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mdss_hdmi_clk", + .parent_names = (const char *[]){ + "hdmi_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mdss_mdp_clk = { + .halt_reg = 0x231c, + .clkr = { + .enable_reg = 0x231c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mdss_mdp_clk", + .parent_names = (const char *[]){ + "mdp_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mdss_mdp_lut_clk = { + .halt_reg = 0x2320, + .clkr = { + .enable_reg = 0x2320, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mdss_mdp_lut_clk", + .parent_names = (const char *[]){ + "mdp_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mdss_pclk0_clk = { + .halt_reg = 0x2314, + .clkr = { + .enable_reg = 0x2314, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mdss_pclk0_clk", + .parent_names = (const char *[]){ + "pclk0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mdss_pclk1_clk = { + .halt_reg = 0x2318, + .clkr = { + .enable_reg = 0x2318, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mdss_pclk1_clk", + .parent_names = (const char *[]){ + "pclk1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mdss_vsync_clk = { + .halt_reg = 0x2328, + .clkr = { + .enable_reg = 0x2328, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mdss_vsync_clk", + .parent_names = (const char *[]){ + "vsync_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mmss_rbcpr_ahb_clk = { + .halt_reg = 0x4088, + .clkr = { + .enable_reg = 0x4088, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mmss_rbcpr_ahb_clk", + .parent_names = (const char *[]){ + "mmss_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mmss_rbcpr_clk = { + .halt_reg = 0x4084, + .clkr = { + .enable_reg = 0x4084, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mmss_rbcpr_clk", + .parent_names = (const char *[]){ + "rbcpr_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mmss_spdm_ahb_clk = { + .halt_reg = 0x0230, + .clkr = { + .enable_reg = 0x0230, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mmss_spdm_ahb_clk", + .parent_names = (const char *[]){ + "mmss_spdm_ahb_div_clk", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mmss_spdm_axi_clk = { + .halt_reg = 0x0210, + .clkr = { + .enable_reg = 0x0210, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mmss_spdm_axi_clk", + .parent_names = (const char *[]){ + "mmss_spdm_axi_div_clk", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mmss_spdm_csi0_clk = { + .halt_reg = 0x023c, + .clkr = { + .enable_reg = 0x023c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mmss_spdm_csi0_clk", + .parent_names = (const char *[]){ + "mmss_spdm_csi0_div_clk", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mmss_spdm_gfx3d_clk = { + .halt_reg = 0x022c, + .clkr = { + .enable_reg = 0x022c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mmss_spdm_gfx3d_clk", + .parent_names = (const char *[]){ + "mmss_spdm_gfx3d_div_clk", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mmss_spdm_jpeg0_clk = { + .halt_reg = 0x0204, + .clkr = { + .enable_reg = 0x0204, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mmss_spdm_jpeg0_clk", + .parent_names = (const char *[]){ + "mmss_spdm_jpeg0_div_clk", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mmss_spdm_jpeg1_clk = { + .halt_reg = 0x0208, + .clkr = { + .enable_reg = 0x0208, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mmss_spdm_jpeg1_clk", + .parent_names = (const char *[]){ + "mmss_spdm_jpeg1_div_clk", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mmss_spdm_jpeg2_clk = { + .halt_reg = 0x0224, + .clkr = { + .enable_reg = 0x0224, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mmss_spdm_jpeg2_clk", + .parent_names = (const char *[]){ + "mmss_spdm_jpeg2_div_clk", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mmss_spdm_mdp_clk = { + .halt_reg = 0x020c, + .clkr = { + .enable_reg = 0x020c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mmss_spdm_mdp_clk", + .parent_names = (const char *[]){ + "mmss_spdm_mdp_div_clk", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mmss_spdm_pclk0_clk = { + .halt_reg = 0x0234, + .clkr = { + .enable_reg = 0x0234, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mmss_spdm_pclk0_clk", + .parent_names = (const char *[]){ + "mmss_spdm_pclk0_div_clk", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mmss_spdm_pclk1_clk = { + .halt_reg = 0x0228, + .clkr = { + .enable_reg = 0x0228, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mmss_spdm_pclk1_clk", + .parent_names = (const char *[]){ + "mmss_spdm_pclk1_div_clk", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mmss_spdm_vcodec0_clk = { + .halt_reg = 0x0214, + .clkr = { + .enable_reg = 0x0214, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mmss_spdm_vcodec0_clk", + .parent_names = (const char *[]){ + "mmss_spdm_vcodec0_div_clk", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mmss_spdm_vfe0_clk = { + .halt_reg = 0x0218, + .clkr = { + .enable_reg = 0x0218, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mmss_spdm_vfe0_clk", + .parent_names = (const char *[]){ + "mmss_spdm_vfe0_div_clk", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mmss_spdm_vfe1_clk = { + .halt_reg = 0x021c, + .clkr = { + .enable_reg = 0x021c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mmss_spdm_vfe1_clk", + .parent_names = (const char *[]){ + "mmss_spdm_vfe1_div_clk", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mmss_spdm_rm_axi_clk = { + .halt_reg = 0x0304, + .clkr = { + .enable_reg = 0x0304, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mmss_spdm_rm_axi_clk", + .parent_names = (const char *[]){ + "mmss_axi_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mmss_spdm_rm_ocmemnoc_clk = { + .halt_reg = 0x0308, + .clkr = { + .enable_reg = 0x0308, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mmss_spdm_rm_ocmemnoc_clk", + .parent_names = (const char *[]){ + "ocmemnoc_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + + +static struct clk_branch mmss_misc_ahb_clk = { + .halt_reg = 0x502c, + .clkr = { + .enable_reg = 0x502c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mmss_misc_ahb_clk", + .parent_names = (const char *[]){ + "mmss_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mmss_mmssnoc_ahb_clk = { + .halt_reg = 0x5024, + .clkr = { + .enable_reg = 0x5024, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mmss_mmssnoc_ahb_clk", + .parent_names = (const char *[]){ + "mmss_ahb_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, + }, +}; + +static struct clk_branch mmss_mmssnoc_bto_ahb_clk = { + .halt_reg = 0x5028, + .clkr = { + .enable_reg = 0x5028, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mmss_mmssnoc_bto_ahb_clk", + .parent_names = (const char *[]){ + "mmss_ahb_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, + }, +}; + +static struct clk_branch mmss_mmssnoc_axi_clk = { + .halt_reg = 0x506c, + .clkr = { + .enable_reg = 0x506c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mmss_mmssnoc_axi_clk", + .parent_names = (const char *[]){ + "mmss_axi_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch mmss_s0_axi_clk = { + .halt_reg = 0x5064, + .clkr = { + .enable_reg = 0x5064, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "mmss_s0_axi_clk", + .parent_names = (const char *[]){ + "mmss_axi_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, + }, +}; + +static struct clk_branch ocmemcx_ahb_clk = { + .halt_reg = 0x405c, + .clkr = { + .enable_reg = 0x405c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "ocmemcx_ahb_clk", + .parent_names = (const char *[]){ + "mmss_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch ocmemcx_ocmemnoc_clk = { + .halt_reg = 0x4058, + .clkr = { + .enable_reg = 0x4058, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "ocmemcx_ocmemnoc_clk", + .parent_names = (const char *[]){ + "ocmemnoc_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch oxili_ocmemgx_clk = { + .halt_reg = 0x402c, + .clkr = { + .enable_reg = 0x402c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "oxili_ocmemgx_clk", + .parent_names = (const char *[]){ + "gfx3d_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch oxili_gfx3d_clk = { + .halt_reg = 0x4028, + .clkr = { + .enable_reg = 0x4028, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "oxili_gfx3d_clk", + .parent_names = (const char *[]){ + "gfx3d_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch oxili_rbbmtimer_clk = { + .halt_reg = 0x40b0, + .clkr = { + .enable_reg = 0x40b0, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "oxili_rbbmtimer_clk", + .parent_names = (const char *[]){ + "rbbmtimer_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch oxilicx_ahb_clk = { + .halt_reg = 0x403c, + .clkr = { + .enable_reg = 0x403c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "oxilicx_ahb_clk", + .parent_names = (const char *[]){ + "mmss_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch venus0_ahb_clk = { + .halt_reg = 0x1030, + .clkr = { + .enable_reg = 0x1030, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "venus0_ahb_clk", + .parent_names = (const char *[]){ + "mmss_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch venus0_axi_clk = { + .halt_reg = 0x1034, + .clkr = { + .enable_reg = 0x1034, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "venus0_axi_clk", + .parent_names = (const char *[]){ + "mmss_axi_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch venus0_core0_vcodec_clk = { + .halt_reg = 0x1048, + .clkr = { + .enable_reg = 0x1048, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "venus0_core0_vcodec_clk", + .parent_names = (const char *[]){ + "vcodec0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch venus0_core1_vcodec_clk = { + .halt_reg = 0x104c, + .clkr = { + .enable_reg = 0x104c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "venus0_core1_vcodec_clk", + .parent_names = (const char *[]){ + "vcodec0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch venus0_ocmemnoc_clk = { + .halt_reg = 0x1038, + .clkr = { + .enable_reg = 0x1038, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "venus0_ocmemnoc_clk", + .parent_names = (const char *[]){ + "ocmemnoc_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch venus0_vcodec0_clk = { + .halt_reg = 0x1028, + .clkr = { + .enable_reg = 0x1028, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "venus0_vcodec0_clk", + .parent_names = (const char *[]){ + "vcodec0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch vpu_ahb_clk = { + .halt_reg = 0x1430, + .clkr = { + .enable_reg = 0x1430, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "vpu_ahb_clk", + .parent_names = (const char *[]){ + "mmss_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch vpu_axi_clk = { + .halt_reg = 0x143c, + .clkr = { + .enable_reg = 0x143c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "vpu_axi_clk", + .parent_names = (const char *[]){ + "mmss_axi_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch vpu_bus_clk = { + .halt_reg = 0x1440, + .clkr = { + .enable_reg = 0x1440, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "vpu_bus_clk", + .parent_names = (const char *[]){ + "vpu_bus_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch vpu_cxo_clk = { + .halt_reg = 0x1434, + .clkr = { + .enable_reg = 0x1434, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "vpu_cxo_clk", + .parent_names = (const char *[]){ "xo" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch vpu_maple_clk = { + .halt_reg = 0x142c, + .clkr = { + .enable_reg = 0x142c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "vpu_maple_clk", + .parent_names = (const char *[]){ + "maple_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch vpu_sleep_clk = { + .halt_reg = 0x1438, + .clkr = { + .enable_reg = 0x1438, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "vpu_sleep_clk", + .parent_names = (const char *[]){ + "sleep_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch vpu_vdp_clk = { + .halt_reg = 0x1428, + .clkr = { + .enable_reg = 0x1428, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "vpu_vdp_clk", + .parent_names = (const char *[]){ + "vdp_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static const struct pll_config mmpll1_config = { + .l = 60, + .m = 25, + .n = 32, + .vco_val = 0x0, + .vco_mask = 0x3 << 20, + .pre_div_val = 0x0, + .pre_div_mask = 0x7 << 12, + .post_div_val = 0x0, + .post_div_mask = 0x3 << 8, + .mn_ena_mask = BIT(24), + .main_output_mask = BIT(0), +}; + +static const struct pll_config mmpll3_config = { + .l = 48, + .m = 7, + .n = 16, + .vco_val = 0x0, + .vco_mask = 0x3 << 20, + .pre_div_val = 0x0, + .pre_div_mask = 0x7 << 12, + .post_div_val = 0x0, + .post_div_mask = 0x3 << 8, + .mn_ena_mask = BIT(24), + .main_output_mask = BIT(0), + .aux_output_mask = BIT(1), +}; + +static struct clk_regmap *mmcc_apq8084_clocks[] = { + [MMSS_AHB_CLK_SRC] = &mmss_ahb_clk_src.clkr, + [MMSS_AXI_CLK_SRC] = &mmss_axi_clk_src.clkr, + [MMPLL0] = &mmpll0.clkr, + [MMPLL0_VOTE] = &mmpll0_vote, + [MMPLL1] = &mmpll1.clkr, + [MMPLL1_VOTE] = &mmpll1_vote, + [MMPLL2] = &mmpll2.clkr, + [MMPLL3] = &mmpll3.clkr, + [MMPLL4] = &mmpll4.clkr, + [CSI0_CLK_SRC] = &csi0_clk_src.clkr, + [CSI1_CLK_SRC] = &csi1_clk_src.clkr, + [CSI2_CLK_SRC] = &csi2_clk_src.clkr, + [CSI3_CLK_SRC] = &csi3_clk_src.clkr, + [VCODEC0_CLK_SRC] = &vcodec0_clk_src.clkr, + [VFE0_CLK_SRC] = &vfe0_clk_src.clkr, + [VFE1_CLK_SRC] = &vfe1_clk_src.clkr, + [MDP_CLK_SRC] = &mdp_clk_src.clkr, + [PCLK0_CLK_SRC] = &pclk0_clk_src.clkr, + [PCLK1_CLK_SRC] = &pclk1_clk_src.clkr, + [OCMEMNOC_CLK_SRC] = &ocmemnoc_clk_src.clkr, + [GFX3D_CLK_SRC] = &gfx3d_clk_src.clkr, + [JPEG0_CLK_SRC] = &jpeg0_clk_src.clkr, + [JPEG1_CLK_SRC] = &jpeg1_clk_src.clkr, + [JPEG2_CLK_SRC] = &jpeg2_clk_src.clkr, + [EDPPIXEL_CLK_SRC] = &edppixel_clk_src.clkr, + [EXTPCLK_CLK_SRC] = &extpclk_clk_src.clkr, + [VP_CLK_SRC] = &vp_clk_src.clkr, + [CCI_CLK_SRC] = &cci_clk_src.clkr, + [CAMSS_GP0_CLK_SRC] = &camss_gp0_clk_src.clkr, + [CAMSS_GP1_CLK_SRC] = &camss_gp1_clk_src.clkr, + [MCLK0_CLK_SRC] = &mclk0_clk_src.clkr, + [MCLK1_CLK_SRC] = &mclk1_clk_src.clkr, + [MCLK2_CLK_SRC] = &mclk2_clk_src.clkr, + [MCLK3_CLK_SRC] = &mclk3_clk_src.clkr, + [CSI0PHYTIMER_CLK_SRC] = &csi0phytimer_clk_src.clkr, + [CSI1PHYTIMER_CLK_SRC] = &csi1phytimer_clk_src.clkr, + [CSI2PHYTIMER_CLK_SRC] = &csi2phytimer_clk_src.clkr, + [CPP_CLK_SRC] = &cpp_clk_src.clkr, + [BYTE0_CLK_SRC] = &byte0_clk_src.clkr, + [BYTE1_CLK_SRC] = &byte1_clk_src.clkr, + [EDPAUX_CLK_SRC] = &edpaux_clk_src.clkr, + [EDPLINK_CLK_SRC] = &edplink_clk_src.clkr, + [ESC0_CLK_SRC] = &esc0_clk_src.clkr, + [ESC1_CLK_SRC] = &esc1_clk_src.clkr, + [HDMI_CLK_SRC] = &hdmi_clk_src.clkr, + [VSYNC_CLK_SRC] = &vsync_clk_src.clkr, + [RBCPR_CLK_SRC] = &rbcpr_clk_src.clkr, + [RBBMTIMER_CLK_SRC] = &rbbmtimer_clk_src.clkr, + [MAPLE_CLK_SRC] = &maple_clk_src.clkr, + [VDP_CLK_SRC] = &vdp_clk_src.clkr, + [VPU_BUS_CLK_SRC] = &vpu_bus_clk_src.clkr, + [MMSS_CXO_CLK] = &mmss_cxo_clk.clkr, + [MMSS_SLEEPCLK_CLK] = &mmss_sleepclk_clk.clkr, + [AVSYNC_AHB_CLK] = &avsync_ahb_clk.clkr, + [AVSYNC_EDPPIXEL_CLK] = &avsync_edppixel_clk.clkr, + [AVSYNC_EXTPCLK_CLK] = &avsync_extpclk_clk.clkr, + [AVSYNC_PCLK0_CLK] = &avsync_pclk0_clk.clkr, + [AVSYNC_PCLK1_CLK] = &avsync_pclk1_clk.clkr, + [AVSYNC_VP_CLK] = &avsync_vp_clk.clkr, + [CAMSS_AHB_CLK] = &camss_ahb_clk.clkr, + [CAMSS_CCI_CCI_AHB_CLK] = &camss_cci_cci_ahb_clk.clkr, + [CAMSS_CCI_CCI_CLK] = &camss_cci_cci_clk.clkr, + [CAMSS_CSI0_AHB_CLK] = &camss_csi0_ahb_clk.clkr, + [CAMSS_CSI0_CLK] = &camss_csi0_clk.clkr, + [CAMSS_CSI0PHY_CLK] = &camss_csi0phy_clk.clkr, + [CAMSS_CSI0PIX_CLK] = &camss_csi0pix_clk.clkr, + [CAMSS_CSI0RDI_CLK] = &camss_csi0rdi_clk.clkr, + [CAMSS_CSI1_AHB_CLK] = &camss_csi1_ahb_clk.clkr, + [CAMSS_CSI1_CLK] = &camss_csi1_clk.clkr, + [CAMSS_CSI1PHY_CLK] = &camss_csi1phy_clk.clkr, + [CAMSS_CSI1PIX_CLK] = &camss_csi1pix_clk.clkr, + [CAMSS_CSI1RDI_CLK] = &camss_csi1rdi_clk.clkr, + [CAMSS_CSI2_AHB_CLK] = &camss_csi2_ahb_clk.clkr, + [CAMSS_CSI2_CLK] = &camss_csi2_clk.clkr, + [CAMSS_CSI2PHY_CLK] = &camss_csi2phy_clk.clkr, + [CAMSS_CSI2PIX_CLK] = &camss_csi2pix_clk.clkr, + [CAMSS_CSI2RDI_CLK] = &camss_csi2rdi_clk.clkr, + [CAMSS_CSI3_AHB_CLK] = &camss_csi3_ahb_clk.clkr, + [CAMSS_CSI3_CLK] = &camss_csi3_clk.clkr, + [CAMSS_CSI3PHY_CLK] = &camss_csi3phy_clk.clkr, + [CAMSS_CSI3PIX_CLK] = &camss_csi3pix_clk.clkr, + [CAMSS_CSI3RDI_CLK] = &camss_csi3rdi_clk.clkr, + [CAMSS_CSI_VFE0_CLK] = &camss_csi_vfe0_clk.clkr, + [CAMSS_CSI_VFE1_CLK] = &camss_csi_vfe1_clk.clkr, + [CAMSS_GP0_CLK] = &camss_gp0_clk.clkr, + [CAMSS_GP1_CLK] = &camss_gp1_clk.clkr, + [CAMSS_ISPIF_AHB_CLK] = &camss_ispif_ahb_clk.clkr, + [CAMSS_JPEG_JPEG0_CLK] = &camss_jpeg_jpeg0_clk.clkr, + [CAMSS_JPEG_JPEG1_CLK] = &camss_jpeg_jpeg1_clk.clkr, + [CAMSS_JPEG_JPEG2_CLK] = &camss_jpeg_jpeg2_clk.clkr, + [CAMSS_JPEG_JPEG_AHB_CLK] = &camss_jpeg_jpeg_ahb_clk.clkr, + [CAMSS_JPEG_JPEG_AXI_CLK] = &camss_jpeg_jpeg_axi_clk.clkr, + [CAMSS_MCLK0_CLK] = &camss_mclk0_clk.clkr, + [CAMSS_MCLK1_CLK] = &camss_mclk1_clk.clkr, + [CAMSS_MCLK2_CLK] = &camss_mclk2_clk.clkr, + [CAMSS_MCLK3_CLK] = &camss_mclk3_clk.clkr, + [CAMSS_MICRO_AHB_CLK] = &camss_micro_ahb_clk.clkr, + [CAMSS_PHY0_CSI0PHYTIMER_CLK] = &camss_phy0_csi0phytimer_clk.clkr, + [CAMSS_PHY1_CSI1PHYTIMER_CLK] = &camss_phy1_csi1phytimer_clk.clkr, + [CAMSS_PHY2_CSI2PHYTIMER_CLK] = &camss_phy2_csi2phytimer_clk.clkr, + [CAMSS_TOP_AHB_CLK] = &camss_top_ahb_clk.clkr, + [CAMSS_VFE_CPP_AHB_CLK] = &camss_vfe_cpp_ahb_clk.clkr, + [CAMSS_VFE_CPP_CLK] = &camss_vfe_cpp_clk.clkr, + [CAMSS_VFE_VFE0_CLK] = &camss_vfe_vfe0_clk.clkr, + [CAMSS_VFE_VFE1_CLK] = &camss_vfe_vfe1_clk.clkr, + [CAMSS_VFE_VFE_AHB_CLK] = &camss_vfe_vfe_ahb_clk.clkr, + [CAMSS_VFE_VFE_AXI_CLK] = &camss_vfe_vfe_axi_clk.clkr, + [MDSS_AHB_CLK] = &mdss_ahb_clk.clkr, + [MDSS_AXI_CLK] = &mdss_axi_clk.clkr, + [MDSS_BYTE0_CLK] = &mdss_byte0_clk.clkr, + [MDSS_BYTE1_CLK] = &mdss_byte1_clk.clkr, + [MDSS_EDPAUX_CLK] = &mdss_edpaux_clk.clkr, + [MDSS_EDPLINK_CLK] = &mdss_edplink_clk.clkr, + [MDSS_EDPPIXEL_CLK] = &mdss_edppixel_clk.clkr, + [MDSS_ESC0_CLK] = &mdss_esc0_clk.clkr, + [MDSS_ESC1_CLK] = &mdss_esc1_clk.clkr, + [MDSS_EXTPCLK_CLK] = &mdss_extpclk_clk.clkr, + [MDSS_HDMI_AHB_CLK] = &mdss_hdmi_ahb_clk.clkr, + [MDSS_HDMI_CLK] = &mdss_hdmi_clk.clkr, + [MDSS_MDP_CLK] = &mdss_mdp_clk.clkr, + [MDSS_MDP_LUT_CLK] = &mdss_mdp_lut_clk.clkr, + [MDSS_PCLK0_CLK] = &mdss_pclk0_clk.clkr, + [MDSS_PCLK1_CLK] = &mdss_pclk1_clk.clkr, + [MDSS_VSYNC_CLK] = &mdss_vsync_clk.clkr, + [MMSS_RBCPR_AHB_CLK] = &mmss_rbcpr_ahb_clk.clkr, + [MMSS_RBCPR_CLK] = &mmss_rbcpr_clk.clkr, + [MMSS_SPDM_AHB_CLK] = &mmss_spdm_ahb_clk.clkr, + [MMSS_SPDM_AXI_CLK] = &mmss_spdm_axi_clk.clkr, + [MMSS_SPDM_CSI0_CLK] = &mmss_spdm_csi0_clk.clkr, + [MMSS_SPDM_GFX3D_CLK] = &mmss_spdm_gfx3d_clk.clkr, + [MMSS_SPDM_JPEG0_CLK] = &mmss_spdm_jpeg0_clk.clkr, + [MMSS_SPDM_JPEG1_CLK] = &mmss_spdm_jpeg1_clk.clkr, + [MMSS_SPDM_JPEG2_CLK] = &mmss_spdm_jpeg2_clk.clkr, + [MMSS_SPDM_MDP_CLK] = &mmss_spdm_mdp_clk.clkr, + [MMSS_SPDM_PCLK0_CLK] = &mmss_spdm_pclk0_clk.clkr, + [MMSS_SPDM_PCLK1_CLK] = &mmss_spdm_pclk1_clk.clkr, + [MMSS_SPDM_VCODEC0_CLK] = &mmss_spdm_vcodec0_clk.clkr, + [MMSS_SPDM_VFE0_CLK] = &mmss_spdm_vfe0_clk.clkr, + [MMSS_SPDM_VFE1_CLK] = &mmss_spdm_vfe1_clk.clkr, + [MMSS_SPDM_RM_AXI_CLK] = &mmss_spdm_rm_axi_clk.clkr, + [MMSS_SPDM_RM_OCMEMNOC_CLK] = &mmss_spdm_rm_ocmemnoc_clk.clkr, + [MMSS_MISC_AHB_CLK] = &mmss_misc_ahb_clk.clkr, + [MMSS_MMSSNOC_AHB_CLK] = &mmss_mmssnoc_ahb_clk.clkr, + [MMSS_MMSSNOC_BTO_AHB_CLK] = &mmss_mmssnoc_bto_ahb_clk.clkr, + [MMSS_MMSSNOC_AXI_CLK] = &mmss_mmssnoc_axi_clk.clkr, + [MMSS_S0_AXI_CLK] = &mmss_s0_axi_clk.clkr, + [OCMEMCX_AHB_CLK] = &ocmemcx_ahb_clk.clkr, + [OCMEMCX_OCMEMNOC_CLK] = &ocmemcx_ocmemnoc_clk.clkr, + [OXILI_OCMEMGX_CLK] = &oxili_ocmemgx_clk.clkr, + [OXILI_GFX3D_CLK] = &oxili_gfx3d_clk.clkr, + [OXILI_RBBMTIMER_CLK] = &oxili_rbbmtimer_clk.clkr, + [OXILICX_AHB_CLK] = &oxilicx_ahb_clk.clkr, + [VENUS0_AHB_CLK] = &venus0_ahb_clk.clkr, + [VENUS0_AXI_CLK] = &venus0_axi_clk.clkr, + [VENUS0_CORE0_VCODEC_CLK] = &venus0_core0_vcodec_clk.clkr, + [VENUS0_CORE1_VCODEC_CLK] = &venus0_core1_vcodec_clk.clkr, + [VENUS0_OCMEMNOC_CLK] = &venus0_ocmemnoc_clk.clkr, + [VENUS0_VCODEC0_CLK] = &venus0_vcodec0_clk.clkr, + [VPU_AHB_CLK] = &vpu_ahb_clk.clkr, + [VPU_AXI_CLK] = &vpu_axi_clk.clkr, + [VPU_BUS_CLK] = &vpu_bus_clk.clkr, + [VPU_CXO_CLK] = &vpu_cxo_clk.clkr, + [VPU_MAPLE_CLK] = &vpu_maple_clk.clkr, + [VPU_SLEEP_CLK] = &vpu_sleep_clk.clkr, + [VPU_VDP_CLK] = &vpu_vdp_clk.clkr, +}; + +static const struct qcom_reset_map mmcc_apq8084_resets[] = { + [MMSS_SPDM_RESET] = { 0x0200 }, + [MMSS_SPDM_RM_RESET] = { 0x0300 }, + [VENUS0_RESET] = { 0x1020 }, + [VPU_RESET] = { 0x1400 }, + [MDSS_RESET] = { 0x2300 }, + [AVSYNC_RESET] = { 0x2400 }, + [CAMSS_PHY0_RESET] = { 0x3020 }, + [CAMSS_PHY1_RESET] = { 0x3050 }, + [CAMSS_PHY2_RESET] = { 0x3080 }, + [CAMSS_CSI0_RESET] = { 0x30b0 }, + [CAMSS_CSI0PHY_RESET] = { 0x30c0 }, + [CAMSS_CSI0RDI_RESET] = { 0x30d0 }, + [CAMSS_CSI0PIX_RESET] = { 0x30e0 }, + [CAMSS_CSI1_RESET] = { 0x3120 }, + [CAMSS_CSI1PHY_RESET] = { 0x3130 }, + [CAMSS_CSI1RDI_RESET] = { 0x3140 }, + [CAMSS_CSI1PIX_RESET] = { 0x3150 }, + [CAMSS_CSI2_RESET] = { 0x3180 }, + [CAMSS_CSI2PHY_RESET] = { 0x3190 }, + [CAMSS_CSI2RDI_RESET] = { 0x31a0 }, + [CAMSS_CSI2PIX_RESET] = { 0x31b0 }, + [CAMSS_CSI3_RESET] = { 0x31e0 }, + [CAMSS_CSI3PHY_RESET] = { 0x31f0 }, + [CAMSS_CSI3RDI_RESET] = { 0x3200 }, + [CAMSS_CSI3PIX_RESET] = { 0x3210 }, + [CAMSS_ISPIF_RESET] = { 0x3220 }, + [CAMSS_CCI_RESET] = { 0x3340 }, + [CAMSS_MCLK0_RESET] = { 0x3380 }, + [CAMSS_MCLK1_RESET] = { 0x33b0 }, + [CAMSS_MCLK2_RESET] = { 0x33e0 }, + [CAMSS_MCLK3_RESET] = { 0x3410 }, + [CAMSS_GP0_RESET] = { 0x3440 }, + [CAMSS_GP1_RESET] = { 0x3470 }, + [CAMSS_TOP_RESET] = { 0x3480 }, + [CAMSS_AHB_RESET] = { 0x3488 }, + [CAMSS_MICRO_RESET] = { 0x3490 }, + [CAMSS_JPEG_RESET] = { 0x35a0 }, + [CAMSS_VFE_RESET] = { 0x36a0 }, + [CAMSS_CSI_VFE0_RESET] = { 0x3700 }, + [CAMSS_CSI_VFE1_RESET] = { 0x3710 }, + [OXILI_RESET] = { 0x4020 }, + [OXILICX_RESET] = { 0x4030 }, + [OCMEMCX_RESET] = { 0x4050 }, + [MMSS_RBCRP_RESET] = { 0x4080 }, + [MMSSNOCAHB_RESET] = { 0x5020 }, + [MMSSNOCAXI_RESET] = { 0x5060 }, +}; + +static const struct regmap_config mmcc_apq8084_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x5104, + .fast_io = true, +}; + +static const struct qcom_cc_desc mmcc_apq8084_desc = { + .config = &mmcc_apq8084_regmap_config, + .clks = mmcc_apq8084_clocks, + .num_clks = ARRAY_SIZE(mmcc_apq8084_clocks), + .resets = mmcc_apq8084_resets, + .num_resets = ARRAY_SIZE(mmcc_apq8084_resets), +}; + +static const struct of_device_id mmcc_apq8084_match_table[] = { + { .compatible = "qcom,mmcc-apq8084" }, + { } +}; +MODULE_DEVICE_TABLE(of, mmcc_apq8084_match_table); + +static int mmcc_apq8084_probe(struct platform_device *pdev) +{ + int ret; + struct regmap *regmap; + + ret = qcom_cc_probe(pdev, &mmcc_apq8084_desc); + if (ret) + return ret; + + regmap = dev_get_regmap(&pdev->dev, NULL); + clk_pll_configure_sr_hpm_lp(&mmpll1, regmap, &mmpll1_config, true); + clk_pll_configure_sr_hpm_lp(&mmpll3, regmap, &mmpll3_config, false); + + return 0; +} + +static int mmcc_apq8084_remove(struct platform_device *pdev) +{ + qcom_cc_remove(pdev); + return 0; +} + +static struct platform_driver mmcc_apq8084_driver = { + .probe = mmcc_apq8084_probe, + .remove = mmcc_apq8084_remove, + .driver = { + .name = "mmcc-apq8084", + .owner = THIS_MODULE, + .of_match_table = mmcc_apq8084_match_table, + }, +}; +module_platform_driver(mmcc_apq8084_driver); + +MODULE_DESCRIPTION("QCOM MMCC APQ8084 Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:mmcc-apq8084"); diff --git a/include/dt-bindings/clock/qcom,mmcc-apq8084.h b/include/dt-bindings/clock/qcom,mmcc-apq8084.h new file mode 100644 index 000000000000..a929f86d0ddd --- /dev/null +++ b/include/dt-bindings/clock/qcom,mmcc-apq8084.h @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * 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 _DT_BINDINGS_CLK_APQ_MMCC_8084_H +#define _DT_BINDINGS_CLK_APQ_MMCC_8084_H + +#define MMSS_AHB_CLK_SRC 0 +#define MMSS_AXI_CLK_SRC 1 +#define MMPLL0 2 +#define MMPLL0_VOTE 3 +#define MMPLL1 4 +#define MMPLL1_VOTE 5 +#define MMPLL2 6 +#define MMPLL3 7 +#define MMPLL4 8 +#define CSI0_CLK_SRC 9 +#define CSI1_CLK_SRC 10 +#define CSI2_CLK_SRC 11 +#define CSI3_CLK_SRC 12 +#define VCODEC0_CLK_SRC 13 +#define VFE0_CLK_SRC 14 +#define VFE1_CLK_SRC 15 +#define MDP_CLK_SRC 16 +#define PCLK0_CLK_SRC 17 +#define PCLK1_CLK_SRC 18 +#define OCMEMNOC_CLK_SRC 19 +#define GFX3D_CLK_SRC 20 +#define JPEG0_CLK_SRC 21 +#define JPEG1_CLK_SRC 22 +#define JPEG2_CLK_SRC 23 +#define EDPPIXEL_CLK_SRC 24 +#define EXTPCLK_CLK_SRC 25 +#define VP_CLK_SRC 26 +#define CCI_CLK_SRC 27 +#define CAMSS_GP0_CLK_SRC 28 +#define CAMSS_GP1_CLK_SRC 29 +#define MCLK0_CLK_SRC 30 +#define MCLK1_CLK_SRC 31 +#define MCLK2_CLK_SRC 32 +#define MCLK3_CLK_SRC 33 +#define CSI0PHYTIMER_CLK_SRC 34 +#define CSI1PHYTIMER_CLK_SRC 35 +#define CSI2PHYTIMER_CLK_SRC 36 +#define CPP_CLK_SRC 37 +#define BYTE0_CLK_SRC 38 +#define BYTE1_CLK_SRC 39 +#define EDPAUX_CLK_SRC 40 +#define EDPLINK_CLK_SRC 41 +#define ESC0_CLK_SRC 42 +#define ESC1_CLK_SRC 43 +#define HDMI_CLK_SRC 44 +#define VSYNC_CLK_SRC 45 +#define RBCPR_CLK_SRC 46 +#define RBBMTIMER_CLK_SRC 47 +#define MAPLE_CLK_SRC 48 +#define VDP_CLK_SRC 49 +#define VPU_BUS_CLK_SRC 50 +#define MMSS_CXO_CLK 51 +#define MMSS_SLEEPCLK_CLK 52 +#define AVSYNC_AHB_CLK 53 +#define AVSYNC_EDPPIXEL_CLK 54 +#define AVSYNC_EXTPCLK_CLK 55 +#define AVSYNC_PCLK0_CLK 56 +#define AVSYNC_PCLK1_CLK 57 +#define AVSYNC_VP_CLK 58 +#define CAMSS_AHB_CLK 59 +#define CAMSS_CCI_CCI_AHB_CLK 60 +#define CAMSS_CCI_CCI_CLK 61 +#define CAMSS_CSI0_AHB_CLK 62 +#define CAMSS_CSI0_CLK 63 +#define CAMSS_CSI0PHY_CLK 64 +#define CAMSS_CSI0PIX_CLK 65 +#define CAMSS_CSI0RDI_CLK 66 +#define CAMSS_CSI1_AHB_CLK 67 +#define CAMSS_CSI1_CLK 68 +#define CAMSS_CSI1PHY_CLK 69 +#define CAMSS_CSI1PIX_CLK 70 +#define CAMSS_CSI1RDI_CLK 71 +#define CAMSS_CSI2_AHB_CLK 72 +#define CAMSS_CSI2_CLK 73 +#define CAMSS_CSI2PHY_CLK 74 +#define CAMSS_CSI2PIX_CLK 75 +#define CAMSS_CSI2RDI_CLK 76 +#define CAMSS_CSI3_AHB_CLK 77 +#define CAMSS_CSI3_CLK 78 +#define CAMSS_CSI3PHY_CLK 79 +#define CAMSS_CSI3PIX_CLK 80 +#define CAMSS_CSI3RDI_CLK 81 +#define CAMSS_CSI_VFE0_CLK 82 +#define CAMSS_CSI_VFE1_CLK 83 +#define CAMSS_GP0_CLK 84 +#define CAMSS_GP1_CLK 85 +#define CAMSS_ISPIF_AHB_CLK 86 +#define CAMSS_JPEG_JPEG0_CLK 87 +#define CAMSS_JPEG_JPEG1_CLK 88 +#define CAMSS_JPEG_JPEG2_CLK 89 +#define CAMSS_JPEG_JPEG_AHB_CLK 90 +#define CAMSS_JPEG_JPEG_AXI_CLK 91 +#define CAMSS_MCLK0_CLK 92 +#define CAMSS_MCLK1_CLK 93 +#define CAMSS_MCLK2_CLK 94 +#define CAMSS_MCLK3_CLK 95 +#define CAMSS_MICRO_AHB_CLK 96 +#define CAMSS_PHY0_CSI0PHYTIMER_CLK 97 +#define CAMSS_PHY1_CSI1PHYTIMER_CLK 98 +#define CAMSS_PHY2_CSI2PHYTIMER_CLK 99 +#define CAMSS_TOP_AHB_CLK 100 +#define CAMSS_VFE_CPP_AHB_CLK 101 +#define CAMSS_VFE_CPP_CLK 102 +#define CAMSS_VFE_VFE0_CLK 103 +#define CAMSS_VFE_VFE1_CLK 104 +#define CAMSS_VFE_VFE_AHB_CLK 105 +#define CAMSS_VFE_VFE_AXI_CLK 106 +#define MDSS_AHB_CLK 107 +#define MDSS_AXI_CLK 108 +#define MDSS_BYTE0_CLK 109 +#define MDSS_BYTE1_CLK 110 +#define MDSS_EDPAUX_CLK 111 +#define MDSS_EDPLINK_CLK 112 +#define MDSS_EDPPIXEL_CLK 113 +#define MDSS_ESC0_CLK 114 +#define MDSS_ESC1_CLK 115 +#define MDSS_EXTPCLK_CLK 116 +#define MDSS_HDMI_AHB_CLK 117 +#define MDSS_HDMI_CLK 118 +#define MDSS_MDP_CLK 119 +#define MDSS_MDP_LUT_CLK 120 +#define MDSS_PCLK0_CLK 121 +#define MDSS_PCLK1_CLK 122 +#define MDSS_VSYNC_CLK 123 +#define MMSS_RBCPR_AHB_CLK 124 +#define MMSS_RBCPR_CLK 125 +#define MMSS_SPDM_AHB_CLK 126 +#define MMSS_SPDM_AXI_CLK 127 +#define MMSS_SPDM_CSI0_CLK 128 +#define MMSS_SPDM_GFX3D_CLK 129 +#define MMSS_SPDM_JPEG0_CLK 130 +#define MMSS_SPDM_JPEG1_CLK 131 +#define MMSS_SPDM_JPEG2_CLK 132 +#define MMSS_SPDM_MDP_CLK 133 +#define MMSS_SPDM_PCLK0_CLK 134 +#define MMSS_SPDM_PCLK1_CLK 135 +#define MMSS_SPDM_VCODEC0_CLK 136 +#define MMSS_SPDM_VFE0_CLK 137 +#define MMSS_SPDM_VFE1_CLK 138 +#define MMSS_SPDM_RM_AXI_CLK 139 +#define MMSS_SPDM_RM_OCMEMNOC_CLK 140 +#define MMSS_MISC_AHB_CLK 141 +#define MMSS_MMSSNOC_AHB_CLK 142 +#define MMSS_MMSSNOC_BTO_AHB_CLK 143 +#define MMSS_MMSSNOC_AXI_CLK 144 +#define MMSS_S0_AXI_CLK 145 +#define OCMEMCX_AHB_CLK 146 +#define OCMEMCX_OCMEMNOC_CLK 147 +#define OXILI_OCMEMGX_CLK 148 +#define OXILI_GFX3D_CLK 149 +#define OXILI_RBBMTIMER_CLK 150 +#define OXILICX_AHB_CLK 151 +#define VENUS0_AHB_CLK 152 +#define VENUS0_AXI_CLK 153 +#define VENUS0_CORE0_VCODEC_CLK 154 +#define VENUS0_CORE1_VCODEC_CLK 155 +#define VENUS0_OCMEMNOC_CLK 156 +#define VENUS0_VCODEC0_CLK 157 +#define VPU_AHB_CLK 158 +#define VPU_AXI_CLK 159 +#define VPU_BUS_CLK 160 +#define VPU_CXO_CLK 161 +#define VPU_MAPLE_CLK 162 +#define VPU_SLEEP_CLK 163 +#define VPU_VDP_CLK 164 + +#endif diff --git a/include/dt-bindings/reset/qcom,mmcc-apq8084.h b/include/dt-bindings/reset/qcom,mmcc-apq8084.h new file mode 100644 index 000000000000..c1671396531d --- /dev/null +++ b/include/dt-bindings/reset/qcom,mmcc-apq8084.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * 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 _DT_BINDINGS_RESET_APQ_MMCC_8084_H +#define _DT_BINDINGS_RESET_APQ_MMCC_8084_H + +#define MMSS_SPDM_RESET 0 +#define MMSS_SPDM_RM_RESET 1 +#define VENUS0_RESET 2 +#define VPU_RESET 3 +#define MDSS_RESET 4 +#define AVSYNC_RESET 5 +#define CAMSS_PHY0_RESET 6 +#define CAMSS_PHY1_RESET 7 +#define CAMSS_PHY2_RESET 8 +#define CAMSS_CSI0_RESET 9 +#define CAMSS_CSI0PHY_RESET 10 +#define CAMSS_CSI0RDI_RESET 11 +#define CAMSS_CSI0PIX_RESET 12 +#define CAMSS_CSI1_RESET 13 +#define CAMSS_CSI1PHY_RESET 14 +#define CAMSS_CSI1RDI_RESET 15 +#define CAMSS_CSI1PIX_RESET 16 +#define CAMSS_CSI2_RESET 17 +#define CAMSS_CSI2PHY_RESET 18 +#define CAMSS_CSI2RDI_RESET 19 +#define CAMSS_CSI2PIX_RESET 20 +#define CAMSS_CSI3_RESET 21 +#define CAMSS_CSI3PHY_RESET 22 +#define CAMSS_CSI3RDI_RESET 23 +#define CAMSS_CSI3PIX_RESET 24 +#define CAMSS_ISPIF_RESET 25 +#define CAMSS_CCI_RESET 26 +#define CAMSS_MCLK0_RESET 27 +#define CAMSS_MCLK1_RESET 28 +#define CAMSS_MCLK2_RESET 29 +#define CAMSS_MCLK3_RESET 30 +#define CAMSS_GP0_RESET 31 +#define CAMSS_GP1_RESET 32 +#define CAMSS_TOP_RESET 33 +#define CAMSS_AHB_RESET 34 +#define CAMSS_MICRO_RESET 35 +#define CAMSS_JPEG_RESET 36 +#define CAMSS_VFE_RESET 37 +#define CAMSS_CSI_VFE0_RESET 38 +#define CAMSS_CSI_VFE1_RESET 39 +#define OXILI_RESET 40 +#define OXILICX_RESET 41 +#define OCMEMCX_RESET 42 +#define MMSS_RBCRP_RESET 43 +#define MMSSNOCAHB_RESET 44 +#define MMSSNOCAXI_RESET 45 + +#endif -- cgit v1.2.3 From 24d8fba44af32163334c1f162e65ba93eb2993fd Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Tue, 17 Jun 2014 14:46:51 -0500 Subject: clk: qcom: Add support for IPQ8064's global clock controller (GCC) Add a driver for the global clock controller found on IPQ8064 based platforms. This should allow most non-multimedia device drivers to probe and control their clocks. This is currently missing clocks for USB HSIC and networking devices. Signed-off-by: Kumar Gala Signed-off-by: Andy Gross Signed-off-by: Stephen Boyd --- .../devicetree/bindings/clock/qcom,gcc.txt | 1 + drivers/clk/qcom/Kconfig | 8 + drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/gcc-ipq806x.c | 2424 ++++++++++++++++++++ include/dt-bindings/clock/qcom,gcc-ipq806x.h | 293 +++ include/dt-bindings/reset/qcom,gcc-ipq806x.h | 132 ++ 6 files changed, 2859 insertions(+) create mode 100644 drivers/clk/qcom/gcc-ipq806x.c create mode 100644 include/dt-bindings/clock/qcom,gcc-ipq806x.h create mode 100644 include/dt-bindings/reset/qcom,gcc-ipq806x.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc.txt b/Documentation/devicetree/bindings/clock/qcom,gcc.txt index 4f3504294baa..aba3d254e037 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc.txt +++ b/Documentation/devicetree/bindings/clock/qcom,gcc.txt @@ -6,6 +6,7 @@ Required properties : "qcom,gcc-apq8064" "qcom,gcc-apq8084" + "qcom,gcc-ipq8064" "qcom,gcc-msm8660" "qcom,gcc-msm8960" "qcom,gcc-msm8974" diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index e5f95161d747..1107351ed346 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -21,6 +21,14 @@ config APQ_MMCC_8084 Say Y if you want to support multimedia devices such as display, graphics, video encode/decode, camera, etc. +config IPQ_GCC_806X + tristate "IPQ806x Global Clock Controller" + depends on COMMON_CLK_QCOM + help + Support for the global clock controller on ipq806x devices. + Say Y if you want to use peripheral devices such as UART, SPI, + i2c, USB, SD/eMMC, etc. + config MSM_GCC_8660 tristate "MSM8660 Global Clock Controller" depends on COMMON_CLK_QCOM diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index f9366261f836..783cfb24faa4 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -10,6 +10,7 @@ clk-qcom-y += reset.o obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o obj-$(CONFIG_APQ_MMCC_8084) += mmcc-apq8084.o +obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o obj-$(CONFIG_MSM_GCC_8660) += gcc-msm8660.o obj-$(CONFIG_MSM_GCC_8960) += gcc-msm8960.o obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o diff --git a/drivers/clk/qcom/gcc-ipq806x.c b/drivers/clk/qcom/gcc-ipq806x.c new file mode 100644 index 000000000000..4032e510d9aa --- /dev/null +++ b/drivers/clk/qcom/gcc-ipq806x.c @@ -0,0 +1,2424 @@ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "common.h" +#include "clk-regmap.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-branch.h" +#include "reset.h" + +static struct clk_pll pll3 = { + .l_reg = 0x3164, + .m_reg = 0x3168, + .n_reg = 0x316c, + .config_reg = 0x3174, + .mode_reg = 0x3160, + .status_reg = 0x3178, + .status_bit = 16, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pll3", + .parent_names = (const char *[]){ "pxo" }, + .num_parents = 1, + .ops = &clk_pll_ops, + }, +}; + +static struct clk_pll pll8 = { + .l_reg = 0x3144, + .m_reg = 0x3148, + .n_reg = 0x314c, + .config_reg = 0x3154, + .mode_reg = 0x3140, + .status_reg = 0x3158, + .status_bit = 16, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pll8", + .parent_names = (const char *[]){ "pxo" }, + .num_parents = 1, + .ops = &clk_pll_ops, + }, +}; + +static struct clk_regmap pll8_vote = { + .enable_reg = 0x34c0, + .enable_mask = BIT(8), + .hw.init = &(struct clk_init_data){ + .name = "pll8_vote", + .parent_names = (const char *[]){ "pll8" }, + .num_parents = 1, + .ops = &clk_pll_vote_ops, + }, +}; + +static struct clk_pll pll14 = { + .l_reg = 0x31c4, + .m_reg = 0x31c8, + .n_reg = 0x31cc, + .config_reg = 0x31d4, + .mode_reg = 0x31c0, + .status_reg = 0x31d8, + .status_bit = 16, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pll14", + .parent_names = (const char *[]){ "pxo" }, + .num_parents = 1, + .ops = &clk_pll_ops, + }, +}; + +static struct clk_regmap pll14_vote = { + .enable_reg = 0x34c0, + .enable_mask = BIT(14), + .hw.init = &(struct clk_init_data){ + .name = "pll14_vote", + .parent_names = (const char *[]){ "pll14" }, + .num_parents = 1, + .ops = &clk_pll_vote_ops, + }, +}; + +#define P_PXO 0 +#define P_PLL8 1 +#define P_PLL3 1 +#define P_PLL0 2 +#define P_CXO 2 + +static const u8 gcc_pxo_pll8_map[] = { + [P_PXO] = 0, + [P_PLL8] = 3, +}; + +static const char *gcc_pxo_pll8[] = { + "pxo", + "pll8_vote", +}; + +static const u8 gcc_pxo_pll8_cxo_map[] = { + [P_PXO] = 0, + [P_PLL8] = 3, + [P_CXO] = 5, +}; + +static const char *gcc_pxo_pll8_cxo[] = { + "pxo", + "pll8_vote", + "cxo", +}; + +static const u8 gcc_pxo_pll3_map[] = { + [P_PXO] = 0, + [P_PLL3] = 1, +}; + +static const u8 gcc_pxo_pll3_sata_map[] = { + [P_PXO] = 0, + [P_PLL3] = 6, +}; + +static const char *gcc_pxo_pll3[] = { + "pxo", + "pll3", +}; + +static const u8 gcc_pxo_pll8_pll0[] = { + [P_PXO] = 0, + [P_PLL8] = 3, + [P_PLL0] = 2, +}; + +static const char *gcc_pxo_pll8_pll0_map[] = { + "pxo", + "pll8_vote", + "pll0", +}; + +static struct freq_tbl clk_tbl_gsbi_uart[] = { + { 1843200, P_PLL8, 2, 6, 625 }, + { 3686400, P_PLL8, 2, 12, 625 }, + { 7372800, P_PLL8, 2, 24, 625 }, + { 14745600, P_PLL8, 2, 48, 625 }, + { 16000000, P_PLL8, 4, 1, 6 }, + { 24000000, P_PLL8, 4, 1, 4 }, + { 32000000, P_PLL8, 4, 1, 3 }, + { 40000000, P_PLL8, 1, 5, 48 }, + { 46400000, P_PLL8, 1, 29, 240 }, + { 48000000, P_PLL8, 4, 1, 2 }, + { 51200000, P_PLL8, 1, 2, 15 }, + { 56000000, P_PLL8, 1, 7, 48 }, + { 58982400, P_PLL8, 1, 96, 625 }, + { 64000000, P_PLL8, 2, 1, 3 }, + { } +}; + +static struct clk_rcg gsbi1_uart_src = { + .ns_reg = 0x29d4, + .md_reg = 0x29d0, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 16, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll8_map, + }, + .freq_tbl = clk_tbl_gsbi_uart, + .clkr = { + .enable_reg = 0x29d4, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gsbi1_uart_src", + .parent_names = gcc_pxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_PARENT_GATE, + }, + }, +}; + +static struct clk_branch gsbi1_uart_clk = { + .halt_reg = 0x2fcc, + .halt_bit = 12, + .clkr = { + .enable_reg = 0x29d4, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gsbi1_uart_clk", + .parent_names = (const char *[]){ + "gsbi1_uart_src", + }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg gsbi2_uart_src = { + .ns_reg = 0x29f4, + .md_reg = 0x29f0, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 16, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll8_map, + }, + .freq_tbl = clk_tbl_gsbi_uart, + .clkr = { + .enable_reg = 0x29f4, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gsbi2_uart_src", + .parent_names = gcc_pxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_PARENT_GATE, + }, + }, +}; + +static struct clk_branch gsbi2_uart_clk = { + .halt_reg = 0x2fcc, + .halt_bit = 8, + .clkr = { + .enable_reg = 0x29f4, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gsbi2_uart_clk", + .parent_names = (const char *[]){ + "gsbi2_uart_src", + }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg gsbi4_uart_src = { + .ns_reg = 0x2a34, + .md_reg = 0x2a30, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 16, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll8_map, + }, + .freq_tbl = clk_tbl_gsbi_uart, + .clkr = { + .enable_reg = 0x2a34, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gsbi4_uart_src", + .parent_names = gcc_pxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_PARENT_GATE, + }, + }, +}; + +static struct clk_branch gsbi4_uart_clk = { + .halt_reg = 0x2fd0, + .halt_bit = 26, + .clkr = { + .enable_reg = 0x2a34, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gsbi4_uart_clk", + .parent_names = (const char *[]){ + "gsbi4_uart_src", + }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg gsbi5_uart_src = { + .ns_reg = 0x2a54, + .md_reg = 0x2a50, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 16, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll8_map, + }, + .freq_tbl = clk_tbl_gsbi_uart, + .clkr = { + .enable_reg = 0x2a54, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gsbi5_uart_src", + .parent_names = gcc_pxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_PARENT_GATE, + }, + }, +}; + +static struct clk_branch gsbi5_uart_clk = { + .halt_reg = 0x2fd0, + .halt_bit = 22, + .clkr = { + .enable_reg = 0x2a54, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gsbi5_uart_clk", + .parent_names = (const char *[]){ + "gsbi5_uart_src", + }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg gsbi6_uart_src = { + .ns_reg = 0x2a74, + .md_reg = 0x2a70, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 16, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll8_map, + }, + .freq_tbl = clk_tbl_gsbi_uart, + .clkr = { + .enable_reg = 0x2a74, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gsbi6_uart_src", + .parent_names = gcc_pxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_PARENT_GATE, + }, + }, +}; + +static struct clk_branch gsbi6_uart_clk = { + .halt_reg = 0x2fd0, + .halt_bit = 18, + .clkr = { + .enable_reg = 0x2a74, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gsbi6_uart_clk", + .parent_names = (const char *[]){ + "gsbi6_uart_src", + }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg gsbi7_uart_src = { + .ns_reg = 0x2a94, + .md_reg = 0x2a90, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 16, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll8_map, + }, + .freq_tbl = clk_tbl_gsbi_uart, + .clkr = { + .enable_reg = 0x2a94, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gsbi7_uart_src", + .parent_names = gcc_pxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_PARENT_GATE, + }, + }, +}; + +static struct clk_branch gsbi7_uart_clk = { + .halt_reg = 0x2fd0, + .halt_bit = 14, + .clkr = { + .enable_reg = 0x2a94, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gsbi7_uart_clk", + .parent_names = (const char *[]){ + "gsbi7_uart_src", + }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct freq_tbl clk_tbl_gsbi_qup[] = { + { 1100000, P_PXO, 1, 2, 49 }, + { 5400000, P_PXO, 1, 1, 5 }, + { 10800000, P_PXO, 1, 2, 5 }, + { 15060000, P_PLL8, 1, 2, 51 }, + { 24000000, P_PLL8, 4, 1, 4 }, + { 25600000, P_PLL8, 1, 1, 15 }, + { 27000000, P_PXO, 1, 0, 0 }, + { 48000000, P_PLL8, 4, 1, 2 }, + { 51200000, P_PLL8, 1, 2, 15 }, + { } +}; + +static struct clk_rcg gsbi1_qup_src = { + .ns_reg = 0x29cc, + .md_reg = 0x29c8, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll8_map, + }, + .freq_tbl = clk_tbl_gsbi_qup, + .clkr = { + .enable_reg = 0x29cc, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gsbi1_qup_src", + .parent_names = gcc_pxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_PARENT_GATE, + }, + }, +}; + +static struct clk_branch gsbi1_qup_clk = { + .halt_reg = 0x2fcc, + .halt_bit = 11, + .clkr = { + .enable_reg = 0x29cc, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gsbi1_qup_clk", + .parent_names = (const char *[]){ "gsbi1_qup_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg gsbi2_qup_src = { + .ns_reg = 0x29ec, + .md_reg = 0x29e8, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll8_map, + }, + .freq_tbl = clk_tbl_gsbi_qup, + .clkr = { + .enable_reg = 0x29ec, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gsbi2_qup_src", + .parent_names = gcc_pxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_PARENT_GATE, + }, + }, +}; + +static struct clk_branch gsbi2_qup_clk = { + .halt_reg = 0x2fcc, + .halt_bit = 6, + .clkr = { + .enable_reg = 0x29ec, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gsbi2_qup_clk", + .parent_names = (const char *[]){ "gsbi2_qup_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg gsbi4_qup_src = { + .ns_reg = 0x2a2c, + .md_reg = 0x2a28, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll8_map, + }, + .freq_tbl = clk_tbl_gsbi_qup, + .clkr = { + .enable_reg = 0x2a2c, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gsbi4_qup_src", + .parent_names = gcc_pxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_PARENT_GATE, + }, + }, +}; + +static struct clk_branch gsbi4_qup_clk = { + .halt_reg = 0x2fd0, + .halt_bit = 24, + .clkr = { + .enable_reg = 0x2a2c, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gsbi4_qup_clk", + .parent_names = (const char *[]){ "gsbi4_qup_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg gsbi5_qup_src = { + .ns_reg = 0x2a4c, + .md_reg = 0x2a48, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll8_map, + }, + .freq_tbl = clk_tbl_gsbi_qup, + .clkr = { + .enable_reg = 0x2a4c, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gsbi5_qup_src", + .parent_names = gcc_pxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_PARENT_GATE, + }, + }, +}; + +static struct clk_branch gsbi5_qup_clk = { + .halt_reg = 0x2fd0, + .halt_bit = 20, + .clkr = { + .enable_reg = 0x2a4c, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gsbi5_qup_clk", + .parent_names = (const char *[]){ "gsbi5_qup_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg gsbi6_qup_src = { + .ns_reg = 0x2a6c, + .md_reg = 0x2a68, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll8_map, + }, + .freq_tbl = clk_tbl_gsbi_qup, + .clkr = { + .enable_reg = 0x2a6c, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gsbi6_qup_src", + .parent_names = gcc_pxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_PARENT_GATE, + }, + }, +}; + +static struct clk_branch gsbi6_qup_clk = { + .halt_reg = 0x2fd0, + .halt_bit = 16, + .clkr = { + .enable_reg = 0x2a6c, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gsbi6_qup_clk", + .parent_names = (const char *[]){ "gsbi6_qup_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg gsbi7_qup_src = { + .ns_reg = 0x2a8c, + .md_reg = 0x2a88, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll8_map, + }, + .freq_tbl = clk_tbl_gsbi_qup, + .clkr = { + .enable_reg = 0x2a8c, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gsbi7_qup_src", + .parent_names = gcc_pxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_PARENT_GATE, + }, + }, +}; + +static struct clk_branch gsbi7_qup_clk = { + .halt_reg = 0x2fd0, + .halt_bit = 12, + .clkr = { + .enable_reg = 0x2a8c, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gsbi7_qup_clk", + .parent_names = (const char *[]){ "gsbi7_qup_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch gsbi1_h_clk = { + .hwcg_reg = 0x29c0, + .hwcg_bit = 6, + .halt_reg = 0x2fcc, + .halt_bit = 13, + .clkr = { + .enable_reg = 0x29c0, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "gsbi1_h_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch gsbi2_h_clk = { + .hwcg_reg = 0x29e0, + .hwcg_bit = 6, + .halt_reg = 0x2fcc, + .halt_bit = 9, + .clkr = { + .enable_reg = 0x29e0, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "gsbi2_h_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch gsbi4_h_clk = { + .hwcg_reg = 0x2a20, + .hwcg_bit = 6, + .halt_reg = 0x2fd0, + .halt_bit = 27, + .clkr = { + .enable_reg = 0x2a20, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "gsbi4_h_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch gsbi5_h_clk = { + .hwcg_reg = 0x2a40, + .hwcg_bit = 6, + .halt_reg = 0x2fd0, + .halt_bit = 23, + .clkr = { + .enable_reg = 0x2a40, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "gsbi5_h_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch gsbi6_h_clk = { + .hwcg_reg = 0x2a60, + .hwcg_bit = 6, + .halt_reg = 0x2fd0, + .halt_bit = 19, + .clkr = { + .enable_reg = 0x2a60, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "gsbi6_h_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch gsbi7_h_clk = { + .hwcg_reg = 0x2a80, + .hwcg_bit = 6, + .halt_reg = 0x2fd0, + .halt_bit = 15, + .clkr = { + .enable_reg = 0x2a80, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "gsbi7_h_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static const struct freq_tbl clk_tbl_gp[] = { + { 12500000, P_PXO, 2, 0, 0 }, + { 25000000, P_PXO, 1, 0, 0 }, + { 64000000, P_PLL8, 2, 1, 3 }, + { 76800000, P_PLL8, 1, 1, 5 }, + { 96000000, P_PLL8, 4, 0, 0 }, + { 128000000, P_PLL8, 3, 0, 0 }, + { 192000000, P_PLL8, 2, 0, 0 }, + { } +}; + +static struct clk_rcg gp0_src = { + .ns_reg = 0x2d24, + .md_reg = 0x2d00, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll8_cxo_map, + }, + .freq_tbl = clk_tbl_gp, + .clkr = { + .enable_reg = 0x2d24, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gp0_src", + .parent_names = gcc_pxo_pll8_cxo, + .num_parents = 3, + .ops = &clk_rcg_ops, + .flags = CLK_SET_PARENT_GATE, + }, + } +}; + +static struct clk_branch gp0_clk = { + .halt_reg = 0x2fd8, + .halt_bit = 7, + .clkr = { + .enable_reg = 0x2d24, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gp0_clk", + .parent_names = (const char *[]){ "gp0_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg gp1_src = { + .ns_reg = 0x2d44, + .md_reg = 0x2d40, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll8_cxo_map, + }, + .freq_tbl = clk_tbl_gp, + .clkr = { + .enable_reg = 0x2d44, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gp1_src", + .parent_names = gcc_pxo_pll8_cxo, + .num_parents = 3, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + } +}; + +static struct clk_branch gp1_clk = { + .halt_reg = 0x2fd8, + .halt_bit = 6, + .clkr = { + .enable_reg = 0x2d44, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gp1_clk", + .parent_names = (const char *[]){ "gp1_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg gp2_src = { + .ns_reg = 0x2d64, + .md_reg = 0x2d60, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll8_cxo_map, + }, + .freq_tbl = clk_tbl_gp, + .clkr = { + .enable_reg = 0x2d64, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gp2_src", + .parent_names = gcc_pxo_pll8_cxo, + .num_parents = 3, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + } +}; + +static struct clk_branch gp2_clk = { + .halt_reg = 0x2fd8, + .halt_bit = 5, + .clkr = { + .enable_reg = 0x2d64, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gp2_clk", + .parent_names = (const char *[]){ "gp2_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch pmem_clk = { + .hwcg_reg = 0x25a0, + .hwcg_bit = 6, + .halt_reg = 0x2fc8, + .halt_bit = 20, + .clkr = { + .enable_reg = 0x25a0, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "pmem_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_rcg prng_src = { + .ns_reg = 0x2e80, + .p = { + .pre_div_shift = 3, + .pre_div_width = 4, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll8_map, + }, + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "prng_src", + .parent_names = gcc_pxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + }, + }, +}; + +static struct clk_branch prng_clk = { + .halt_reg = 0x2fd8, + .halt_check = BRANCH_HALT_VOTED, + .halt_bit = 10, + .clkr = { + .enable_reg = 0x3080, + .enable_mask = BIT(10), + .hw.init = &(struct clk_init_data){ + .name = "prng_clk", + .parent_names = (const char *[]){ "prng_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + }, + }, +}; + +static const struct freq_tbl clk_tbl_sdc[] = { + { 144000, P_PXO, 5, 18,625 }, + { 400000, P_PLL8, 4, 1, 240 }, + { 16000000, P_PLL8, 4, 1, 6 }, + { 17070000, P_PLL8, 1, 2, 45 }, + { 20210000, P_PLL8, 1, 1, 19 }, + { 24000000, P_PLL8, 4, 1, 4 }, + { 48000000, P_PLL8, 4, 1, 2 }, + { 64000000, P_PLL8, 3, 1, 2 }, + { 96000000, P_PLL8, 4, 0, 0 }, + { 192000000, P_PLL8, 2, 0, 0 }, + { } +}; + +static struct clk_rcg sdc1_src = { + .ns_reg = 0x282c, + .md_reg = 0x2828, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll8_map, + }, + .freq_tbl = clk_tbl_sdc, + .clkr = { + .enable_reg = 0x282c, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "sdc1_src", + .parent_names = gcc_pxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + } +}; + +static struct clk_branch sdc1_clk = { + .halt_reg = 0x2fc8, + .halt_bit = 6, + .clkr = { + .enable_reg = 0x282c, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "sdc1_clk", + .parent_names = (const char *[]){ "sdc1_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_rcg sdc3_src = { + .ns_reg = 0x286c, + .md_reg = 0x2868, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll8_map, + }, + .freq_tbl = clk_tbl_sdc, + .clkr = { + .enable_reg = 0x286c, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "sdc3_src", + .parent_names = gcc_pxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + } +}; + +static struct clk_branch sdc3_clk = { + .halt_reg = 0x2fc8, + .halt_bit = 4, + .clkr = { + .enable_reg = 0x286c, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "sdc3_clk", + .parent_names = (const char *[]){ "sdc3_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch sdc1_h_clk = { + .hwcg_reg = 0x2820, + .hwcg_bit = 6, + .halt_reg = 0x2fc8, + .halt_bit = 11, + .clkr = { + .enable_reg = 0x2820, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "sdc1_h_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch sdc3_h_clk = { + .hwcg_reg = 0x2860, + .hwcg_bit = 6, + .halt_reg = 0x2fc8, + .halt_bit = 9, + .clkr = { + .enable_reg = 0x2860, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "sdc3_h_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static const struct freq_tbl clk_tbl_tsif_ref[] = { + { 105000, P_PXO, 1, 1, 256 }, + { } +}; + +static struct clk_rcg tsif_ref_src = { + .ns_reg = 0x2710, + .md_reg = 0x270c, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 16, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll8_map, + }, + .freq_tbl = clk_tbl_tsif_ref, + .clkr = { + .enable_reg = 0x2710, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "tsif_ref_src", + .parent_names = gcc_pxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + } +}; + +static struct clk_branch tsif_ref_clk = { + .halt_reg = 0x2fd4, + .halt_bit = 5, + .clkr = { + .enable_reg = 0x2710, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "tsif_ref_clk", + .parent_names = (const char *[]){ "tsif_ref_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch tsif_h_clk = { + .hwcg_reg = 0x2700, + .hwcg_bit = 6, + .halt_reg = 0x2fd4, + .halt_bit = 7, + .clkr = { + .enable_reg = 0x2700, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "tsif_h_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch dma_bam_h_clk = { + .hwcg_reg = 0x25c0, + .hwcg_bit = 6, + .halt_reg = 0x2fc8, + .halt_bit = 12, + .clkr = { + .enable_reg = 0x25c0, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "dma_bam_h_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch adm0_clk = { + .halt_reg = 0x2fdc, + .halt_check = BRANCH_HALT_VOTED, + .halt_bit = 12, + .clkr = { + .enable_reg = 0x3080, + .enable_mask = BIT(2), + .hw.init = &(struct clk_init_data){ + .name = "adm0_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch adm0_pbus_clk = { + .hwcg_reg = 0x2208, + .hwcg_bit = 6, + .halt_reg = 0x2fdc, + .halt_check = BRANCH_HALT_VOTED, + .halt_bit = 11, + .clkr = { + .enable_reg = 0x3080, + .enable_mask = BIT(3), + .hw.init = &(struct clk_init_data){ + .name = "adm0_pbus_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch pmic_arb0_h_clk = { + .halt_reg = 0x2fd8, + .halt_check = BRANCH_HALT_VOTED, + .halt_bit = 22, + .clkr = { + .enable_reg = 0x3080, + .enable_mask = BIT(8), + .hw.init = &(struct clk_init_data){ + .name = "pmic_arb0_h_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch pmic_arb1_h_clk = { + .halt_reg = 0x2fd8, + .halt_check = BRANCH_HALT_VOTED, + .halt_bit = 21, + .clkr = { + .enable_reg = 0x3080, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "pmic_arb1_h_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch pmic_ssbi2_clk = { + .halt_reg = 0x2fd8, + .halt_check = BRANCH_HALT_VOTED, + .halt_bit = 23, + .clkr = { + .enable_reg = 0x3080, + .enable_mask = BIT(7), + .hw.init = &(struct clk_init_data){ + .name = "pmic_ssbi2_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch rpm_msg_ram_h_clk = { + .hwcg_reg = 0x27e0, + .hwcg_bit = 6, + .halt_reg = 0x2fd8, + .halt_check = BRANCH_HALT_VOTED, + .halt_bit = 12, + .clkr = { + .enable_reg = 0x3080, + .enable_mask = BIT(6), + .hw.init = &(struct clk_init_data){ + .name = "rpm_msg_ram_h_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static const struct freq_tbl clk_tbl_pcie_ref[] = { + { 100000000, P_PLL3, 12, 0, 0 }, + { } +}; + +static struct clk_rcg pcie_ref_src = { + .ns_reg = 0x3860, + .p = { + .pre_div_shift = 3, + .pre_div_width = 4, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll3_map, + }, + .freq_tbl = clk_tbl_pcie_ref, + .clkr = { + .enable_reg = 0x3860, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "pcie_ref_src", + .parent_names = gcc_pxo_pll3, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + }, +}; + +static struct clk_branch pcie_ref_src_clk = { + .halt_reg = 0x2fdc, + .halt_bit = 30, + .clkr = { + .enable_reg = 0x3860, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "pcie_ref_src_clk", + .parent_names = (const char *[]){ "pcie_ref_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch pcie_a_clk = { + .halt_reg = 0x2fc0, + .halt_bit = 13, + .clkr = { + .enable_reg = 0x22c0, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "pcie_a_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch pcie_aux_clk = { + .halt_reg = 0x2fdc, + .halt_bit = 31, + .clkr = { + .enable_reg = 0x22c8, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "pcie_aux_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch pcie_h_clk = { + .halt_reg = 0x2fd4, + .halt_bit = 8, + .clkr = { + .enable_reg = 0x22cc, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "pcie_h_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch pcie_phy_clk = { + .halt_reg = 0x2fdc, + .halt_bit = 29, + .clkr = { + .enable_reg = 0x22d0, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "pcie_phy_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_rcg pcie1_ref_src = { + .ns_reg = 0x3aa0, + .p = { + .pre_div_shift = 3, + .pre_div_width = 4, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll3_map, + }, + .freq_tbl = clk_tbl_pcie_ref, + .clkr = { + .enable_reg = 0x3aa0, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "pcie1_ref_src", + .parent_names = gcc_pxo_pll3, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + }, +}; + +static struct clk_branch pcie1_ref_src_clk = { + .halt_reg = 0x2fdc, + .halt_bit = 27, + .clkr = { + .enable_reg = 0x3aa0, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "pcie1_ref_src_clk", + .parent_names = (const char *[]){ "pcie1_ref_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch pcie1_a_clk = { + .halt_reg = 0x2fc0, + .halt_bit = 10, + .clkr = { + .enable_reg = 0x3a80, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "pcie1_a_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch pcie1_aux_clk = { + .halt_reg = 0x2fdc, + .halt_bit = 28, + .clkr = { + .enable_reg = 0x3a88, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "pcie1_aux_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch pcie1_h_clk = { + .halt_reg = 0x2fd4, + .halt_bit = 9, + .clkr = { + .enable_reg = 0x3a8c, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "pcie1_h_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch pcie1_phy_clk = { + .halt_reg = 0x2fdc, + .halt_bit = 26, + .clkr = { + .enable_reg = 0x3a90, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "pcie1_phy_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_rcg pcie2_ref_src = { + .ns_reg = 0x3ae0, + .p = { + .pre_div_shift = 3, + .pre_div_width = 4, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll3_map, + }, + .freq_tbl = clk_tbl_pcie_ref, + .clkr = { + .enable_reg = 0x3ae0, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "pcie2_ref_src", + .parent_names = gcc_pxo_pll3, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + }, +}; + +static struct clk_branch pcie2_ref_src_clk = { + .halt_reg = 0x2fdc, + .halt_bit = 24, + .clkr = { + .enable_reg = 0x3ae0, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "pcie2_ref_src_clk", + .parent_names = (const char *[]){ "pcie2_ref_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch pcie2_a_clk = { + .halt_reg = 0x2fc0, + .halt_bit = 9, + .clkr = { + .enable_reg = 0x3ac0, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "pcie2_a_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch pcie2_aux_clk = { + .halt_reg = 0x2fdc, + .halt_bit = 25, + .clkr = { + .enable_reg = 0x3ac8, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "pcie2_aux_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch pcie2_h_clk = { + .halt_reg = 0x2fd4, + .halt_bit = 10, + .clkr = { + .enable_reg = 0x3acc, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "pcie2_h_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch pcie2_phy_clk = { + .halt_reg = 0x2fdc, + .halt_bit = 23, + .clkr = { + .enable_reg = 0x3ad0, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "pcie2_phy_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static const struct freq_tbl clk_tbl_sata_ref[] = { + { 100000000, P_PLL3, 12, 0, 0 }, + { } +}; + +static struct clk_rcg sata_ref_src = { + .ns_reg = 0x2c08, + .p = { + .pre_div_shift = 3, + .pre_div_width = 4, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll3_sata_map, + }, + .freq_tbl = clk_tbl_sata_ref, + .clkr = { + .enable_reg = 0x2c08, + .enable_mask = BIT(7), + .hw.init = &(struct clk_init_data){ + .name = "sata_ref_src", + .parent_names = gcc_pxo_pll3, + .num_parents = 2, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + }, +}; + +static struct clk_branch sata_rxoob_clk = { + .halt_reg = 0x2fdc, + .halt_bit = 20, + .clkr = { + .enable_reg = 0x2c0c, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "sata_rxoob_clk", + .parent_names = (const char *[]){ "sata_ref_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch sata_pmalive_clk = { + .halt_reg = 0x2fdc, + .halt_bit = 19, + .clkr = { + .enable_reg = 0x2c10, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "sata_pmalive_clk", + .parent_names = (const char *[]){ "sata_ref_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch sata_phy_ref_clk = { + .halt_reg = 0x2fdc, + .halt_bit = 18, + .clkr = { + .enable_reg = 0x2c14, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "sata_phy_ref_clk", + .parent_names = (const char *[]){ "pxo" }, + .num_parents = 1, + .ops = &clk_branch_ops, + }, + }, +}; + +static struct clk_branch sata_a_clk = { + .halt_reg = 0x2fc0, + .halt_bit = 12, + .clkr = { + .enable_reg = 0x2c20, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "sata_a_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch sata_h_clk = { + .halt_reg = 0x2fdc, + .halt_bit = 21, + .clkr = { + .enable_reg = 0x2c00, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "sata_h_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch sfab_sata_s_h_clk = { + .halt_reg = 0x2fc4, + .halt_bit = 14, + .clkr = { + .enable_reg = 0x2480, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "sfab_sata_s_h_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_branch sata_phy_cfg_clk = { + .halt_reg = 0x2fcc, + .halt_bit = 14, + .clkr = { + .enable_reg = 0x2c40, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "sata_phy_cfg_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static const struct freq_tbl clk_tbl_usb30_master[] = { + { 125000000, P_PLL0, 1, 5, 32 }, + { } +}; + +static struct clk_rcg usb30_master_clk_src = { + .ns_reg = 0x3b2c, + .md_reg = 0x3b28, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll8_pll0, + }, + .freq_tbl = clk_tbl_usb30_master, + .clkr = { + .enable_reg = 0x3b2c, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "usb30_master_ref_src", + .parent_names = gcc_pxo_pll8_pll0_map, + .num_parents = 3, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + }, +}; + +static struct clk_branch usb30_0_branch_clk = { + .halt_reg = 0x2fc4, + .halt_bit = 22, + .clkr = { + .enable_reg = 0x3b24, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "usb30_0_branch_clk", + .parent_names = (const char *[]){ "usb30_master_ref_src", }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch usb30_1_branch_clk = { + .halt_reg = 0x2fc4, + .halt_bit = 17, + .clkr = { + .enable_reg = 0x3b34, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "usb30_1_branch_clk", + .parent_names = (const char *[]){ "usb30_master_ref_src", }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static const struct freq_tbl clk_tbl_usb30_utmi[] = { + { 60000000, P_PLL8, 1, 5, 32 }, + { } +}; + +static struct clk_rcg usb30_utmi_clk = { + .ns_reg = 0x3b44, + .md_reg = 0x3b40, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll8_pll0, + }, + .freq_tbl = clk_tbl_usb30_utmi, + .clkr = { + .enable_reg = 0x3b44, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "usb30_utmi_clk", + .parent_names = gcc_pxo_pll8_pll0_map, + .num_parents = 3, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + }, +}; + +static struct clk_branch usb30_0_utmi_clk_ctl = { + .halt_reg = 0x2fc4, + .halt_bit = 21, + .clkr = { + .enable_reg = 0x3b48, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "usb30_0_utmi_clk_ctl", + .parent_names = (const char *[]){ "usb30_utmi_clk", }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch usb30_1_utmi_clk_ctl = { + .halt_reg = 0x2fc4, + .halt_bit = 15, + .clkr = { + .enable_reg = 0x3b4c, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "usb30_1_utmi_clk_ctl", + .parent_names = (const char *[]){ "usb30_utmi_clk", }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static const struct freq_tbl clk_tbl_usb[] = { + { 60000000, P_PLL8, 1, 5, 32 }, + { } +}; + +static struct clk_rcg usb_hs1_xcvr_clk_src = { + .ns_reg = 0x290C, + .md_reg = 0x2908, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll8_pll0, + }, + .freq_tbl = clk_tbl_usb, + .clkr = { + .enable_reg = 0x2968, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "usb_hs1_xcvr_src", + .parent_names = gcc_pxo_pll8_pll0_map, + .num_parents = 3, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + }, +}; + +static struct clk_branch usb_hs1_xcvr_clk = { + .halt_reg = 0x2fcc, + .halt_bit = 17, + .clkr = { + .enable_reg = 0x290c, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "usb_hs1_xcvr_clk", + .parent_names = (const char *[]){ "usb_hs1_xcvr_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch usb_hs1_h_clk = { + .hwcg_reg = 0x2900, + .hwcg_bit = 6, + .halt_reg = 0x2fc8, + .halt_bit = 1, + .clkr = { + .enable_reg = 0x2900, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "usb_hs1_h_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_rcg usb_fs1_xcvr_clk_src = { + .ns_reg = 0x2968, + .md_reg = 0x2964, + .mn = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 7, + .mnctr_mode_shift = 5, + .n_val_shift = 16, + .m_val_shift = 16, + .width = 8, + }, + .p = { + .pre_div_shift = 3, + .pre_div_width = 2, + }, + .s = { + .src_sel_shift = 0, + .parent_map = gcc_pxo_pll8_pll0, + }, + .freq_tbl = clk_tbl_usb, + .clkr = { + .enable_reg = 0x2968, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "usb_fs1_xcvr_src", + .parent_names = gcc_pxo_pll8_pll0_map, + .num_parents = 3, + .ops = &clk_rcg_ops, + .flags = CLK_SET_RATE_GATE, + }, + }, +}; + +static struct clk_branch usb_fs1_xcvr_clk = { + .halt_reg = 0x2fcc, + .halt_bit = 17, + .clkr = { + .enable_reg = 0x2968, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "usb_fs1_xcvr_clk", + .parent_names = (const char *[]){ "usb_fs1_xcvr_src", }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch usb_fs1_sys_clk = { + .halt_reg = 0x2fcc, + .halt_bit = 18, + .clkr = { + .enable_reg = 0x296c, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "usb_fs1_sys_clk", + .parent_names = (const char *[]){ "usb_fs1_xcvr_src", }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch usb_fs1_h_clk = { + .halt_reg = 0x2fcc, + .halt_bit = 19, + .clkr = { + .enable_reg = 0x2960, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "usb_fs1_h_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + +static struct clk_regmap *gcc_ipq806x_clks[] = { + [PLL3] = &pll3.clkr, + [PLL8] = &pll8.clkr, + [PLL8_VOTE] = &pll8_vote, + [PLL14] = &pll14.clkr, + [PLL14_VOTE] = &pll14_vote, + [GSBI1_UART_SRC] = &gsbi1_uart_src.clkr, + [GSBI1_UART_CLK] = &gsbi1_uart_clk.clkr, + [GSBI2_UART_SRC] = &gsbi2_uart_src.clkr, + [GSBI2_UART_CLK] = &gsbi2_uart_clk.clkr, + [GSBI4_UART_SRC] = &gsbi4_uart_src.clkr, + [GSBI4_UART_CLK] = &gsbi4_uart_clk.clkr, + [GSBI5_UART_SRC] = &gsbi5_uart_src.clkr, + [GSBI5_UART_CLK] = &gsbi5_uart_clk.clkr, + [GSBI6_UART_SRC] = &gsbi6_uart_src.clkr, + [GSBI6_UART_CLK] = &gsbi6_uart_clk.clkr, + [GSBI7_UART_SRC] = &gsbi7_uart_src.clkr, + [GSBI7_UART_CLK] = &gsbi7_uart_clk.clkr, + [GSBI1_QUP_SRC] = &gsbi1_qup_src.clkr, + [GSBI1_QUP_CLK] = &gsbi1_qup_clk.clkr, + [GSBI2_QUP_SRC] = &gsbi2_qup_src.clkr, + [GSBI2_QUP_CLK] = &gsbi2_qup_clk.clkr, + [GSBI4_QUP_SRC] = &gsbi4_qup_src.clkr, + [GSBI4_QUP_CLK] = &gsbi4_qup_clk.clkr, + [GSBI5_QUP_SRC] = &gsbi5_qup_src.clkr, + [GSBI5_QUP_CLK] = &gsbi5_qup_clk.clkr, + [GSBI6_QUP_SRC] = &gsbi6_qup_src.clkr, + [GSBI6_QUP_CLK] = &gsbi6_qup_clk.clkr, + [GSBI7_QUP_SRC] = &gsbi7_qup_src.clkr, + [GSBI7_QUP_CLK] = &gsbi7_qup_clk.clkr, + [GP0_SRC] = &gp0_src.clkr, + [GP0_CLK] = &gp0_clk.clkr, + [GP1_SRC] = &gp1_src.clkr, + [GP1_CLK] = &gp1_clk.clkr, + [GP2_SRC] = &gp2_src.clkr, + [GP2_CLK] = &gp2_clk.clkr, + [PMEM_A_CLK] = &pmem_clk.clkr, + [PRNG_SRC] = &prng_src.clkr, + [PRNG_CLK] = &prng_clk.clkr, + [SDC1_SRC] = &sdc1_src.clkr, + [SDC1_CLK] = &sdc1_clk.clkr, + [SDC3_SRC] = &sdc3_src.clkr, + [SDC3_CLK] = &sdc3_clk.clkr, + [TSIF_REF_SRC] = &tsif_ref_src.clkr, + [TSIF_REF_CLK] = &tsif_ref_clk.clkr, + [DMA_BAM_H_CLK] = &dma_bam_h_clk.clkr, + [GSBI1_H_CLK] = &gsbi1_h_clk.clkr, + [GSBI2_H_CLK] = &gsbi2_h_clk.clkr, + [GSBI4_H_CLK] = &gsbi4_h_clk.clkr, + [GSBI5_H_CLK] = &gsbi5_h_clk.clkr, + [GSBI6_H_CLK] = &gsbi6_h_clk.clkr, + [GSBI7_H_CLK] = &gsbi7_h_clk.clkr, + [TSIF_H_CLK] = &tsif_h_clk.clkr, + [SDC1_H_CLK] = &sdc1_h_clk.clkr, + [SDC3_H_CLK] = &sdc3_h_clk.clkr, + [ADM0_CLK] = &adm0_clk.clkr, + [ADM0_PBUS_CLK] = &adm0_pbus_clk.clkr, + [PCIE_A_CLK] = &pcie_a_clk.clkr, + [PCIE_AUX_CLK] = &pcie_aux_clk.clkr, + [PCIE_H_CLK] = &pcie_h_clk.clkr, + [PCIE_PHY_CLK] = &pcie_phy_clk.clkr, + [SFAB_SATA_S_H_CLK] = &sfab_sata_s_h_clk.clkr, + [PMIC_ARB0_H_CLK] = &pmic_arb0_h_clk.clkr, + [PMIC_ARB1_H_CLK] = &pmic_arb1_h_clk.clkr, + [PMIC_SSBI2_CLK] = &pmic_ssbi2_clk.clkr, + [RPM_MSG_RAM_H_CLK] = &rpm_msg_ram_h_clk.clkr, + [SATA_H_CLK] = &sata_h_clk.clkr, + [SATA_CLK_SRC] = &sata_ref_src.clkr, + [SATA_RXOOB_CLK] = &sata_rxoob_clk.clkr, + [SATA_PMALIVE_CLK] = &sata_pmalive_clk.clkr, + [SATA_PHY_REF_CLK] = &sata_phy_ref_clk.clkr, + [SATA_A_CLK] = &sata_a_clk.clkr, + [SATA_PHY_CFG_CLK] = &sata_phy_cfg_clk.clkr, + [PCIE_ALT_REF_SRC] = &pcie_ref_src.clkr, + [PCIE_ALT_REF_CLK] = &pcie_ref_src_clk.clkr, + [PCIE_1_A_CLK] = &pcie1_a_clk.clkr, + [PCIE_1_AUX_CLK] = &pcie1_aux_clk.clkr, + [PCIE_1_H_CLK] = &pcie1_h_clk.clkr, + [PCIE_1_PHY_CLK] = &pcie1_phy_clk.clkr, + [PCIE_1_ALT_REF_SRC] = &pcie1_ref_src.clkr, + [PCIE_1_ALT_REF_CLK] = &pcie1_ref_src_clk.clkr, + [PCIE_2_A_CLK] = &pcie2_a_clk.clkr, + [PCIE_2_AUX_CLK] = &pcie2_aux_clk.clkr, + [PCIE_2_H_CLK] = &pcie2_h_clk.clkr, + [PCIE_2_PHY_CLK] = &pcie2_phy_clk.clkr, + [PCIE_2_ALT_REF_SRC] = &pcie2_ref_src.clkr, + [PCIE_2_ALT_REF_CLK] = &pcie2_ref_src_clk.clkr, + [USB30_MASTER_SRC] = &usb30_master_clk_src.clkr, + [USB30_0_MASTER_CLK] = &usb30_0_branch_clk.clkr, + [USB30_1_MASTER_CLK] = &usb30_1_branch_clk.clkr, + [USB30_UTMI_SRC] = &usb30_utmi_clk.clkr, + [USB30_0_UTMI_CLK] = &usb30_0_utmi_clk_ctl.clkr, + [USB30_1_UTMI_CLK] = &usb30_1_utmi_clk_ctl.clkr, + [USB_HS1_H_CLK] = &usb_hs1_h_clk.clkr, + [USB_HS1_XCVR_SRC] = &usb_hs1_xcvr_clk_src.clkr, + [USB_HS1_XCVR_CLK] = &usb_hs1_xcvr_clk.clkr, + [USB_FS1_H_CLK] = &usb_fs1_h_clk.clkr, + [USB_FS1_XCVR_SRC] = &usb_fs1_xcvr_clk_src.clkr, + [USB_FS1_XCVR_CLK] = &usb_fs1_xcvr_clk.clkr, + [USB_FS1_SYSTEM_CLK] = &usb_fs1_sys_clk.clkr, +}; + +static const struct qcom_reset_map gcc_ipq806x_resets[] = { + [QDSS_STM_RESET] = { 0x2060, 6 }, + [AFAB_SMPSS_S_RESET] = { 0x20b8, 2 }, + [AFAB_SMPSS_M1_RESET] = { 0x20b8, 1 }, + [AFAB_SMPSS_M0_RESET] = { 0x20b8, 0 }, + [AFAB_EBI1_CH0_RESET] = { 0x20c0, 7 }, + [AFAB_EBI1_CH1_RESET] = { 0x20c4, 7 }, + [SFAB_ADM0_M0_RESET] = { 0x21e0, 7 }, + [SFAB_ADM0_M1_RESET] = { 0x21e4, 7 }, + [SFAB_ADM0_M2_RESET] = { 0x21e8, 7 }, + [ADM0_C2_RESET] = { 0x220c, 4 }, + [ADM0_C1_RESET] = { 0x220c, 3 }, + [ADM0_C0_RESET] = { 0x220c, 2 }, + [ADM0_PBUS_RESET] = { 0x220c, 1 }, + [ADM0_RESET] = { 0x220c, 0 }, + [QDSS_CLKS_SW_RESET] = { 0x2260, 5 }, + [QDSS_POR_RESET] = { 0x2260, 4 }, + [QDSS_TSCTR_RESET] = { 0x2260, 3 }, + [QDSS_HRESET_RESET] = { 0x2260, 2 }, + [QDSS_AXI_RESET] = { 0x2260, 1 }, + [QDSS_DBG_RESET] = { 0x2260, 0 }, + [SFAB_PCIE_M_RESET] = { 0x22d8, 1 }, + [SFAB_PCIE_S_RESET] = { 0x22d8, 0 }, + [PCIE_EXT_RESET] = { 0x22dc, 6 }, + [PCIE_PHY_RESET] = { 0x22dc, 5 }, + [PCIE_PCI_RESET] = { 0x22dc, 4 }, + [PCIE_POR_RESET] = { 0x22dc, 3 }, + [PCIE_HCLK_RESET] = { 0x22dc, 2 }, + [PCIE_ACLK_RESET] = { 0x22dc, 0 }, + [SFAB_LPASS_RESET] = { 0x23a0, 7 }, + [SFAB_AFAB_M_RESET] = { 0x23e0, 7 }, + [AFAB_SFAB_M0_RESET] = { 0x2420, 7 }, + [AFAB_SFAB_M1_RESET] = { 0x2424, 7 }, + [SFAB_SATA_S_RESET] = { 0x2480, 7 }, + [SFAB_DFAB_M_RESET] = { 0x2500, 7 }, + [DFAB_SFAB_M_RESET] = { 0x2520, 7 }, + [DFAB_SWAY0_RESET] = { 0x2540, 7 }, + [DFAB_SWAY1_RESET] = { 0x2544, 7 }, + [DFAB_ARB0_RESET] = { 0x2560, 7 }, + [DFAB_ARB1_RESET] = { 0x2564, 7 }, + [PPSS_PROC_RESET] = { 0x2594, 1 }, + [PPSS_RESET] = { 0x2594, 0 }, + [DMA_BAM_RESET] = { 0x25c0, 7 }, + [SPS_TIC_H_RESET] = { 0x2600, 7 }, + [SFAB_CFPB_M_RESET] = { 0x2680, 7 }, + [SFAB_CFPB_S_RESET] = { 0x26c0, 7 }, + [TSIF_H_RESET] = { 0x2700, 7 }, + [CE1_H_RESET] = { 0x2720, 7 }, + [CE1_CORE_RESET] = { 0x2724, 7 }, + [CE1_SLEEP_RESET] = { 0x2728, 7 }, + [CE2_H_RESET] = { 0x2740, 7 }, + [CE2_CORE_RESET] = { 0x2744, 7 }, + [SFAB_SFPB_M_RESET] = { 0x2780, 7 }, + [SFAB_SFPB_S_RESET] = { 0x27a0, 7 }, + [RPM_PROC_RESET] = { 0x27c0, 7 }, + [PMIC_SSBI2_RESET] = { 0x280c, 12 }, + [SDC1_RESET] = { 0x2830, 0 }, + [SDC2_RESET] = { 0x2850, 0 }, + [SDC3_RESET] = { 0x2870, 0 }, + [SDC4_RESET] = { 0x2890, 0 }, + [USB_HS1_RESET] = { 0x2910, 0 }, + [USB_HSIC_RESET] = { 0x2934, 0 }, + [USB_FS1_XCVR_RESET] = { 0x2974, 1 }, + [USB_FS1_RESET] = { 0x2974, 0 }, + [GSBI1_RESET] = { 0x29dc, 0 }, + [GSBI2_RESET] = { 0x29fc, 0 }, + [GSBI3_RESET] = { 0x2a1c, 0 }, + [GSBI4_RESET] = { 0x2a3c, 0 }, + [GSBI5_RESET] = { 0x2a5c, 0 }, + [GSBI6_RESET] = { 0x2a7c, 0 }, + [GSBI7_RESET] = { 0x2a9c, 0 }, + [SPDM_RESET] = { 0x2b6c, 0 }, + [SEC_CTRL_RESET] = { 0x2b80, 7 }, + [TLMM_H_RESET] = { 0x2ba0, 7 }, + [SFAB_SATA_M_RESET] = { 0x2c18, 0 }, + [SATA_RESET] = { 0x2c1c, 0 }, + [TSSC_RESET] = { 0x2ca0, 7 }, + [PDM_RESET] = { 0x2cc0, 12 }, + [MPM_H_RESET] = { 0x2da0, 7 }, + [MPM_RESET] = { 0x2da4, 0 }, + [SFAB_SMPSS_S_RESET] = { 0x2e00, 7 }, + [PRNG_RESET] = { 0x2e80, 12 }, + [SFAB_CE3_M_RESET] = { 0x36c8, 1 }, + [SFAB_CE3_S_RESET] = { 0x36c8, 0 }, + [CE3_SLEEP_RESET] = { 0x36d0, 7 }, + [PCIE_1_M_RESET] = { 0x3a98, 1 }, + [PCIE_1_S_RESET] = { 0x3a98, 0 }, + [PCIE_1_EXT_RESET] = { 0x3a9c, 6 }, + [PCIE_1_PHY_RESET] = { 0x3a9c, 5 }, + [PCIE_1_PCI_RESET] = { 0x3a9c, 4 }, + [PCIE_1_POR_RESET] = { 0x3a9c, 3 }, + [PCIE_1_HCLK_RESET] = { 0x3a9c, 2 }, + [PCIE_1_ACLK_RESET] = { 0x3a9c, 0 }, + [PCIE_2_M_RESET] = { 0x3ad8, 1 }, + [PCIE_2_S_RESET] = { 0x3ad8, 0 }, + [PCIE_2_EXT_RESET] = { 0x3adc, 6 }, + [PCIE_2_PHY_RESET] = { 0x3adc, 5 }, + [PCIE_2_PCI_RESET] = { 0x3adc, 4 }, + [PCIE_2_POR_RESET] = { 0x3adc, 3 }, + [PCIE_2_HCLK_RESET] = { 0x3adc, 2 }, + [PCIE_2_ACLK_RESET] = { 0x3adc, 0 }, + [SFAB_USB30_S_RESET] = { 0x3b54, 1 }, + [SFAB_USB30_M_RESET] = { 0x3b54, 0 }, + [USB30_0_PORT2_HS_PHY_RESET] = { 0x3b50, 5 }, + [USB30_0_MASTER_RESET] = { 0x3b50, 4 }, + [USB30_0_SLEEP_RESET] = { 0x3b50, 3 }, + [USB30_0_UTMI_PHY_RESET] = { 0x3b50, 2 }, + [USB30_0_POWERON_RESET] = { 0x3b50, 1 }, + [USB30_0_PHY_RESET] = { 0x3b50, 0 }, + [USB30_1_MASTER_RESET] = { 0x3b58, 4 }, + [USB30_1_SLEEP_RESET] = { 0x3b58, 3 }, + [USB30_1_UTMI_PHY_RESET] = { 0x3b58, 2 }, + [USB30_1_POWERON_RESET] = { 0x3b58, 1 }, + [USB30_1_PHY_RESET] = { 0x3b58, 0 }, + [NSSFB0_RESET] = { 0x3b60, 6 }, + [NSSFB1_RESET] = { 0x3b60, 7 }, +}; + +static const struct regmap_config gcc_ipq806x_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x3e40, + .fast_io = true, +}; + +static const struct qcom_cc_desc gcc_ipq806x_desc = { + .config = &gcc_ipq806x_regmap_config, + .clks = gcc_ipq806x_clks, + .num_clks = ARRAY_SIZE(gcc_ipq806x_clks), + .resets = gcc_ipq806x_resets, + .num_resets = ARRAY_SIZE(gcc_ipq806x_resets), +}; + +static const struct of_device_id gcc_ipq806x_match_table[] = { + { .compatible = "qcom,gcc-ipq8064" }, + { } +}; +MODULE_DEVICE_TABLE(of, gcc_ipq806x_match_table); + +static int gcc_ipq806x_probe(struct platform_device *pdev) +{ + struct clk *clk; + struct device *dev = &pdev->dev; + + /* Temporary until RPM clocks supported */ + clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 25000000); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + clk = clk_register_fixed_rate(dev, "pxo", NULL, CLK_IS_ROOT, 25000000); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + return qcom_cc_probe(pdev, &gcc_ipq806x_desc); +} + +static int gcc_ipq806x_remove(struct platform_device *pdev) +{ + qcom_cc_remove(pdev); + return 0; +} + +static struct platform_driver gcc_ipq806x_driver = { + .probe = gcc_ipq806x_probe, + .remove = gcc_ipq806x_remove, + .driver = { + .name = "gcc-ipq806x", + .owner = THIS_MODULE, + .of_match_table = gcc_ipq806x_match_table, + }, +}; + +static int __init gcc_ipq806x_init(void) +{ + return platform_driver_register(&gcc_ipq806x_driver); +} +core_initcall(gcc_ipq806x_init); + +static void __exit gcc_ipq806x_exit(void) +{ + platform_driver_unregister(&gcc_ipq806x_driver); +} +module_exit(gcc_ipq806x_exit); + +MODULE_DESCRIPTION("QCOM GCC IPQ806x Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:gcc-ipq806x"); diff --git a/include/dt-bindings/clock/qcom,gcc-ipq806x.h b/include/dt-bindings/clock/qcom,gcc-ipq806x.h new file mode 100644 index 000000000000..b857cadb0bd4 --- /dev/null +++ b/include/dt-bindings/clock/qcom,gcc-ipq806x.h @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * 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 _DT_BINDINGS_CLK_GCC_IPQ806X_H +#define _DT_BINDINGS_CLK_GCC_IPQ806X_H + +#define AFAB_CLK_SRC 0 +#define QDSS_STM_CLK 1 +#define SCSS_A_CLK 2 +#define SCSS_H_CLK 3 +#define AFAB_CORE_CLK 4 +#define SCSS_XO_SRC_CLK 5 +#define AFAB_EBI1_CH0_A_CLK 6 +#define AFAB_EBI1_CH1_A_CLK 7 +#define AFAB_AXI_S0_FCLK 8 +#define AFAB_AXI_S1_FCLK 9 +#define AFAB_AXI_S2_FCLK 10 +#define AFAB_AXI_S3_FCLK 11 +#define AFAB_AXI_S4_FCLK 12 +#define SFAB_CORE_CLK 13 +#define SFAB_AXI_S0_FCLK 14 +#define SFAB_AXI_S1_FCLK 15 +#define SFAB_AXI_S2_FCLK 16 +#define SFAB_AXI_S3_FCLK 17 +#define SFAB_AXI_S4_FCLK 18 +#define SFAB_AXI_S5_FCLK 19 +#define SFAB_AHB_S0_FCLK 20 +#define SFAB_AHB_S1_FCLK 21 +#define SFAB_AHB_S2_FCLK 22 +#define SFAB_AHB_S3_FCLK 23 +#define SFAB_AHB_S4_FCLK 24 +#define SFAB_AHB_S5_FCLK 25 +#define SFAB_AHB_S6_FCLK 26 +#define SFAB_AHB_S7_FCLK 27 +#define QDSS_AT_CLK_SRC 28 +#define QDSS_AT_CLK 29 +#define QDSS_TRACECLKIN_CLK_SRC 30 +#define QDSS_TRACECLKIN_CLK 31 +#define QDSS_TSCTR_CLK_SRC 32 +#define QDSS_TSCTR_CLK 33 +#define SFAB_ADM0_M0_A_CLK 34 +#define SFAB_ADM0_M1_A_CLK 35 +#define SFAB_ADM0_M2_H_CLK 36 +#define ADM0_CLK 37 +#define ADM0_PBUS_CLK 38 +#define IMEM0_A_CLK 39 +#define QDSS_H_CLK 40 +#define PCIE_A_CLK 41 +#define PCIE_AUX_CLK 42 +#define PCIE_H_CLK 43 +#define PCIE_PHY_CLK 44 +#define SFAB_CLK_SRC 45 +#define SFAB_LPASS_Q6_A_CLK 46 +#define SFAB_AFAB_M_A_CLK 47 +#define AFAB_SFAB_M0_A_CLK 48 +#define AFAB_SFAB_M1_A_CLK 49 +#define SFAB_SATA_S_H_CLK 50 +#define DFAB_CLK_SRC 51 +#define DFAB_CLK 52 +#define SFAB_DFAB_M_A_CLK 53 +#define DFAB_SFAB_M_A_CLK 54 +#define DFAB_SWAY0_H_CLK 55 +#define DFAB_SWAY1_H_CLK 56 +#define DFAB_ARB0_H_CLK 57 +#define DFAB_ARB1_H_CLK 58 +#define PPSS_H_CLK 59 +#define PPSS_PROC_CLK 60 +#define PPSS_TIMER0_CLK 61 +#define PPSS_TIMER1_CLK 62 +#define PMEM_A_CLK 63 +#define DMA_BAM_H_CLK 64 +#define SIC_H_CLK 65 +#define SPS_TIC_H_CLK 66 +#define CFPB_2X_CLK_SRC 67 +#define CFPB_CLK 68 +#define CFPB0_H_CLK 69 +#define CFPB1_H_CLK 70 +#define CFPB2_H_CLK 71 +#define SFAB_CFPB_M_H_CLK 72 +#define CFPB_MASTER_H_CLK 73 +#define SFAB_CFPB_S_H_CLK 74 +#define CFPB_SPLITTER_H_CLK 75 +#define TSIF_H_CLK 76 +#define TSIF_INACTIVITY_TIMERS_CLK 77 +#define TSIF_REF_SRC 78 +#define TSIF_REF_CLK 79 +#define CE1_H_CLK 80 +#define CE1_CORE_CLK 81 +#define CE1_SLEEP_CLK 82 +#define CE2_H_CLK 83 +#define CE2_CORE_CLK 84 +#define SFPB_H_CLK_SRC 85 +#define SFPB_H_CLK 86 +#define SFAB_SFPB_M_H_CLK 87 +#define SFAB_SFPB_S_H_CLK 88 +#define RPM_PROC_CLK 89 +#define RPM_BUS_H_CLK 90 +#define RPM_SLEEP_CLK 91 +#define RPM_TIMER_CLK 92 +#define RPM_MSG_RAM_H_CLK 93 +#define PMIC_ARB0_H_CLK 94 +#define PMIC_ARB1_H_CLK 95 +#define PMIC_SSBI2_SRC 96 +#define PMIC_SSBI2_CLK 97 +#define SDC1_H_CLK 98 +#define SDC2_H_CLK 99 +#define SDC3_H_CLK 100 +#define SDC4_H_CLK 101 +#define SDC1_SRC 102 +#define SDC1_CLK 103 +#define SDC2_SRC 104 +#define SDC2_CLK 105 +#define SDC3_SRC 106 +#define SDC3_CLK 107 +#define SDC4_SRC 108 +#define SDC4_CLK 109 +#define USB_HS1_H_CLK 110 +#define USB_HS1_XCVR_SRC 111 +#define USB_HS1_XCVR_CLK 112 +#define USB_HSIC_H_CLK 113 +#define USB_HSIC_XCVR_SRC 114 +#define USB_HSIC_XCVR_CLK 115 +#define USB_HSIC_SYSTEM_CLK_SRC 116 +#define USB_HSIC_SYSTEM_CLK 117 +#define CFPB0_C0_H_CLK 118 +#define CFPB0_D0_H_CLK 119 +#define CFPB0_C1_H_CLK 120 +#define CFPB0_D1_H_CLK 121 +#define USB_FS1_H_CLK 122 +#define USB_FS1_XCVR_SRC 123 +#define USB_FS1_XCVR_CLK 124 +#define USB_FS1_SYSTEM_CLK 125 +#define GSBI_COMMON_SIM_SRC 126 +#define GSBI1_H_CLK 127 +#define GSBI2_H_CLK 128 +#define GSBI3_H_CLK 129 +#define GSBI4_H_CLK 130 +#define GSBI5_H_CLK 131 +#define GSBI6_H_CLK 132 +#define GSBI7_H_CLK 133 +#define GSBI1_QUP_SRC 134 +#define GSBI1_QUP_CLK 135 +#define GSBI2_QUP_SRC 136 +#define GSBI2_QUP_CLK 137 +#define GSBI3_QUP_SRC 138 +#define GSBI3_QUP_CLK 139 +#define GSBI4_QUP_SRC 140 +#define GSBI4_QUP_CLK 141 +#define GSBI5_QUP_SRC 142 +#define GSBI5_QUP_CLK 143 +#define GSBI6_QUP_SRC 144 +#define GSBI6_QUP_CLK 145 +#define GSBI7_QUP_SRC 146 +#define GSBI7_QUP_CLK 147 +#define GSBI1_UART_SRC 148 +#define GSBI1_UART_CLK 149 +#define GSBI2_UART_SRC 150 +#define GSBI2_UART_CLK 151 +#define GSBI3_UART_SRC 152 +#define GSBI3_UART_CLK 153 +#define GSBI4_UART_SRC 154 +#define GSBI4_UART_CLK 155 +#define GSBI5_UART_SRC 156 +#define GSBI5_UART_CLK 157 +#define GSBI6_UART_SRC 158 +#define GSBI6_UART_CLK 159 +#define GSBI7_UART_SRC 160 +#define GSBI7_UART_CLK 161 +#define GSBI1_SIM_CLK 162 +#define GSBI2_SIM_CLK 163 +#define GSBI3_SIM_CLK 164 +#define GSBI4_SIM_CLK 165 +#define GSBI5_SIM_CLK 166 +#define GSBI6_SIM_CLK 167 +#define GSBI7_SIM_CLK 168 +#define USB_HSIC_HSIC_CLK_SRC 169 +#define USB_HSIC_HSIC_CLK 170 +#define USB_HSIC_HSIO_CAL_CLK 171 +#define SPDM_CFG_H_CLK 172 +#define SPDM_MSTR_H_CLK 173 +#define SPDM_FF_CLK_SRC 174 +#define SPDM_FF_CLK 175 +#define SEC_CTRL_CLK 176 +#define SEC_CTRL_ACC_CLK_SRC 177 +#define SEC_CTRL_ACC_CLK 178 +#define TLMM_H_CLK 179 +#define TLMM_CLK 180 +#define SATA_H_CLK 181 +#define SATA_CLK_SRC 182 +#define SATA_RXOOB_CLK 183 +#define SATA_PMALIVE_CLK 184 +#define SATA_PHY_REF_CLK 185 +#define SATA_A_CLK 186 +#define SATA_PHY_CFG_CLK 187 +#define TSSC_CLK_SRC 188 +#define TSSC_CLK 189 +#define PDM_SRC 190 +#define PDM_CLK 191 +#define GP0_SRC 192 +#define GP0_CLK 193 +#define GP1_SRC 194 +#define GP1_CLK 195 +#define GP2_SRC 196 +#define GP2_CLK 197 +#define MPM_CLK 198 +#define EBI1_CLK_SRC 199 +#define EBI1_CH0_CLK 200 +#define EBI1_CH1_CLK 201 +#define EBI1_2X_CLK 202 +#define EBI1_CH0_DQ_CLK 203 +#define EBI1_CH1_DQ_CLK 204 +#define EBI1_CH0_CA_CLK 205 +#define EBI1_CH1_CA_CLK 206 +#define EBI1_XO_CLK 207 +#define SFAB_SMPSS_S_H_CLK 208 +#define PRNG_SRC 209 +#define PRNG_CLK 210 +#define PXO_SRC 211 +#define SPDM_CY_PORT0_CLK 212 +#define SPDM_CY_PORT1_CLK 213 +#define SPDM_CY_PORT2_CLK 214 +#define SPDM_CY_PORT3_CLK 215 +#define SPDM_CY_PORT4_CLK 216 +#define SPDM_CY_PORT5_CLK 217 +#define SPDM_CY_PORT6_CLK 218 +#define SPDM_CY_PORT7_CLK 219 +#define PLL0 220 +#define PLL0_VOTE 221 +#define PLL3 222 +#define PLL3_VOTE 223 +#define PLL4 224 +#define PLL4_VOTE 225 +#define PLL8 226 +#define PLL8_VOTE 227 +#define PLL9 228 +#define PLL10 229 +#define PLL11 230 +#define PLL12 231 +#define PLL14 232 +#define PLL14_VOTE 233 +#define PLL18 234 +#define CE5_SRC 235 +#define CE5_H_CLK 236 +#define CE5_CORE_CLK 237 +#define CE3_SLEEP_CLK 238 +#define SFAB_AHB_S8_FCLK 239 +#define SPDM_CY_PORT8_CLK 246 +#define PCIE_ALT_REF_SRC 247 +#define PCIE_ALT_REF_CLK 248 +#define PCIE_1_A_CLK 249 +#define PCIE_1_AUX_CLK 250 +#define PCIE_1_H_CLK 251 +#define PCIE_1_PHY_CLK 252 +#define PCIE_1_ALT_REF_SRC 253 +#define PCIE_1_ALT_REF_CLK 254 +#define PCIE_2_A_CLK 255 +#define PCIE_2_AUX_CLK 256 +#define PCIE_2_H_CLK 257 +#define PCIE_2_PHY_CLK 258 +#define PCIE_2_ALT_REF_SRC 259 +#define PCIE_2_ALT_REF_CLK 260 +#define EBI2_CLK 261 +#define USB30_SLEEP_CLK 262 +#define USB30_UTMI_SRC 263 +#define USB30_0_UTMI_CLK 264 +#define USB30_1_UTMI_CLK 265 +#define USB30_MASTER_SRC 266 +#define USB30_0_MASTER_CLK 267 +#define USB30_1_MASTER_CLK 268 +#define GMAC_CORE1_CLK_SRC 269 +#define GMAC_CORE2_CLK_SRC 270 +#define GMAC_CORE3_CLK_SRC 271 +#define GMAC_CORE4_CLK_SRC 272 +#define GMAC_CORE1_CLK 273 +#define GMAC_CORE2_CLK 274 +#define GMAC_CORE3_CLK 275 +#define GMAC_CORE4_CLK 276 +#define UBI32_CORE1_CLK_SRC 277 +#define UBI32_CORE2_CLK_SRC 278 +#define UBI32_CORE1_CLK 279 +#define UBI32_CORE2_CLK 280 + +#endif diff --git a/include/dt-bindings/reset/qcom,gcc-ipq806x.h b/include/dt-bindings/reset/qcom,gcc-ipq806x.h new file mode 100644 index 000000000000..0ad5ef930b5d --- /dev/null +++ b/include/dt-bindings/reset/qcom,gcc-ipq806x.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * 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 _DT_BINDINGS_RESET_IPQ_806X_H +#define _DT_BINDINGS_RESET_IPQ_806X_H + +#define QDSS_STM_RESET 0 +#define AFAB_SMPSS_S_RESET 1 +#define AFAB_SMPSS_M1_RESET 2 +#define AFAB_SMPSS_M0_RESET 3 +#define AFAB_EBI1_CH0_RESET 4 +#define AFAB_EBI1_CH1_RESET 5 +#define SFAB_ADM0_M0_RESET 6 +#define SFAB_ADM0_M1_RESET 7 +#define SFAB_ADM0_M2_RESET 8 +#define ADM0_C2_RESET 9 +#define ADM0_C1_RESET 10 +#define ADM0_C0_RESET 11 +#define ADM0_PBUS_RESET 12 +#define ADM0_RESET 13 +#define QDSS_CLKS_SW_RESET 14 +#define QDSS_POR_RESET 15 +#define QDSS_TSCTR_RESET 16 +#define QDSS_HRESET_RESET 17 +#define QDSS_AXI_RESET 18 +#define QDSS_DBG_RESET 19 +#define SFAB_PCIE_M_RESET 20 +#define SFAB_PCIE_S_RESET 21 +#define PCIE_EXT_RESET 22 +#define PCIE_PHY_RESET 23 +#define PCIE_PCI_RESET 24 +#define PCIE_POR_RESET 25 +#define PCIE_HCLK_RESET 26 +#define PCIE_ACLK_RESET 27 +#define SFAB_LPASS_RESET 28 +#define SFAB_AFAB_M_RESET 29 +#define AFAB_SFAB_M0_RESET 30 +#define AFAB_SFAB_M1_RESET 31 +#define SFAB_SATA_S_RESET 32 +#define SFAB_DFAB_M_RESET 33 +#define DFAB_SFAB_M_RESET 34 +#define DFAB_SWAY0_RESET 35 +#define DFAB_SWAY1_RESET 36 +#define DFAB_ARB0_RESET 37 +#define DFAB_ARB1_RESET 38 +#define PPSS_PROC_RESET 39 +#define PPSS_RESET 40 +#define DMA_BAM_RESET 41 +#define SPS_TIC_H_RESET 42 +#define SFAB_CFPB_M_RESET 43 +#define SFAB_CFPB_S_RESET 44 +#define TSIF_H_RESET 45 +#define CE1_H_RESET 46 +#define CE1_CORE_RESET 47 +#define CE1_SLEEP_RESET 48 +#define CE2_H_RESET 49 +#define CE2_CORE_RESET 50 +#define SFAB_SFPB_M_RESET 51 +#define SFAB_SFPB_S_RESET 52 +#define RPM_PROC_RESET 53 +#define PMIC_SSBI2_RESET 54 +#define SDC1_RESET 55 +#define SDC2_RESET 56 +#define SDC3_RESET 57 +#define SDC4_RESET 58 +#define USB_HS1_RESET 59 +#define USB_HSIC_RESET 60 +#define USB_FS1_XCVR_RESET 61 +#define USB_FS1_RESET 62 +#define GSBI1_RESET 63 +#define GSBI2_RESET 64 +#define GSBI3_RESET 65 +#define GSBI4_RESET 66 +#define GSBI5_RESET 67 +#define GSBI6_RESET 68 +#define GSBI7_RESET 69 +#define SPDM_RESET 70 +#define SEC_CTRL_RESET 71 +#define TLMM_H_RESET 72 +#define SFAB_SATA_M_RESET 73 +#define SATA_RESET 74 +#define TSSC_RESET 75 +#define PDM_RESET 76 +#define MPM_H_RESET 77 +#define MPM_RESET 78 +#define SFAB_SMPSS_S_RESET 79 +#define PRNG_RESET 80 +#define SFAB_CE3_M_RESET 81 +#define SFAB_CE3_S_RESET 82 +#define CE3_SLEEP_RESET 83 +#define PCIE_1_M_RESET 84 +#define PCIE_1_S_RESET 85 +#define PCIE_1_EXT_RESET 86 +#define PCIE_1_PHY_RESET 87 +#define PCIE_1_PCI_RESET 88 +#define PCIE_1_POR_RESET 89 +#define PCIE_1_HCLK_RESET 90 +#define PCIE_1_ACLK_RESET 91 +#define PCIE_2_M_RESET 92 +#define PCIE_2_S_RESET 93 +#define PCIE_2_EXT_RESET 94 +#define PCIE_2_PHY_RESET 95 +#define PCIE_2_PCI_RESET 96 +#define PCIE_2_POR_RESET 97 +#define PCIE_2_HCLK_RESET 98 +#define PCIE_2_ACLK_RESET 99 +#define SFAB_USB30_S_RESET 100 +#define SFAB_USB30_M_RESET 101 +#define USB30_0_PORT2_HS_PHY_RESET 102 +#define USB30_0_MASTER_RESET 103 +#define USB30_0_SLEEP_RESET 104 +#define USB30_0_UTMI_PHY_RESET 105 +#define USB30_0_POWERON_RESET 106 +#define USB30_0_PHY_RESET 107 +#define USB30_1_MASTER_RESET 108 +#define USB30_1_SLEEP_RESET 109 +#define USB30_1_UTMI_PHY_RESET 110 +#define USB30_1_POWERON_RESET 111 +#define USB30_1_PHY_RESET 112 +#define NSSFB0_RESET 113 +#define NSSFB1_RESET 114 +#endif -- cgit v1.2.3 From e216ce60a9e05ab399d098f05cd86fd95c9da8d5 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 15 Jul 2014 14:52:22 -0700 Subject: clk: qcom: Add support for APQ8064 multimedia clocks The APQ8064 multimedia clock controller is fairly similar to the 8960 multimedia clock controller, except that gfx2d0/1 has been removed and the gfx3d frequency is slightly faster when using the newly introduced PLL15. We also add vcap clocks and a couple new TV clocks. Signed-off-by: Stephen Boyd --- .../devicetree/bindings/clock/qcom,mmcc.txt | 1 + drivers/clk/qcom/mmcc-msm8960.c | 433 ++++++++++++++++++++- include/dt-bindings/clock/qcom,mmcc-msm8960.h | 8 + include/dt-bindings/reset/qcom,mmcc-msm8960.h | 8 + 4 files changed, 448 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/qcom,mmcc.txt b/Documentation/devicetree/bindings/clock/qcom,mmcc.txt index 4f1f6be9e66d..29ebf84d25af 100644 --- a/Documentation/devicetree/bindings/clock/qcom,mmcc.txt +++ b/Documentation/devicetree/bindings/clock/qcom,mmcc.txt @@ -4,6 +4,7 @@ Qualcomm Multimedia Clock & Reset Controller Binding Required properties : - compatible : shall contain only one of the following: + "qcom,mmcc-apq8064" "qcom,mmcc-apq8084" "qcom,mmcc-msm8660" "qcom,mmcc-msm8960" diff --git a/drivers/clk/qcom/mmcc-msm8960.c b/drivers/clk/qcom/mmcc-msm8960.c index 7985982ab4e9..255f127e8f3b 100644 --- a/drivers/clk/qcom/mmcc-msm8960.c +++ b/drivers/clk/qcom/mmcc-msm8960.c @@ -37,6 +37,7 @@ #define P_PLL8 1 #define P_PLL2 2 #define P_PLL3 3 +#define P_PLL15 3 #define F_MN(f, s, _m, _n) { .freq = f, .src = s, .m = _m, .n = _n } @@ -59,6 +60,20 @@ static u8 mmcc_pxo_pll8_pll2_pll3_map[] = { [P_PLL3] = 3, }; +static const char *mmcc_pxo_pll8_pll2_pll15[] = { + "pxo", + "pll8_vote", + "pll2", + "pll15", +}; + +static u8 mmcc_pxo_pll8_pll2_pll15_map[] = { + [P_PXO] = 0, + [P_PLL8] = 2, + [P_PLL2] = 1, + [P_PLL15] = 3, +}; + static const char *mmcc_pxo_pll8_pll2_pll3[] = { "pxo", "pll8_vote", @@ -82,6 +97,36 @@ static struct clk_pll pll2 = { }, }; +static struct clk_pll pll15 = { + .l_reg = 0x33c, + .m_reg = 0x340, + .n_reg = 0x344, + .config_reg = 0x348, + .mode_reg = 0x338, + .status_reg = 0x350, + .status_bit = 16, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pll15", + .parent_names = (const char *[]){ "pxo" }, + .num_parents = 1, + .ops = &clk_pll_ops, + }, +}; + +static const struct pll_config pll15_config = { + .l = 33, + .m = 1, + .n = 3, + .vco_val = 0x2 << 16, + .vco_mask = 0x3 << 16, + .pre_div_val = 0x0, + .pre_div_mask = BIT(19), + .post_div_val = 0x0, + .post_div_mask = 0x3 << 20, + .mn_ena_mask = BIT(22), + .main_output_mask = BIT(23), +}; + static struct freq_tbl clk_tbl_cam[] = { { 6000000, P_PLL8, 4, 1, 16 }, { 8000000, P_PLL8, 4, 1, 12 }, @@ -863,6 +908,27 @@ static struct freq_tbl clk_tbl_gfx3d[] = { { } }; +static struct freq_tbl clk_tbl_gfx3d_8064[] = { + F_MN( 27000000, P_PXO, 0, 0), + F_MN( 48000000, P_PLL8, 1, 8), + F_MN( 54857000, P_PLL8, 1, 7), + F_MN( 64000000, P_PLL8, 1, 6), + F_MN( 76800000, P_PLL8, 1, 5), + F_MN( 96000000, P_PLL8, 1, 4), + F_MN(128000000, P_PLL8, 1, 3), + F_MN(145455000, P_PLL2, 2, 11), + F_MN(160000000, P_PLL2, 1, 5), + F_MN(177778000, P_PLL2, 2, 9), + F_MN(192000000, P_PLL8, 1, 2), + F_MN(200000000, P_PLL2, 1, 4), + F_MN(228571000, P_PLL2, 2, 7), + F_MN(266667000, P_PLL2, 1, 3), + F_MN(320000000, P_PLL2, 2, 5), + F_MN(400000000, P_PLL2, 1, 2), + F_MN(450000000, P_PLL15, 1, 2), + { } +}; + static struct clk_dyn_rcg gfx3d_src = { .ns_reg = 0x008c, .md_reg[0] = 0x0084, @@ -905,6 +971,13 @@ static struct clk_dyn_rcg gfx3d_src = { }, }; +static const struct clk_init_data gfx3d_8064_init = { + .name = "gfx3d_src", + .parent_names = mmcc_pxo_pll8_pll2_pll15, + .num_parents = 4, + .ops = &clk_dyn_rcg_ops, +}; + static struct clk_branch gfx3d_clk = { .halt_reg = 0x01c8, .halt_bit = 4, @@ -921,6 +994,91 @@ static struct clk_branch gfx3d_clk = { }, }; +static struct freq_tbl clk_tbl_vcap[] = { + F_MN( 27000000, P_PXO, 0, 0), + F_MN( 54860000, P_PLL8, 1, 7), + F_MN( 64000000, P_PLL8, 1, 6), + F_MN( 76800000, P_PLL8, 1, 5), + F_MN(128000000, P_PLL8, 1, 3), + F_MN(160000000, P_PLL2, 1, 5), + F_MN(200000000, P_PLL2, 1, 4), + { } +}; + +static struct clk_dyn_rcg vcap_src = { + .ns_reg = 0x021c, + .md_reg[0] = 0x01ec, + .md_reg[1] = 0x0218, + .mn[0] = { + .mnctr_en_bit = 8, + .mnctr_reset_bit = 23, + .mnctr_mode_shift = 9, + .n_val_shift = 18, + .m_val_shift = 4, + .width = 4, + }, + .mn[1] = { + .mnctr_en_bit = 5, + .mnctr_reset_bit = 22, + .mnctr_mode_shift = 6, + .n_val_shift = 14, + .m_val_shift = 4, + .width = 4, + }, + .s[0] = { + .src_sel_shift = 3, + .parent_map = mmcc_pxo_pll8_pll2_map, + }, + .s[1] = { + .src_sel_shift = 0, + .parent_map = mmcc_pxo_pll8_pll2_map, + }, + .mux_sel_bit = 11, + .freq_tbl = clk_tbl_vcap, + .clkr = { + .enable_reg = 0x0178, + .enable_mask = BIT(2), + .hw.init = &(struct clk_init_data){ + .name = "vcap_src", + .parent_names = mmcc_pxo_pll8_pll2, + .num_parents = 3, + .ops = &clk_dyn_rcg_ops, + }, + }, +}; + +static struct clk_branch vcap_clk = { + .halt_reg = 0x0240, + .halt_bit = 15, + .clkr = { + .enable_reg = 0x0178, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "vcap_clk", + .parent_names = (const char *[]){ "vcap_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch vcap_npl_clk = { + .halt_reg = 0x0240, + .halt_bit = 25, + .clkr = { + .enable_reg = 0x0178, + .enable_mask = BIT(13), + .hw.init = &(struct clk_init_data){ + .name = "vcap_npl_clk", + .parent_names = (const char *[]){ "vcap_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + static struct freq_tbl clk_tbl_ijpeg[] = { { 27000000, P_PXO, 1, 0, 0 }, { 36570000, P_PLL8, 1, 2, 21 }, @@ -1323,6 +1481,38 @@ static struct clk_branch hdmi_tv_clk = { }, }; +static struct clk_branch rgb_tv_clk = { + .halt_reg = 0x0240, + .halt_bit = 27, + .clkr = { + .enable_reg = 0x0124, + .enable_mask = BIT(14), + .hw.init = &(struct clk_init_data){ + .parent_names = tv_src_name, + .num_parents = 1, + .name = "rgb_tv_clk", + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch npl_tv_clk = { + .halt_reg = 0x0240, + .halt_bit = 26, + .clkr = { + .enable_reg = 0x0124, + .enable_mask = BIT(16), + .hw.init = &(struct clk_init_data){ + .parent_names = tv_src_name, + .num_parents = 1, + .name = "npl_tv_clk", + .ops = &clk_branch_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + static struct clk_branch hdmi_app_clk = { .halt_reg = 0x01cc, .halt_bit = 25, @@ -1698,6 +1888,22 @@ static struct clk_branch rot_axi_clk = { }, }; +static struct clk_branch vcap_axi_clk = { + .halt_reg = 0x0240, + .halt_bit = 20, + .hwcg_reg = 0x0244, + .hwcg_bit = 11, + .clkr = { + .enable_reg = 0x0244, + .enable_mask = BIT(12), + .hw.init = &(struct clk_init_data){ + .name = "vcap_axi_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + static struct clk_branch vpe_axi_clk = { .hwcg_reg = 0x0020, .hwcg_bit = 27, @@ -2000,6 +2206,20 @@ static struct clk_branch tv_enc_ahb_clk = { }, }; +static struct clk_branch vcap_ahb_clk = { + .halt_reg = 0x0240, + .halt_bit = 23, + .clkr = { + .enable_reg = 0x0248, + .enable_mask = BIT(1), + .hw.init = &(struct clk_init_data){ + .name = "vcap_ahb_clk", + .ops = &clk_branch_ops, + .flags = CLK_IS_ROOT, + }, + }, +}; + static struct clk_branch vcodec_ahb_clk = { .hwcg_reg = 0x0038, .hwcg_bit = 26, @@ -2212,6 +2432,175 @@ static const struct qcom_reset_map mmcc_msm8960_resets[] = { [CSI_RDI2_RESET] = { 0x0214 }, }; +static struct clk_regmap *mmcc_apq8064_clks[] = { + [AMP_AHB_CLK] = &_ahb_clk.clkr, + [DSI2_S_AHB_CLK] = &dsi2_s_ahb_clk.clkr, + [JPEGD_AHB_CLK] = &jpegd_ahb_clk.clkr, + [DSI_S_AHB_CLK] = &dsi_s_ahb_clk.clkr, + [DSI2_M_AHB_CLK] = &dsi2_m_ahb_clk.clkr, + [VPE_AHB_CLK] = &vpe_ahb_clk.clkr, + [SMMU_AHB_CLK] = &smmu_ahb_clk.clkr, + [HDMI_M_AHB_CLK] = &hdmi_m_ahb_clk.clkr, + [VFE_AHB_CLK] = &vfe_ahb_clk.clkr, + [ROT_AHB_CLK] = &rot_ahb_clk.clkr, + [VCODEC_AHB_CLK] = &vcodec_ahb_clk.clkr, + [MDP_AHB_CLK] = &mdp_ahb_clk.clkr, + [DSI_M_AHB_CLK] = &dsi_m_ahb_clk.clkr, + [CSI_AHB_CLK] = &csi_ahb_clk.clkr, + [MMSS_IMEM_AHB_CLK] = &mmss_imem_ahb_clk.clkr, + [IJPEG_AHB_CLK] = &ijpeg_ahb_clk.clkr, + [HDMI_S_AHB_CLK] = &hdmi_s_ahb_clk.clkr, + [GFX3D_AHB_CLK] = &gfx3d_ahb_clk.clkr, + [JPEGD_AXI_CLK] = &jpegd_axi_clk.clkr, + [GMEM_AXI_CLK] = &gmem_axi_clk.clkr, + [MDP_AXI_CLK] = &mdp_axi_clk.clkr, + [MMSS_IMEM_AXI_CLK] = &mmss_imem_axi_clk.clkr, + [IJPEG_AXI_CLK] = &ijpeg_axi_clk.clkr, + [GFX3D_AXI_CLK] = &gfx3d_axi_clk.clkr, + [VCODEC_AXI_CLK] = &vcodec_axi_clk.clkr, + [VFE_AXI_CLK] = &vfe_axi_clk.clkr, + [VPE_AXI_CLK] = &vpe_axi_clk.clkr, + [ROT_AXI_CLK] = &rot_axi_clk.clkr, + [VCODEC_AXI_A_CLK] = &vcodec_axi_a_clk.clkr, + [VCODEC_AXI_B_CLK] = &vcodec_axi_b_clk.clkr, + [CSI0_SRC] = &csi0_src.clkr, + [CSI0_CLK] = &csi0_clk.clkr, + [CSI0_PHY_CLK] = &csi0_phy_clk.clkr, + [CSI1_SRC] = &csi1_src.clkr, + [CSI1_CLK] = &csi1_clk.clkr, + [CSI1_PHY_CLK] = &csi1_phy_clk.clkr, + [CSI2_SRC] = &csi2_src.clkr, + [CSI2_CLK] = &csi2_clk.clkr, + [CSI2_PHY_CLK] = &csi2_phy_clk.clkr, + [CSI_PIX_CLK] = &csi_pix_clk.clkr, + [CSI_RDI_CLK] = &csi_rdi_clk.clkr, + [MDP_VSYNC_CLK] = &mdp_vsync_clk.clkr, + [HDMI_APP_CLK] = &hdmi_app_clk.clkr, + [CSI_PIX1_CLK] = &csi_pix1_clk.clkr, + [CSI_RDI2_CLK] = &csi_rdi2_clk.clkr, + [CSI_RDI1_CLK] = &csi_rdi1_clk.clkr, + [GFX3D_SRC] = &gfx3d_src.clkr, + [GFX3D_CLK] = &gfx3d_clk.clkr, + [IJPEG_SRC] = &ijpeg_src.clkr, + [IJPEG_CLK] = &ijpeg_clk.clkr, + [JPEGD_SRC] = &jpegd_src.clkr, + [JPEGD_CLK] = &jpegd_clk.clkr, + [MDP_SRC] = &mdp_src.clkr, + [MDP_CLK] = &mdp_clk.clkr, + [MDP_LUT_CLK] = &mdp_lut_clk.clkr, + [ROT_SRC] = &rot_src.clkr, + [ROT_CLK] = &rot_clk.clkr, + [TV_DAC_CLK] = &tv_dac_clk.clkr, + [HDMI_TV_CLK] = &hdmi_tv_clk.clkr, + [MDP_TV_CLK] = &mdp_tv_clk.clkr, + [TV_SRC] = &tv_src.clkr, + [VCODEC_SRC] = &vcodec_src.clkr, + [VCODEC_CLK] = &vcodec_clk.clkr, + [VFE_SRC] = &vfe_src.clkr, + [VFE_CLK] = &vfe_clk.clkr, + [VFE_CSI_CLK] = &vfe_csi_clk.clkr, + [VPE_SRC] = &vpe_src.clkr, + [VPE_CLK] = &vpe_clk.clkr, + [CAMCLK0_SRC] = &camclk0_src.clkr, + [CAMCLK0_CLK] = &camclk0_clk.clkr, + [CAMCLK1_SRC] = &camclk1_src.clkr, + [CAMCLK1_CLK] = &camclk1_clk.clkr, + [CAMCLK2_SRC] = &camclk2_src.clkr, + [CAMCLK2_CLK] = &camclk2_clk.clkr, + [CSIPHYTIMER_SRC] = &csiphytimer_src.clkr, + [CSIPHY2_TIMER_CLK] = &csiphy2_timer_clk.clkr, + [CSIPHY1_TIMER_CLK] = &csiphy1_timer_clk.clkr, + [CSIPHY0_TIMER_CLK] = &csiphy0_timer_clk.clkr, + [PLL2] = &pll2.clkr, + [RGB_TV_CLK] = &rgb_tv_clk.clkr, + [NPL_TV_CLK] = &npl_tv_clk.clkr, + [VCAP_AHB_CLK] = &vcap_ahb_clk.clkr, + [VCAP_AXI_CLK] = &vcap_axi_clk.clkr, + [VCAP_SRC] = &vcap_src.clkr, + [VCAP_CLK] = &vcap_clk.clkr, + [VCAP_NPL_CLK] = &vcap_npl_clk.clkr, + [PLL15] = &pll15.clkr, +}; + +static const struct qcom_reset_map mmcc_apq8064_resets[] = { + [GFX3D_AXI_RESET] = { 0x0208, 17 }, + [VCAP_AXI_RESET] = { 0x0208, 16 }, + [VPE_AXI_RESET] = { 0x0208, 15 }, + [IJPEG_AXI_RESET] = { 0x0208, 14 }, + [MPD_AXI_RESET] = { 0x0208, 13 }, + [VFE_AXI_RESET] = { 0x0208, 9 }, + [SP_AXI_RESET] = { 0x0208, 8 }, + [VCODEC_AXI_RESET] = { 0x0208, 7 }, + [ROT_AXI_RESET] = { 0x0208, 6 }, + [VCODEC_AXI_A_RESET] = { 0x0208, 5 }, + [VCODEC_AXI_B_RESET] = { 0x0208, 4 }, + [FAB_S3_AXI_RESET] = { 0x0208, 3 }, + [FAB_S2_AXI_RESET] = { 0x0208, 2 }, + [FAB_S1_AXI_RESET] = { 0x0208, 1 }, + [FAB_S0_AXI_RESET] = { 0x0208 }, + [SMMU_GFX3D_ABH_RESET] = { 0x020c, 31 }, + [SMMU_VPE_AHB_RESET] = { 0x020c, 30 }, + [SMMU_VFE_AHB_RESET] = { 0x020c, 29 }, + [SMMU_ROT_AHB_RESET] = { 0x020c, 28 }, + [SMMU_VCODEC_B_AHB_RESET] = { 0x020c, 27 }, + [SMMU_VCODEC_A_AHB_RESET] = { 0x020c, 26 }, + [SMMU_MDP1_AHB_RESET] = { 0x020c, 25 }, + [SMMU_MDP0_AHB_RESET] = { 0x020c, 24 }, + [SMMU_JPEGD_AHB_RESET] = { 0x020c, 23 }, + [SMMU_IJPEG_AHB_RESET] = { 0x020c, 22 }, + [APU_AHB_RESET] = { 0x020c, 18 }, + [CSI_AHB_RESET] = { 0x020c, 17 }, + [TV_ENC_AHB_RESET] = { 0x020c, 15 }, + [VPE_AHB_RESET] = { 0x020c, 14 }, + [FABRIC_AHB_RESET] = { 0x020c, 13 }, + [GFX3D_AHB_RESET] = { 0x020c, 10 }, + [HDMI_AHB_RESET] = { 0x020c, 9 }, + [MSSS_IMEM_AHB_RESET] = { 0x020c, 8 }, + [IJPEG_AHB_RESET] = { 0x020c, 7 }, + [DSI_M_AHB_RESET] = { 0x020c, 6 }, + [DSI_S_AHB_RESET] = { 0x020c, 5 }, + [JPEGD_AHB_RESET] = { 0x020c, 4 }, + [MDP_AHB_RESET] = { 0x020c, 3 }, + [ROT_AHB_RESET] = { 0x020c, 2 }, + [VCODEC_AHB_RESET] = { 0x020c, 1 }, + [VFE_AHB_RESET] = { 0x020c, 0 }, + [SMMU_VCAP_AHB_RESET] = { 0x0200, 3 }, + [VCAP_AHB_RESET] = { 0x0200, 2 }, + [DSI2_M_AHB_RESET] = { 0x0200, 1 }, + [DSI2_S_AHB_RESET] = { 0x0200, 0 }, + [CSIPHY2_RESET] = { 0x0210, 31 }, + [CSI_PIX1_RESET] = { 0x0210, 30 }, + [CSIPHY0_RESET] = { 0x0210, 29 }, + [CSIPHY1_RESET] = { 0x0210, 28 }, + [CSI_RDI_RESET] = { 0x0210, 27 }, + [CSI_PIX_RESET] = { 0x0210, 26 }, + [DSI2_RESET] = { 0x0210, 25 }, + [VFE_CSI_RESET] = { 0x0210, 24 }, + [MDP_RESET] = { 0x0210, 21 }, + [AMP_RESET] = { 0x0210, 20 }, + [JPEGD_RESET] = { 0x0210, 19 }, + [CSI1_RESET] = { 0x0210, 18 }, + [VPE_RESET] = { 0x0210, 17 }, + [MMSS_FABRIC_RESET] = { 0x0210, 16 }, + [VFE_RESET] = { 0x0210, 15 }, + [GFX3D_RESET] = { 0x0210, 12 }, + [HDMI_RESET] = { 0x0210, 11 }, + [MMSS_IMEM_RESET] = { 0x0210, 10 }, + [IJPEG_RESET] = { 0x0210, 9 }, + [CSI0_RESET] = { 0x0210, 8 }, + [DSI_RESET] = { 0x0210, 7 }, + [VCODEC_RESET] = { 0x0210, 6 }, + [MDP_TV_RESET] = { 0x0210, 4 }, + [MDP_VSYNC_RESET] = { 0x0210, 3 }, + [ROT_RESET] = { 0x0210, 2 }, + [TV_HDMI_RESET] = { 0x0210, 1 }, + [VCAP_NPL_RESET] = { 0x0214, 4 }, + [VCAP_RESET] = { 0x0214, 3 }, + [CSI2_RESET] = { 0x0214, 2 }, + [CSI_RDI1_RESET] = { 0x0214, 1 }, + [CSI_RDI2_RESET] = { 0x0214 }, +}; + static const struct regmap_config mmcc_msm8960_regmap_config = { .reg_bits = 32, .reg_stride = 4, @@ -2220,6 +2609,14 @@ static const struct regmap_config mmcc_msm8960_regmap_config = { .fast_io = true, }; +static const struct regmap_config mmcc_apq8064_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x350, + .fast_io = true, +}; + static const struct qcom_cc_desc mmcc_msm8960_desc = { .config = &mmcc_msm8960_regmap_config, .clks = mmcc_msm8960_clks, @@ -2228,15 +2625,47 @@ static const struct qcom_cc_desc mmcc_msm8960_desc = { .num_resets = ARRAY_SIZE(mmcc_msm8960_resets), }; +static const struct qcom_cc_desc mmcc_apq8064_desc = { + .config = &mmcc_apq8064_regmap_config, + .clks = mmcc_apq8064_clks, + .num_clks = ARRAY_SIZE(mmcc_apq8064_clks), + .resets = mmcc_apq8064_resets, + .num_resets = ARRAY_SIZE(mmcc_apq8064_resets), +}; + static const struct of_device_id mmcc_msm8960_match_table[] = { - { .compatible = "qcom,mmcc-msm8960" }, + { .compatible = "qcom,mmcc-msm8960", .data = &mmcc_msm8960_desc }, + { .compatible = "qcom,mmcc-apq8064", .data = &mmcc_apq8064_desc }, { } }; MODULE_DEVICE_TABLE(of, mmcc_msm8960_match_table); static int mmcc_msm8960_probe(struct platform_device *pdev) { - return qcom_cc_probe(pdev, &mmcc_msm8960_desc); + const struct of_device_id *match; + struct regmap *regmap; + bool is_8064; + struct device *dev = &pdev->dev; + + match = of_match_device(mmcc_msm8960_match_table, dev); + if (!match) + return -EINVAL; + + is_8064 = of_device_is_compatible(dev->of_node, "qcom,mmcc-apq8064"); + if (is_8064) { + gfx3d_src.freq_tbl = clk_tbl_gfx3d_8064; + gfx3d_src.clkr.hw.init = &gfx3d_8064_init; + gfx3d_src.s[0].parent_map = mmcc_pxo_pll8_pll2_pll15_map; + gfx3d_src.s[1].parent_map = mmcc_pxo_pll8_pll2_pll15_map; + } + + regmap = qcom_cc_map(pdev, match->data); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + clk_pll_configure_sr(&pll15, regmap, &pll15_config, false); + + return qcom_cc_really_probe(pdev, match->data, regmap); } static int mmcc_msm8960_remove(struct platform_device *pdev) diff --git a/include/dt-bindings/clock/qcom,mmcc-msm8960.h b/include/dt-bindings/clock/qcom,mmcc-msm8960.h index 5868ef14a777..85041b28f97f 100644 --- a/include/dt-bindings/clock/qcom,mmcc-msm8960.h +++ b/include/dt-bindings/clock/qcom,mmcc-msm8960.h @@ -133,5 +133,13 @@ #define CSIPHY0_TIMER_CLK 116 #define PLL1 117 #define PLL2 118 +#define RGB_TV_CLK 119 +#define NPL_TV_CLK 120 +#define VCAP_AHB_CLK 121 +#define VCAP_AXI_CLK 122 +#define VCAP_SRC 123 +#define VCAP_CLK 124 +#define VCAP_NPL_CLK 125 +#define PLL15 126 #endif diff --git a/include/dt-bindings/reset/qcom,mmcc-msm8960.h b/include/dt-bindings/reset/qcom,mmcc-msm8960.h index ba36ec680118..11741113a841 100644 --- a/include/dt-bindings/reset/qcom,mmcc-msm8960.h +++ b/include/dt-bindings/reset/qcom,mmcc-msm8960.h @@ -89,5 +89,13 @@ #define CSI2_RESET 72 #define CSI_RDI1_RESET 73 #define CSI_RDI2_RESET 74 +#define GFX3D_AXI_RESET 75 +#define VCAP_AXI_RESET 76 +#define SMMU_VCAP_AHB_RESET 77 +#define VCAP_AHB_RESET 78 +#define CSI_RDI_RESET 79 +#define CSI_PIX_RESET 80 +#define VCAP_NPL_RESET 81 +#define VCAP_RESET 82 #endif -- cgit v1.2.3 From ba574dc8563c7c1d1d1c5bf3c1c99ec88513402e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 15 Jul 2014 22:03:22 +0200 Subject: ACPI / hotplug: Simplify acpi_set_hp_context() Since all of the acpi_set_hp_context() callers pass at least one NULL function pointer and one caller passes NULL function pointers only to it, drop function pointer arguments from acpi_set_hp_context() and make the callers initialize the function pointers in struct acpi_hotplug_context by themselves before passing it to acpi_set_hp_context(). Signed-off-by: Rafael J. Wysocki Acked-by: Bjorn Helgaas --- drivers/acpi/scan.c | 4 +++- drivers/pci/hotplug/acpiphp_glue.c | 7 ++++--- include/acpi/acpi_bus.h | 8 +------- 3 files changed, 8 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index f775fa0d850f..3ffc9306538b 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -77,7 +77,9 @@ void acpi_initialize_hp_context(struct acpi_device *adev, void (*uevent)(struct acpi_device *, u32)) { acpi_lock_hp_context(); - acpi_set_hp_context(adev, hp, notify, uevent, NULL); + hp->notify = notify; + hp->uevent = uevent; + acpi_set_hp_context(adev, hp); acpi_unlock_hp_context(); } EXPORT_SYMBOL_GPL(acpi_initialize_hp_context); diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 08043f9c1dd5..24a43d4e9933 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -80,8 +80,9 @@ static struct acpiphp_context *acpiphp_init_context(struct acpi_device *adev) return NULL; context->refcount = 1; - acpi_set_hp_context(adev, &context->hp, acpiphp_hotplug_notify, NULL, - acpiphp_post_dock_fixup); + context->hp.notify = acpiphp_hotplug_notify; + context->hp.fixup = acpiphp_post_dock_fixup; + acpi_set_hp_context(adev, &context->hp); return context; } @@ -876,7 +877,7 @@ void acpiphp_enumerate_slots(struct pci_bus *bus) goto err; root_context->root_bridge = bridge; - acpi_set_hp_context(adev, &root_context->hp, NULL, NULL, NULL); + acpi_set_hp_context(adev, &root_context->hp); } else { struct acpiphp_context *context; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index b5714580801a..e0720a569b2c 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -372,15 +372,9 @@ static inline void acpi_set_device_status(struct acpi_device *adev, u32 sta) } static inline void acpi_set_hp_context(struct acpi_device *adev, - struct acpi_hotplug_context *hp, - int (*notify)(struct acpi_device *, u32), - void (*uevent)(struct acpi_device *, u32), - void (*fixup)(struct acpi_device *)) + struct acpi_hotplug_context *hp) { hp->self = adev; - hp->notify = notify; - hp->uevent = uevent; - hp->fixup = fixup; adev->hp = hp; } -- cgit v1.2.3 From f65f6455fc082c7488af34f6955eb928903c3ae5 Mon Sep 17 00:00:00 2001 From: Kristina Martšenko Date: Wed, 16 Jul 2014 02:30:19 +0300 Subject: ARM: OMAP2+: remove DSP platform device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It was added to support DSP Bridge. Since DSP Bridge was removed, and nothing else is using the platform device, remove it too. Signed-off-by: Kristina Martšenko Cc: Omar Ramirez Luna Cc: Suman Anna Cc: Felipe Contreras Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-omap2/Makefile | 4 - arch/arm/mach-omap2/common.c | 2 - arch/arm/mach-omap2/dsp.c | 134 --------------------------------- include/linux/platform_data/dsp-omap.h | 34 --------- 4 files changed, 174 deletions(-) delete mode 100644 arch/arm/mach-omap2/dsp.c delete mode 100644 include/linux/platform_data/dsp-omap.h (limited to 'include') diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index 8ca99e9321e3..fa7800015753 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile @@ -232,10 +232,6 @@ obj-$(CONFIG_HW_PERF_EVENTS) += pmu.o iommu-$(CONFIG_OMAP_IOMMU) := omap-iommu.o obj-y += $(iommu-m) $(iommu-y) -ifneq ($(CONFIG_TIDSPBRIDGE),) -obj-y += dsp.o -endif - # OMAP2420 MSDI controller integration support ("MMC") obj-$(CONFIG_SOC_OMAP2420) += msdi.o diff --git a/arch/arm/mach-omap2/common.c b/arch/arm/mach-omap2/common.c index 2dabb9ecb986..484cdadfb187 100644 --- a/arch/arm/mach-omap2/common.c +++ b/arch/arm/mach-omap2/common.c @@ -14,7 +14,6 @@ */ #include #include -#include #include "common.h" #include "omap-secure.h" @@ -30,7 +29,6 @@ int __weak omap_secure_ram_reserve_memblock(void) void __init omap_reserve(void) { - omap_dsp_reserve_sdram_memblock(); omap_secure_ram_reserve_memblock(); omap_barrier_reserve_memblock(); } diff --git a/arch/arm/mach-omap2/dsp.c b/arch/arm/mach-omap2/dsp.c deleted file mode 100644 index f7492df1cbba..000000000000 --- a/arch/arm/mach-omap2/dsp.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * TI's OMAP DSP platform device registration - * - * Copyright (C) 2005-2006 Texas Instruments, Inc. - * Copyright (C) 2009 Nokia Corporation - * - * Written by Hiroshi DOYU - * - * 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. - */ - -/* - * XXX The function pointers to the PRM/CM functions are incorrect and - * should be removed. No device driver should be changing PRM/CM bits - * directly; that's a layering violation -- those bits are the responsibility - * of the OMAP PM core code. - */ - -#include -#include - -#include - -#include "control.h" -#include "cm2xxx_3xxx.h" -#include "prm2xxx_3xxx.h" -#ifdef CONFIG_TIDSPBRIDGE_DVFS -#include "omap-pm.h" -#endif -#include "soc.h" - -#include - -static struct platform_device *omap_dsp_pdev; - -static struct omap_dsp_platform_data omap_dsp_pdata __initdata = { -#ifdef CONFIG_TIDSPBRIDGE_DVFS - .dsp_set_min_opp = omap_pm_dsp_set_min_opp, - .dsp_get_opp = omap_pm_dsp_get_opp, - .cpu_set_freq = omap_pm_cpu_set_freq, - .cpu_get_freq = omap_pm_cpu_get_freq, -#endif - .dsp_prm_read = omap2_prm_read_mod_reg, - .dsp_prm_write = omap2_prm_write_mod_reg, - .dsp_prm_rmw_bits = omap2_prm_rmw_mod_reg_bits, - .dsp_cm_read = omap2_cm_read_mod_reg, - .dsp_cm_write = omap2_cm_write_mod_reg, - .dsp_cm_rmw_bits = omap2_cm_rmw_mod_reg_bits, - - .set_bootaddr = omap_ctrl_write_dsp_boot_addr, - .set_bootmode = omap_ctrl_write_dsp_boot_mode, -}; - -static phys_addr_t omap_dsp_phys_mempool_base; - -void __init omap_dsp_reserve_sdram_memblock(void) -{ - phys_addr_t size = CONFIG_TIDSPBRIDGE_MEMPOOL_SIZE; - phys_addr_t paddr; - - if (!cpu_is_omap34xx()) - return; - - if (!size) - return; - - paddr = arm_memblock_steal(size, SZ_1M); - if (!paddr) { - pr_err("%s: failed to reserve %llx bytes\n", - __func__, (unsigned long long)size); - return; - } - - omap_dsp_phys_mempool_base = paddr; -} - -static phys_addr_t omap_dsp_get_mempool_base(void) -{ - return omap_dsp_phys_mempool_base; -} - -static int __init omap_dsp_init(void) -{ - struct platform_device *pdev; - int err = -ENOMEM; - struct omap_dsp_platform_data *pdata = &omap_dsp_pdata; - - if (!cpu_is_omap34xx()) - return 0; - - pdata->phys_mempool_base = omap_dsp_get_mempool_base(); - - if (pdata->phys_mempool_base) { - pdata->phys_mempool_size = CONFIG_TIDSPBRIDGE_MEMPOOL_SIZE; - pr_info("%s: %llx bytes @ %llx\n", __func__, - (unsigned long long)pdata->phys_mempool_size, - (unsigned long long)pdata->phys_mempool_base); - } - - pdev = platform_device_alloc("omap-dsp", -1); - if (!pdev) - goto err_out; - - err = platform_device_add_data(pdev, pdata, sizeof(*pdata)); - if (err) - goto err_out; - - err = platform_device_add(pdev); - if (err) - goto err_out; - - omap_dsp_pdev = pdev; - return 0; - -err_out: - platform_device_put(pdev); - return err; -} -module_init(omap_dsp_init); - -static void __exit omap_dsp_exit(void) -{ - if (!cpu_is_omap34xx()) - return; - - platform_device_unregister(omap_dsp_pdev); -} -module_exit(omap_dsp_exit); - -MODULE_AUTHOR("Hiroshi DOYU"); -MODULE_DESCRIPTION("TI's OMAP DSP platform device registration"); -MODULE_LICENSE("GPL"); diff --git a/include/linux/platform_data/dsp-omap.h b/include/linux/platform_data/dsp-omap.h deleted file mode 100644 index 5927709b1908..000000000000 --- a/include/linux/platform_data/dsp-omap.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef __OMAP_DSP_H__ -#define __OMAP_DSP_H__ - -#include - -struct omap_dsp_platform_data { - void (*dsp_set_min_opp) (u8 opp_id); - u8 (*dsp_get_opp) (void); - void (*cpu_set_freq) (unsigned long f); - unsigned long (*cpu_get_freq) (void); - unsigned long mpu_speed[6]; - - /* functions to write and read PRCM registers */ - void (*dsp_prm_write)(u32, s16 , u16); - u32 (*dsp_prm_read)(s16 , u16); - u32 (*dsp_prm_rmw_bits)(u32, u32, s16, s16); - void (*dsp_cm_write)(u32, s16 , u16); - u32 (*dsp_cm_read)(s16 , u16); - u32 (*dsp_cm_rmw_bits)(u32, u32, s16, s16); - - void (*set_bootaddr)(u32); - void (*set_bootmode)(u8); - - phys_addr_t phys_mempool_base; - phys_addr_t phys_mempool_size; -}; - -#if defined(CONFIG_TIDSPBRIDGE) || defined(CONFIG_TIDSPBRIDGE_MODULE) -extern void omap_dsp_reserve_sdram_memblock(void); -#else -static inline void omap_dsp_reserve_sdram_memblock(void) { } -#endif - -#endif -- cgit v1.2.3 From ba165a90b59812ab1d9cd2943fd104cfc25c601e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 16 Jul 2014 11:42:26 +0300 Subject: Bluetooth: Add proper defines for HCI connection role All HCI commands and events, including LE ones, use 0x00 for master role and 0x01 for slave role. It makes therefore sense to add generic defines for these instead of the current LE_CONN_ROLE_MASTER. Having clean defines will also make it possible to provide simpler internal APIs. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 6 +++--- net/bluetooth/hci_event.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 2fee852816ee..f0a3d8890760 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -401,6 +401,9 @@ enum { /* The core spec defines 127 as the "not available" value */ #define HCI_TX_POWER_INVALID 127 +#define HCI_ROLE_MASTER 0x00 +#define HCI_ROLE_SLAVE 0x01 + /* Extended Inquiry Response field types */ #define EIR_FLAGS 0x01 /* flags */ #define EIR_UUID16_SOME 0x02 /* 16-bit UUID, more available */ @@ -1713,9 +1716,6 @@ struct hci_ev_sync_train_complete { #define HCI_EV_SLAVE_PAGE_RESP_TIMEOUT 0x54 -/* Low energy meta events */ -#define LE_CONN_ROLE_MASTER 0x00 - #define HCI_EV_LE_CONN_COMPLETE 0x01 struct hci_ev_le_conn_complete { __u8 status; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 68d335e193bf..13f83c48face 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4116,7 +4116,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) conn->dst_type = ev->bdaddr_type; - if (ev->role == LE_CONN_ROLE_MASTER) { + if (ev->role == HCI_ROLE_MASTER) { conn->out = true; set_bit(HCI_CONN_MASTER, &conn->flags); } -- cgit v1.2.3 From 40bef302f6323d1ee6fb3dc0e62edb0f446d0339 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 16 Jul 2014 11:42:27 +0300 Subject: Bluetooth: Convert HCI_CONN_MASTER flag to a conn->role variable Having a dedicated u8 role variable in the hci_conn struct greatly simplifies tracking of the role, since this is the native way that it's represented on the HCI level. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_conn.c | 13 ++++++------- net/bluetooth/hci_event.c | 27 +++++++++------------------ net/bluetooth/l2cap_core.c | 4 ++-- net/bluetooth/smp.c | 12 ++++++------ 5 files changed, 24 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b52c2ef3f56d..e335c5fd8824 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -371,6 +371,7 @@ struct hci_conn { __u16 state; __u8 mode; __u8 type; + __u8 role; bool out; __u8 attempt; __u8 dev_class[3]; @@ -540,7 +541,6 @@ enum { HCI_CONN_POWER_SAVE, HCI_CONN_REMOTE_OOB, HCI_CONN_FLUSH_KEY, - HCI_CONN_MASTER, HCI_CONN_ENCRYPT, HCI_CONN_AUTH, HCI_CONN_SECURE, diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 490ee8846d9e..6c1c5048984c 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -66,8 +66,7 @@ static void hci_acl_create_connection(struct hci_conn *conn) conn->state = BT_CONNECT; conn->out = true; - - set_bit(HCI_CONN_MASTER, &conn->flags); + conn->role = HCI_ROLE_MASTER; conn->attempt++; @@ -335,7 +334,7 @@ static void hci_conn_timeout(struct work_struct *work) * event handling and hci_clock_offset_evt function. */ if (conn->type == ACL_LINK && - test_bit(HCI_CONN_MASTER, &conn->flags)) { + conn->role == HCI_ROLE_MASTER) { struct hci_dev *hdev = conn->hdev; struct hci_cp_read_clock_offset cp; @@ -786,8 +785,8 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, goto create_conn; } - conn->out = true; - set_bit(HCI_CONN_MASTER, &conn->flags); + conn->out = true; + conn->role = HCI_ROLE_MASTER; params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); if (params) { @@ -1076,7 +1075,7 @@ int hci_conn_switch_role(struct hci_conn *conn, __u8 role) { BT_DBG("hcon %p", conn); - if (!role && test_bit(HCI_CONN_MASTER, &conn->flags)) + if (role == conn->role) return 1; if (!test_and_set_bit(HCI_CONN_RSWITCH_PEND, &conn->flags)) { @@ -1151,7 +1150,7 @@ static u32 get_link_mode(struct hci_conn *conn) { u32 link_mode = 0; - if (test_bit(HCI_CONN_MASTER, &conn->flags)) + if (conn->role == HCI_ROLE_MASTER) link_mode |= HCI_LM_MASTER; if (test_bit(HCI_CONN_ENCRYPT, &conn->flags)) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 13f83c48face..3b1d2dadedc8 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -101,12 +101,8 @@ static void hci_cc_role_discovery(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle)); - if (conn) { - if (rp->role) - clear_bit(HCI_CONN_MASTER, &conn->flags); - else - set_bit(HCI_CONN_MASTER, &conn->flags); - } + if (conn) + conn->role = rp->role; hci_dev_unlock(hdev); } @@ -1420,8 +1416,8 @@ static void hci_cs_create_conn(struct hci_dev *hdev, __u8 status) if (!conn) { conn = hci_conn_add(hdev, ACL_LINK, &cp->bdaddr); if (conn) { - conn->out = true; - set_bit(HCI_CONN_MASTER, &conn->flags); + conn->out = true; + conn->role = HCI_ROLE_MASTER; } else BT_ERR("No memory for new connection"); } @@ -2924,12 +2920,8 @@ static void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb) conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); if (conn) { - if (!ev->status) { - if (ev->role) - clear_bit(HCI_CONN_MASTER, &conn->flags); - else - set_bit(HCI_CONN_MASTER, &conn->flags); - } + if (!ev->status) + conn->role = ev->role; clear_bit(HCI_CONN_RSWITCH_PEND, &conn->flags); @@ -4116,10 +4108,9 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) conn->dst_type = ev->bdaddr_type; - if (ev->role == HCI_ROLE_MASTER) { + conn->role = ev->role; + if (conn->role == HCI_ROLE_MASTER) conn->out = true; - set_bit(HCI_CONN_MASTER, &conn->flags); - } /* If we didn't have a hci_conn object previously * but we're in master role this must be something @@ -4527,7 +4518,7 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev, return send_conn_param_neg_reply(hdev, handle, HCI_ERROR_INVALID_LL_PARAMS); - if (test_bit(HCI_CONN_MASTER, &hcon->flags)) { + if (hcon->role == HCI_ROLE_MASTER) { struct hci_conn_params *params; u8 store_hint; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 8538cb07b0c0..ea68d3219b7e 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1487,7 +1487,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) * been configured for this connection. If not, then trigger * the connection update procedure. */ - if (!test_bit(HCI_CONN_MASTER, &hcon->flags) && + if (hcon->role == HCI_ROLE_SLAVE && (hcon->le_conn_interval < hcon->le_conn_min_interval || hcon->le_conn_interval > hcon->le_conn_max_interval)) { struct l2cap_conn_param_update_req req; @@ -5227,7 +5227,7 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, u16 min, max, latency, to_multiplier; int err; - if (!test_bit(HCI_CONN_MASTER, &hcon->flags)) + if (hcon->role != HCI_ROLE_MASTER) return -EINVAL; if (cmd_len != sizeof(struct l2cap_conn_param_update_req)) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 8339d6b0f2b8..78eeb8b5970a 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -445,7 +445,7 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, * Confirms and the slave Enters the passkey. */ if (method == OVERLAP) { - if (test_bit(HCI_CONN_MASTER, &hcon->flags)) + if (hcon->role == HCI_ROLE_MASTER) method = CFM_PASSKEY; else method = REQ_PASSKEY; @@ -686,7 +686,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) if (skb->len < sizeof(*req)) return SMP_INVALID_PARAMS; - if (test_bit(HCI_CONN_MASTER, &conn->hcon->flags)) + if (conn->hcon->role != HCI_ROLE_SLAVE) return SMP_CMD_NOTSUPP; if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags)) @@ -755,7 +755,7 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) if (skb->len < sizeof(*rsp)) return SMP_INVALID_PARAMS; - if (!test_bit(HCI_CONN_MASTER, &conn->hcon->flags)) + if (conn->hcon->role != HCI_ROLE_MASTER) return SMP_CMD_NOTSUPP; skb_pull(skb, sizeof(*rsp)); @@ -903,7 +903,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) if (skb->len < sizeof(*rp)) return SMP_INVALID_PARAMS; - if (!test_bit(HCI_CONN_MASTER, &conn->hcon->flags)) + if (hcon->role != HCI_ROLE_MASTER) return SMP_CMD_NOTSUPP; sec_level = authreq_to_seclevel(rp->auth_req); @@ -961,7 +961,7 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level) if (sec_level > hcon->pending_sec_level) hcon->pending_sec_level = sec_level; - if (test_bit(HCI_CONN_MASTER, &hcon->flags)) + if (hcon->role == HCI_ROLE_MASTER) if (smp_ltk_encrypt(conn, hcon->pending_sec_level)) return 0; @@ -981,7 +981,7 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level) hcon->pending_sec_level > BT_SECURITY_MEDIUM) authreq |= SMP_AUTH_MITM; - if (test_bit(HCI_CONN_MASTER, &hcon->flags)) { + if (hcon->role == HCI_ROLE_MASTER) { struct smp_cmd_pairing cp; build_pairing_cmd(conn, &cp, NULL, authreq); -- cgit v1.2.3 From e804d25d4a07c0ff9e5e1c58ea5ee67232aa9af8 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 16 Jul 2014 11:42:28 +0300 Subject: Bluetooth: Use explicit role instead of a bool in function parameters To make the code more understandable it makes sense to use the new HCI defines for connection role instead of a "bool master" parameter. This makes it immediately clear when looking at the function calls what the last parameter is describing. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 6 +++--- net/bluetooth/hci_conn.c | 7 ++++--- net/bluetooth/hci_core.c | 19 +++++++++++-------- net/bluetooth/hci_event.c | 5 ++--- net/bluetooth/l2cap_core.c | 9 ++++++--- net/bluetooth/mgmt.c | 4 ++-- net/bluetooth/smp.c | 4 ++-- 7 files changed, 30 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index e335c5fd8824..abe5083becd3 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -707,7 +707,7 @@ struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle); struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, u8 dst_type, u8 sec_level, u16 conn_timeout, - bool master); + u8 role); struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst, u8 sec_level, u8 auth_type); struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst, @@ -881,12 +881,12 @@ struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len, bool *persistent); struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand, - bool master); + u8 role); struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type, u8 authenticated, u8 tk[16], u8 enc_size, __le16 ediv, __le64 rand); struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 addr_type, bool master); + u8 addr_type, u8 role); int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type); void hci_smp_ltks_clear(struct hci_dev *hdev); int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 6c1c5048984c..6edd55340157 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -697,7 +697,7 @@ static void hci_req_directed_advertising(struct hci_request *req, struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, u8 dst_type, u8 sec_level, u16 conn_timeout, - bool master) + u8 role) { struct hci_conn_params *params; struct hci_conn *conn; @@ -769,8 +769,10 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, &enable); } + conn->role = role; + /* If requested to connect as slave use directed advertising */ - if (!master) { + if (conn->role == HCI_ROLE_SLAVE) { /* If we're active scanning most controllers are unable * to initiate advertising. Simply reject the attempt. */ @@ -786,7 +788,6 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, } conn->out = true; - conn->role = HCI_ROLE_MASTER; params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); if (params) { diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 172041e2b15a..f575abdf2b4e 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3121,13 +3121,16 @@ static bool hci_persistent_key(struct hci_dev *hdev, struct hci_conn *conn, return false; } -static bool ltk_type_master(u8 type) +static u8 ltk_role(u8 type) { - return (type == SMP_LTK); + if (type == SMP_LTK) + return HCI_ROLE_MASTER; + + return HCI_ROLE_SLAVE; } struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand, - bool master) + u8 role) { struct smp_ltk *k; @@ -3135,7 +3138,7 @@ struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand, if (k->ediv != ediv || k->rand != rand) continue; - if (ltk_type_master(k->type) != master) + if (ltk_role(k->type) != role) continue; return k; @@ -3145,14 +3148,14 @@ struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand, } struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 addr_type, bool master) + u8 addr_type, u8 role) { struct smp_ltk *k; list_for_each_entry(k, &hdev->long_term_keys, list) if (addr_type == k->bdaddr_type && bacmp(bdaddr, &k->bdaddr) == 0 && - ltk_type_master(k->type) == master) + ltk_role(k->type) == role) return k; return NULL; @@ -3247,9 +3250,9 @@ struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 tk[16], u8 enc_size, __le16 ediv, __le64 rand) { struct smp_ltk *key, *old_key; - bool master = ltk_type_master(type); + u8 role = ltk_role(type); - old_key = hci_find_ltk_by_addr(hdev, bdaddr, addr_type, master); + old_key = hci_find_ltk_by_addr(hdev, bdaddr, addr_type, role); if (old_key) key = old_key; else { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 3b1d2dadedc8..5f7fd410fb3b 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4263,9 +4263,8 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, return; connect: - /* Request connection in master = true role */ conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW, - HCI_LE_AUTOCONN_TIMEOUT, true); + HCI_LE_AUTOCONN_TIMEOUT, HCI_ROLE_MASTER); if (!IS_ERR(conn)) return; @@ -4443,7 +4442,7 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb) if (conn == NULL) goto not_found; - ltk = hci_find_ltk(hdev, ev->ediv, ev->rand, conn->out); + ltk = hci_find_ltk(hdev, ev->ediv, ev->rand, conn->role); if (ltk == NULL) goto not_found; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index ea68d3219b7e..d0f36336b6ce 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -7128,7 +7128,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, chan->dcid = cid; if (bdaddr_type_is_le(dst_type)) { - bool master; + u8 role; /* Convert from L2CAP channel address type to HCI address type */ @@ -7137,10 +7137,13 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, else dst_type = ADDR_LE_DEV_RANDOM; - master = !test_bit(HCI_ADVERTISING, &hdev->dev_flags); + if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) + role = HCI_ROLE_SLAVE; + else + role = HCI_ROLE_MASTER; hcon = hci_connect_le(hdev, dst, dst_type, chan->sec_level, - HCI_LE_CONN_TIMEOUT, master); + HCI_LE_CONN_TIMEOUT, role); } else { u8 auth_type = l2cap_get_auth_type(chan); hcon = hci_connect_acl(hdev, dst, chan->sec_level, auth_type); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 7703b72653ff..b981bfb87f86 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3154,9 +3154,9 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, */ hci_conn_params_add(hdev, &cp->addr.bdaddr, addr_type); - /* Request a connection with master = true role */ conn = hci_connect_le(hdev, &cp->addr.bdaddr, addr_type, - sec_level, HCI_LE_CONN_TIMEOUT, true); + sec_level, HCI_LE_CONN_TIMEOUT, + HCI_ROLE_MASTER); } if (IS_ERR(conn)) { diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 78eeb8b5970a..70b726518d7b 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -849,7 +849,7 @@ static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level) struct hci_conn *hcon = conn->hcon; key = hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type, - hcon->out); + hcon->role); if (!key) return false; @@ -881,7 +881,7 @@ bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level) */ if (test_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags) && hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type, - hcon->out)) + hcon->role)) return false; if (hcon->sec_level >= sec_level) -- cgit v1.2.3 From a5c4e309b9f23b9de5475029b2cb1641ec293137 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 16 Jul 2014 11:56:07 +0300 Subject: Bluetooth: Add a role parameter to hci_conn_add() We need to be able to track slave vs master LE connections in hci_conn_hash, and to be able to do that we need to know the role of the connection by the time hci_conn_add_has() is called. This means in practice the hci_conn_add() call that creates the hci_conn_object. This patch adds a new role parameter to hci_conn_add() function to give the object its initial role value, and updates the callers to pass the appropriate role to it. Since the function now takes care of initializing both conn->role and conn->out values we can remove some other unnecessary assignments. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 3 ++- net/bluetooth/amp.c | 4 ++-- net/bluetooth/hci_conn.c | 17 +++++++++-------- net/bluetooth/hci_event.c | 17 ++++++----------- 4 files changed, 19 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index abe5083becd3..3de000fbecdc 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -695,7 +695,8 @@ void hci_disconnect(struct hci_conn *conn, __u8 reason); bool hci_setup_sync(struct hci_conn *conn, __u16 handle); void hci_sco_setup(struct hci_conn *conn, __u8 status); -struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst); +struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst, + u8 role); int hci_conn_del(struct hci_conn *conn); void hci_conn_hash_flush(struct hci_dev *hdev); void hci_conn_check_pending(struct hci_dev *hdev); diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index bb39509b3f06..e60603a8969f 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -113,8 +113,9 @@ struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr, { bdaddr_t *dst = &mgr->l2cap_conn->hcon->dst; struct hci_conn *hcon; + u8 role = out ? HCI_ROLE_MASTER : HCI_ROLE_SLAVE; - hcon = hci_conn_add(hdev, AMP_LINK, dst); + hcon = hci_conn_add(hdev, AMP_LINK, dst, role); if (!hcon) return NULL; @@ -125,7 +126,6 @@ struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr, hcon->handle = __next_handle(mgr); hcon->remote_id = remote_id; hcon->amp_mgr = amp_mgr_get(mgr); - hcon->out = out; return hcon; } diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 6edd55340157..ad5f0b819e90 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -421,7 +421,8 @@ static void le_conn_timeout(struct work_struct *work) hci_le_create_connection_cancel(conn); } -struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) +struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst, + u8 role) { struct hci_conn *conn; @@ -435,6 +436,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) bacpy(&conn->src, &hdev->bdaddr); conn->hdev = hdev; conn->type = type; + conn->role = role; conn->mode = HCI_CM_ACTIVE; conn->state = BT_OPEN; conn->auth_type = HCI_AT_GENERAL_BONDING; @@ -447,6 +449,9 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) set_bit(HCI_CONN_POWER_SAVE, &conn->flags); conn->disc_timeout = HCI_DISCONN_TIMEOUT; + if (conn->role == HCI_ROLE_MASTER) + conn->out = true; + switch (type) { case ACL_LINK: conn->pkt_type = hdev->pkt_type & ACL_PTYPE_MASK; @@ -746,7 +751,7 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, dst_type = ADDR_LE_DEV_RANDOM; } - conn = hci_conn_add(hdev, LE_LINK, dst); + conn = hci_conn_add(hdev, LE_LINK, dst, role); if (!conn) return ERR_PTR(-ENOMEM); @@ -769,8 +774,6 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, &enable); } - conn->role = role; - /* If requested to connect as slave use directed advertising */ if (conn->role == HCI_ROLE_SLAVE) { /* If we're active scanning most controllers are unable @@ -787,8 +790,6 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, goto create_conn; } - conn->out = true; - params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); if (params) { conn->le_conn_min_interval = params->conn_min_interval; @@ -837,7 +838,7 @@ struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst, acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); if (!acl) { - acl = hci_conn_add(hdev, ACL_LINK, dst); + acl = hci_conn_add(hdev, ACL_LINK, dst, HCI_ROLE_MASTER); if (!acl) return ERR_PTR(-ENOMEM); } @@ -866,7 +867,7 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst, sco = hci_conn_hash_lookup_ba(hdev, type, dst); if (!sco) { - sco = hci_conn_add(hdev, type, dst); + sco = hci_conn_add(hdev, type, dst, HCI_ROLE_MASTER); if (!sco) { hci_conn_drop(acl); return ERR_PTR(-ENOMEM); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 5f7fd410fb3b..c68b93e11686 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1414,11 +1414,9 @@ static void hci_cs_create_conn(struct hci_dev *hdev, __u8 status) } } else { if (!conn) { - conn = hci_conn_add(hdev, ACL_LINK, &cp->bdaddr); - if (conn) { - conn->out = true; - conn->role = HCI_ROLE_MASTER; - } else + conn = hci_conn_add(hdev, ACL_LINK, &cp->bdaddr, + HCI_ROLE_MASTER); + if (!conn) BT_ERR("No memory for new connection"); } } @@ -2156,7 +2154,8 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr); if (!conn) { - conn = hci_conn_add(hdev, ev->link_type, &ev->bdaddr); + conn = hci_conn_add(hdev, ev->link_type, &ev->bdaddr, + HCI_ROLE_SLAVE); if (!conn) { BT_ERR("No memory for new connection"); hci_dev_unlock(hdev); @@ -4100,7 +4099,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT); if (!conn) { - conn = hci_conn_add(hdev, LE_LINK, &ev->bdaddr); + conn = hci_conn_add(hdev, LE_LINK, &ev->bdaddr, ev->role); if (!conn) { BT_ERR("No memory for new connection"); goto unlock; @@ -4108,10 +4107,6 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) conn->dst_type = ev->bdaddr_type; - conn->role = ev->role; - if (conn->role == HCI_ROLE_MASTER) - conn->out = true; - /* If we didn't have a hci_conn object previously * but we're in master role this must be something * initiated using a white list. Since white list based -- cgit v1.2.3 From f8218dc6605a7b2af843f9ff5d66229a4a0b1c45 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 16 Jul 2014 11:56:08 +0300 Subject: Bluetooth: Track number of LE slave connections Most (probably all) controllers can only deal with a single slave LE connection at a time. This patch adds a counter for such connections so that the number can be quickly looked up without iterating the connections list. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 3de000fbecdc..73e16ecfd6b9 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -83,6 +83,7 @@ struct hci_conn_hash { unsigned int amp_num; unsigned int sco_num; unsigned int le_num; + unsigned int le_num_slave; }; struct bdaddr_list { @@ -575,6 +576,8 @@ static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c) break; case LE_LINK: h->le_num++; + if (c->role == HCI_ROLE_SLAVE) + h->le_num_slave++; break; case SCO_LINK: case ESCO_LINK: @@ -599,6 +602,8 @@ static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c) break; case LE_LINK: h->le_num--; + if (c->role == HCI_ROLE_SLAVE) + h->le_num_slave--; break; case SCO_LINK: case ESCO_LINK: -- cgit v1.2.3 From 046a619d8e9746fa4c0e29e8c6b78e16efc008a8 Mon Sep 17 00:00:00 2001 From: Jason Low Date: Mon, 14 Jul 2014 10:27:48 -0700 Subject: locking/spinlocks/mcs: Rename optimistic_spin_queue() to optimistic_spin_node() Currently, the per-cpu nodes structure for the cancellable MCS spinlock is named "optimistic_spin_queue". However, in a follow up patch in the series we will be introducing a new structure that serves as the new "handle" for the lock. It would make more sense if that structure is named "optimistic_spin_queue". Additionally, since the current use of the "optimistic_spin_queue" structure are "nodes", it might be better if we rename them to "node" anyway. This preparatory patch renames all current "optimistic_spin_queue" to "optimistic_spin_node". Signed-off-by: Jason Low Signed-off-by: Peter Zijlstra Cc: Scott Norton Cc: "Paul E. McKenney" Cc: Dave Chinner Cc: Waiman Long Cc: Davidlohr Bueso Cc: Rik van Riel Cc: Andrew Morton Cc: "H. Peter Anvin" Cc: Steven Rostedt Cc: Tim Chen Cc: Konrad Rzeszutek Wilk Cc: Aswin Chandramouleeswaran Cc: Linus Torvalds Cc: Chris Mason Cc: Heiko Carstens Cc: Josef Bacik Link: http://lkml.kernel.org/r/1405358872-3732-2-git-send-email-jason.low2@hp.com Signed-off-by: Ingo Molnar --- include/linux/mutex.h | 4 ++-- include/linux/rwsem.h | 4 ++-- kernel/locking/mcs_spinlock.c | 24 ++++++++++++------------ kernel/locking/mcs_spinlock.h | 8 ++++---- 4 files changed, 20 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/linux/mutex.h b/include/linux/mutex.h index 11692dea18aa..885f3f56a77f 100644 --- a/include/linux/mutex.h +++ b/include/linux/mutex.h @@ -46,7 +46,7 @@ * - detects multi-task circular deadlocks and prints out all affected * locks and tasks (and only those tasks) */ -struct optimistic_spin_queue; +struct optimistic_spin_node; struct mutex { /* 1: unlocked, 0: locked, negative: locked, possible waiters */ atomic_t count; @@ -56,7 +56,7 @@ struct mutex { struct task_struct *owner; #endif #ifdef CONFIG_MUTEX_SPIN_ON_OWNER - struct optimistic_spin_queue *osq; /* Spinner MCS lock */ + struct optimistic_spin_node *osq; /* Spinner MCS lock */ #endif #ifdef CONFIG_DEBUG_MUTEXES const char *name; diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h index 8d79708146aa..ba3f108ddea1 100644 --- a/include/linux/rwsem.h +++ b/include/linux/rwsem.h @@ -16,7 +16,7 @@ #include -struct optimistic_spin_queue; +struct optimistic_spin_node; struct rw_semaphore; #ifdef CONFIG_RWSEM_GENERIC_SPINLOCK @@ -33,7 +33,7 @@ struct rw_semaphore { * if the owner is running on the cpu. */ struct task_struct *owner; - struct optimistic_spin_queue *osq; /* spinner MCS lock */ + struct optimistic_spin_node *osq; /* spinner MCS lock */ #endif #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map; diff --git a/kernel/locking/mcs_spinlock.c b/kernel/locking/mcs_spinlock.c index 838dc9e00669..e9866f70e828 100644 --- a/kernel/locking/mcs_spinlock.c +++ b/kernel/locking/mcs_spinlock.c @@ -14,18 +14,18 @@ * called from interrupt context and we have preemption disabled while * spinning. */ -static DEFINE_PER_CPU_SHARED_ALIGNED(struct optimistic_spin_queue, osq_node); +static DEFINE_PER_CPU_SHARED_ALIGNED(struct optimistic_spin_node, osq_node); /* * Get a stable @node->next pointer, either for unlock() or unqueue() purposes. * Can return NULL in case we were the last queued and we updated @lock instead. */ -static inline struct optimistic_spin_queue * -osq_wait_next(struct optimistic_spin_queue **lock, - struct optimistic_spin_queue *node, - struct optimistic_spin_queue *prev) +static inline struct optimistic_spin_node * +osq_wait_next(struct optimistic_spin_node **lock, + struct optimistic_spin_node *node, + struct optimistic_spin_node *prev) { - struct optimistic_spin_queue *next = NULL; + struct optimistic_spin_node *next = NULL; for (;;) { if (*lock == node && cmpxchg(lock, node, prev) == node) { @@ -59,10 +59,10 @@ osq_wait_next(struct optimistic_spin_queue **lock, return next; } -bool osq_lock(struct optimistic_spin_queue **lock) +bool osq_lock(struct optimistic_spin_node **lock) { - struct optimistic_spin_queue *node = this_cpu_ptr(&osq_node); - struct optimistic_spin_queue *prev, *next; + struct optimistic_spin_node *node = this_cpu_ptr(&osq_node); + struct optimistic_spin_node *prev, *next; node->locked = 0; node->next = NULL; @@ -149,10 +149,10 @@ unqueue: return false; } -void osq_unlock(struct optimistic_spin_queue **lock) +void osq_unlock(struct optimistic_spin_node **lock) { - struct optimistic_spin_queue *node = this_cpu_ptr(&osq_node); - struct optimistic_spin_queue *next; + struct optimistic_spin_node *node = this_cpu_ptr(&osq_node); + struct optimistic_spin_node *next; /* * Fast path for the uncontended case. diff --git a/kernel/locking/mcs_spinlock.h b/kernel/locking/mcs_spinlock.h index a2dbac4aca6b..c99dc0052f49 100644 --- a/kernel/locking/mcs_spinlock.h +++ b/kernel/locking/mcs_spinlock.h @@ -118,12 +118,12 @@ void mcs_spin_unlock(struct mcs_spinlock **lock, struct mcs_spinlock *node) * mutex_lock()/rwsem_down_{read,write}() etc. */ -struct optimistic_spin_queue { - struct optimistic_spin_queue *next, *prev; +struct optimistic_spin_node { + struct optimistic_spin_node *next, *prev; int locked; /* 1 if lock acquired */ }; -extern bool osq_lock(struct optimistic_spin_queue **lock); -extern void osq_unlock(struct optimistic_spin_queue **lock); +extern bool osq_lock(struct optimistic_spin_node **lock); +extern void osq_unlock(struct optimistic_spin_node **lock); #endif /* __LINUX_MCS_SPINLOCK_H */ -- cgit v1.2.3 From 90631822c5d307b5410500806e8ac3e63928aa3e Mon Sep 17 00:00:00 2001 From: Jason Low Date: Mon, 14 Jul 2014 10:27:49 -0700 Subject: locking/spinlocks/mcs: Convert osq lock to atomic_t to reduce overhead The cancellable MCS spinlock is currently used to queue threads that are doing optimistic spinning. It uses per-cpu nodes, where a thread obtaining the lock would access and queue the local node corresponding to the CPU that it's running on. Currently, the cancellable MCS lock is implemented by using pointers to these nodes. In this patch, instead of operating on pointers to the per-cpu nodes, we store the CPU numbers in which the per-cpu nodes correspond to in atomic_t. A similar concept is used with the qspinlock. By operating on the CPU # of the nodes using atomic_t instead of pointers to those nodes, this can reduce the overhead of the cancellable MCS spinlock by 32 bits (on 64 bit systems). Signed-off-by: Jason Low Signed-off-by: Peter Zijlstra Cc: Scott Norton Cc: "Paul E. McKenney" Cc: Dave Chinner Cc: Waiman Long Cc: Davidlohr Bueso Cc: Rik van Riel Cc: Andrew Morton Cc: "H. Peter Anvin" Cc: Steven Rostedt Cc: Tim Chen Cc: Konrad Rzeszutek Wilk Cc: Aswin Chandramouleeswaran Cc: Linus Torvalds Cc: Chris Mason Cc: Heiko Carstens Cc: Josef Bacik Link: http://lkml.kernel.org/r/1405358872-3732-3-git-send-email-jason.low2@hp.com Signed-off-by: Ingo Molnar --- include/linux/mutex.h | 4 ++-- include/linux/osq_lock.h | 19 ++++++++++++++++++ include/linux/rwsem.h | 7 +++---- kernel/locking/mcs_spinlock.c | 46 ++++++++++++++++++++++++++++++++++++------- kernel/locking/mcs_spinlock.h | 5 +++-- kernel/locking/mutex.c | 2 +- kernel/locking/rwsem-xadd.c | 2 +- 7 files changed, 68 insertions(+), 17 deletions(-) create mode 100644 include/linux/osq_lock.h (limited to 'include') diff --git a/include/linux/mutex.h b/include/linux/mutex.h index 885f3f56a77f..42aa9b9ecd5f 100644 --- a/include/linux/mutex.h +++ b/include/linux/mutex.h @@ -17,6 +17,7 @@ #include #include #include +#include /* * Simple, straightforward mutexes with strict semantics: @@ -46,7 +47,6 @@ * - detects multi-task circular deadlocks and prints out all affected * locks and tasks (and only those tasks) */ -struct optimistic_spin_node; struct mutex { /* 1: unlocked, 0: locked, negative: locked, possible waiters */ atomic_t count; @@ -56,7 +56,7 @@ struct mutex { struct task_struct *owner; #endif #ifdef CONFIG_MUTEX_SPIN_ON_OWNER - struct optimistic_spin_node *osq; /* Spinner MCS lock */ + struct optimistic_spin_queue osq; /* Spinner MCS lock */ #endif #ifdef CONFIG_DEBUG_MUTEXES const char *name; diff --git a/include/linux/osq_lock.h b/include/linux/osq_lock.h new file mode 100644 index 000000000000..b001682bf7cb --- /dev/null +++ b/include/linux/osq_lock.h @@ -0,0 +1,19 @@ +#ifndef __LINUX_OSQ_LOCK_H +#define __LINUX_OSQ_LOCK_H + +/* + * An MCS like lock especially tailored for optimistic spinning for sleeping + * lock implementations (mutex, rwsem, etc). + */ + +#define OSQ_UNLOCKED_VAL (0) + +struct optimistic_spin_queue { + /* + * Stores an encoded value of the CPU # of the tail node in the queue. + * If the queue is empty, then it's set to OSQ_UNLOCKED_VAL. + */ + atomic_t tail; +}; + +#endif diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h index ba3f108ddea1..9fdcdd03507d 100644 --- a/include/linux/rwsem.h +++ b/include/linux/rwsem.h @@ -13,10 +13,9 @@ #include #include #include - #include +#include -struct optimistic_spin_node; struct rw_semaphore; #ifdef CONFIG_RWSEM_GENERIC_SPINLOCK @@ -33,7 +32,7 @@ struct rw_semaphore { * if the owner is running on the cpu. */ struct task_struct *owner; - struct optimistic_spin_node *osq; /* spinner MCS lock */ + struct optimistic_spin_queue osq; /* spinner MCS lock */ #endif #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map; @@ -70,7 +69,7 @@ static inline int rwsem_is_locked(struct rw_semaphore *sem) __RAW_SPIN_LOCK_UNLOCKED(name.wait_lock), \ LIST_HEAD_INIT((name).wait_list), \ NULL, /* owner */ \ - NULL /* mcs lock */ \ + { ATOMIC_INIT(OSQ_UNLOCKED_VAL) } /* osq */ \ __RWSEM_DEP_MAP_INIT(name) } #else #define __RWSEM_INITIALIZER(name) \ diff --git a/kernel/locking/mcs_spinlock.c b/kernel/locking/mcs_spinlock.c index e9866f70e828..32fc16c0a545 100644 --- a/kernel/locking/mcs_spinlock.c +++ b/kernel/locking/mcs_spinlock.c @@ -16,19 +16,45 @@ */ static DEFINE_PER_CPU_SHARED_ALIGNED(struct optimistic_spin_node, osq_node); +/* + * We use the value 0 to represent "no CPU", thus the encoded value + * will be the CPU number incremented by 1. + */ +static inline int encode_cpu(int cpu_nr) +{ + return cpu_nr + 1; +} + +static inline struct optimistic_spin_node *decode_cpu(int encoded_cpu_val) +{ + int cpu_nr = encoded_cpu_val - 1; + + return per_cpu_ptr(&osq_node, cpu_nr); +} + /* * Get a stable @node->next pointer, either for unlock() or unqueue() purposes. * Can return NULL in case we were the last queued and we updated @lock instead. */ static inline struct optimistic_spin_node * -osq_wait_next(struct optimistic_spin_node **lock, +osq_wait_next(struct optimistic_spin_queue *lock, struct optimistic_spin_node *node, struct optimistic_spin_node *prev) { struct optimistic_spin_node *next = NULL; + int curr = encode_cpu(smp_processor_id()); + int old; + + /* + * If there is a prev node in queue, then the 'old' value will be + * the prev node's CPU #, else it's set to OSQ_UNLOCKED_VAL since if + * we're currently last in queue, then the queue will then become empty. + */ + old = prev ? prev->cpu : OSQ_UNLOCKED_VAL; for (;;) { - if (*lock == node && cmpxchg(lock, node, prev) == node) { + if (atomic_read(&lock->tail) == curr && + atomic_cmpxchg(&lock->tail, curr, old) == curr) { /* * We were the last queued, we moved @lock back. @prev * will now observe @lock and will complete its @@ -59,18 +85,23 @@ osq_wait_next(struct optimistic_spin_node **lock, return next; } -bool osq_lock(struct optimistic_spin_node **lock) +bool osq_lock(struct optimistic_spin_queue *lock) { struct optimistic_spin_node *node = this_cpu_ptr(&osq_node); struct optimistic_spin_node *prev, *next; + int curr = encode_cpu(smp_processor_id()); + int old; node->locked = 0; node->next = NULL; + node->cpu = curr; - node->prev = prev = xchg(lock, node); - if (likely(prev == NULL)) + old = atomic_xchg(&lock->tail, curr); + if (old == OSQ_UNLOCKED_VAL) return true; + prev = decode_cpu(old); + node->prev = prev; ACCESS_ONCE(prev->next) = node; /* @@ -149,15 +180,16 @@ unqueue: return false; } -void osq_unlock(struct optimistic_spin_node **lock) +void osq_unlock(struct optimistic_spin_queue *lock) { struct optimistic_spin_node *node = this_cpu_ptr(&osq_node); struct optimistic_spin_node *next; + int curr = encode_cpu(smp_processor_id()); /* * Fast path for the uncontended case. */ - if (likely(cmpxchg(lock, node, NULL) == node)) + if (likely(atomic_cmpxchg(&lock->tail, curr, OSQ_UNLOCKED_VAL) == curr)) return; /* diff --git a/kernel/locking/mcs_spinlock.h b/kernel/locking/mcs_spinlock.h index c99dc0052f49..74356dc0ce29 100644 --- a/kernel/locking/mcs_spinlock.h +++ b/kernel/locking/mcs_spinlock.h @@ -121,9 +121,10 @@ void mcs_spin_unlock(struct mcs_spinlock **lock, struct mcs_spinlock *node) struct optimistic_spin_node { struct optimistic_spin_node *next, *prev; int locked; /* 1 if lock acquired */ + int cpu; /* encoded CPU # value */ }; -extern bool osq_lock(struct optimistic_spin_node **lock); -extern void osq_unlock(struct optimistic_spin_node **lock); +extern bool osq_lock(struct optimistic_spin_queue *lock); +extern void osq_unlock(struct optimistic_spin_queue *lock); #endif /* __LINUX_MCS_SPINLOCK_H */ diff --git a/kernel/locking/mutex.c b/kernel/locking/mutex.c index bc73d33c6760..d9b313906caa 100644 --- a/kernel/locking/mutex.c +++ b/kernel/locking/mutex.c @@ -60,7 +60,7 @@ __mutex_init(struct mutex *lock, const char *name, struct lock_class_key *key) INIT_LIST_HEAD(&lock->wait_list); mutex_clear_owner(lock); #ifdef CONFIG_MUTEX_SPIN_ON_OWNER - lock->osq = NULL; + atomic_set(&lock->osq.tail, OSQ_UNLOCKED_VAL); #endif debug_mutex_init(lock, name, key); diff --git a/kernel/locking/rwsem-xadd.c b/kernel/locking/rwsem-xadd.c index c40c7d28661d..b77a6230bbf6 100644 --- a/kernel/locking/rwsem-xadd.c +++ b/kernel/locking/rwsem-xadd.c @@ -84,7 +84,7 @@ void __init_rwsem(struct rw_semaphore *sem, const char *name, INIT_LIST_HEAD(&sem->wait_list); #ifdef CONFIG_SMP sem->owner = NULL; - sem->osq = NULL; + atomic_set(&sem->osq.tail, OSQ_UNLOCKED_VAL); #endif } -- cgit v1.2.3 From 4d9d951e6b5df85ccfca2c5bd8b4f5c71d256b65 Mon Sep 17 00:00:00 2001 From: Jason Low Date: Mon, 14 Jul 2014 10:27:50 -0700 Subject: locking/spinlocks/mcs: Introduce and use init macro and function for osq locks Currently, we initialize the osq lock by directly setting the lock's values. It would be preferable if we use an init macro to do the initialization like we do with other locks. This patch introduces and uses a macro and function for initializing the osq lock. Signed-off-by: Jason Low Signed-off-by: Peter Zijlstra Cc: Scott Norton Cc: "Paul E. McKenney" Cc: Dave Chinner Cc: Waiman Long Cc: Davidlohr Bueso Cc: Rik van Riel Cc: Andrew Morton Cc: "H. Peter Anvin" Cc: Steven Rostedt Cc: Tim Chen Cc: Konrad Rzeszutek Wilk Cc: Aswin Chandramouleeswaran Cc: Linus Torvalds Cc: Chris Mason Cc: Josef Bacik Link: http://lkml.kernel.org/r/1405358872-3732-4-git-send-email-jason.low2@hp.com Signed-off-by: Ingo Molnar --- include/linux/osq_lock.h | 8 ++++++++ include/linux/rwsem.h | 2 +- kernel/locking/mutex.c | 2 +- kernel/locking/rwsem-xadd.c | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/osq_lock.h b/include/linux/osq_lock.h index b001682bf7cb..90230d5811c5 100644 --- a/include/linux/osq_lock.h +++ b/include/linux/osq_lock.h @@ -16,4 +16,12 @@ struct optimistic_spin_queue { atomic_t tail; }; +/* Init macro and function. */ +#define OSQ_LOCK_UNLOCKED { ATOMIC_INIT(OSQ_UNLOCKED_VAL) } + +static inline void osq_lock_init(struct optimistic_spin_queue *lock) +{ + atomic_set(&lock->tail, OSQ_UNLOCKED_VAL); +} + #endif diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h index 9fdcdd03507d..25cd9aa2f3d7 100644 --- a/include/linux/rwsem.h +++ b/include/linux/rwsem.h @@ -69,7 +69,7 @@ static inline int rwsem_is_locked(struct rw_semaphore *sem) __RAW_SPIN_LOCK_UNLOCKED(name.wait_lock), \ LIST_HEAD_INIT((name).wait_list), \ NULL, /* owner */ \ - { ATOMIC_INIT(OSQ_UNLOCKED_VAL) } /* osq */ \ + OSQ_LOCK_UNLOCKED /* osq */ \ __RWSEM_DEP_MAP_INIT(name) } #else #define __RWSEM_INITIALIZER(name) \ diff --git a/kernel/locking/mutex.c b/kernel/locking/mutex.c index d9b313906caa..acca2c1a3c5e 100644 --- a/kernel/locking/mutex.c +++ b/kernel/locking/mutex.c @@ -60,7 +60,7 @@ __mutex_init(struct mutex *lock, const char *name, struct lock_class_key *key) INIT_LIST_HEAD(&lock->wait_list); mutex_clear_owner(lock); #ifdef CONFIG_MUTEX_SPIN_ON_OWNER - atomic_set(&lock->osq.tail, OSQ_UNLOCKED_VAL); + osq_lock_init(&lock->osq); #endif debug_mutex_init(lock, name, key); diff --git a/kernel/locking/rwsem-xadd.c b/kernel/locking/rwsem-xadd.c index b77a6230bbf6..7190592c2645 100644 --- a/kernel/locking/rwsem-xadd.c +++ b/kernel/locking/rwsem-xadd.c @@ -84,7 +84,7 @@ void __init_rwsem(struct rw_semaphore *sem, const char *name, INIT_LIST_HEAD(&sem->wait_list); #ifdef CONFIG_SMP sem->owner = NULL; - atomic_set(&sem->osq.tail, OSQ_UNLOCKED_VAL); + osq_lock_init(&sem->osq); #endif } -- cgit v1.2.3 From 466af29bf4270e84261712428a1304c28e3743fa Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 6 Jun 2014 18:52:06 +0200 Subject: sched/deadline: Kill task_struct->pi_top_task Remove task_struct->pi_top_task. The only user, rt_mutex_setprio(), can use a local. Signed-off-by: Oleg Nesterov Signed-off-by: Peter Zijlstra Cc: Juri Lelli Cc: Alex Thorlton Cc: Andrew Morton Cc: Daeseok Youn Cc: Dario Faggioli Cc: Davidlohr Bueso Cc: David Rientjes Cc: Eric W. Biederman Cc: Linus Torvalds Cc: Matthew Dempsky Cc: Michal Simek Cc: Oleg Nesterov Link: http://lkml.kernel.org/r/20140606165206.GB29465@redhat.com Signed-off-by: Ingo Molnar --- include/linux/sched.h | 2 -- kernel/fork.c | 1 - kernel/sched/core.c | 6 +++--- 3 files changed, 3 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 306f4f0c987a..c9c9ff723525 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1440,8 +1440,6 @@ struct task_struct { struct rb_node *pi_waiters_leftmost; /* Deadlock detection and priority inheritance handling */ struct rt_mutex_waiter *pi_blocked_on; - /* Top pi_waiters task */ - struct task_struct *pi_top_task; #endif #ifdef CONFIG_DEBUG_MUTEXES diff --git a/kernel/fork.c b/kernel/fork.c index d2799d1fc952..6ff87f4429a4 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1095,7 +1095,6 @@ static void rt_mutex_init_task(struct task_struct *p) p->pi_waiters = RB_ROOT; p->pi_waiters_leftmost = NULL; p->pi_blocked_on = NULL; - p->pi_top_task = NULL; #endif } diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 2dbc63d1a847..cf7695a6c1d2 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2980,7 +2980,6 @@ void rt_mutex_setprio(struct task_struct *p, int prio) } trace_sched_pi_setprio(p, prio); - p->pi_top_task = rt_mutex_get_top_task(p); oldprio = p->prio; prev_class = p->sched_class; on_rq = p->on_rq; @@ -3000,8 +2999,9 @@ void rt_mutex_setprio(struct task_struct *p, int prio) * running task */ if (dl_prio(prio)) { - if (!dl_prio(p->normal_prio) || (p->pi_top_task && - dl_entity_preempt(&p->pi_top_task->dl, &p->dl))) { + struct task_struct *pi_task = rt_mutex_get_top_task(p); + if (!dl_prio(p->normal_prio) || + (pi_task && dl_entity_preempt(&pi_task->dl, &p->dl))) { p->dl.dl_boosted = 1; p->dl.dl_throttled = 0; enqueue_flag = ENQUEUE_REPLENISH; -- cgit v1.2.3 From 8875125efe8402c4d84b08291e68f1281baba8e2 Mon Sep 17 00:00:00 2001 From: Kirill Tkhai Date: Sun, 29 Jun 2014 00:03:57 +0400 Subject: sched: Transform resched_task() into resched_curr() We always use resched_task() with rq->curr argument. It's not possible to reschedule any task but rq's current. The patch introduces resched_curr(struct rq *) to replace all of the repeating patterns. The main aim is cleanup, but there is a little size profit too: (before) $ size kernel/sched/built-in.o text data bss dec hex filename 155274 16445 7042 178761 2ba49 kernel/sched/built-in.o $ size vmlinux text data bss dec hex filename 7411490 1178376 991232 9581098 92322a vmlinux (after) $ size kernel/sched/built-in.o text data bss dec hex filename 155130 16445 7042 178617 2b9b9 kernel/sched/built-in.o $ size vmlinux text data bss dec hex filename 7411362 1178376 991232 9580970 9231aa vmlinux I was choosing between resched_curr() and resched_rq(), and the first name looks better for me. A little lie in Documentation/trace/ftrace.txt. I have not actually collected the tracing again. With a hope the patch won't make execution times much worse :) Signed-off-by: Kirill Tkhai Signed-off-by: Peter Zijlstra Cc: Linus Torvalds Cc: Randy Dunlap Cc: Steven Rostedt Link: http://lkml.kernel.org/r/20140628200219.1778.18735.stgit@localhost Signed-off-by: Ingo Molnar --- Documentation/trace/ftrace.txt | 2 +- include/linux/sched.h | 6 +++--- kernel/sched/core.c | 25 +++++++++++++------------ kernel/sched/deadline.c | 16 ++++++++-------- kernel/sched/fair.c | 20 ++++++++++---------- kernel/sched/idle_task.c | 2 +- kernel/sched/rt.c | 27 ++++++++++++++------------- kernel/sched/sched.h | 2 +- 8 files changed, 51 insertions(+), 49 deletions(-) (limited to 'include') diff --git a/Documentation/trace/ftrace.txt b/Documentation/trace/ftrace.txt index 2479b2a0c77c..4da42616939f 100644 --- a/Documentation/trace/ftrace.txt +++ b/Documentation/trace/ftrace.txt @@ -1515,7 +1515,7 @@ Doing the same with chrt -r 5 and function-trace set. -0 3d.h4 1us+: 0:120:R + [003] 2448: 94:R sleep -0 3d.h4 2us : ttwu_do_activate.constprop.87 <-try_to_wake_up -0 3d.h3 3us : check_preempt_curr <-ttwu_do_wakeup - -0 3d.h3 3us : resched_task <-check_preempt_curr + -0 3d.h3 3us : resched_curr <-check_preempt_curr -0 3dNh3 4us : task_woken_rt <-ttwu_do_wakeup -0 3dNh3 4us : _raw_spin_unlock <-try_to_wake_up -0 3dNh3 4us : sub_preempt_count <-_raw_spin_unlock diff --git a/include/linux/sched.h b/include/linux/sched.h index c9c9ff723525..41a195385081 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2786,7 +2786,7 @@ static inline bool __must_check current_set_polling_and_test(void) /* * Polling state must be visible before we test NEED_RESCHED, - * paired by resched_task() + * paired by resched_curr() */ smp_mb__after_atomic(); @@ -2804,7 +2804,7 @@ static inline bool __must_check current_clr_polling_and_test(void) /* * Polling state must be visible before we test NEED_RESCHED, - * paired by resched_task() + * paired by resched_curr() */ smp_mb__after_atomic(); @@ -2836,7 +2836,7 @@ static inline void current_clr_polling(void) * TIF_NEED_RESCHED and the IPI handler, scheduler_ipi(), will also * fold. */ - smp_mb(); /* paired with resched_task() */ + smp_mb(); /* paired with resched_curr() */ preempt_fold_need_resched(); } diff --git a/kernel/sched/core.c b/kernel/sched/core.c index cf7695a6c1d2..2f960813c582 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -589,30 +589,31 @@ static bool set_nr_if_polling(struct task_struct *p) #endif /* - * resched_task - mark a task 'to be rescheduled now'. + * resched_curr - mark rq's current task 'to be rescheduled now'. * * On UP this means the setting of the need_resched flag, on SMP it * might also involve a cross-CPU call to trigger the scheduler on * the target CPU. */ -void resched_task(struct task_struct *p) +void resched_curr(struct rq *rq) { + struct task_struct *curr = rq->curr; int cpu; - lockdep_assert_held(&task_rq(p)->lock); + lockdep_assert_held(&rq->lock); - if (test_tsk_need_resched(p)) + if (test_tsk_need_resched(curr)) return; - cpu = task_cpu(p); + cpu = cpu_of(rq); if (cpu == smp_processor_id()) { - set_tsk_need_resched(p); + set_tsk_need_resched(curr); set_preempt_need_resched(); return; } - if (set_nr_and_not_polling(p)) + if (set_nr_and_not_polling(curr)) smp_send_reschedule(cpu); else trace_sched_wake_idle_without_ipi(cpu); @@ -625,7 +626,7 @@ void resched_cpu(int cpu) if (!raw_spin_trylock_irqsave(&rq->lock, flags)) return; - resched_task(cpu_curr(cpu)); + resched_curr(rq); raw_spin_unlock_irqrestore(&rq->lock, flags); } @@ -1027,7 +1028,7 @@ void check_preempt_curr(struct rq *rq, struct task_struct *p, int flags) if (class == rq->curr->sched_class) break; if (class == p->sched_class) { - resched_task(rq->curr); + resched_curr(rq); break; } } @@ -3073,7 +3074,7 @@ void set_user_nice(struct task_struct *p, long nice) * lowered its priority, then reschedule its CPU: */ if (delta < 0 || (delta > 0 && task_running(rq, p))) - resched_task(rq->curr); + resched_curr(rq); } out_unlock: task_rq_unlock(rq, p, &flags); @@ -4299,7 +4300,7 @@ again: * fairness. */ if (preempt && rq != p_rq) - resched_task(p_rq->curr); + resched_curr(p_rq); } out_unlock: @@ -7106,7 +7107,7 @@ static void normalize_task(struct rq *rq, struct task_struct *p) __setscheduler(rq, p, &attr); if (on_rq) { enqueue_task(rq, p, 0); - resched_task(rq->curr); + resched_curr(rq); } check_class_changed(rq, p, prev_class, old_prio); diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c index fc4f98b1258f..df0b77a8caca 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -535,7 +535,7 @@ again: if (task_has_dl_policy(rq->curr)) check_preempt_curr_dl(rq, p, 0); else - resched_task(rq->curr); + resched_curr(rq); #ifdef CONFIG_SMP /* * Queueing this task back might have overloaded rq, @@ -634,7 +634,7 @@ static void update_curr_dl(struct rq *rq) enqueue_task_dl(rq, curr, ENQUEUE_REPLENISH); if (!is_leftmost(curr, &rq->dl)) - resched_task(curr); + resched_curr(rq); } /* @@ -964,7 +964,7 @@ static void check_preempt_equal_dl(struct rq *rq, struct task_struct *p) cpudl_find(&rq->rd->cpudl, p, NULL) != -1) return; - resched_task(rq->curr); + resched_curr(rq); } static int pull_dl_task(struct rq *this_rq); @@ -979,7 +979,7 @@ static void check_preempt_curr_dl(struct rq *rq, struct task_struct *p, int flags) { if (dl_entity_preempt(&p->dl, &rq->curr->dl)) { - resched_task(rq->curr); + resched_curr(rq); return; } @@ -1333,7 +1333,7 @@ retry: if (dl_task(rq->curr) && dl_time_before(next_task->dl.deadline, rq->curr->dl.deadline) && rq->curr->nr_cpus_allowed > 1) { - resched_task(rq->curr); + resched_curr(rq); return 0; } @@ -1373,7 +1373,7 @@ retry: set_task_cpu(next_task, later_rq->cpu); activate_task(later_rq, next_task, 0); - resched_task(later_rq->curr); + resched_curr(later_rq); double_unlock_balance(rq, later_rq); @@ -1632,14 +1632,14 @@ static void prio_changed_dl(struct rq *rq, struct task_struct *p, */ if (dl_time_before(rq->dl.earliest_dl.curr, p->dl.deadline) && rq->curr == p) - resched_task(p); + resched_curr(rq); #else /* * Again, we don't know if p has a earlier * or later deadline, so let's blindly set a * (maybe not needed) rescheduling point. */ - resched_task(p); + resched_curr(rq); #endif /* CONFIG_SMP */ } else switched_to_dl(rq, p); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 923fe32db6b3..f5f0cc91518c 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -2923,7 +2923,7 @@ check_preempt_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr) ideal_runtime = sched_slice(cfs_rq, curr); delta_exec = curr->sum_exec_runtime - curr->prev_sum_exec_runtime; if (delta_exec > ideal_runtime) { - resched_task(rq_of(cfs_rq)->curr); + resched_curr(rq_of(cfs_rq)); /* * The current task ran long enough, ensure it doesn't get * re-elected due to buddy favours. @@ -2947,7 +2947,7 @@ check_preempt_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr) return; if (delta > ideal_runtime) - resched_task(rq_of(cfs_rq)->curr); + resched_curr(rq_of(cfs_rq)); } static void @@ -3087,7 +3087,7 @@ entity_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr, int queued) * validating it and just reschedule. */ if (queued) { - resched_task(rq_of(cfs_rq)->curr); + resched_curr(rq_of(cfs_rq)); return; } /* @@ -3278,7 +3278,7 @@ static void __account_cfs_rq_runtime(struct cfs_rq *cfs_rq, u64 delta_exec) * hierarchy can be throttled */ if (!assign_cfs_rq_runtime(cfs_rq) && likely(cfs_rq->curr)) - resched_task(rq_of(cfs_rq)->curr); + resched_curr(rq_of(cfs_rq)); } static __always_inline @@ -3438,7 +3438,7 @@ void unthrottle_cfs_rq(struct cfs_rq *cfs_rq) /* determine whether we need to wake up potentially idle cpu */ if (rq->curr == rq->idle && rq->cfs.nr_running) - resched_task(rq->curr); + resched_curr(rq); } static u64 distribute_cfs_runtime(struct cfs_bandwidth *cfs_b, @@ -3897,7 +3897,7 @@ static void hrtick_start_fair(struct rq *rq, struct task_struct *p) if (delta < 0) { if (rq->curr == p) - resched_task(p); + resched_curr(rq); return; } @@ -4766,7 +4766,7 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int wake_ return; preempt: - resched_task(curr); + resched_curr(rq); /* * Only set the backward buddy when the current task is still * on the rq. This can happen when a wakeup gets interleaved @@ -7457,7 +7457,7 @@ static void task_fork_fair(struct task_struct *p) * 'current' within the tree based on its new key value. */ swap(curr->vruntime, se->vruntime); - resched_task(rq->curr); + resched_curr(rq); } se->vruntime -= cfs_rq->min_vruntime; @@ -7482,7 +7482,7 @@ prio_changed_fair(struct rq *rq, struct task_struct *p, int oldprio) */ if (rq->curr == p) { if (p->prio > oldprio) - resched_task(rq->curr); + resched_curr(rq); } else check_preempt_curr(rq, p, 0); } @@ -7545,7 +7545,7 @@ static void switched_to_fair(struct rq *rq, struct task_struct *p) * if we can still preempt the current task. */ if (rq->curr == p) - resched_task(rq->curr); + resched_curr(rq); else check_preempt_curr(rq, p, 0); } diff --git a/kernel/sched/idle_task.c b/kernel/sched/idle_task.c index 879f2b75266a..67ad4e7f506a 100644 --- a/kernel/sched/idle_task.c +++ b/kernel/sched/idle_task.c @@ -20,7 +20,7 @@ select_task_rq_idle(struct task_struct *p, int cpu, int sd_flag, int flags) */ static void check_preempt_curr_idle(struct rq *rq, struct task_struct *p, int flags) { - resched_task(rq->idle); + resched_curr(rq); } static struct task_struct * diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index 671a8b5fdb6f..5f6edca4fafd 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -463,9 +463,10 @@ static void dequeue_rt_entity(struct sched_rt_entity *rt_se); static void sched_rt_rq_enqueue(struct rt_rq *rt_rq) { struct task_struct *curr = rq_of_rt_rq(rt_rq)->curr; + struct rq *rq = rq_of_rt_rq(rt_rq); struct sched_rt_entity *rt_se; - int cpu = cpu_of(rq_of_rt_rq(rt_rq)); + int cpu = cpu_of(rq); rt_se = rt_rq->tg->rt_se[cpu]; @@ -476,7 +477,7 @@ static void sched_rt_rq_enqueue(struct rt_rq *rt_rq) enqueue_rt_entity(rt_se, false); if (rt_rq->highest_prio.curr < curr->prio) - resched_task(curr); + resched_curr(rq); } } @@ -566,7 +567,7 @@ static inline void sched_rt_rq_enqueue(struct rt_rq *rt_rq) return; enqueue_top_rt_rq(rt_rq); - resched_task(rq->curr); + resched_curr(rq); } static inline void sched_rt_rq_dequeue(struct rt_rq *rt_rq) @@ -951,7 +952,7 @@ static void update_curr_rt(struct rq *rq) raw_spin_lock(&rt_rq->rt_runtime_lock); rt_rq->rt_time += delta_exec; if (sched_rt_runtime_exceeded(rt_rq)) - resched_task(curr); + resched_curr(rq); raw_spin_unlock(&rt_rq->rt_runtime_lock); } } @@ -1366,7 +1367,7 @@ static void check_preempt_equal_prio(struct rq *rq, struct task_struct *p) * to try and push current away: */ requeue_task_rt(rq, p, 1); - resched_task(rq->curr); + resched_curr(rq); } #endif /* CONFIG_SMP */ @@ -1377,7 +1378,7 @@ static void check_preempt_equal_prio(struct rq *rq, struct task_struct *p) static void check_preempt_curr_rt(struct rq *rq, struct task_struct *p, int flags) { if (p->prio < rq->curr->prio) { - resched_task(rq->curr); + resched_curr(rq); return; } @@ -1693,7 +1694,7 @@ retry: * just reschedule current. */ if (unlikely(next_task->prio < rq->curr->prio)) { - resched_task(rq->curr); + resched_curr(rq); return 0; } @@ -1740,7 +1741,7 @@ retry: activate_task(lowest_rq, next_task, 0); ret = 1; - resched_task(lowest_rq->curr); + resched_curr(lowest_rq); double_unlock_balance(rq, lowest_rq); @@ -1939,7 +1940,7 @@ static void switched_from_rt(struct rq *rq, struct task_struct *p) return; if (pull_rt_task(rq)) - resched_task(rq->curr); + resched_curr(rq); } void __init init_sched_rt_class(void) @@ -1977,7 +1978,7 @@ static void switched_to_rt(struct rq *rq, struct task_struct *p) check_resched = 0; #endif /* CONFIG_SMP */ if (check_resched && p->prio < rq->curr->prio) - resched_task(rq->curr); + resched_curr(rq); } } @@ -2006,11 +2007,11 @@ prio_changed_rt(struct rq *rq, struct task_struct *p, int oldprio) * Only reschedule if p is still on the same runqueue. */ if (p->prio > rq->rt.highest_prio.curr && rq->curr == p) - resched_task(p); + resched_curr(rq); #else /* For UP simply resched on drop of prio */ if (oldprio < p->prio) - resched_task(p); + resched_curr(rq); #endif /* CONFIG_SMP */ } else { /* @@ -2019,7 +2020,7 @@ prio_changed_rt(struct rq *rq, struct task_struct *p, int oldprio) * then reschedule. */ if (p->prio < rq->curr->prio) - resched_task(rq->curr); + resched_curr(rq); } } diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 0191ed563bdd..1283945d1ace 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1199,7 +1199,7 @@ extern void init_sched_rt_class(void); extern void init_sched_fair_class(void); extern void init_sched_dl_class(void); -extern void resched_task(struct task_struct *p); +extern void resched_curr(struct rq *rq); extern void resched_cpu(int cpu); extern struct rt_bandwidth def_rt_bandwidth; -- cgit v1.2.3 From 13b9a962a2594ee880c5d50d7f70964da1d4fe5a Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 16 Jul 2014 14:54:55 +0200 Subject: locking/rwsem: Rename 'activity' to 'count' There are two definitions of struct rw_semaphore, one in linux/rwsem.h and one in linux/rwsem-spinlock.h. For some reason they have different names for the initial field. This makes it impossible to use C99 named initialization for __RWSEM_INITIALIZER() -- or we have to duplicate that entire thing along with the structure definitions. The simpler patch is renaming the rwsem-spinlock variant to match the regular rwsem. This allows us to switch to C99 named initialization. Signed-off-by: Peter Zijlstra Cc: Linus Torvalds Link: http://lkml.kernel.org/n/tip-bmrZolsbGmautmzrerog27io@git.kernel.org Signed-off-by: Ingo Molnar --- include/linux/rwsem-spinlock.h | 8 ++++---- kernel/locking/rwsem-spinlock.c | 28 ++++++++++++++-------------- 2 files changed, 18 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/rwsem-spinlock.h b/include/linux/rwsem-spinlock.h index d5b13bc07a0b..561e8615528d 100644 --- a/include/linux/rwsem-spinlock.h +++ b/include/linux/rwsem-spinlock.h @@ -15,13 +15,13 @@ #ifdef __KERNEL__ /* * the rw-semaphore definition - * - if activity is 0 then there are no active readers or writers - * - if activity is +ve then that is the number of active readers - * - if activity is -1 then there is one active writer + * - if count is 0 then there are no active readers or writers + * - if count is +ve then that is the number of active readers + * - if count is -1 then there is one active writer * - if wait_list is not empty, then there are processes waiting for the semaphore */ struct rw_semaphore { - __s32 activity; + __s32 count; raw_spinlock_t wait_lock; struct list_head wait_list; #ifdef CONFIG_DEBUG_LOCK_ALLOC diff --git a/kernel/locking/rwsem-spinlock.c b/kernel/locking/rwsem-spinlock.c index 9be8a9144978..2c93571162cb 100644 --- a/kernel/locking/rwsem-spinlock.c +++ b/kernel/locking/rwsem-spinlock.c @@ -26,7 +26,7 @@ int rwsem_is_locked(struct rw_semaphore *sem) unsigned long flags; if (raw_spin_trylock_irqsave(&sem->wait_lock, flags)) { - ret = (sem->activity != 0); + ret = (sem->count != 0); raw_spin_unlock_irqrestore(&sem->wait_lock, flags); } return ret; @@ -46,7 +46,7 @@ void __init_rwsem(struct rw_semaphore *sem, const char *name, debug_check_no_locks_freed((void *)sem, sizeof(*sem)); lockdep_init_map(&sem->dep_map, name, key, 0); #endif - sem->activity = 0; + sem->count = 0; raw_spin_lock_init(&sem->wait_lock); INIT_LIST_HEAD(&sem->wait_list); } @@ -95,7 +95,7 @@ __rwsem_do_wake(struct rw_semaphore *sem, int wakewrite) waiter = list_entry(next, struct rwsem_waiter, list); } while (waiter->type != RWSEM_WAITING_FOR_WRITE); - sem->activity += woken; + sem->count += woken; out: return sem; @@ -126,9 +126,9 @@ void __sched __down_read(struct rw_semaphore *sem) raw_spin_lock_irqsave(&sem->wait_lock, flags); - if (sem->activity >= 0 && list_empty(&sem->wait_list)) { + if (sem->count >= 0 && list_empty(&sem->wait_list)) { /* granted */ - sem->activity++; + sem->count++; raw_spin_unlock_irqrestore(&sem->wait_lock, flags); goto out; } @@ -170,9 +170,9 @@ int __down_read_trylock(struct rw_semaphore *sem) raw_spin_lock_irqsave(&sem->wait_lock, flags); - if (sem->activity >= 0 && list_empty(&sem->wait_list)) { + if (sem->count >= 0 && list_empty(&sem->wait_list)) { /* granted */ - sem->activity++; + sem->count++; ret = 1; } @@ -206,7 +206,7 @@ void __sched __down_write_nested(struct rw_semaphore *sem, int subclass) * itself into sleep and waiting for system woke it or someone * else in the head of the wait list up. */ - if (sem->activity == 0) + if (sem->count == 0) break; set_task_state(tsk, TASK_UNINTERRUPTIBLE); raw_spin_unlock_irqrestore(&sem->wait_lock, flags); @@ -214,7 +214,7 @@ void __sched __down_write_nested(struct rw_semaphore *sem, int subclass) raw_spin_lock_irqsave(&sem->wait_lock, flags); } /* got the lock */ - sem->activity = -1; + sem->count = -1; list_del(&waiter.list); raw_spin_unlock_irqrestore(&sem->wait_lock, flags); @@ -235,9 +235,9 @@ int __down_write_trylock(struct rw_semaphore *sem) raw_spin_lock_irqsave(&sem->wait_lock, flags); - if (sem->activity == 0) { + if (sem->count == 0) { /* got the lock */ - sem->activity = -1; + sem->count = -1; ret = 1; } @@ -255,7 +255,7 @@ void __up_read(struct rw_semaphore *sem) raw_spin_lock_irqsave(&sem->wait_lock, flags); - if (--sem->activity == 0 && !list_empty(&sem->wait_list)) + if (--sem->count == 0 && !list_empty(&sem->wait_list)) sem = __rwsem_wake_one_writer(sem); raw_spin_unlock_irqrestore(&sem->wait_lock, flags); @@ -270,7 +270,7 @@ void __up_write(struct rw_semaphore *sem) raw_spin_lock_irqsave(&sem->wait_lock, flags); - sem->activity = 0; + sem->count = 0; if (!list_empty(&sem->wait_list)) sem = __rwsem_do_wake(sem, 1); @@ -287,7 +287,7 @@ void __downgrade_write(struct rw_semaphore *sem) raw_spin_lock_irqsave(&sem->wait_lock, flags); - sem->activity = 1; + sem->count = 1; if (!list_empty(&sem->wait_list)) sem = __rwsem_do_wake(sem, 0); -- cgit v1.2.3 From ce069fc920e5734558b3d9cbef1ab06cf01ee793 Mon Sep 17 00:00:00 2001 From: Jason Low Date: Mon, 14 Jul 2014 10:27:52 -0700 Subject: locking/rwsem: Reduce the size of struct rw_semaphore Recent optimistic spinning additions to rwsem provide significant performance benefits on many workloads on large machines. The cost of it was increasing the size of the rwsem structure by up to 128 bits. However, now that the previous patches in this series bring the overhead of struct optimistic_spin_queue to 32 bits, this patch reorders some fields in struct rw_semaphore such that we can reduce the overhead of the rwsem structure by 64 bits (on 64 bit systems). The extra overhead required for rwsem optimistic spinning would now be up to 8 additional bytes instead of up to 16 bytes. Additionally, the size of rwsem would now be more in line with mutexes. Signed-off-by: Jason Low Signed-off-by: Peter Zijlstra Cc: Scott Norton Cc: "Paul E. McKenney" Cc: Dave Chinner Cc: Waiman Long Cc: Davidlohr Bueso Cc: Rik van Riel Cc: Andrew Morton Cc: "H. Peter Anvin" Cc: Steven Rostedt Cc: Tim Chen Cc: Konrad Rzeszutek Wilk Cc: Aswin Chandramouleeswaran Cc: Linus Torvalds Cc: Chris Mason Cc: Josef Bacik Link: http://lkml.kernel.org/r/1405358872-3732-6-git-send-email-jason.low2@hp.com Signed-off-by: Ingo Molnar --- include/linux/rwsem.h | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h index 25cd9aa2f3d7..716807f0eb2d 100644 --- a/include/linux/rwsem.h +++ b/include/linux/rwsem.h @@ -24,15 +24,15 @@ struct rw_semaphore; /* All arch specific implementations share the same struct */ struct rw_semaphore { long count; - raw_spinlock_t wait_lock; struct list_head wait_list; + raw_spinlock_t wait_lock; #ifdef CONFIG_SMP + struct optimistic_spin_queue osq; /* spinner MCS lock */ /* * Write owner. Used as a speculative check to see * if the owner is running on the cpu. */ struct task_struct *owner; - struct optimistic_spin_queue osq; /* spinner MCS lock */ #endif #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map; @@ -64,21 +64,18 @@ static inline int rwsem_is_locked(struct rw_semaphore *sem) #endif #if defined(CONFIG_SMP) && !defined(CONFIG_RWSEM_GENERIC_SPINLOCK) -#define __RWSEM_INITIALIZER(name) \ - { RWSEM_UNLOCKED_VALUE, \ - __RAW_SPIN_LOCK_UNLOCKED(name.wait_lock), \ - LIST_HEAD_INIT((name).wait_list), \ - NULL, /* owner */ \ - OSQ_LOCK_UNLOCKED /* osq */ \ - __RWSEM_DEP_MAP_INIT(name) } +#define __RWSEM_OPT_INIT(lockname) , .osq = OSQ_LOCK_UNLOCKED, .owner = NULL #else -#define __RWSEM_INITIALIZER(name) \ - { RWSEM_UNLOCKED_VALUE, \ - __RAW_SPIN_LOCK_UNLOCKED(name.wait_lock), \ - LIST_HEAD_INIT((name).wait_list) \ - __RWSEM_DEP_MAP_INIT(name) } +#define __RWSEM_OPT_INIT(lockname) #endif +#define __RWSEM_INITIALIZER(name) \ + { .count = RWSEM_UNLOCKED_VALUE, \ + .wait_list = LIST_HEAD_INIT((name).wait_list), \ + .wait_lock = __RAW_SPIN_LOCK_UNLOCKED(name.wait_lock) \ + __RWSEM_OPT_INIT(name) \ + __RWSEM_DEP_MAP_INIT(name) } + #define DECLARE_RWSEM(name) \ struct rw_semaphore name = __RWSEM_INITIALIZER(name) -- cgit v1.2.3 From 5db6c6fefb1ca0e81e3bd6dd8998bf51c453d823 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Fri, 11 Jul 2014 14:00:06 -0700 Subject: locking/rwsem: Add CONFIG_RWSEM_SPIN_ON_OWNER Just like with mutexes (CONFIG_MUTEX_SPIN_ON_OWNER), encapsulate the dependencies for rwsem optimistic spinning. No logical changes here as it continues to depend on both SMP and the XADD algorithm variant. Signed-off-by: Davidlohr Bueso Acked-by: Jason Low [ Also make it depend on ARCH_SUPPORTS_ATOMIC_RMW. ] Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1405112406-13052-2-git-send-email-davidlohr@hp.com Cc: aswin@hp.com Cc: Chris Mason Cc: Davidlohr Bueso Cc: Josef Bacik Cc: Linus Torvalds Cc: Waiman Long Signed-off-by: Ingo Molnar Signed-off-by: Ingo Molnar --- include/linux/rwsem.h | 6 ++++-- kernel/Kconfig.locks | 4 ++++ kernel/locking/rwsem-xadd.c | 4 ++-- kernel/locking/rwsem.c | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h index 716807f0eb2d..035d3c57fc8a 100644 --- a/include/linux/rwsem.h +++ b/include/linux/rwsem.h @@ -14,7 +14,9 @@ #include #include #include +#ifdef CONFIG_RWSEM_SPIN_ON_OWNER #include +#endif struct rw_semaphore; @@ -26,7 +28,7 @@ struct rw_semaphore { long count; struct list_head wait_list; raw_spinlock_t wait_lock; -#ifdef CONFIG_SMP +#ifdef CONFIG_RWSEM_SPIN_ON_OWNER struct optimistic_spin_queue osq; /* spinner MCS lock */ /* * Write owner. Used as a speculative check to see @@ -63,7 +65,7 @@ static inline int rwsem_is_locked(struct rw_semaphore *sem) # define __RWSEM_DEP_MAP_INIT(lockname) #endif -#if defined(CONFIG_SMP) && !defined(CONFIG_RWSEM_GENERIC_SPINLOCK) +#ifdef CONFIG_RWSEM_SPIN_ON_OWNER #define __RWSEM_OPT_INIT(lockname) , .osq = OSQ_LOCK_UNLOCKED, .owner = NULL #else #define __RWSEM_OPT_INIT(lockname) diff --git a/kernel/Kconfig.locks b/kernel/Kconfig.locks index 81907941d921..76768ee812b2 100644 --- a/kernel/Kconfig.locks +++ b/kernel/Kconfig.locks @@ -227,6 +227,10 @@ config MUTEX_SPIN_ON_OWNER def_bool y depends on SMP && !DEBUG_MUTEXES && ARCH_SUPPORTS_ATOMIC_RMW +config RWSEM_SPIN_ON_OWNER + def_bool y + depends on SMP && RWSEM_XCHGADD_ALGORITHM && ARCH_SUPPORTS_ATOMIC_RMW + config ARCH_USE_QUEUE_RWLOCK bool diff --git a/kernel/locking/rwsem-xadd.c b/kernel/locking/rwsem-xadd.c index 7190592c2645..a2391ac135c8 100644 --- a/kernel/locking/rwsem-xadd.c +++ b/kernel/locking/rwsem-xadd.c @@ -82,7 +82,7 @@ void __init_rwsem(struct rw_semaphore *sem, const char *name, sem->count = RWSEM_UNLOCKED_VALUE; raw_spin_lock_init(&sem->wait_lock); INIT_LIST_HEAD(&sem->wait_list); -#ifdef CONFIG_SMP +#ifdef CONFIG_RWSEM_SPIN_ON_OWNER sem->owner = NULL; osq_lock_init(&sem->osq); #endif @@ -262,7 +262,7 @@ static inline bool rwsem_try_write_lock(long count, struct rw_semaphore *sem) return false; } -#ifdef CONFIG_SMP +#ifdef CONFIG_RWSEM_SPIN_ON_OWNER /* * Try to acquire write lock before the writer has been put on wait queue. */ diff --git a/kernel/locking/rwsem.c b/kernel/locking/rwsem.c index 42f806de49d4..e2d3bc7f03b4 100644 --- a/kernel/locking/rwsem.c +++ b/kernel/locking/rwsem.c @@ -12,7 +12,7 @@ #include -#if defined(CONFIG_SMP) && defined(CONFIG_RWSEM_XCHGADD_ALGORITHM) +#ifdef CONFIG_RWSEM_SPIN_ON_OWNER static inline void rwsem_set_owner(struct rw_semaphore *sem) { sem->owner = current; -- cgit v1.2.3 From a509ea840b8e29e512764803e30b805c7ea89038 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Wed, 9 Jul 2014 17:45:10 +0200 Subject: ARM: mvebu: extend PMSU code to support dynamic frequency scaling This commit adds the necessary code in the Marvell EBU PMSU driver to support dynamic frequency scaling. In essence, what this new code does is that it: * registers the frequency operating points supported by the CPU; * registers a clock notifier of the CPU clocks. The notifier function listens to the newly introduced APPLY_RATE_CHANGE event, and uses that to finalize the frequency transition by doing the part of the procedure that involves the PMSU; * registers a platform device for the cpufreq-generic driver, which will take care of the CPU frequency transitions. Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1404920715-19834-3-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/pmsu.c | 162 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/mvebu-pmsu.h | 20 ++++++ 2 files changed, 182 insertions(+) create mode 100644 include/linux/mvebu-pmsu.h (limited to 'include') diff --git a/arch/arm/mach-mvebu/pmsu.c b/arch/arm/mach-mvebu/pmsu.c index 53a55c8520bf..db7d9ab298b6 100644 --- a/arch/arm/mach-mvebu/pmsu.c +++ b/arch/arm/mach-mvebu/pmsu.c @@ -18,20 +18,26 @@ #define pr_fmt(fmt) "mvebu-pmsu: " fmt +#include #include +#include #include #include #include +#include #include #include +#include #include #include +#include #include #include #include #include #include #include "common.h" +#include "armada-370-xp.h" static void __iomem *pmsu_mp_base; @@ -57,6 +63,10 @@ static void __iomem *pmsu_mp_base; #define PMSU_STATUS_AND_MASK_IRQ_MASK BIT(24) #define PMSU_STATUS_AND_MASK_FIQ_MASK BIT(25) +#define PMSU_EVENT_STATUS_AND_MASK(cpu) ((cpu * 0x100) + 0x120) +#define PMSU_EVENT_STATUS_AND_MASK_DFS_DONE BIT(1) +#define PMSU_EVENT_STATUS_AND_MASK_DFS_DONE_MASK BIT(17) + #define PMSU_BOOT_ADDR_REDIRECT_OFFSET(cpu) ((cpu * 0x100) + 0x124) /* PMSU fabric registers */ @@ -296,3 +306,155 @@ int __init armada_370_xp_cpu_pm_init(void) arch_initcall(armada_370_xp_cpu_pm_init); early_initcall(armada_370_xp_pmsu_init); + +static void mvebu_pmsu_dfs_request_local(void *data) +{ + u32 reg; + u32 cpu = smp_processor_id(); + unsigned long flags; + + local_irq_save(flags); + + /* Prepare to enter idle */ + reg = readl(pmsu_mp_base + PMSU_STATUS_AND_MASK(cpu)); + reg |= PMSU_STATUS_AND_MASK_CPU_IDLE_WAIT | + PMSU_STATUS_AND_MASK_IRQ_MASK | + PMSU_STATUS_AND_MASK_FIQ_MASK; + writel(reg, pmsu_mp_base + PMSU_STATUS_AND_MASK(cpu)); + + /* Request the DFS transition */ + reg = readl(pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(cpu)); + reg |= PMSU_CONTROL_AND_CONFIG_DFS_REQ; + writel(reg, pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(cpu)); + + /* The fact of entering idle will trigger the DFS transition */ + wfi(); + + /* + * We're back from idle, the DFS transition has completed, + * clear the idle wait indication. + */ + reg = readl(pmsu_mp_base + PMSU_STATUS_AND_MASK(cpu)); + reg &= ~PMSU_STATUS_AND_MASK_CPU_IDLE_WAIT; + writel(reg, pmsu_mp_base + PMSU_STATUS_AND_MASK(cpu)); + + local_irq_restore(flags); +} + +int mvebu_pmsu_dfs_request(int cpu) +{ + unsigned long timeout; + int hwcpu = cpu_logical_map(cpu); + u32 reg; + + /* Clear any previous DFS DONE event */ + reg = readl(pmsu_mp_base + PMSU_EVENT_STATUS_AND_MASK(hwcpu)); + reg &= ~PMSU_EVENT_STATUS_AND_MASK_DFS_DONE; + writel(reg, pmsu_mp_base + PMSU_EVENT_STATUS_AND_MASK(hwcpu)); + + /* Mask the DFS done interrupt, since we are going to poll */ + reg = readl(pmsu_mp_base + PMSU_EVENT_STATUS_AND_MASK(hwcpu)); + reg |= PMSU_EVENT_STATUS_AND_MASK_DFS_DONE_MASK; + writel(reg, pmsu_mp_base + PMSU_EVENT_STATUS_AND_MASK(hwcpu)); + + /* Trigger the DFS on the appropriate CPU */ + smp_call_function_single(cpu, mvebu_pmsu_dfs_request_local, + NULL, false); + + /* Poll until the DFS done event is generated */ + timeout = jiffies + HZ; + while (time_before(jiffies, timeout)) { + reg = readl(pmsu_mp_base + PMSU_EVENT_STATUS_AND_MASK(hwcpu)); + if (reg & PMSU_EVENT_STATUS_AND_MASK_DFS_DONE) + break; + udelay(10); + } + + if (time_after(jiffies, timeout)) + return -ETIME; + + /* Restore the DFS mask to its original state */ + reg = readl(pmsu_mp_base + PMSU_EVENT_STATUS_AND_MASK(hwcpu)); + reg &= ~PMSU_EVENT_STATUS_AND_MASK_DFS_DONE_MASK; + writel(reg, pmsu_mp_base + PMSU_EVENT_STATUS_AND_MASK(hwcpu)); + + return 0; +} + +static int __init armada_xp_pmsu_cpufreq_init(void) +{ + struct device_node *np; + struct resource res; + int ret, cpu; + + if (!of_machine_is_compatible("marvell,armadaxp")) + return 0; + + /* + * In order to have proper cpufreq handling, we need to ensure + * that the Device Tree description of the CPU clock includes + * the definition of the PMU DFS registers. If not, we do not + * register the clock notifier and the cpufreq driver. This + * piece of code is only for compatibility with old Device + * Trees. + */ + np = of_find_compatible_node(NULL, NULL, "marvell,armada-xp-cpu-clock"); + if (!np) + return 0; + + ret = of_address_to_resource(np, 1, &res); + if (ret) { + pr_warn(FW_WARN "not enabling cpufreq, deprecated armada-xp-cpu-clock binding\n"); + of_node_put(np); + return 0; + } + + of_node_put(np); + + /* + * For each CPU, this loop registers the operating points + * supported (which are the nominal CPU frequency and half of + * it), and registers the clock notifier that will take care + * of doing the PMSU part of a frequency transition. + */ + for_each_possible_cpu(cpu) { + struct device *cpu_dev; + struct clk *clk; + int ret; + + cpu_dev = get_cpu_device(cpu); + if (!cpu_dev) { + pr_err("Cannot get CPU %d\n", cpu); + continue; + } + + clk = clk_get(cpu_dev, 0); + if (!clk) { + pr_err("Cannot get clock for CPU %d\n", cpu); + return -ENODEV; + } + + /* + * In case of a failure of dev_pm_opp_add(), we don't + * bother with cleaning up the registered OPP (there's + * no function to do so), and simply cancel the + * registration of the cpufreq device. + */ + ret = dev_pm_opp_add(cpu_dev, clk_get_rate(clk), 0); + if (ret) { + clk_put(clk); + return ret; + } + + ret = dev_pm_opp_add(cpu_dev, clk_get_rate(clk) / 2, 0); + if (ret) { + clk_put(clk); + return ret; + } + } + + platform_device_register_simple("cpufreq-generic", -1, NULL, 0); + return 0; +} + +device_initcall(armada_xp_pmsu_cpufreq_init); diff --git a/include/linux/mvebu-pmsu.h b/include/linux/mvebu-pmsu.h new file mode 100644 index 000000000000..b918d07efe23 --- /dev/null +++ b/include/linux/mvebu-pmsu.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2012 Marvell + * + * 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. + */ + +#ifndef __MVEBU_PMSU_H__ +#define __MVEBU_PMSU_H__ + +#ifdef CONFIG_MACH_MVEBU_V7 +int mvebu_pmsu_dfs_request(int cpu); +#else +static inline int mvebu_pmsu_dfs_request(int cpu) { return -ENODEV; } +#endif + +#endif /* __MVEBU_PMSU_H__ */ -- cgit v1.2.3 From 743162013d40ca612b4cb53d3a200dff2d9ab26e Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 7 Jul 2014 15:16:04 +1000 Subject: sched: Remove proliferation of wait_on_bit() action functions The current "wait_on_bit" interface requires an 'action' function to be provided which does the actual waiting. There are over 20 such functions, many of them identical. Most cases can be satisfied by one of just two functions, one which uses io_schedule() and one which just uses schedule(). So: Rename wait_on_bit and wait_on_bit_lock to wait_on_bit_action and wait_on_bit_lock_action to make it explicit that they need an action function. Introduce new wait_on_bit{,_lock} and wait_on_bit{,_lock}_io which are *not* given an action function but implicitly use a standard one. The decision to error-out if a signal is pending is now made based on the 'mode' argument rather than being encoded in the action function. All instances of the old wait_on_bit and wait_on_bit_lock which can use the new version have been changed accordingly and their action functions have been discarded. wait_on_bit{_lock} does not return any specific error code in the event of a signal so the caller must check for non-zero and interpolate their own error code as appropriate. The wait_on_bit() call in __fscache_wait_on_invalidate() was ambiguous as it specified TASK_UNINTERRUPTIBLE but used fscache_wait_bit_interruptible as an action function. David Howells confirms this should be uniformly "uninterruptible" The main remaining user of wait_on_bit{,_lock}_action is NFS which needs to use a freezer-aware schedule() call. A comment in fs/gfs2/glock.c notes that having multiple 'action' functions is useful as they display differently in the 'wchan' field of 'ps'. (and /proc/$PID/wchan). As the new bit_wait{,_io} functions are tagged "__sched", they will not show up at all, but something higher in the stack. So the distinction will still be visible, only with different function names (gds2_glock_wait versus gfs2_glock_dq_wait in the gfs2/glock.c case). Since first version of this patch (against 3.15) two new action functions appeared, on in NFS and one in CIFS. CIFS also now uses an action function that makes the same freezer aware schedule call as NFS. Signed-off-by: NeilBrown Acked-by: David Howells (fscache, keys) Acked-by: Steven Whitehouse (gfs2) Acked-by: Peter Zijlstra Cc: Oleg Nesterov Cc: Steve French Cc: Linus Torvalds Link: http://lkml.kernel.org/r/20140707051603.28027.72349.stgit@notabene.brown Signed-off-by: Ingo Molnar --- Documentation/filesystems/caching/operations.txt | 2 +- drivers/md/dm-bufio.c | 41 +++----- drivers/md/dm-snap.c | 10 +- drivers/media/usb/dvb-usb-v2/dvb_usb_core.c | 12 +-- fs/btrfs/extent_io.c | 10 +- fs/buffer.c | 11 +-- fs/cifs/connect.c | 10 +- fs/cifs/file.c | 9 +- fs/cifs/inode.c | 4 +- fs/cifs/misc.c | 2 +- fs/fs-writeback.c | 3 +- fs/fscache/cookie.c | 7 +- fs/fscache/internal.h | 2 - fs/fscache/main.c | 18 ---- fs/fscache/page.c | 4 +- fs/gfs2/glock.c | 25 +---- fs/gfs2/lock_dlm.c | 8 +- fs/gfs2/ops_fstype.c | 11 +-- fs/gfs2/recovery.c | 8 +- fs/gfs2/super.c | 8 +- fs/inode.c | 7 -- fs/jbd2/transaction.c | 10 +- fs/nfs/file.c | 4 +- fs/nfs/filelayout/filelayoutdev.c | 4 +- fs/nfs/inode.c | 4 +- fs/nfs/nfs4state.c | 4 +- fs/nfs/pagelist.c | 12 +-- fs/nfs/pnfs.c | 2 +- fs/nfs/write.c | 4 +- include/linux/wait.h | 115 ++++++++++++++++++++++- include/linux/writeback.h | 3 +- kernel/ptrace.c | 8 +- kernel/sched/wait.c | 18 ++++ mm/filemap.c | 20 +--- mm/ksm.c | 8 +- net/bluetooth/hci_core.c | 8 +- security/keys/gc.c | 11 +-- security/keys/request_key.c | 23 +---- 38 files changed, 195 insertions(+), 275 deletions(-) (limited to 'include') diff --git a/Documentation/filesystems/caching/operations.txt b/Documentation/filesystems/caching/operations.txt index bee2a5f93d60..a1c052cbba35 100644 --- a/Documentation/filesystems/caching/operations.txt +++ b/Documentation/filesystems/caching/operations.txt @@ -90,7 +90,7 @@ operations: to be cleared before proceeding: wait_on_bit(&op->flags, FSCACHE_OP_WAITING, - fscache_wait_bit, TASK_UNINTERRUPTIBLE); + TASK_UNINTERRUPTIBLE); (2) The operation may be fast asynchronous (FSCACHE_OP_FAST), in which case it diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c index 4e84095833db..96c92b75452f 100644 --- a/drivers/md/dm-bufio.c +++ b/drivers/md/dm-bufio.c @@ -614,16 +614,6 @@ static void write_endio(struct bio *bio, int error) wake_up_bit(&b->state, B_WRITING); } -/* - * This function is called when wait_on_bit is actually waiting. - */ -static int do_io_schedule(void *word) -{ - io_schedule(); - - return 0; -} - /* * Initiate a write on a dirty buffer, but don't wait for it. * @@ -640,8 +630,7 @@ static void __write_dirty_buffer(struct dm_buffer *b, return; clear_bit(B_DIRTY, &b->state); - wait_on_bit_lock(&b->state, B_WRITING, - do_io_schedule, TASK_UNINTERRUPTIBLE); + wait_on_bit_lock_io(&b->state, B_WRITING, TASK_UNINTERRUPTIBLE); if (!write_list) submit_io(b, WRITE, b->block, write_endio); @@ -675,9 +664,9 @@ static void __make_buffer_clean(struct dm_buffer *b) if (!b->state) /* fast case */ return; - wait_on_bit(&b->state, B_READING, do_io_schedule, TASK_UNINTERRUPTIBLE); + wait_on_bit_io(&b->state, B_READING, TASK_UNINTERRUPTIBLE); __write_dirty_buffer(b, NULL); - wait_on_bit(&b->state, B_WRITING, do_io_schedule, TASK_UNINTERRUPTIBLE); + wait_on_bit_io(&b->state, B_WRITING, TASK_UNINTERRUPTIBLE); } /* @@ -1030,7 +1019,7 @@ static void *new_read(struct dm_bufio_client *c, sector_t block, if (need_submit) submit_io(b, READ, b->block, read_endio); - wait_on_bit(&b->state, B_READING, do_io_schedule, TASK_UNINTERRUPTIBLE); + wait_on_bit_io(&b->state, B_READING, TASK_UNINTERRUPTIBLE); if (b->read_error) { int error = b->read_error; @@ -1209,15 +1198,13 @@ again: dropped_lock = 1; b->hold_count++; dm_bufio_unlock(c); - wait_on_bit(&b->state, B_WRITING, - do_io_schedule, - TASK_UNINTERRUPTIBLE); + wait_on_bit_io(&b->state, B_WRITING, + TASK_UNINTERRUPTIBLE); dm_bufio_lock(c); b->hold_count--; } else - wait_on_bit(&b->state, B_WRITING, - do_io_schedule, - TASK_UNINTERRUPTIBLE); + wait_on_bit_io(&b->state, B_WRITING, + TASK_UNINTERRUPTIBLE); } if (!test_bit(B_DIRTY, &b->state) && @@ -1321,15 +1308,15 @@ retry: __write_dirty_buffer(b, NULL); if (b->hold_count == 1) { - wait_on_bit(&b->state, B_WRITING, - do_io_schedule, TASK_UNINTERRUPTIBLE); + wait_on_bit_io(&b->state, B_WRITING, + TASK_UNINTERRUPTIBLE); set_bit(B_DIRTY, &b->state); __unlink_buffer(b); __link_buffer(b, new_block, LIST_DIRTY); } else { sector_t old_block; - wait_on_bit_lock(&b->state, B_WRITING, - do_io_schedule, TASK_UNINTERRUPTIBLE); + wait_on_bit_lock_io(&b->state, B_WRITING, + TASK_UNINTERRUPTIBLE); /* * Relink buffer to "new_block" so that write_callback * sees "new_block" as a block number. @@ -1341,8 +1328,8 @@ retry: __unlink_buffer(b); __link_buffer(b, new_block, b->list_mode); submit_io(b, WRITE, new_block, write_endio); - wait_on_bit(&b->state, B_WRITING, - do_io_schedule, TASK_UNINTERRUPTIBLE); + wait_on_bit_io(&b->state, B_WRITING, + TASK_UNINTERRUPTIBLE); __unlink_buffer(b); __link_buffer(b, old_block, b->list_mode); } diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index 5bd2290cfb1e..864b03f47727 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c @@ -1032,21 +1032,13 @@ static void start_merge(struct dm_snapshot *s) snapshot_merge_next_chunks(s); } -static int wait_schedule(void *ptr) -{ - schedule(); - - return 0; -} - /* * Stop the merging process and wait until it finishes. */ static void stop_merge(struct dm_snapshot *s) { set_bit(SHUTDOWN_MERGE, &s->state_bits); - wait_on_bit(&s->state_bits, RUNNING_MERGE, wait_schedule, - TASK_UNINTERRUPTIBLE); + wait_on_bit(&s->state_bits, RUNNING_MERGE, TASK_UNINTERRUPTIBLE); clear_bit(SHUTDOWN_MERGE, &s->state_bits); } diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index e35580618936..f296394bb7c5 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -253,13 +253,6 @@ static int dvb_usbv2_adapter_stream_exit(struct dvb_usb_adapter *adap) return usb_urb_exitv2(&adap->stream); } -static int wait_schedule(void *ptr) -{ - schedule(); - - return 0; -} - static int dvb_usb_start_feed(struct dvb_demux_feed *dvbdmxfeed) { struct dvb_usb_adapter *adap = dvbdmxfeed->demux->priv; @@ -273,8 +266,7 @@ static int dvb_usb_start_feed(struct dvb_demux_feed *dvbdmxfeed) dvbdmxfeed->pid, dvbdmxfeed->index); /* wait init is done */ - wait_on_bit(&adap->state_bits, ADAP_INIT, wait_schedule, - TASK_UNINTERRUPTIBLE); + wait_on_bit(&adap->state_bits, ADAP_INIT, TASK_UNINTERRUPTIBLE); if (adap->active_fe == -1) return -EINVAL; @@ -568,7 +560,7 @@ static int dvb_usb_fe_sleep(struct dvb_frontend *fe) if (!adap->suspend_resume_active) { set_bit(ADAP_SLEEP, &adap->state_bits); - wait_on_bit(&adap->state_bits, ADAP_STREAMING, wait_schedule, + wait_on_bit(&adap->state_bits, ADAP_STREAMING, TASK_UNINTERRUPTIBLE); } diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index a389820d158b..3e11aab9f391 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -3437,16 +3437,10 @@ done_unlocked: return 0; } -static int eb_wait(void *word) -{ - io_schedule(); - return 0; -} - void wait_on_extent_buffer_writeback(struct extent_buffer *eb) { - wait_on_bit(&eb->bflags, EXTENT_BUFFER_WRITEBACK, eb_wait, - TASK_UNINTERRUPTIBLE); + wait_on_bit_io(&eb->bflags, EXTENT_BUFFER_WRITEBACK, + TASK_UNINTERRUPTIBLE); } static noinline_for_stack int diff --git a/fs/buffer.c b/fs/buffer.c index eba6e4f621ce..8f05111bbb8b 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -61,16 +61,9 @@ inline void touch_buffer(struct buffer_head *bh) } EXPORT_SYMBOL(touch_buffer); -static int sleep_on_buffer(void *word) -{ - io_schedule(); - return 0; -} - void __lock_buffer(struct buffer_head *bh) { - wait_on_bit_lock(&bh->b_state, BH_Lock, sleep_on_buffer, - TASK_UNINTERRUPTIBLE); + wait_on_bit_lock_io(&bh->b_state, BH_Lock, TASK_UNINTERRUPTIBLE); } EXPORT_SYMBOL(__lock_buffer); @@ -123,7 +116,7 @@ EXPORT_SYMBOL(buffer_check_dirty_writeback); */ void __wait_on_buffer(struct buffer_head * bh) { - wait_on_bit(&bh->b_state, BH_Lock, sleep_on_buffer, TASK_UNINTERRUPTIBLE); + wait_on_bit_io(&bh->b_state, BH_Lock, TASK_UNINTERRUPTIBLE); } EXPORT_SYMBOL(__wait_on_buffer); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 20d75b8ddb26..b98366f21f9e 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -3934,13 +3934,6 @@ cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb) return tlink_tcon(cifs_sb_master_tlink(cifs_sb)); } -static int -cifs_sb_tcon_pending_wait(void *unused) -{ - schedule(); - return signal_pending(current) ? -ERESTARTSYS : 0; -} - /* find and return a tlink with given uid */ static struct tcon_link * tlink_rb_search(struct rb_root *root, kuid_t uid) @@ -4039,11 +4032,10 @@ cifs_sb_tlink(struct cifs_sb_info *cifs_sb) } else { wait_for_construction: ret = wait_on_bit(&tlink->tl_flags, TCON_LINK_PENDING, - cifs_sb_tcon_pending_wait, TASK_INTERRUPTIBLE); if (ret) { cifs_put_tlink(tlink); - return ERR_PTR(ret); + return ERR_PTR(-ERESTARTSYS); } /* if it's good, return it */ diff --git a/fs/cifs/file.c b/fs/cifs/file.c index e90a1e9aa627..b88b1ade4d3d 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -3618,13 +3618,6 @@ static int cifs_launder_page(struct page *page) return rc; } -static int -cifs_pending_writers_wait(void *unused) -{ - schedule(); - return 0; -} - void cifs_oplock_break(struct work_struct *work) { struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo, @@ -3636,7 +3629,7 @@ void cifs_oplock_break(struct work_struct *work) int rc = 0; wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS, - cifs_pending_writers_wait, TASK_UNINTERRUPTIBLE); + TASK_UNINTERRUPTIBLE); server->ops->downgrade_oplock(server, cinode, test_bit(CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, &cinode->flags)); diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index a174605f6afa..213c4580b4e3 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1794,8 +1794,8 @@ cifs_revalidate_mapping(struct inode *inode) int rc; unsigned long *flags = &CIFS_I(inode)->flags; - rc = wait_on_bit_lock(flags, CIFS_INO_LOCK, cifs_wait_bit_killable, - TASK_KILLABLE); + rc = wait_on_bit_lock_action(flags, CIFS_INO_LOCK, cifs_wait_bit_killable, + TASK_KILLABLE); if (rc) return rc; diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 3b0c62e622da..6bf55d0ed494 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -582,7 +582,7 @@ int cifs_get_writer(struct cifsInodeInfo *cinode) start: rc = wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_OPLOCK_BREAK, - cifs_oplock_break_wait, TASK_KILLABLE); + TASK_KILLABLE); if (rc) return rc; diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index be568b7311d6..ef9bef118342 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -342,7 +342,8 @@ static void __inode_wait_for_writeback(struct inode *inode) wqh = bit_waitqueue(&inode->i_state, __I_SYNC); while (inode->i_state & I_SYNC) { spin_unlock(&inode->i_lock); - __wait_on_bit(wqh, &wq, inode_wait, TASK_UNINTERRUPTIBLE); + __wait_on_bit(wqh, &wq, bit_wait, + TASK_UNINTERRUPTIBLE); spin_lock(&inode->i_lock); } } diff --git a/fs/fscache/cookie.c b/fs/fscache/cookie.c index aec01be91b0a..89acec742e0b 100644 --- a/fs/fscache/cookie.c +++ b/fs/fscache/cookie.c @@ -160,7 +160,7 @@ void __fscache_enable_cookie(struct fscache_cookie *cookie, _enter("%p", cookie); wait_on_bit_lock(&cookie->flags, FSCACHE_COOKIE_ENABLEMENT_LOCK, - fscache_wait_bit, TASK_UNINTERRUPTIBLE); + TASK_UNINTERRUPTIBLE); if (test_bit(FSCACHE_COOKIE_ENABLED, &cookie->flags)) goto out_unlock; @@ -255,7 +255,7 @@ static int fscache_acquire_non_index_cookie(struct fscache_cookie *cookie) if (!fscache_defer_lookup) { _debug("non-deferred lookup %p", &cookie->flags); wait_on_bit(&cookie->flags, FSCACHE_COOKIE_LOOKING_UP, - fscache_wait_bit, TASK_UNINTERRUPTIBLE); + TASK_UNINTERRUPTIBLE); _debug("complete"); if (test_bit(FSCACHE_COOKIE_UNAVAILABLE, &cookie->flags)) goto unavailable; @@ -463,7 +463,6 @@ void __fscache_wait_on_invalidate(struct fscache_cookie *cookie) _enter("%p", cookie); wait_on_bit(&cookie->flags, FSCACHE_COOKIE_INVALIDATING, - fscache_wait_bit_interruptible, TASK_UNINTERRUPTIBLE); _leave(""); @@ -525,7 +524,7 @@ void __fscache_disable_cookie(struct fscache_cookie *cookie, bool invalidate) } wait_on_bit_lock(&cookie->flags, FSCACHE_COOKIE_ENABLEMENT_LOCK, - fscache_wait_bit, TASK_UNINTERRUPTIBLE); + TASK_UNINTERRUPTIBLE); if (!test_and_clear_bit(FSCACHE_COOKIE_ENABLED, &cookie->flags)) goto out_unlock_enable; diff --git a/fs/fscache/internal.h b/fs/fscache/internal.h index bc6c08fcfddd..7872a62ef30c 100644 --- a/fs/fscache/internal.h +++ b/fs/fscache/internal.h @@ -97,8 +97,6 @@ static inline bool fscache_object_congested(void) return workqueue_congested(WORK_CPU_UNBOUND, fscache_object_wq); } -extern int fscache_wait_bit(void *); -extern int fscache_wait_bit_interruptible(void *); extern int fscache_wait_atomic_t(atomic_t *); /* diff --git a/fs/fscache/main.c b/fs/fscache/main.c index 63f868e869b9..a31b83c5cbd9 100644 --- a/fs/fscache/main.c +++ b/fs/fscache/main.c @@ -196,24 +196,6 @@ static void __exit fscache_exit(void) module_exit(fscache_exit); -/* - * wait_on_bit() sleep function for uninterruptible waiting - */ -int fscache_wait_bit(void *flags) -{ - schedule(); - return 0; -} - -/* - * wait_on_bit() sleep function for interruptible waiting - */ -int fscache_wait_bit_interruptible(void *flags) -{ - schedule(); - return signal_pending(current); -} - /* * wait_on_atomic_t() sleep function for uninterruptible waiting */ diff --git a/fs/fscache/page.c b/fs/fscache/page.c index ed70714503fa..85332b9d19d1 100644 --- a/fs/fscache/page.c +++ b/fs/fscache/page.c @@ -298,7 +298,6 @@ int fscache_wait_for_deferred_lookup(struct fscache_cookie *cookie) jif = jiffies; if (wait_on_bit(&cookie->flags, FSCACHE_COOKIE_LOOKING_UP, - fscache_wait_bit_interruptible, TASK_INTERRUPTIBLE) != 0) { fscache_stat(&fscache_n_retrievals_intr); _leave(" = -ERESTARTSYS"); @@ -342,7 +341,6 @@ int fscache_wait_for_operation_activation(struct fscache_object *object, if (stat_op_waits) fscache_stat(stat_op_waits); if (wait_on_bit(&op->flags, FSCACHE_OP_WAITING, - fscache_wait_bit_interruptible, TASK_INTERRUPTIBLE) != 0) { ret = fscache_cancel_op(op, do_cancel); if (ret == 0) @@ -351,7 +349,7 @@ int fscache_wait_for_operation_activation(struct fscache_object *object, /* it's been removed from the pending queue by another party, * so we should get to run shortly */ wait_on_bit(&op->flags, FSCACHE_OP_WAITING, - fscache_wait_bit, TASK_UNINTERRUPTIBLE); + TASK_UNINTERRUPTIBLE); } _debug("<<< GO"); diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index c355f7320e44..770e16716d81 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -855,27 +855,6 @@ void gfs2_holder_uninit(struct gfs2_holder *gh) gh->gh_ip = 0; } -/** - * gfs2_glock_holder_wait - * @word: unused - * - * This function and gfs2_glock_demote_wait both show up in the WCHAN - * field. Thus I've separated these otherwise identical functions in - * order to be more informative to the user. - */ - -static int gfs2_glock_holder_wait(void *word) -{ - schedule(); - return 0; -} - -static int gfs2_glock_demote_wait(void *word) -{ - schedule(); - return 0; -} - /** * gfs2_glock_wait - wait on a glock acquisition * @gh: the glock holder @@ -888,7 +867,7 @@ int gfs2_glock_wait(struct gfs2_holder *gh) unsigned long time1 = jiffies; might_sleep(); - wait_on_bit(&gh->gh_iflags, HIF_WAIT, gfs2_glock_holder_wait, TASK_UNINTERRUPTIBLE); + wait_on_bit(&gh->gh_iflags, HIF_WAIT, TASK_UNINTERRUPTIBLE); if (time_after(jiffies, time1 + HZ)) /* have we waited > a second? */ /* Lengthen the minimum hold time. */ gh->gh_gl->gl_hold_time = min(gh->gh_gl->gl_hold_time + @@ -1128,7 +1107,7 @@ void gfs2_glock_dq_wait(struct gfs2_holder *gh) struct gfs2_glock *gl = gh->gh_gl; gfs2_glock_dq(gh); might_sleep(); - wait_on_bit(&gl->gl_flags, GLF_DEMOTE, gfs2_glock_demote_wait, TASK_UNINTERRUPTIBLE); + wait_on_bit(&gl->gl_flags, GLF_DEMOTE, TASK_UNINTERRUPTIBLE); } /** diff --git a/fs/gfs2/lock_dlm.c b/fs/gfs2/lock_dlm.c index 91f274de1246..992ca5b1e045 100644 --- a/fs/gfs2/lock_dlm.c +++ b/fs/gfs2/lock_dlm.c @@ -936,12 +936,6 @@ fail: return error; } -static int dlm_recovery_wait(void *word) -{ - schedule(); - return 0; -} - static int control_first_done(struct gfs2_sbd *sdp) { struct lm_lockstruct *ls = &sdp->sd_lockstruct; @@ -976,7 +970,7 @@ restart: fs_info(sdp, "control_first_done wait gen %u\n", start_gen); wait_on_bit(&ls->ls_recover_flags, DFL_DLM_RECOVERY, - dlm_recovery_wait, TASK_UNINTERRUPTIBLE); + TASK_UNINTERRUPTIBLE); goto restart; } diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index bc564c0d6d16..d3eae244076e 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -1024,20 +1024,13 @@ void gfs2_lm_unmount(struct gfs2_sbd *sdp) lm->lm_unmount(sdp); } -static int gfs2_journalid_wait(void *word) -{ - if (signal_pending(current)) - return -EINTR; - schedule(); - return 0; -} - static int wait_on_journal(struct gfs2_sbd *sdp) { if (sdp->sd_lockstruct.ls_ops->lm_mount == NULL) return 0; - return wait_on_bit(&sdp->sd_flags, SDF_NOJOURNALID, gfs2_journalid_wait, TASK_INTERRUPTIBLE); + return wait_on_bit(&sdp->sd_flags, SDF_NOJOURNALID, TASK_INTERRUPTIBLE) + ? -EINTR : 0; } void gfs2_online_uevent(struct gfs2_sbd *sdp) diff --git a/fs/gfs2/recovery.c b/fs/gfs2/recovery.c index 94555d4c5698..573bd3b758fa 100644 --- a/fs/gfs2/recovery.c +++ b/fs/gfs2/recovery.c @@ -591,12 +591,6 @@ done: wake_up_bit(&jd->jd_flags, JDF_RECOVERY); } -static int gfs2_recovery_wait(void *word) -{ - schedule(); - return 0; -} - int gfs2_recover_journal(struct gfs2_jdesc *jd, bool wait) { int rv; @@ -609,7 +603,7 @@ int gfs2_recover_journal(struct gfs2_jdesc *jd, bool wait) BUG_ON(!rv); if (wait) - wait_on_bit(&jd->jd_flags, JDF_RECOVERY, gfs2_recovery_wait, + wait_on_bit(&jd->jd_flags, JDF_RECOVERY, TASK_UNINTERRUPTIBLE); return wait ? jd->jd_recover_error : 0; diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 1319b5c4ec68..2607ff13d486 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -864,12 +864,6 @@ static int gfs2_make_fs_ro(struct gfs2_sbd *sdp) return error; } -static int gfs2_umount_recovery_wait(void *word) -{ - schedule(); - return 0; -} - /** * gfs2_put_super - Unmount the filesystem * @sb: The VFS superblock @@ -894,7 +888,7 @@ restart: continue; spin_unlock(&sdp->sd_jindex_spin); wait_on_bit(&jd->jd_flags, JDF_RECOVERY, - gfs2_umount_recovery_wait, TASK_UNINTERRUPTIBLE); + TASK_UNINTERRUPTIBLE); goto restart; } spin_unlock(&sdp->sd_jindex_spin); diff --git a/fs/inode.c b/fs/inode.c index 6eecb7ff0b9a..5938f3928944 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1695,13 +1695,6 @@ int inode_needs_sync(struct inode *inode) } EXPORT_SYMBOL(inode_needs_sync); -int inode_wait(void *word) -{ - schedule(); - return 0; -} -EXPORT_SYMBOL(inode_wait); - /* * If we try to find an inode in the inode hash while it is being * deleted, we have to wait until the filesystem completes its diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index 6f0f590cc5a3..5f09370c90a8 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -763,12 +763,6 @@ static void warn_dirty_buffer(struct buffer_head *bh) bdevname(bh->b_bdev, b), (unsigned long long)bh->b_blocknr); } -static int sleep_on_shadow_bh(void *word) -{ - io_schedule(); - return 0; -} - /* * If the buffer is already part of the current transaction, then there * is nothing we need to do. If it is already part of a prior @@ -906,8 +900,8 @@ repeat: if (buffer_shadow(bh)) { JBUFFER_TRACE(jh, "on shadow: sleep"); jbd_unlock_bh_state(bh); - wait_on_bit(&bh->b_state, BH_Shadow, - sleep_on_shadow_bh, TASK_UNINTERRUPTIBLE); + wait_on_bit_io(&bh->b_state, BH_Shadow, + TASK_UNINTERRUPTIBLE); goto repeat; } diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 4042ff58fe3f..524dd80d1898 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -361,8 +361,8 @@ start: * Prevent starvation issues if someone is doing a consistency * sync-to-disk */ - ret = wait_on_bit(&NFS_I(mapping->host)->flags, NFS_INO_FLUSHING, - nfs_wait_bit_killable, TASK_KILLABLE); + ret = wait_on_bit_action(&NFS_I(mapping->host)->flags, NFS_INO_FLUSHING, + nfs_wait_bit_killable, TASK_KILLABLE); if (ret) return ret; diff --git a/fs/nfs/filelayout/filelayoutdev.c b/fs/nfs/filelayout/filelayoutdev.c index 44bf0140a4c7..e2a0361e24c6 100644 --- a/fs/nfs/filelayout/filelayoutdev.c +++ b/fs/nfs/filelayout/filelayoutdev.c @@ -783,8 +783,8 @@ nfs4_fl_select_ds_fh(struct pnfs_layout_segment *lseg, u32 j) static void nfs4_wait_ds_connect(struct nfs4_pnfs_ds *ds) { might_sleep(); - wait_on_bit(&ds->ds_state, NFS4DS_CONNECTING, - nfs_wait_bit_killable, TASK_KILLABLE); + wait_on_bit_action(&ds->ds_state, NFS4DS_CONNECTING, + nfs_wait_bit_killable, TASK_KILLABLE); } static void nfs4_clear_ds_conn_bit(struct nfs4_pnfs_ds *ds) diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 9927913c97c2..b7b710e7d08e 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -1074,8 +1074,8 @@ int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping) * the bit lock here if it looks like we're going to be doing that. */ for (;;) { - ret = wait_on_bit(bitlock, NFS_INO_INVALIDATING, - nfs_wait_bit_killable, TASK_KILLABLE); + ret = wait_on_bit_action(bitlock, NFS_INO_INVALIDATING, + nfs_wait_bit_killable, TASK_KILLABLE); if (ret) goto out; spin_lock(&inode->i_lock); diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 848f6853c59e..42f121182167 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1251,8 +1251,8 @@ int nfs4_wait_clnt_recover(struct nfs_client *clp) might_sleep(); atomic_inc(&clp->cl_count); - res = wait_on_bit(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING, - nfs_wait_bit_killable, TASK_KILLABLE); + res = wait_on_bit_action(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING, + nfs_wait_bit_killable, TASK_KILLABLE); if (res) goto out; if (clp->cl_cons_state < 0) diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index b6ee3a6ee96d..6104d3500b49 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -138,12 +138,6 @@ nfs_iocounter_wait(struct nfs_io_counter *c) return __nfs_iocounter_wait(c); } -static int nfs_wait_bit_uninterruptible(void *word) -{ - io_schedule(); - return 0; -} - /* * nfs_page_group_lock - lock the head of the page group * @req - request in group that is to be locked @@ -158,7 +152,6 @@ nfs_page_group_lock(struct nfs_page *req) WARN_ON_ONCE(head != head->wb_head); wait_on_bit_lock(&head->wb_flags, PG_HEADLOCK, - nfs_wait_bit_uninterruptible, TASK_UNINTERRUPTIBLE); } @@ -425,9 +418,8 @@ void nfs_release_request(struct nfs_page *req) int nfs_wait_on_request(struct nfs_page *req) { - return wait_on_bit(&req->wb_flags, PG_BUSY, - nfs_wait_bit_uninterruptible, - TASK_UNINTERRUPTIBLE); + return wait_on_bit_io(&req->wb_flags, PG_BUSY, + TASK_UNINTERRUPTIBLE); } /* diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index 6fdcd233d6f7..a8914b335617 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -1885,7 +1885,7 @@ pnfs_layoutcommit_inode(struct inode *inode, bool sync) if (test_and_set_bit(NFS_INO_LAYOUTCOMMITTING, &nfsi->flags)) { if (!sync) goto out; - status = wait_on_bit_lock(&nfsi->flags, + status = wait_on_bit_lock_action(&nfsi->flags, NFS_INO_LAYOUTCOMMITTING, nfs_wait_bit_killable, TASK_KILLABLE); diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 98ff061ccaf3..f05f321f9d3d 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -397,7 +397,7 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc) int err; /* Stop dirtying of new pages while we sync */ - err = wait_on_bit_lock(bitlock, NFS_INO_FLUSHING, + err = wait_on_bit_lock_action(bitlock, NFS_INO_FLUSHING, nfs_wait_bit_killable, TASK_KILLABLE); if (err) goto out_err; @@ -1475,7 +1475,7 @@ int nfs_commit_inode(struct inode *inode, int how) return error; if (!may_wait) goto out_mark_dirty; - error = wait_on_bit(&NFS_I(inode)->flags, + error = wait_on_bit_action(&NFS_I(inode)->flags, NFS_INO_COMMIT, nfs_wait_bit_killable, TASK_KILLABLE); diff --git a/include/linux/wait.h b/include/linux/wait.h index bd68819f0815..73960ff09e56 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -854,11 +854,14 @@ int wake_bit_function(wait_queue_t *wait, unsigned mode, int sync, void *key); (wait)->flags = 0; \ } while (0) + +extern int bit_wait(void *); +extern int bit_wait_io(void *); + /** * wait_on_bit - wait for a bit to be cleared * @word: the word being waited on, a kernel virtual address * @bit: the bit of the word being waited on - * @action: the function used to sleep, which may take special actions * @mode: the task state to sleep in * * There is a standard hashed waitqueue table for generic use. This @@ -867,9 +870,62 @@ int wake_bit_function(wait_queue_t *wait, unsigned mode, int sync, void *key); * call wait_on_bit() in threads waiting for the bit to clear. * One uses wait_on_bit() where one is waiting for the bit to clear, * but has no intention of setting it. + * Returned value will be zero if the bit was cleared, or non-zero + * if the process received a signal and the mode permitted wakeup + * on that signal. + */ +static inline int +wait_on_bit(void *word, int bit, unsigned mode) +{ + if (!test_bit(bit, word)) + return 0; + return out_of_line_wait_on_bit(word, bit, + bit_wait, + mode); +} + +/** + * wait_on_bit_io - wait for a bit to be cleared + * @word: the word being waited on, a kernel virtual address + * @bit: the bit of the word being waited on + * @mode: the task state to sleep in + * + * Use the standard hashed waitqueue table to wait for a bit + * to be cleared. This is similar to wait_on_bit(), but calls + * io_schedule() instead of schedule() for the actual waiting. + * + * Returned value will be zero if the bit was cleared, or non-zero + * if the process received a signal and the mode permitted wakeup + * on that signal. + */ +static inline int +wait_on_bit_io(void *word, int bit, unsigned mode) +{ + if (!test_bit(bit, word)) + return 0; + return out_of_line_wait_on_bit(word, bit, + bit_wait_io, + mode); +} + +/** + * wait_on_bit_action - wait for a bit to be cleared + * @word: the word being waited on, a kernel virtual address + * @bit: the bit of the word being waited on + * @action: the function used to sleep, which may take special actions + * @mode: the task state to sleep in + * + * Use the standard hashed waitqueue table to wait for a bit + * to be cleared, and allow the waiting action to be specified. + * This is like wait_on_bit() but allows fine control of how the waiting + * is done. + * + * Returned value will be zero if the bit was cleared, or non-zero + * if the process received a signal and the mode permitted wakeup + * on that signal. */ static inline int -wait_on_bit(void *word, int bit, int (*action)(void *), unsigned mode) +wait_on_bit_action(void *word, int bit, int (*action)(void *), unsigned mode) { if (!test_bit(bit, word)) return 0; @@ -880,7 +936,6 @@ wait_on_bit(void *word, int bit, int (*action)(void *), unsigned mode) * wait_on_bit_lock - wait for a bit to be cleared, when wanting to set it * @word: the word being waited on, a kernel virtual address * @bit: the bit of the word being waited on - * @action: the function used to sleep, which may take special actions * @mode: the task state to sleep in * * There is a standard hashed waitqueue table for generic use. This @@ -891,9 +946,61 @@ wait_on_bit(void *word, int bit, int (*action)(void *), unsigned mode) * wait_on_bit() in threads waiting to be able to set the bit. * One uses wait_on_bit_lock() where one is waiting for the bit to * clear with the intention of setting it, and when done, clearing it. + * + * Returns zero if the bit was (eventually) found to be clear and was + * set. Returns non-zero if a signal was delivered to the process and + * the @mode allows that signal to wake the process. + */ +static inline int +wait_on_bit_lock(void *word, int bit, unsigned mode) +{ + if (!test_and_set_bit(bit, word)) + return 0; + return out_of_line_wait_on_bit_lock(word, bit, bit_wait, mode); +} + +/** + * wait_on_bit_lock_io - wait for a bit to be cleared, when wanting to set it + * @word: the word being waited on, a kernel virtual address + * @bit: the bit of the word being waited on + * @mode: the task state to sleep in + * + * Use the standard hashed waitqueue table to wait for a bit + * to be cleared and then to atomically set it. This is similar + * to wait_on_bit(), but calls io_schedule() instead of schedule() + * for the actual waiting. + * + * Returns zero if the bit was (eventually) found to be clear and was + * set. Returns non-zero if a signal was delivered to the process and + * the @mode allows that signal to wake the process. + */ +static inline int +wait_on_bit_lock_io(void *word, int bit, unsigned mode) +{ + if (!test_and_set_bit(bit, word)) + return 0; + return out_of_line_wait_on_bit_lock(word, bit, bit_wait_io, mode); +} + +/** + * wait_on_bit_lock_action - wait for a bit to be cleared, when wanting to set it + * @word: the word being waited on, a kernel virtual address + * @bit: the bit of the word being waited on + * @action: the function used to sleep, which may take special actions + * @mode: the task state to sleep in + * + * Use the standard hashed waitqueue table to wait for a bit + * to be cleared and then to set it, and allow the waiting action + * to be specified. + * This is like wait_on_bit() but allows fine control of how the waiting + * is done. + * + * Returns zero if the bit was (eventually) found to be clear and was + * set. Returns non-zero if a signal was delivered to the process and + * the @mode allows that signal to wake the process. */ static inline int -wait_on_bit_lock(void *word, int bit, int (*action)(void *), unsigned mode) +wait_on_bit_lock_action(void *word, int bit, int (*action)(void *), unsigned mode) { if (!test_and_set_bit(bit, word)) return 0; diff --git a/include/linux/writeback.h b/include/linux/writeback.h index 5777c13849ba..a219be961c0a 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -90,7 +90,6 @@ struct writeback_control { * fs/fs-writeback.c */ struct bdi_writeback; -int inode_wait(void *); void writeback_inodes_sb(struct super_block *, enum wb_reason reason); void writeback_inodes_sb_nr(struct super_block *, unsigned long nr, enum wb_reason reason); @@ -105,7 +104,7 @@ void inode_wait_for_writeback(struct inode *inode); static inline void wait_on_inode(struct inode *inode) { might_sleep(); - wait_on_bit(&inode->i_state, __I_NEW, inode_wait, TASK_UNINTERRUPTIBLE); + wait_on_bit(&inode->i_state, __I_NEW, TASK_UNINTERRUPTIBLE); } /* diff --git a/kernel/ptrace.c b/kernel/ptrace.c index adf98622cb32..54e75226c2c4 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -28,12 +28,6 @@ #include -static int ptrace_trapping_sleep_fn(void *flags) -{ - schedule(); - return 0; -} - /* * ptrace a task: make the debugger its new parent and * move it to the ptrace list. @@ -371,7 +365,7 @@ unlock_creds: out: if (!retval) { wait_on_bit(&task->jobctl, JOBCTL_TRAPPING_BIT, - ptrace_trapping_sleep_fn, TASK_UNINTERRUPTIBLE); + TASK_UNINTERRUPTIBLE); proc_ptrace_connector(task, PTRACE_ATTACH); } diff --git a/kernel/sched/wait.c b/kernel/sched/wait.c index 0ffa20ae657b..a104879e88f2 100644 --- a/kernel/sched/wait.c +++ b/kernel/sched/wait.c @@ -502,3 +502,21 @@ void wake_up_atomic_t(atomic_t *p) __wake_up_bit(atomic_t_waitqueue(p), p, WAIT_ATOMIC_T_BIT_NR); } EXPORT_SYMBOL(wake_up_atomic_t); + +__sched int bit_wait(void *word) +{ + if (signal_pending_state(current->state, current)) + return 1; + schedule(); + return 0; +} +EXPORT_SYMBOL(bit_wait); + +__sched int bit_wait_io(void *word) +{ + if (signal_pending_state(current->state, current)) + return 1; + io_schedule(); + return 0; +} +EXPORT_SYMBOL(bit_wait_io); diff --git a/mm/filemap.c b/mm/filemap.c index dafb06f70a09..d175917e2411 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -241,18 +241,6 @@ void delete_from_page_cache(struct page *page) } EXPORT_SYMBOL(delete_from_page_cache); -static int sleep_on_page(void *word) -{ - io_schedule(); - return 0; -} - -static int sleep_on_page_killable(void *word) -{ - sleep_on_page(word); - return fatal_signal_pending(current) ? -EINTR : 0; -} - static int filemap_check_errors(struct address_space *mapping) { int ret = 0; @@ -692,7 +680,7 @@ void wait_on_page_bit(struct page *page, int bit_nr) DEFINE_WAIT_BIT(wait, &page->flags, bit_nr); if (test_bit(bit_nr, &page->flags)) - __wait_on_bit(page_waitqueue(page), &wait, sleep_on_page, + __wait_on_bit(page_waitqueue(page), &wait, bit_wait_io, TASK_UNINTERRUPTIBLE); } EXPORT_SYMBOL(wait_on_page_bit); @@ -705,7 +693,7 @@ int wait_on_page_bit_killable(struct page *page, int bit_nr) return 0; return __wait_on_bit(page_waitqueue(page), &wait, - sleep_on_page_killable, TASK_KILLABLE); + bit_wait_io, TASK_KILLABLE); } /** @@ -806,7 +794,7 @@ void __lock_page(struct page *page) { DEFINE_WAIT_BIT(wait, &page->flags, PG_locked); - __wait_on_bit_lock(page_waitqueue(page), &wait, sleep_on_page, + __wait_on_bit_lock(page_waitqueue(page), &wait, bit_wait_io, TASK_UNINTERRUPTIBLE); } EXPORT_SYMBOL(__lock_page); @@ -816,7 +804,7 @@ int __lock_page_killable(struct page *page) DEFINE_WAIT_BIT(wait, &page->flags, PG_locked); return __wait_on_bit_lock(page_waitqueue(page), &wait, - sleep_on_page_killable, TASK_KILLABLE); + bit_wait_io, TASK_KILLABLE); } EXPORT_SYMBOL_GPL(__lock_page_killable); diff --git a/mm/ksm.c b/mm/ksm.c index 346ddc9e4c0d..fb7590222706 100644 --- a/mm/ksm.c +++ b/mm/ksm.c @@ -1978,18 +1978,12 @@ void ksm_migrate_page(struct page *newpage, struct page *oldpage) #endif /* CONFIG_MIGRATION */ #ifdef CONFIG_MEMORY_HOTREMOVE -static int just_wait(void *word) -{ - schedule(); - return 0; -} - static void wait_while_offlining(void) { while (ksm_run & KSM_RUN_OFFLINE) { mutex_unlock(&ksm_thread_mutex); wait_on_bit(&ksm_run, ilog2(KSM_RUN_OFFLINE), - just_wait, TASK_UNINTERRUPTIBLE); + TASK_UNINTERRUPTIBLE); mutex_lock(&ksm_thread_mutex); } } diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 0a43cce9a914..e090bffe1bf8 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2186,12 +2186,6 @@ static void hci_inq_req(struct hci_request *req, unsigned long opt) hci_req_add(req, HCI_OP_INQUIRY, sizeof(cp), &cp); } -static int wait_inquiry(void *word) -{ - schedule(); - return signal_pending(current); -} - int hci_inquiry(void __user *arg) { __u8 __user *ptr = arg; @@ -2242,7 +2236,7 @@ int hci_inquiry(void __user *arg) /* Wait until Inquiry procedure finishes (HCI_INQUIRY flag is * cleared). If it is interrupted by a signal, return -EINTR. */ - if (wait_on_bit(&hdev->flags, HCI_INQUIRY, wait_inquiry, + if (wait_on_bit(&hdev->flags, HCI_INQUIRY, TASK_INTERRUPTIBLE)) return -EINTR; } diff --git a/security/keys/gc.c b/security/keys/gc.c index d3222b6d7d59..9609a7f0faea 100644 --- a/security/keys/gc.c +++ b/security/keys/gc.c @@ -91,15 +91,6 @@ static void key_gc_timer_func(unsigned long data) key_schedule_gc_links(); } -/* - * wait_on_bit() sleep function for uninterruptible waiting - */ -static int key_gc_wait_bit(void *flags) -{ - schedule(); - return 0; -} - /* * Reap keys of dead type. * @@ -123,7 +114,7 @@ void key_gc_keytype(struct key_type *ktype) schedule_work(&key_gc_work); kdebug("sleep"); - wait_on_bit(&key_gc_flags, KEY_GC_REAPING_KEYTYPE, key_gc_wait_bit, + wait_on_bit(&key_gc_flags, KEY_GC_REAPING_KEYTYPE, TASK_UNINTERRUPTIBLE); key_gc_dead_keytype = NULL; diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 381411941cc1..26a94f18af94 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -21,24 +21,6 @@ #define key_negative_timeout 60 /* default timeout on a negative key's existence */ -/* - * wait_on_bit() sleep function for uninterruptible waiting - */ -static int key_wait_bit(void *flags) -{ - schedule(); - return 0; -} - -/* - * wait_on_bit() sleep function for interruptible waiting - */ -static int key_wait_bit_intr(void *flags) -{ - schedule(); - return signal_pending(current) ? -ERESTARTSYS : 0; -} - /** * complete_request_key - Complete the construction of a key. * @cons: The key construction record. @@ -592,10 +574,9 @@ int wait_for_key_construction(struct key *key, bool intr) int ret; ret = wait_on_bit(&key->flags, KEY_FLAG_USER_CONSTRUCT, - intr ? key_wait_bit_intr : key_wait_bit, intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); - if (ret < 0) - return ret; + if (ret) + return -ERESTARTSYS; if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) { smp_rmb(); return key->type_data.reject_error; -- cgit v1.2.3 From c1221321b7c25b53204447cff9949a6d5a7ddddc Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 7 Jul 2014 15:16:04 +1000 Subject: sched: Allow wait_on_bit_action() functions to support a timeout It is currently not possible for various wait_on_bit functions to implement a timeout. While the "action" function that is called to do the waiting could certainly use schedule_timeout(), there is no way to carry forward the remaining timeout after a false wake-up. As false-wakeups a clearly possible at least due to possible hash collisions in bit_waitqueue(), this is a real problem. The 'action' function is currently passed a pointer to the word containing the bit being waited on. No current action functions use this pointer. So changing it to something else will be a little noisy but will have no immediate effect. This patch changes the 'action' function to take a pointer to the "struct wait_bit_key", which contains a pointer to the word containing the bit so nothing is really lost. It also adds a 'private' field to "struct wait_bit_key", which is initialized to zero. An action function can now implement a timeout with something like static int timed_out_waiter(struct wait_bit_key *key) { unsigned long waited; if (key->private == 0) { key->private = jiffies; if (key->private == 0) key->private -= 1; } waited = jiffies - key->private; if (waited > 10 * HZ) return -EAGAIN; schedule_timeout(waited - 10 * HZ); return 0; } If any other need for context in a waiter were found it would be easy to use ->private for some other purpose, or even extend "struct wait_bit_key". My particular need is to support timeouts in nfs_release_page() to avoid deadlocks with loopback mounted NFS. While wait_on_bit_timeout() would be a cleaner interface, it will not meet my need. I need the timeout to be sensitive to the state of the connection with the server, which could change. So I need to use an 'action' interface. Signed-off-by: NeilBrown Acked-by: Peter Zijlstra Cc: Oleg Nesterov Cc: Steve French Cc: David Howells Cc: Steven Whitehouse Cc: Linus Torvalds Link: http://lkml.kernel.org/r/20140707051604.28027.41257.stgit@notabene.brown Signed-off-by: Ingo Molnar --- fs/cifs/inode.c | 2 +- fs/nfs/inode.c | 2 +- fs/nfs/internal.h | 2 +- fs/nfs/pagelist.c | 2 +- include/linux/sunrpc/sched.h | 2 +- include/linux/wait.h | 18 ++++++++++-------- kernel/sched/wait.c | 16 ++++++++-------- net/sunrpc/sched.c | 4 ++-- 8 files changed, 25 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 213c4580b4e3..41de3935caa0 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1780,7 +1780,7 @@ cifs_invalidate_mapping(struct inode *inode) * @word: long word containing the bit lock */ static int -cifs_wait_bit_killable(void *word) +cifs_wait_bit_killable(struct wait_bit_key *key) { if (fatal_signal_pending(current)) return -ERESTARTSYS; diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index b7b710e7d08e..abd37a380535 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -75,7 +75,7 @@ nfs_fattr_to_ino_t(struct nfs_fattr *fattr) * nfs_wait_bit_killable - helper for functions that are sleeping on bit locks * @word: long word containing the bit lock */ -int nfs_wait_bit_killable(void *word) +int nfs_wait_bit_killable(struct wait_bit_key *key) { if (fatal_signal_pending(current)) return -ERESTARTSYS; diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 82ddbf46660e..e0193d63630c 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -347,7 +347,7 @@ extern int nfs_drop_inode(struct inode *); extern void nfs_clear_inode(struct inode *); extern void nfs_evict_inode(struct inode *); void nfs_zap_acl_cache(struct inode *inode); -extern int nfs_wait_bit_killable(void *word); +extern int nfs_wait_bit_killable(struct wait_bit_key *key); /* super.c */ extern const struct super_operations nfs_sops; diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index 6104d3500b49..745a612dbe22 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -117,7 +117,7 @@ __nfs_iocounter_wait(struct nfs_io_counter *c) set_bit(NFS_IO_INPROGRESS, &c->flags); if (atomic_read(&c->io_count) == 0) break; - ret = nfs_wait_bit_killable(&c->flags); + ret = nfs_wait_bit_killable(&q.key); } while (atomic_read(&c->io_count) != 0); finish_wait(wq, &q.wait); return ret; diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index ad7dbe2cfecd..1a8959944c5f 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -236,7 +236,7 @@ void * rpc_malloc(struct rpc_task *, size_t); void rpc_free(void *); int rpciod_up(void); void rpciod_down(void); -int __rpc_wait_for_completion_task(struct rpc_task *task, int (*)(void *)); +int __rpc_wait_for_completion_task(struct rpc_task *task, wait_bit_action_f *); #ifdef RPC_DEBUG struct net; void rpc_show_tasks(struct net *); diff --git a/include/linux/wait.h b/include/linux/wait.h index 73960ff09e56..6fb1ba5f9b2f 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -25,6 +25,7 @@ struct wait_bit_key { void *flags; int bit_nr; #define WAIT_ATOMIC_T_BIT_NR -1 + unsigned long private; }; struct wait_bit_queue { @@ -141,18 +142,19 @@ __remove_wait_queue(wait_queue_head_t *head, wait_queue_t *old) list_del(&old->task_list); } +typedef int wait_bit_action_f(struct wait_bit_key *); void __wake_up(wait_queue_head_t *q, unsigned int mode, int nr, void *key); void __wake_up_locked_key(wait_queue_head_t *q, unsigned int mode, void *key); void __wake_up_sync_key(wait_queue_head_t *q, unsigned int mode, int nr, void *key); void __wake_up_locked(wait_queue_head_t *q, unsigned int mode, int nr); void __wake_up_sync(wait_queue_head_t *q, unsigned int mode, int nr); void __wake_up_bit(wait_queue_head_t *, void *, int); -int __wait_on_bit(wait_queue_head_t *, struct wait_bit_queue *, int (*)(void *), unsigned); -int __wait_on_bit_lock(wait_queue_head_t *, struct wait_bit_queue *, int (*)(void *), unsigned); +int __wait_on_bit(wait_queue_head_t *, struct wait_bit_queue *, wait_bit_action_f *, unsigned); +int __wait_on_bit_lock(wait_queue_head_t *, struct wait_bit_queue *, wait_bit_action_f *, unsigned); void wake_up_bit(void *, int); void wake_up_atomic_t(atomic_t *); -int out_of_line_wait_on_bit(void *, int, int (*)(void *), unsigned); -int out_of_line_wait_on_bit_lock(void *, int, int (*)(void *), unsigned); +int out_of_line_wait_on_bit(void *, int, wait_bit_action_f *, unsigned); +int out_of_line_wait_on_bit_lock(void *, int, wait_bit_action_f *, unsigned); int out_of_line_wait_on_atomic_t(atomic_t *, int (*)(atomic_t *), unsigned); wait_queue_head_t *bit_waitqueue(void *, int); @@ -855,8 +857,8 @@ int wake_bit_function(wait_queue_t *wait, unsigned mode, int sync, void *key); } while (0) -extern int bit_wait(void *); -extern int bit_wait_io(void *); +extern int bit_wait(struct wait_bit_key *); +extern int bit_wait_io(struct wait_bit_key *); /** * wait_on_bit - wait for a bit to be cleared @@ -925,7 +927,7 @@ wait_on_bit_io(void *word, int bit, unsigned mode) * on that signal. */ static inline int -wait_on_bit_action(void *word, int bit, int (*action)(void *), unsigned mode) +wait_on_bit_action(void *word, int bit, wait_bit_action_f *action, unsigned mode) { if (!test_bit(bit, word)) return 0; @@ -1000,7 +1002,7 @@ wait_on_bit_lock_io(void *word, int bit, unsigned mode) * the @mode allows that signal to wake the process. */ static inline int -wait_on_bit_lock_action(void *word, int bit, int (*action)(void *), unsigned mode) +wait_on_bit_lock_action(void *word, int bit, wait_bit_action_f *action, unsigned mode) { if (!test_and_set_bit(bit, word)) return 0; diff --git a/kernel/sched/wait.c b/kernel/sched/wait.c index a104879e88f2..15cab1a4f84e 100644 --- a/kernel/sched/wait.c +++ b/kernel/sched/wait.c @@ -319,14 +319,14 @@ EXPORT_SYMBOL(wake_bit_function); */ int __sched __wait_on_bit(wait_queue_head_t *wq, struct wait_bit_queue *q, - int (*action)(void *), unsigned mode) + wait_bit_action_f *action, unsigned mode) { int ret = 0; do { prepare_to_wait(wq, &q->wait, mode); if (test_bit(q->key.bit_nr, q->key.flags)) - ret = (*action)(q->key.flags); + ret = (*action)(&q->key); } while (test_bit(q->key.bit_nr, q->key.flags) && !ret); finish_wait(wq, &q->wait); return ret; @@ -334,7 +334,7 @@ __wait_on_bit(wait_queue_head_t *wq, struct wait_bit_queue *q, EXPORT_SYMBOL(__wait_on_bit); int __sched out_of_line_wait_on_bit(void *word, int bit, - int (*action)(void *), unsigned mode) + wait_bit_action_f *action, unsigned mode) { wait_queue_head_t *wq = bit_waitqueue(word, bit); DEFINE_WAIT_BIT(wait, word, bit); @@ -345,7 +345,7 @@ EXPORT_SYMBOL(out_of_line_wait_on_bit); int __sched __wait_on_bit_lock(wait_queue_head_t *wq, struct wait_bit_queue *q, - int (*action)(void *), unsigned mode) + wait_bit_action_f *action, unsigned mode) { do { int ret; @@ -353,7 +353,7 @@ __wait_on_bit_lock(wait_queue_head_t *wq, struct wait_bit_queue *q, prepare_to_wait_exclusive(wq, &q->wait, mode); if (!test_bit(q->key.bit_nr, q->key.flags)) continue; - ret = action(q->key.flags); + ret = action(&q->key); if (!ret) continue; abort_exclusive_wait(wq, &q->wait, mode, &q->key); @@ -365,7 +365,7 @@ __wait_on_bit_lock(wait_queue_head_t *wq, struct wait_bit_queue *q, EXPORT_SYMBOL(__wait_on_bit_lock); int __sched out_of_line_wait_on_bit_lock(void *word, int bit, - int (*action)(void *), unsigned mode) + wait_bit_action_f *action, unsigned mode) { wait_queue_head_t *wq = bit_waitqueue(word, bit); DEFINE_WAIT_BIT(wait, word, bit); @@ -503,7 +503,7 @@ void wake_up_atomic_t(atomic_t *p) } EXPORT_SYMBOL(wake_up_atomic_t); -__sched int bit_wait(void *word) +__sched int bit_wait(struct wait_bit_key *word) { if (signal_pending_state(current->state, current)) return 1; @@ -512,7 +512,7 @@ __sched int bit_wait(void *word) } EXPORT_SYMBOL(bit_wait); -__sched int bit_wait_io(void *word) +__sched int bit_wait_io(struct wait_bit_key *word) { if (signal_pending_state(current->state, current)) return 1; diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index c0365c14b858..9358c79fd589 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -250,7 +250,7 @@ void rpc_destroy_wait_queue(struct rpc_wait_queue *queue) } EXPORT_SYMBOL_GPL(rpc_destroy_wait_queue); -static int rpc_wait_bit_killable(void *word) +static int rpc_wait_bit_killable(struct wait_bit_key *key) { if (fatal_signal_pending(current)) return -ERESTARTSYS; @@ -309,7 +309,7 @@ static int rpc_complete_task(struct rpc_task *task) * to enforce taking of the wq->lock and hence avoid races with * rpc_complete_task(). */ -int __rpc_wait_for_completion_task(struct rpc_task *task, int (*action)(void *)) +int __rpc_wait_for_completion_task(struct rpc_task *task, wait_bit_action_f *action) { if (action == NULL) action = rpc_wait_bit_killable; -- cgit v1.2.3 From 646d7043adf3d92de5d3db1244a82a12628303de Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Fri, 11 Jul 2014 14:39:10 -0400 Subject: ftrace: Allow archs to specify if they need a separate function graph trampoline Currently if an arch supports function graph tracing, the core code will just assign the function graph trampoline to the function graph addr that gets called. But as the old method for function graph tracing always calls the function trampoline first and that calls the function graph trampoline, some archs may have the function graph trampoline dependent on operations that were done in the function trampoline. This causes function graph tracer to break on those archs. Instead of having the default be to set the function graph ftrace_ops to the function graph trampoline, have it instead just set it to zero which will keep it from jumping to a trampoline that is not set up to be jumped directly too. Link: http://lkml.kernel.org/r/53BED155.9040607@nvidia.com Reported-by: Tuomas Tynkkynen Tested-by: Tuomas Tynkkynen Signed-off-by: Steven Rostedt --- include/linux/ftrace.h | 10 ++++++++++ kernel/trace/ftrace.c | 6 ++++-- 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 11e18fd58b1a..4807a39e7ae1 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -453,6 +453,16 @@ void ftrace_modify_all_code(int command); #endif #endif +/* + * If an arch would like functions that are only traced + * by the function graph tracer to jump directly to its own + * trampoline, then they can define FTRACE_GRAPH_TRAMP_ADDR + * to be that address to jump to. + */ +#ifndef FTRACE_GRAPH_TRAMP_ADDR +#define FTRACE_GRAPH_TRAMP_ADDR ((unsigned long) 0) +#endif + #ifdef CONFIG_FUNCTION_GRAPH_TRACER extern void ftrace_graph_caller(void); extern int ftrace_enable_ftrace_graph_caller(void); diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 45aac1a742c5..1776153ea6e0 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -5366,7 +5366,8 @@ int register_ftrace_graph(trace_func_graph_ret_t retfunc, #ifdef CONFIG_DYNAMIC_FTRACE /* Optimize function graph calling (if implemented by arch) */ - global_ops.trampoline = FTRACE_GRAPH_ADDR; + if (FTRACE_GRAPH_TRAMP_ADDR != 0) + global_ops.trampoline = FTRACE_GRAPH_TRAMP_ADDR; #endif ret = ftrace_startup(&global_ops, FTRACE_START_FUNC_RET); @@ -5390,7 +5391,8 @@ void unregister_ftrace_graph(void) ftrace_shutdown(&global_ops, FTRACE_STOP_FUNC_RET); global_ops.flags &= ~FTRACE_OPS_FL_STUB; #ifdef CONFIG_DYNAMIC_FTRACE - global_ops.trampoline = 0; + if (FTRACE_GRAPH_TRAMP_ADDR != 0) + global_ops.trampoline = 0; #endif unregister_pm_notifier(&ftrace_suspend_notifier); unregister_trace_sched_switch(ftrace_graph_probe_sched_switch, NULL); -- cgit v1.2.3 From 8ecef00fe1f33658ee36e902dba6850b51312073 Mon Sep 17 00:00:00 2001 From: Ulrich Hecht Date: Thu, 10 Jul 2014 09:53:59 +0200 Subject: usb: renesas_usbhs: add R-Car Gen. 2 init and power control In preparation for DT conversion to reduce reliance on platform device callbacks. Signed-off-by: Ulrich Hecht Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/Makefile | 2 +- drivers/usb/renesas_usbhs/common.c | 66 +++++++++++++++++++++++++++++--- drivers/usb/renesas_usbhs/common.h | 2 + drivers/usb/renesas_usbhs/rcar2.c | 77 ++++++++++++++++++++++++++++++++++++++ drivers/usb/renesas_usbhs/rcar2.h | 4 ++ include/linux/usb/renesas_usbhs.h | 6 +++ 6 files changed, 151 insertions(+), 6 deletions(-) create mode 100644 drivers/usb/renesas_usbhs/rcar2.c create mode 100644 drivers/usb/renesas_usbhs/rcar2.h (limited to 'include') diff --git a/drivers/usb/renesas_usbhs/Makefile b/drivers/usb/renesas_usbhs/Makefile index bc8aef4311a1..9e47f477b6d2 100644 --- a/drivers/usb/renesas_usbhs/Makefile +++ b/drivers/usb/renesas_usbhs/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs.o -renesas_usbhs-y := common.o mod.o pipe.o fifo.o +renesas_usbhs-y := common.o mod.o pipe.o fifo.o rcar2.o ifneq ($(CONFIG_USB_RENESAS_USBHS_HCD),) renesas_usbhs-y += mod_host.o diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 17267b0a2e95..1b9bf8d83235 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -15,12 +15,14 @@ * */ #include +#include #include #include #include #include #include #include "common.h" +#include "rcar2.h" /* * image of renesas_usbhs @@ -284,6 +286,8 @@ static void usbhsc_set_buswait(struct usbhs_priv *priv) /* * platform default param */ + +/* commonly used on old SH-Mobile SoCs */ static u32 usbhsc_default_pipe_type[] = { USB_ENDPOINT_XFER_CONTROL, USB_ENDPOINT_XFER_ISOC, @@ -297,6 +301,26 @@ static u32 usbhsc_default_pipe_type[] = { USB_ENDPOINT_XFER_INT, }; +/* commonly used on newer SH-Mobile and R-Car SoCs */ +static u32 usbhsc_new_pipe_type[] = { + USB_ENDPOINT_XFER_CONTROL, + USB_ENDPOINT_XFER_ISOC, + USB_ENDPOINT_XFER_ISOC, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_INT, + USB_ENDPOINT_XFER_INT, + USB_ENDPOINT_XFER_INT, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, + USB_ENDPOINT_XFER_BULK, +}; + /* * power control */ @@ -423,8 +447,7 @@ static int usbhs_probe(struct platform_device *pdev) int ret; /* check platform information */ - if (!info || - !info->platform_callback.get_id) { + if (!info) { dev_err(&pdev->dev, "no platform information\n"); return -EINVAL; } @@ -451,13 +474,32 @@ static int usbhs_probe(struct platform_device *pdev) /* * care platform info */ - memcpy(&priv->pfunc, - &info->platform_callback, - sizeof(struct renesas_usbhs_platform_callback)); + memcpy(&priv->dparam, &info->driver_param, sizeof(struct renesas_usbhs_driver_param)); + switch (priv->dparam.type) { + case USBHS_TYPE_R8A7790: + case USBHS_TYPE_R8A7791: + priv->pfunc = usbhs_rcar2_ops; + if (!priv->dparam.pipe_type) { + priv->dparam.pipe_type = usbhsc_new_pipe_type; + priv->dparam.pipe_size = + ARRAY_SIZE(usbhsc_new_pipe_type); + } + break; + default: + if (!info->platform_callback.get_id) { + dev_err(&pdev->dev, "no platform callbacks"); + return -EINVAL; + } + memcpy(&priv->pfunc, + &info->platform_callback, + sizeof(struct renesas_usbhs_platform_callback)); + break; + } + /* set driver callback functions for platform */ dfunc = &info->driver_callback; dfunc->notify_hotplug = usbhsc_drvcllbck_notify_hotplug; @@ -507,6 +549,20 @@ static int usbhs_probe(struct platform_device *pdev) */ usbhs_sys_clock_ctrl(priv, 0); + /* check GPIO determining if USB function should be enabled */ + if (priv->dparam.enable_gpio) { + gpio_request_one(priv->dparam.enable_gpio, GPIOF_IN, NULL); + ret = !gpio_get_value(priv->dparam.enable_gpio); + gpio_free(priv->dparam.enable_gpio); + if (ret) { + dev_warn(&pdev->dev, + "USB function not selected (GPIO %d)\n", + priv->dparam.enable_gpio); + ret = -ENOTSUPP; + goto probe_end_mod_exit; + } + } + /* * platform call * diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index c69dd2fba360..a7996da6a1bd 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -268,6 +268,8 @@ struct usbhs_priv { * fifo control */ struct usbhs_fifo_info fifo_info; + + struct usb_phy *phy; }; /* diff --git a/drivers/usb/renesas_usbhs/rcar2.c b/drivers/usb/renesas_usbhs/rcar2.c new file mode 100644 index 000000000000..e6b9dcc1c289 --- /dev/null +++ b/drivers/usb/renesas_usbhs/rcar2.c @@ -0,0 +1,77 @@ +/* + * Renesas USB driver R-Car Gen. 2 initialization and power control + * + * Copyright (C) 2014 Ulrich Hecht + * + * 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 "common.h" +#include "rcar2.h" + +static int usbhs_rcar2_hardware_init(struct platform_device *pdev) +{ + struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); + struct usb_phy *phy; + + phy = usb_get_phy_dev(&pdev->dev, 0); + if (IS_ERR(phy)) + return PTR_ERR(phy); + + priv->phy = phy; + return 0; +} + +static int usbhs_rcar2_hardware_exit(struct platform_device *pdev) +{ + struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); + + if (!priv->phy) + return 0; + + usb_put_phy(priv->phy); + priv->phy = NULL; + + return 0; +} + +static int usbhs_rcar2_power_ctrl(struct platform_device *pdev, + void __iomem *base, int enable) +{ + struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); + + if (!priv->phy) + return -ENODEV; + + if (enable) { + int retval = usb_phy_init(priv->phy); + + if (!retval) + retval = usb_phy_set_suspend(priv->phy, 0); + return retval; + } + + usb_phy_set_suspend(priv->phy, 1); + usb_phy_shutdown(priv->phy); + return 0; +} + +static int usbhs_rcar2_get_id(struct platform_device *pdev) +{ + return USBHS_GADGET; +} + +const struct renesas_usbhs_platform_callback usbhs_rcar2_ops = { + .hardware_init = usbhs_rcar2_hardware_init, + .hardware_exit = usbhs_rcar2_hardware_exit, + .power_ctrl = usbhs_rcar2_power_ctrl, + .get_id = usbhs_rcar2_get_id, +}; diff --git a/drivers/usb/renesas_usbhs/rcar2.h b/drivers/usb/renesas_usbhs/rcar2.h new file mode 100644 index 000000000000..f07f10d9b3b2 --- /dev/null +++ b/drivers/usb/renesas_usbhs/rcar2.h @@ -0,0 +1,4 @@ +#include "common.h" + +extern const struct renesas_usbhs_platform_callback + usbhs_rcar2_ops; diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h index e452ba6ec6bd..d5952bb66752 100644 --- a/include/linux/usb/renesas_usbhs.h +++ b/include/linux/usb/renesas_usbhs.h @@ -153,6 +153,9 @@ struct renesas_usbhs_driver_param { */ int pio_dma_border; /* default is 64byte */ + u32 type; + u32 enable_gpio; + /* * option: */ @@ -160,6 +163,9 @@ struct renesas_usbhs_driver_param { u32 has_sudmac:1; /* for SUDMAC */ }; +#define USBHS_TYPE_R8A7790 1 +#define USBHS_TYPE_R8A7791 2 + /* * option: * -- cgit v1.2.3 From 8fe8bc7773303e3c49be348c3180bc9785104dfc Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 9 Jul 2014 16:25:23 +0200 Subject: i2c: s6000: remove duplicate driver It turned out that the s6000 simply has a designware IP core and should use the designated driver for it which is way more maintained and feature complete. There are currently no users in tree, and not even a toolchain for s6000 seems to be available. So, simply remove this duplicate. If someone needs assistance in converting to the designware driver, the i2c list will be there to help. Signed-off-by: Wolfram Sang --- drivers/i2c/busses/Kconfig | 10 - drivers/i2c/busses/Makefile | 1 - drivers/i2c/busses/i2c-s6000.c | 404 ----------------------------------------- drivers/i2c/busses/i2c-s6000.h | 79 -------- include/linux/i2c/s6000.h | 10 - 5 files changed, 504 deletions(-) delete mode 100644 drivers/i2c/busses/i2c-s6000.c delete mode 100644 drivers/i2c/busses/i2c-s6000.h delete mode 100644 include/linux/i2c/s6000.h (limited to 'include') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 9f7d5859cf65..d25dd120c011 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -700,16 +700,6 @@ config I2C_S3C2410 Say Y here to include support for I2C controller in the Samsung SoCs. -config I2C_S6000 - tristate "S6000 I2C support" - depends on XTENSA_VARIANT_S6000 - help - This driver supports the on chip I2C device on the - S6000 xtensa processor family. - - To compile this driver as a module, choose M here. The module - will be called i2c-s6000. - config I2C_SH7760 tristate "Renesas SH7760 I2C Controller" depends on CPU_SUBTYPE_SH7760 diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index dd9a7f8e873f..1958b490105e 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -68,7 +68,6 @@ obj-$(CONFIG_I2C_QUP) += i2c-qup.o obj-$(CONFIG_I2C_RIIC) += i2c-riic.o obj-$(CONFIG_I2C_RK3X) += i2c-rk3x.o obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o -obj-$(CONFIG_I2C_S6000) += i2c-s6000.o obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o diff --git a/drivers/i2c/busses/i2c-s6000.c b/drivers/i2c/busses/i2c-s6000.c deleted file mode 100644 index dd186a037684..000000000000 --- a/drivers/i2c/busses/i2c-s6000.c +++ /dev/null @@ -1,404 +0,0 @@ -/* - * drivers/i2c/busses/i2c-s6000.c - * - * Description: Driver for S6000 Family I2C Interface - * Copyright (c) 2008 emlix GmbH - * Author: Oskar Schirmer - * - * Partially based on i2c-bfin-twi.c driver by - * Copyright (c) 2005-2007 Analog Devices, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "i2c-s6000.h" - -#define DRV_NAME "i2c-s6000" - -#define POLL_TIMEOUT (2 * HZ) - -struct s6i2c_if { - u8 __iomem *reg; /* memory mapped registers */ - int irq; - spinlock_t lock; - struct i2c_msg *msgs; /* messages currently handled */ - int msgs_num; /* nb of msgs to do */ - int msgs_push; /* nb of msgs read/written */ - int msgs_done; /* nb of msgs finally handled */ - unsigned push; /* nb of bytes read/written in msg */ - unsigned done; /* nb of bytes finally handled */ - int timeout_count; /* timeout retries left */ - struct timer_list timeout_timer; - struct i2c_adapter adap; - struct completion complete; - struct clk *clk; - struct resource *res; -}; - -static inline u16 i2c_rd16(struct s6i2c_if *iface, unsigned n) -{ - return readw(iface->reg + (n)); -} - -static inline void i2c_wr16(struct s6i2c_if *iface, unsigned n, u16 v) -{ - writew(v, iface->reg + (n)); -} - -static inline u32 i2c_rd32(struct s6i2c_if *iface, unsigned n) -{ - return readl(iface->reg + (n)); -} - -static inline void i2c_wr32(struct s6i2c_if *iface, unsigned n, u32 v) -{ - writel(v, iface->reg + (n)); -} - -static struct s6i2c_if s6i2c_if; - -static void s6i2c_handle_interrupt(struct s6i2c_if *iface) -{ - if (i2c_rd16(iface, S6_I2C_INTRSTAT) & (1 << S6_I2C_INTR_TXABRT)) { - i2c_rd16(iface, S6_I2C_CLRTXABRT); - i2c_wr16(iface, S6_I2C_INTRMASK, 0); - complete(&iface->complete); - return; - } - if (iface->msgs_done >= iface->msgs_num) { - dev_err(&iface->adap.dev, "s6i2c: spurious I2C irq: %04x\n", - i2c_rd16(iface, S6_I2C_INTRSTAT)); - i2c_wr16(iface, S6_I2C_INTRMASK, 0); - return; - } - while ((iface->msgs_push < iface->msgs_num) - && (i2c_rd16(iface, S6_I2C_STATUS) & (1 << S6_I2C_STATUS_TFNF))) { - struct i2c_msg *m = &iface->msgs[iface->msgs_push]; - if (!(m->flags & I2C_M_RD)) - i2c_wr16(iface, S6_I2C_DATACMD, m->buf[iface->push]); - else - i2c_wr16(iface, S6_I2C_DATACMD, - 1 << S6_I2C_DATACMD_READ); - if (++iface->push >= m->len) { - iface->push = 0; - iface->msgs_push += 1; - } - } - do { - struct i2c_msg *m = &iface->msgs[iface->msgs_done]; - if (!(m->flags & I2C_M_RD)) { - if (iface->msgs_done < iface->msgs_push) - iface->msgs_done += 1; - else - break; - } else if (i2c_rd16(iface, S6_I2C_STATUS) - & (1 << S6_I2C_STATUS_RFNE)) { - m->buf[iface->done] = i2c_rd16(iface, S6_I2C_DATACMD); - if (++iface->done >= m->len) { - iface->done = 0; - iface->msgs_done += 1; - } - } else{ - break; - } - } while (iface->msgs_done < iface->msgs_num); - if (iface->msgs_done >= iface->msgs_num) { - i2c_wr16(iface, S6_I2C_INTRMASK, 1 << S6_I2C_INTR_TXABRT); - complete(&iface->complete); - } else if (iface->msgs_push >= iface->msgs_num) { - i2c_wr16(iface, S6_I2C_INTRMASK, (1 << S6_I2C_INTR_TXABRT) | - (1 << S6_I2C_INTR_RXFULL)); - } else { - i2c_wr16(iface, S6_I2C_INTRMASK, (1 << S6_I2C_INTR_TXABRT) | - (1 << S6_I2C_INTR_TXEMPTY) | - (1 << S6_I2C_INTR_RXFULL)); - } -} - -static irqreturn_t s6i2c_interrupt_entry(int irq, void *dev_id) -{ - struct s6i2c_if *iface = dev_id; - if (!(i2c_rd16(iface, S6_I2C_STATUS) & ((1 << S6_I2C_INTR_RXUNDER) - | (1 << S6_I2C_INTR_RXOVER) - | (1 << S6_I2C_INTR_RXFULL) - | (1 << S6_I2C_INTR_TXOVER) - | (1 << S6_I2C_INTR_TXEMPTY) - | (1 << S6_I2C_INTR_RDREQ) - | (1 << S6_I2C_INTR_TXABRT) - | (1 << S6_I2C_INTR_RXDONE) - | (1 << S6_I2C_INTR_ACTIVITY) - | (1 << S6_I2C_INTR_STOPDET) - | (1 << S6_I2C_INTR_STARTDET) - | (1 << S6_I2C_INTR_GENCALL)))) - return IRQ_NONE; - - spin_lock(&iface->lock); - del_timer(&iface->timeout_timer); - s6i2c_handle_interrupt(iface); - spin_unlock(&iface->lock); - return IRQ_HANDLED; -} - -static void s6i2c_timeout(unsigned long data) -{ - struct s6i2c_if *iface = (struct s6i2c_if *)data; - unsigned long flags; - - spin_lock_irqsave(&iface->lock, flags); - s6i2c_handle_interrupt(iface); - if (--iface->timeout_count > 0) { - iface->timeout_timer.expires = jiffies + POLL_TIMEOUT; - add_timer(&iface->timeout_timer); - } else { - complete(&iface->complete); - i2c_wr16(iface, S6_I2C_INTRMASK, 0); - } - spin_unlock_irqrestore(&iface->lock, flags); -} - -static int s6i2c_master_xfer(struct i2c_adapter *adap, - struct i2c_msg *msgs, int num) -{ - struct s6i2c_if *iface = adap->algo_data; - int i; - if (num == 0) - return 0; - if (i2c_rd16(iface, S6_I2C_STATUS) & (1 << S6_I2C_STATUS_ACTIVITY)) - yield(); - i2c_wr16(iface, S6_I2C_INTRMASK, 0); - i2c_rd16(iface, S6_I2C_CLRINTR); - for (i = 0; i < num; i++) { - if (msgs[i].flags & I2C_M_TEN) { - dev_err(&adap->dev, - "s6i2c: 10 bits addr not supported\n"); - return -EINVAL; - } - if (msgs[i].len == 0) { - dev_err(&adap->dev, - "s6i2c: zero length message not supported\n"); - return -EINVAL; - } - if (msgs[i].addr != msgs[0].addr) { - dev_err(&adap->dev, - "s6i2c: multiple xfer cannot change target\n"); - return -EINVAL; - } - } - - iface->msgs = msgs; - iface->msgs_num = num; - iface->msgs_push = 0; - iface->msgs_done = 0; - iface->push = 0; - iface->done = 0; - iface->timeout_count = 10; - i2c_wr16(iface, S6_I2C_TAR, msgs[0].addr); - i2c_wr16(iface, S6_I2C_ENABLE, 1); - i2c_wr16(iface, S6_I2C_INTRMASK, (1 << S6_I2C_INTR_TXEMPTY) | - (1 << S6_I2C_INTR_TXABRT)); - - iface->timeout_timer.expires = jiffies + POLL_TIMEOUT; - add_timer(&iface->timeout_timer); - wait_for_completion(&iface->complete); - del_timer_sync(&iface->timeout_timer); - while (i2c_rd32(iface, S6_I2C_TXFLR) > 0) - schedule(); - while (i2c_rd16(iface, S6_I2C_STATUS) & (1 << S6_I2C_STATUS_ACTIVITY)) - schedule(); - - i2c_wr16(iface, S6_I2C_INTRMASK, 0); - i2c_wr16(iface, S6_I2C_ENABLE, 0); - return iface->msgs_done; -} - -static u32 s6i2c_functionality(struct i2c_adapter *adap) -{ - return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; -} - -static struct i2c_algorithm s6i2c_algorithm = { - .master_xfer = s6i2c_master_xfer, - .functionality = s6i2c_functionality, -}; - -static u16 nanoseconds_on_clk(struct s6i2c_if *iface, u32 ns) -{ - u32 dividend = ((clk_get_rate(iface->clk) / 1000) * ns) / 1000000; - if (dividend > 0xffff) - return 0xffff; - return dividend; -} - -static int s6i2c_probe(struct platform_device *dev) -{ - struct s6i2c_if *iface = &s6i2c_if; - struct i2c_adapter *p_adap; - const char *clock; - int bus_num, rc; - spin_lock_init(&iface->lock); - init_completion(&iface->complete); - iface->irq = platform_get_irq(dev, 0); - if (iface->irq < 0) { - rc = iface->irq; - goto err_out; - } - iface->res = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!iface->res) { - rc = -ENXIO; - goto err_out; - } - iface->res = request_mem_region(iface->res->start, - resource_size(iface->res), - dev->dev.bus_id); - if (!iface->res) { - rc = -EBUSY; - goto err_out; - } - iface->reg = ioremap_nocache(iface->res->start, - resource_size(iface->res)); - if (!iface->reg) { - rc = -ENOMEM; - goto err_reg; - } - - clock = 0; - bus_num = -1; - if (dev_get_platdata(&dev->dev)) { - struct s6_i2c_platform_data *pdata = - dev_get_platdata(&dev->dev); - bus_num = pdata->bus_num; - clock = pdata->clock; - } - iface->clk = clk_get(&dev->dev, clock); - if (IS_ERR(iface->clk)) { - rc = PTR_ERR(iface->clk); - goto err_map; - } - rc = clk_enable(iface->clk); - if (rc < 0) - goto err_clk_put; - init_timer(&iface->timeout_timer); - iface->timeout_timer.function = s6i2c_timeout; - iface->timeout_timer.data = (unsigned long)iface; - - p_adap = &iface->adap; - strlcpy(p_adap->name, dev->name, sizeof(p_adap->name)); - p_adap->algo = &s6i2c_algorithm; - p_adap->algo_data = iface; - p_adap->nr = bus_num; - p_adap->class = 0; - p_adap->dev.parent = &dev->dev; - i2c_wr16(iface, S6_I2C_INTRMASK, 0); - rc = request_irq(iface->irq, s6i2c_interrupt_entry, - IRQF_SHARED, dev->name, iface); - if (rc) { - dev_err(&p_adap->dev, "s6i2c: can't get IRQ %d\n", iface->irq); - goto err_clk_dis; - } - - i2c_wr16(iface, S6_I2C_ENABLE, 0); - udelay(1); - i2c_wr32(iface, S6_I2C_SRESET, 1 << S6_I2C_SRESET_IC_SRST); - i2c_wr16(iface, S6_I2C_CLRTXABRT, 1); - i2c_wr16(iface, S6_I2C_CON, - (1 << S6_I2C_CON_MASTER) | - (S6_I2C_CON_SPEED_NORMAL << S6_I2C_CON_SPEED) | - (0 << S6_I2C_CON_10BITSLAVE) | - (0 << S6_I2C_CON_10BITMASTER) | - (1 << S6_I2C_CON_RESTARTENA) | - (1 << S6_I2C_CON_SLAVEDISABLE)); - i2c_wr16(iface, S6_I2C_SSHCNT, nanoseconds_on_clk(iface, 4000)); - i2c_wr16(iface, S6_I2C_SSLCNT, nanoseconds_on_clk(iface, 4700)); - i2c_wr16(iface, S6_I2C_FSHCNT, nanoseconds_on_clk(iface, 600)); - i2c_wr16(iface, S6_I2C_FSLCNT, nanoseconds_on_clk(iface, 1300)); - i2c_wr16(iface, S6_I2C_RXTL, 0); - i2c_wr16(iface, S6_I2C_TXTL, 0); - - platform_set_drvdata(dev, iface); - rc = i2c_add_numbered_adapter(p_adap); - if (rc) - goto err_irq_free; - return 0; - -err_irq_free: - free_irq(iface->irq, iface); -err_clk_dis: - clk_disable(iface->clk); -err_clk_put: - clk_put(iface->clk); -err_map: - iounmap(iface->reg); -err_reg: - release_mem_region(iface->res->start, - resource_size(iface->res)); -err_out: - return rc; -} - -static int s6i2c_remove(struct platform_device *pdev) -{ - struct s6i2c_if *iface = platform_get_drvdata(pdev); - i2c_wr16(iface, S6_I2C_ENABLE, 0); - i2c_del_adapter(&iface->adap); - free_irq(iface->irq, iface); - clk_disable(iface->clk); - clk_put(iface->clk); - iounmap(iface->reg); - release_mem_region(iface->res->start, - resource_size(iface->res)); - return 0; -} - -static struct platform_driver s6i2c_driver = { - .probe = s6i2c_probe, - .remove = s6i2c_remove, - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, - }, -}; - -static int __init s6i2c_init(void) -{ - pr_info("I2C: S6000 I2C driver\n"); - return platform_driver_register(&s6i2c_driver); -} - -static void __exit s6i2c_exit(void) -{ - platform_driver_unregister(&s6i2c_driver); -} - -MODULE_DESCRIPTION("I2C-Bus adapter routines for S6000 I2C"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRV_NAME); - -subsys_initcall(s6i2c_init); -module_exit(s6i2c_exit); diff --git a/drivers/i2c/busses/i2c-s6000.h b/drivers/i2c/busses/i2c-s6000.h deleted file mode 100644 index 4936f9f2256f..000000000000 --- a/drivers/i2c/busses/i2c-s6000.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * drivers/i2c/busses/i2c-s6000.h - * - * 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. - * - * Copyright (C) 2008 Emlix GmbH - * Author: Oskar Schirmer - */ - -#ifndef __DRIVERS_I2C_BUSSES_I2C_S6000_H -#define __DRIVERS_I2C_BUSSES_I2C_S6000_H - -#define S6_I2C_CON 0x000 -#define S6_I2C_CON_MASTER 0 -#define S6_I2C_CON_SPEED 1 -#define S6_I2C_CON_SPEED_NORMAL 1 -#define S6_I2C_CON_SPEED_FAST 2 -#define S6_I2C_CON_SPEED_MASK 3 -#define S6_I2C_CON_10BITSLAVE 3 -#define S6_I2C_CON_10BITMASTER 4 -#define S6_I2C_CON_RESTARTENA 5 -#define S6_I2C_CON_SLAVEDISABLE 6 -#define S6_I2C_TAR 0x004 -#define S6_I2C_TAR_GCORSTART 10 -#define S6_I2C_TAR_SPECIAL 11 -#define S6_I2C_SAR 0x008 -#define S6_I2C_HSMADDR 0x00C -#define S6_I2C_DATACMD 0x010 -#define S6_I2C_DATACMD_READ 8 -#define S6_I2C_SSHCNT 0x014 -#define S6_I2C_SSLCNT 0x018 -#define S6_I2C_FSHCNT 0x01C -#define S6_I2C_FSLCNT 0x020 -#define S6_I2C_INTRSTAT 0x02C -#define S6_I2C_INTRMASK 0x030 -#define S6_I2C_RAWINTR 0x034 -#define S6_I2C_INTR_RXUNDER 0 -#define S6_I2C_INTR_RXOVER 1 -#define S6_I2C_INTR_RXFULL 2 -#define S6_I2C_INTR_TXOVER 3 -#define S6_I2C_INTR_TXEMPTY 4 -#define S6_I2C_INTR_RDREQ 5 -#define S6_I2C_INTR_TXABRT 6 -#define S6_I2C_INTR_RXDONE 7 -#define S6_I2C_INTR_ACTIVITY 8 -#define S6_I2C_INTR_STOPDET 9 -#define S6_I2C_INTR_STARTDET 10 -#define S6_I2C_INTR_GENCALL 11 -#define S6_I2C_RXTL 0x038 -#define S6_I2C_TXTL 0x03C -#define S6_I2C_CLRINTR 0x040 -#define S6_I2C_CLRRXUNDER 0x044 -#define S6_I2C_CLRRXOVER 0x048 -#define S6_I2C_CLRTXOVER 0x04C -#define S6_I2C_CLRRDREQ 0x050 -#define S6_I2C_CLRTXABRT 0x054 -#define S6_I2C_CLRRXDONE 0x058 -#define S6_I2C_CLRACTIVITY 0x05C -#define S6_I2C_CLRSTOPDET 0x060 -#define S6_I2C_CLRSTARTDET 0x064 -#define S6_I2C_CLRGENCALL 0x068 -#define S6_I2C_ENABLE 0x06C -#define S6_I2C_STATUS 0x070 -#define S6_I2C_STATUS_ACTIVITY 0 -#define S6_I2C_STATUS_TFNF 1 -#define S6_I2C_STATUS_TFE 2 -#define S6_I2C_STATUS_RFNE 3 -#define S6_I2C_STATUS_RFF 4 -#define S6_I2C_TXFLR 0x074 -#define S6_I2C_RXFLR 0x078 -#define S6_I2C_SRESET 0x07C -#define S6_I2C_SRESET_IC_SRST 0 -#define S6_I2C_SRESET_IC_MASTER_SRST 1 -#define S6_I2C_SRESET_IC_SLAVE_SRST 2 -#define S6_I2C_TXABRTSOURCE 0x080 - -#endif diff --git a/include/linux/i2c/s6000.h b/include/linux/i2c/s6000.h deleted file mode 100644 index d9b34bfdae76..000000000000 --- a/include/linux/i2c/s6000.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef __LINUX_I2C_S6000_H -#define __LINUX_I2C_S6000_H - -struct s6_i2c_platform_data { - const char *clock; /* the clock to use */ - int bus_num; /* the bus number to register */ -}; - -#endif - -- cgit v1.2.3 From 3e5454d6568c203bca712e1976b052c345f47b44 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Tue, 15 Jul 2014 20:48:13 +0200 Subject: tracing: Kill destroy_preds() and destroy_file_preds() destroy_preds() makes no sense. The only caller, event_remove(), actually wants destroy_file_preds(). __trace_remove_event_call() does destroy_call_preds() which takes care of call->filter. And after the previous change we can simply remove destroy_preds() from event_remove(), we are going to call remove_event_from_tracers() which in turn calls remove_event_file_dir()->free_event_filter(). Link: http://lkml.kernel.org/p/20140715184813.GA20488@redhat.com Signed-off-by: Oleg Nesterov Signed-off-by: Steven Rostedt --- include/linux/ftrace_event.h | 1 - kernel/trace/trace_events.c | 1 - kernel/trace/trace_events_filter.c | 20 -------------------- 3 files changed, 22 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index cff3106ffe2c..738d46539269 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -404,7 +404,6 @@ enum event_trigger_type { ETT_EVENT_ENABLE = (1 << 3), }; -extern void destroy_preds(struct ftrace_event_file *file); extern void destroy_call_preds(struct ftrace_event_call *call); extern int filter_match_preds(struct event_filter *filter, void *rec); diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 2de53628689f..85914edf5059 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -1621,7 +1621,6 @@ static void event_remove(struct ftrace_event_call *call) if (file->event_call != call) continue; ftrace_event_enable_disable(file, 0); - destroy_preds(file); /* * The do_for_each_event_file() is * a double loop. After finding the call for this diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index 8a8631926a07..30fc66f5cdca 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c @@ -810,26 +810,6 @@ void destroy_call_preds(struct ftrace_event_call *call) call->filter = NULL; } -static void destroy_file_preds(struct ftrace_event_file *file) -{ - __free_filter(file->filter); - file->filter = NULL; -} - -/* - * Called when destroying the ftrace_event_file. - * The file is being freed, so we do not need to worry about - * the file being currently used. This is for module code removing - * the tracepoints from within it. - */ -void destroy_preds(struct ftrace_event_file *file) -{ - if (file->event_call->flags & TRACE_EVENT_FL_USE_CALL_FILTER) - destroy_call_preds(file->event_call); - else - destroy_file_preds(file); -} - static struct event_filter *__alloc_filter(void) { struct event_filter *filter; -- cgit v1.2.3 From 57375747b6fac0f6cf7b302c4a8adb9043ea8e3b Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Tue, 15 Jul 2014 20:48:16 +0200 Subject: tracing: Kill destroy_call_preds() Remove destroy_call_preds(). Its only caller, __trace_remove_event_call(), can use free_event_filter() and nullify ->filter by hand. Perhaps we could keep this trivial helper although imo it is pointless, but then it should be static in trace_events.c. Link: http://lkml.kernel.org/p/20140715184816.GA20495@redhat.com Signed-off-by: Oleg Nesterov Signed-off-by: Steven Rostedt --- include/linux/ftrace_event.h | 1 - kernel/trace/trace_events.c | 3 ++- kernel/trace/trace_events_filter.c | 6 ------ 3 files changed, 2 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index 738d46539269..f434d75e083b 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -404,7 +404,6 @@ enum event_trigger_type { ETT_EVENT_ENABLE = (1 << 3), }; -extern void destroy_call_preds(struct ftrace_event_call *call); extern int filter_match_preds(struct event_filter *filter, void *rec); extern int filter_check_discard(struct ftrace_event_file *file, void *rec, diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 85914edf5059..0d8ee29f6b9a 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -1748,7 +1748,8 @@ static void __trace_remove_event_call(struct ftrace_event_call *call) { event_remove(call); trace_destroy_fields(call); - destroy_call_preds(call); + free_event_filter(call->filter); + call->filter = NULL; } static int probe_remove_event_call(struct ftrace_event_call *call) diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index 30fc66f5cdca..1edec329be29 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c @@ -804,12 +804,6 @@ void free_event_filter(struct event_filter *filter) __free_filter(filter); } -void destroy_call_preds(struct ftrace_event_call *call) -{ - __free_filter(call->filter); - call->filter = NULL; -} - static struct event_filter *__alloc_filter(void) { struct event_filter *filter; -- cgit v1.2.3 From e738d3ebbf70ebadb83f3a4060dc34f422c2fbe7 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Tue, 15 Jul 2014 20:48:27 +0200 Subject: tracing: Kill ftrace_event_call->files Remove ftrace_event_call->files. It has no users, and in fact even the commit ae63b31e4d0e "tracing: Separate out trace events from global variables" which added this member did not use it. Link: http://lkml.kernel.org/p/20140715184827.GA20508@redhat.com Signed-off-by: Oleg Nesterov Signed-off-by: Steven Rostedt --- include/linux/ftrace_event.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index f434d75e083b..06c6faa9e5cc 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -272,7 +272,6 @@ struct ftrace_event_call { struct trace_event event; const char *print_fmt; struct event_filter *filter; - struct list_head *files; void *mod; void *data; /* -- cgit v1.2.3 From 4cc901613bd79dfa22d8aea996c2e9f74c04f8f2 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Tue, 8 Jul 2014 10:08:36 +0800 Subject: PCI/MSI: Remove unused function msi_remove_pci_irq_vectors() msi_remove_pci_irq_vectors() is unused, so remove it. Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas --- drivers/pci/msi.c | 18 ------------------ include/linux/pci.h | 2 -- 2 files changed, 20 deletions(-) (limited to 'include') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index a59d673d074e..dd0a259e3aaa 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -1014,24 +1014,6 @@ void pci_disable_msix(struct pci_dev *dev) } EXPORT_SYMBOL(pci_disable_msix); -/** - * msi_remove_pci_irq_vectors - reclaim MSI(X) irqs to unused state - * @dev: pointer to the pci_dev data structure of MSI(X) device function - * - * Being called during hotplug remove, from which the device function - * is hot-removed. All previous assigned MSI/MSI-X irqs, if - * allocated for this device function, are reclaimed to unused state, - * which may be used later on. - **/ -void msi_remove_pci_irq_vectors(struct pci_dev *dev) -{ - if (!pci_msi_enable || !dev) - return; - - if (dev->msi_enabled || dev->msix_enabled) - free_msi_irqs(dev); -} - void pci_no_msi(void) { pci_msi_enable = 0; diff --git a/include/linux/pci.h b/include/linux/pci.h index 466bcd111d85..66bd22fec38f 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1186,7 +1186,6 @@ int pci_msix_vec_count(struct pci_dev *dev); int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec); void pci_msix_shutdown(struct pci_dev *dev); void pci_disable_msix(struct pci_dev *dev); -void msi_remove_pci_irq_vectors(struct pci_dev *dev); void pci_restore_msi_state(struct pci_dev *dev); int pci_msi_enabled(void); int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec); @@ -1217,7 +1216,6 @@ static inline int pci_enable_msix(struct pci_dev *dev, { return -ENOSYS; } static inline void pci_msix_shutdown(struct pci_dev *dev) { } static inline void pci_disable_msix(struct pci_dev *dev) { } -static inline void msi_remove_pci_irq_vectors(struct pci_dev *dev) { } static inline void pci_restore_msi_state(struct pci_dev *dev) { } static inline int pci_msi_enabled(void) { return 0; } static inline int pci_enable_msi_range(struct pci_dev *dev, int minvec, -- cgit v1.2.3 From 63b949382c5f263746b1c177f6ff84de2201ae9d Mon Sep 17 00:00:00 2001 From: Geir Ola Vaagland Date: Sat, 12 Jul 2014 20:30:36 +0200 Subject: net: sctp: implement rfc6458, 5.3.4. SCTP_SNDINFO cmsg support This patch implements section 5.3.4. of RFC6458, that is, support for 'SCTP Send Information Structure' (SCTP_SNDINFO) which can be placed into ancillary data cmsghdr structure for sendmsg() calls. The sctp_sndinfo structure is defined as per RFC as below ... struct sctp_sndinfo { uint16_t snd_sid; uint16_t snd_flags; uint32_t snd_ppid; uint32_t snd_context; sctp_assoc_t snd_assoc_id; }; ... and supplied under cmsg_level IPPROTO_SCTP, cmsg_type SCTP_SNDINFO, while cmsg_data[] contains struct sctp_sndinfo. An sctp_sndinfo item always corresponds to the data in msg_iov. Joint work with Daniel Borkmann. Signed-off-by: Geir Ola Vaagland Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 3 +- include/uapi/linux/sctp.h | 22 +++++++++++-- net/sctp/socket.c | 77 ++++++++++++++++++++++++++++++++++------------ 3 files changed, 79 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index f38588bf3462..7af9a0f5d8ce 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1919,7 +1919,8 @@ struct sctp_chunk *sctp_get_ecne_prepend(struct sctp_association *asoc); /* A convenience structure to parse out SCTP specific CMSGs. */ typedef struct sctp_cmsgs { struct sctp_initmsg *init; - struct sctp_sndrcvinfo *info; + struct sctp_sndrcvinfo *srinfo; + struct sctp_sndinfo *sinfo; } sctp_cmsgs_t; /* Structure for tracking memory objects */ diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h index 266022a2be4a..a387761f7e02 100644 --- a/include/uapi/linux/sctp.h +++ b/include/uapi/linux/sctp.h @@ -154,6 +154,22 @@ struct sctp_sndrcvinfo { sctp_assoc_t sinfo_assoc_id; }; +/* 5.3.4 SCTP Send Information Structure (SCTP_SNDINFO) + * + * This cmsghdr structure specifies SCTP options for sendmsg(). + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ ------------------- + * IPPROTO_SCTP SCTP_SNDINFO struct sctp_sndinfo + */ +struct sctp_sndinfo { + __u16 snd_sid; + __u16 snd_flags; + __u32 snd_ppid; + __u32 snd_context; + sctp_assoc_t snd_assoc_id; +}; + /* * sinfo_flags: 16 bits (unsigned integer) * @@ -177,10 +193,12 @@ typedef union { /* These are cmsg_types. */ typedef enum sctp_cmsg_type { - SCTP_INIT, /* 5.2.1 SCTP Initiation Structure */ + SCTP_INIT, /* 5.2.1 SCTP Initiation Structure */ #define SCTP_INIT SCTP_INIT - SCTP_SNDRCV, /* 5.2.2 SCTP Header Information Structure */ + SCTP_SNDRCV, /* 5.2.2 SCTP Header Information Structure */ #define SCTP_SNDRCV SCTP_SNDRCV + SCTP_SNDINFO, /* 5.3.4 SCTP Send Information Structure */ +#define SCTP_SNDINFO SCTP_SNDINFO } sctp_cmsg_t; /* diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 429899689408..d61729e99856 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1602,12 +1602,13 @@ static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, struct sctp_initmsg *sinit; sctp_assoc_t associd = 0; sctp_cmsgs_t cmsgs = { NULL }; - int err; sctp_scope_t scope; - long timeo; - __u16 sinfo_flags = 0; + bool fill_sinfo_ttl = false; struct sctp_datamsg *datamsg; int msg_flags = msg->msg_flags; + __u16 sinfo_flags = 0; + long timeo; + int err; err = 0; sp = sctp_sk(sk); @@ -1648,10 +1649,21 @@ static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, msg_name = msg->msg_name; } - sinfo = cmsgs.info; sinit = cmsgs.init; + if (cmsgs.sinfo != NULL) { + memset(&default_sinfo, 0, sizeof(default_sinfo)); + default_sinfo.sinfo_stream = cmsgs.sinfo->snd_sid; + default_sinfo.sinfo_flags = cmsgs.sinfo->snd_flags; + default_sinfo.sinfo_ppid = cmsgs.sinfo->snd_ppid; + default_sinfo.sinfo_context = cmsgs.sinfo->snd_context; + default_sinfo.sinfo_assoc_id = cmsgs.sinfo->snd_assoc_id; - /* Did the user specify SNDRCVINFO? */ + sinfo = &default_sinfo; + fill_sinfo_ttl = true; + } else { + sinfo = cmsgs.srinfo; + } + /* Did the user specify SNDINFO/SNDRCVINFO? */ if (sinfo) { sinfo_flags = sinfo->sinfo_flags; associd = sinfo->sinfo_assoc_id; @@ -1858,8 +1870,8 @@ static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, pr_debug("%s: we have a valid association\n", __func__); if (!sinfo) { - /* If the user didn't specify SNDRCVINFO, make up one with - * some defaults. + /* If the user didn't specify SNDINFO/SNDRCVINFO, make up + * one with some defaults. */ memset(&default_sinfo, 0, sizeof(default_sinfo)); default_sinfo.sinfo_stream = asoc->default_stream; @@ -1868,7 +1880,13 @@ static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, default_sinfo.sinfo_context = asoc->default_context; default_sinfo.sinfo_timetolive = asoc->default_timetolive; default_sinfo.sinfo_assoc_id = sctp_assoc2id(asoc); + sinfo = &default_sinfo; + } else if (fill_sinfo_ttl) { + /* In case SNDINFO was specified, we still need to fill + * it with a default ttl from the assoc here. + */ + sinfo->sinfo_timetolive = asoc->default_timetolive; } /* API 7.1.7, the sndbuf size per association bounds the @@ -6390,8 +6408,7 @@ static int sctp_msghdr_parse(const struct msghdr *msg, sctp_cmsgs_t *cmsgs) struct cmsghdr *cmsg; struct msghdr *my_msg = (struct msghdr *)msg; - for (cmsg = CMSG_FIRSTHDR(msg); - cmsg != NULL; + for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(my_msg, cmsg)) { if (!CMSG_OK(my_msg, cmsg)) return -EINVAL; @@ -6404,7 +6421,7 @@ static int sctp_msghdr_parse(const struct msghdr *msg, sctp_cmsgs_t *cmsgs) switch (cmsg->cmsg_type) { case SCTP_INIT: /* SCTP Socket API Extension - * 5.2.1 SCTP Initiation Structure (SCTP_INIT) + * 5.3.1 SCTP Initiation Structure (SCTP_INIT) * * This cmsghdr structure provides information for * initializing new SCTP associations with sendmsg(). @@ -6416,15 +6433,15 @@ static int sctp_msghdr_parse(const struct msghdr *msg, sctp_cmsgs_t *cmsgs) * ------------ ------------ ---------------------- * IPPROTO_SCTP SCTP_INIT struct sctp_initmsg */ - if (cmsg->cmsg_len != - CMSG_LEN(sizeof(struct sctp_initmsg))) + if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct sctp_initmsg))) return -EINVAL; - cmsgs->init = (struct sctp_initmsg *)CMSG_DATA(cmsg); + + cmsgs->init = CMSG_DATA(cmsg); break; case SCTP_SNDRCV: /* SCTP Socket API Extension - * 5.2.2 SCTP Header Information Structure(SCTP_SNDRCV) + * 5.3.2 SCTP Header Information Structure(SCTP_SNDRCV) * * This cmsghdr structure specifies SCTP options for * sendmsg() and describes SCTP header information @@ -6434,24 +6451,44 @@ static int sctp_msghdr_parse(const struct msghdr *msg, sctp_cmsgs_t *cmsgs) * ------------ ------------ ---------------------- * IPPROTO_SCTP SCTP_SNDRCV struct sctp_sndrcvinfo */ - if (cmsg->cmsg_len != - CMSG_LEN(sizeof(struct sctp_sndrcvinfo))) + if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct sctp_sndrcvinfo))) return -EINVAL; - cmsgs->info = - (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg); + cmsgs->srinfo = CMSG_DATA(cmsg); - /* Minimally, validate the sinfo_flags. */ - if (cmsgs->info->sinfo_flags & + if (cmsgs->srinfo->sinfo_flags & ~(SCTP_UNORDERED | SCTP_ADDR_OVER | SCTP_ABORT | SCTP_EOF)) return -EINVAL; break; + case SCTP_SNDINFO: + /* SCTP Socket API Extension + * 5.3.4 SCTP Send Information Structure (SCTP_SNDINFO) + * + * This cmsghdr structure specifies SCTP options for + * sendmsg(). This structure and SCTP_RCVINFO replaces + * SCTP_SNDRCV which has been deprecated. + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ --------------------- + * IPPROTO_SCTP SCTP_SNDINFO struct sctp_sndinfo + */ + if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct sctp_sndinfo))) + return -EINVAL; + + cmsgs->sinfo = CMSG_DATA(cmsg); + + if (cmsgs->sinfo->snd_flags & + ~(SCTP_UNORDERED | SCTP_ADDR_OVER | + SCTP_ABORT | SCTP_EOF)) + return -EINVAL; + break; default: return -EINVAL; } } + return 0; } -- cgit v1.2.3 From 0d3a421d284812d07970b4ccee74d4fa38737e4d Mon Sep 17 00:00:00 2001 From: Geir Ola Vaagland Date: Sat, 12 Jul 2014 20:30:37 +0200 Subject: net: sctp: implement rfc6458, 5.3.5. SCTP_RCVINFO cmsg support This patch implements section 5.3.5. of RFC6458, that is, support for 'SCTP Receive Information Structure' (SCTP_RCVINFO) which is placed into ancillary data cmsghdr structure for each recvmsg() call. This option can be enabled/disabled via setsockopt(2) on SOL_SCTP level by setting an int value with 1/0 for SCTP_RECVRCVINFO in user space applications as per RFC6458, section 8.1.29. The sctp_rcvinfo structure is defined as per RFC as below ... struct sctp_rcvinfo { uint16_t rcv_sid; uint16_t rcv_ssn; uint16_t rcv_flags; <-- 2 bytes hole --> uint32_t rcv_ppid; uint32_t rcv_tsn; uint32_t rcv_cumtsn; uint32_t rcv_context; sctp_assoc_t rcv_assoc_id; }; ... and provided under cmsg_level IPPROTO_SCTP, cmsg_type SCTP_RCVINFO, while cmsg_data[] contains struct sctp_rcvinfo. An sctp_rcvinfo item always corresponds to the data in msg_iov. Joint work with Daniel Borkmann. Signed-off-by: Geir Ola Vaagland Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 3 +++ include/net/sctp/ulpevent.h | 5 ++++- include/uapi/linux/sctp.h | 32 ++++++++++++++++++++++------- net/sctp/socket.c | 49 ++++++++++++++++++++++++++++++++++++++++++++- net/sctp/ulpevent.c | 25 +++++++++++++++++++++++ 5 files changed, 105 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 7af9a0f5d8ce..11d5df015370 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -207,7 +207,9 @@ struct sctp_sock { struct sctp_paddrparams paddrparam; struct sctp_event_subscribe subscribe; struct sctp_assocparams assocparams; + int user_frag; + __u32 autoclose; __u8 nodelay; __u8 disable_fragments; @@ -215,6 +217,7 @@ struct sctp_sock { __u8 frag_interleave; __u32 adaptation_ind; __u32 pd_point; + __u8 recvrcvinfo; atomic_t pd_mode; /* Receive to here while partial delivery is in effect. */ diff --git a/include/net/sctp/ulpevent.h b/include/net/sctp/ulpevent.h index daacb32b55b5..e8095f973e94 100644 --- a/include/net/sctp/ulpevent.h +++ b/include/net/sctp/ulpevent.h @@ -129,7 +129,10 @@ struct sctp_ulpevent *sctp_ulpevent_make_sender_dry_event( const struct sctp_association *asoc, gfp_t gfp); void sctp_ulpevent_read_sndrcvinfo(const struct sctp_ulpevent *event, - struct msghdr *); + struct msghdr *); +void sctp_ulpevent_read_rcvinfo(const struct sctp_ulpevent *event, + struct msghdr *); + __u16 sctp_ulpevent_get_notification_type(const struct sctp_ulpevent *event); /* Is this event type enabled? */ diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h index a387761f7e02..29b81bbfc53d 100644 --- a/include/uapi/linux/sctp.h +++ b/include/uapi/linux/sctp.h @@ -95,6 +95,7 @@ typedef __s32 sctp_assoc_t; #define SCTP_GET_ASSOC_ID_LIST 29 /* Read only */ #define SCTP_AUTO_ASCONF 30 #define SCTP_PEER_ADDR_THLDS 31 +#define SCTP_RECVRCVINFO 32 /* Internal Socket Options. Some of the sctp library functions are * implemented using these socket options. @@ -110,8 +111,7 @@ typedef __s32 sctp_assoc_t; #define SCTP_SOCKOPT_CONNECTX3 111 /* CONNECTX requests (updated) */ #define SCTP_GET_ASSOC_STATS 112 /* Read only */ -/* - * 5.2.1 SCTP Initiation Structure (SCTP_INIT) +/* 5.3.1 SCTP Initiation Structure (SCTP_INIT) * * This cmsghdr structure provides information for initializing new * SCTP associations with sendmsg(). The SCTP_INITMSG socket option @@ -121,7 +121,6 @@ typedef __s32 sctp_assoc_t; * cmsg_level cmsg_type cmsg_data[] * ------------ ------------ ---------------------- * IPPROTO_SCTP SCTP_INIT struct sctp_initmsg - * */ struct sctp_initmsg { __u16 sinit_num_ostreams; @@ -130,8 +129,7 @@ struct sctp_initmsg { __u16 sinit_max_init_timeo; }; -/* - * 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV) +/* 5.3.2 SCTP Header Information Structure (SCTP_SNDRCV) * * This cmsghdr structure specifies SCTP options for sendmsg() and * describes SCTP header information about a received message through @@ -140,7 +138,6 @@ struct sctp_initmsg { * cmsg_level cmsg_type cmsg_data[] * ------------ ------------ ---------------------- * IPPROTO_SCTP SCTP_SNDRCV struct sctp_sndrcvinfo - * */ struct sctp_sndrcvinfo { __u16 sinfo_stream; @@ -170,13 +167,32 @@ struct sctp_sndinfo { sctp_assoc_t snd_assoc_id; }; +/* 5.3.5 SCTP Receive Information Structure (SCTP_RCVINFO) + * + * This cmsghdr structure describes SCTP receive information + * about a received message through recvmsg(). + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ ------------------- + * IPPROTO_SCTP SCTP_RCVINFO struct sctp_rcvinfo + */ +struct sctp_rcvinfo { + __u16 rcv_sid; + __u16 rcv_ssn; + __u16 rcv_flags; + __u32 rcv_ppid; + __u32 rcv_tsn; + __u32 rcv_cumtsn; + __u32 rcv_context; + sctp_assoc_t rcv_assoc_id; +}; + /* * sinfo_flags: 16 bits (unsigned integer) * * This field may contain any of the following flags and is composed of * a bitwise OR of these values. */ - enum sctp_sinfo_flags { SCTP_UNORDERED = 1, /* Send/receive message unordered. */ SCTP_ADDR_OVER = 2, /* Override the primary destination. */ @@ -199,6 +215,8 @@ typedef enum sctp_cmsg_type { #define SCTP_SNDRCV SCTP_SNDRCV SCTP_SNDINFO, /* 5.3.4 SCTP Send Information Structure */ #define SCTP_SNDINFO SCTP_SNDINFO + SCTP_RCVINFO, /* 5.3.5 SCTP Receive Information Structure */ +#define SCTP_RCVINFO SCTP_RCVINFO } sctp_cmsg_t; /* diff --git a/net/sctp/socket.c b/net/sctp/socket.c index d61729e99856..9c193887c5cd 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -2112,9 +2112,13 @@ static int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, sp->pf->skb_msgname(skb, msg->msg_name, addr_len); } + /* Check if we allow SCTP_RCVINFO. */ + if (sp->recvrcvinfo) + sctp_ulpevent_read_rcvinfo(event, msg); /* Check if we allow SCTP_SNDRCVINFO. */ if (sp->subscribe.sctp_data_io_event) sctp_ulpevent_read_sndrcvinfo(event, msg); + #if 0 /* FIXME: we should be calling IP/IPv6 layers. */ if (sk->sk_protinfo.af_inet.cmsg_flags) @@ -3541,7 +3545,6 @@ static int sctp_setsockopt_auto_asconf(struct sock *sk, char __user *optval, return 0; } - /* * SCTP_PEER_ADDR_THLDS * @@ -3592,6 +3595,22 @@ static int sctp_setsockopt_paddr_thresholds(struct sock *sk, return 0; } +static int sctp_setsockopt_recvrcvinfo(struct sock *sk, + char __user *optval, + unsigned int optlen) +{ + int val; + + if (optlen < sizeof(int)) + return -EINVAL; + if (get_user(val, (int __user *) optval)) + return -EFAULT; + + sctp_sk(sk)->recvrcvinfo = (val == 0) ? 0 : 1; + + return 0; +} + /* API 6.2 setsockopt(), getsockopt() * * Applications use setsockopt() and getsockopt() to set or retrieve @@ -3743,6 +3762,9 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname, case SCTP_PEER_ADDR_THLDS: retval = sctp_setsockopt_paddr_thresholds(sk, optval, optlen); break; + case SCTP_RECVRCVINFO: + retval = sctp_setsockopt_recvrcvinfo(sk, optval, optlen); + break; default: retval = -ENOPROTOOPT; break; @@ -3989,6 +4011,8 @@ static int sctp_init_sock(struct sock *sk) /* Enable Nagle algorithm by default. */ sp->nodelay = 0; + sp->recvrcvinfo = 0; + /* Enable by default. */ sp->v4mapped = 1; @@ -5770,6 +5794,26 @@ static int sctp_getsockopt_assoc_stats(struct sock *sk, int len, return 0; } +static int sctp_getsockopt_recvrcvinfo(struct sock *sk, int len, + char __user *optval, + int __user *optlen) +{ + int val = 0; + + if (len < sizeof(int)) + return -EINVAL; + + len = sizeof(int); + if (sctp_sk(sk)->recvrcvinfo) + val = 1; + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, &val, len)) + return -EFAULT; + + return 0; +} + static int sctp_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen) { @@ -5913,6 +5957,9 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname, case SCTP_GET_ASSOC_STATS: retval = sctp_getsockopt_assoc_stats(sk, len, optval, optlen); break; + case SCTP_RECVRCVINFO: + retval = sctp_getsockopt_recvrcvinfo(sk, len, optval, optlen); + break; default: retval = -ENOPROTOOPT; break; diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c index b6842fdb53d4..b31f365f18ab 100644 --- a/net/sctp/ulpevent.c +++ b/net/sctp/ulpevent.c @@ -886,6 +886,31 @@ void sctp_ulpevent_read_sndrcvinfo(const struct sctp_ulpevent *event, sizeof(sinfo), &sinfo); } +/* RFC6458, Section 5.3.5 SCTP Receive Information Structure + * (SCTP_SNDRCV) + */ +void sctp_ulpevent_read_rcvinfo(const struct sctp_ulpevent *event, + struct msghdr *msghdr) +{ + struct sctp_rcvinfo rinfo; + + if (sctp_ulpevent_is_notification(event)) + return; + + memset(&rinfo, 0, sizeof(struct sctp_rcvinfo)); + rinfo.rcv_sid = event->stream; + rinfo.rcv_ssn = event->ssn; + rinfo.rcv_ppid = event->ppid; + rinfo.rcv_flags = event->flags; + rinfo.rcv_tsn = event->tsn; + rinfo.rcv_cumtsn = event->cumtsn; + rinfo.rcv_assoc_id = sctp_assoc2id(event->asoc); + rinfo.rcv_context = event->asoc->default_rcv_context; + + put_cmsg(msghdr, IPPROTO_SCTP, SCTP_RCVINFO, + sizeof(rinfo), &rinfo); +} + /* Do accounting for bytes received and hold a reference to the association * for each skb. */ -- cgit v1.2.3 From 2347c80ff127b94ddaa675e2b78ab4cef46dc905 Mon Sep 17 00:00:00 2001 From: Geir Ola Vaagland Date: Sat, 12 Jul 2014 20:30:38 +0200 Subject: net: sctp: implement rfc6458, 5.3.6. SCTP_NXTINFO cmsg support This patch implements section 5.3.6. of RFC6458, that is, support for 'SCTP Next Receive Information Structure' (SCTP_NXTINFO) which is placed into ancillary data cmsghdr structure for each recvmsg() call, if this information is already available when delivering the current message. This option can be enabled/disabled via setsockopt(2) on SOL_SCTP level by setting an int value with 1/0 for SCTP_RECVNXTINFO in user space applications as per RFC6458, section 8.1.30. The sctp_nxtinfo structure is defined as per RFC as below ... struct sctp_nxtinfo { uint16_t nxt_sid; uint16_t nxt_flags; uint32_t nxt_ppid; uint32_t nxt_length; sctp_assoc_t nxt_assoc_id; }; ... and provided under cmsg_level IPPROTO_SCTP, cmsg_type SCTP_NXTINFO, while cmsg_data[] contains struct sctp_nxtinfo. Joint work with Daniel Borkmann. Signed-off-by: Geir Ola Vaagland Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/net/sctp/sctp.h | 1 + include/net/sctp/structs.h | 1 + include/net/sctp/ulpevent.h | 9 ++------ include/uapi/linux/sctp.h | 47 +++++++++++++++++++++++++++++----------- net/sctp/socket.c | 52 +++++++++++++++++++++++++++++++++++++++++---- net/sctp/ulpevent.c | 38 +++++++++++++++++++++++++++++++++ 6 files changed, 125 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index c2035c96a2ee..90c1cccd164d 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -109,6 +109,7 @@ void sctp_copy_sock(struct sock *newsk, struct sock *sk, struct sctp_association *asoc); extern struct percpu_counter sctp_sockets_allocated; int sctp_asconf_mgmt(struct sctp_sock *, struct sctp_sockaddr_entry *); +struct sk_buff *sctp_skb_recv_datagram(struct sock *, int, int, int *); /* * sctp/primitive.c diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 11d5df015370..7741d1b66967 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -218,6 +218,7 @@ struct sctp_sock { __u32 adaptation_ind; __u32 pd_point; __u8 recvrcvinfo; + __u8 recvnxtinfo; atomic_t pd_mode; /* Receive to here while partial delivery is in effect. */ diff --git a/include/net/sctp/ulpevent.h b/include/net/sctp/ulpevent.h index e8095f973e94..cccdcfd14973 100644 --- a/include/net/sctp/ulpevent.h +++ b/include/net/sctp/ulpevent.h @@ -132,6 +132,8 @@ void sctp_ulpevent_read_sndrcvinfo(const struct sctp_ulpevent *event, struct msghdr *); void sctp_ulpevent_read_rcvinfo(const struct sctp_ulpevent *event, struct msghdr *); +void sctp_ulpevent_read_nxtinfo(const struct sctp_ulpevent *event, + struct msghdr *, struct sock *sk); __u16 sctp_ulpevent_get_notification_type(const struct sctp_ulpevent *event); @@ -158,10 +160,3 @@ static inline int sctp_ulpevent_is_enabled(const struct sctp_ulpevent *event, } #endif /* __sctp_ulpevent_h__ */ - - - - - - - diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h index 29b81bbfc53d..222f82ffeca4 100644 --- a/include/uapi/linux/sctp.h +++ b/include/uapi/linux/sctp.h @@ -96,6 +96,7 @@ typedef __s32 sctp_assoc_t; #define SCTP_AUTO_ASCONF 30 #define SCTP_PEER_ADDR_THLDS 31 #define SCTP_RECVRCVINFO 32 +#define SCTP_RECVNXTINFO 33 /* Internal Socket Options. Some of the sctp library functions are * implemented using these socket options. @@ -111,6 +112,13 @@ typedef __s32 sctp_assoc_t; #define SCTP_SOCKOPT_CONNECTX3 111 /* CONNECTX requests (updated) */ #define SCTP_GET_ASSOC_STATS 112 /* Read only */ +/* These are bit fields for msghdr->msg_flags. See section 5.1. */ +/* On user space Linux, these live in as an enum. */ +enum sctp_msg_flags { + MSG_NOTIFICATION = 0x8000, +#define MSG_NOTIFICATION MSG_NOTIFICATION +}; + /* 5.3.1 SCTP Initiation Structure (SCTP_INIT) * * This cmsghdr structure provides information for initializing new @@ -187,6 +195,25 @@ struct sctp_rcvinfo { sctp_assoc_t rcv_assoc_id; }; +/* 5.3.6 SCTP Next Receive Information Structure (SCTP_NXTINFO) + * + * This cmsghdr structure describes SCTP receive information + * of the next message that will be delivered through recvmsg() + * if this information is already available when delivering + * the current message. + * + * cmsg_level cmsg_type cmsg_data[] + * ------------ ------------ ------------------- + * IPPROTO_SCTP SCTP_NXTINFO struct sctp_nxtinfo + */ +struct sctp_nxtinfo { + __u16 nxt_sid; + __u16 nxt_flags; + __u32 nxt_ppid; + __u32 nxt_length; + sctp_assoc_t nxt_assoc_id; +}; + /* * sinfo_flags: 16 bits (unsigned integer) * @@ -194,11 +221,12 @@ struct sctp_rcvinfo { * a bitwise OR of these values. */ enum sctp_sinfo_flags { - SCTP_UNORDERED = 1, /* Send/receive message unordered. */ - SCTP_ADDR_OVER = 2, /* Override the primary destination. */ - SCTP_ABORT=4, /* Send an ABORT message to the peer. */ - SCTP_SACK_IMMEDIATELY = 8, /* SACK should be sent without delay */ - SCTP_EOF=MSG_FIN, /* Initiate graceful shutdown process. */ + SCTP_UNORDERED = (1 << 0), /* Send/receive message unordered. */ + SCTP_ADDR_OVER = (1 << 1), /* Override the primary destination. */ + SCTP_ABORT = (1 << 2), /* Send an ABORT message to the peer. */ + SCTP_SACK_IMMEDIATELY = (1 << 3), /* SACK should be sent without delay. */ + SCTP_NOTIFICATION = MSG_NOTIFICATION, /* Next message is not user msg but notification. */ + SCTP_EOF = MSG_FIN, /* Initiate graceful shutdown process. */ }; typedef union { @@ -217,6 +245,8 @@ typedef enum sctp_cmsg_type { #define SCTP_SNDINFO SCTP_SNDINFO SCTP_RCVINFO, /* 5.3.5 SCTP Receive Information Structure */ #define SCTP_RCVINFO SCTP_RCVINFO + SCTP_NXTINFO, /* 5.3.6 SCTP Next Receive Information Structure */ +#define SCTP_NXTINFO SCTP_NXTINFO } sctp_cmsg_t; /* @@ -844,13 +874,6 @@ struct sctp_assoc_stats { __u64 sas_ictrlchunks; /* Control chunks received */ }; -/* These are bit fields for msghdr->msg_flags. See section 5.1. */ -/* On user space Linux, these live in as an enum. */ -enum sctp_msg_flags { - MSG_NOTIFICATION = 0x8000, -#define MSG_NOTIFICATION MSG_NOTIFICATION -}; - /* * 8.1 sctp_bindx() * diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 9c193887c5cd..9bca87ee5152 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -2060,8 +2060,6 @@ static int sctp_skb_pull(struct sk_buff *skb, int len) * flags - flags sent or received with the user message, see Section * 5 for complete description of the flags. */ -static struct sk_buff *sctp_skb_recv_datagram(struct sock *, int, int, int *); - static int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len, int noblock, int flags, int *addr_len) @@ -2112,6 +2110,9 @@ static int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, sp->pf->skb_msgname(skb, msg->msg_name, addr_len); } + /* Check if we allow SCTP_NXTINFO. */ + if (sp->recvnxtinfo) + sctp_ulpevent_read_nxtinfo(event, msg, sk); /* Check if we allow SCTP_RCVINFO. */ if (sp->recvrcvinfo) sctp_ulpevent_read_rcvinfo(event, msg); @@ -3611,6 +3612,22 @@ static int sctp_setsockopt_recvrcvinfo(struct sock *sk, return 0; } +static int sctp_setsockopt_recvnxtinfo(struct sock *sk, + char __user *optval, + unsigned int optlen) +{ + int val; + + if (optlen < sizeof(int)) + return -EINVAL; + if (get_user(val, (int __user *) optval)) + return -EFAULT; + + sctp_sk(sk)->recvnxtinfo = (val == 0) ? 0 : 1; + + return 0; +} + /* API 6.2 setsockopt(), getsockopt() * * Applications use setsockopt() and getsockopt() to set or retrieve @@ -3765,6 +3782,9 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname, case SCTP_RECVRCVINFO: retval = sctp_setsockopt_recvrcvinfo(sk, optval, optlen); break; + case SCTP_RECVNXTINFO: + retval = sctp_setsockopt_recvnxtinfo(sk, optval, optlen); + break; default: retval = -ENOPROTOOPT; break; @@ -4012,6 +4032,7 @@ static int sctp_init_sock(struct sock *sk) sp->nodelay = 0; sp->recvrcvinfo = 0; + sp->recvnxtinfo = 0; /* Enable by default. */ sp->v4mapped = 1; @@ -5814,6 +5835,26 @@ static int sctp_getsockopt_recvrcvinfo(struct sock *sk, int len, return 0; } +static int sctp_getsockopt_recvnxtinfo(struct sock *sk, int len, + char __user *optval, + int __user *optlen) +{ + int val = 0; + + if (len < sizeof(int)) + return -EINVAL; + + len = sizeof(int); + if (sctp_sk(sk)->recvnxtinfo) + val = 1; + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, &val, len)) + return -EFAULT; + + return 0; +} + static int sctp_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen) { @@ -5960,6 +6001,9 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname, case SCTP_RECVRCVINFO: retval = sctp_getsockopt_recvrcvinfo(sk, len, optval, optlen); break; + case SCTP_RECVNXTINFO: + retval = sctp_getsockopt_recvnxtinfo(sk, len, optval, optlen); + break; default: retval = -ENOPROTOOPT; break; @@ -6602,8 +6646,8 @@ out: * Note: This is pretty much the same routine as in core/datagram.c * with a few changes to make lksctp work. */ -static struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags, - int noblock, int *err) +struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags, + int noblock, int *err) { int error; struct sk_buff *skb; diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c index b31f365f18ab..e049298ecfa0 100644 --- a/net/sctp/ulpevent.c +++ b/net/sctp/ulpevent.c @@ -911,6 +911,44 @@ void sctp_ulpevent_read_rcvinfo(const struct sctp_ulpevent *event, sizeof(rinfo), &rinfo); } +/* RFC6458, Section 5.3.6. SCTP Next Receive Information Structure + * (SCTP_NXTINFO) + */ +static void __sctp_ulpevent_read_nxtinfo(const struct sctp_ulpevent *event, + struct msghdr *msghdr, + const struct sk_buff *skb) +{ + struct sctp_nxtinfo nxtinfo; + + memset(&nxtinfo, 0, sizeof(nxtinfo)); + nxtinfo.nxt_sid = event->stream; + nxtinfo.nxt_ppid = event->ppid; + nxtinfo.nxt_flags = event->flags; + if (sctp_ulpevent_is_notification(event)) + nxtinfo.nxt_flags |= SCTP_NOTIFICATION; + nxtinfo.nxt_length = skb->len; + nxtinfo.nxt_assoc_id = sctp_assoc2id(event->asoc); + + put_cmsg(msghdr, IPPROTO_SCTP, SCTP_NXTINFO, + sizeof(nxtinfo), &nxtinfo); +} + +void sctp_ulpevent_read_nxtinfo(const struct sctp_ulpevent *event, + struct msghdr *msghdr, + struct sock *sk) +{ + struct sk_buff *skb; + int err; + + skb = sctp_skb_recv_datagram(sk, MSG_PEEK, 1, &err); + if (skb != NULL) { + __sctp_ulpevent_read_nxtinfo(sctp_skb2event(skb), + msghdr, skb); + /* Just release refcount here. */ + kfree_skb(skb); + } +} + /* Do accounting for bytes received and hold a reference to the association * for each skb. */ -- cgit v1.2.3 From 6b3fd5f3a2bbc8464a8e0bf134a183b8fa026439 Mon Sep 17 00:00:00 2001 From: Geir Ola Vaagland Date: Sat, 12 Jul 2014 20:30:39 +0200 Subject: net: sctp: implement rfc6458, 8.1.31. SCTP_DEFAULT_SNDINFO support This patch implements section 8.1.31. of RFC6458, which adds support for setting/retrieving SCTP_DEFAULT_SNDINFO: Applications that wish to use the sendto() system call may wish to specify a default set of parameters that would normally be supplied through the inclusion of ancillary data. This socket option allows such an application to set the default sctp_sndinfo structure. The application that wishes to use this socket option simply passes the sctp_sndinfo structure (defined in Section 5.3.4) to this call. The input parameters accepted by this call include snd_sid, snd_flags, snd_ppid, and snd_context. The snd_flags parameter is composed of a bitwise OR of SCTP_UNORDERED, SCTP_EOF, and SCTP_SENDALL. The snd_assoc_id field specifies the association to which to apply the parameters. For a one-to-many style socket, any of the predefined constants are also allowed in this field. The field is ignored for one-to-one style sockets. Joint work with Daniel Borkmann. Signed-off-by: Geir Ola Vaagland Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/uapi/linux/sctp.h | 1 + net/sctp/socket.c | 107 ++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 99 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h index 222f82ffeca4..ce70fe6b45df 100644 --- a/include/uapi/linux/sctp.h +++ b/include/uapi/linux/sctp.h @@ -97,6 +97,7 @@ typedef __s32 sctp_assoc_t; #define SCTP_PEER_ADDR_THLDS 31 #define SCTP_RECVRCVINFO 32 #define SCTP_RECVNXTINFO 33 +#define SCTP_DEFAULT_SNDINFO 34 /* Internal Socket Options. Some of the sctp library functions are * implemented using these socket options. diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 9bca87ee5152..d95a50c013c9 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -2770,19 +2770,22 @@ static int sctp_setsockopt_default_send_param(struct sock *sk, char __user *optval, unsigned int optlen) { - struct sctp_sndrcvinfo info; - struct sctp_association *asoc; struct sctp_sock *sp = sctp_sk(sk); + struct sctp_association *asoc; + struct sctp_sndrcvinfo info; - if (optlen != sizeof(struct sctp_sndrcvinfo)) + if (optlen != sizeof(info)) return -EINVAL; if (copy_from_user(&info, optval, optlen)) return -EFAULT; + if (info.sinfo_flags & + ~(SCTP_UNORDERED | SCTP_ADDR_OVER | + SCTP_ABORT | SCTP_EOF)) + return -EINVAL; asoc = sctp_id2assoc(sk, info.sinfo_assoc_id); if (!asoc && info.sinfo_assoc_id && sctp_style(sk, UDP)) return -EINVAL; - if (asoc) { asoc->default_stream = info.sinfo_stream; asoc->default_flags = info.sinfo_flags; @@ -2800,6 +2803,44 @@ static int sctp_setsockopt_default_send_param(struct sock *sk, return 0; } +/* RFC6458, Section 8.1.31. Set/get Default Send Parameters + * (SCTP_DEFAULT_SNDINFO) + */ +static int sctp_setsockopt_default_sndinfo(struct sock *sk, + char __user *optval, + unsigned int optlen) +{ + struct sctp_sock *sp = sctp_sk(sk); + struct sctp_association *asoc; + struct sctp_sndinfo info; + + if (optlen != sizeof(info)) + return -EINVAL; + if (copy_from_user(&info, optval, optlen)) + return -EFAULT; + if (info.snd_flags & + ~(SCTP_UNORDERED | SCTP_ADDR_OVER | + SCTP_ABORT | SCTP_EOF)) + return -EINVAL; + + asoc = sctp_id2assoc(sk, info.snd_assoc_id); + if (!asoc && info.snd_assoc_id && sctp_style(sk, UDP)) + return -EINVAL; + if (asoc) { + asoc->default_stream = info.snd_sid; + asoc->default_flags = info.snd_flags; + asoc->default_ppid = info.snd_ppid; + asoc->default_context = info.snd_context; + } else { + sp->default_stream = info.snd_sid; + sp->default_flags = info.snd_flags; + sp->default_ppid = info.snd_ppid; + sp->default_context = info.snd_context; + } + + return 0; +} + /* 7.1.10 Set Primary Address (SCTP_PRIMARY_ADDR) * * Requests that the local SCTP stack use the enclosed peer address as @@ -3725,6 +3766,9 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname, retval = sctp_setsockopt_default_send_param(sk, optval, optlen); break; + case SCTP_DEFAULT_SNDINFO: + retval = sctp_setsockopt_default_sndinfo(sk, optval, optlen); + break; case SCTP_PRIMARY_ADDR: retval = sctp_setsockopt_primary_addr(sk, optval, optlen); break; @@ -5027,14 +5071,14 @@ static int sctp_getsockopt_default_send_param(struct sock *sk, int len, char __user *optval, int __user *optlen) { - struct sctp_sndrcvinfo info; - struct sctp_association *asoc; struct sctp_sock *sp = sctp_sk(sk); + struct sctp_association *asoc; + struct sctp_sndrcvinfo info; - if (len < sizeof(struct sctp_sndrcvinfo)) + if (len < sizeof(info)) return -EINVAL; - len = sizeof(struct sctp_sndrcvinfo); + len = sizeof(info); if (copy_from_user(&info, optval, len)) return -EFAULT; @@ -5042,7 +5086,6 @@ static int sctp_getsockopt_default_send_param(struct sock *sk, asoc = sctp_id2assoc(sk, info.sinfo_assoc_id); if (!asoc && info.sinfo_assoc_id && sctp_style(sk, UDP)) return -EINVAL; - if (asoc) { info.sinfo_stream = asoc->default_stream; info.sinfo_flags = asoc->default_flags; @@ -5065,6 +5108,48 @@ static int sctp_getsockopt_default_send_param(struct sock *sk, return 0; } +/* RFC6458, Section 8.1.31. Set/get Default Send Parameters + * (SCTP_DEFAULT_SNDINFO) + */ +static int sctp_getsockopt_default_sndinfo(struct sock *sk, int len, + char __user *optval, + int __user *optlen) +{ + struct sctp_sock *sp = sctp_sk(sk); + struct sctp_association *asoc; + struct sctp_sndinfo info; + + if (len < sizeof(info)) + return -EINVAL; + + len = sizeof(info); + + if (copy_from_user(&info, optval, len)) + return -EFAULT; + + asoc = sctp_id2assoc(sk, info.snd_assoc_id); + if (!asoc && info.snd_assoc_id && sctp_style(sk, UDP)) + return -EINVAL; + if (asoc) { + info.snd_sid = asoc->default_stream; + info.snd_flags = asoc->default_flags; + info.snd_ppid = asoc->default_ppid; + info.snd_context = asoc->default_context; + } else { + info.snd_sid = sp->default_stream; + info.snd_flags = sp->default_flags; + info.snd_ppid = sp->default_ppid; + info.snd_context = sp->default_context; + } + + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, &info, len)) + return -EFAULT; + + return 0; +} + /* * * 7.1.5 SCTP_NODELAY @@ -5924,6 +6009,10 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname, retval = sctp_getsockopt_default_send_param(sk, len, optval, optlen); break; + case SCTP_DEFAULT_SNDINFO: + retval = sctp_getsockopt_default_sndinfo(sk, len, + optval, optlen); + break; case SCTP_PRIMARY_ADDR: retval = sctp_getsockopt_primary_addr(sk, len, optval, optlen); break; -- cgit v1.2.3 From 7523a271682fc1f35355da344249ce8f8c8b8cb9 Mon Sep 17 00:00:00 2001 From: Omair Mohammed Abdullah Date: Tue, 15 Jul 2014 21:34:48 +0530 Subject: ASoC: core: add a helper for extended byte controls using TLV ALSA supports arbitrary length TLVs for each kcontrol that can be used to pass metadata about the control (e.g. volumes, enum information). The same transport mechanism is now used for arbitrary length data by defining a new helper. Signed-off-by: Omair Mohammed Abdullah Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- include/sound/soc.h | 14 +++++++++++++- sound/soc/soc-core.c | 21 +++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index ed9e2d7e5fdc..688fa667dee5 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -270,7 +270,14 @@ .get = xhandler_get, .put = xhandler_put, \ .private_value = (unsigned long)&(struct soc_bytes_ext) \ {.max = xcount} } - +#define SND_SOC_BYTES_TLV(xname, xcount, xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE | \ + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ + .tlv.c = (snd_soc_bytes_tlv_callback), \ + .info = snd_soc_info_bytes_ext, \ + .private_value = (unsigned long)&(struct soc_bytes_ext) \ + {.max = xcount, .get = xhandler_get, .put = xhandler_put, } } #define SOC_SINGLE_XR_SX(xname, xregbase, xregcount, xnbits, \ xmin, xmax, xinvert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ @@ -552,6 +559,8 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *ucontrol); +int snd_soc_bytes_tlv_callback(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *tlv); int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol, @@ -1119,6 +1128,9 @@ struct soc_bytes { struct soc_bytes_ext { int max; + /* used for TLV byte control */ + int (*get)(unsigned int __user *bytes, unsigned int size); + int (*put)(const unsigned int __user *bytes, unsigned int size); }; /* multi register control */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index b87d7d882e6d..a9ce63f634b2 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3267,6 +3267,27 @@ int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_soc_bytes_info_ext); +int snd_soc_bytes_tlv_callback(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *tlv) +{ + struct soc_bytes_ext *params = (void *)kcontrol->private_value; + unsigned int count = size < params->max ? size : params->max; + int ret = -ENXIO; + + switch (op_flag) { + case SNDRV_CTL_TLV_OP_READ: + if (params->get) + ret = params->get(tlv, count); + break; + case SNDRV_CTL_TLV_OP_WRITE: + if (params->put) + ret = params->put(tlv, count); + break; + } + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_bytes_tlv_callback); + /** * snd_soc_info_xr_sx - signed multi register info callback * @kcontrol: mreg control -- cgit v1.2.3 From 88bd870f02dff5c9445286e185f21873f25a977f Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Tue, 8 Jul 2014 23:19:34 +0200 Subject: ASoC: core: Add initial support for DAI multicodec DAI link assumes a one to one mapping between CPU DAI and CODEC. In some cases, the same CPU DAI can be connected to several codecs. This is the case for example, if you connect two mono codecs to the same I2S link in order to have a stereo card. The current ASoC implementation does not allow such setup. Add support for DAI links composed of a single CPU DAI and multiple CODECs. Sound cards have to pass the CODECs array in the corresponding DAI link through a new 'snd_soc_dai_link_component' struct. Each CODEC in this array is described in the same manner single CODEC DAIs are (either DT/OF node or codec_name). Multi-codec links are not supported in the case of CODEC to CODEC links. Just print a warning if it happens. Based on an original code done by Misael. Signed-off-by: Benoit Cousson Signed-off-by: Misael Lopez Cruz Signed-off-by: Fabien Parent Tested-by: Lars-Peter Clausen Reviewed-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dai.h | 4 + include/sound/soc.h | 13 +++ sound/soc/soc-core.c | 293 +++++++++++++++++++++++++++++++++--------------- 3 files changed, 220 insertions(+), 90 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 031be2ab75d0..e8b3080d196a 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -272,6 +272,10 @@ struct snd_soc_dai { struct snd_soc_codec *codec; struct snd_soc_component *component; + /* CODEC TDM slot masks and params (for fixup) */ + unsigned int tx_mask; + unsigned int rx_mask; + struct snd_soc_card *card; struct list_head list; diff --git a/include/sound/soc.h b/include/sound/soc.h index 9a5b4f6fe847..f2142cf3f243 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -846,6 +846,12 @@ struct snd_soc_platform_driver { int (*bespoke_trigger)(struct snd_pcm_substream *, int); }; +struct snd_soc_dai_link_component { + const char *name; + const struct device_node *of_node; + const char *dai_name; +}; + struct snd_soc_platform { struct device *dev; const struct snd_soc_platform_driver *driver; @@ -891,6 +897,10 @@ struct snd_soc_dai_link { const struct device_node *codec_of_node; /* You MUST specify the DAI name within the codec */ const char *codec_dai_name; + + struct snd_soc_dai_link_component *codecs; + unsigned int num_codecs; + /* * 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 @@ -1089,6 +1099,9 @@ struct snd_soc_pcm_runtime { struct snd_soc_dai *codec_dai; struct snd_soc_dai *cpu_dai; + struct snd_soc_dai **codec_dais; + unsigned int num_codecs; + struct delayed_work delayed_work; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_dpcm_root; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index c32d8399e770..6e47610f73a0 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -576,7 +576,7 @@ int snd_soc_suspend(struct device *dev) { struct snd_soc_card *card = dev_get_drvdata(dev); struct snd_soc_codec *codec; - int i; + int i, j; /* If the initialization of this soc device failed, there is no codec * associated with it. Just bail out in this case. @@ -596,14 +596,17 @@ int snd_soc_suspend(struct device *dev) /* mute any active DACs */ for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_dai *dai = card->rtd[i].codec_dai; - struct snd_soc_dai_driver *drv = dai->driver; if (card->rtd[i].dai_link->ignore_suspend) continue; - if (drv->ops->digital_mute && dai->playback_active) - drv->ops->digital_mute(dai, 1); + for (j = 0; j < card->rtd[i].num_codecs; j++) { + struct snd_soc_dai *dai = card->rtd[i].codec_dais[j]; + struct snd_soc_dai_driver *drv = dai->driver; + + if (drv->ops->digital_mute && dai->playback_active) + drv->ops->digital_mute(dai, 1); + } } /* suspend all pcms */ @@ -634,8 +637,12 @@ int snd_soc_suspend(struct device *dev) /* close any waiting streams and save state */ for (i = 0; i < card->num_rtd; i++) { + struct snd_soc_dai **codec_dais = card->rtd[i].codec_dais; flush_delayed_work(&card->rtd[i].delayed_work); - card->rtd[i].codec->dapm.suspend_bias_level = card->rtd[i].codec->dapm.bias_level; + for (j = 0; j < card->rtd[i].num_codecs; j++) { + codec_dais[j]->codec->dapm.suspend_bias_level = + codec_dais[j]->codec->dapm.bias_level; + } } for (i = 0; i < card->num_rtd; i++) { @@ -719,7 +726,7 @@ static void soc_resume_deferred(struct work_struct *work) struct snd_soc_card *card = container_of(work, struct snd_soc_card, deferred_resume_work); struct snd_soc_codec *codec; - int i; + int i, j; /* our power state is still SNDRV_CTL_POWER_D3hot from suspend time, * so userspace apps are blocked from touching us @@ -780,14 +787,17 @@ static void soc_resume_deferred(struct work_struct *work) /* unmute any active DACs */ for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_dai *dai = card->rtd[i].codec_dai; - struct snd_soc_dai_driver *drv = dai->driver; if (card->rtd[i].dai_link->ignore_suspend) continue; - if (drv->ops->digital_mute && dai->playback_active) - drv->ops->digital_mute(dai, 0); + for (j = 0; j < card->rtd[i].num_codecs; j++) { + struct snd_soc_dai *dai = card->rtd[i].codec_dais[j]; + struct snd_soc_dai_driver *drv = dai->driver; + + if (drv->ops->digital_mute && dai->playback_active) + drv->ops->digital_mute(dai, 0); + } } for (i = 0; i < card->num_rtd; i++) { @@ -832,12 +842,19 @@ int snd_soc_resume(struct device *dev) /* activate pins from sleep state */ for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; - struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai; + struct snd_soc_pcm_runtime *rtd = &card->rtd[i]; + struct snd_soc_dai **codec_dais = rtd->codec_dais; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int j; + if (cpu_dai->active) pinctrl_pm_select_default_state(cpu_dai->dev); - if (codec_dai->active) - pinctrl_pm_select_default_state(codec_dai->dev); + + for (j = 0; j < rtd->num_codecs; j++) { + struct snd_soc_dai *codec_dai = codec_dais[j]; + if (codec_dai->active) + pinctrl_pm_select_default_state(codec_dai->dev); + } } /* AC97 devices might have other drivers hanging off them so @@ -869,8 +886,9 @@ EXPORT_SYMBOL_GPL(snd_soc_resume); static const struct snd_soc_dai_ops null_dai_ops = { }; -static struct snd_soc_codec *soc_find_codec(const struct device_node *codec_of_node, - const char *codec_name) +static struct snd_soc_codec *soc_find_codec( + const struct device_node *codec_of_node, + const char *codec_name) { struct snd_soc_codec *codec; @@ -908,9 +926,12 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num) struct snd_soc_dai_link *dai_link = &card->dai_link[num]; struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; struct snd_soc_component *component; + struct snd_soc_dai_link_component *codecs = dai_link->codecs; + struct snd_soc_dai **codec_dais = rtd->codec_dais; struct snd_soc_platform *platform; struct snd_soc_dai *cpu_dai; const char *platform_name; + int i; dev_dbg(card->dev, "ASoC: binding %s at idx %d\n", dai_link->name, num); @@ -937,24 +958,30 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num) return -EPROBE_DEFER; } - /* Find CODEC from registered list */ - rtd->codec = soc_find_codec(dai_link->codec_of_node, - dai_link->codec_name); - if (!rtd->codec) { - dev_err(card->dev, "ASoC: CODEC %s not registered\n", - dai_link->codec_name); - return -EPROBE_DEFER; - } + rtd->num_codecs = dai_link->num_codecs; - /* Find CODEC DAI from registered list */ - rtd->codec_dai = soc_find_codec_dai(rtd->codec, - dai_link->codec_dai_name); - if (!rtd->codec_dai) { - dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n", - dai_link->codec_dai_name); - return -EPROBE_DEFER; + /* Find CODEC from registered CODECs */ + for (i = 0; i < rtd->num_codecs; i++) { + struct snd_soc_codec *codec; + codec = soc_find_codec(codecs[i].of_node, codecs[i].name); + if (!codec) { + dev_err(card->dev, "ASoC: CODEC %s not registered\n", + codecs[i].name); + return -EPROBE_DEFER; + } + + codec_dais[i] = soc_find_codec_dai(codec, codecs[i].dai_name); + if (!codec_dais[i]) { + dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n", + codecs[i].dai_name); + return -EPROBE_DEFER; + } } + /* Single codec links expect codec and codec_dai in runtime data */ + rtd->codec_dai = codec_dais[0]; + rtd->codec = rtd->codec_dai->codec; + /* if there's no platform we match on the empty platform */ platform_name = dai_link->platform_name; if (!platform_name && !dai_link->platform_of_node) @@ -1045,8 +1072,8 @@ static void soc_remove_codec_dai(struct snd_soc_dai *codec_dai, int order) static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order) { struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; - struct snd_soc_dai *codec_dai = rtd->codec_dai, *cpu_dai = rtd->cpu_dai; - int err; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int i, err; /* unregister the rtd device */ if (rtd->dev_registered) { @@ -1057,7 +1084,8 @@ static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order) } /* remove the CODEC DAI */ - soc_remove_codec_dai(codec_dai, order); + for (i = 0; i < rtd->num_codecs; i++) + soc_remove_codec_dai(rtd->codec_dais[i], order); /* remove the cpu_dai */ if (cpu_dai && cpu_dai->probed && @@ -1080,9 +1108,9 @@ static void soc_remove_link_components(struct snd_soc_card *card, int num, { struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_platform *platform = rtd->platform; struct snd_soc_codec *codec; + int i; /* remove the platform */ if (platform && platform->probed && @@ -1091,8 +1119,8 @@ static void soc_remove_link_components(struct snd_soc_card *card, int num, } /* remove the CODEC-side CODEC */ - if (codec_dai) { - codec = codec_dai->codec; + for (i = 0; i < rtd->num_codecs; i++) { + codec = rtd->codec_dais[i]->codec; if (codec && codec->probed && codec->driver->remove_order == order) soc_remove_codec(codec); @@ -1335,9 +1363,8 @@ static int soc_probe_link_components(struct snd_soc_card *card, int num, { struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_platform *platform = rtd->platform; - int ret; + int i, ret; /* probe the CPU-side component, if it is a CODEC */ if (cpu_dai->codec && @@ -1348,12 +1375,14 @@ static int soc_probe_link_components(struct snd_soc_card *card, int num, return ret; } - /* probe the CODEC-side component */ - if (!codec_dai->codec->probed && - codec_dai->codec->driver->probe_order == order) { - ret = soc_probe_codec(card, codec_dai->codec); - if (ret < 0) - return ret; + /* probe the CODEC-side components */ + for (i = 0; i < rtd->num_codecs; i++) { + if (!rtd->codec_dais[i]->codec->probed && + rtd->codec_dais[i]->codec->driver->probe_order == order) { + ret = soc_probe_codec(card, rtd->codec_dais[i]->codec); + if (ret < 0) + return ret; + } } /* probe the platform */ @@ -1400,6 +1429,9 @@ static int soc_link_dai_widgets(struct snd_soc_card *card, struct snd_soc_dapm_widget *play_w, *capture_w; int ret; + if (rtd->num_codecs > 1) + dev_warn(card->dev, "ASoC: Multiple codecs not supported yet\n"); + /* link the DAI widgets */ play_w = codec_dai->playback_widget; capture_w = cpu_dai->capture_widget; @@ -1432,19 +1464,18 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) { struct snd_soc_dai_link *dai_link = &card->dai_link[num]; struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; - struct snd_soc_codec *codec = rtd->codec; struct snd_soc_platform *platform = rtd->platform; - struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret; + int i, ret; dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n", card->name, num, order); /* config components */ cpu_dai->platform = platform; - codec_dai->card = card; cpu_dai->card = card; + for (i = 0; i < rtd->num_codecs; i++) + rtd->codec_dais[i]->card = card; /* set default power off timeout */ rtd->pmdown_time = pmdown_time; @@ -1471,9 +1502,11 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) } /* probe the CODEC DAI */ - ret = soc_probe_codec_dai(card, codec_dai, order); - if (ret) - return ret; + for (i = 0; i < rtd->num_codecs; i++) { + ret = soc_probe_codec_dai(card, rtd->codec_dais[i], order); + if (ret) + return ret; + } /* complete DAI probe during last probe */ if (order != SND_SOC_COMP_ORDER_LAST) @@ -1541,8 +1574,11 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) } /* add platform data for AC97 devices */ - if (rtd->codec_dai->driver->ac97_control) - snd_ac97_dev_add_pdata(codec->ac97, rtd->cpu_dai->ac97_pdata); + for (i = 0; i < rtd->num_codecs; i++) { + if (rtd->codec_dais[i]->driver->ac97_control) + snd_ac97_dev_add_pdata(rtd->codec_dais[i]->codec->ac97, + rtd->cpu_dai->ac97_pdata); + } return 0; } @@ -1580,11 +1616,6 @@ static int soc_register_ac97_codec(struct snd_soc_codec *codec, return 0; } -static int soc_register_ac97_dai_link(struct snd_soc_pcm_runtime *rtd) -{ - return soc_register_ac97_codec(rtd->codec, rtd->codec_dai); -} - static void soc_unregister_ac97_codec(struct snd_soc_codec *codec) { if (codec->ac97_registered) { @@ -1593,9 +1624,30 @@ static void soc_unregister_ac97_codec(struct snd_soc_codec *codec) } } +static int soc_register_ac97_dai_link(struct snd_soc_pcm_runtime *rtd) +{ + int i, ret; + + for (i = 0; i < rtd->num_codecs; i++) { + struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; + + ret = soc_register_ac97_codec(codec_dai->codec, codec_dai); + if (ret) { + while (--i >= 0) + soc_unregister_ac97_codec(codec_dai->codec); + return ret; + } + } + + return 0; +} + static void soc_unregister_ac97_dai_link(struct snd_soc_pcm_runtime *rtd) { - soc_unregister_ac97_codec(rtd->codec); + int i; + + for (i = 0; i < rtd->num_codecs; i++) + soc_unregister_ac97_codec(rtd->codec_dais[i]->codec); } #endif @@ -1794,16 +1846,23 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) card->num_dapm_routes); for (i = 0; i < card->num_links; i++) { + struct snd_soc_pcm_runtime *rtd = &card->rtd[i]; dai_link = &card->dai_link[i]; dai_fmt = dai_link->dai_fmt; if (dai_fmt) { - ret = snd_soc_dai_set_fmt(card->rtd[i].codec_dai, - dai_fmt); - if (ret != 0 && ret != -ENOTSUPP) - dev_warn(card->rtd[i].codec_dai->dev, - "ASoC: Failed to set DAI format: %d\n", - ret); + struct snd_soc_dai **codec_dais = rtd->codec_dais; + int j; + + for (j = 0; j < rtd->num_codecs; j++) { + struct snd_soc_dai *codec_dai = codec_dais[j]; + + ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt); + if (ret != 0 && ret != -ENOTSUPP) + dev_warn(codec_dai->dev, + "ASoC: Failed to set DAI format: %d\n", + ret); + } } /* If this is a regular CPU link there will be a platform */ @@ -2002,10 +2061,15 @@ int snd_soc_poweroff(struct device *dev) /* deactivate pins to sleep state */ for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; - struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai; - pinctrl_pm_select_sleep_state(codec_dai->dev); + struct snd_soc_pcm_runtime *rtd = &card->rtd[i]; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int j; + pinctrl_pm_select_sleep_state(cpu_dai->dev); + for (j = 0; j < rtd->num_codecs; j++) { + struct snd_soc_dai *codec_dai = rtd->codec_dais[j]; + pinctrl_pm_select_sleep_state(codec_dai->dev); + } } return 0; @@ -3585,6 +3649,9 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, else snd_soc_xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask); + dai->tx_mask = tx_mask; + dai->rx_mask = rx_mask; + if (dai->driver && dai->driver->ops->set_tdm_slot) return dai->driver->ops->set_tdm_slot(dai, tx_mask, rx_mask, slots, slot_width); @@ -3657,6 +3724,33 @@ int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute, } EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute); +static int snd_soc_init_multicodec(struct snd_soc_card *card, + struct snd_soc_dai_link *dai_link) +{ + /* Legacy codec/codec_dai link is a single entry in multicodec */ + if (dai_link->codec_name || dai_link->codec_of_node || + dai_link->codec_dai_name) { + dai_link->num_codecs = 1; + + dai_link->codecs = devm_kzalloc(card->dev, + sizeof(struct snd_soc_dai_link_component), + GFP_KERNEL); + if (!dai_link->codecs) + return -ENOMEM; + + dai_link->codecs[0].name = dai_link->codec_name; + dai_link->codecs[0].of_node = dai_link->codec_of_node; + dai_link->codecs[0].dai_name = dai_link->codec_dai_name; + } + + if (!dai_link->codecs) { + dev_err(card->dev, "ASoC: DAI link has no CODECs\n"); + return -EINVAL; + } + + return 0; +} + /** * snd_soc_register_card - Register a card with the ASoC core * @@ -3665,7 +3759,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute); */ int snd_soc_register_card(struct snd_soc_card *card) { - int i, ret; + int i, j, ret; if (!card->name || !card->dev) return -EINVAL; @@ -3673,22 +3767,29 @@ int snd_soc_register_card(struct snd_soc_card *card) for (i = 0; i < card->num_links; i++) { struct snd_soc_dai_link *link = &card->dai_link[i]; - /* - * Codec must be specified by 1 of name or OF node, - * not both or neither. - */ - if (!!link->codec_name == !!link->codec_of_node) { - dev_err(card->dev, - "ASoC: Neither/both codec name/of_node are set for %s\n", - link->name); - return -EINVAL; + ret = snd_soc_init_multicodec(card, link); + if (ret) { + dev_err(card->dev, "ASoC: failed to init multicodec\n"); + return ret; } - /* Codec DAI name must be specified */ - if (!link->codec_dai_name) { - dev_err(card->dev, - "ASoC: codec_dai_name not set for %s\n", - link->name); - return -EINVAL; + + for (j = 0; j < link->num_codecs; j++) { + /* + * Codec must be specified by 1 of name or OF node, + * not both or neither. + */ + if (!!link->codecs[j].name == + !!link->codecs[j].of_node) { + dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n", + link->name); + return -EINVAL; + } + /* Codec DAI name must be specified */ + if (!link->codecs[j].dai_name) { + dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n", + link->name); + return -EINVAL; + } } /* @@ -3744,6 +3845,12 @@ int snd_soc_register_card(struct snd_soc_card *card) for (i = 0; i < card->num_links; i++) { card->rtd[i].card = card; card->rtd[i].dai_link = &card->dai_link[i]; + card->rtd[i].codec_dais = devm_kzalloc(card->dev, + sizeof(struct snd_soc_dai *) * + (card->rtd[i].dai_link->num_codecs), + GFP_KERNEL); + if (card->rtd[i].codec_dais == NULL) + return -ENOMEM; } for (i = 0; i < card->num_aux_devs; i++) @@ -3760,10 +3867,16 @@ int snd_soc_register_card(struct snd_soc_card *card) /* deactivate pins to sleep state */ for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; - struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai; - if (!codec_dai->active) - pinctrl_pm_select_sleep_state(codec_dai->dev); + struct snd_soc_pcm_runtime *rtd = &card->rtd[i]; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int j; + + for (j = 0; j < rtd->num_codecs; j++) { + struct snd_soc_dai *codec_dai = rtd->codec_dais[j]; + if (!codec_dai->active) + pinctrl_pm_select_sleep_state(codec_dai->dev); + } + if (!cpu_dai->active) pinctrl_pm_select_sleep_state(cpu_dai->dev); } -- cgit v1.2.3 From 93e6958a3674d2fa42e2c24ad5156e65da1d8621 Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Tue, 8 Jul 2014 23:19:38 +0200 Subject: ASoC: pcm: Add soc_dai_hw_params helper Add a function helper to factorize the hw_params code. Suggested by Lars-Peter Clausen Signed-off-by: Benoit Cousson Tested-by: Lars-Peter Clausen Reviewed-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 4 ++++ sound/soc/soc-dapm.c | 28 ++++++++-------------------- sound/soc/soc-pcm.c | 43 ++++++++++++++++++++++++------------------- 3 files changed, 36 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index f2142cf3f243..98555f833ab4 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -436,6 +436,10 @@ int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream, int snd_soc_platform_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_platform *platform); +int soc_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai); + /* Jack reporting */ int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type, struct snd_soc_jack *jack); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 5c63c3b49f3f..9a047f360993 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3214,27 +3214,15 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - if (source->driver->ops && source->driver->ops->hw_params) { - substream.stream = SNDRV_PCM_STREAM_CAPTURE; - ret = source->driver->ops->hw_params(&substream, - params, source); - if (ret != 0) { - dev_err(source->dev, - "ASoC: hw_params() failed: %d\n", ret); - goto out; - } - } + substream.stream = SNDRV_PCM_STREAM_CAPTURE; + ret = soc_dai_hw_params(&substream, params, source); + if (ret < 0) + goto out; - if (sink->driver->ops && sink->driver->ops->hw_params) { - substream.stream = SNDRV_PCM_STREAM_PLAYBACK; - ret = sink->driver->ops->hw_params(&substream, params, - sink); - if (ret != 0) { - dev_err(sink->dev, - "ASoC: hw_params() failed: %d\n", ret); - goto out; - } - } + substream.stream = SNDRV_PCM_STREAM_PLAYBACK; + ret = soc_dai_hw_params(&substream, params, sink); + if (ret < 0) + goto out; break; case SND_SOC_DAPM_POST_PMU: diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index ec56d1831a86..3ec87d8aa181 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -789,6 +789,24 @@ static void soc_pcm_codec_params_fixup(struct snd_pcm_hw_params *params, interval->max = channels; } +int soc_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int ret; + + if (dai->driver->ops && dai->driver->ops->hw_params) { + ret = dai->driver->ops->hw_params(substream, params, dai); + if (ret < 0) { + dev_err(dai->dev, "ASoC: can't set %s hw params: %d\n", + dai->name, ret); + return ret; + } + } + + return 0; +} + /* * Called by ALSA when the hardware params are set by application. This * function can also be called multiple times and can allocate buffers @@ -832,17 +850,9 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, soc_pcm_codec_params_fixup(&codec_params, codec_dai->rx_mask); - if (codec_dai->driver->ops && - codec_dai->driver->ops->hw_params) { - ret = codec_dai->driver->ops->hw_params(substream, - &codec_params, codec_dai); - if (ret < 0) { - dev_err(codec_dai->dev, - "ASoC: can't set %s hw params: %d\n", - codec_dai->name, ret); - goto codec_err; - } - } + ret = soc_dai_hw_params(substream, &codec_params, codec_dai); + if(ret < 0) + goto codec_err; codec_dai->rate = params_rate(&codec_params); codec_dai->channels = params_channels(&codec_params); @@ -850,14 +860,9 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, params_format(&codec_params)); } - if (cpu_dai->driver->ops && cpu_dai->driver->ops->hw_params) { - ret = cpu_dai->driver->ops->hw_params(substream, params, cpu_dai); - if (ret < 0) { - dev_err(cpu_dai->dev, "ASoC: %s hw params failed: %d\n", - cpu_dai->name, ret); - goto interface_err; - } - } + ret = soc_dai_hw_params(substream, params, cpu_dai); + if (ret < 0) + goto interface_err; if (platform->driver->ops && platform->driver->ops->hw_params) { ret = platform->driver->ops->hw_params(substream, params); -- cgit v1.2.3 From 2dc41cff7545d55c6294525c811594576f8e119c Mon Sep 17 00:00:00 2001 From: David Held Date: Tue, 15 Jul 2014 23:28:32 -0400 Subject: udp: Use hash2 for long hash1 chains in __udp*_lib_mcast_deliver. Many multicast sources can have the same port which can result in a very large list when hashing by port only. Hash by address and port instead if this is the case. This makes multicast more similar to unicast. On a 24-core machine receiving from 500 multicast sockets on the same port, before this patch 80% of system CPU was used up by spin locking and only ~25% of packets were successfully delivered. With this patch, all packets are delivered and kernel overhead is ~8% system CPU on spinlocks. Signed-off-by: David Held Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/sock.h | 14 ++++++++++++++ net/ipv4/udp.c | 31 +++++++++++++++++++++---------- net/ipv6/udp.c | 30 ++++++++++++++++++++---------- 3 files changed, 55 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index 29e48a6d1ded..28f734601b50 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -660,6 +660,20 @@ static inline void sk_add_bind_node(struct sock *sk, #define sk_for_each_bound(__sk, list) \ hlist_for_each_entry(__sk, list, sk_bind_node) +/** + * sk_nulls_for_each_entry_offset - iterate over a list at a given struct offset + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @head: the head for your list. + * @offset: offset of hlist_node within the struct. + * + */ +#define sk_nulls_for_each_entry_offset(tpos, pos, head, offset) \ + for (pos = (head)->first; \ + (!is_a_nulls(pos)) && \ + ({ tpos = (typeof(*tpos) *)((void *)pos - offset); 1;}); \ + pos = pos->next) + static inline struct user_namespace *sk_user_ns(struct sock *sk) { /* Careful only use this in a context where these parameters diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index bbcc33737ef1..f31053b90ee0 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1619,6 +1619,8 @@ static void flush_stack(struct sock **stack, unsigned int count, if (skb1 && udp_queue_rcv_skb(sk, skb1) <= 0) skb1 = NULL; + + sock_put(sk); } if (unlikely(skb1)) kfree_skb(skb1); @@ -1651,10 +1653,20 @@ static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb, unsigned short hnum = ntohs(uh->dest); struct udp_hslot *hslot = udp_hashslot(udptable, net, hnum); int dif = skb->dev->ifindex; - unsigned int i, count = 0; + unsigned int count = 0, offset = offsetof(typeof(*sk), sk_nulls_node); + unsigned int hash2 = 0, hash2_any = 0, use_hash2 = (hslot->count > 10); + + if (use_hash2) { + hash2_any = udp4_portaddr_hash(net, htonl(INADDR_ANY), hnum) & + udp_table.mask; + hash2 = udp4_portaddr_hash(net, daddr, hnum) & udp_table.mask; +start_lookup: + hslot = &udp_table.hash2[hash2]; + offset = offsetof(typeof(*sk), __sk_common.skc_portaddr_node); + } spin_lock(&hslot->lock); - sk_nulls_for_each(sk, node, &hslot->head) { + sk_nulls_for_each_entry_offset(sk, node, &hslot->head, offset) { if (__udp_is_mcast_sock(net, sk, uh->dest, daddr, uh->source, saddr, @@ -1664,24 +1676,23 @@ static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb, count = 0; } stack[count++] = sk; + sock_hold(sk); } } - /* - * before releasing chain lock, we must take a reference on sockets - */ - for (i = 0; i < count; i++) - sock_hold(stack[i]); spin_unlock(&hslot->lock); + /* Also lookup *:port if we are using hash2 and haven't done so yet. */ + if (use_hash2 && hash2 != hash2_any) { + hash2 = hash2_any; + goto start_lookup; + } + /* * do the slow work with no lock held */ if (count) { flush_stack(stack, count, skb, count - 1); - - for (i = 0; i < count; i++) - sock_put(stack[i]); } else { kfree_skb(skb); } diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 7d3bd80085be..f9d8800bb72f 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -745,6 +745,7 @@ static void flush_stack(struct sock **stack, unsigned int count, if (skb1 && udpv6_queue_rcv_skb(sk, skb1) <= 0) skb1 = NULL; + sock_put(sk); } if (unlikely(skb1)) kfree_skb(skb1); @@ -774,10 +775,20 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb, unsigned short hnum = ntohs(uh->dest); struct udp_hslot *hslot = udp_hashslot(udptable, net, hnum); int dif = inet6_iif(skb); - unsigned int i, count = 0; + unsigned int count = 0, offset = offsetof(typeof(*sk), sk_nulls_node); + unsigned int hash2 = 0, hash2_any = 0, use_hash2 = (hslot->count > 10); + + if (use_hash2) { + hash2_any = udp6_portaddr_hash(net, &in6addr_any, hnum) & + udp_table.mask; + hash2 = udp6_portaddr_hash(net, daddr, hnum) & udp_table.mask; +start_lookup: + hslot = &udp_table.hash2[hash2]; + offset = offsetof(typeof(*sk), __sk_common.skc_portaddr_node); + } spin_lock(&hslot->lock); - sk_nulls_for_each(sk, node, &hslot->head) { + sk_nulls_for_each_entry_offset(sk, node, &hslot->head, offset) { if (__udp_v6_is_mcast_sock(net, sk, uh->dest, daddr, uh->source, saddr, @@ -791,21 +802,20 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb, count = 0; } stack[count++] = sk; + sock_hold(sk); } } - /* - * before releasing the lock, we must take reference on sockets - */ - for (i = 0; i < count; i++) - sock_hold(stack[i]); spin_unlock(&hslot->lock); + /* Also lookup *:port if we are using hash2 and haven't done so yet. */ + if (use_hash2 && hash2 != hash2_any) { + hash2 = hash2_any; + goto start_lookup; + } + if (count) { flush_stack(stack, count, skb, count - 1); - - for (i = 0; i < count; i++) - sock_put(stack[i]); } else { kfree_skb(skb); } -- cgit v1.2.3 From 1373a7739e886cda3ed4848293dc9c46bc5d0d6a Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 16 Jul 2014 06:55:46 -0400 Subject: net: clean up some sparse endianness warnings in ipv6.h sparse is throwing warnings when building sunrpc modules due to some endianness shenanigans in ipv6.h. Specifically: CHECK net/sunrpc/addr.c include/net/ipv6.h:573:17: warning: restricted __be64 degrades to integer include/net/ipv6.h:577:34: warning: restricted __be32 degrades to integer include/net/ipv6.h:573:17: warning: restricted __be64 degrades to integer include/net/ipv6.h:577:34: warning: restricted __be32 degrades to integer Sprinkle some endianness fixups to silence them. These should all get fixed up at compile time, so I don't think this will add any extra work to be done at runtime. Signed-off-by: Jeff Layton Signed-off-by: David S. Miller --- include/net/ipv6.h | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/ipv6.h b/include/net/ipv6.h index b9ac2357e7db..a25017247457 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -558,24 +558,29 @@ static inline u32 __ipv6_addr_jhash(const struct in6_addr *a, const u32 initval) static inline bool ipv6_addr_loopback(const struct in6_addr *a) { #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64 - const unsigned long *ul = (const unsigned long *)a; + const __be64 *be = (const __be64 *)a; - return (ul[0] | (ul[1] ^ cpu_to_be64(1))) == 0UL; + return (be[0] | (be[1] ^ cpu_to_be64(1))) == 0UL; #else return (a->s6_addr32[0] | a->s6_addr32[1] | - a->s6_addr32[2] | (a->s6_addr32[3] ^ htonl(1))) == 0; + a->s6_addr32[2] | (a->s6_addr32[3] ^ cpu_to_be32(1))) == 0; #endif } +/* + * Note that we must __force cast these to unsigned long to make sparse happy, + * since all of the endian-annotated types are fixed size regardless of arch. + */ static inline bool ipv6_addr_v4mapped(const struct in6_addr *a) { return ( #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64 - *(__be64 *)a | + *(unsigned long *)a | #else - (a->s6_addr32[0] | a->s6_addr32[1]) | + (__force unsigned long)(a->s6_addr32[0] | a->s6_addr32[1]) | #endif - (a->s6_addr32[2] ^ htonl(0x0000ffff))) == 0UL; + (__force unsigned long)(a->s6_addr32[2] ^ + cpu_to_be32(0x0000ffff))) == 0UL; } /* -- cgit v1.2.3 From 3a6bfbc91df04b081a44d419e0260bad54abddf7 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Sun, 29 Jun 2014 15:09:33 -0700 Subject: arch, locking: Ciao arch_mutex_cpu_relax() The arch_mutex_cpu_relax() function, introduced by 34b133f, is hacky and ugly. It was added a few years ago to address the fact that common cpu_relax() calls include yielding on s390, and thus impact the optimistic spinning functionality of mutexes. Nowadays we use this function well beyond mutexes: rwsem, qrwlock, mcs and lockref. Since the macro that defines the call is in the mutex header, any users must include mutex.h and the naming is misleading as well. This patch (i) renames the call to cpu_relax_lowlatency ("relax, but only if you can do it with very low latency") and (ii) defines it in each arch's asm/processor.h local header, just like for regular cpu_relax functions. On all archs, except s390, cpu_relax_lowlatency is simply cpu_relax, and thus we can take it out of mutex.h. While this can seem redundant, I believe it is a good choice as it allows us to move out arch specific logic from generic locking primitives and enables future(?) archs to transparently define it, similarly to System Z. Signed-off-by: Davidlohr Bueso Signed-off-by: Peter Zijlstra Cc: Andrew Morton Cc: Anton Blanchard Cc: Aurelien Jacquiot Cc: Benjamin Herrenschmidt Cc: Bharat Bhushan Cc: Catalin Marinas Cc: Chen Liqin Cc: Chris Metcalf Cc: Christian Borntraeger Cc: Chris Zankel Cc: David Howells Cc: David S. Miller Cc: Deepthi Dharwar Cc: Dominik Dingel Cc: Fenghua Yu Cc: Geert Uytterhoeven Cc: Guan Xuetao Cc: Haavard Skinnemoen Cc: Hans-Christian Egtvedt Cc: Heiko Carstens Cc: Helge Deller Cc: Hirokazu Takata Cc: Ivan Kokshaysky Cc: James E.J. Bottomley Cc: James Hogan Cc: Jason Wang Cc: Jesper Nilsson Cc: Joe Perches Cc: Jonas Bonn Cc: Joseph Myers Cc: Kees Cook Cc: Koichi Yasutake Cc: Lennox Wu Cc: Linus Torvalds Cc: Mark Salter Cc: Martin Schwidefsky Cc: Matt Turner Cc: Max Filippov Cc: Michael Neuling Cc: Michal Simek Cc: Mikael Starvik Cc: Nicolas Pitre Cc: Paolo Bonzini Cc: Paul Burton Cc: Paul E. McKenney Cc: Paul Gortmaker Cc: Paul Mackerras Cc: Qais Yousef Cc: Qiaowei Ren Cc: Rafael Wysocki Cc: Ralf Baechle Cc: Richard Henderson Cc: Richard Kuo Cc: Russell King Cc: Steven Miao Cc: Steven Rostedt Cc: Stratos Karafotis Cc: Tim Chen Cc: Tony Luck Cc: Vasily Kulikov Cc: Vineet Gupta Cc: Vineet Gupta Cc: Waiman Long Cc: Will Deacon Cc: Wolfram Sang Cc: adi-buildroot-devel@lists.sourceforge.net Cc: linux390@de.ibm.com Cc: linux-alpha@vger.kernel.org Cc: linux-am33-list@redhat.com Cc: linux-arm-kernel@lists.infradead.org Cc: linux-c6x-dev@linux-c6x.org Cc: linux-cris-kernel@axis.com Cc: linux-hexagon@vger.kernel.org Cc: linux-ia64@vger.kernel.org Cc: linux@lists.openrisc.net Cc: linux-m32r-ja@ml.linux-m32r.org Cc: linux-m32r@ml.linux-m32r.org Cc: linux-m68k@lists.linux-m68k.org Cc: linux-metag@vger.kernel.org Cc: linux-mips@linux-mips.org Cc: linux-parisc@vger.kernel.org Cc: linuxppc-dev@lists.ozlabs.org Cc: linux-s390@vger.kernel.org Cc: linux-sh@vger.kernel.org Cc: linux-xtensa@linux-xtensa.org Cc: sparclinux@vger.kernel.org Link: http://lkml.kernel.org/r/1404079773.2619.4.camel@buesod1.americas.hpqcorp.net Signed-off-by: Ingo Molnar --- arch/alpha/include/asm/processor.h | 1 + arch/arc/include/asm/processor.h | 2 ++ arch/arm/include/asm/processor.h | 2 ++ arch/arm64/include/asm/processor.h | 1 + arch/avr32/include/asm/processor.h | 1 + arch/blackfin/include/asm/processor.h | 2 +- arch/c6x/include/asm/processor.h | 1 + arch/cris/include/asm/processor.h | 1 + arch/hexagon/include/asm/processor.h | 1 + arch/ia64/include/asm/processor.h | 1 + arch/m32r/include/asm/processor.h | 1 + arch/m68k/include/asm/processor.h | 1 + arch/metag/include/asm/processor.h | 1 + arch/microblaze/include/asm/processor.h | 1 + arch/mips/include/asm/processor.h | 1 + arch/mn10300/include/asm/processor.h | 2 ++ arch/openrisc/include/asm/processor.h | 1 + arch/parisc/include/asm/processor.h | 1 + arch/powerpc/include/asm/processor.h | 2 ++ arch/s390/include/asm/processor.h | 2 +- arch/score/include/asm/processor.h | 1 + arch/sh/include/asm/processor.h | 1 + arch/sparc/include/asm/processor_32.h | 2 ++ arch/sparc/include/asm/processor_64.h | 1 + arch/tile/include/asm/processor.h | 2 ++ arch/unicore32/include/asm/processor.h | 1 + arch/x86/include/asm/processor.h | 2 ++ arch/x86/um/asm/processor.h | 3 ++- arch/xtensa/include/asm/processor.h | 1 + include/linux/mutex.h | 4 ---- kernel/locking/mcs_spinlock.c | 8 +++----- kernel/locking/mcs_spinlock.h | 4 ++-- kernel/locking/mutex.c | 4 ++-- kernel/locking/qrwlock.c | 9 ++++----- kernel/locking/rwsem-xadd.c | 4 ++-- lib/lockref.c | 3 +-- 36 files changed, 51 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/arch/alpha/include/asm/processor.h b/arch/alpha/include/asm/processor.h index 6cb7fe85c4b5..b4cf03690394 100644 --- a/arch/alpha/include/asm/processor.h +++ b/arch/alpha/include/asm/processor.h @@ -57,6 +57,7 @@ unsigned long get_wchan(struct task_struct *p); ((tsk) == current ? rdusp() : task_thread_info(tsk)->pcb.usp) #define cpu_relax() barrier() +#define cpu_relax_lowlatency() cpu_relax() #define ARCH_HAS_PREFETCH #define ARCH_HAS_PREFETCHW diff --git a/arch/arc/include/asm/processor.h b/arch/arc/include/asm/processor.h index d99f9b37cd15..82588f3ba77f 100644 --- a/arch/arc/include/asm/processor.h +++ b/arch/arc/include/asm/processor.h @@ -62,6 +62,8 @@ unsigned long thread_saved_pc(struct task_struct *t); #define cpu_relax() do { } while (0) #endif +#define cpu_relax_lowlatency() cpu_relax() + #define copy_segments(tsk, mm) do { } while (0) #define release_segments(mm) do { } while (0) diff --git a/arch/arm/include/asm/processor.h b/arch/arm/include/asm/processor.h index c3d5fc124a05..8a1e8e995dae 100644 --- a/arch/arm/include/asm/processor.h +++ b/arch/arm/include/asm/processor.h @@ -82,6 +82,8 @@ unsigned long get_wchan(struct task_struct *p); #define cpu_relax() barrier() #endif +#define cpu_relax_lowlatency() cpu_relax() + #define task_pt_regs(p) \ ((struct pt_regs *)(THREAD_START_SP + task_stack_page(p)) - 1) diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h index 34de2a8f7d93..4610b0daf1bf 100644 --- a/arch/arm64/include/asm/processor.h +++ b/arch/arm64/include/asm/processor.h @@ -129,6 +129,7 @@ extern void release_thread(struct task_struct *); unsigned long get_wchan(struct task_struct *p); #define cpu_relax() barrier() +#define cpu_relax_lowlatency() cpu_relax() /* Thread switching */ extern struct task_struct *cpu_switch_to(struct task_struct *prev, diff --git a/arch/avr32/include/asm/processor.h b/arch/avr32/include/asm/processor.h index 972adcc1e8f4..941593c7d9f3 100644 --- a/arch/avr32/include/asm/processor.h +++ b/arch/avr32/include/asm/processor.h @@ -92,6 +92,7 @@ extern struct avr32_cpuinfo boot_cpu_data; #define TASK_UNMAPPED_BASE (PAGE_ALIGN(TASK_SIZE / 3)) #define cpu_relax() barrier() +#define cpu_relax_lowlatency() cpu_relax() #define cpu_sync_pipeline() asm volatile("sub pc, -2" : : : "memory") struct cpu_context { diff --git a/arch/blackfin/include/asm/processor.h b/arch/blackfin/include/asm/processor.h index d0e72e9475a6..7acd46653df3 100644 --- a/arch/blackfin/include/asm/processor.h +++ b/arch/blackfin/include/asm/processor.h @@ -99,7 +99,7 @@ unsigned long get_wchan(struct task_struct *p); #define KSTK_ESP(tsk) ((tsk) == current ? rdusp() : (tsk)->thread.usp) #define cpu_relax() smp_mb() - +#define cpu_relax_lowlatency() cpu_relax() /* Get the Silicon Revision of the chip */ static inline uint32_t __pure bfin_revid(void) diff --git a/arch/c6x/include/asm/processor.h b/arch/c6x/include/asm/processor.h index b9eb3da7f278..f2ef31be2f8b 100644 --- a/arch/c6x/include/asm/processor.h +++ b/arch/c6x/include/asm/processor.h @@ -121,6 +121,7 @@ extern unsigned long get_wchan(struct task_struct *p); #define KSTK_ESP(task) (task_pt_regs(task)->sp) #define cpu_relax() do { } while (0) +#define cpu_relax_lowlatency() cpu_relax() extern const struct seq_operations cpuinfo_op; diff --git a/arch/cris/include/asm/processor.h b/arch/cris/include/asm/processor.h index 15b815df29c1..862126b58116 100644 --- a/arch/cris/include/asm/processor.h +++ b/arch/cris/include/asm/processor.h @@ -63,6 +63,7 @@ static inline void release_thread(struct task_struct *dead_task) #define init_stack (init_thread_union.stack) #define cpu_relax() barrier() +#define cpu_relax_lowlatency() cpu_relax() void default_idle(void); diff --git a/arch/hexagon/include/asm/processor.h b/arch/hexagon/include/asm/processor.h index 45a825402f63..d8501137c8d0 100644 --- a/arch/hexagon/include/asm/processor.h +++ b/arch/hexagon/include/asm/processor.h @@ -56,6 +56,7 @@ struct thread_struct { } #define cpu_relax() __vmyield() +#define cpu_relax_lowlatency() cpu_relax() /* * Decides where the kernel will search for a free chunk of vm space during diff --git a/arch/ia64/include/asm/processor.h b/arch/ia64/include/asm/processor.h index efd1b927ccb7..c7367130ab14 100644 --- a/arch/ia64/include/asm/processor.h +++ b/arch/ia64/include/asm/processor.h @@ -548,6 +548,7 @@ ia64_eoi (void) } #define cpu_relax() ia64_hint(ia64_hint_pause) +#define cpu_relax_lowlatency() cpu_relax() static inline int ia64_get_irr(unsigned int vector) diff --git a/arch/m32r/include/asm/processor.h b/arch/m32r/include/asm/processor.h index 5767367550c6..9f8fd9bef70f 100644 --- a/arch/m32r/include/asm/processor.h +++ b/arch/m32r/include/asm/processor.h @@ -133,5 +133,6 @@ unsigned long get_wchan(struct task_struct *p); #define KSTK_ESP(tsk) ((tsk)->thread.sp) #define cpu_relax() barrier() +#define cpu_relax_lowlatency() cpu_relax() #endif /* _ASM_M32R_PROCESSOR_H */ diff --git a/arch/m68k/include/asm/processor.h b/arch/m68k/include/asm/processor.h index b0768a657920..20dda1d4b860 100644 --- a/arch/m68k/include/asm/processor.h +++ b/arch/m68k/include/asm/processor.h @@ -176,5 +176,6 @@ unsigned long get_wchan(struct task_struct *p); #define task_pt_regs(tsk) ((struct pt_regs *) ((tsk)->thread.esp0)) #define cpu_relax() barrier() +#define cpu_relax_lowlatency() cpu_relax() #endif diff --git a/arch/metag/include/asm/processor.h b/arch/metag/include/asm/processor.h index a8a37477c66e..881071c07942 100644 --- a/arch/metag/include/asm/processor.h +++ b/arch/metag/include/asm/processor.h @@ -155,6 +155,7 @@ unsigned long get_wchan(struct task_struct *p); #define user_stack_pointer(regs) ((regs)->ctx.AX[0].U0) #define cpu_relax() barrier() +#define cpu_relax_lowlatency() cpu_relax() extern void setup_priv(void); diff --git a/arch/microblaze/include/asm/processor.h b/arch/microblaze/include/asm/processor.h index 9d31b057c355..497a988d79c2 100644 --- a/arch/microblaze/include/asm/processor.h +++ b/arch/microblaze/include/asm/processor.h @@ -22,6 +22,7 @@ extern const struct seq_operations cpuinfo_op; # define cpu_relax() barrier() +# define cpu_relax_lowlatency() cpu_relax() #define task_pt_regs(tsk) \ (((struct pt_regs *)(THREAD_SIZE + task_stack_page(tsk))) - 1) diff --git a/arch/mips/include/asm/processor.h b/arch/mips/include/asm/processor.h index ad70cba8daff..d5098bc554f4 100644 --- a/arch/mips/include/asm/processor.h +++ b/arch/mips/include/asm/processor.h @@ -367,6 +367,7 @@ unsigned long get_wchan(struct task_struct *p); #define KSTK_STATUS(tsk) (task_pt_regs(tsk)->cp0_status) #define cpu_relax() barrier() +#define cpu_relax_lowlatency() cpu_relax() /* * Return_address is a replacement for __builtin_return_address(count) diff --git a/arch/mn10300/include/asm/processor.h b/arch/mn10300/include/asm/processor.h index 8b80b19d0c8a..769d5ed8e992 100644 --- a/arch/mn10300/include/asm/processor.h +++ b/arch/mn10300/include/asm/processor.h @@ -68,7 +68,9 @@ extern struct mn10300_cpuinfo cpu_data[]; extern void identify_cpu(struct mn10300_cpuinfo *); extern void print_cpu_info(struct mn10300_cpuinfo *); extern void dodgy_tsc(void); + #define cpu_relax() barrier() +#define cpu_relax_lowlatency() cpu_relax() /* * User space process size: 1.75GB (default). diff --git a/arch/openrisc/include/asm/processor.h b/arch/openrisc/include/asm/processor.h index cab746fa9e87..4d235e3d2534 100644 --- a/arch/openrisc/include/asm/processor.h +++ b/arch/openrisc/include/asm/processor.h @@ -101,6 +101,7 @@ extern unsigned long thread_saved_pc(struct task_struct *t); #define init_stack (init_thread_union.stack) #define cpu_relax() barrier() +#define cpu_relax_lowlatency() cpu_relax() #endif /* __ASSEMBLY__ */ #endif /* __ASM_OPENRISC_PROCESSOR_H */ diff --git a/arch/parisc/include/asm/processor.h b/arch/parisc/include/asm/processor.h index d951c9681ab3..689a8ade3606 100644 --- a/arch/parisc/include/asm/processor.h +++ b/arch/parisc/include/asm/processor.h @@ -338,6 +338,7 @@ extern unsigned long get_wchan(struct task_struct *p); #define KSTK_ESP(tsk) ((tsk)->thread.regs.gr[30]) #define cpu_relax() barrier() +#define cpu_relax_lowlatency() cpu_relax() /* Used as a macro to identify the combined VIPT/PIPT cached * CPUs which require a guarantee of coherency (no inequivalent diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h index 6d59072e13a7..dda7ac4c80bd 100644 --- a/arch/powerpc/include/asm/processor.h +++ b/arch/powerpc/include/asm/processor.h @@ -400,6 +400,8 @@ static inline unsigned long __pack_fe01(unsigned int fpmode) #define cpu_relax() barrier() #endif +#define cpu_relax_lowlatency() cpu_relax() + /* Check that a certain kernel stack pointer is valid in task_struct p */ int validate_sp(unsigned long sp, struct task_struct *p, unsigned long nbytes); diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h index 6f02d452bbee..e568fc8a7250 100644 --- a/arch/s390/include/asm/processor.h +++ b/arch/s390/include/asm/processor.h @@ -217,7 +217,7 @@ static inline void cpu_relax(void) barrier(); } -#define arch_mutex_cpu_relax() barrier() +#define cpu_relax_lowlatency() barrier() static inline void psw_set_key(unsigned int key) { diff --git a/arch/score/include/asm/processor.h b/arch/score/include/asm/processor.h index d9a922d8711b..851f441991d2 100644 --- a/arch/score/include/asm/processor.h +++ b/arch/score/include/asm/processor.h @@ -24,6 +24,7 @@ extern unsigned long get_wchan(struct task_struct *p); #define current_text_addr() ({ __label__ _l; _l: &&_l; }) #define cpu_relax() barrier() +#define cpu_relax_lowlatency() cpu_relax() #define release_thread(thread) do {} while (0) /* diff --git a/arch/sh/include/asm/processor.h b/arch/sh/include/asm/processor.h index 5448f9bbf4ab..1506897648aa 100644 --- a/arch/sh/include/asm/processor.h +++ b/arch/sh/include/asm/processor.h @@ -97,6 +97,7 @@ extern struct sh_cpuinfo cpu_data[]; #define cpu_sleep() __asm__ __volatile__ ("sleep" : : : "memory") #define cpu_relax() barrier() +#define cpu_relax_lowlatency() cpu_relax() void default_idle(void); void stop_this_cpu(void *); diff --git a/arch/sparc/include/asm/processor_32.h b/arch/sparc/include/asm/processor_32.h index a564817bbc2e..812fd08f3e62 100644 --- a/arch/sparc/include/asm/processor_32.h +++ b/arch/sparc/include/asm/processor_32.h @@ -119,6 +119,8 @@ extern struct task_struct *last_task_used_math; int do_mathemu(struct pt_regs *regs, struct task_struct *fpt); #define cpu_relax() barrier() +#define cpu_relax_lowlatency() cpu_relax() + extern void (*sparc_idle)(void); #endif diff --git a/arch/sparc/include/asm/processor_64.h b/arch/sparc/include/asm/processor_64.h index 7028fe1a7c04..6924bdefe148 100644 --- a/arch/sparc/include/asm/processor_64.h +++ b/arch/sparc/include/asm/processor_64.h @@ -216,6 +216,7 @@ unsigned long get_wchan(struct task_struct *task); "nop\n\t" \ ".previous" \ ::: "memory") +#define cpu_relax_lowlatency() cpu_relax() /* Prefetch support. This is tuned for UltraSPARC-III and later. * UltraSPARC-I will treat these as nops, and UltraSPARC-II has diff --git a/arch/tile/include/asm/processor.h b/arch/tile/include/asm/processor.h index 42323636c459..dd4f9f17e30a 100644 --- a/arch/tile/include/asm/processor.h +++ b/arch/tile/include/asm/processor.h @@ -266,6 +266,8 @@ static inline void cpu_relax(void) barrier(); } +#define cpu_relax_lowlatency() cpu_relax() + /* Info on this processor (see fs/proc/cpuinfo.c) */ struct seq_operations; extern const struct seq_operations cpuinfo_op; diff --git a/arch/unicore32/include/asm/processor.h b/arch/unicore32/include/asm/processor.h index 4eaa42167667..8d21b7adf26b 100644 --- a/arch/unicore32/include/asm/processor.h +++ b/arch/unicore32/include/asm/processor.h @@ -71,6 +71,7 @@ extern void release_thread(struct task_struct *); unsigned long get_wchan(struct task_struct *p); #define cpu_relax() barrier() +#define cpu_relax_lowlatency() cpu_relax() #define task_pt_regs(p) \ ((struct pt_regs *)(THREAD_START_SP + task_stack_page(p)) - 1) diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index a4ea02351f4d..32cc237f8e20 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -696,6 +696,8 @@ static inline void cpu_relax(void) rep_nop(); } +#define cpu_relax_lowlatency() cpu_relax() + /* Stop speculative execution and prefetching of modified code. */ static inline void sync_core(void) { diff --git a/arch/x86/um/asm/processor.h b/arch/x86/um/asm/processor.h index 04f82e020f2b..2a206d2b14ab 100644 --- a/arch/x86/um/asm/processor.h +++ b/arch/x86/um/asm/processor.h @@ -25,7 +25,8 @@ static inline void rep_nop(void) __asm__ __volatile__("rep;nop": : :"memory"); } -#define cpu_relax() rep_nop() +#define cpu_relax() rep_nop() +#define cpu_relax_lowlatency() cpu_relax() #include diff --git a/arch/xtensa/include/asm/processor.h b/arch/xtensa/include/asm/processor.h index abb59708a3b7..b61bdf0eea25 100644 --- a/arch/xtensa/include/asm/processor.h +++ b/arch/xtensa/include/asm/processor.h @@ -182,6 +182,7 @@ extern unsigned long get_wchan(struct task_struct *p); #define KSTK_ESP(tsk) (task_pt_regs(tsk)->areg[1]) #define cpu_relax() barrier() +#define cpu_relax_lowlatency() cpu_relax() /* Special register access. */ diff --git a/include/linux/mutex.h b/include/linux/mutex.h index 42aa9b9ecd5f..8d5535c58cc2 100644 --- a/include/linux/mutex.h +++ b/include/linux/mutex.h @@ -176,8 +176,4 @@ extern void mutex_unlock(struct mutex *lock); extern int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock); -#ifndef arch_mutex_cpu_relax -# define arch_mutex_cpu_relax() cpu_relax() -#endif - #endif /* __LINUX_MUTEX_H */ diff --git a/kernel/locking/mcs_spinlock.c b/kernel/locking/mcs_spinlock.c index be9ee1559fca..9887a905a762 100644 --- a/kernel/locking/mcs_spinlock.c +++ b/kernel/locking/mcs_spinlock.c @@ -1,6 +1,4 @@ - #include -#include #include #include "mcs_spinlock.h" @@ -79,7 +77,7 @@ osq_wait_next(struct optimistic_spin_queue *lock, break; } - arch_mutex_cpu_relax(); + cpu_relax_lowlatency(); } return next; @@ -120,7 +118,7 @@ bool osq_lock(struct optimistic_spin_queue *lock) if (need_resched()) goto unqueue; - arch_mutex_cpu_relax(); + cpu_relax_lowlatency(); } return true; @@ -146,7 +144,7 @@ unqueue: if (smp_load_acquire(&node->locked)) return true; - arch_mutex_cpu_relax(); + cpu_relax_lowlatency(); /* * Or we race against a concurrent unqueue()'s step-B, in which diff --git a/kernel/locking/mcs_spinlock.h b/kernel/locking/mcs_spinlock.h index 74356dc0ce29..23e89c5930e9 100644 --- a/kernel/locking/mcs_spinlock.h +++ b/kernel/locking/mcs_spinlock.h @@ -27,7 +27,7 @@ struct mcs_spinlock { #define arch_mcs_spin_lock_contended(l) \ do { \ while (!(smp_load_acquire(l))) \ - arch_mutex_cpu_relax(); \ + cpu_relax_lowlatency(); \ } while (0) #endif @@ -104,7 +104,7 @@ void mcs_spin_unlock(struct mcs_spinlock **lock, struct mcs_spinlock *node) return; /* Wait until the next pointer is set */ while (!(next = ACCESS_ONCE(node->next))) - arch_mutex_cpu_relax(); + cpu_relax_lowlatency(); } /* Pass lock to next waiter. */ diff --git a/kernel/locking/mutex.c b/kernel/locking/mutex.c index d3100521388c..ae712b25e492 100644 --- a/kernel/locking/mutex.c +++ b/kernel/locking/mutex.c @@ -146,7 +146,7 @@ int mutex_spin_on_owner(struct mutex *lock, struct task_struct *owner) if (need_resched()) break; - arch_mutex_cpu_relax(); + cpu_relax_lowlatency(); } rcu_read_unlock(); @@ -464,7 +464,7 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass, * memory barriers as we'll eventually observe the right * values at the cost of a few extra spins. */ - arch_mutex_cpu_relax(); + cpu_relax_lowlatency(); } osq_unlock(&lock->osq); slowpath: diff --git a/kernel/locking/qrwlock.c b/kernel/locking/qrwlock.c index fb5b8ac411a5..f956ede7f90d 100644 --- a/kernel/locking/qrwlock.c +++ b/kernel/locking/qrwlock.c @@ -20,7 +20,6 @@ #include #include #include -#include #include /** @@ -35,7 +34,7 @@ static __always_inline void rspin_until_writer_unlock(struct qrwlock *lock, u32 cnts) { while ((cnts & _QW_WMASK) == _QW_LOCKED) { - arch_mutex_cpu_relax(); + cpu_relax_lowlatency(); cnts = smp_load_acquire((u32 *)&lock->cnts); } } @@ -75,7 +74,7 @@ void queue_read_lock_slowpath(struct qrwlock *lock) * to make sure that the write lock isn't taken. */ while (atomic_read(&lock->cnts) & _QW_WMASK) - arch_mutex_cpu_relax(); + cpu_relax_lowlatency(); cnts = atomic_add_return(_QR_BIAS, &lock->cnts) - _QR_BIAS; rspin_until_writer_unlock(lock, cnts); @@ -114,7 +113,7 @@ void queue_write_lock_slowpath(struct qrwlock *lock) cnts | _QW_WAITING) == cnts)) break; - arch_mutex_cpu_relax(); + cpu_relax_lowlatency(); } /* When no more readers, set the locked flag */ @@ -125,7 +124,7 @@ void queue_write_lock_slowpath(struct qrwlock *lock) _QW_LOCKED) == _QW_WAITING)) break; - arch_mutex_cpu_relax(); + cpu_relax_lowlatency(); } unlock: arch_spin_unlock(&lock->lock); diff --git a/kernel/locking/rwsem-xadd.c b/kernel/locking/rwsem-xadd.c index a2391ac135c8..d6203faf2eb1 100644 --- a/kernel/locking/rwsem-xadd.c +++ b/kernel/locking/rwsem-xadd.c @@ -329,7 +329,7 @@ bool rwsem_spin_on_owner(struct rw_semaphore *sem, struct task_struct *owner) if (need_resched()) break; - arch_mutex_cpu_relax(); + cpu_relax_lowlatency(); } rcu_read_unlock(); @@ -381,7 +381,7 @@ static bool rwsem_optimistic_spin(struct rw_semaphore *sem) * memory barriers as we'll eventually observe the right * values at the cost of a few extra spins. */ - arch_mutex_cpu_relax(); + cpu_relax_lowlatency(); } osq_unlock(&sem->osq); done: diff --git a/lib/lockref.c b/lib/lockref.c index f07a40d33871..d2233de9a86e 100644 --- a/lib/lockref.c +++ b/lib/lockref.c @@ -1,6 +1,5 @@ #include #include -#include #if USE_CMPXCHG_LOCKREF @@ -29,7 +28,7 @@ if (likely(old.lock_count == prev.lock_count)) { \ SUCCESS; \ } \ - arch_mutex_cpu_relax(); \ + cpu_relax_lowlatency(); \ } \ } while (0) -- cgit v1.2.3 From 306a7f9139318a28063282a15b9f9ebacf09c9b9 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 17 Jul 2014 13:17:24 +0200 Subject: ARM: tegra: Move includes to include/soc/tegra In order to not clutter the include/linux directory with SoC specific headers, move the Tegra-specific headers out into a separate directory. Signed-off-by: Thierry Reding --- arch/arm/mach-tegra/fuse.c | 3 +- arch/arm/mach-tegra/pmc.c | 3 +- arch/arm/mach-tegra/powergate.c | 3 +- drivers/amba/tegra-ahb.c | 3 +- drivers/clk/tegra/clk-periph-gate.c | 3 +- drivers/clk/tegra/clk-tegra30.c | 5 +- drivers/clk/tegra/clk.c | 3 +- drivers/gpu/drm/tegra/gr3d.c | 3 +- drivers/gpu/drm/tegra/sor.c | 3 +- drivers/iommu/tegra-smmu.c | 3 +- drivers/pci/host/pci-tegra.c | 5 +- include/linux/tegra-ahb.h | 19 ----- include/linux/tegra-cpuidle.h | 25 ------- include/linux/tegra-powergate.h | 134 ------------------------------------ include/linux/tegra-soc.h | 22 ------ include/soc/tegra/ahb.h | 19 +++++ include/soc/tegra/cpuidle.h | 25 +++++++ include/soc/tegra/fuse.h | 22 ++++++ include/soc/tegra/powergate.h | 134 ++++++++++++++++++++++++++++++++++++ 19 files changed, 225 insertions(+), 212 deletions(-) delete mode 100644 include/linux/tegra-ahb.h delete mode 100644 include/linux/tegra-cpuidle.h delete mode 100644 include/linux/tegra-powergate.h delete mode 100644 include/linux/tegra-soc.h create mode 100644 include/soc/tegra/ahb.h create mode 100644 include/soc/tegra/cpuidle.h create mode 100644 include/soc/tegra/fuse.h create mode 100644 include/soc/tegra/powergate.h (limited to 'include') diff --git a/arch/arm/mach-tegra/fuse.c b/arch/arm/mach-tegra/fuse.c index c9ac23b385be..930fef861227 100644 --- a/arch/arm/mach-tegra/fuse.c +++ b/arch/arm/mach-tegra/fuse.c @@ -23,7 +23,8 @@ #include #include #include -#include + +#include #include "fuse.h" #include "iomap.h" diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c index 7c7123e7557b..0f457801eaca 100644 --- a/arch/arm/mach-tegra/pmc.c +++ b/arch/arm/mach-tegra/pmc.c @@ -20,7 +20,8 @@ #include #include #include -#include + +#include #include "flowctrl.h" #include "fuse.h" diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c index 4cefc5cd6bed..7b148bc6c995 100644 --- a/arch/arm/mach-tegra/powergate.c +++ b/arch/arm/mach-tegra/powergate.c @@ -29,7 +29,8 @@ #include #include #include -#include + +#include #include "fuse.h" #include "iomap.h" diff --git a/drivers/amba/tegra-ahb.c b/drivers/amba/tegra-ahb.c index 558a239954e8..d8961ef4d2e7 100644 --- a/drivers/amba/tegra-ahb.c +++ b/drivers/amba/tegra-ahb.c @@ -25,7 +25,8 @@ #include #include #include -#include + +#include #define DRV_NAME "tegra-ahb" diff --git a/drivers/clk/tegra/clk-periph-gate.c b/drivers/clk/tegra/clk-periph-gate.c index 507015314827..0aa8830ae7cc 100644 --- a/drivers/clk/tegra/clk-periph-gate.c +++ b/drivers/clk/tegra/clk-periph-gate.c @@ -20,7 +20,8 @@ #include #include #include -#include + +#include #include "clk.h" diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index 8b10c38b6e3c..5679ffdb3f8c 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -22,8 +22,11 @@ #include #include #include -#include + +#include + #include + #include "clk.h" #include "clk-id.h" diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c index c0a7d7723510..f4503ba97400 100644 --- a/drivers/clk/tegra/clk.c +++ b/drivers/clk/tegra/clk.c @@ -19,7 +19,8 @@ #include #include #include -#include + +#include #include "clk.h" diff --git a/drivers/gpu/drm/tegra/gr3d.c b/drivers/gpu/drm/tegra/gr3d.c index 30f5ba9bd6d0..69974851e564 100644 --- a/drivers/gpu/drm/tegra/gr3d.c +++ b/drivers/gpu/drm/tegra/gr3d.c @@ -12,7 +12,8 @@ #include #include #include -#include + +#include #include "drm.h" #include "gem.h" diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 27c979b50111..eafd0b8a71d2 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -11,7 +11,8 @@ #include #include #include -#include + +#include #include diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 605b5b46a903..230d06c9328b 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -35,7 +35,8 @@ #include #include #include -#include + +#include #include #include diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index 083cf37ca047..a2f0f88be382 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -41,11 +41,12 @@ #include #include #include -#include -#include #include #include +#include +#include + #include #include #include diff --git a/include/linux/tegra-ahb.h b/include/linux/tegra-ahb.h deleted file mode 100644 index f1cd075ceee1..000000000000 --- a/include/linux/tegra-ahb.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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. - */ - -#ifndef __LINUX_AHB_H__ -#define __LINUX_AHB_H__ - -extern int tegra_ahb_enable_smmu(struct device_node *ahb); - -#endif /* __LINUX_AHB_H__ */ diff --git a/include/linux/tegra-cpuidle.h b/include/linux/tegra-cpuidle.h deleted file mode 100644 index 9c6286bbf662..000000000000 --- a/include/linux/tegra-cpuidle.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2013, 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. - */ - -#ifndef __LINUX_TEGRA_CPUIDLE_H__ -#define __LINUX_TEGRA_CPUIDLE_H__ - -#ifdef CONFIG_CPU_IDLE -void tegra_cpuidle_pcie_irqs_in_use(void); -#else -static inline void tegra_cpuidle_pcie_irqs_in_use(void) -{ -} -#endif - -#endif diff --git a/include/linux/tegra-powergate.h b/include/linux/tegra-powergate.h deleted file mode 100644 index 46f0a07812b4..000000000000 --- a/include/linux/tegra-powergate.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (c) 2010 Google, Inc - * - * Author: - * Colin Cross - * - * 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 _MACH_TEGRA_POWERGATE_H_ -#define _MACH_TEGRA_POWERGATE_H_ - -struct clk; -struct reset_control; - -#define TEGRA_POWERGATE_CPU 0 -#define TEGRA_POWERGATE_3D 1 -#define TEGRA_POWERGATE_VENC 2 -#define TEGRA_POWERGATE_PCIE 3 -#define TEGRA_POWERGATE_VDEC 4 -#define TEGRA_POWERGATE_L2 5 -#define TEGRA_POWERGATE_MPE 6 -#define TEGRA_POWERGATE_HEG 7 -#define TEGRA_POWERGATE_SATA 8 -#define TEGRA_POWERGATE_CPU1 9 -#define TEGRA_POWERGATE_CPU2 10 -#define TEGRA_POWERGATE_CPU3 11 -#define TEGRA_POWERGATE_CELP 12 -#define TEGRA_POWERGATE_3D1 13 -#define TEGRA_POWERGATE_CPU0 14 -#define TEGRA_POWERGATE_C0NC 15 -#define TEGRA_POWERGATE_C1NC 16 -#define TEGRA_POWERGATE_SOR 17 -#define TEGRA_POWERGATE_DIS 18 -#define TEGRA_POWERGATE_DISB 19 -#define TEGRA_POWERGATE_XUSBA 20 -#define TEGRA_POWERGATE_XUSBB 21 -#define TEGRA_POWERGATE_XUSBC 22 -#define TEGRA_POWERGATE_VIC 23 -#define TEGRA_POWERGATE_IRAM 24 - -#define TEGRA_POWERGATE_3D0 TEGRA_POWERGATE_3D - -#define TEGRA_IO_RAIL_CSIA 0 -#define TEGRA_IO_RAIL_CSIB 1 -#define TEGRA_IO_RAIL_DSI 2 -#define TEGRA_IO_RAIL_MIPI_BIAS 3 -#define TEGRA_IO_RAIL_PEX_BIAS 4 -#define TEGRA_IO_RAIL_PEX_CLK1 5 -#define TEGRA_IO_RAIL_PEX_CLK2 6 -#define TEGRA_IO_RAIL_USB0 9 -#define TEGRA_IO_RAIL_USB1 10 -#define TEGRA_IO_RAIL_USB2 11 -#define TEGRA_IO_RAIL_USB_BIAS 12 -#define TEGRA_IO_RAIL_NAND 13 -#define TEGRA_IO_RAIL_UART 14 -#define TEGRA_IO_RAIL_BB 15 -#define TEGRA_IO_RAIL_AUDIO 17 -#define TEGRA_IO_RAIL_HSIC 19 -#define TEGRA_IO_RAIL_COMP 22 -#define TEGRA_IO_RAIL_HDMI 28 -#define TEGRA_IO_RAIL_PEX_CNTRL 32 -#define TEGRA_IO_RAIL_SDMMC1 33 -#define TEGRA_IO_RAIL_SDMMC3 34 -#define TEGRA_IO_RAIL_SDMMC4 35 -#define TEGRA_IO_RAIL_CAM 36 -#define TEGRA_IO_RAIL_RES 37 -#define TEGRA_IO_RAIL_HV 38 -#define TEGRA_IO_RAIL_DSIB 39 -#define TEGRA_IO_RAIL_DSIC 40 -#define TEGRA_IO_RAIL_DSID 41 -#define TEGRA_IO_RAIL_CSIE 44 -#define TEGRA_IO_RAIL_LVDS 57 -#define TEGRA_IO_RAIL_SYS_DDC 58 - -#ifdef CONFIG_ARCH_TEGRA -int tegra_powergate_is_powered(int id); -int tegra_powergate_power_on(int id); -int tegra_powergate_power_off(int id); -int tegra_powergate_remove_clamping(int id); - -/* Must be called with clk disabled, and returns with clk enabled */ -int tegra_powergate_sequence_power_up(int id, struct clk *clk, - struct reset_control *rst); - -int tegra_io_rail_power_on(int id); -int tegra_io_rail_power_off(int id); -#else -static inline int tegra_powergate_is_powered(int id) -{ - return -ENOSYS; -} - -static inline int tegra_powergate_power_on(int id) -{ - return -ENOSYS; -} - -static inline int tegra_powergate_power_off(int id) -{ - return -ENOSYS; -} - -static inline int tegra_powergate_remove_clamping(int id) -{ - return -ENOSYS; -} - -static inline int tegra_powergate_sequence_power_up(int id, struct clk *clk, - struct reset_control *rst) -{ - return -ENOSYS; -} - -static inline int tegra_io_rail_power_on(int id) -{ - return -ENOSYS; -} - -static inline int tegra_io_rail_power_off(int id) -{ - return -ENOSYS; -} -#endif - -#endif /* _MACH_TEGRA_POWERGATE_H_ */ diff --git a/include/linux/tegra-soc.h b/include/linux/tegra-soc.h deleted file mode 100644 index 95f611d78f3a..000000000000 --- a/include/linux/tegra-soc.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * 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, see . - */ - -#ifndef __LINUX_TEGRA_SOC_H_ -#define __LINUX_TEGRA_SOC_H_ - -u32 tegra_read_chipid(void); - -#endif /* __LINUX_TEGRA_SOC_H_ */ diff --git a/include/soc/tegra/ahb.h b/include/soc/tegra/ahb.h new file mode 100644 index 000000000000..504eb6f957e5 --- /dev/null +++ b/include/soc/tegra/ahb.h @@ -0,0 +1,19 @@ +/* + * 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. + */ + +#ifndef __SOC_TEGRA_AHB_H__ +#define __SOC_TEGRA_AHB_H__ + +extern int tegra_ahb_enable_smmu(struct device_node *ahb); + +#endif /* __SOC_TEGRA_AHB_H__ */ diff --git a/include/soc/tegra/cpuidle.h b/include/soc/tegra/cpuidle.h new file mode 100644 index 000000000000..ea04f4225638 --- /dev/null +++ b/include/soc/tegra/cpuidle.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2013, 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. + */ + +#ifndef __SOC_TEGRA_CPUIDLE_H__ +#define __SOC_TEGRA_CPUIDLE_H__ + +#ifdef CONFIG_CPU_IDLE +void tegra_cpuidle_pcie_irqs_in_use(void); +#else +static inline void tegra_cpuidle_pcie_irqs_in_use(void) +{ +} +#endif + +#endif /* __SOC_TEGRA_CPUIDLE_H__ */ diff --git a/include/soc/tegra/fuse.h b/include/soc/tegra/fuse.h new file mode 100644 index 000000000000..85f555c89ad5 --- /dev/null +++ b/include/soc/tegra/fuse.h @@ -0,0 +1,22 @@ +/* + * 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, see . + */ + +#ifndef __SOC_TEGRA_FUSE_H__ +#define __SOC_TEGRA_FUSE_H__ + +u32 tegra_read_chipid(void); + +#endif /* __SOC_TEGRA_FUSE_H__ */ diff --git a/include/soc/tegra/powergate.h b/include/soc/tegra/powergate.h new file mode 100644 index 000000000000..c16912ed1a8d --- /dev/null +++ b/include/soc/tegra/powergate.h @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2010 Google, Inc + * + * Author: + * Colin Cross + * + * 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 __SOC_TEGRA_POWERGATE_H__ +#define __SOC_TEGRA_POWERGATE_H__ + +struct clk; +struct reset_control; + +#define TEGRA_POWERGATE_CPU 0 +#define TEGRA_POWERGATE_3D 1 +#define TEGRA_POWERGATE_VENC 2 +#define TEGRA_POWERGATE_PCIE 3 +#define TEGRA_POWERGATE_VDEC 4 +#define TEGRA_POWERGATE_L2 5 +#define TEGRA_POWERGATE_MPE 6 +#define TEGRA_POWERGATE_HEG 7 +#define TEGRA_POWERGATE_SATA 8 +#define TEGRA_POWERGATE_CPU1 9 +#define TEGRA_POWERGATE_CPU2 10 +#define TEGRA_POWERGATE_CPU3 11 +#define TEGRA_POWERGATE_CELP 12 +#define TEGRA_POWERGATE_3D1 13 +#define TEGRA_POWERGATE_CPU0 14 +#define TEGRA_POWERGATE_C0NC 15 +#define TEGRA_POWERGATE_C1NC 16 +#define TEGRA_POWERGATE_SOR 17 +#define TEGRA_POWERGATE_DIS 18 +#define TEGRA_POWERGATE_DISB 19 +#define TEGRA_POWERGATE_XUSBA 20 +#define TEGRA_POWERGATE_XUSBB 21 +#define TEGRA_POWERGATE_XUSBC 22 +#define TEGRA_POWERGATE_VIC 23 +#define TEGRA_POWERGATE_IRAM 24 + +#define TEGRA_POWERGATE_3D0 TEGRA_POWERGATE_3D + +#define TEGRA_IO_RAIL_CSIA 0 +#define TEGRA_IO_RAIL_CSIB 1 +#define TEGRA_IO_RAIL_DSI 2 +#define TEGRA_IO_RAIL_MIPI_BIAS 3 +#define TEGRA_IO_RAIL_PEX_BIAS 4 +#define TEGRA_IO_RAIL_PEX_CLK1 5 +#define TEGRA_IO_RAIL_PEX_CLK2 6 +#define TEGRA_IO_RAIL_USB0 9 +#define TEGRA_IO_RAIL_USB1 10 +#define TEGRA_IO_RAIL_USB2 11 +#define TEGRA_IO_RAIL_USB_BIAS 12 +#define TEGRA_IO_RAIL_NAND 13 +#define TEGRA_IO_RAIL_UART 14 +#define TEGRA_IO_RAIL_BB 15 +#define TEGRA_IO_RAIL_AUDIO 17 +#define TEGRA_IO_RAIL_HSIC 19 +#define TEGRA_IO_RAIL_COMP 22 +#define TEGRA_IO_RAIL_HDMI 28 +#define TEGRA_IO_RAIL_PEX_CNTRL 32 +#define TEGRA_IO_RAIL_SDMMC1 33 +#define TEGRA_IO_RAIL_SDMMC3 34 +#define TEGRA_IO_RAIL_SDMMC4 35 +#define TEGRA_IO_RAIL_CAM 36 +#define TEGRA_IO_RAIL_RES 37 +#define TEGRA_IO_RAIL_HV 38 +#define TEGRA_IO_RAIL_DSIB 39 +#define TEGRA_IO_RAIL_DSIC 40 +#define TEGRA_IO_RAIL_DSID 41 +#define TEGRA_IO_RAIL_CSIE 44 +#define TEGRA_IO_RAIL_LVDS 57 +#define TEGRA_IO_RAIL_SYS_DDC 58 + +#ifdef CONFIG_ARCH_TEGRA +int tegra_powergate_is_powered(int id); +int tegra_powergate_power_on(int id); +int tegra_powergate_power_off(int id); +int tegra_powergate_remove_clamping(int id); + +/* Must be called with clk disabled, and returns with clk enabled */ +int tegra_powergate_sequence_power_up(int id, struct clk *clk, + struct reset_control *rst); + +int tegra_io_rail_power_on(int id); +int tegra_io_rail_power_off(int id); +#else +static inline int tegra_powergate_is_powered(int id) +{ + return -ENOSYS; +} + +static inline int tegra_powergate_power_on(int id) +{ + return -ENOSYS; +} + +static inline int tegra_powergate_power_off(int id) +{ + return -ENOSYS; +} + +static inline int tegra_powergate_remove_clamping(int id) +{ + return -ENOSYS; +} + +static inline int tegra_powergate_sequence_power_up(int id, struct clk *clk, + struct reset_control *rst) +{ + return -ENOSYS; +} + +static inline int tegra_io_rail_power_on(int id) +{ + return -ENOSYS; +} + +static inline int tegra_io_rail_power_off(int id) +{ + return -ENOSYS; +} +#endif + +#endif /* __SOC_TEGRA_POWERGATE_H__ */ -- cgit v1.2.3 From 304664eab93f9e95a8d28fbd9702ede88bb10cc5 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 11 Jul 2014 09:52:41 +0200 Subject: ARM: tegra: Use a function to get the chip ID Instead of using a simple variable access to get at the Tegra chip ID, use a function so that we can run additional code. This can be used to determine where the chip ID is being accessed without being available. That in turn will be handy for resolving boot sequence dependencies in order to convert more code to regular initcalls rather than a sequence fixed by Tegra SoC setup code. Signed-off-by: Thierry Reding --- arch/arm/mach-tegra/cpuidle.c | 7 ++++--- arch/arm/mach-tegra/flowctrl.c | 7 ++++--- arch/arm/mach-tegra/fuse.c | 19 +++++++++++++------ arch/arm/mach-tegra/fuse.h | 6 ------ arch/arm/mach-tegra/hotplug.c | 11 ++++++----- arch/arm/mach-tegra/platsmp.c | 11 ++++++----- arch/arm/mach-tegra/pm.c | 11 ++++++----- arch/arm/mach-tegra/pmc.c | 4 ++-- arch/arm/mach-tegra/powergate.c | 8 ++++---- arch/arm/mach-tegra/reset-handler.S | 3 ++- arch/arm/mach-tegra/reset.c | 4 +++- arch/arm/mach-tegra/sleep-tegra30.S | 3 ++- arch/arm/mach-tegra/tegra.c | 4 +++- include/soc/tegra/fuse.h | 10 ++++++++++ 14 files changed, 65 insertions(+), 43 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-tegra/cpuidle.c b/arch/arm/mach-tegra/cpuidle.c index 7bc5d8d667fe..316563141add 100644 --- a/arch/arm/mach-tegra/cpuidle.c +++ b/arch/arm/mach-tegra/cpuidle.c @@ -24,12 +24,13 @@ #include #include -#include "fuse.h" +#include + #include "cpuidle.h" void __init tegra_cpuidle_init(void) { - switch (tegra_chip_id) { + switch (tegra_get_chip_id()) { case TEGRA20: if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)) tegra20_cpuidle_init(); @@ -49,7 +50,7 @@ void __init tegra_cpuidle_init(void) void tegra_cpuidle_pcie_irqs_in_use(void) { - switch (tegra_chip_id) { + switch (tegra_get_chip_id()) { case TEGRA20: if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)) tegra20_cpuidle_pcie_irqs_in_use(); diff --git a/arch/arm/mach-tegra/flowctrl.c b/arch/arm/mach-tegra/flowctrl.c index fde581d78398..ec55d1de1b55 100644 --- a/arch/arm/mach-tegra/flowctrl.c +++ b/arch/arm/mach-tegra/flowctrl.c @@ -23,9 +23,10 @@ #include #include +#include + #include "flowctrl.h" #include "iomap.h" -#include "fuse.h" static u8 flowctrl_offset_halt_cpu[] = { FLOW_CTRL_HALT_CPU0_EVENTS, @@ -76,7 +77,7 @@ void flowctrl_cpu_suspend_enter(unsigned int cpuid) int i; reg = flowctrl_read_cpu_csr(cpuid); - switch (tegra_chip_id) { + switch (tegra_get_chip_id()) { case TEGRA20: /* clear wfe bitmap */ reg &= ~TEGRA20_FLOW_CTRL_CSR_WFE_BITMAP; @@ -117,7 +118,7 @@ void flowctrl_cpu_suspend_exit(unsigned int cpuid) /* Disable powergating via flow controller for CPU0 */ reg = flowctrl_read_cpu_csr(cpuid); - switch (tegra_chip_id) { + switch (tegra_get_chip_id()) { case TEGRA20: /* clear wfe bitmap */ reg &= ~TEGRA20_FLOW_CTRL_CSR_WFE_BITMAP; diff --git a/arch/arm/mach-tegra/fuse.c b/arch/arm/mach-tegra/fuse.c index af283039e37f..b22e76a40965 100644 --- a/arch/arm/mach-tegra/fuse.c +++ b/arch/arm/mach-tegra/fuse.c @@ -51,7 +51,6 @@ int tegra_sku_id; int tegra_cpu_process_id; int tegra_core_process_id; -int tegra_chip_id; int tegra_cpu_speedo_id; /* only exist in Tegra30 and later */ int tegra_soc_speedo_id; enum tegra_revision tegra_revision; @@ -124,7 +123,7 @@ static enum tegra_revision tegra_get_revision(u32 id) case 2: return TEGRA_REVISION_A02; case 3: - if (tegra_chip_id == TEGRA20 && + if (tegra_get_chip_id() == TEGRA20 && (tegra_spare_fuse(18) || tegra_spare_fuse(19))) return TEGRA_REVISION_A03p; else @@ -155,6 +154,13 @@ u32 tegra_read_chipid(void) return readl_relaxed(IO_ADDRESS(TEGRA_APB_MISC_BASE) + 0x804); } +u8 tegra_get_chip_id(void) +{ + u32 id = tegra_read_chipid(); + + return (id >> 8) & 0xff; +} + static void __init tegra20_fuse_init_randomness(void) { u32 randomness[2]; @@ -185,6 +191,7 @@ void __init tegra_init_fuse(void) { u32 id; u32 randomness[5]; + u8 chip_id; u32 reg = readl(IO_ADDRESS(TEGRA_CLK_RESET_BASE + 0x48)); reg |= 1 << 28; @@ -209,9 +216,9 @@ void __init tegra_init_fuse(void) id = tegra_read_chipid(); randomness[2] = id; - tegra_chip_id = (id >> 8) & 0xff; + chip_id = (id >> 8) & 0xff; - switch (tegra_chip_id) { + switch (chip_id) { case TEGRA20: tegra_fuse_spare_bit = TEGRA20_FUSE_SPARE_BIT; tegra_init_speedo_data = &tegra20_init_speedo_data; @@ -224,7 +231,7 @@ void __init tegra_init_fuse(void) tegra_init_speedo_data = &tegra114_init_speedo_data; break; default: - pr_warn("Tegra: unknown chip id %d\n", tegra_chip_id); + pr_warn("Tegra: unknown chip id %d\n", chip_id); tegra_fuse_spare_bit = TEGRA20_FUSE_SPARE_BIT; tegra_init_speedo_data = &tegra_get_process_id; } @@ -235,7 +242,7 @@ void __init tegra_init_fuse(void) randomness[4] = (tegra_cpu_speedo_id << 16) | tegra_soc_speedo_id; add_device_randomness(randomness, sizeof(randomness)); - switch (tegra_chip_id) { + switch (chip_id) { case TEGRA20: tegra20_fuse_init_randomness(); break; diff --git a/arch/arm/mach-tegra/fuse.h b/arch/arm/mach-tegra/fuse.h index c01d04785d67..7a08b4b70c8d 100644 --- a/arch/arm/mach-tegra/fuse.h +++ b/arch/arm/mach-tegra/fuse.h @@ -26,11 +26,6 @@ #define SKU_ID_AP25E 27 #define SKU_ID_T25E 28 -#define TEGRA20 0x20 -#define TEGRA30 0x30 -#define TEGRA114 0x35 -#define TEGRA124 0x40 - #ifndef __ASSEMBLY__ enum tegra_revision { TEGRA_REVISION_UNKNOWN = 0, @@ -45,7 +40,6 @@ enum tegra_revision { extern int tegra_sku_id; extern int tegra_cpu_process_id; extern int tegra_core_process_id; -extern int tegra_chip_id; extern int tegra_cpu_speedo_id; /* only exist in Tegra30 and later */ extern int tegra_soc_speedo_id; extern enum tegra_revision tegra_revision; diff --git a/arch/arm/mach-tegra/hotplug.c b/arch/arm/mach-tegra/hotplug.c index 7842b252dda5..d60339c996cb 100644 --- a/arch/arm/mach-tegra/hotplug.c +++ b/arch/arm/mach-tegra/hotplug.c @@ -12,9 +12,10 @@ #include #include +#include + #include -#include "fuse.h" #include "sleep.h" static void (*tegra_hotplug_shutdown)(void); @@ -52,12 +53,12 @@ void __init tegra_hotplug_init(void) if (!IS_ENABLED(CONFIG_HOTPLUG_CPU)) return; - if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) && tegra_chip_id == TEGRA20) + if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) && tegra_get_chip_id() == TEGRA20) tegra_hotplug_shutdown = tegra20_hotplug_shutdown; - if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) && tegra_chip_id == TEGRA30) + if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) && tegra_get_chip_id() == TEGRA30) tegra_hotplug_shutdown = tegra30_hotplug_shutdown; - if (IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC) && tegra_chip_id == TEGRA114) + if (IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC) && tegra_get_chip_id() == TEGRA114) tegra_hotplug_shutdown = tegra30_hotplug_shutdown; - if (IS_ENABLED(CONFIG_ARCH_TEGRA_124_SOC) && tegra_chip_id == TEGRA124) + if (IS_ENABLED(CONFIG_ARCH_TEGRA_124_SOC) && tegra_get_chip_id() == TEGRA124) tegra_hotplug_shutdown = tegra30_hotplug_shutdown; } diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c index c403edd0fcc7..0466a145b500 100644 --- a/arch/arm/mach-tegra/platsmp.c +++ b/arch/arm/mach-tegra/platsmp.c @@ -21,6 +21,8 @@ #include #include +#include + #include #include #include @@ -28,7 +30,6 @@ #include "common.h" #include "flowctrl.h" -#include "fuse.h" #include "iomap.h" #include "pmc.h" #include "reset.h" @@ -170,13 +171,13 @@ static int tegra114_boot_secondary(unsigned int cpu, struct task_struct *idle) static int tegra_boot_secondary(unsigned int cpu, struct task_struct *idle) { - if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) && tegra_chip_id == TEGRA20) + if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) && tegra_get_chip_id() == TEGRA20) return tegra20_boot_secondary(cpu, idle); - if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) && tegra_chip_id == TEGRA30) + if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) && tegra_get_chip_id() == TEGRA30) return tegra30_boot_secondary(cpu, idle); - if (IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC) && tegra_chip_id == TEGRA114) + if (IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC) && tegra_get_chip_id() == TEGRA114) return tegra114_boot_secondary(cpu, idle); - if (IS_ENABLED(CONFIG_ARCH_TEGRA_124_SOC) && tegra_chip_id == TEGRA124) + if (IS_ENABLED(CONFIG_ARCH_TEGRA_124_SOC) && tegra_get_chip_id() == TEGRA124) return tegra114_boot_secondary(cpu, idle); return -EINVAL; diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c index ae4826e43171..94db3b6664df 100644 --- a/arch/arm/mach-tegra/pm.c +++ b/arch/arm/mach-tegra/pm.c @@ -27,6 +27,8 @@ #include #include +#include + #include #include #include @@ -35,7 +37,6 @@ #include #include "flowctrl.h" -#include "fuse.h" #include "iomap.h" #include "pmc.h" #include "pm.h" @@ -53,7 +54,7 @@ static int (*tegra_sleep_func)(unsigned long v2p); static void tegra_tear_down_cpu_init(void) { - switch (tegra_chip_id) { + switch (tegra_get_chip_id()) { case TEGRA20: if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)) tegra_tear_down_cpu = tegra20_tear_down_cpu; @@ -143,7 +144,7 @@ bool tegra_set_cpu_in_lp2(void) if ((phy_cpu_id == 0) && cpumask_equal(cpu_lp2_mask, cpu_online_mask)) last_cpu = true; - else if (tegra_chip_id == TEGRA20 && phy_cpu_id == 1) + else if (tegra_get_chip_id() == TEGRA20 && phy_cpu_id == 1) tegra20_cpu_set_resettable_soon(); spin_unlock(&tegra_lp2_lock); @@ -212,7 +213,7 @@ static int tegra_sleep_core(unsigned long v2p) */ static bool tegra_lp1_iram_hook(void) { - switch (tegra_chip_id) { + switch (tegra_get_chip_id()) { case TEGRA20: if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)) tegra20_lp1_iram_hook(); @@ -242,7 +243,7 @@ static bool tegra_lp1_iram_hook(void) static bool tegra_sleep_core_init(void) { - switch (tegra_chip_id) { + switch (tegra_get_chip_id()) { case TEGRA20: if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)) tegra20_sleep_core_init(); diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c index a602f6c76b10..69df18090c8b 100644 --- a/arch/arm/mach-tegra/pmc.c +++ b/arch/arm/mach-tegra/pmc.c @@ -21,10 +21,10 @@ #include #include +#include #include #include "flowctrl.h" -#include "fuse.h" #include "pm.h" #include "pmc.h" #include "sleep.h" @@ -252,7 +252,7 @@ void tegra_pmc_pm_set(enum tegra_suspend_mode mode) reg |= TEGRA_POWER_CPU_PWRREQ_OE; reg &= ~TEGRA_POWER_EFFECT_LP0; - switch (tegra_chip_id) { + switch (tegra_get_chip_id()) { case TEGRA20: case TEGRA30: break; diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c index c12044de629b..0a14b8638437 100644 --- a/arch/arm/mach-tegra/powergate.c +++ b/arch/arm/mach-tegra/powergate.c @@ -30,9 +30,9 @@ #include #include +#include #include -#include "fuse.h" #include "iomap.h" #define DPD_SAMPLE 0x020 @@ -158,7 +158,7 @@ int tegra_powergate_remove_clamping(int id) * The Tegra124 GPU has a separate register (with different semantics) * to remove clamps. */ - if (tegra_chip_id == TEGRA124) { + if (tegra_get_chip_id() == TEGRA124) { if (id == TEGRA_POWERGATE_3D) { pmc_write(0, GPU_RG_CNTRL); return 0; @@ -228,7 +228,7 @@ int tegra_cpu_powergate_id(int cpuid) int __init tegra_powergate_init(void) { - switch (tegra_chip_id) { + switch (tegra_get_chip_id()) { case TEGRA20: tegra_num_powerdomains = 7; break; @@ -369,7 +369,7 @@ int __init tegra_powergate_debugfs_init(void) { struct dentry *d; - switch (tegra_chip_id) { + switch (tegra_get_chip_id()) { case TEGRA20: powergate_name = powergate_name_t20; break; diff --git a/arch/arm/mach-tegra/reset-handler.S b/arch/arm/mach-tegra/reset-handler.S index d916c84487ae..7b2baab0f0bd 100644 --- a/arch/arm/mach-tegra/reset-handler.S +++ b/arch/arm/mach-tegra/reset-handler.S @@ -17,11 +17,12 @@ #include #include +#include + #include #include #include "flowctrl.h" -#include "fuse.h" #include "iomap.h" #include "reset.h" #include "sleep.h" diff --git a/arch/arm/mach-tegra/reset.c b/arch/arm/mach-tegra/reset.c index b90507922a8c..f94fdf89d457 100644 --- a/arch/arm/mach-tegra/reset.c +++ b/arch/arm/mach-tegra/reset.c @@ -19,6 +19,8 @@ #include #include +#include + #include #include #include @@ -53,7 +55,7 @@ static void __init tegra_cpu_reset_handler_set(const u32 reset_address) * Prevent further modifications to the physical reset vector. * NOTE: Has no effect on chips prior to Tegra30. */ - if (tegra_chip_id != TEGRA20) { + if (tegra_get_chip_id() != TEGRA20) { reg = readl(sb_ctrl); reg |= 2; writel(reg, sb_ctrl); diff --git a/arch/arm/mach-tegra/sleep-tegra30.S b/arch/arm/mach-tegra/sleep-tegra30.S index e240b875183b..8ea699b8e3cb 100644 --- a/arch/arm/mach-tegra/sleep-tegra30.S +++ b/arch/arm/mach-tegra/sleep-tegra30.S @@ -16,12 +16,13 @@ #include +#include + #include #include #include #include "flowctrl.h" -#include "fuse.h" #include "irammap.h" #include "sleep.h" diff --git a/arch/arm/mach-tegra/tegra.c b/arch/arm/mach-tegra/tegra.c index 7a9f30289049..8be25c41249a 100644 --- a/arch/arm/mach-tegra/tegra.c +++ b/arch/arm/mach-tegra/tegra.c @@ -35,6 +35,8 @@ #include #include +#include + #include #include #include @@ -104,7 +106,7 @@ static void __init tegra_dt_init(void) soc_dev_attr->family = kasprintf(GFP_KERNEL, "Tegra"); soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%d", tegra_revision); - soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%d", tegra_chip_id); + soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%u", tegra_get_chip_id()); soc_dev = soc_device_register(soc_dev_attr); if (IS_ERR(soc_dev)) { diff --git a/include/soc/tegra/fuse.h b/include/soc/tegra/fuse.h index 85f555c89ad5..0e03f104fbf8 100644 --- a/include/soc/tegra/fuse.h +++ b/include/soc/tegra/fuse.h @@ -17,6 +17,16 @@ #ifndef __SOC_TEGRA_FUSE_H__ #define __SOC_TEGRA_FUSE_H__ +#define TEGRA20 0x20 +#define TEGRA30 0x30 +#define TEGRA114 0x35 +#define TEGRA124 0x40 + +#ifndef __ASSEMBLY__ + u32 tegra_read_chipid(void); +u8 tegra_get_chip_id(void); + +#endif /* __ASSEMBLY__ */ #endif /* __SOC_TEGRA_FUSE_H__ */ -- cgit v1.2.3 From 3f394f80645bf0c38a30042ba605c71663331035 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Thu, 12 Jun 2014 18:36:35 +0300 Subject: ARM: tegra: export apb dma readl/writel Export APB DMA readl and writel. These are needed because we can't access the fuses directly on Tegra20 without potentially causing a system hang. Also have the APB DMA readl and writel return an error in case of a read failure instead of just returning zero or ignore write failures. Signed-off-by: Peter De Schrijver Signed-off-by: Stephen Warren Signed-off-by: Thierry Reding --- arch/arm/mach-tegra/apbio.c | 51 +++++++++++++++++++++++++++------------------ include/soc/tegra/fuse.h | 14 +++++++++++++ 2 files changed, 45 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-tegra/apbio.c b/arch/arm/mach-tegra/apbio.c index 5f9647b3f81d..f2488722c79c 100644 --- a/arch/arm/mach-tegra/apbio.c +++ b/arch/arm/mach-tegra/apbio.c @@ -32,8 +32,8 @@ static u32 *tegra_apb_bb; static dma_addr_t tegra_apb_bb_phys; static DECLARE_COMPLETION(tegra_apb_wait); -static u32 tegra_apb_readl_direct(unsigned long offset); -static void tegra_apb_writel_direct(u32 value, unsigned long offset); +static int tegra_apb_readl_direct(unsigned long offset, u32 *value); +static int tegra_apb_writel_direct(u32 value, unsigned long offset); static struct dma_chan *tegra_apb_dma_chan; static struct dma_slave_config dma_sconfig; @@ -128,58 +128,64 @@ static int do_dma_transfer(unsigned long apb_add, return 0; } -static u32 tegra_apb_readl_using_dma(unsigned long offset) +int tegra_apb_readl_using_dma(unsigned long offset, u32 *value) { int ret; if (!tegra_apb_dma_chan && !tegra_apb_dma_init()) - return tegra_apb_readl_direct(offset); + return tegra_apb_readl_direct(offset, value); mutex_lock(&tegra_apb_dma_lock); ret = do_dma_transfer(offset, DMA_DEV_TO_MEM); - if (ret < 0) { + if (ret < 0) pr_err("error in reading offset 0x%08lx using dma\n", offset); - *(u32 *)tegra_apb_bb = 0; - } + else + *value = *tegra_apb_bb; + mutex_unlock(&tegra_apb_dma_lock); - return *((u32 *)tegra_apb_bb); + + return ret; } -static void tegra_apb_writel_using_dma(u32 value, unsigned long offset) +int tegra_apb_writel_using_dma(u32 value, unsigned long offset) { int ret; - if (!tegra_apb_dma_chan && !tegra_apb_dma_init()) { - tegra_apb_writel_direct(value, offset); - return; - } + if (!tegra_apb_dma_chan && !tegra_apb_dma_init()) + return tegra_apb_writel_direct(value, offset); mutex_lock(&tegra_apb_dma_lock); *((u32 *)tegra_apb_bb) = value; ret = do_dma_transfer(offset, DMA_MEM_TO_DEV); + mutex_unlock(&tegra_apb_dma_lock); if (ret < 0) pr_err("error in writing offset 0x%08lx using dma\n", offset); - mutex_unlock(&tegra_apb_dma_lock); + + return ret; } #else #define tegra_apb_readl_using_dma tegra_apb_readl_direct #define tegra_apb_writel_using_dma tegra_apb_writel_direct #endif -typedef u32 (*apbio_read_fptr)(unsigned long offset); -typedef void (*apbio_write_fptr)(u32 value, unsigned long offset); +typedef int (*apbio_read_fptr)(unsigned long offset, u32 *value); +typedef int (*apbio_write_fptr)(u32 value, unsigned long offset); static apbio_read_fptr apbio_read; static apbio_write_fptr apbio_write; -static u32 tegra_apb_readl_direct(unsigned long offset) +static int tegra_apb_readl_direct(unsigned long offset, u32 *value) { - return readl(IO_ADDRESS(offset)); + *value = readl(IO_ADDRESS(offset)); + + return 0; } -static void tegra_apb_writel_direct(u32 value, unsigned long offset) +static int tegra_apb_writel_direct(u32 value, unsigned long offset) { writel(value, IO_ADDRESS(offset)); + + return 0; } void tegra_apb_io_init(void) @@ -197,7 +203,12 @@ void tegra_apb_io_init(void) u32 tegra_apb_readl(unsigned long offset) { - return apbio_read(offset); + u32 val; + + if (apbio_read(offset, &val) < 0) + return 0; + else + return val; } void tegra_apb_writel(u32 value, unsigned long offset) diff --git a/include/soc/tegra/fuse.h b/include/soc/tegra/fuse.h index 0e03f104fbf8..a43a750dd0a3 100644 --- a/include/soc/tegra/fuse.h +++ b/include/soc/tegra/fuse.h @@ -27,6 +27,20 @@ u32 tegra_read_chipid(void); u8 tegra_get_chip_id(void); +#if defined(CONFIG_TEGRA20_APB_DMA) +int tegra_apb_readl_using_dma(unsigned long offset, u32 *value); +int tegra_apb_writel_using_dma(u32 value, unsigned long offset); +#else +static inline int tegra_apb_readl_using_dma(unsigned long offset, u32 *value) +{ + return -EINVAL; +} +static inline int tegra_apb_writel_using_dma(u32 value, unsigned long offset) +{ + return -EINVAL; +} +#endif /* CONFIG_TEGRA20_APB_DMA */ + #endif /* __ASSEMBLY__ */ #endif /* __SOC_TEGRA_FUSE_H__ */ -- cgit v1.2.3 From 0ba2aeb6dab80920edd9cf5b93b1ea4d6913b8f3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 16 Apr 2014 09:41:25 -0300 Subject: [media] v4l2-ctrls: increase internal min/max/step/def to 64 bit While VIDIOC_QUERYCTRL is limited to 32 bit min/max/step/def values for controls, the upcoming VIDIOC_QUERY_EXT_CTRL isn't. So increase the internal representation to 64 bits in preparation. Because of these changes the msi3101 driver has been modified slightly to fix a formatting issue (%d becomes %lld), vivi had to be modified as well to cope with the new 64-bit min/max values and the PIXEL_RATE control in a few sensor drivers required proper min/max/def values. Signed-off-by: Hans Verkuil Reviewed-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9v032.c | 2 +- drivers/media/i2c/smiapp/smiapp-core.c | 4 +- drivers/media/platform/vivi.c | 5 +- drivers/media/v4l2-core/v4l2-common.c | 6 +- drivers/media/v4l2-core/v4l2-ctrls.c | 93 +++++++++++++++--------- drivers/staging/media/msi3101/msi001.c | 2 +- drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c | 6 +- include/media/v4l2-ctrls.h | 42 +++++------ 8 files changed, 94 insertions(+), 66 deletions(-) (limited to 'include') diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 40172b8d8ea2..65e6e1a9d104 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -931,7 +931,7 @@ static int mt9v032_probe(struct i2c_client *client, mt9v032->pixel_rate = v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, - V4L2_CID_PIXEL_RATE, 0, 0, 1, 0); + V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1); if (pdata && pdata->link_freqs) { unsigned int def = 0; diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 446c82c58a62..72350f0befc5 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -533,7 +533,7 @@ static int smiapp_init_controls(struct smiapp_sensor *sensor) sensor->pixel_rate_parray = v4l2_ctrl_new_std( &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_PIXEL_RATE, 0, 0, 1, 0); + V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1); if (sensor->pixel_array->ctrl_handler.error) { dev_err(&client->dev, @@ -562,7 +562,7 @@ static int smiapp_init_controls(struct smiapp_sensor *sensor) sensor->pixel_rate_csi = v4l2_ctrl_new_std( &sensor->src->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_PIXEL_RATE, 0, 0, 1, 0); + V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1); if (sensor->src->ctrl_handler.error) { dev_err(&client->dev, diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index b7fbcdfc5862..cd535b38469d 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -1236,7 +1236,7 @@ static const struct v4l2_ctrl_config vivi_ctrl_int32 = { .id = VIVI_CID_CUSTOM_BASE + 2, .name = "Integer 32 Bits", .type = V4L2_CTRL_TYPE_INTEGER, - .min = 0x80000000, + .min = -0x80000000LL, .max = 0x7fffffff, .step = 1, }; @@ -1246,6 +1246,9 @@ static const struct v4l2_ctrl_config vivi_ctrl_int64 = { .id = VIVI_CID_CUSTOM_BASE + 3, .name = "Integer 64 Bits", .type = V4L2_CTRL_TYPE_INTEGER64, + .min = LLONG_MIN, + .max = LLONG_MAX, + .step = 1, }; static const char * const vivi_ctrl_menu_strings[] = { diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 433d6d77942e..ccaa38f65cf1 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -111,9 +111,13 @@ int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl, EXPORT_SYMBOL(v4l2_ctrl_check); /* Fill in a struct v4l2_queryctrl */ -int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def) +int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 _min, s32 _max, s32 _step, s32 _def) { const char *name; + s64 min = _min; + s64 max = _max; + u64 step = _step; + s64 def = _def; v4l2_ctrl_fill(qctrl->id, &name, &qctrl->type, &min, &max, &step, &def, &qctrl->flags); diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 55c683254102..e132fa21ccd6 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -877,7 +877,7 @@ const char *v4l2_ctrl_get_name(u32 id) EXPORT_SYMBOL(v4l2_ctrl_get_name); void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, - s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags) + s64 *min, s64 *max, u64 *step, s64 *def, u32 *flags) { *name = v4l2_ctrl_get_name(id); *flags = 0; @@ -1041,14 +1041,23 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, *type = V4L2_CTRL_TYPE_INTEGER; *flags |= V4L2_CTRL_FLAG_READ_ONLY; break; - case V4L2_CID_MPEG_VIDEO_DEC_FRAME: case V4L2_CID_MPEG_VIDEO_DEC_PTS: - *flags |= V4L2_CTRL_FLAG_VOLATILE; - /* Fall through */ + *type = V4L2_CTRL_TYPE_INTEGER64; + *flags |= V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY; + *min = *def = 0; + *max = 0x1ffffffffLL; + *step = 1; + break; + case V4L2_CID_MPEG_VIDEO_DEC_FRAME: + *type = V4L2_CTRL_TYPE_INTEGER64; + *flags |= V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY; + *min = *def = 0; + *max = 0x7fffffffffffffffLL; + *step = 1; + break; case V4L2_CID_PIXEL_RATE: *type = V4L2_CTRL_TYPE_INTEGER64; *flags |= V4L2_CTRL_FLAG_READ_ONLY; - *min = *max = *step = *def = 0; break; default: *type = V4L2_CTRL_TYPE_INTEGER; @@ -1352,7 +1361,7 @@ static int cluster_changed(struct v4l2_ctrl *master) /* Control range checking */ static int check_range(enum v4l2_ctrl_type type, - s32 min, s32 max, u32 step, s32 def) + s64 min, s64 max, u64 step, s64 def) { switch (type) { case V4L2_CTRL_TYPE_BOOLEAN: @@ -1360,7 +1369,8 @@ static int check_range(enum v4l2_ctrl_type type, return -ERANGE; /* fall through */ case V4L2_CTRL_TYPE_INTEGER: - if (step <= 0 || min > max || def < min || def > max) + case V4L2_CTRL_TYPE_INTEGER64: + if (step == 0 || min > max || def < min || def > max) return -ERANGE; return 0; case V4L2_CTRL_TYPE_BITMASK: @@ -1385,23 +1395,30 @@ static int check_range(enum v4l2_ctrl_type type, } } +/* Round towards the closest legal value */ +#define ROUND_TO_RANGE(val, offset_type, ctrl) \ +({ \ + offset_type offset; \ + val += (ctrl)->step / 2; \ + val = clamp_t(typeof(val), val, \ + (ctrl)->minimum, (ctrl)->maximum); \ + offset = (val) - (ctrl)->minimum; \ + offset = (ctrl)->step * (offset / (ctrl)->step); \ + val = (ctrl)->minimum + offset; \ + 0; \ +}) + /* Validate a new control */ static int validate_new(const struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c) { size_t len; - u32 offset; - s32 val; switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER: - /* Round towards the closest legal value */ - val = c->value + ctrl->step / 2; - val = clamp(val, ctrl->minimum, ctrl->maximum); - offset = val - ctrl->minimum; - offset = ctrl->step * (offset / ctrl->step); - c->value = ctrl->minimum + offset; - return 0; + return ROUND_TO_RANGE(*(s32 *)&c->value, u32, ctrl); + case V4L2_CTRL_TYPE_INTEGER64: + return ROUND_TO_RANGE(*(s64 *)&c->value64, u64, ctrl); case V4L2_CTRL_TYPE_BOOLEAN: c->value = !!c->value; @@ -1427,9 +1444,6 @@ static int validate_new(const struct v4l2_ctrl *ctrl, c->value = 0; return 0; - case V4L2_CTRL_TYPE_INTEGER64: - return 0; - case V4L2_CTRL_TYPE_STRING: len = strlen(c->string); if (len < ctrl->minimum) @@ -1653,7 +1667,7 @@ unlock: static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, const char *name, enum v4l2_ctrl_type type, - s32 min, s32 max, u32 step, s32 def, + s64 min, s64 max, u64 step, s64 def, u32 flags, const char * const *qmenu, const s64 *qmenu_int, void *priv) { @@ -1738,10 +1752,10 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, const s64 *qmenu_int = cfg->qmenu_int; enum v4l2_ctrl_type type = cfg->type; u32 flags = cfg->flags; - s32 min = cfg->min; - s32 max = cfg->max; - u32 step = cfg->step; - s32 def = cfg->def; + s64 min = cfg->min; + s64 max = cfg->max; + u64 step = cfg->step; + s64 def = cfg->def; if (name == NULL) v4l2_ctrl_fill(cfg->id, &name, &type, &min, &max, &step, @@ -1774,7 +1788,7 @@ EXPORT_SYMBOL(v4l2_ctrl_new_custom); /* Helper function for standard non-menu controls */ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, - u32 id, s32 min, s32 max, u32 step, s32 def) + u32 id, s64 min, s64 max, u64 step, s64 def) { const char *name; enum v4l2_ctrl_type type; @@ -1794,15 +1808,17 @@ EXPORT_SYMBOL(v4l2_ctrl_new_std); /* Helper function for standard menu controls */ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, - u32 id, s32 max, s32 mask, s32 def) + u32 id, u8 _max, u64 mask, u8 _def) { const char * const *qmenu = NULL; const s64 *qmenu_int = NULL; unsigned int qmenu_int_len = 0; const char *name; enum v4l2_ctrl_type type; - s32 min; - s32 step; + s64 min; + s64 max = _max; + s64 def = _def; + u64 step; u32 flags; v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags); @@ -1823,14 +1839,16 @@ EXPORT_SYMBOL(v4l2_ctrl_new_std_menu); /* Helper function for standard menu controls with driver defined menu */ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl, - const struct v4l2_ctrl_ops *ops, u32 id, s32 max, - s32 mask, s32 def, const char * const *qmenu) + const struct v4l2_ctrl_ops *ops, u32 id, u8 _max, + u64 mask, u8 _def, const char * const *qmenu) { enum v4l2_ctrl_type type; const char *name; u32 flags; - s32 step; - s32 min; + u64 step; + s64 min; + s64 max = _max; + s64 def = _def; /* v4l2_ctrl_new_std_menu_items() should only be called for * standard controls without a standard menu. @@ -1854,12 +1872,14 @@ EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items); /* Helper function for standard integer menu controls */ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, - u32 id, s32 max, s32 def, const s64 *qmenu_int) + u32 id, u8 _max, u8 _def, const s64 *qmenu_int) { const char *name; enum v4l2_ctrl_type type; - s32 min; - s32 step; + s64 min; + u64 step; + s64 max = _max; + s64 def = _def; u32 flags; v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags); @@ -2887,13 +2907,14 @@ void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void EXPORT_SYMBOL(v4l2_ctrl_notify); int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, - s32 min, s32 max, u32 step, s32 def) + s64 min, s64 max, u64 step, s64 def) { int ret = check_range(ctrl->type, min, max, step, def); struct v4l2_ext_control c; switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER: + case V4L2_CTRL_TYPE_INTEGER64: case V4L2_CTRL_TYPE_BOOLEAN: case V4L2_CTRL_TYPE_MENU: case V4L2_CTRL_TYPE_INTEGER_MENU: diff --git a/drivers/staging/media/msi3101/msi001.c b/drivers/staging/media/msi3101/msi001.c index bd0b93cb6c53..ee99e372c943 100644 --- a/drivers/staging/media/msi3101/msi001.c +++ b/drivers/staging/media/msi3101/msi001.c @@ -381,7 +381,7 @@ static int msi001_s_ctrl(struct v4l2_ctrl *ctrl) int ret; dev_dbg(&s->spi->dev, - "%s: id=%d name=%s val=%d min=%d max=%d step=%d\n", + "%s: id=%d name=%s val=%d min=%lld max=%lld step=%lld\n", __func__, ctrl->id, ctrl->name, ctrl->val, ctrl->minimum, ctrl->maximum, ctrl->step); diff --git a/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c index cee127362b0b..f81b1c84dae1 100644 --- a/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c +++ b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c @@ -1337,7 +1337,7 @@ static int rtl2832_sdr_s_ctrl(struct v4l2_ctrl *ctrl) int ret; dev_dbg(&s->udev->dev, - "%s: id=%d name=%s val=%d min=%d max=%d step=%d\n", + "%s: id=%d name=%s val=%d min=%lld max=%lld step=%lld\n", __func__, ctrl->id, ctrl->name, ctrl->val, ctrl->minimum, ctrl->maximum, ctrl->step); @@ -1350,8 +1350,8 @@ static int rtl2832_sdr_s_ctrl(struct v4l2_ctrl *ctrl) s32 val = s->f_adc + s->bandwidth->step / 2; u32 offset; - val = clamp(val, s->bandwidth->minimum, - s->bandwidth->maximum); + val = clamp_t(s32, val, s->bandwidth->minimum, + s->bandwidth->maximum); offset = val - s->bandwidth->minimum; offset = s->bandwidth->step * (offset / s->bandwidth->step); diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 16f7f2606516..a2e8f038a8ed 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -131,10 +131,10 @@ struct v4l2_ctrl { u32 id; const char *name; enum v4l2_ctrl_type type; - s32 minimum, maximum, default_value; + s64 minimum, maximum, default_value; union { - u32 step; - u32 menu_skip_mask; + u64 step; + u64 menu_skip_mask; }; union { const char * const *qmenu; @@ -216,9 +216,9 @@ struct v4l2_ctrl_handler { * @menu_skip_mask: The control's skip mask for menu controls. This makes it * easy to skip menu items that are not valid. If bit X is set, * then menu item X is skipped. Of course, this only works for - * menus with <= 32 menu items. There are no menus that come + * menus with <= 64 menu items. There are no menus that come * close to that number, so this is OK. Should we ever need more, - * then this will have to be extended to a u64 or a bit array. + * then this will have to be extended to a bit array. * @qmenu: A const char * array for all menu items. Array entries that are * empty strings ("") correspond to non-existing menu items (this * is in addition to the menu_skip_mask above). The last entry @@ -231,12 +231,12 @@ struct v4l2_ctrl_config { u32 id; const char *name; enum v4l2_ctrl_type type; - s32 min; - s32 max; - u32 step; - s32 def; + s64 min; + s64 max; + u64 step; + s64 def; u32 flags; - u32 menu_skip_mask; + u64 menu_skip_mask; const char * const *qmenu; const s64 *qmenu_int; unsigned int is_private:1; @@ -257,7 +257,7 @@ struct v4l2_ctrl_config { * control framework this function will no longer be exported. */ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, - s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags); + s64 *min, s64 *max, u64 *step, s64 *def, u32 *flags); /** v4l2_ctrl_handler_init_class() - Initialize the control handler. @@ -362,7 +362,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, */ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, - u32 id, s32 min, s32 max, u32 step, s32 def); + u32 id, s64 min, s64 max, u64 step, s64 def); /** v4l2_ctrl_new_std_menu() - Allocate and initialize a new standard V4L2 menu control. * @hdl: The control handler. @@ -372,9 +372,9 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, * @mask: The control's skip mask for menu controls. This makes it * easy to skip menu items that are not valid. If bit X is set, * then menu item X is skipped. Of course, this only works for - * menus with <= 32 menu items. There are no menus that come + * menus with <= 64 menu items. There are no menus that come * close to that number, so this is OK. Should we ever need more, - * then this will have to be extended to a u64 or a bit array. + * then this will have to be extended to a bit array. * @def: The control's default value. * * Same as v4l2_ctrl_new_std(), but @min is set to 0 and the @mask value @@ -384,7 +384,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, */ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, - u32 id, s32 max, s32 mask, s32 def); + u32 id, u8 max, u64 mask, u8 def); /** v4l2_ctrl_new_std_menu_items() - Create a new standard V4L2 menu control * with driver specific menu. @@ -395,9 +395,9 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, * @mask: The control's skip mask for menu controls. This makes it * easy to skip menu items that are not valid. If bit X is set, * then menu item X is skipped. Of course, this only works for - * menus with <= 32 menu items. There are no menus that come + * menus with <= 64 menu items. There are no menus that come * close to that number, so this is OK. Should we ever need more, - * then this will have to be extended to a u64 or a bit array. + * then this will have to be extended to a bit array. * @def: The control's default value. * @qmenu: The new menu. * @@ -406,8 +406,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, * */ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl, - const struct v4l2_ctrl_ops *ops, u32 id, s32 max, - s32 mask, s32 def, const char * const *qmenu); + const struct v4l2_ctrl_ops *ops, u32 id, u8 max, + u64 mask, u8 def, const char * const *qmenu); /** v4l2_ctrl_new_int_menu() - Create a new standard V4L2 integer menu control. * @hdl: The control handler. @@ -424,7 +424,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl, */ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, - u32 id, s32 max, s32 def, const s64 *qmenu_int); + u32 id, u8 max, u8 def, const s64 *qmenu_int); /** v4l2_ctrl_add_ctrl() - Add a control from another handler to this handler. * @hdl: The control handler. @@ -560,7 +560,7 @@ void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed); * take the lock itself. */ int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, - s32 min, s32 max, u32 step, s32 def); + s64 min, s64 max, u64 step, s64 def); /** v4l2_ctrl_lock() - Helper function to lock the handler * associated with the control. -- cgit v1.2.3 From 35874f3617b38e0c1f72163407c41d554a8f5939 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Thu, 12 Jun 2014 18:36:36 +0300 Subject: ARM: tegra: move fuse exports to soc/tegra/fuse.h All fuse related functionality will move to a driver in the following patches. To prepare for this, export all the required functionality in a global header file and move all users of fuse.h to soc/tegra/fuse.h. While we're at it, remove tegra_bct_strapping, as its only user was removed in Commit a7cbe92cef27 ("ARM: tegra: remove tegra EMC scaling driver"). Signed-off-by: Peter De Schrijver Signed-off-by: Stephen Warren Signed-off-by: Thierry Reding --- arch/arm/mach-tegra/fuse.h | 13 ------------- arch/arm/mach-tegra/tegra.c | 1 - arch/arm/mach-tegra/tegra114_speedo.c | 2 ++ arch/arm/mach-tegra/tegra20_speedo.c | 2 ++ arch/arm/mach-tegra/tegra30_speedo.c | 2 ++ include/soc/tegra/fuse.h | 16 ++++++++++++++++ 6 files changed, 22 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-tegra/fuse.h b/arch/arm/mach-tegra/fuse.h index 7a08b4b70c8d..48a48861c19b 100644 --- a/arch/arm/mach-tegra/fuse.h +++ b/arch/arm/mach-tegra/fuse.h @@ -27,27 +27,14 @@ #define SKU_ID_T25E 28 #ifndef __ASSEMBLY__ -enum tegra_revision { - TEGRA_REVISION_UNKNOWN = 0, - TEGRA_REVISION_A01, - TEGRA_REVISION_A02, - TEGRA_REVISION_A03, - TEGRA_REVISION_A03p, - TEGRA_REVISION_A04, - TEGRA_REVISION_MAX, -}; extern int tegra_sku_id; extern int tegra_cpu_process_id; extern int tegra_core_process_id; extern int tegra_cpu_speedo_id; /* only exist in Tegra30 and later */ extern int tegra_soc_speedo_id; -extern enum tegra_revision tegra_revision; - -extern int tegra_bct_strapping; unsigned long long tegra_chip_uid(void); -void tegra_init_fuse(void); bool tegra_spare_fuse(int bit); u32 tegra_fuse_readl(unsigned long offset); diff --git a/arch/arm/mach-tegra/tegra.c b/arch/arm/mach-tegra/tegra.c index 8be25c41249a..a359931c5952 100644 --- a/arch/arm/mach-tegra/tegra.c +++ b/arch/arm/mach-tegra/tegra.c @@ -48,7 +48,6 @@ #include "board.h" #include "common.h" #include "cpuidle.h" -#include "fuse.h" #include "iomap.h" #include "irq.h" #include "pmc.h" diff --git a/arch/arm/mach-tegra/tegra114_speedo.c b/arch/arm/mach-tegra/tegra114_speedo.c index 86eca17e5286..d0a6d5925f5f 100644 --- a/arch/arm/mach-tegra/tegra114_speedo.c +++ b/arch/arm/mach-tegra/tegra114_speedo.c @@ -17,6 +17,8 @@ #include #include +#include + #include "fuse.h" #define CORE_PROCESS_CORNERS_NUM 2 diff --git a/arch/arm/mach-tegra/tegra20_speedo.c b/arch/arm/mach-tegra/tegra20_speedo.c index dcd29a0b0187..2907cf8de2b1 100644 --- a/arch/arm/mach-tegra/tegra20_speedo.c +++ b/arch/arm/mach-tegra/tegra20_speedo.c @@ -17,6 +17,8 @@ #include #include +#include + #include "fuse.h" #define CPU_SPEEDO_LSBIT 20 diff --git a/arch/arm/mach-tegra/tegra30_speedo.c b/arch/arm/mach-tegra/tegra30_speedo.c index 7c0038326cf5..6f6102cd1e6b 100644 --- a/arch/arm/mach-tegra/tegra30_speedo.c +++ b/arch/arm/mach-tegra/tegra30_speedo.c @@ -17,6 +17,8 @@ #include #include +#include + #include "fuse.h" #define CORE_PROCESS_CORNERS_NUM 1 diff --git a/include/soc/tegra/fuse.h b/include/soc/tegra/fuse.h index a43a750dd0a3..822eb348e107 100644 --- a/include/soc/tegra/fuse.h +++ b/include/soc/tegra/fuse.h @@ -27,6 +27,22 @@ u32 tegra_read_chipid(void); u8 tegra_get_chip_id(void); +enum tegra_revision { + TEGRA_REVISION_UNKNOWN = 0, + TEGRA_REVISION_A01, + TEGRA_REVISION_A02, + TEGRA_REVISION_A03, + TEGRA_REVISION_A03p, + TEGRA_REVISION_A04, + TEGRA_REVISION_MAX, +}; + +u32 tegra_read_straps(void); +u32 tegra_read_chipid(void); +void tegra_init_fuse(void); + +extern enum tegra_revision tegra_revision; + #if defined(CONFIG_TEGRA20_APB_DMA) int tegra_apb_readl_using_dma(unsigned long offset, u32 *value); int tegra_apb_writel_using_dma(u32 value, unsigned long offset); -- cgit v1.2.3 From 783c8f4c84451bc444e314a71b447239c6ef6fd9 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Thu, 12 Jun 2014 18:36:37 +0300 Subject: soc/tegra: Add efuse driver for Tegra Implement fuse driver for Tegra20, Tegra30, Tegra114 and Tegra124. This replaces functionality previously provided in arch/arm/mach-tegra, which is removed in this patch. While at it, move the only user of the global tegra_revision variable over to tegra_sku_info.revision and export tegra_fuse_readl() to allow drivers to read calibration fuses. Signed-off-by: Peter De Schrijver Signed-off-by: Stephen Warren Signed-off-by: Thierry Reding --- Documentation/ABI/testing/sysfs-driver-tegra-fuse | 11 + arch/arm/mach-tegra/Makefile | 4 - arch/arm/mach-tegra/fuse.c | 260 ------------------- arch/arm/mach-tegra/fuse.h | 60 ----- arch/arm/mach-tegra/reset.c | 1 - arch/arm/mach-tegra/tegra.c | 3 +- arch/arm/mach-tegra/tegra114_speedo.c | 106 -------- arch/arm/mach-tegra/tegra20_speedo.c | 111 -------- arch/arm/mach-tegra/tegra30_speedo.c | 294 ---------------------- drivers/misc/fuse/Makefile | 1 + drivers/soc/Makefile | 1 + drivers/soc/tegra/Makefile | 1 + drivers/soc/tegra/fuse/Makefile | 8 + drivers/soc/tegra/fuse/fuse-tegra.c | 156 ++++++++++++ drivers/soc/tegra/fuse/fuse-tegra20.c | 142 +++++++++++ drivers/soc/tegra/fuse/fuse-tegra30.c | 224 +++++++++++++++++ drivers/soc/tegra/fuse/fuse.h | 71 ++++++ drivers/soc/tegra/fuse/speedo-tegra114.c | 110 ++++++++ drivers/soc/tegra/fuse/speedo-tegra124.c | 168 +++++++++++++ drivers/soc/tegra/fuse/speedo-tegra20.c | 110 ++++++++ drivers/soc/tegra/fuse/speedo-tegra30.c | 288 +++++++++++++++++++++ drivers/soc/tegra/fuse/tegra-apbmisc.c | 112 +++++++++ include/soc/tegra/fuse.h | 20 +- 23 files changed, 1424 insertions(+), 838 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-driver-tegra-fuse delete mode 100644 arch/arm/mach-tegra/fuse.c delete mode 100644 arch/arm/mach-tegra/fuse.h delete mode 100644 arch/arm/mach-tegra/tegra114_speedo.c delete mode 100644 arch/arm/mach-tegra/tegra20_speedo.c delete mode 100644 arch/arm/mach-tegra/tegra30_speedo.c create mode 100644 drivers/misc/fuse/Makefile create mode 100644 drivers/soc/tegra/Makefile create mode 100644 drivers/soc/tegra/fuse/Makefile create mode 100644 drivers/soc/tegra/fuse/fuse-tegra.c create mode 100644 drivers/soc/tegra/fuse/fuse-tegra20.c create mode 100644 drivers/soc/tegra/fuse/fuse-tegra30.c create mode 100644 drivers/soc/tegra/fuse/fuse.h create mode 100644 drivers/soc/tegra/fuse/speedo-tegra114.c create mode 100644 drivers/soc/tegra/fuse/speedo-tegra124.c create mode 100644 drivers/soc/tegra/fuse/speedo-tegra20.c create mode 100644 drivers/soc/tegra/fuse/speedo-tegra30.c create mode 100644 drivers/soc/tegra/fuse/tegra-apbmisc.c (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-driver-tegra-fuse b/Documentation/ABI/testing/sysfs-driver-tegra-fuse new file mode 100644 index 000000000000..69f5af632657 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-tegra-fuse @@ -0,0 +1,11 @@ +What: /sys/devices/*//fuse +Date: February 2014 +Contact: Peter De Schrijver +Description: read-only access to the efuses on Tegra20, Tegra30, Tegra114 + and Tegra124 SoC's from NVIDIA. The efuses contain write once + data programmed at the factory. The data is layed out in 32bit + words in LSB first format. Each bit represents a single value + as decoded from the fuse registers. Bits order/assignment + exactly matches the HW registers, including any unused bits. +Users: any user space application which wants to read the efuses on + Tegra SoC's diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index 6fbfbb77dcd9..e8601bb56f98 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -2,7 +2,6 @@ asflags-y += -march=armv7-a obj-y += io.o obj-y += irq.o -obj-y += fuse.o obj-y += pmc.o obj-y += flowctrl.o obj-y += powergate.o @@ -13,13 +12,11 @@ obj-y += reset-handler.o obj-y += sleep.o obj-y += tegra.o obj-$(CONFIG_CPU_IDLE) += cpuidle.o -obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20_speedo.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += sleep-tegra20.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += pm-tegra20.o ifeq ($(CONFIG_CPU_IDLE),y) obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += cpuidle-tegra20.o endif -obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30_speedo.o obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += sleep-tegra30.o obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += pm-tegra30.o ifeq ($(CONFIG_CPU_IDLE),y) @@ -28,7 +25,6 @@ endif obj-$(CONFIG_SMP) += platsmp.o headsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o -obj-$(CONFIG_ARCH_TEGRA_114_SOC) += tegra114_speedo.o obj-$(CONFIG_ARCH_TEGRA_114_SOC) += sleep-tegra30.o obj-$(CONFIG_ARCH_TEGRA_114_SOC) += pm-tegra30.o ifeq ($(CONFIG_CPU_IDLE),y) diff --git a/arch/arm/mach-tegra/fuse.c b/arch/arm/mach-tegra/fuse.c deleted file mode 100644 index b22e76a40965..000000000000 --- a/arch/arm/mach-tegra/fuse.c +++ /dev/null @@ -1,260 +0,0 @@ -/* - * arch/arm/mach-tegra/fuse.c - * - * Copyright (C) 2010 Google, Inc. - * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. - * - * Author: - * Colin Cross - * - * 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. - * - */ - -#include -#include -#include -#include -#include - -#include - -#include "apbio.h" -#include "fuse.h" -#include "iomap.h" - -/* Tegra20 only */ -#define FUSE_UID_LOW 0x108 -#define FUSE_UID_HIGH 0x10c - -/* Tegra30 and later */ -#define FUSE_VENDOR_CODE 0x200 -#define FUSE_FAB_CODE 0x204 -#define FUSE_LOT_CODE_0 0x208 -#define FUSE_LOT_CODE_1 0x20c -#define FUSE_WAFER_ID 0x210 -#define FUSE_X_COORDINATE 0x214 -#define FUSE_Y_COORDINATE 0x218 - -#define FUSE_SKU_INFO 0x110 - -#define TEGRA20_FUSE_SPARE_BIT 0x200 -#define TEGRA30_FUSE_SPARE_BIT 0x244 - -int tegra_sku_id; -int tegra_cpu_process_id; -int tegra_core_process_id; -int tegra_cpu_speedo_id; /* only exist in Tegra30 and later */ -int tegra_soc_speedo_id; -enum tegra_revision tegra_revision; - -static struct clk *fuse_clk; -static int tegra_fuse_spare_bit; -static void (*tegra_init_speedo_data)(void); - -/* The BCT to use at boot is specified by board straps that can be read - * through a APB misc register and decoded. 2 bits, i.e. 4 possible BCTs. - */ -int tegra_bct_strapping; - -#define STRAP_OPT 0x008 -#define GMI_AD0 (1 << 4) -#define GMI_AD1 (1 << 5) -#define RAM_ID_MASK (GMI_AD0 | GMI_AD1) -#define RAM_CODE_SHIFT 4 - -static const char *tegra_revision_name[TEGRA_REVISION_MAX] = { - [TEGRA_REVISION_UNKNOWN] = "unknown", - [TEGRA_REVISION_A01] = "A01", - [TEGRA_REVISION_A02] = "A02", - [TEGRA_REVISION_A03] = "A03", - [TEGRA_REVISION_A03p] = "A03 prime", - [TEGRA_REVISION_A04] = "A04", -}; - -static void tegra_fuse_enable_clk(void) -{ - if (IS_ERR(fuse_clk)) - fuse_clk = clk_get_sys(NULL, "fuse"); - if (IS_ERR(fuse_clk)) - return; - clk_prepare_enable(fuse_clk); -} - -static void tegra_fuse_disable_clk(void) -{ - if (IS_ERR(fuse_clk)) - return; - clk_disable_unprepare(fuse_clk); -} - -u32 tegra_fuse_readl(unsigned long offset) -{ - return tegra_apb_readl(TEGRA_FUSE_BASE + offset); -} - -bool tegra_spare_fuse(int bit) -{ - bool ret; - - tegra_fuse_enable_clk(); - - ret = tegra_fuse_readl(tegra_fuse_spare_bit + bit * 4); - - tegra_fuse_disable_clk(); - - return ret; -} - -static enum tegra_revision tegra_get_revision(u32 id) -{ - u32 minor_rev = (id >> 16) & 0xf; - - switch (minor_rev) { - case 1: - return TEGRA_REVISION_A01; - case 2: - return TEGRA_REVISION_A02; - case 3: - if (tegra_get_chip_id() == TEGRA20 && - (tegra_spare_fuse(18) || tegra_spare_fuse(19))) - return TEGRA_REVISION_A03p; - else - return TEGRA_REVISION_A03; - case 4: - return TEGRA_REVISION_A04; - default: - return TEGRA_REVISION_UNKNOWN; - } -} - -static void tegra_get_process_id(void) -{ - u32 reg; - - tegra_fuse_enable_clk(); - - reg = tegra_fuse_readl(tegra_fuse_spare_bit); - tegra_cpu_process_id = (reg >> 6) & 3; - reg = tegra_fuse_readl(tegra_fuse_spare_bit); - tegra_core_process_id = (reg >> 12) & 3; - - tegra_fuse_disable_clk(); -} - -u32 tegra_read_chipid(void) -{ - return readl_relaxed(IO_ADDRESS(TEGRA_APB_MISC_BASE) + 0x804); -} - -u8 tegra_get_chip_id(void) -{ - u32 id = tegra_read_chipid(); - - return (id >> 8) & 0xff; -} - -static void __init tegra20_fuse_init_randomness(void) -{ - u32 randomness[2]; - - randomness[0] = tegra_fuse_readl(FUSE_UID_LOW); - randomness[1] = tegra_fuse_readl(FUSE_UID_HIGH); - - add_device_randomness(randomness, sizeof(randomness)); -} - -/* Applies to Tegra30 or later */ -static void __init tegra30_fuse_init_randomness(void) -{ - u32 randomness[7]; - - randomness[0] = tegra_fuse_readl(FUSE_VENDOR_CODE); - randomness[1] = tegra_fuse_readl(FUSE_FAB_CODE); - randomness[2] = tegra_fuse_readl(FUSE_LOT_CODE_0); - randomness[3] = tegra_fuse_readl(FUSE_LOT_CODE_1); - randomness[4] = tegra_fuse_readl(FUSE_WAFER_ID); - randomness[5] = tegra_fuse_readl(FUSE_X_COORDINATE); - randomness[6] = tegra_fuse_readl(FUSE_Y_COORDINATE); - - add_device_randomness(randomness, sizeof(randomness)); -} - -void __init tegra_init_fuse(void) -{ - u32 id; - u32 randomness[5]; - u8 chip_id; - - u32 reg = readl(IO_ADDRESS(TEGRA_CLK_RESET_BASE + 0x48)); - reg |= 1 << 28; - writel(reg, IO_ADDRESS(TEGRA_CLK_RESET_BASE + 0x48)); - - /* - * Enable FUSE clock. This needs to be hardcoded because the clock - * subsystem is not active during early boot. - */ - reg = readl(IO_ADDRESS(TEGRA_CLK_RESET_BASE + 0x14)); - reg |= 1 << 7; - writel(reg, IO_ADDRESS(TEGRA_CLK_RESET_BASE + 0x14)); - fuse_clk = ERR_PTR(-EINVAL); - - reg = tegra_fuse_readl(FUSE_SKU_INFO); - randomness[0] = reg; - tegra_sku_id = reg & 0xFF; - - reg = tegra_apb_readl(TEGRA_APB_MISC_BASE + STRAP_OPT); - randomness[1] = reg; - tegra_bct_strapping = (reg & RAM_ID_MASK) >> RAM_CODE_SHIFT; - - id = tegra_read_chipid(); - randomness[2] = id; - chip_id = (id >> 8) & 0xff; - - switch (chip_id) { - case TEGRA20: - tegra_fuse_spare_bit = TEGRA20_FUSE_SPARE_BIT; - tegra_init_speedo_data = &tegra20_init_speedo_data; - break; - case TEGRA30: - tegra_fuse_spare_bit = TEGRA30_FUSE_SPARE_BIT; - tegra_init_speedo_data = &tegra30_init_speedo_data; - break; - case TEGRA114: - tegra_init_speedo_data = &tegra114_init_speedo_data; - break; - default: - pr_warn("Tegra: unknown chip id %d\n", chip_id); - tegra_fuse_spare_bit = TEGRA20_FUSE_SPARE_BIT; - tegra_init_speedo_data = &tegra_get_process_id; - } - - tegra_revision = tegra_get_revision(id); - tegra_init_speedo_data(); - randomness[3] = (tegra_cpu_process_id << 16) | tegra_core_process_id; - randomness[4] = (tegra_cpu_speedo_id << 16) | tegra_soc_speedo_id; - - add_device_randomness(randomness, sizeof(randomness)); - switch (chip_id) { - case TEGRA20: - tegra20_fuse_init_randomness(); - break; - case TEGRA30: - case TEGRA114: - default: - tegra30_fuse_init_randomness(); - break; - } - - pr_info("Tegra Revision: %s SKU: %d CPU Process: %d Core Process: %d\n", - tegra_revision_name[tegra_revision], - tegra_sku_id, tegra_cpu_process_id, - tegra_core_process_id); -} diff --git a/arch/arm/mach-tegra/fuse.h b/arch/arm/mach-tegra/fuse.h deleted file mode 100644 index 48a48861c19b..000000000000 --- a/arch/arm/mach-tegra/fuse.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2010 Google, Inc. - * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. - * - * Author: - * Colin Cross - * - * 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 __MACH_TEGRA_FUSE_H -#define __MACH_TEGRA_FUSE_H - -#define SKU_ID_T20 8 -#define SKU_ID_T25SE 20 -#define SKU_ID_AP25 23 -#define SKU_ID_T25 24 -#define SKU_ID_AP25E 27 -#define SKU_ID_T25E 28 - -#ifndef __ASSEMBLY__ - -extern int tegra_sku_id; -extern int tegra_cpu_process_id; -extern int tegra_core_process_id; -extern int tegra_cpu_speedo_id; /* only exist in Tegra30 and later */ -extern int tegra_soc_speedo_id; - -unsigned long long tegra_chip_uid(void); -bool tegra_spare_fuse(int bit); -u32 tegra_fuse_readl(unsigned long offset); - -#ifdef CONFIG_ARCH_TEGRA_2x_SOC -void tegra20_init_speedo_data(void); -#else -static inline void tegra20_init_speedo_data(void) {} -#endif - -#ifdef CONFIG_ARCH_TEGRA_3x_SOC -void tegra30_init_speedo_data(void); -#else -static inline void tegra30_init_speedo_data(void) {} -#endif - -#ifdef CONFIG_ARCH_TEGRA_114_SOC -void tegra114_init_speedo_data(void); -#else -static inline void tegra114_init_speedo_data(void) {} -#endif -#endif /* __ASSEMBLY__ */ - -#endif diff --git a/arch/arm/mach-tegra/reset.c b/arch/arm/mach-tegra/reset.c index f94fdf89d457..5377495d41b8 100644 --- a/arch/arm/mach-tegra/reset.c +++ b/arch/arm/mach-tegra/reset.c @@ -25,7 +25,6 @@ #include #include -#include "fuse.h" #include "iomap.h" #include "irammap.h" #include "reset.h" diff --git a/arch/arm/mach-tegra/tegra.c b/arch/arm/mach-tegra/tegra.c index a359931c5952..c0faae8c0822 100644 --- a/arch/arm/mach-tegra/tegra.c +++ b/arch/arm/mach-tegra/tegra.c @@ -104,7 +104,8 @@ static void __init tegra_dt_init(void) goto out; soc_dev_attr->family = kasprintf(GFP_KERNEL, "Tegra"); - soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%d", tegra_revision); + soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%d", + tegra_sku_info.revision); soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%u", tegra_get_chip_id()); soc_dev = soc_device_register(soc_dev_attr); diff --git a/arch/arm/mach-tegra/tegra114_speedo.c b/arch/arm/mach-tegra/tegra114_speedo.c deleted file mode 100644 index d0a6d5925f5f..000000000000 --- a/arch/arm/mach-tegra/tegra114_speedo.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2013, 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, see . - */ - -#include -#include - -#include - -#include "fuse.h" - -#define CORE_PROCESS_CORNERS_NUM 2 -#define CPU_PROCESS_CORNERS_NUM 2 - -enum { - THRESHOLD_INDEX_0, - THRESHOLD_INDEX_1, - THRESHOLD_INDEX_COUNT, -}; - -static const u32 core_process_speedos[][CORE_PROCESS_CORNERS_NUM] = { - {1123, UINT_MAX}, - {0, UINT_MAX}, -}; - -static const u32 cpu_process_speedos[][CPU_PROCESS_CORNERS_NUM] = { - {1695, UINT_MAX}, - {0, UINT_MAX}, -}; - -static void rev_sku_to_speedo_ids(int rev, int sku, int *threshold) -{ - u32 tmp; - - switch (sku) { - case 0x00: - case 0x10: - case 0x05: - case 0x06: - tegra_cpu_speedo_id = 1; - tegra_soc_speedo_id = 0; - *threshold = THRESHOLD_INDEX_0; - break; - - case 0x03: - case 0x04: - tegra_cpu_speedo_id = 2; - tegra_soc_speedo_id = 1; - *threshold = THRESHOLD_INDEX_1; - break; - - default: - pr_err("Tegra114 Unknown SKU %d\n", sku); - tegra_cpu_speedo_id = 0; - tegra_soc_speedo_id = 0; - *threshold = THRESHOLD_INDEX_0; - break; - } - - if (rev == TEGRA_REVISION_A01) { - tmp = tegra_fuse_readl(0x270) << 1; - tmp |= tegra_fuse_readl(0x26c); - if (!tmp) - tegra_cpu_speedo_id = 0; - } -} - -void tegra114_init_speedo_data(void) -{ - u32 cpu_speedo_val; - u32 core_speedo_val; - int threshold; - int i; - - BUILD_BUG_ON(ARRAY_SIZE(cpu_process_speedos) != - THRESHOLD_INDEX_COUNT); - BUILD_BUG_ON(ARRAY_SIZE(core_process_speedos) != - THRESHOLD_INDEX_COUNT); - - rev_sku_to_speedo_ids(tegra_revision, tegra_sku_id, &threshold); - - cpu_speedo_val = tegra_fuse_readl(0x12c) + 1024; - core_speedo_val = tegra_fuse_readl(0x134); - - for (i = 0; i < CPU_PROCESS_CORNERS_NUM; i++) - if (cpu_speedo_val < cpu_process_speedos[threshold][i]) - break; - tegra_cpu_process_id = i; - - for (i = 0; i < CORE_PROCESS_CORNERS_NUM; i++) - if (core_speedo_val < core_process_speedos[threshold][i]) - break; - tegra_core_process_id = i; -} diff --git a/arch/arm/mach-tegra/tegra20_speedo.c b/arch/arm/mach-tegra/tegra20_speedo.c deleted file mode 100644 index 2907cf8de2b1..000000000000 --- a/arch/arm/mach-tegra/tegra20_speedo.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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, see . - */ - -#include -#include - -#include - -#include "fuse.h" - -#define CPU_SPEEDO_LSBIT 20 -#define CPU_SPEEDO_MSBIT 29 -#define CPU_SPEEDO_REDUND_LSBIT 30 -#define CPU_SPEEDO_REDUND_MSBIT 39 -#define CPU_SPEEDO_REDUND_OFFS (CPU_SPEEDO_REDUND_MSBIT - CPU_SPEEDO_MSBIT) - -#define CORE_SPEEDO_LSBIT 40 -#define CORE_SPEEDO_MSBIT 47 -#define CORE_SPEEDO_REDUND_LSBIT 48 -#define CORE_SPEEDO_REDUND_MSBIT 55 -#define CORE_SPEEDO_REDUND_OFFS (CORE_SPEEDO_REDUND_MSBIT - CORE_SPEEDO_MSBIT) - -#define SPEEDO_MULT 4 - -#define PROCESS_CORNERS_NUM 4 - -#define SPEEDO_ID_SELECT_0(rev) ((rev) <= 2) -#define SPEEDO_ID_SELECT_1(sku) \ - (((sku) != 20) && ((sku) != 23) && ((sku) != 24) && \ - ((sku) != 27) && ((sku) != 28)) - -enum { - SPEEDO_ID_0, - SPEEDO_ID_1, - SPEEDO_ID_2, - SPEEDO_ID_COUNT, -}; - -static const u32 cpu_process_speedos[][PROCESS_CORNERS_NUM] = { - {315, 366, 420, UINT_MAX}, - {303, 368, 419, UINT_MAX}, - {316, 331, 383, UINT_MAX}, -}; - -static const u32 core_process_speedos[][PROCESS_CORNERS_NUM] = { - {165, 195, 224, UINT_MAX}, - {165, 195, 224, UINT_MAX}, - {165, 195, 224, UINT_MAX}, -}; - -void tegra20_init_speedo_data(void) -{ - u32 reg; - u32 val; - int i; - - BUILD_BUG_ON(ARRAY_SIZE(cpu_process_speedos) != SPEEDO_ID_COUNT); - BUILD_BUG_ON(ARRAY_SIZE(core_process_speedos) != SPEEDO_ID_COUNT); - - if (SPEEDO_ID_SELECT_0(tegra_revision)) - tegra_soc_speedo_id = SPEEDO_ID_0; - else if (SPEEDO_ID_SELECT_1(tegra_sku_id)) - tegra_soc_speedo_id = SPEEDO_ID_1; - else - tegra_soc_speedo_id = SPEEDO_ID_2; - - val = 0; - for (i = CPU_SPEEDO_MSBIT; i >= CPU_SPEEDO_LSBIT; i--) { - reg = tegra_spare_fuse(i) | - tegra_spare_fuse(i + CPU_SPEEDO_REDUND_OFFS); - val = (val << 1) | (reg & 0x1); - } - val = val * SPEEDO_MULT; - pr_debug("%s CPU speedo value %u\n", __func__, val); - - for (i = 0; i < (PROCESS_CORNERS_NUM - 1); i++) { - if (val <= cpu_process_speedos[tegra_soc_speedo_id][i]) - break; - } - tegra_cpu_process_id = i; - - val = 0; - for (i = CORE_SPEEDO_MSBIT; i >= CORE_SPEEDO_LSBIT; i--) { - reg = tegra_spare_fuse(i) | - tegra_spare_fuse(i + CORE_SPEEDO_REDUND_OFFS); - val = (val << 1) | (reg & 0x1); - } - val = val * SPEEDO_MULT; - pr_debug("%s Core speedo value %u\n", __func__, val); - - for (i = 0; i < (PROCESS_CORNERS_NUM - 1); i++) { - if (val <= core_process_speedos[tegra_soc_speedo_id][i]) - break; - } - tegra_core_process_id = i; - - pr_info("Tegra20 Soc Speedo ID %d", tegra_soc_speedo_id); -} diff --git a/arch/arm/mach-tegra/tegra30_speedo.c b/arch/arm/mach-tegra/tegra30_speedo.c deleted file mode 100644 index 6f6102cd1e6b..000000000000 --- a/arch/arm/mach-tegra/tegra30_speedo.c +++ /dev/null @@ -1,294 +0,0 @@ -/* - * 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, see . - */ - -#include -#include - -#include - -#include "fuse.h" - -#define CORE_PROCESS_CORNERS_NUM 1 -#define CPU_PROCESS_CORNERS_NUM 6 - -#define FUSE_SPEEDO_CALIB_0 0x114 -#define FUSE_PACKAGE_INFO 0X1FC -#define FUSE_TEST_PROG_VER 0X128 - -#define G_SPEEDO_BIT_MINUS1 58 -#define G_SPEEDO_BIT_MINUS1_R 59 -#define G_SPEEDO_BIT_MINUS2 60 -#define G_SPEEDO_BIT_MINUS2_R 61 -#define LP_SPEEDO_BIT_MINUS1 62 -#define LP_SPEEDO_BIT_MINUS1_R 63 -#define LP_SPEEDO_BIT_MINUS2 64 -#define LP_SPEEDO_BIT_MINUS2_R 65 - -enum { - THRESHOLD_INDEX_0, - THRESHOLD_INDEX_1, - THRESHOLD_INDEX_2, - THRESHOLD_INDEX_3, - THRESHOLD_INDEX_4, - THRESHOLD_INDEX_5, - THRESHOLD_INDEX_6, - THRESHOLD_INDEX_7, - THRESHOLD_INDEX_8, - THRESHOLD_INDEX_9, - THRESHOLD_INDEX_10, - THRESHOLD_INDEX_11, - THRESHOLD_INDEX_COUNT, -}; - -static const u32 core_process_speedos[][CORE_PROCESS_CORNERS_NUM] = { - {180}, - {170}, - {195}, - {180}, - {168}, - {192}, - {180}, - {170}, - {195}, - {180}, - {180}, - {180}, -}; - -static const u32 cpu_process_speedos[][CPU_PROCESS_CORNERS_NUM] = { - {306, 338, 360, 376, UINT_MAX}, - {295, 336, 358, 375, UINT_MAX}, - {325, 325, 358, 375, UINT_MAX}, - {325, 325, 358, 375, UINT_MAX}, - {292, 324, 348, 364, UINT_MAX}, - {324, 324, 348, 364, UINT_MAX}, - {324, 324, 348, 364, UINT_MAX}, - {295, 336, 358, 375, UINT_MAX}, - {358, 358, 358, 358, 397, UINT_MAX}, - {364, 364, 364, 364, 397, UINT_MAX}, - {295, 336, 358, 375, 391, UINT_MAX}, - {295, 336, 358, 375, 391, UINT_MAX}, -}; - -static int threshold_index; -static int package_id; - -static void fuse_speedo_calib(u32 *speedo_g, u32 *speedo_lp) -{ - u32 reg; - int ate_ver; - int bit_minus1; - int bit_minus2; - - reg = tegra_fuse_readl(FUSE_SPEEDO_CALIB_0); - - *speedo_lp = (reg & 0xFFFF) * 4; - *speedo_g = ((reg >> 16) & 0xFFFF) * 4; - - ate_ver = tegra_fuse_readl(FUSE_TEST_PROG_VER); - pr_info("%s: ATE prog ver %d.%d\n", __func__, ate_ver/10, ate_ver%10); - - if (ate_ver >= 26) { - bit_minus1 = tegra_spare_fuse(LP_SPEEDO_BIT_MINUS1); - bit_minus1 |= tegra_spare_fuse(LP_SPEEDO_BIT_MINUS1_R); - bit_minus2 = tegra_spare_fuse(LP_SPEEDO_BIT_MINUS2); - bit_minus2 |= tegra_spare_fuse(LP_SPEEDO_BIT_MINUS2_R); - *speedo_lp |= (bit_minus1 << 1) | bit_minus2; - - bit_minus1 = tegra_spare_fuse(G_SPEEDO_BIT_MINUS1); - bit_minus1 |= tegra_spare_fuse(G_SPEEDO_BIT_MINUS1_R); - bit_minus2 = tegra_spare_fuse(G_SPEEDO_BIT_MINUS2); - bit_minus2 |= tegra_spare_fuse(G_SPEEDO_BIT_MINUS2_R); - *speedo_g |= (bit_minus1 << 1) | bit_minus2; - } else { - *speedo_lp |= 0x3; - *speedo_g |= 0x3; - } -} - -static void rev_sku_to_speedo_ids(int rev, int sku) -{ - switch (rev) { - case TEGRA_REVISION_A01: - tegra_cpu_speedo_id = 0; - tegra_soc_speedo_id = 0; - threshold_index = THRESHOLD_INDEX_0; - break; - case TEGRA_REVISION_A02: - case TEGRA_REVISION_A03: - switch (sku) { - case 0x87: - case 0x82: - tegra_cpu_speedo_id = 1; - tegra_soc_speedo_id = 1; - threshold_index = THRESHOLD_INDEX_1; - break; - case 0x81: - switch (package_id) { - case 1: - tegra_cpu_speedo_id = 2; - tegra_soc_speedo_id = 2; - threshold_index = THRESHOLD_INDEX_2; - break; - case 2: - tegra_cpu_speedo_id = 4; - tegra_soc_speedo_id = 1; - threshold_index = THRESHOLD_INDEX_7; - break; - default: - pr_err("Tegra30: Unknown pkg %d\n", package_id); - BUG(); - break; - } - break; - case 0x80: - switch (package_id) { - case 1: - tegra_cpu_speedo_id = 5; - tegra_soc_speedo_id = 2; - threshold_index = THRESHOLD_INDEX_8; - break; - case 2: - tegra_cpu_speedo_id = 6; - tegra_soc_speedo_id = 2; - threshold_index = THRESHOLD_INDEX_9; - break; - default: - pr_err("Tegra30: Unknown pkg %d\n", package_id); - BUG(); - break; - } - break; - case 0x83: - switch (package_id) { - case 1: - tegra_cpu_speedo_id = 7; - tegra_soc_speedo_id = 1; - threshold_index = THRESHOLD_INDEX_10; - break; - case 2: - tegra_cpu_speedo_id = 3; - tegra_soc_speedo_id = 2; - threshold_index = THRESHOLD_INDEX_3; - break; - default: - pr_err("Tegra30: Unknown pkg %d\n", package_id); - BUG(); - break; - } - break; - case 0x8F: - tegra_cpu_speedo_id = 8; - tegra_soc_speedo_id = 1; - threshold_index = THRESHOLD_INDEX_11; - break; - case 0x08: - tegra_cpu_speedo_id = 1; - tegra_soc_speedo_id = 1; - threshold_index = THRESHOLD_INDEX_4; - break; - case 0x02: - tegra_cpu_speedo_id = 2; - tegra_soc_speedo_id = 2; - threshold_index = THRESHOLD_INDEX_5; - break; - case 0x04: - tegra_cpu_speedo_id = 3; - tegra_soc_speedo_id = 2; - threshold_index = THRESHOLD_INDEX_6; - break; - case 0: - switch (package_id) { - case 1: - tegra_cpu_speedo_id = 2; - tegra_soc_speedo_id = 2; - threshold_index = THRESHOLD_INDEX_2; - break; - case 2: - tegra_cpu_speedo_id = 3; - tegra_soc_speedo_id = 2; - threshold_index = THRESHOLD_INDEX_3; - break; - default: - pr_err("Tegra30: Unknown pkg %d\n", package_id); - BUG(); - break; - } - break; - default: - pr_warn("Tegra30: Unknown SKU %d\n", sku); - tegra_cpu_speedo_id = 0; - tegra_soc_speedo_id = 0; - threshold_index = THRESHOLD_INDEX_0; - break; - } - break; - default: - pr_warn("Tegra30: Unknown chip rev %d\n", rev); - tegra_cpu_speedo_id = 0; - tegra_soc_speedo_id = 0; - threshold_index = THRESHOLD_INDEX_0; - break; - } -} - -void tegra30_init_speedo_data(void) -{ - u32 cpu_speedo_val; - u32 core_speedo_val; - int i; - - BUILD_BUG_ON(ARRAY_SIZE(cpu_process_speedos) != - THRESHOLD_INDEX_COUNT); - BUILD_BUG_ON(ARRAY_SIZE(core_process_speedos) != - THRESHOLD_INDEX_COUNT); - - package_id = tegra_fuse_readl(FUSE_PACKAGE_INFO) & 0x0F; - - rev_sku_to_speedo_ids(tegra_revision, tegra_sku_id); - fuse_speedo_calib(&cpu_speedo_val, &core_speedo_val); - pr_debug("%s CPU speedo value %u\n", __func__, cpu_speedo_val); - pr_debug("%s Core speedo value %u\n", __func__, core_speedo_val); - - for (i = 0; i < CPU_PROCESS_CORNERS_NUM; i++) { - if (cpu_speedo_val < cpu_process_speedos[threshold_index][i]) - break; - } - tegra_cpu_process_id = i - 1; - - if (tegra_cpu_process_id == -1) { - pr_warn("Tegra30: CPU speedo value %3d out of range", - cpu_speedo_val); - tegra_cpu_process_id = 0; - tegra_cpu_speedo_id = 1; - } - - for (i = 0; i < CORE_PROCESS_CORNERS_NUM; i++) { - if (core_speedo_val < core_process_speedos[threshold_index][i]) - break; - } - tegra_core_process_id = i - 1; - - if (tegra_core_process_id == -1) { - pr_warn("Tegra30: CORE speedo value %3d out of range", - core_speedo_val); - tegra_core_process_id = 0; - tegra_soc_speedo_id = 1; - } - - pr_info("Tegra30: CPU Speedo ID %d, Soc Speedo ID %d", - tegra_cpu_speedo_id, tegra_soc_speedo_id); -} diff --git a/drivers/misc/fuse/Makefile b/drivers/misc/fuse/Makefile new file mode 100644 index 000000000000..0679c4febc89 --- /dev/null +++ b/drivers/misc/fuse/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_ARCH_TEGRA) += tegra/ diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 0f7c44793b29..3b1b95d932d1 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_ARCH_QCOM) += qcom/ +obj-$(CONFIG_ARCH_TEGRA) += tegra/ diff --git a/drivers/soc/tegra/Makefile b/drivers/soc/tegra/Makefile new file mode 100644 index 000000000000..236600f91bb3 --- /dev/null +++ b/drivers/soc/tegra/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_ARCH_TEGRA) += fuse/ diff --git a/drivers/soc/tegra/fuse/Makefile b/drivers/soc/tegra/fuse/Makefile new file mode 100644 index 000000000000..3af357da91f3 --- /dev/null +++ b/drivers/soc/tegra/fuse/Makefile @@ -0,0 +1,8 @@ +obj-y += fuse-tegra.o +obj-y += fuse-tegra30.o +obj-y += tegra-apbmisc.o +obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += fuse-tegra20.o +obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += speedo-tegra20.o +obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += speedo-tegra30.o +obj-$(CONFIG_ARCH_TEGRA_114_SOC) += speedo-tegra114.o +obj-$(CONFIG_ARCH_TEGRA_124_SOC) += speedo-tegra124.o diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c new file mode 100644 index 000000000000..03742edcfe83 --- /dev/null +++ b/drivers/soc/tegra/fuse/fuse-tegra.c @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2013-2014, 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, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "fuse.h" + +static u32 (*fuse_readl)(const unsigned int offset); +static int fuse_size; +struct tegra_sku_info tegra_sku_info; + +static const char *tegra_revision_name[TEGRA_REVISION_MAX] = { + [TEGRA_REVISION_UNKNOWN] = "unknown", + [TEGRA_REVISION_A01] = "A01", + [TEGRA_REVISION_A02] = "A02", + [TEGRA_REVISION_A03] = "A03", + [TEGRA_REVISION_A03p] = "A03 prime", + [TEGRA_REVISION_A04] = "A04", +}; + +static u8 fuse_readb(const unsigned int offset) +{ + u32 val; + + val = fuse_readl(round_down(offset, 4)); + val >>= (offset % 4) * 8; + val &= 0xff; + + return val; +} + +static ssize_t fuse_read(struct file *fd, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t pos, size_t size) +{ + int i; + + if (pos < 0 || pos >= fuse_size) + return 0; + + if (size > fuse_size - pos) + size = fuse_size - pos; + + for (i = 0; i < size; i++) + buf[i] = fuse_readb(pos + i); + + return i; +} + +static struct bin_attribute fuse_bin_attr = { + .attr = { .name = "fuse", .mode = S_IRUGO, }, + .read = fuse_read, +}; + +static const struct of_device_id car_match[] __initconst = { + { .compatible = "nvidia,tegra20-car", }, + { .compatible = "nvidia,tegra30-car", }, + { .compatible = "nvidia,tegra114-car", }, + { .compatible = "nvidia,tegra124-car", }, + {}, +}; + +static void tegra_enable_fuse_clk(void __iomem *base) +{ + u32 reg; + + reg = readl_relaxed(base + 0x48); + reg |= 1 << 28; + writel(reg, base + 0x48); + + /* + * Enable FUSE clock. This needs to be hardcoded because the clock + * subsystem is not active during early boot. + */ + reg = readl(base + 0x14); + reg |= 1 << 7; + writel(reg, base + 0x14); +} + +int tegra_fuse_readl(unsigned long offset, u32 *value) +{ + if (!fuse_readl) + return -EPROBE_DEFER; + + *value = fuse_readl(offset); + + return 0; +} +EXPORT_SYMBOL(tegra_fuse_readl); + +int tegra_fuse_create_sysfs(struct device *dev, int size, + u32 (*readl)(const unsigned int offset)) +{ + if (fuse_size) + return -ENODEV; + + fuse_bin_attr.size = size; + fuse_bin_attr.read = fuse_read; + + fuse_size = size; + fuse_readl = readl; + + return device_create_bin_file(dev, &fuse_bin_attr); +} + +void __init tegra_init_fuse(void) +{ + struct device_node *np; + void __iomem *car_base; + + tegra_init_apbmisc(); + + np = of_find_matching_node(NULL, car_match); + car_base = of_iomap(np, 0); + if (car_base) { + tegra_enable_fuse_clk(car_base); + iounmap(car_base); + } else { + pr_err("Could not enable fuse clk. ioremap tegra car failed.\n"); + return; + } + + if (tegra_get_chip_id() == TEGRA20) + tegra20_init_fuse_early(); + else + tegra30_init_fuse_early(); + + pr_info("Tegra Revision: %s SKU: %d CPU Process: %d Core Process: %d\n", + tegra_revision_name[tegra_sku_info.revision], + tegra_sku_info.sku_id, tegra_sku_info.cpu_process_id, + tegra_sku_info.core_process_id); + pr_debug("Tegra CPU Speedo ID %d, Soc Speedo ID %d\n", + tegra_sku_info.cpu_speedo_id, tegra_sku_info.soc_speedo_id); +} diff --git a/drivers/soc/tegra/fuse/fuse-tegra20.c b/drivers/soc/tegra/fuse/fuse-tegra20.c new file mode 100644 index 000000000000..7a9d0e045490 --- /dev/null +++ b/drivers/soc/tegra/fuse/fuse-tegra20.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2013-2014, 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, see . + * + * Based on drivers/misc/eeprom/sunxi_sid.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "fuse.h" + +#define FUSE_BEGIN 0x100 +#define FUSE_SIZE 0x1f8 +#define FUSE_UID_LOW 0x08 +#define FUSE_UID_HIGH 0x0c + +static phys_addr_t fuse_phys; +static struct clk *fuse_clk; +static void __iomem __initdata *fuse_base; + +static u32 tegra20_fuse_readl(const unsigned int offset) +{ + int ret; + u32 val; + + clk_prepare_enable(fuse_clk); + + ret = tegra_apb_readl_using_dma(fuse_phys + FUSE_BEGIN + offset, &val); + + clk_disable_unprepare(fuse_clk); + + return (ret < 0) ? 0 : val; +} + +static const struct of_device_id tegra20_fuse_of_match[] = { + { .compatible = "nvidia,tegra20-efuse" }, + {}, +}; + +static int tegra20_fuse_probe(struct platform_device *pdev) +{ + struct resource *res; + + fuse_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(fuse_clk)) { + dev_err(&pdev->dev, "missing clock"); + return PTR_ERR(fuse_clk); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + fuse_phys = res->start; + + if (tegra_fuse_create_sysfs(&pdev->dev, FUSE_SIZE, tegra20_fuse_readl)) + return -ENODEV; + + dev_dbg(&pdev->dev, "loaded\n"); + + return 0; +} + +static struct platform_driver tegra20_fuse_driver = { + .probe = tegra20_fuse_probe, + .driver = { + .name = "tegra20_fuse", + .owner = THIS_MODULE, + .of_match_table = tegra20_fuse_of_match, + } +}; + +static int __init tegra20_fuse_init(void) +{ + return platform_driver_register(&tegra20_fuse_driver); +} +postcore_initcall(tegra20_fuse_init); + +/* Early boot code. This code is called before the devices are created */ + +u32 __init tegra20_fuse_early(const unsigned int offset) +{ + return readl_relaxed(fuse_base + FUSE_BEGIN + offset); +} + +bool __init tegra20_spare_fuse_early(int spare_bit) +{ + u32 offset = spare_bit * 4; + bool value; + + value = tegra20_fuse_early(offset + 0x100); + + return value; +} + +static void __init tegra20_fuse_add_randomness(void) +{ + u32 randomness[7]; + + randomness[0] = tegra_sku_info.sku_id; + randomness[1] = tegra_read_straps(); + randomness[2] = tegra_read_chipid(); + randomness[3] = tegra_sku_info.cpu_process_id << 16; + randomness[3] |= tegra_sku_info.core_process_id; + randomness[4] = tegra_sku_info.cpu_speedo_id << 16; + randomness[4] |= tegra_sku_info.soc_speedo_id; + randomness[5] = tegra20_fuse_early(FUSE_UID_LOW); + randomness[6] = tegra20_fuse_early(FUSE_UID_HIGH); + + add_device_randomness(randomness, sizeof(randomness)); +} + +void __init tegra20_init_fuse_early(void) +{ + fuse_base = ioremap(TEGRA_FUSE_BASE, TEGRA_FUSE_SIZE); + + tegra_init_revision(); + tegra20_init_speedo_data(&tegra_sku_info); + tegra20_fuse_add_randomness(); + + iounmap(fuse_base); +} diff --git a/drivers/soc/tegra/fuse/fuse-tegra30.c b/drivers/soc/tegra/fuse/fuse-tegra30.c new file mode 100644 index 000000000000..5999cf34ab70 --- /dev/null +++ b/drivers/soc/tegra/fuse/fuse-tegra30.c @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2013-2014, 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, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "fuse.h" + +#define FUSE_BEGIN 0x100 + +/* Tegra30 and later */ +#define FUSE_VENDOR_CODE 0x100 +#define FUSE_FAB_CODE 0x104 +#define FUSE_LOT_CODE_0 0x108 +#define FUSE_LOT_CODE_1 0x10c +#define FUSE_WAFER_ID 0x110 +#define FUSE_X_COORDINATE 0x114 +#define FUSE_Y_COORDINATE 0x118 + +#define FUSE_HAS_REVISION_INFO BIT(0) + +enum speedo_idx { + SPEEDO_TEGRA30 = 0, + SPEEDO_TEGRA114, + SPEEDO_TEGRA124, +}; + +struct tegra_fuse_info { + int size; + int spare_bit; + enum speedo_idx speedo_idx; +}; + +static void __iomem *fuse_base; +static struct clk *fuse_clk; +static struct tegra_fuse_info *fuse_info; + +u32 tegra30_fuse_readl(const unsigned int offset) +{ + u32 val; + + /* + * early in the boot, the fuse clock will be enabled by + * tegra_init_fuse() + */ + + if (fuse_clk) + clk_prepare_enable(fuse_clk); + + val = readl_relaxed(fuse_base + FUSE_BEGIN + offset); + + if (fuse_clk) + clk_disable_unprepare(fuse_clk); + + return val; +} + +static struct tegra_fuse_info tegra30_info = { + .size = 0x2a4, + .spare_bit = 0x144, + .speedo_idx = SPEEDO_TEGRA30, +}; + +static struct tegra_fuse_info tegra114_info = { + .size = 0x2a0, + .speedo_idx = SPEEDO_TEGRA114, +}; + +static struct tegra_fuse_info tegra124_info = { + .size = 0x300, + .speedo_idx = SPEEDO_TEGRA124, +}; + +static const struct of_device_id tegra30_fuse_of_match[] = { + { .compatible = "nvidia,tegra30-efuse", .data = &tegra30_info }, + { .compatible = "nvidia,tegra114-efuse", .data = &tegra114_info }, + { .compatible = "nvidia,tegra124-efuse", .data = &tegra124_info }, + {}, +}; + +static int tegra30_fuse_probe(struct platform_device *pdev) +{ + const struct of_device_id *of_dev_id; + + of_dev_id = of_match_device(tegra30_fuse_of_match, &pdev->dev); + if (!of_dev_id) + return -ENODEV; + + fuse_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(fuse_clk)) { + dev_err(&pdev->dev, "missing clock"); + return PTR_ERR(fuse_clk); + } + + platform_set_drvdata(pdev, NULL); + + if (tegra_fuse_create_sysfs(&pdev->dev, fuse_info->size, + tegra30_fuse_readl)) + return -ENODEV; + + dev_dbg(&pdev->dev, "loaded\n"); + + return 0; +} + +static struct platform_driver tegra30_fuse_driver = { + .probe = tegra30_fuse_probe, + .driver = { + .name = "tegra_fuse", + .owner = THIS_MODULE, + .of_match_table = tegra30_fuse_of_match, + } +}; + +static int __init tegra30_fuse_init(void) +{ + return platform_driver_register(&tegra30_fuse_driver); +} +postcore_initcall(tegra30_fuse_init); + +/* Early boot code. This code is called before the devices are created */ + +typedef void (*speedo_f)(struct tegra_sku_info *sku_info); + +static speedo_f __initdata speedo_tbl[] = { + [SPEEDO_TEGRA30] = tegra30_init_speedo_data, + [SPEEDO_TEGRA114] = tegra114_init_speedo_data, + [SPEEDO_TEGRA124] = tegra124_init_speedo_data, +}; + +static void __init tegra30_fuse_add_randomness(void) +{ + u32 randomness[12]; + + randomness[0] = tegra_sku_info.sku_id; + randomness[1] = tegra_read_straps(); + randomness[2] = tegra_read_chipid(); + randomness[3] = tegra_sku_info.cpu_process_id << 16; + randomness[3] |= tegra_sku_info.core_process_id; + randomness[4] = tegra_sku_info.cpu_speedo_id << 16; + randomness[4] |= tegra_sku_info.soc_speedo_id; + randomness[5] = tegra30_fuse_readl(FUSE_VENDOR_CODE); + randomness[6] = tegra30_fuse_readl(FUSE_FAB_CODE); + randomness[7] = tegra30_fuse_readl(FUSE_LOT_CODE_0); + randomness[8] = tegra30_fuse_readl(FUSE_LOT_CODE_1); + randomness[9] = tegra30_fuse_readl(FUSE_WAFER_ID); + randomness[10] = tegra30_fuse_readl(FUSE_X_COORDINATE); + randomness[11] = tegra30_fuse_readl(FUSE_Y_COORDINATE); + + add_device_randomness(randomness, sizeof(randomness)); +} + +static void __init legacy_fuse_init(void) +{ + switch (tegra_get_chip_id()) { + case TEGRA30: + fuse_info = &tegra30_info; + break; + case TEGRA114: + fuse_info = &tegra114_info; + break; + case TEGRA124: + fuse_info = &tegra124_info; + break; + default: + return; + } + + fuse_base = ioremap(TEGRA_FUSE_BASE, TEGRA_FUSE_SIZE); +} + +bool __init tegra30_spare_fuse(int spare_bit) +{ + u32 offset = fuse_info->spare_bit + spare_bit * 4; + + return tegra30_fuse_readl(offset) & 1; +} + +void __init tegra30_init_fuse_early(void) +{ + struct device_node *np; + const struct of_device_id *of_match; + + np = of_find_matching_node_and_match(NULL, tegra30_fuse_of_match, + &of_match); + if (np) { + fuse_base = of_iomap(np, 0); + fuse_info = (struct tegra_fuse_info *)of_match->data; + } else + legacy_fuse_init(); + + if (!fuse_base) { + pr_warn("fuse DT node missing and unknown chip id: 0x%02x\n", + tegra_get_chip_id()); + return; + } + + tegra_init_revision(); + speedo_tbl[fuse_info->speedo_idx](&tegra_sku_info); + tegra30_fuse_add_randomness(); +} diff --git a/drivers/soc/tegra/fuse/fuse.h b/drivers/soc/tegra/fuse/fuse.h new file mode 100644 index 000000000000..b3442770df09 --- /dev/null +++ b/drivers/soc/tegra/fuse/fuse.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2010 Google, Inc. + * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. + * + * Author: + * Colin Cross + * + * 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 __DRIVERS_MISC_TEGRA_FUSE_H +#define __DRIVERS_MISC_TEGRA_FUSE_H + +#define TEGRA_FUSE_BASE 0x7000f800 +#define TEGRA_FUSE_SIZE 0x400 + +int tegra_fuse_create_sysfs(struct device *dev, int size, + u32 (*readl)(const unsigned int offset)); + +bool tegra30_spare_fuse(int bit); +u32 tegra30_fuse_readl(const unsigned int offset); +void tegra30_init_fuse_early(void); +void tegra_init_revision(void); +void tegra_init_apbmisc(void); + +#ifdef CONFIG_ARCH_TEGRA_2x_SOC +void tegra20_init_speedo_data(struct tegra_sku_info *sku_info); +bool tegra20_spare_fuse_early(int spare_bit); +void tegra20_init_fuse_early(void); +u32 tegra20_fuse_early(const unsigned int offset); +#else +static inline void tegra20_init_speedo_data(struct tegra_sku_info *sku_info) {} +static inline bool tegra20_spare_fuse_early(int spare_bit, void *fuse_base) +{ + return false; +} +static inline void tegra20_init_fuse_early(void); +static inline tegra20_fuse_early(const unsigned int offset); +{ + return 0; +} +#endif + + +#ifdef CONFIG_ARCH_TEGRA_3x_SOC +void tegra30_init_speedo_data(struct tegra_sku_info *sku_info); +#else +static inline void tegra30_init_speedo_data(struct tegra_sku_info *sku_info) {} +#endif + +#ifdef CONFIG_ARCH_TEGRA_114_SOC +void tegra114_init_speedo_data(struct tegra_sku_info *sku_info); +#else +static inline void tegra114_init_speedo_data(struct tegra_sku_info *sku_info) {} +#endif + +#ifdef CONFIG_ARCH_TEGRA_124_SOC +void tegra124_init_speedo_data(struct tegra_sku_info *sku_info); +#else +static inline void tegra124_init_speedo_data(struct tegra_sku_info *sku_info) {} +#endif + +#endif diff --git a/drivers/soc/tegra/fuse/speedo-tegra114.c b/drivers/soc/tegra/fuse/speedo-tegra114.c new file mode 100644 index 000000000000..2a6ca036f09f --- /dev/null +++ b/drivers/soc/tegra/fuse/speedo-tegra114.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2013-2014, 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, see . + */ + +#include +#include +#include + +#include + +#include "fuse.h" + +#define CORE_PROCESS_CORNERS 2 +#define CPU_PROCESS_CORNERS 2 + +enum { + THRESHOLD_INDEX_0, + THRESHOLD_INDEX_1, + THRESHOLD_INDEX_COUNT, +}; + +static const u32 __initconst core_process_speedos[][CORE_PROCESS_CORNERS] = { + {1123, UINT_MAX}, + {0, UINT_MAX}, +}; + +static const u32 __initconst cpu_process_speedos[][CPU_PROCESS_CORNERS] = { + {1695, UINT_MAX}, + {0, UINT_MAX}, +}; + +static void __init rev_sku_to_speedo_ids(struct tegra_sku_info *sku_info, + int *threshold) +{ + u32 tmp; + u32 sku = sku_info->sku_id; + enum tegra_revision rev = sku_info->revision; + + switch (sku) { + case 0x00: + case 0x10: + case 0x05: + case 0x06: + sku_info->cpu_speedo_id = 1; + sku_info->soc_speedo_id = 0; + *threshold = THRESHOLD_INDEX_0; + break; + + case 0x03: + case 0x04: + sku_info->cpu_speedo_id = 2; + sku_info->soc_speedo_id = 1; + *threshold = THRESHOLD_INDEX_1; + break; + + default: + pr_err("Tegra Unknown SKU %d\n", sku); + sku_info->cpu_speedo_id = 0; + sku_info->soc_speedo_id = 0; + *threshold = THRESHOLD_INDEX_0; + break; + } + + if (rev == TEGRA_REVISION_A01) { + tmp = tegra30_fuse_readl(0x270) << 1; + tmp |= tegra30_fuse_readl(0x26c); + if (!tmp) + sku_info->cpu_speedo_id = 0; + } +} + +void __init tegra114_init_speedo_data(struct tegra_sku_info *sku_info) +{ + u32 cpu_speedo_val; + u32 core_speedo_val; + int threshold; + int i; + + BUILD_BUG_ON(ARRAY_SIZE(cpu_process_speedos) != + THRESHOLD_INDEX_COUNT); + BUILD_BUG_ON(ARRAY_SIZE(core_process_speedos) != + THRESHOLD_INDEX_COUNT); + + rev_sku_to_speedo_ids(sku_info, &threshold); + + cpu_speedo_val = tegra30_fuse_readl(0x12c) + 1024; + core_speedo_val = tegra30_fuse_readl(0x134); + + for (i = 0; i < CPU_PROCESS_CORNERS; i++) + if (cpu_speedo_val < cpu_process_speedos[threshold][i]) + break; + sku_info->cpu_process_id = i; + + for (i = 0; i < CORE_PROCESS_CORNERS; i++) + if (core_speedo_val < core_process_speedos[threshold][i]) + break; + sku_info->core_process_id = i; +} diff --git a/drivers/soc/tegra/fuse/speedo-tegra124.c b/drivers/soc/tegra/fuse/speedo-tegra124.c new file mode 100644 index 000000000000..46362387d974 --- /dev/null +++ b/drivers/soc/tegra/fuse/speedo-tegra124.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2013-2014, 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, see . + */ + +#include +#include +#include + +#include + +#include "fuse.h" + +#define CPU_PROCESS_CORNERS 2 +#define GPU_PROCESS_CORNERS 2 +#define CORE_PROCESS_CORNERS 2 + +#define FUSE_CPU_SPEEDO_0 0x14 +#define FUSE_CPU_SPEEDO_1 0x2c +#define FUSE_CPU_SPEEDO_2 0x30 +#define FUSE_SOC_SPEEDO_0 0x34 +#define FUSE_SOC_SPEEDO_1 0x38 +#define FUSE_SOC_SPEEDO_2 0x3c +#define FUSE_CPU_IDDQ 0x18 +#define FUSE_SOC_IDDQ 0x40 +#define FUSE_GPU_IDDQ 0x128 +#define FUSE_FT_REV 0x28 + +enum { + THRESHOLD_INDEX_0, + THRESHOLD_INDEX_1, + THRESHOLD_INDEX_COUNT, +}; + +static const u32 __initconst cpu_process_speedos[][CPU_PROCESS_CORNERS] = { + {2190, UINT_MAX}, + {0, UINT_MAX}, +}; + +static const u32 __initconst gpu_process_speedos[][GPU_PROCESS_CORNERS] = { + {1965, UINT_MAX}, + {0, UINT_MAX}, +}; + +static const u32 __initconst core_process_speedos[][CORE_PROCESS_CORNERS] = { + {2101, UINT_MAX}, + {0, UINT_MAX}, +}; + +static void __init rev_sku_to_speedo_ids(struct tegra_sku_info *sku_info, + int *threshold) +{ + int sku = sku_info->sku_id; + + /* Assign to default */ + sku_info->cpu_speedo_id = 0; + sku_info->soc_speedo_id = 0; + sku_info->gpu_speedo_id = 0; + *threshold = THRESHOLD_INDEX_0; + + switch (sku) { + case 0x00: /* Eng sku */ + case 0x0F: + case 0x23: + /* Using the default */ + break; + case 0x83: + sku_info->cpu_speedo_id = 2; + break; + + case 0x1F: + case 0x87: + case 0x27: + sku_info->cpu_speedo_id = 2; + sku_info->soc_speedo_id = 0; + sku_info->gpu_speedo_id = 1; + *threshold = THRESHOLD_INDEX_0; + break; + case 0x81: + case 0x21: + case 0x07: + sku_info->cpu_speedo_id = 1; + sku_info->soc_speedo_id = 1; + sku_info->gpu_speedo_id = 1; + *threshold = THRESHOLD_INDEX_1; + break; + case 0x49: + case 0x4A: + case 0x48: + sku_info->cpu_speedo_id = 4; + sku_info->soc_speedo_id = 2; + sku_info->gpu_speedo_id = 3; + *threshold = THRESHOLD_INDEX_1; + break; + default: + pr_err("Tegra Unknown SKU %d\n", sku); + /* Using the default for the error case */ + break; + } +} + +void __init tegra124_init_speedo_data(struct tegra_sku_info *sku_info) +{ + int i, threshold, cpu_speedo_0_value, soc_speedo_0_value; + int cpu_iddq_value, gpu_iddq_value, soc_iddq_value; + + BUILD_BUG_ON(ARRAY_SIZE(cpu_process_speedos) != + THRESHOLD_INDEX_COUNT); + BUILD_BUG_ON(ARRAY_SIZE(gpu_process_speedos) != + THRESHOLD_INDEX_COUNT); + BUILD_BUG_ON(ARRAY_SIZE(core_process_speedos) != + THRESHOLD_INDEX_COUNT); + + cpu_speedo_0_value = tegra30_fuse_readl(FUSE_CPU_SPEEDO_0); + + /* GPU Speedo is stored in CPU_SPEEDO_2 */ + sku_info->gpu_speedo_value = tegra30_fuse_readl(FUSE_CPU_SPEEDO_2); + + soc_speedo_0_value = tegra30_fuse_readl(FUSE_SOC_SPEEDO_0); + + cpu_iddq_value = tegra30_fuse_readl(FUSE_CPU_IDDQ); + soc_iddq_value = tegra30_fuse_readl(FUSE_SOC_IDDQ); + gpu_iddq_value = tegra30_fuse_readl(FUSE_GPU_IDDQ); + + sku_info->cpu_speedo_value = cpu_speedo_0_value; + + if (sku_info->cpu_speedo_value == 0) { + pr_warn("Tegra Warning: Speedo value not fused.\n"); + WARN_ON(1); + return; + } + + rev_sku_to_speedo_ids(sku_info, &threshold); + + sku_info->cpu_iddq_value = tegra30_fuse_readl(FUSE_CPU_IDDQ); + + for (i = 0; i < GPU_PROCESS_CORNERS; i++) + if (sku_info->gpu_speedo_value < + gpu_process_speedos[threshold][i]) + break; + sku_info->gpu_process_id = i; + + for (i = 0; i < CPU_PROCESS_CORNERS; i++) + if (sku_info->cpu_speedo_value < + cpu_process_speedos[threshold][i]) + break; + sku_info->cpu_process_id = i; + + for (i = 0; i < CORE_PROCESS_CORNERS; i++) + if (soc_speedo_0_value < + core_process_speedos[threshold][i]) + break; + sku_info->core_process_id = i; + + pr_debug("Tegra GPU Speedo ID=%d, Speedo Value=%d\n", + sku_info->gpu_speedo_id, sku_info->gpu_speedo_value); +} diff --git a/drivers/soc/tegra/fuse/speedo-tegra20.c b/drivers/soc/tegra/fuse/speedo-tegra20.c new file mode 100644 index 000000000000..eff1b63f330d --- /dev/null +++ b/drivers/soc/tegra/fuse/speedo-tegra20.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2012-2014, 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, see . + */ + +#include +#include +#include + +#include + +#include "fuse.h" + +#define CPU_SPEEDO_LSBIT 20 +#define CPU_SPEEDO_MSBIT 29 +#define CPU_SPEEDO_REDUND_LSBIT 30 +#define CPU_SPEEDO_REDUND_MSBIT 39 +#define CPU_SPEEDO_REDUND_OFFS (CPU_SPEEDO_REDUND_MSBIT - CPU_SPEEDO_MSBIT) + +#define CORE_SPEEDO_LSBIT 40 +#define CORE_SPEEDO_MSBIT 47 +#define CORE_SPEEDO_REDUND_LSBIT 48 +#define CORE_SPEEDO_REDUND_MSBIT 55 +#define CORE_SPEEDO_REDUND_OFFS (CORE_SPEEDO_REDUND_MSBIT - CORE_SPEEDO_MSBIT) + +#define SPEEDO_MULT 4 + +#define PROCESS_CORNERS_NUM 4 + +#define SPEEDO_ID_SELECT_0(rev) ((rev) <= 2) +#define SPEEDO_ID_SELECT_1(sku) \ + (((sku) != 20) && ((sku) != 23) && ((sku) != 24) && \ + ((sku) != 27) && ((sku) != 28)) + +enum { + SPEEDO_ID_0, + SPEEDO_ID_1, + SPEEDO_ID_2, + SPEEDO_ID_COUNT, +}; + +static const u32 __initconst cpu_process_speedos[][PROCESS_CORNERS_NUM] = { + {315, 366, 420, UINT_MAX}, + {303, 368, 419, UINT_MAX}, + {316, 331, 383, UINT_MAX}, +}; + +static const u32 __initconst core_process_speedos[][PROCESS_CORNERS_NUM] = { + {165, 195, 224, UINT_MAX}, + {165, 195, 224, UINT_MAX}, + {165, 195, 224, UINT_MAX}, +}; + +void __init tegra20_init_speedo_data(struct tegra_sku_info *sku_info) +{ + u32 reg; + u32 val; + int i; + + BUILD_BUG_ON(ARRAY_SIZE(cpu_process_speedos) != SPEEDO_ID_COUNT); + BUILD_BUG_ON(ARRAY_SIZE(core_process_speedos) != SPEEDO_ID_COUNT); + + if (SPEEDO_ID_SELECT_0(sku_info->revision)) + sku_info->soc_speedo_id = SPEEDO_ID_0; + else if (SPEEDO_ID_SELECT_1(sku_info->sku_id)) + sku_info->soc_speedo_id = SPEEDO_ID_1; + else + sku_info->soc_speedo_id = SPEEDO_ID_2; + + val = 0; + for (i = CPU_SPEEDO_MSBIT; i >= CPU_SPEEDO_LSBIT; i--) { + reg = tegra20_spare_fuse_early(i) | + tegra20_spare_fuse_early(i + CPU_SPEEDO_REDUND_OFFS); + val = (val << 1) | (reg & 0x1); + } + val = val * SPEEDO_MULT; + pr_debug("Tegra CPU speedo value %u\n", val); + + for (i = 0; i < (PROCESS_CORNERS_NUM - 1); i++) { + if (val <= cpu_process_speedos[sku_info->soc_speedo_id][i]) + break; + } + sku_info->cpu_process_id = i; + + val = 0; + for (i = CORE_SPEEDO_MSBIT; i >= CORE_SPEEDO_LSBIT; i--) { + reg = tegra20_spare_fuse_early(i) | + tegra20_spare_fuse_early(i + CORE_SPEEDO_REDUND_OFFS); + val = (val << 1) | (reg & 0x1); + } + val = val * SPEEDO_MULT; + pr_debug("Core speedo value %u\n", val); + + for (i = 0; i < (PROCESS_CORNERS_NUM - 1); i++) { + if (val <= core_process_speedos[sku_info->soc_speedo_id][i]) + break; + } + sku_info->core_process_id = i; +} diff --git a/drivers/soc/tegra/fuse/speedo-tegra30.c b/drivers/soc/tegra/fuse/speedo-tegra30.c new file mode 100644 index 000000000000..b17f0dcdfebe --- /dev/null +++ b/drivers/soc/tegra/fuse/speedo-tegra30.c @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2012-2014, 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, see . + */ + +#include +#include +#include + +#include + +#include "fuse.h" + +#define CORE_PROCESS_CORNERS 1 +#define CPU_PROCESS_CORNERS 6 + +#define FUSE_SPEEDO_CALIB_0 0x14 +#define FUSE_PACKAGE_INFO 0XFC +#define FUSE_TEST_PROG_VER 0X28 + +#define G_SPEEDO_BIT_MINUS1 58 +#define G_SPEEDO_BIT_MINUS1_R 59 +#define G_SPEEDO_BIT_MINUS2 60 +#define G_SPEEDO_BIT_MINUS2_R 61 +#define LP_SPEEDO_BIT_MINUS1 62 +#define LP_SPEEDO_BIT_MINUS1_R 63 +#define LP_SPEEDO_BIT_MINUS2 64 +#define LP_SPEEDO_BIT_MINUS2_R 65 + +enum { + THRESHOLD_INDEX_0, + THRESHOLD_INDEX_1, + THRESHOLD_INDEX_2, + THRESHOLD_INDEX_3, + THRESHOLD_INDEX_4, + THRESHOLD_INDEX_5, + THRESHOLD_INDEX_6, + THRESHOLD_INDEX_7, + THRESHOLD_INDEX_8, + THRESHOLD_INDEX_9, + THRESHOLD_INDEX_10, + THRESHOLD_INDEX_11, + THRESHOLD_INDEX_COUNT, +}; + +static const u32 __initconst core_process_speedos[][CORE_PROCESS_CORNERS] = { + {180}, + {170}, + {195}, + {180}, + {168}, + {192}, + {180}, + {170}, + {195}, + {180}, + {180}, + {180}, +}; + +static const u32 __initconst cpu_process_speedos[][CPU_PROCESS_CORNERS] = { + {306, 338, 360, 376, UINT_MAX}, + {295, 336, 358, 375, UINT_MAX}, + {325, 325, 358, 375, UINT_MAX}, + {325, 325, 358, 375, UINT_MAX}, + {292, 324, 348, 364, UINT_MAX}, + {324, 324, 348, 364, UINT_MAX}, + {324, 324, 348, 364, UINT_MAX}, + {295, 336, 358, 375, UINT_MAX}, + {358, 358, 358, 358, 397, UINT_MAX}, + {364, 364, 364, 364, 397, UINT_MAX}, + {295, 336, 358, 375, 391, UINT_MAX}, + {295, 336, 358, 375, 391, UINT_MAX}, +}; + +static int threshold_index __initdata; + +static void __init fuse_speedo_calib(u32 *speedo_g, u32 *speedo_lp) +{ + u32 reg; + int ate_ver; + int bit_minus1; + int bit_minus2; + + reg = tegra30_fuse_readl(FUSE_SPEEDO_CALIB_0); + + *speedo_lp = (reg & 0xFFFF) * 4; + *speedo_g = ((reg >> 16) & 0xFFFF) * 4; + + ate_ver = tegra30_fuse_readl(FUSE_TEST_PROG_VER); + pr_debug("Tegra ATE prog ver %d.%d\n", ate_ver/10, ate_ver%10); + + if (ate_ver >= 26) { + bit_minus1 = tegra30_spare_fuse(LP_SPEEDO_BIT_MINUS1); + bit_minus1 |= tegra30_spare_fuse(LP_SPEEDO_BIT_MINUS1_R); + bit_minus2 = tegra30_spare_fuse(LP_SPEEDO_BIT_MINUS2); + bit_minus2 |= tegra30_spare_fuse(LP_SPEEDO_BIT_MINUS2_R); + *speedo_lp |= (bit_minus1 << 1) | bit_minus2; + + bit_minus1 = tegra30_spare_fuse(G_SPEEDO_BIT_MINUS1); + bit_minus1 |= tegra30_spare_fuse(G_SPEEDO_BIT_MINUS1_R); + bit_minus2 = tegra30_spare_fuse(G_SPEEDO_BIT_MINUS2); + bit_minus2 |= tegra30_spare_fuse(G_SPEEDO_BIT_MINUS2_R); + *speedo_g |= (bit_minus1 << 1) | bit_minus2; + } else { + *speedo_lp |= 0x3; + *speedo_g |= 0x3; + } +} + +static void __init rev_sku_to_speedo_ids(struct tegra_sku_info *sku_info) +{ + int package_id = tegra30_fuse_readl(FUSE_PACKAGE_INFO) & 0x0F; + + switch (sku_info->revision) { + case TEGRA_REVISION_A01: + sku_info->cpu_speedo_id = 0; + sku_info->soc_speedo_id = 0; + threshold_index = THRESHOLD_INDEX_0; + break; + case TEGRA_REVISION_A02: + case TEGRA_REVISION_A03: + switch (sku_info->sku_id) { + case 0x87: + case 0x82: + sku_info->cpu_speedo_id = 1; + sku_info->soc_speedo_id = 1; + threshold_index = THRESHOLD_INDEX_1; + break; + case 0x81: + switch (package_id) { + case 1: + sku_info->cpu_speedo_id = 2; + sku_info->soc_speedo_id = 2; + threshold_index = THRESHOLD_INDEX_2; + break; + case 2: + sku_info->cpu_speedo_id = 4; + sku_info->soc_speedo_id = 1; + threshold_index = THRESHOLD_INDEX_7; + break; + default: + pr_err("Tegra Unknown pkg %d\n", package_id); + break; + } + break; + case 0x80: + switch (package_id) { + case 1: + sku_info->cpu_speedo_id = 5; + sku_info->soc_speedo_id = 2; + threshold_index = THRESHOLD_INDEX_8; + break; + case 2: + sku_info->cpu_speedo_id = 6; + sku_info->soc_speedo_id = 2; + threshold_index = THRESHOLD_INDEX_9; + break; + default: + pr_err("Tegra Unknown pkg %d\n", package_id); + break; + } + break; + case 0x83: + switch (package_id) { + case 1: + sku_info->cpu_speedo_id = 7; + sku_info->soc_speedo_id = 1; + threshold_index = THRESHOLD_INDEX_10; + break; + case 2: + sku_info->cpu_speedo_id = 3; + sku_info->soc_speedo_id = 2; + threshold_index = THRESHOLD_INDEX_3; + break; + default: + pr_err("Tegra Unknown pkg %d\n", package_id); + break; + } + break; + case 0x8F: + sku_info->cpu_speedo_id = 8; + sku_info->soc_speedo_id = 1; + threshold_index = THRESHOLD_INDEX_11; + break; + case 0x08: + sku_info->cpu_speedo_id = 1; + sku_info->soc_speedo_id = 1; + threshold_index = THRESHOLD_INDEX_4; + break; + case 0x02: + sku_info->cpu_speedo_id = 2; + sku_info->soc_speedo_id = 2; + threshold_index = THRESHOLD_INDEX_5; + break; + case 0x04: + sku_info->cpu_speedo_id = 3; + sku_info->soc_speedo_id = 2; + threshold_index = THRESHOLD_INDEX_6; + break; + case 0: + switch (package_id) { + case 1: + sku_info->cpu_speedo_id = 2; + sku_info->soc_speedo_id = 2; + threshold_index = THRESHOLD_INDEX_2; + break; + case 2: + sku_info->cpu_speedo_id = 3; + sku_info->soc_speedo_id = 2; + threshold_index = THRESHOLD_INDEX_3; + break; + default: + pr_err("Tegra Unknown pkg %d\n", package_id); + break; + } + break; + default: + pr_warn("Tegra Unknown SKU %d\n", sku_info->sku_id); + sku_info->cpu_speedo_id = 0; + sku_info->soc_speedo_id = 0; + threshold_index = THRESHOLD_INDEX_0; + break; + } + break; + default: + pr_warn("Tegra Unknown chip rev %d\n", sku_info->revision); + sku_info->cpu_speedo_id = 0; + sku_info->soc_speedo_id = 0; + threshold_index = THRESHOLD_INDEX_0; + break; + } +} + +void __init tegra30_init_speedo_data(struct tegra_sku_info *sku_info) +{ + u32 cpu_speedo_val; + u32 core_speedo_val; + int i; + + BUILD_BUG_ON(ARRAY_SIZE(cpu_process_speedos) != + THRESHOLD_INDEX_COUNT); + BUILD_BUG_ON(ARRAY_SIZE(core_process_speedos) != + THRESHOLD_INDEX_COUNT); + + + rev_sku_to_speedo_ids(sku_info); + fuse_speedo_calib(&cpu_speedo_val, &core_speedo_val); + pr_debug("Tegra CPU speedo value %u\n", cpu_speedo_val); + pr_debug("Tegra Core speedo value %u\n", core_speedo_val); + + for (i = 0; i < CPU_PROCESS_CORNERS; i++) { + if (cpu_speedo_val < cpu_process_speedos[threshold_index][i]) + break; + } + sku_info->cpu_process_id = i - 1; + + if (sku_info->cpu_process_id == -1) { + pr_warn("Tegra CPU speedo value %3d out of range", + cpu_speedo_val); + sku_info->cpu_process_id = 0; + sku_info->cpu_speedo_id = 1; + } + + for (i = 0; i < CORE_PROCESS_CORNERS; i++) { + if (core_speedo_val < core_process_speedos[threshold_index][i]) + break; + } + sku_info->core_process_id = i - 1; + + if (sku_info->core_process_id == -1) { + pr_warn("Tegra CORE speedo value %3d out of range", + core_speedo_val); + sku_info->core_process_id = 0; + sku_info->soc_speedo_id = 1; + } +} diff --git a/drivers/soc/tegra/fuse/tegra-apbmisc.c b/drivers/soc/tegra/fuse/tegra-apbmisc.c new file mode 100644 index 000000000000..bfc1d54ac4ad --- /dev/null +++ b/drivers/soc/tegra/fuse/tegra-apbmisc.c @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2014, 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, see . + * + */ + +#include +#include +#include +#include + +#include + +#include "fuse.h" + +#define APBMISC_BASE 0x70000800 +#define APBMISC_SIZE 0x64 +#define FUSE_SKU_INFO 0x10 + +static void __iomem *apbmisc_base; +static void __iomem *strapping_base; + +u32 tegra_read_chipid(void) +{ + return readl_relaxed(apbmisc_base + 4); +} + +u8 tegra_get_chip_id(void) +{ + u32 id = tegra_read_chipid(); + + return (id >> 8) & 0xff; +} + +u32 tegra_read_straps(void) +{ + if (strapping_base) + return readl_relaxed(strapping_base); + else + return 0; +} + +static const struct of_device_id apbmisc_match[] __initconst = { + { .compatible = "nvidia,tegra20-apbmisc", }, + {}, +}; + +void __init tegra_init_revision(void) +{ + u32 id, chip_id, minor_rev; + int rev; + + id = tegra_read_chipid(); + chip_id = (id >> 8) & 0xff; + minor_rev = (id >> 16) & 0xf; + + switch (minor_rev) { + case 1: + rev = TEGRA_REVISION_A01; + break; + case 2: + rev = TEGRA_REVISION_A02; + break; + case 3: + if (chip_id == TEGRA20 && (tegra20_spare_fuse_early(18) || + tegra20_spare_fuse_early(19))) + rev = TEGRA_REVISION_A03p; + else + rev = TEGRA_REVISION_A03; + break; + case 4: + rev = TEGRA_REVISION_A04; + break; + default: + rev = TEGRA_REVISION_UNKNOWN; + } + + tegra_sku_info.revision = rev; + + if (chip_id == TEGRA20) + tegra_sku_info.sku_id = tegra20_fuse_early(FUSE_SKU_INFO); + else + tegra_sku_info.sku_id = tegra30_fuse_readl(FUSE_SKU_INFO); +} + +void __init tegra_init_apbmisc(void) +{ + struct device_node *np; + + np = of_find_matching_node(NULL, apbmisc_match); + apbmisc_base = of_iomap(np, 0); + if (!apbmisc_base) { + pr_warn("ioremap tegra apbmisc failed. using %08x instead\n", + APBMISC_BASE); + apbmisc_base = ioremap(APBMISC_BASE, APBMISC_SIZE); + } + + strapping_base = of_iomap(np, 1); + if (!strapping_base) + pr_err("ioremap tegra strapping_base failed\n"); +} diff --git a/include/soc/tegra/fuse.h b/include/soc/tegra/fuse.h index 822eb348e107..51ac804deba5 100644 --- a/include/soc/tegra/fuse.h +++ b/include/soc/tegra/fuse.h @@ -22,6 +22,9 @@ #define TEGRA114 0x35 #define TEGRA124 0x40 +#define TEGRA_FUSE_SKU_CALIB_0 0xf0 +#define TEGRA30_FUSE_SATA_CALIB 0x124 + #ifndef __ASSEMBLY__ u32 tegra_read_chipid(void); @@ -37,11 +40,26 @@ enum tegra_revision { TEGRA_REVISION_MAX, }; +struct tegra_sku_info { + int sku_id; + int cpu_process_id; + int cpu_speedo_id; + int cpu_speedo_value; + int cpu_iddq_value; + int core_process_id; + int soc_speedo_id; + int gpu_speedo_id; + int gpu_process_id; + int gpu_speedo_value; + enum tegra_revision revision; +}; + u32 tegra_read_straps(void); u32 tegra_read_chipid(void); void tegra_init_fuse(void); +int tegra_fuse_readl(unsigned long offset, u32 *value); -extern enum tegra_revision tegra_revision; +extern struct tegra_sku_info tegra_sku_info; #if defined(CONFIG_TEGRA20_APB_DMA) int tegra_apb_readl_using_dma(unsigned long offset, u32 *value); -- cgit v1.2.3 From 0d827a4343b596b71a1741328c4e5687ce654e19 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Thu, 12 Jun 2014 18:36:40 +0300 Subject: soc/tegra: fuse: move APB DMA into Tegra20 fuse driver The Tegra20 fuse driver is the only user of tegra_apb_readl_using_dma(). Therefore we can simply the code by incorporating the APB DMA handling into the driver directly. tegra_apb_writel_using_dma() is dropped because there are no users. Signed-off-by: Peter De Schrijver Signed-off-by: Stephen Warren Signed-off-by: Thierry Reding --- arch/arm/mach-tegra/Makefile | 1 - arch/arm/mach-tegra/apbio.c | 217 ---------------------------------- arch/arm/mach-tegra/apbio.h | 22 ---- arch/arm/mach-tegra/tegra.c | 2 - drivers/soc/tegra/fuse/fuse-tegra20.c | 79 ++++++++++++- include/soc/tegra/fuse.h | 14 --- 6 files changed, 76 insertions(+), 259 deletions(-) delete mode 100644 arch/arm/mach-tegra/apbio.c delete mode 100644 arch/arm/mach-tegra/apbio.h (limited to 'include') diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index e8601bb56f98..c303b55de22e 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -5,7 +5,6 @@ obj-y += irq.o obj-y += pmc.o obj-y += flowctrl.o obj-y += powergate.o -obj-y += apbio.o obj-y += pm.o obj-y += reset.o obj-y += reset-handler.o diff --git a/arch/arm/mach-tegra/apbio.c b/arch/arm/mach-tegra/apbio.c deleted file mode 100644 index f2488722c79c..000000000000 --- a/arch/arm/mach-tegra/apbio.c +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright (C) 2010 NVIDIA Corporation. - * Copyright (C) 2010 Google, Inc. - * - * 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. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "apbio.h" -#include "iomap.h" - -#if defined(CONFIG_TEGRA20_APB_DMA) -static DEFINE_MUTEX(tegra_apb_dma_lock); -static u32 *tegra_apb_bb; -static dma_addr_t tegra_apb_bb_phys; -static DECLARE_COMPLETION(tegra_apb_wait); - -static int tegra_apb_readl_direct(unsigned long offset, u32 *value); -static int tegra_apb_writel_direct(u32 value, unsigned long offset); - -static struct dma_chan *tegra_apb_dma_chan; -static struct dma_slave_config dma_sconfig; - -static bool tegra_apb_dma_init(void) -{ - dma_cap_mask_t mask; - - mutex_lock(&tegra_apb_dma_lock); - - /* Check to see if we raced to setup */ - if (tegra_apb_dma_chan) - goto skip_init; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - tegra_apb_dma_chan = dma_request_channel(mask, NULL, NULL); - if (!tegra_apb_dma_chan) { - /* - * This is common until the device is probed, so don't - * shout about it. - */ - pr_debug("%s: can not allocate dma channel\n", __func__); - goto err_dma_alloc; - } - - tegra_apb_bb = dma_alloc_coherent(NULL, sizeof(u32), - &tegra_apb_bb_phys, GFP_KERNEL); - if (!tegra_apb_bb) { - pr_err("%s: can not allocate bounce buffer\n", __func__); - goto err_buff_alloc; - } - - dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - dma_sconfig.src_maxburst = 1; - dma_sconfig.dst_maxburst = 1; - -skip_init: - mutex_unlock(&tegra_apb_dma_lock); - return true; - -err_buff_alloc: - dma_release_channel(tegra_apb_dma_chan); - tegra_apb_dma_chan = NULL; - -err_dma_alloc: - mutex_unlock(&tegra_apb_dma_lock); - return false; -} - -static void apb_dma_complete(void *args) -{ - complete(&tegra_apb_wait); -} - -static int do_dma_transfer(unsigned long apb_add, - enum dma_transfer_direction dir) -{ - struct dma_async_tx_descriptor *dma_desc; - int ret; - - if (dir == DMA_DEV_TO_MEM) - dma_sconfig.src_addr = apb_add; - else - dma_sconfig.dst_addr = apb_add; - - ret = dmaengine_slave_config(tegra_apb_dma_chan, &dma_sconfig); - if (ret) - return ret; - - dma_desc = dmaengine_prep_slave_single(tegra_apb_dma_chan, - tegra_apb_bb_phys, sizeof(u32), dir, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!dma_desc) - return -EINVAL; - - dma_desc->callback = apb_dma_complete; - dma_desc->callback_param = NULL; - - reinit_completion(&tegra_apb_wait); - - dmaengine_submit(dma_desc); - dma_async_issue_pending(tegra_apb_dma_chan); - ret = wait_for_completion_timeout(&tegra_apb_wait, - msecs_to_jiffies(50)); - - if (WARN(ret == 0, "apb read dma timed out")) { - dmaengine_terminate_all(tegra_apb_dma_chan); - return -EFAULT; - } - return 0; -} - -int tegra_apb_readl_using_dma(unsigned long offset, u32 *value) -{ - int ret; - - if (!tegra_apb_dma_chan && !tegra_apb_dma_init()) - return tegra_apb_readl_direct(offset, value); - - mutex_lock(&tegra_apb_dma_lock); - ret = do_dma_transfer(offset, DMA_DEV_TO_MEM); - if (ret < 0) - pr_err("error in reading offset 0x%08lx using dma\n", offset); - else - *value = *tegra_apb_bb; - - mutex_unlock(&tegra_apb_dma_lock); - - return ret; -} - -int tegra_apb_writel_using_dma(u32 value, unsigned long offset) -{ - int ret; - - if (!tegra_apb_dma_chan && !tegra_apb_dma_init()) - return tegra_apb_writel_direct(value, offset); - - mutex_lock(&tegra_apb_dma_lock); - *((u32 *)tegra_apb_bb) = value; - ret = do_dma_transfer(offset, DMA_MEM_TO_DEV); - mutex_unlock(&tegra_apb_dma_lock); - if (ret < 0) - pr_err("error in writing offset 0x%08lx using dma\n", offset); - - return ret; -} -#else -#define tegra_apb_readl_using_dma tegra_apb_readl_direct -#define tegra_apb_writel_using_dma tegra_apb_writel_direct -#endif - -typedef int (*apbio_read_fptr)(unsigned long offset, u32 *value); -typedef int (*apbio_write_fptr)(u32 value, unsigned long offset); - -static apbio_read_fptr apbio_read; -static apbio_write_fptr apbio_write; - -static int tegra_apb_readl_direct(unsigned long offset, u32 *value) -{ - *value = readl(IO_ADDRESS(offset)); - - return 0; -} - -static int tegra_apb_writel_direct(u32 value, unsigned long offset) -{ - writel(value, IO_ADDRESS(offset)); - - return 0; -} - -void tegra_apb_io_init(void) -{ - /* Need to use dma only when it is Tegra20 based platform */ - if (of_machine_is_compatible("nvidia,tegra20") || - !of_have_populated_dt()) { - apbio_read = tegra_apb_readl_using_dma; - apbio_write = tegra_apb_writel_using_dma; - } else { - apbio_read = tegra_apb_readl_direct; - apbio_write = tegra_apb_writel_direct; - } -} - -u32 tegra_apb_readl(unsigned long offset) -{ - u32 val; - - if (apbio_read(offset, &val) < 0) - return 0; - else - return val; -} - -void tegra_apb_writel(u32 value, unsigned long offset) -{ - apbio_write(value, offset); -} diff --git a/arch/arm/mach-tegra/apbio.h b/arch/arm/mach-tegra/apbio.h deleted file mode 100644 index f05d71c303c7..000000000000 --- a/arch/arm/mach-tegra/apbio.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2010 NVIDIA Corporation. - * Copyright (C) 2010 Google, Inc. - * - * 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 __MACH_TEGRA_APBIO_H -#define __MACH_TEGRA_APBIO_H - -void tegra_apb_io_init(void); -u32 tegra_apb_readl(unsigned long offset); -void tegra_apb_writel(u32 value, unsigned long offset); -#endif diff --git a/arch/arm/mach-tegra/tegra.c b/arch/arm/mach-tegra/tegra.c index c0faae8c0822..6ccbc8ca1db5 100644 --- a/arch/arm/mach-tegra/tegra.c +++ b/arch/arm/mach-tegra/tegra.c @@ -44,7 +44,6 @@ #include #include -#include "apbio.h" #include "board.h" #include "common.h" #include "cpuidle.h" @@ -74,7 +73,6 @@ u32 tegra_uart_config[3] = { static void __init tegra_init_early(void) { of_register_trusted_foundations(); - tegra_apb_io_init(); tegra_init_fuse(); tegra_cpu_reset_handler_init(); tegra_powergate_init(); diff --git a/drivers/soc/tegra/fuse/fuse-tegra20.c b/drivers/soc/tegra/fuse/fuse-tegra20.c index 7a9d0e045490..7cb63ab6aac2 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra20.c +++ b/drivers/soc/tegra/fuse/fuse-tegra20.c @@ -18,6 +18,9 @@ #include #include +#include +#include +#include #include #include #include @@ -39,18 +42,58 @@ static phys_addr_t fuse_phys; static struct clk *fuse_clk; static void __iomem __initdata *fuse_base; +static DEFINE_MUTEX(apb_dma_lock); +static DECLARE_COMPLETION(apb_dma_wait); +static struct dma_chan *apb_dma_chan; +static struct dma_slave_config dma_sconfig; +static u32 *apb_buffer; +static dma_addr_t apb_buffer_phys; + +static void apb_dma_complete(void *args) +{ + complete(&apb_dma_wait); +} + static u32 tegra20_fuse_readl(const unsigned int offset) { int ret; - u32 val; + u32 val = 0; + struct dma_async_tx_descriptor *dma_desc; + + mutex_lock(&apb_dma_lock); + + dma_sconfig.src_addr = fuse_phys + FUSE_BEGIN + offset; + ret = dmaengine_slave_config(apb_dma_chan, &dma_sconfig); + if (ret) + goto out; + + dma_desc = dmaengine_prep_slave_single(apb_dma_chan, apb_buffer_phys, + sizeof(u32), DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!dma_desc) + goto out; + + dma_desc->callback = apb_dma_complete; + dma_desc->callback_param = NULL; + + reinit_completion(&apb_dma_wait); clk_prepare_enable(fuse_clk); - ret = tegra_apb_readl_using_dma(fuse_phys + FUSE_BEGIN + offset, &val); + dmaengine_submit(dma_desc); + dma_async_issue_pending(apb_dma_chan); + ret = wait_for_completion_timeout(&apb_dma_wait, msecs_to_jiffies(50)); + + if (WARN(ret == 0, "apb read dma timed out")) + dmaengine_terminate_all(apb_dma_chan); + else + val = *apb_buffer; clk_disable_unprepare(fuse_clk); +out: + mutex_unlock(&apb_dma_lock); - return (ret < 0) ? 0 : val; + return val; } static const struct of_device_id tegra20_fuse_of_match[] = { @@ -58,9 +101,35 @@ static const struct of_device_id tegra20_fuse_of_match[] = { {}, }; +static int apb_dma_init(void) +{ + dma_cap_mask_t mask; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + apb_dma_chan = dma_request_channel(mask, NULL, NULL); + if (!apb_dma_chan) + return -EPROBE_DEFER; + + apb_buffer = dma_alloc_coherent(NULL, sizeof(u32), &apb_buffer_phys, + GFP_KERNEL); + if (!apb_buffer) { + dma_release_channel(apb_dma_chan); + return -ENOMEM; + } + + dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dma_sconfig.src_maxburst = 1; + dma_sconfig.dst_maxburst = 1; + + return 0; +} + static int tegra20_fuse_probe(struct platform_device *pdev) { struct resource *res; + int err; fuse_clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(fuse_clk)) { @@ -73,6 +142,10 @@ static int tegra20_fuse_probe(struct platform_device *pdev) return -EINVAL; fuse_phys = res->start; + err = apb_dma_init(); + if (err) + return err; + if (tegra_fuse_create_sysfs(&pdev->dev, FUSE_SIZE, tegra20_fuse_readl)) return -ENODEV; diff --git a/include/soc/tegra/fuse.h b/include/soc/tegra/fuse.h index 51ac804deba5..738712d75cfe 100644 --- a/include/soc/tegra/fuse.h +++ b/include/soc/tegra/fuse.h @@ -61,20 +61,6 @@ int tegra_fuse_readl(unsigned long offset, u32 *value); extern struct tegra_sku_info tegra_sku_info; -#if defined(CONFIG_TEGRA20_APB_DMA) -int tegra_apb_readl_using_dma(unsigned long offset, u32 *value); -int tegra_apb_writel_using_dma(u32 value, unsigned long offset); -#else -static inline int tegra_apb_readl_using_dma(unsigned long offset, u32 *value) -{ - return -EINVAL; -} -static inline int tegra_apb_writel_using_dma(u32 value, unsigned long offset) -{ - return -EINVAL; -} -#endif /* CONFIG_TEGRA20_APB_DMA */ - #endif /* __ASSEMBLY__ */ #endif /* __SOC_TEGRA_FUSE_H__ */ -- cgit v1.2.3 From e7cafc45258c852c5176cd421615846e79a3d307 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 17 Jul 2014 15:35:38 +0300 Subject: Bluetooth: Pass initiator/acceptor information to hci_conn_security() We're interested in whether an authentication request is because of a remote or local action. So far hci_conn_security() has been used both for incoming and outgoing actions (e.g. RFCOMM or L2CAP connect requests) so without some modifications it cannot know which peer is responsible for requesting authentication. This patch adds a new "bool initiator" parameter to hci_conn_security() to indicate which side is responsible for the request and updates the current users to pass this information correspondingly. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 3 ++- include/net/bluetooth/l2cap.h | 2 +- net/bluetooth/hci_conn.c | 3 ++- net/bluetooth/l2cap_core.c | 15 ++++++++------- net/bluetooth/l2cap_sock.c | 2 +- net/bluetooth/mgmt.c | 2 +- net/bluetooth/rfcomm/core.c | 3 ++- 7 files changed, 17 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 73e16ecfd6b9..eb2b9c9e3480 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -720,7 +720,8 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst, __u16 setting); int hci_conn_check_link_mode(struct hci_conn *conn); int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level); -int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type); +int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type, + bool initiator); int hci_conn_change_link_key(struct hci_conn *conn); int hci_conn_switch_role(struct hci_conn *conn, __u8 role); diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 1fffd92808b0..8df15ad0d43f 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -905,7 +905,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *dst, u8 dst_type); 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); +int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator); 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); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index ad5f0b819e90..76c5a38e5997 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -973,7 +973,8 @@ 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) +int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type, + bool initiator) { BT_DBG("hcon %p", conn); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index d0f36336b6ce..c8c259f21d80 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -775,7 +775,7 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan) } /* Service level security */ -int l2cap_chan_check_security(struct l2cap_chan *chan) +int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator) { struct l2cap_conn *conn = chan->conn; __u8 auth_type; @@ -785,7 +785,8 @@ int l2cap_chan_check_security(struct l2cap_chan *chan) auth_type = l2cap_get_auth_type(chan); - return hci_conn_security(conn->hcon, chan->sec_level, auth_type); + return hci_conn_security(conn->hcon, chan->sec_level, auth_type, + initiator); } static u8 l2cap_get_ident(struct l2cap_conn *conn) @@ -1278,7 +1279,7 @@ static void l2cap_do_start(struct l2cap_chan *chan) if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE)) return; - if (l2cap_chan_check_security(chan) && + if (l2cap_chan_check_security(chan, true) && __l2cap_no_conn_pending(chan)) { l2cap_start_connection(chan); } @@ -1357,7 +1358,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) } if (chan->state == BT_CONNECT) { - if (!l2cap_chan_check_security(chan) || + if (!l2cap_chan_check_security(chan, true) || !__l2cap_no_conn_pending(chan)) { l2cap_chan_unlock(chan); continue; @@ -1379,7 +1380,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) rsp.scid = cpu_to_le16(chan->dcid); rsp.dcid = cpu_to_le16(chan->scid); - if (l2cap_chan_check_security(chan)) { + if (l2cap_chan_check_security(chan, false)) { if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) { rsp.result = cpu_to_le16(L2CAP_CR_PEND); rsp.status = cpu_to_le16(L2CAP_CS_AUTHOR_PEND); @@ -3849,7 +3850,7 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn, chan->ident = cmd->ident; if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) { - if (l2cap_chan_check_security(chan)) { + if (l2cap_chan_check_security(chan, false)) { if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) { l2cap_state_change(chan, BT_CONNECT2); result = L2CAP_CR_PEND; @@ -7191,7 +7192,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, if (hcon->state == BT_CONNECTED) { if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { __clear_chan_timer(chan); - if (l2cap_chan_check_security(chan)) + if (l2cap_chan_check_security(chan, true)) l2cap_state_change(chan, BT_CONNECTED); } else l2cap_do_start(chan); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 0bc67dc5dcce..3bb1cdf34f07 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -797,7 +797,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, } else if ((sk->sk_state == BT_CONNECT2 && test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) || sk->sk_state == BT_CONNECTED) { - if (!l2cap_chan_check_security(chan)) + if (!l2cap_chan_check_security(chan, true)) set_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags); else sk->sk_state_change(sk); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index b981bfb87f86..190668367e42 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3202,7 +3202,7 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, cmd->user_data = conn; if (conn->state == BT_CONNECTED && - hci_conn_security(conn, sec_level, auth_type)) + hci_conn_security(conn, sec_level, auth_type, true)) pairing_complete(cmd, 0); err = 0; diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 754b6fe4f742..a0690a84f3e9 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -227,7 +227,8 @@ static int rfcomm_check_security(struct rfcomm_dlc *d) break; } - return hci_conn_security(conn->hcon, d->sec_level, auth_type); + return hci_conn_security(conn->hcon, d->sec_level, auth_type, + d->out); } static void rfcomm_session_timeout(unsigned long arg) -- cgit v1.2.3 From 977f8fce0279e5f96dc5c5068610d60b9ae94802 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 17 Jul 2014 15:35:39 +0300 Subject: Bluetooth: Introduce a flag to track who really initiates authentication Even though our side requests authentication, the original action that caused it may be remotely triggered, such as an incoming L2CAP or RFCOMM connect request. To track this information introduce a new hci_conn flag called HCI_CONN_AUTH_INITIATOR. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_conn.c | 3 +++ net/bluetooth/hci_event.c | 5 +++++ 3 files changed, 9 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index eb2b9c9e3480..996ed065b6c2 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -547,6 +547,7 @@ enum { HCI_CONN_SECURE, HCI_CONN_FIPS, HCI_CONN_STK_ENCRYPT, + HCI_CONN_AUTH_INITIATOR, }; static inline bool hci_conn_ssp_enabled(struct hci_conn *conn) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 76c5a38e5997..0d76054efd26 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -1027,6 +1027,9 @@ auth: if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) return 0; + if (initiator) + set_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags); + if (!hci_conn_auth(conn, sec_level, auth_type)) return 0; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 495d6d5c1146..af2cdca03d73 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1645,6 +1645,8 @@ static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status) if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) { struct hci_cp_auth_requested auth_cp; + set_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags); + auth_cp.handle = __cpu_to_le16(conn->handle); hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, sizeof(auth_cp), &auth_cp); @@ -2387,6 +2389,9 @@ check_auth: if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) { struct hci_cp_auth_requested cp; + + set_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags); + cp.handle = __cpu_to_le16(conn->handle); hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp); } -- cgit v1.2.3 From a2686766c81e18fb1ab9375cf5d3cbd54a3bed2c Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 16 Jul 2014 14:01:44 +0200 Subject: soc/tegra: Implement runtime check for Tegra SoCs Subsequent patches will move some of the initialization code from SoC setup code to regular initcalls. To prevent breakage on other SoCs in multi-platform builds, these initcalls need to check that they indeed run on Tegra. Signed-off-by: Thierry Reding --- drivers/soc/tegra/Makefile | 2 ++ drivers/soc/tegra/common.c | 30 ++++++++++++++++++++++++++++++ include/soc/tegra/common.h | 14 ++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 drivers/soc/tegra/common.c create mode 100644 include/soc/tegra/common.h (limited to 'include') diff --git a/drivers/soc/tegra/Makefile b/drivers/soc/tegra/Makefile index 236600f91bb3..db34987ed9dc 100644 --- a/drivers/soc/tegra/Makefile +++ b/drivers/soc/tegra/Makefile @@ -1 +1,3 @@ obj-$(CONFIG_ARCH_TEGRA) += fuse/ + +obj-$(CONFIG_ARCH_TEGRA) += common.o diff --git a/drivers/soc/tegra/common.c b/drivers/soc/tegra/common.c new file mode 100644 index 000000000000..a71cb74f3674 --- /dev/null +++ b/drivers/soc/tegra/common.c @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved. + * + * 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 + +static const struct of_device_id tegra_machine_match[] = { + { .compatible = "nvidia,tegra20", }, + { .compatible = "nvidia,tegra30", }, + { .compatible = "nvidia,tegra114", }, + { .compatible = "nvidia,tegra124", }, + { } +}; + +bool soc_is_tegra(void) +{ + struct device_node *root; + + root = of_find_node_by_path("/"); + if (!root) + return false; + + return of_match_node(tegra_machine_match, root) != NULL; +} diff --git a/include/soc/tegra/common.h b/include/soc/tegra/common.h new file mode 100644 index 000000000000..fc13a9a134e9 --- /dev/null +++ b/include/soc/tegra/common.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2014 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __SOC_TEGRA_COMMON_H__ +#define __SOC_TEGRA_COMMON_H__ + +bool soc_is_tegra(void); + +#endif /* __SOC_TEGRA_COMMON_H__ */ -- cgit v1.2.3 From 24fa5af81059af90c723bec6aacc3cd2b2809d14 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 11 Jul 2014 11:13:30 +0200 Subject: soc/tegra: fuse: Set up in early initcall Rather than rely on explicit initialization order called from SoC setup code, use a plain initcall and rely on initcall ordering to take care of dependencies. This driver exposes some functionality (querying the chip ID) needed at very early stages of the boot process. An early initcall is good enough provided that some of the dependencies are deferred to later stages. To make sure any abuses are easily caught, output a warning message if the chip ID is queried while it can't be read yet. Signed-off-by: Thierry Reding --- arch/arm/mach-tegra/tegra.c | 1 - drivers/soc/tegra/fuse/fuse-tegra.c | 11 +++++++++-- drivers/soc/tegra/fuse/tegra-apbmisc.c | 7 +++++-- include/soc/tegra/fuse.h | 1 - 4 files changed, 14 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-tegra/tegra.c b/arch/arm/mach-tegra/tegra.c index e5733fa78911..c9176db0b9c0 100644 --- a/arch/arm/mach-tegra/tegra.c +++ b/arch/arm/mach-tegra/tegra.c @@ -73,7 +73,6 @@ u32 tegra_uart_config[3] = { static void __init tegra_init_early(void) { of_register_trusted_foundations(); - tegra_init_fuse(); tegra_cpu_reset_handler_init(); tegra_powergate_init(); } diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c index 03742edcfe83..11a5043959dc 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra.c +++ b/drivers/soc/tegra/fuse/fuse-tegra.c @@ -23,6 +23,7 @@ #include #include +#include #include #include "fuse.h" @@ -125,11 +126,14 @@ int tegra_fuse_create_sysfs(struct device *dev, int size, return device_create_bin_file(dev, &fuse_bin_attr); } -void __init tegra_init_fuse(void) +static int __init tegra_init_fuse(void) { struct device_node *np; void __iomem *car_base; + if (!soc_is_tegra()) + return 0; + tegra_init_apbmisc(); np = of_find_matching_node(NULL, car_match); @@ -139,7 +143,7 @@ void __init tegra_init_fuse(void) iounmap(car_base); } else { pr_err("Could not enable fuse clk. ioremap tegra car failed.\n"); - return; + return -ENXIO; } if (tegra_get_chip_id() == TEGRA20) @@ -153,4 +157,7 @@ void __init tegra_init_fuse(void) tegra_sku_info.core_process_id); pr_debug("Tegra CPU Speedo ID %d, Soc Speedo ID %d\n", tegra_sku_info.cpu_speedo_id, tegra_sku_info.soc_speedo_id); + + return 0; } +early_initcall(tegra_init_fuse); diff --git a/drivers/soc/tegra/fuse/tegra-apbmisc.c b/drivers/soc/tegra/fuse/tegra-apbmisc.c index bfc1d54ac4ad..3bf5aba4caaa 100644 --- a/drivers/soc/tegra/fuse/tegra-apbmisc.c +++ b/drivers/soc/tegra/fuse/tegra-apbmisc.c @@ -38,9 +38,12 @@ u32 tegra_read_chipid(void) u8 tegra_get_chip_id(void) { - u32 id = tegra_read_chipid(); + if (!apbmisc_base) { + WARN(1, "Tegra Chip ID not yet available\n"); + return 0; + } - return (id >> 8) & 0xff; + return (tegra_read_chipid() >> 8) & 0xff; } u32 tegra_read_straps(void) diff --git a/include/soc/tegra/fuse.h b/include/soc/tegra/fuse.h index 738712d75cfe..8e1249474e84 100644 --- a/include/soc/tegra/fuse.h +++ b/include/soc/tegra/fuse.h @@ -56,7 +56,6 @@ struct tegra_sku_info { u32 tegra_read_straps(void); u32 tegra_read_chipid(void); -void tegra_init_fuse(void); int tegra_fuse_readl(unsigned long offset, u32 *value); extern struct tegra_sku_info tegra_sku_info; -- cgit v1.2.3 From 7232398abc6a7186e315425638c367d50c674718 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 11 Jul 2014 13:19:06 +0200 Subject: ARM: tegra: Convert PMC to a driver This commit converts the PMC support code to a platform driver. Because the boot process needs to call into this driver very early, also set up a minimal environment via an early initcall. Signed-off-by: Thierry Reding --- arch/arm/mach-tegra/Makefile | 2 - arch/arm/mach-tegra/board.h | 7 - arch/arm/mach-tegra/platsmp.c | 2 +- arch/arm/mach-tegra/pm.c | 32 +- arch/arm/mach-tegra/pm.h | 10 +- arch/arm/mach-tegra/pmc.c | 414 ----------------- arch/arm/mach-tegra/pmc.h | 49 -- arch/arm/mach-tegra/powergate.c | 516 ---------------------- arch/arm/mach-tegra/tegra.c | 7 +- drivers/clk/tegra/clk-tegra30.c | 2 +- drivers/gpu/drm/tegra/gr3d.c | 2 +- drivers/gpu/drm/tegra/sor.c | 2 +- drivers/pci/host/pci-tegra.c | 2 +- drivers/soc/tegra/Makefile | 1 + drivers/soc/tegra/pmc.c | 957 ++++++++++++++++++++++++++++++++++++++++ include/soc/tegra/pm.h | 38 ++ include/soc/tegra/pmc.h | 157 +++++++ include/soc/tegra/powergate.h | 134 ------ 18 files changed, 1184 insertions(+), 1150 deletions(-) delete mode 100644 arch/arm/mach-tegra/pmc.c delete mode 100644 arch/arm/mach-tegra/pmc.h delete mode 100644 arch/arm/mach-tegra/powergate.c create mode 100644 drivers/soc/tegra/pmc.c create mode 100644 include/soc/tegra/pm.h create mode 100644 include/soc/tegra/pmc.h delete mode 100644 include/soc/tegra/powergate.h (limited to 'include') diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index c303b55de22e..e48a74458c25 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -2,9 +2,7 @@ asflags-y += -march=armv7-a obj-y += io.o obj-y += irq.o -obj-y += pmc.o obj-y += flowctrl.o -obj-y += powergate.o obj-y += pm.o obj-y += reset.o obj-y += reset-handler.o diff --git a/arch/arm/mach-tegra/board.h b/arch/arm/mach-tegra/board.h index bcf5dbf69d58..da90c89296b9 100644 --- a/arch/arm/mach-tegra/board.h +++ b/arch/arm/mach-tegra/board.h @@ -28,13 +28,6 @@ void __init tegra_map_common_io(void); void __init tegra_init_irq(void); -int __init tegra_powergate_init(void); -#if defined(CONFIG_ARCH_TEGRA_2x_SOC) && defined(CONFIG_DEBUG_FS) -int __init tegra_powergate_debugfs_init(void); -#else -static inline int tegra_powergate_debugfs_init(void) { return 0; } -#endif - void __init tegra_paz00_wifikill_init(void); #endif diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c index 0466a145b500..b45086666648 100644 --- a/arch/arm/mach-tegra/platsmp.c +++ b/arch/arm/mach-tegra/platsmp.c @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -31,7 +32,6 @@ #include "common.h" #include "flowctrl.h" #include "iomap.h" -#include "pmc.h" #include "reset.h" static cpumask_t tegra_cpu_init_mask; diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c index 94db3b6664df..b0f48a3946fa 100644 --- a/arch/arm/mach-tegra/pm.c +++ b/arch/arm/mach-tegra/pm.c @@ -28,6 +28,8 @@ #include #include +#include +#include #include #include @@ -38,7 +40,6 @@ #include "flowctrl.h" #include "iomap.h" -#include "pmc.h" #include "pm.h" #include "reset.h" #include "sleep.h" @@ -167,9 +168,29 @@ static int tegra_sleep_cpu(unsigned long v2p) return 0; } +static void tegra_pm_set(enum tegra_suspend_mode mode) +{ + u32 value; + + switch (tegra_get_chip_id()) { + case TEGRA20: + case TEGRA30: + break; + default: + /* Turn off CRAIL */ + value = flowctrl_read_cpu_csr(0); + value &= ~FLOW_CTRL_CSR_ENABLE_EXT_MASK; + value |= FLOW_CTRL_CSR_ENABLE_EXT_CRAIL; + flowctrl_write_cpu_csr(0, value); + break; + } + + tegra_pmc_enter_suspend_mode(mode); +} + void tegra_idle_lp2_last(void) { - tegra_pmc_pm_set(TEGRA_SUSPEND_LP2); + tegra_pm_set(TEGRA_SUSPEND_LP2); cpu_cluster_pm_enter(); suspend_cpu_complex(); @@ -268,8 +289,6 @@ static bool tegra_sleep_core_init(void) static void tegra_suspend_enter_lp1(void) { - tegra_pmc_suspend(); - /* copy the reset vector & SDRAM shutdown code into IRAM */ memcpy(iram_save_addr, IO_ADDRESS(TEGRA_IRAM_LPx_RESUME_AREA), iram_save_size); @@ -281,8 +300,6 @@ static void tegra_suspend_enter_lp1(void) static void tegra_suspend_exit_lp1(void) { - tegra_pmc_resume(); - /* restore IRAM */ memcpy(IO_ADDRESS(TEGRA_IRAM_LPx_RESUME_AREA), iram_save_addr, iram_save_size); @@ -307,7 +324,7 @@ static int tegra_suspend_enter(suspend_state_t state) pr_info("Entering suspend state %s\n", lp_state[mode]); - tegra_pmc_pm_set(mode); + tegra_pm_set(mode); local_fiq_disable(); @@ -355,7 +372,6 @@ void __init tegra_init_suspend(void) return; tegra_tear_down_cpu_init(); - tegra_pmc_suspend_init(); if (mode >= TEGRA_SUSPEND_LP1) { if (!tegra_lp1_iram_hook() || !tegra_sleep_core_init()) { diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h index f4a89698e5b0..83bc87583446 100644 --- a/arch/arm/mach-tegra/pm.h +++ b/arch/arm/mach-tegra/pm.h @@ -21,12 +21,11 @@ #ifndef _MACH_TEGRA_PM_H_ #define _MACH_TEGRA_PM_H_ -#include "pmc.h" - struct tegra_lp1_iram { void *start_addr; void *end_addr; }; + extern struct tegra_lp1_iram tegra_lp1_iram; extern void (*tegra_sleep_core_finish)(unsigned long v2p); @@ -42,15 +41,8 @@ void tegra_idle_lp2_last(void); extern void (*tegra_tear_down_cpu)(void); #ifdef CONFIG_PM_SLEEP -enum tegra_suspend_mode tegra_pm_validate_suspend_mode( - enum tegra_suspend_mode mode); void tegra_init_suspend(void); #else -static inline enum tegra_suspend_mode tegra_pm_validate_suspend_mode( - enum tegra_suspend_mode mode) -{ - return TEGRA_SUSPEND_NONE; -} static inline void tegra_init_suspend(void) {} #endif diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c deleted file mode 100644 index 69df18090c8b..000000000000 --- a/arch/arm/mach-tegra/pmc.c +++ /dev/null @@ -1,414 +0,0 @@ -/* - * Copyright (C) 2012,2013 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, see . - * - */ - -#include -#include -#include -#include -#include - -#include -#include - -#include "flowctrl.h" -#include "pm.h" -#include "pmc.h" -#include "sleep.h" - -#define TEGRA_POWER_SYSCLK_POLARITY (1 << 10) /* sys clk polarity */ -#define TEGRA_POWER_SYSCLK_OE (1 << 11) /* system clock enable */ -#define TEGRA_POWER_EFFECT_LP0 (1 << 14) /* LP0 when CPU pwr gated */ -#define TEGRA_POWER_CPU_PWRREQ_POLARITY (1 << 15) /* CPU pwr req polarity */ -#define TEGRA_POWER_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */ - -#define PMC_CTRL 0x0 -#define PMC_CTRL_INTR_LOW (1 << 17) -#define PMC_PWRGATE_TOGGLE 0x30 -#define PMC_PWRGATE_TOGGLE_START (1 << 8) -#define PMC_REMOVE_CLAMPING 0x34 -#define PMC_PWRGATE_STATUS 0x38 - -#define PMC_SCRATCH0 0x50 -#define PMC_SCRATCH0_MODE_RECOVERY (1 << 31) -#define PMC_SCRATCH0_MODE_BOOTLOADER (1 << 30) -#define PMC_SCRATCH0_MODE_RCM (1 << 1) -#define PMC_SCRATCH0_MODE_MASK (PMC_SCRATCH0_MODE_RECOVERY | \ - PMC_SCRATCH0_MODE_BOOTLOADER | \ - PMC_SCRATCH0_MODE_RCM) - -#define PMC_CPUPWRGOOD_TIMER 0xc8 -#define PMC_CPUPWROFF_TIMER 0xcc - -static u8 tegra_cpu_domains[] = { - 0xFF, /* not available for CPU0 */ - TEGRA_POWERGATE_CPU1, - TEGRA_POWERGATE_CPU2, - TEGRA_POWERGATE_CPU3, -}; -static DEFINE_SPINLOCK(tegra_powergate_lock); - -static void __iomem *tegra_pmc_base; -static bool tegra_pmc_invert_interrupt; -static struct clk *tegra_pclk; - -struct pmc_pm_data { - u32 cpu_good_time; /* CPU power good time in uS */ - u32 cpu_off_time; /* CPU power off time in uS */ - u32 core_osc_time; /* Core power good osc time in uS */ - u32 core_pmu_time; /* Core power good pmu time in uS */ - u32 core_off_time; /* Core power off time in uS */ - bool corereq_high; /* Core power request active-high */ - bool sysclkreq_high; /* System clock request active-high */ - bool combined_req; /* Combined pwr req for CPU & Core */ - bool cpu_pwr_good_en; /* CPU power good signal is enabled */ - u32 lp0_vec_phy_addr; /* The phy addr of LP0 warm boot code */ - u32 lp0_vec_size; /* The size of LP0 warm boot code */ - enum tegra_suspend_mode suspend_mode; -}; -static struct pmc_pm_data pmc_pm_data; - -static inline u32 tegra_pmc_readl(u32 reg) -{ - return readl(tegra_pmc_base + reg); -} - -static inline void tegra_pmc_writel(u32 val, u32 reg) -{ - writel(val, tegra_pmc_base + reg); -} - -static int tegra_pmc_get_cpu_powerdomain_id(int cpuid) -{ - if (cpuid <= 0 || cpuid >= num_possible_cpus()) - return -EINVAL; - return tegra_cpu_domains[cpuid]; -} - -static bool tegra_pmc_powergate_is_powered(int id) -{ - return (tegra_pmc_readl(PMC_PWRGATE_STATUS) >> id) & 1; -} - -static int tegra_pmc_powergate_set(int id, bool new_state) -{ - bool old_state; - unsigned long flags; - - spin_lock_irqsave(&tegra_powergate_lock, flags); - - old_state = tegra_pmc_powergate_is_powered(id); - WARN_ON(old_state == new_state); - - tegra_pmc_writel(PMC_PWRGATE_TOGGLE_START | id, PMC_PWRGATE_TOGGLE); - - spin_unlock_irqrestore(&tegra_powergate_lock, flags); - - return 0; -} - -static int tegra_pmc_powergate_remove_clamping(int id) -{ - u32 mask; - - /* - * Tegra has a bug where PCIE and VDE clamping masks are - * swapped relatively to the partition ids. - */ - if (id == TEGRA_POWERGATE_VDEC) - mask = (1 << TEGRA_POWERGATE_PCIE); - else if (id == TEGRA_POWERGATE_PCIE) - mask = (1 << TEGRA_POWERGATE_VDEC); - else - mask = (1 << id); - - tegra_pmc_writel(mask, PMC_REMOVE_CLAMPING); - - return 0; -} - -bool tegra_pmc_cpu_is_powered(int cpuid) -{ - int id; - - id = tegra_pmc_get_cpu_powerdomain_id(cpuid); - if (id < 0) - return false; - return tegra_pmc_powergate_is_powered(id); -} - -int tegra_pmc_cpu_power_on(int cpuid) -{ - int id; - - id = tegra_pmc_get_cpu_powerdomain_id(cpuid); - if (id < 0) - return id; - return tegra_pmc_powergate_set(id, true); -} - -int tegra_pmc_cpu_remove_clamping(int cpuid) -{ - int id; - - id = tegra_pmc_get_cpu_powerdomain_id(cpuid); - if (id < 0) - return id; - return tegra_pmc_powergate_remove_clamping(id); -} - -void tegra_pmc_restart(enum reboot_mode mode, const char *cmd) -{ - u32 val; - - val = tegra_pmc_readl(PMC_SCRATCH0); - val &= ~PMC_SCRATCH0_MODE_MASK; - - if (cmd) { - if (strcmp(cmd, "recovery") == 0) - val |= PMC_SCRATCH0_MODE_RECOVERY; - - if (strcmp(cmd, "bootloader") == 0) - val |= PMC_SCRATCH0_MODE_BOOTLOADER; - - if (strcmp(cmd, "forced-recovery") == 0) - val |= PMC_SCRATCH0_MODE_RCM; - } - - tegra_pmc_writel(val, PMC_SCRATCH0); - - val = tegra_pmc_readl(0); - val |= 0x10; - tegra_pmc_writel(val, 0); -} - -#ifdef CONFIG_PM_SLEEP -static void set_power_timers(u32 us_on, u32 us_off, unsigned long rate) -{ - unsigned long long ticks; - unsigned long long pclk; - static unsigned long tegra_last_pclk; - - if (WARN_ON_ONCE(rate <= 0)) - pclk = 100000000; - else - pclk = rate; - - if ((rate != tegra_last_pclk)) { - ticks = (us_on * pclk) + 999999ull; - do_div(ticks, 1000000); - tegra_pmc_writel((unsigned long)ticks, PMC_CPUPWRGOOD_TIMER); - - ticks = (us_off * pclk) + 999999ull; - do_div(ticks, 1000000); - tegra_pmc_writel((unsigned long)ticks, PMC_CPUPWROFF_TIMER); - wmb(); - } - tegra_last_pclk = pclk; -} - -enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void) -{ - return pmc_pm_data.suspend_mode; -} - -void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode) -{ - if (mode < TEGRA_SUSPEND_NONE || mode >= TEGRA_MAX_SUSPEND_MODE) - return; - - pmc_pm_data.suspend_mode = mode; -} - -void tegra_pmc_suspend(void) -{ - tegra_pmc_writel(virt_to_phys(tegra_resume), PMC_SCRATCH41); -} - -void tegra_pmc_resume(void) -{ - tegra_pmc_writel(0x0, PMC_SCRATCH41); -} - -void tegra_pmc_pm_set(enum tegra_suspend_mode mode) -{ - u32 reg, csr_reg; - unsigned long rate = 0; - - reg = tegra_pmc_readl(PMC_CTRL); - reg |= TEGRA_POWER_CPU_PWRREQ_OE; - reg &= ~TEGRA_POWER_EFFECT_LP0; - - switch (tegra_get_chip_id()) { - case TEGRA20: - case TEGRA30: - break; - default: - /* Turn off CRAIL */ - csr_reg = flowctrl_read_cpu_csr(0); - csr_reg &= ~FLOW_CTRL_CSR_ENABLE_EXT_MASK; - csr_reg |= FLOW_CTRL_CSR_ENABLE_EXT_CRAIL; - flowctrl_write_cpu_csr(0, csr_reg); - break; - } - - switch (mode) { - case TEGRA_SUSPEND_LP1: - rate = 32768; - break; - case TEGRA_SUSPEND_LP2: - rate = clk_get_rate(tegra_pclk); - break; - default: - break; - } - - set_power_timers(pmc_pm_data.cpu_good_time, pmc_pm_data.cpu_off_time, - rate); - - tegra_pmc_writel(reg, PMC_CTRL); -} - -void tegra_pmc_suspend_init(void) -{ - u32 reg; - - /* Always enable CPU power request */ - reg = tegra_pmc_readl(PMC_CTRL); - reg |= TEGRA_POWER_CPU_PWRREQ_OE; - tegra_pmc_writel(reg, PMC_CTRL); - - reg = tegra_pmc_readl(PMC_CTRL); - - if (!pmc_pm_data.sysclkreq_high) - reg |= TEGRA_POWER_SYSCLK_POLARITY; - else - reg &= ~TEGRA_POWER_SYSCLK_POLARITY; - - /* configure the output polarity while the request is tristated */ - tegra_pmc_writel(reg, PMC_CTRL); - - /* now enable the request */ - reg |= TEGRA_POWER_SYSCLK_OE; - tegra_pmc_writel(reg, PMC_CTRL); -} -#endif - -static const struct of_device_id matches[] __initconst = { - { .compatible = "nvidia,tegra124-pmc" }, - { .compatible = "nvidia,tegra114-pmc" }, - { .compatible = "nvidia,tegra30-pmc" }, - { .compatible = "nvidia,tegra20-pmc" }, - { } -}; - -void __init tegra_pmc_init_irq(void) -{ - struct device_node *np; - u32 val; - - np = of_find_matching_node(NULL, matches); - BUG_ON(!np); - - tegra_pmc_base = of_iomap(np, 0); - - tegra_pmc_invert_interrupt = of_property_read_bool(np, - "nvidia,invert-interrupt"); - - val = tegra_pmc_readl(PMC_CTRL); - if (tegra_pmc_invert_interrupt) - val |= PMC_CTRL_INTR_LOW; - else - val &= ~PMC_CTRL_INTR_LOW; - tegra_pmc_writel(val, PMC_CTRL); -} - -void __init tegra_pmc_init(void) -{ - struct device_node *np; - u32 prop; - enum tegra_suspend_mode suspend_mode; - u32 core_good_time[2] = {0, 0}; - u32 lp0_vec[2] = {0, 0}; - - np = of_find_matching_node(NULL, matches); - BUG_ON(!np); - - tegra_pclk = of_clk_get_by_name(np, "pclk"); - WARN_ON(IS_ERR(tegra_pclk)); - - /* Grabbing the power management configurations */ - if (of_property_read_u32(np, "nvidia,suspend-mode", &prop)) { - suspend_mode = TEGRA_SUSPEND_NONE; - } else { - switch (prop) { - case 0: - suspend_mode = TEGRA_SUSPEND_LP0; - break; - case 1: - suspend_mode = TEGRA_SUSPEND_LP1; - break; - case 2: - suspend_mode = TEGRA_SUSPEND_LP2; - break; - default: - suspend_mode = TEGRA_SUSPEND_NONE; - break; - } - } - suspend_mode = tegra_pm_validate_suspend_mode(suspend_mode); - - if (of_property_read_u32(np, "nvidia,cpu-pwr-good-time", &prop)) - suspend_mode = TEGRA_SUSPEND_NONE; - pmc_pm_data.cpu_good_time = prop; - - if (of_property_read_u32(np, "nvidia,cpu-pwr-off-time", &prop)) - suspend_mode = TEGRA_SUSPEND_NONE; - pmc_pm_data.cpu_off_time = prop; - - if (of_property_read_u32_array(np, "nvidia,core-pwr-good-time", - core_good_time, ARRAY_SIZE(core_good_time))) - suspend_mode = TEGRA_SUSPEND_NONE; - pmc_pm_data.core_osc_time = core_good_time[0]; - pmc_pm_data.core_pmu_time = core_good_time[1]; - - if (of_property_read_u32(np, "nvidia,core-pwr-off-time", - &prop)) - suspend_mode = TEGRA_SUSPEND_NONE; - pmc_pm_data.core_off_time = prop; - - pmc_pm_data.corereq_high = of_property_read_bool(np, - "nvidia,core-power-req-active-high"); - - pmc_pm_data.sysclkreq_high = of_property_read_bool(np, - "nvidia,sys-clock-req-active-high"); - - pmc_pm_data.combined_req = of_property_read_bool(np, - "nvidia,combined-power-req"); - - pmc_pm_data.cpu_pwr_good_en = of_property_read_bool(np, - "nvidia,cpu-pwr-good-en"); - - if (of_property_read_u32_array(np, "nvidia,lp0-vec", lp0_vec, - ARRAY_SIZE(lp0_vec))) - if (suspend_mode == TEGRA_SUSPEND_LP0) - suspend_mode = TEGRA_SUSPEND_LP1; - - pmc_pm_data.lp0_vec_phy_addr = lp0_vec[0]; - pmc_pm_data.lp0_vec_size = lp0_vec[1]; - - pmc_pm_data.suspend_mode = suspend_mode; -} diff --git a/arch/arm/mach-tegra/pmc.h b/arch/arm/mach-tegra/pmc.h deleted file mode 100644 index 59e19c344298..000000000000 --- a/arch/arm/mach-tegra/pmc.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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, see . - * - */ - -#ifndef __MACH_TEGRA_PMC_H -#define __MACH_TEGRA_PMC_H - -#include - -enum tegra_suspend_mode { - TEGRA_SUSPEND_NONE = 0, - TEGRA_SUSPEND_LP2, /* CPU voltage off */ - TEGRA_SUSPEND_LP1, /* CPU voltage off, DRAM self-refresh */ - TEGRA_SUSPEND_LP0, /* CPU + core voltage off, DRAM self-refresh */ - TEGRA_MAX_SUSPEND_MODE, -}; - -#ifdef CONFIG_PM_SLEEP -enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void); -void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode); -void tegra_pmc_suspend(void); -void tegra_pmc_resume(void); -void tegra_pmc_pm_set(enum tegra_suspend_mode mode); -void tegra_pmc_suspend_init(void); -#endif - -bool tegra_pmc_cpu_is_powered(int cpuid); -int tegra_pmc_cpu_power_on(int cpuid); -int tegra_pmc_cpu_remove_clamping(int cpuid); - -void tegra_pmc_restart(enum reboot_mode mode, const char *cmd); - -void tegra_pmc_init_irq(void); -void tegra_pmc_init(void); - -#endif diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c deleted file mode 100644 index 0a14b8638437..000000000000 --- a/arch/arm/mach-tegra/powergate.c +++ /dev/null @@ -1,516 +0,0 @@ -/* - * drivers/powergate/tegra-powergate.c - * - * Copyright (c) 2010 Google, Inc - * - * Author: - * Colin Cross - * - * 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. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "iomap.h" - -#define DPD_SAMPLE 0x020 -#define DPD_SAMPLE_ENABLE (1 << 0) -#define DPD_SAMPLE_DISABLE (0 << 0) - -#define PWRGATE_TOGGLE 0x30 -#define PWRGATE_TOGGLE_START (1 << 8) - -#define REMOVE_CLAMPING 0x34 - -#define PWRGATE_STATUS 0x38 - -#define IO_DPD_REQ 0x1b8 -#define IO_DPD_REQ_CODE_IDLE (0 << 30) -#define IO_DPD_REQ_CODE_OFF (1 << 30) -#define IO_DPD_REQ_CODE_ON (2 << 30) -#define IO_DPD_REQ_CODE_MASK (3 << 30) - -#define IO_DPD_STATUS 0x1bc -#define IO_DPD2_REQ 0x1c0 -#define IO_DPD2_STATUS 0x1c4 -#define SEL_DPD_TIM 0x1c8 - -#define GPU_RG_CNTRL 0x2d4 - -static int tegra_num_powerdomains; -static int tegra_num_cpu_domains; -static const u8 *tegra_cpu_domains; - -static const u8 tegra30_cpu_domains[] = { - TEGRA_POWERGATE_CPU, - TEGRA_POWERGATE_CPU1, - TEGRA_POWERGATE_CPU2, - TEGRA_POWERGATE_CPU3, -}; - -static const u8 tegra114_cpu_domains[] = { - TEGRA_POWERGATE_CPU0, - TEGRA_POWERGATE_CPU1, - TEGRA_POWERGATE_CPU2, - TEGRA_POWERGATE_CPU3, -}; - -static const u8 tegra124_cpu_domains[] = { - TEGRA_POWERGATE_CPU0, - TEGRA_POWERGATE_CPU1, - TEGRA_POWERGATE_CPU2, - TEGRA_POWERGATE_CPU3, -}; - -static DEFINE_SPINLOCK(tegra_powergate_lock); - -static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE); - -static u32 pmc_read(unsigned long reg) -{ - return readl(pmc + reg); -} - -static void pmc_write(u32 val, unsigned long reg) -{ - writel(val, pmc + reg); -} - -static int tegra_powergate_set(int id, bool new_state) -{ - bool status; - unsigned long flags; - - spin_lock_irqsave(&tegra_powergate_lock, flags); - - status = pmc_read(PWRGATE_STATUS) & (1 << id); - - if (status == new_state) { - spin_unlock_irqrestore(&tegra_powergate_lock, flags); - return 0; - } - - pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE); - - spin_unlock_irqrestore(&tegra_powergate_lock, flags); - - return 0; -} - -int tegra_powergate_power_on(int id) -{ - if (id < 0 || id >= tegra_num_powerdomains) - return -EINVAL; - - return tegra_powergate_set(id, true); -} - -int tegra_powergate_power_off(int id) -{ - if (id < 0 || id >= tegra_num_powerdomains) - return -EINVAL; - - return tegra_powergate_set(id, false); -} -EXPORT_SYMBOL(tegra_powergate_power_off); - -int tegra_powergate_is_powered(int id) -{ - u32 status; - - if (id < 0 || id >= tegra_num_powerdomains) - return -EINVAL; - - status = pmc_read(PWRGATE_STATUS) & (1 << id); - return !!status; -} - -int tegra_powergate_remove_clamping(int id) -{ - u32 mask; - - if (id < 0 || id >= tegra_num_powerdomains) - return -EINVAL; - - /* - * The Tegra124 GPU has a separate register (with different semantics) - * to remove clamps. - */ - if (tegra_get_chip_id() == TEGRA124) { - if (id == TEGRA_POWERGATE_3D) { - pmc_write(0, GPU_RG_CNTRL); - return 0; - } - } - - /* - * Tegra 2 has a bug where PCIE and VDE clamping masks are - * swapped relatively to the partition ids - */ - if (id == TEGRA_POWERGATE_VDEC) - mask = (1 << TEGRA_POWERGATE_PCIE); - else if (id == TEGRA_POWERGATE_PCIE) - mask = (1 << TEGRA_POWERGATE_VDEC); - else - mask = (1 << id); - - pmc_write(mask, REMOVE_CLAMPING); - - return 0; -} -EXPORT_SYMBOL(tegra_powergate_remove_clamping); - -/* Must be called with clk disabled, and returns with clk enabled */ -int tegra_powergate_sequence_power_up(int id, struct clk *clk, - struct reset_control *rst) -{ - int ret; - - reset_control_assert(rst); - - ret = tegra_powergate_power_on(id); - if (ret) - goto err_power; - - ret = clk_prepare_enable(clk); - if (ret) - goto err_clk; - - udelay(10); - - ret = tegra_powergate_remove_clamping(id); - if (ret) - goto err_clamp; - - udelay(10); - reset_control_deassert(rst); - - return 0; - -err_clamp: - clk_disable_unprepare(clk); -err_clk: - tegra_powergate_power_off(id); -err_power: - return ret; -} -EXPORT_SYMBOL(tegra_powergate_sequence_power_up); - -int tegra_cpu_powergate_id(int cpuid) -{ - if (cpuid > 0 && cpuid < tegra_num_cpu_domains) - return tegra_cpu_domains[cpuid]; - - return -EINVAL; -} - -int __init tegra_powergate_init(void) -{ - switch (tegra_get_chip_id()) { - case TEGRA20: - tegra_num_powerdomains = 7; - break; - case TEGRA30: - tegra_num_powerdomains = 14; - tegra_num_cpu_domains = 4; - tegra_cpu_domains = tegra30_cpu_domains; - break; - case TEGRA114: - tegra_num_powerdomains = 23; - tegra_num_cpu_domains = 4; - tegra_cpu_domains = tegra114_cpu_domains; - break; - case TEGRA124: - tegra_num_powerdomains = 25; - tegra_num_cpu_domains = 4; - tegra_cpu_domains = tegra124_cpu_domains; - break; - default: - /* Unknown Tegra variant. Disable powergating */ - tegra_num_powerdomains = 0; - break; - } - - return 0; -} - -#ifdef CONFIG_DEBUG_FS - -static const char * const *powergate_name; - -static const char * const powergate_name_t20[] = { - [TEGRA_POWERGATE_CPU] = "cpu", - [TEGRA_POWERGATE_3D] = "3d", - [TEGRA_POWERGATE_VENC] = "venc", - [TEGRA_POWERGATE_VDEC] = "vdec", - [TEGRA_POWERGATE_PCIE] = "pcie", - [TEGRA_POWERGATE_L2] = "l2", - [TEGRA_POWERGATE_MPE] = "mpe", -}; - -static const char * const powergate_name_t30[] = { - [TEGRA_POWERGATE_CPU] = "cpu0", - [TEGRA_POWERGATE_3D] = "3d0", - [TEGRA_POWERGATE_VENC] = "venc", - [TEGRA_POWERGATE_VDEC] = "vdec", - [TEGRA_POWERGATE_PCIE] = "pcie", - [TEGRA_POWERGATE_L2] = "l2", - [TEGRA_POWERGATE_MPE] = "mpe", - [TEGRA_POWERGATE_HEG] = "heg", - [TEGRA_POWERGATE_SATA] = "sata", - [TEGRA_POWERGATE_CPU1] = "cpu1", - [TEGRA_POWERGATE_CPU2] = "cpu2", - [TEGRA_POWERGATE_CPU3] = "cpu3", - [TEGRA_POWERGATE_CELP] = "celp", - [TEGRA_POWERGATE_3D1] = "3d1", -}; - -static const char * const powergate_name_t114[] = { - [TEGRA_POWERGATE_CPU] = "crail", - [TEGRA_POWERGATE_3D] = "3d", - [TEGRA_POWERGATE_VENC] = "venc", - [TEGRA_POWERGATE_VDEC] = "vdec", - [TEGRA_POWERGATE_MPE] = "mpe", - [TEGRA_POWERGATE_HEG] = "heg", - [TEGRA_POWERGATE_CPU1] = "cpu1", - [TEGRA_POWERGATE_CPU2] = "cpu2", - [TEGRA_POWERGATE_CPU3] = "cpu3", - [TEGRA_POWERGATE_CELP] = "celp", - [TEGRA_POWERGATE_CPU0] = "cpu0", - [TEGRA_POWERGATE_C0NC] = "c0nc", - [TEGRA_POWERGATE_C1NC] = "c1nc", - [TEGRA_POWERGATE_DIS] = "dis", - [TEGRA_POWERGATE_DISB] = "disb", - [TEGRA_POWERGATE_XUSBA] = "xusba", - [TEGRA_POWERGATE_XUSBB] = "xusbb", - [TEGRA_POWERGATE_XUSBC] = "xusbc", -}; - -static const char * const powergate_name_t124[] = { - [TEGRA_POWERGATE_CPU] = "crail", - [TEGRA_POWERGATE_3D] = "3d", - [TEGRA_POWERGATE_VENC] = "venc", - [TEGRA_POWERGATE_PCIE] = "pcie", - [TEGRA_POWERGATE_VDEC] = "vdec", - [TEGRA_POWERGATE_L2] = "l2", - [TEGRA_POWERGATE_MPE] = "mpe", - [TEGRA_POWERGATE_HEG] = "heg", - [TEGRA_POWERGATE_SATA] = "sata", - [TEGRA_POWERGATE_CPU1] = "cpu1", - [TEGRA_POWERGATE_CPU2] = "cpu2", - [TEGRA_POWERGATE_CPU3] = "cpu3", - [TEGRA_POWERGATE_CELP] = "celp", - [TEGRA_POWERGATE_CPU0] = "cpu0", - [TEGRA_POWERGATE_C0NC] = "c0nc", - [TEGRA_POWERGATE_C1NC] = "c1nc", - [TEGRA_POWERGATE_SOR] = "sor", - [TEGRA_POWERGATE_DIS] = "dis", - [TEGRA_POWERGATE_DISB] = "disb", - [TEGRA_POWERGATE_XUSBA] = "xusba", - [TEGRA_POWERGATE_XUSBB] = "xusbb", - [TEGRA_POWERGATE_XUSBC] = "xusbc", - [TEGRA_POWERGATE_VIC] = "vic", - [TEGRA_POWERGATE_IRAM] = "iram", -}; - -static int powergate_show(struct seq_file *s, void *data) -{ - int i; - - seq_printf(s, " powergate powered\n"); - seq_printf(s, "------------------\n"); - - for (i = 0; i < tegra_num_powerdomains; i++) { - if (!powergate_name[i]) - continue; - - seq_printf(s, " %9s %7s\n", powergate_name[i], - tegra_powergate_is_powered(i) ? "yes" : "no"); - } - - return 0; -} - -static int powergate_open(struct inode *inode, struct file *file) -{ - return single_open(file, powergate_show, inode->i_private); -} - -static const struct file_operations powergate_fops = { - .open = powergate_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -int __init tegra_powergate_debugfs_init(void) -{ - struct dentry *d; - - switch (tegra_get_chip_id()) { - case TEGRA20: - powergate_name = powergate_name_t20; - break; - case TEGRA30: - powergate_name = powergate_name_t30; - break; - case TEGRA114: - powergate_name = powergate_name_t114; - break; - case TEGRA124: - powergate_name = powergate_name_t124; - break; - } - - if (powergate_name) { - d = debugfs_create_file("powergate", S_IRUGO, NULL, NULL, - &powergate_fops); - if (!d) - return -ENOMEM; - } - - return 0; -} - -#endif - -static int tegra_io_rail_prepare(int id, unsigned long *request, - unsigned long *status, unsigned int *bit) -{ - unsigned long rate, value; - struct clk *clk; - - *bit = id % 32; - - /* - * There are two sets of 30 bits to select IO rails, but bits 30 and - * 31 are control bits rather than IO rail selection bits. - */ - if (id > 63 || *bit == 30 || *bit == 31) - return -EINVAL; - - if (id < 32) { - *status = IO_DPD_STATUS; - *request = IO_DPD_REQ; - } else { - *status = IO_DPD2_STATUS; - *request = IO_DPD2_REQ; - } - - clk = clk_get_sys(NULL, "pclk"); - if (IS_ERR(clk)) - return PTR_ERR(clk); - - rate = clk_get_rate(clk); - clk_put(clk); - - pmc_write(DPD_SAMPLE_ENABLE, DPD_SAMPLE); - - /* must be at least 200 ns, in APB (PCLK) clock cycles */ - value = DIV_ROUND_UP(1000000000, rate); - value = DIV_ROUND_UP(200, value); - pmc_write(value, SEL_DPD_TIM); - - return 0; -} - -static int tegra_io_rail_poll(unsigned long offset, unsigned long mask, - unsigned long val, unsigned long timeout) -{ - unsigned long value; - - timeout = jiffies + msecs_to_jiffies(timeout); - - while (time_after(timeout, jiffies)) { - value = pmc_read(offset); - if ((value & mask) == val) - return 0; - - usleep_range(250, 1000); - } - - return -ETIMEDOUT; -} - -static void tegra_io_rail_unprepare(void) -{ - pmc_write(DPD_SAMPLE_DISABLE, DPD_SAMPLE); -} - -int tegra_io_rail_power_on(int id) -{ - unsigned long request, status, value; - unsigned int bit, mask; - int err; - - err = tegra_io_rail_prepare(id, &request, &status, &bit); - if (err < 0) - return err; - - mask = 1 << bit; - - value = pmc_read(request); - value |= mask; - value &= ~IO_DPD_REQ_CODE_MASK; - value |= IO_DPD_REQ_CODE_OFF; - pmc_write(value, request); - - err = tegra_io_rail_poll(status, mask, 0, 250); - if (err < 0) - return err; - - tegra_io_rail_unprepare(); - - return 0; -} -EXPORT_SYMBOL(tegra_io_rail_power_on); - -int tegra_io_rail_power_off(int id) -{ - unsigned long request, status, value; - unsigned int bit, mask; - int err; - - err = tegra_io_rail_prepare(id, &request, &status, &bit); - if (err < 0) - return err; - - mask = 1 << bit; - - value = pmc_read(request); - value |= mask; - value &= ~IO_DPD_REQ_CODE_MASK; - value |= IO_DPD_REQ_CODE_ON; - pmc_write(value, request); - - err = tegra_io_rail_poll(status, mask, mask, 250); - if (err < 0) - return err; - - tegra_io_rail_unprepare(); - - return 0; -} -EXPORT_SYMBOL(tegra_io_rail_power_off); diff --git a/arch/arm/mach-tegra/tegra.c b/arch/arm/mach-tegra/tegra.c index c9176db0b9c0..5ef5173dec83 100644 --- a/arch/arm/mach-tegra/tegra.c +++ b/arch/arm/mach-tegra/tegra.c @@ -36,6 +36,7 @@ #include #include +#include #include #include @@ -49,7 +50,6 @@ #include "cpuidle.h" #include "iomap.h" #include "irq.h" -#include "pmc.h" #include "pm.h" #include "reset.h" #include "sleep.h" @@ -74,12 +74,10 @@ static void __init tegra_init_early(void) { of_register_trusted_foundations(); tegra_cpu_reset_handler_init(); - tegra_powergate_init(); } static void __init tegra_dt_init_irq(void) { - tegra_pmc_init_irq(); tegra_init_irq(); irqchip_init(); tegra_legacy_irq_syscore_init(); @@ -91,8 +89,6 @@ static void __init tegra_dt_init(void) struct soc_device *soc_dev; struct device *parent = NULL; - tegra_pmc_init(); - tegra_clocks_apply_init_table(); soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); @@ -142,7 +138,6 @@ static void __init tegra_dt_init_late(void) tegra_init_suspend(); tegra_cpuidle_init(); - tegra_powergate_debugfs_init(); for (i = 0; i < ARRAY_SIZE(board_init_funcs); i++) { if (of_machine_is_compatible(board_init_funcs[i].machine)) { diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index 5679ffdb3f8c..5bbacd01094f 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -23,7 +23,7 @@ #include #include -#include +#include #include diff --git a/drivers/gpu/drm/tegra/gr3d.c b/drivers/gpu/drm/tegra/gr3d.c index 69974851e564..2d07616a7d80 100644 --- a/drivers/gpu/drm/tegra/gr3d.c +++ b/drivers/gpu/drm/tegra/gr3d.c @@ -13,7 +13,7 @@ #include #include -#include +#include #include "drm.h" #include "gem.h" diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index eafd0b8a71d2..7f8ca5d4718f 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -12,7 +12,7 @@ #include #include -#include +#include #include diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index a2f0f88be382..acbfee0d7947 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -45,7 +45,7 @@ #include #include -#include +#include #include #include diff --git a/drivers/soc/tegra/Makefile b/drivers/soc/tegra/Makefile index db34987ed9dc..cdaad9d53a05 100644 --- a/drivers/soc/tegra/Makefile +++ b/drivers/soc/tegra/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_ARCH_TEGRA) += fuse/ obj-$(CONFIG_ARCH_TEGRA) += common.o +obj-$(CONFIG_ARCH_TEGRA) += pmc.o diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c new file mode 100644 index 000000000000..a2c0ceb95f8f --- /dev/null +++ b/drivers/soc/tegra/pmc.c @@ -0,0 +1,957 @@ +/* + * drivers/soc/tegra/pmc.c + * + * Copyright (c) 2010 Google, Inc + * + * Author: + * Colin Cross + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define PMC_CNTRL 0x0 +#define PMC_CNTRL_SYSCLK_POLARITY (1 << 10) /* sys clk polarity */ +#define PMC_CNTRL_SYSCLK_OE (1 << 11) /* system clock enable */ +#define PMC_CNTRL_SIDE_EFFECT_LP0 (1 << 14) /* LP0 when CPU pwr gated */ +#define PMC_CNTRL_CPU_PWRREQ_POLARITY (1 << 15) /* CPU pwr req polarity */ +#define PMC_CNTRL_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */ +#define PMC_CNTRL_INTR_POLARITY (1 << 17) /* inverts INTR polarity */ + +#define DPD_SAMPLE 0x020 +#define DPD_SAMPLE_ENABLE (1 << 0) +#define DPD_SAMPLE_DISABLE (0 << 0) + +#define PWRGATE_TOGGLE 0x30 +#define PWRGATE_TOGGLE_START (1 << 8) + +#define REMOVE_CLAMPING 0x34 + +#define PWRGATE_STATUS 0x38 + +#define PMC_SCRATCH0 0x50 +#define PMC_SCRATCH0_MODE_RECOVERY (1 << 31) +#define PMC_SCRATCH0_MODE_BOOTLOADER (1 << 30) +#define PMC_SCRATCH0_MODE_RCM (1 << 1) +#define PMC_SCRATCH0_MODE_MASK (PMC_SCRATCH0_MODE_RECOVERY | \ + PMC_SCRATCH0_MODE_BOOTLOADER | \ + PMC_SCRATCH0_MODE_RCM) + +#define PMC_CPUPWRGOOD_TIMER 0xc8 +#define PMC_CPUPWROFF_TIMER 0xcc + +#define PMC_SCRATCH41 0x140 + +#define IO_DPD_REQ 0x1b8 +#define IO_DPD_REQ_CODE_IDLE (0 << 30) +#define IO_DPD_REQ_CODE_OFF (1 << 30) +#define IO_DPD_REQ_CODE_ON (2 << 30) +#define IO_DPD_REQ_CODE_MASK (3 << 30) + +#define IO_DPD_STATUS 0x1bc +#define IO_DPD2_REQ 0x1c0 +#define IO_DPD2_STATUS 0x1c4 +#define SEL_DPD_TIM 0x1c8 + +#define GPU_RG_CNTRL 0x2d4 + +struct tegra_pmc_soc { + unsigned int num_powergates; + const char *const *powergates; + unsigned int num_cpu_powergates; + const u8 *cpu_powergates; +}; + +/** + * struct tegra_pmc - NVIDIA Tegra PMC + * @base: pointer to I/O remapped register region + * @clk: pointer to pclk clock + * @rate: currently configured rate of pclk + * @suspend_mode: lowest suspend mode available + * @cpu_good_time: CPU power good time (in microseconds) + * @cpu_off_time: CPU power off time (in microsecends) + * @core_osc_time: core power good OSC time (in microseconds) + * @core_pmu_time: core power good PMU time (in microseconds) + * @core_off_time: core power off time (in microseconds) + * @corereq_high: core power request is active-high + * @sysclkreq_high: system clock request is active-high + * @combined_req: combined power request for CPU & core + * @cpu_pwr_good_en: CPU power good signal is enabled + * @lp0_vec_phys: physical base address of the LP0 warm boot code + * @lp0_vec_size: size of the LP0 warm boot code + * @powergates_lock: mutex for power gate register access + */ +struct tegra_pmc { + void __iomem *base; + struct clk *clk; + + const struct tegra_pmc_soc *soc; + + unsigned long rate; + + enum tegra_suspend_mode suspend_mode; + u32 cpu_good_time; + u32 cpu_off_time; + u32 core_osc_time; + u32 core_pmu_time; + u32 core_off_time; + bool corereq_high; + bool sysclkreq_high; + bool combined_req; + bool cpu_pwr_good_en; + u32 lp0_vec_phys; + u32 lp0_vec_size; + + struct mutex powergates_lock; +}; + +static struct tegra_pmc *pmc = &(struct tegra_pmc) { + .base = NULL, + .suspend_mode = TEGRA_SUSPEND_NONE, +}; + +static u32 tegra_pmc_readl(unsigned long offset) +{ + return readl(pmc->base + offset); +} + +static void tegra_pmc_writel(u32 value, unsigned long offset) +{ + writel(value, pmc->base + offset); +} + +/** + * tegra_powergate_set() - set the state of a partition + * @id: partition ID + * @new_state: new state of the partition + */ +static int tegra_powergate_set(int id, bool new_state) +{ + bool status; + + mutex_lock(&pmc->powergates_lock); + + status = tegra_pmc_readl(PWRGATE_STATUS) & (1 << id); + + if (status == new_state) { + mutex_unlock(&pmc->powergates_lock); + return 0; + } + + tegra_pmc_writel(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE); + + mutex_unlock(&pmc->powergates_lock); + + return 0; +} + +/** + * tegra_powergate_power_on() - power on partition + * @id: partition ID + */ +int tegra_powergate_power_on(int id) +{ + if (!pmc->soc || id < 0 || id >= pmc->soc->num_powergates) + return -EINVAL; + + return tegra_powergate_set(id, true); +} + +/** + * tegra_powergate_power_off() - power off partition + * @id: partition ID + */ +int tegra_powergate_power_off(int id) +{ + if (!pmc->soc || id < 0 || id >= pmc->soc->num_powergates) + return -EINVAL; + + return tegra_powergate_set(id, false); +} +EXPORT_SYMBOL(tegra_powergate_power_off); + +/** + * tegra_powergate_is_powered() - check if partition is powered + * @id: partition ID + */ +int tegra_powergate_is_powered(int id) +{ + u32 status; + + if (!pmc->soc || id < 0 || id >= pmc->soc->num_powergates) + return -EINVAL; + + status = tegra_pmc_readl(PWRGATE_STATUS) & (1 << id); + return !!status; +} + +/** + * tegra_powergate_remove_clamping() - remove power clamps for partition + * @id: partition ID + */ +int tegra_powergate_remove_clamping(int id) +{ + u32 mask; + + if (!pmc->soc || id < 0 || id >= pmc->soc->num_powergates) + return -EINVAL; + + /* + * The Tegra124 GPU has a separate register (with different semantics) + * to remove clamps. + */ + if (tegra_get_chip_id() == TEGRA124) { + if (id == TEGRA_POWERGATE_3D) { + tegra_pmc_writel(0, GPU_RG_CNTRL); + return 0; + } + } + + /* + * Tegra 2 has a bug where PCIE and VDE clamping masks are + * swapped relatively to the partition ids + */ + if (id == TEGRA_POWERGATE_VDEC) + mask = (1 << TEGRA_POWERGATE_PCIE); + else if (id == TEGRA_POWERGATE_PCIE) + mask = (1 << TEGRA_POWERGATE_VDEC); + else + mask = (1 << id); + + tegra_pmc_writel(mask, REMOVE_CLAMPING); + + return 0; +} +EXPORT_SYMBOL(tegra_powergate_remove_clamping); + +/** + * tegra_powergate_sequence_power_up() - power up partition + * @id: partition ID + * @clk: clock for partition + * @rst: reset for partition + * + * Must be called with clk disabled, and returns with clk enabled. + */ +int tegra_powergate_sequence_power_up(int id, struct clk *clk, + struct reset_control *rst) +{ + int ret; + + reset_control_assert(rst); + + ret = tegra_powergate_power_on(id); + if (ret) + goto err_power; + + ret = clk_prepare_enable(clk); + if (ret) + goto err_clk; + + usleep_range(10, 20); + + ret = tegra_powergate_remove_clamping(id); + if (ret) + goto err_clamp; + + usleep_range(10, 20); + reset_control_deassert(rst); + + return 0; + +err_clamp: + clk_disable_unprepare(clk); +err_clk: + tegra_powergate_power_off(id); +err_power: + return ret; +} +EXPORT_SYMBOL(tegra_powergate_sequence_power_up); + +#ifdef CONFIG_SMP +/** + * tegra_get_cpu_powergate_id() - convert from CPU ID to partition ID + * @cpuid: CPU partition ID + * + * Returns the partition ID corresponding to the CPU partition ID or a + * negative error code on failure. + */ +static int tegra_get_cpu_powergate_id(int cpuid) +{ + if (pmc->soc && cpuid > 0 && cpuid < pmc->soc->num_cpu_powergates) + return pmc->soc->cpu_powergates[cpuid]; + + return -EINVAL; +} + +/** + * tegra_pmc_cpu_is_powered() - check if CPU partition is powered + * @cpuid: CPU partition ID + */ +bool tegra_pmc_cpu_is_powered(int cpuid) +{ + int id; + + id = tegra_get_cpu_powergate_id(cpuid); + if (id < 0) + return false; + + return tegra_powergate_is_powered(id); +} + +/** + * tegra_pmc_cpu_power_on() - power on CPU partition + * @cpuid: CPU partition ID + */ +int tegra_pmc_cpu_power_on(int cpuid) +{ + int id; + + id = tegra_get_cpu_powergate_id(cpuid); + if (id < 0) + return id; + + return tegra_powergate_set(id, true); +} + +/** + * tegra_pmc_cpu_remove_clamping() - remove power clamps for CPU partition + * @cpuid: CPU partition ID + */ +int tegra_pmc_cpu_remove_clamping(int cpuid) +{ + int id; + + id = tegra_get_cpu_powergate_id(cpuid); + if (id < 0) + return id; + + return tegra_powergate_remove_clamping(id); +} +#endif /* CONFIG_SMP */ + +/** + * tegra_pmc_restart() - reboot the system + * @mode: which mode to reboot in + * @cmd: reboot command + */ +void tegra_pmc_restart(enum reboot_mode mode, const char *cmd) +{ + u32 value; + + value = tegra_pmc_readl(PMC_SCRATCH0); + value &= ~PMC_SCRATCH0_MODE_MASK; + + if (cmd) { + if (strcmp(cmd, "recovery") == 0) + value |= PMC_SCRATCH0_MODE_RECOVERY; + + if (strcmp(cmd, "bootloader") == 0) + value |= PMC_SCRATCH0_MODE_BOOTLOADER; + + if (strcmp(cmd, "forced-recovery") == 0) + value |= PMC_SCRATCH0_MODE_RCM; + } + + tegra_pmc_writel(value, PMC_SCRATCH0); + + value = tegra_pmc_readl(0); + value |= 0x10; + tegra_pmc_writel(value, 0); +} + +static int powergate_show(struct seq_file *s, void *data) +{ + unsigned int i; + + seq_printf(s, " powergate powered\n"); + seq_printf(s, "------------------\n"); + + for (i = 0; i < pmc->soc->num_powergates; i++) { + if (!pmc->soc->powergates[i]) + continue; + + seq_printf(s, " %9s %7s\n", pmc->soc->powergates[i], + tegra_powergate_is_powered(i) ? "yes" : "no"); + } + + return 0; +} + +static int powergate_open(struct inode *inode, struct file *file) +{ + return single_open(file, powergate_show, inode->i_private); +} + +static const struct file_operations powergate_fops = { + .open = powergate_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int tegra_powergate_debugfs_init(void) +{ + struct dentry *d; + + d = debugfs_create_file("powergate", S_IRUGO, NULL, NULL, + &powergate_fops); + if (!d) + return -ENOMEM; + + return 0; +} + +static int tegra_io_rail_prepare(int id, unsigned long *request, + unsigned long *status, unsigned int *bit) +{ + unsigned long rate, value; + struct clk *clk; + + *bit = id % 32; + + /* + * There are two sets of 30 bits to select IO rails, but bits 30 and + * 31 are control bits rather than IO rail selection bits. + */ + if (id > 63 || *bit == 30 || *bit == 31) + return -EINVAL; + + if (id < 32) { + *status = IO_DPD_STATUS; + *request = IO_DPD_REQ; + } else { + *status = IO_DPD2_STATUS; + *request = IO_DPD2_REQ; + } + + clk = clk_get_sys(NULL, "pclk"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + rate = clk_get_rate(clk); + clk_put(clk); + + tegra_pmc_writel(DPD_SAMPLE_ENABLE, DPD_SAMPLE); + + /* must be at least 200 ns, in APB (PCLK) clock cycles */ + value = DIV_ROUND_UP(1000000000, rate); + value = DIV_ROUND_UP(200, value); + tegra_pmc_writel(value, SEL_DPD_TIM); + + return 0; +} + +static int tegra_io_rail_poll(unsigned long offset, unsigned long mask, + unsigned long val, unsigned long timeout) +{ + unsigned long value; + + timeout = jiffies + msecs_to_jiffies(timeout); + + while (time_after(timeout, jiffies)) { + value = tegra_pmc_readl(offset); + if ((value & mask) == val) + return 0; + + usleep_range(250, 1000); + } + + return -ETIMEDOUT; +} + +static void tegra_io_rail_unprepare(void) +{ + tegra_pmc_writel(DPD_SAMPLE_DISABLE, DPD_SAMPLE); +} + +int tegra_io_rail_power_on(int id) +{ + unsigned long request, status, value; + unsigned int bit, mask; + int err; + + err = tegra_io_rail_prepare(id, &request, &status, &bit); + if (err < 0) + return err; + + mask = 1 << bit; + + value = tegra_pmc_readl(request); + value |= mask; + value &= ~IO_DPD_REQ_CODE_MASK; + value |= IO_DPD_REQ_CODE_OFF; + tegra_pmc_writel(value, request); + + err = tegra_io_rail_poll(status, mask, 0, 250); + if (err < 0) + return err; + + tegra_io_rail_unprepare(); + + return 0; +} +EXPORT_SYMBOL(tegra_io_rail_power_on); + +int tegra_io_rail_power_off(int id) +{ + unsigned long request, status, value; + unsigned int bit, mask; + int err; + + err = tegra_io_rail_prepare(id, &request, &status, &bit); + if (err < 0) + return err; + + mask = 1 << bit; + + value = tegra_pmc_readl(request); + value |= mask; + value &= ~IO_DPD_REQ_CODE_MASK; + value |= IO_DPD_REQ_CODE_ON; + tegra_pmc_writel(value, request); + + err = tegra_io_rail_poll(status, mask, mask, 250); + if (err < 0) + return err; + + tegra_io_rail_unprepare(); + + return 0; +} +EXPORT_SYMBOL(tegra_io_rail_power_off); + +#ifdef CONFIG_PM_SLEEP +enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void) +{ + return pmc->suspend_mode; +} + +void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode) +{ + if (mode < TEGRA_SUSPEND_NONE || mode >= TEGRA_MAX_SUSPEND_MODE) + return; + + pmc->suspend_mode = mode; +} + +void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode) +{ + unsigned long long rate = 0; + u32 value; + + switch (mode) { + case TEGRA_SUSPEND_LP1: + rate = 32768; + break; + + case TEGRA_SUSPEND_LP2: + rate = clk_get_rate(pmc->clk); + break; + + default: + break; + } + + if (WARN_ON_ONCE(rate == 0)) + rate = 100000000; + + if (rate != pmc->rate) { + u64 ticks; + + ticks = pmc->cpu_good_time * rate + USEC_PER_SEC - 1; + do_div(ticks, USEC_PER_SEC); + tegra_pmc_writel(ticks, PMC_CPUPWRGOOD_TIMER); + + ticks = pmc->cpu_off_time * rate + USEC_PER_SEC - 1; + do_div(ticks, USEC_PER_SEC); + tegra_pmc_writel(ticks, PMC_CPUPWROFF_TIMER); + + wmb(); + + pmc->rate = rate; + } + + value = tegra_pmc_readl(PMC_CNTRL); + value &= ~PMC_CNTRL_SIDE_EFFECT_LP0; + value |= PMC_CNTRL_CPU_PWRREQ_OE; + tegra_pmc_writel(value, PMC_CNTRL); +} +#endif + +static int tegra_pmc_parse_dt(struct tegra_pmc *pmc, struct device_node *np) +{ + u32 value, values[2]; + + if (of_property_read_u32(np, "nvidia,suspend-mode", &value)) { + } else { + switch (value) { + case 0: + pmc->suspend_mode = TEGRA_SUSPEND_LP0; + break; + + case 1: + pmc->suspend_mode = TEGRA_SUSPEND_LP1; + break; + + case 2: + pmc->suspend_mode = TEGRA_SUSPEND_LP2; + break; + + default: + pmc->suspend_mode = TEGRA_SUSPEND_NONE; + break; + } + } + + pmc->suspend_mode = tegra_pm_validate_suspend_mode(pmc->suspend_mode); + + if (of_property_read_u32(np, "nvidia,cpu-pwr-good-time", &value)) + pmc->suspend_mode = TEGRA_SUSPEND_NONE; + + pmc->cpu_good_time = value; + + if (of_property_read_u32(np, "nvidia,cpu-pwr-off-time", &value)) + pmc->suspend_mode = TEGRA_SUSPEND_NONE; + + pmc->cpu_off_time = value; + + if (of_property_read_u32_array(np, "nvidia,core-pwr-good-time", + values, ARRAY_SIZE(values))) + pmc->suspend_mode = TEGRA_SUSPEND_NONE; + + pmc->core_osc_time = values[0]; + pmc->core_pmu_time = values[1]; + + if (of_property_read_u32(np, "nvidia,core-pwr-off-time", &value)) + pmc->suspend_mode = TEGRA_SUSPEND_NONE; + + pmc->core_off_time = value; + + pmc->corereq_high = of_property_read_bool(np, + "nvidia,core-power-req-active-high"); + + pmc->sysclkreq_high = of_property_read_bool(np, + "nvidia,sys-clock-req-active-high"); + + pmc->combined_req = of_property_read_bool(np, + "nvidia,combined-power-req"); + + pmc->cpu_pwr_good_en = of_property_read_bool(np, + "nvidia,cpu-pwr-good-en"); + + if (of_property_read_u32_array(np, "nvidia,lp0-vec", values, + ARRAY_SIZE(values))) + if (pmc->suspend_mode == TEGRA_SUSPEND_LP0) + pmc->suspend_mode = TEGRA_SUSPEND_LP1; + + pmc->lp0_vec_phys = values[0]; + pmc->lp0_vec_size = values[1]; + + return 0; +} + +static void tegra_pmc_init(struct tegra_pmc *pmc) +{ + u32 value; + + /* Always enable CPU power request */ + value = tegra_pmc_readl(PMC_CNTRL); + value |= PMC_CNTRL_CPU_PWRREQ_OE; + tegra_pmc_writel(value, PMC_CNTRL); + + value = tegra_pmc_readl(PMC_CNTRL); + + if (pmc->sysclkreq_high) + value &= ~PMC_CNTRL_SYSCLK_POLARITY; + else + value |= PMC_CNTRL_SYSCLK_POLARITY; + + /* configure the output polarity while the request is tristated */ + tegra_pmc_writel(value, PMC_CNTRL); + + /* now enable the request */ + value = tegra_pmc_readl(PMC_CNTRL); + value |= PMC_CNTRL_SYSCLK_OE; + tegra_pmc_writel(value, PMC_CNTRL); +} + +static int tegra_pmc_probe(struct platform_device *pdev) +{ + void __iomem *base = pmc->base; + struct resource *res; + int err; + + err = tegra_pmc_parse_dt(pmc, pdev->dev.of_node); + if (err < 0) + return err; + + /* take over the memory region from the early initialization */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pmc->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pmc->base)) + return PTR_ERR(pmc->base); + + iounmap(base); + + pmc->clk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(pmc->clk)) { + err = PTR_ERR(pmc->clk); + dev_err(&pdev->dev, "failed to get pclk: %d\n", err); + return err; + } + + tegra_pmc_init(pmc); + + if (IS_ENABLED(CONFIG_DEBUG_FS)) { + err = tegra_powergate_debugfs_init(); + if (err < 0) + return err; + } + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tegra_pmc_suspend(struct device *dev) +{ + tegra_pmc_writel(virt_to_phys(tegra_resume), PMC_SCRATCH41); + + return 0; +} + +static int tegra_pmc_resume(struct device *dev) +{ + tegra_pmc_writel(0x0, PMC_SCRATCH41); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(tegra_pmc_pm_ops, tegra_pmc_suspend, tegra_pmc_resume); + +static const char * const tegra20_powergates[] = { + [TEGRA_POWERGATE_CPU] = "cpu", + [TEGRA_POWERGATE_3D] = "3d", + [TEGRA_POWERGATE_VENC] = "venc", + [TEGRA_POWERGATE_VDEC] = "vdec", + [TEGRA_POWERGATE_PCIE] = "pcie", + [TEGRA_POWERGATE_L2] = "l2", + [TEGRA_POWERGATE_MPE] = "mpe", +}; + +static const struct tegra_pmc_soc tegra20_pmc_soc = { + .num_powergates = ARRAY_SIZE(tegra20_powergates), + .powergates = tegra20_powergates, + .num_cpu_powergates = 0, + .cpu_powergates = NULL, +}; + +static const char * const tegra30_powergates[] = { + [TEGRA_POWERGATE_CPU] = "cpu0", + [TEGRA_POWERGATE_3D] = "3d0", + [TEGRA_POWERGATE_VENC] = "venc", + [TEGRA_POWERGATE_VDEC] = "vdec", + [TEGRA_POWERGATE_PCIE] = "pcie", + [TEGRA_POWERGATE_L2] = "l2", + [TEGRA_POWERGATE_MPE] = "mpe", + [TEGRA_POWERGATE_HEG] = "heg", + [TEGRA_POWERGATE_SATA] = "sata", + [TEGRA_POWERGATE_CPU1] = "cpu1", + [TEGRA_POWERGATE_CPU2] = "cpu2", + [TEGRA_POWERGATE_CPU3] = "cpu3", + [TEGRA_POWERGATE_CELP] = "celp", + [TEGRA_POWERGATE_3D1] = "3d1", +}; + +static const u8 tegra30_cpu_powergates[] = { + TEGRA_POWERGATE_CPU, + TEGRA_POWERGATE_CPU1, + TEGRA_POWERGATE_CPU2, + TEGRA_POWERGATE_CPU3, +}; + +static const struct tegra_pmc_soc tegra30_pmc_soc = { + .num_powergates = ARRAY_SIZE(tegra30_powergates), + .powergates = tegra30_powergates, + .num_cpu_powergates = ARRAY_SIZE(tegra30_cpu_powergates), + .cpu_powergates = tegra30_cpu_powergates, +}; + +static const char * const tegra114_powergates[] = { + [TEGRA_POWERGATE_CPU] = "crail", + [TEGRA_POWERGATE_3D] = "3d", + [TEGRA_POWERGATE_VENC] = "venc", + [TEGRA_POWERGATE_VDEC] = "vdec", + [TEGRA_POWERGATE_MPE] = "mpe", + [TEGRA_POWERGATE_HEG] = "heg", + [TEGRA_POWERGATE_CPU1] = "cpu1", + [TEGRA_POWERGATE_CPU2] = "cpu2", + [TEGRA_POWERGATE_CPU3] = "cpu3", + [TEGRA_POWERGATE_CELP] = "celp", + [TEGRA_POWERGATE_CPU0] = "cpu0", + [TEGRA_POWERGATE_C0NC] = "c0nc", + [TEGRA_POWERGATE_C1NC] = "c1nc", + [TEGRA_POWERGATE_DIS] = "dis", + [TEGRA_POWERGATE_DISB] = "disb", + [TEGRA_POWERGATE_XUSBA] = "xusba", + [TEGRA_POWERGATE_XUSBB] = "xusbb", + [TEGRA_POWERGATE_XUSBC] = "xusbc", +}; + +static const u8 tegra114_cpu_powergates[] = { + TEGRA_POWERGATE_CPU0, + TEGRA_POWERGATE_CPU1, + TEGRA_POWERGATE_CPU2, + TEGRA_POWERGATE_CPU3, +}; + +static const struct tegra_pmc_soc tegra114_pmc_soc = { + .num_powergates = ARRAY_SIZE(tegra114_powergates), + .powergates = tegra114_powergates, + .num_cpu_powergates = ARRAY_SIZE(tegra114_cpu_powergates), + .cpu_powergates = tegra114_cpu_powergates, +}; + +static const char * const tegra124_powergates[] = { + [TEGRA_POWERGATE_CPU] = "crail", + [TEGRA_POWERGATE_3D] = "3d", + [TEGRA_POWERGATE_VENC] = "venc", + [TEGRA_POWERGATE_PCIE] = "pcie", + [TEGRA_POWERGATE_VDEC] = "vdec", + [TEGRA_POWERGATE_L2] = "l2", + [TEGRA_POWERGATE_MPE] = "mpe", + [TEGRA_POWERGATE_HEG] = "heg", + [TEGRA_POWERGATE_SATA] = "sata", + [TEGRA_POWERGATE_CPU1] = "cpu1", + [TEGRA_POWERGATE_CPU2] = "cpu2", + [TEGRA_POWERGATE_CPU3] = "cpu3", + [TEGRA_POWERGATE_CELP] = "celp", + [TEGRA_POWERGATE_CPU0] = "cpu0", + [TEGRA_POWERGATE_C0NC] = "c0nc", + [TEGRA_POWERGATE_C1NC] = "c1nc", + [TEGRA_POWERGATE_SOR] = "sor", + [TEGRA_POWERGATE_DIS] = "dis", + [TEGRA_POWERGATE_DISB] = "disb", + [TEGRA_POWERGATE_XUSBA] = "xusba", + [TEGRA_POWERGATE_XUSBB] = "xusbb", + [TEGRA_POWERGATE_XUSBC] = "xusbc", + [TEGRA_POWERGATE_VIC] = "vic", + [TEGRA_POWERGATE_IRAM] = "iram", +}; + +static const u8 tegra124_cpu_powergates[] = { + TEGRA_POWERGATE_CPU0, + TEGRA_POWERGATE_CPU1, + TEGRA_POWERGATE_CPU2, + TEGRA_POWERGATE_CPU3, +}; + +static const struct tegra_pmc_soc tegra124_pmc_soc = { + .num_powergates = ARRAY_SIZE(tegra124_powergates), + .powergates = tegra124_powergates, + .num_cpu_powergates = ARRAY_SIZE(tegra124_cpu_powergates), + .cpu_powergates = tegra124_cpu_powergates, +}; + +static const struct of_device_id tegra_pmc_match[] = { + { .compatible = "nvidia,tegra124-pmc", .data = &tegra124_pmc_soc }, + { .compatible = "nvidia,tegra114-pmc", .data = &tegra114_pmc_soc }, + { .compatible = "nvidia,tegra30-pmc", .data = &tegra30_pmc_soc }, + { .compatible = "nvidia,tegra20-pmc", .data = &tegra20_pmc_soc }, + { } +}; + +static struct platform_driver tegra_pmc_driver = { + .driver = { + .name = "tegra-pmc", + .suppress_bind_attrs = true, + .of_match_table = tegra_pmc_match, + .pm = &tegra_pmc_pm_ops, + }, + .probe = tegra_pmc_probe, +}; +module_platform_driver(tegra_pmc_driver); + +/* + * Early initialization to allow access to registers in the very early boot + * process. + */ +static int __init tegra_pmc_early_init(void) +{ + const struct of_device_id *match; + struct device_node *np; + struct resource regs; + bool invert; + u32 value; + + if (!soc_is_tegra()) + return 0; + + np = of_find_matching_node_and_match(NULL, tegra_pmc_match, &match); + if (!np) { + pr_warn("PMC device node not found, disabling powergating\n"); + + regs.start = 0x7000e400; + regs.end = 0x7000e7ff; + regs.flags = IORESOURCE_MEM; + + pr_warn("Using memory region %pR\n", ®s); + } else { + pmc->soc = match->data; + } + + if (of_address_to_resource(np, 0, ®s) < 0) { + pr_err("failed to get PMC registers\n"); + return -ENXIO; + } + + pmc->base = ioremap_nocache(regs.start, resource_size(®s)); + if (!pmc->base) { + pr_err("failed to map PMC registers\n"); + return -ENXIO; + } + + mutex_init(&pmc->powergates_lock); + + invert = of_property_read_bool(np, "nvidia,invert-interrupt"); + + value = tegra_pmc_readl(PMC_CNTRL); + + if (invert) + value |= PMC_CNTRL_INTR_POLARITY; + else + value &= ~PMC_CNTRL_INTR_POLARITY; + + tegra_pmc_writel(value, PMC_CNTRL); + + return 0; +} +early_initcall(tegra_pmc_early_init); diff --git a/include/soc/tegra/pm.h b/include/soc/tegra/pm.h new file mode 100644 index 000000000000..30fe2078a547 --- /dev/null +++ b/include/soc/tegra/pm.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2014 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __SOC_TEGRA_PM_H__ +#define __SOC_TEGRA_PM_H__ + +enum tegra_suspend_mode { + TEGRA_SUSPEND_NONE = 0, + TEGRA_SUSPEND_LP2, /* CPU voltage off */ + TEGRA_SUSPEND_LP1, /* CPU voltage off, DRAM self-refresh */ + TEGRA_SUSPEND_LP0, /* CPU + core voltage off, DRAM self-refresh */ + TEGRA_MAX_SUSPEND_MODE, +}; + +#ifdef CONFIG_PM_SLEEP +enum tegra_suspend_mode +tegra_pm_validate_suspend_mode(enum tegra_suspend_mode mode); + +/* low-level resume entry point */ +void tegra_resume(void); +#else +static inline enum tegra_suspend_mode +tegra_pm_validate_suspend_mode(enum tegra_suspend_mode mode) +{ + return TEGRA_SUSPEND_NONE; +} + +static inline void tegra_resume(void) +{ +} +#endif /* CONFIG_PM_SLEEP */ + +#endif /* __SOC_TEGRA_PM_H__ */ diff --git a/include/soc/tegra/pmc.h b/include/soc/tegra/pmc.h new file mode 100644 index 000000000000..65a93273e72f --- /dev/null +++ b/include/soc/tegra/pmc.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2010 Google, Inc + * Copyright (c) 2014 NVIDIA Corporation + * + * Author: + * Colin Cross + * + * 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 __SOC_TEGRA_PMC_H__ +#define __SOC_TEGRA_PMC_H__ + +#include + +#include + +struct clk; +struct reset_control; + +void tegra_pmc_restart(enum reboot_mode mode, const char *cmd); + +#ifdef CONFIG_PM_SLEEP +enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void); +void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode); +void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode); +#endif /* CONFIG_PM_SLEEP */ + +#ifdef CONFIG_SMP +bool tegra_pmc_cpu_is_powered(int cpuid); +int tegra_pmc_cpu_power_on(int cpuid); +int tegra_pmc_cpu_remove_clamping(int cpuid); +#endif /* CONFIG_SMP */ + +/* + * powergate and I/O rail APIs + */ + +#define TEGRA_POWERGATE_CPU 0 +#define TEGRA_POWERGATE_3D 1 +#define TEGRA_POWERGATE_VENC 2 +#define TEGRA_POWERGATE_PCIE 3 +#define TEGRA_POWERGATE_VDEC 4 +#define TEGRA_POWERGATE_L2 5 +#define TEGRA_POWERGATE_MPE 6 +#define TEGRA_POWERGATE_HEG 7 +#define TEGRA_POWERGATE_SATA 8 +#define TEGRA_POWERGATE_CPU1 9 +#define TEGRA_POWERGATE_CPU2 10 +#define TEGRA_POWERGATE_CPU3 11 +#define TEGRA_POWERGATE_CELP 12 +#define TEGRA_POWERGATE_3D1 13 +#define TEGRA_POWERGATE_CPU0 14 +#define TEGRA_POWERGATE_C0NC 15 +#define TEGRA_POWERGATE_C1NC 16 +#define TEGRA_POWERGATE_SOR 17 +#define TEGRA_POWERGATE_DIS 18 +#define TEGRA_POWERGATE_DISB 19 +#define TEGRA_POWERGATE_XUSBA 20 +#define TEGRA_POWERGATE_XUSBB 21 +#define TEGRA_POWERGATE_XUSBC 22 +#define TEGRA_POWERGATE_VIC 23 +#define TEGRA_POWERGATE_IRAM 24 + +#define TEGRA_POWERGATE_3D0 TEGRA_POWERGATE_3D + +#define TEGRA_IO_RAIL_CSIA 0 +#define TEGRA_IO_RAIL_CSIB 1 +#define TEGRA_IO_RAIL_DSI 2 +#define TEGRA_IO_RAIL_MIPI_BIAS 3 +#define TEGRA_IO_RAIL_PEX_BIAS 4 +#define TEGRA_IO_RAIL_PEX_CLK1 5 +#define TEGRA_IO_RAIL_PEX_CLK2 6 +#define TEGRA_IO_RAIL_USB0 9 +#define TEGRA_IO_RAIL_USB1 10 +#define TEGRA_IO_RAIL_USB2 11 +#define TEGRA_IO_RAIL_USB_BIAS 12 +#define TEGRA_IO_RAIL_NAND 13 +#define TEGRA_IO_RAIL_UART 14 +#define TEGRA_IO_RAIL_BB 15 +#define TEGRA_IO_RAIL_AUDIO 17 +#define TEGRA_IO_RAIL_HSIC 19 +#define TEGRA_IO_RAIL_COMP 22 +#define TEGRA_IO_RAIL_HDMI 28 +#define TEGRA_IO_RAIL_PEX_CNTRL 32 +#define TEGRA_IO_RAIL_SDMMC1 33 +#define TEGRA_IO_RAIL_SDMMC3 34 +#define TEGRA_IO_RAIL_SDMMC4 35 +#define TEGRA_IO_RAIL_CAM 36 +#define TEGRA_IO_RAIL_RES 37 +#define TEGRA_IO_RAIL_HV 38 +#define TEGRA_IO_RAIL_DSIB 39 +#define TEGRA_IO_RAIL_DSIC 40 +#define TEGRA_IO_RAIL_DSID 41 +#define TEGRA_IO_RAIL_CSIE 44 +#define TEGRA_IO_RAIL_LVDS 57 +#define TEGRA_IO_RAIL_SYS_DDC 58 + +#ifdef CONFIG_ARCH_TEGRA +int tegra_powergate_is_powered(int id); +int tegra_powergate_power_on(int id); +int tegra_powergate_power_off(int id); +int tegra_powergate_remove_clamping(int id); + +/* Must be called with clk disabled, and returns with clk enabled */ +int tegra_powergate_sequence_power_up(int id, struct clk *clk, + struct reset_control *rst); + +int tegra_io_rail_power_on(int id); +int tegra_io_rail_power_off(int id); +#else +static inline int tegra_powergate_is_powered(int id) +{ + return -ENOSYS; +} + +static inline int tegra_powergate_power_on(int id) +{ + return -ENOSYS; +} + +static inline int tegra_powergate_power_off(int id) +{ + return -ENOSYS; +} + +static inline int tegra_powergate_remove_clamping(int id) +{ + return -ENOSYS; +} + +static inline int tegra_powergate_sequence_power_up(int id, struct clk *clk, + struct reset_control *rst) +{ + return -ENOSYS; +} + +static inline int tegra_io_rail_power_on(int id) +{ + return -ENOSYS; +} + +static inline int tegra_io_rail_power_off(int id) +{ + return -ENOSYS; +} +#endif /* CONFIG_ARCH_TEGRA */ + +#endif /* __SOC_TEGRA_PMC_H__ */ diff --git a/include/soc/tegra/powergate.h b/include/soc/tegra/powergate.h deleted file mode 100644 index c16912ed1a8d..000000000000 --- a/include/soc/tegra/powergate.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (c) 2010 Google, Inc - * - * Author: - * Colin Cross - * - * 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 __SOC_TEGRA_POWERGATE_H__ -#define __SOC_TEGRA_POWERGATE_H__ - -struct clk; -struct reset_control; - -#define TEGRA_POWERGATE_CPU 0 -#define TEGRA_POWERGATE_3D 1 -#define TEGRA_POWERGATE_VENC 2 -#define TEGRA_POWERGATE_PCIE 3 -#define TEGRA_POWERGATE_VDEC 4 -#define TEGRA_POWERGATE_L2 5 -#define TEGRA_POWERGATE_MPE 6 -#define TEGRA_POWERGATE_HEG 7 -#define TEGRA_POWERGATE_SATA 8 -#define TEGRA_POWERGATE_CPU1 9 -#define TEGRA_POWERGATE_CPU2 10 -#define TEGRA_POWERGATE_CPU3 11 -#define TEGRA_POWERGATE_CELP 12 -#define TEGRA_POWERGATE_3D1 13 -#define TEGRA_POWERGATE_CPU0 14 -#define TEGRA_POWERGATE_C0NC 15 -#define TEGRA_POWERGATE_C1NC 16 -#define TEGRA_POWERGATE_SOR 17 -#define TEGRA_POWERGATE_DIS 18 -#define TEGRA_POWERGATE_DISB 19 -#define TEGRA_POWERGATE_XUSBA 20 -#define TEGRA_POWERGATE_XUSBB 21 -#define TEGRA_POWERGATE_XUSBC 22 -#define TEGRA_POWERGATE_VIC 23 -#define TEGRA_POWERGATE_IRAM 24 - -#define TEGRA_POWERGATE_3D0 TEGRA_POWERGATE_3D - -#define TEGRA_IO_RAIL_CSIA 0 -#define TEGRA_IO_RAIL_CSIB 1 -#define TEGRA_IO_RAIL_DSI 2 -#define TEGRA_IO_RAIL_MIPI_BIAS 3 -#define TEGRA_IO_RAIL_PEX_BIAS 4 -#define TEGRA_IO_RAIL_PEX_CLK1 5 -#define TEGRA_IO_RAIL_PEX_CLK2 6 -#define TEGRA_IO_RAIL_USB0 9 -#define TEGRA_IO_RAIL_USB1 10 -#define TEGRA_IO_RAIL_USB2 11 -#define TEGRA_IO_RAIL_USB_BIAS 12 -#define TEGRA_IO_RAIL_NAND 13 -#define TEGRA_IO_RAIL_UART 14 -#define TEGRA_IO_RAIL_BB 15 -#define TEGRA_IO_RAIL_AUDIO 17 -#define TEGRA_IO_RAIL_HSIC 19 -#define TEGRA_IO_RAIL_COMP 22 -#define TEGRA_IO_RAIL_HDMI 28 -#define TEGRA_IO_RAIL_PEX_CNTRL 32 -#define TEGRA_IO_RAIL_SDMMC1 33 -#define TEGRA_IO_RAIL_SDMMC3 34 -#define TEGRA_IO_RAIL_SDMMC4 35 -#define TEGRA_IO_RAIL_CAM 36 -#define TEGRA_IO_RAIL_RES 37 -#define TEGRA_IO_RAIL_HV 38 -#define TEGRA_IO_RAIL_DSIB 39 -#define TEGRA_IO_RAIL_DSIC 40 -#define TEGRA_IO_RAIL_DSID 41 -#define TEGRA_IO_RAIL_CSIE 44 -#define TEGRA_IO_RAIL_LVDS 57 -#define TEGRA_IO_RAIL_SYS_DDC 58 - -#ifdef CONFIG_ARCH_TEGRA -int tegra_powergate_is_powered(int id); -int tegra_powergate_power_on(int id); -int tegra_powergate_power_off(int id); -int tegra_powergate_remove_clamping(int id); - -/* Must be called with clk disabled, and returns with clk enabled */ -int tegra_powergate_sequence_power_up(int id, struct clk *clk, - struct reset_control *rst); - -int tegra_io_rail_power_on(int id); -int tegra_io_rail_power_off(int id); -#else -static inline int tegra_powergate_is_powered(int id) -{ - return -ENOSYS; -} - -static inline int tegra_powergate_power_on(int id) -{ - return -ENOSYS; -} - -static inline int tegra_powergate_power_off(int id) -{ - return -ENOSYS; -} - -static inline int tegra_powergate_remove_clamping(int id) -{ - return -ENOSYS; -} - -static inline int tegra_powergate_sequence_power_up(int id, struct clk *clk, - struct reset_control *rst) -{ - return -ENOSYS; -} - -static inline int tegra_io_rail_power_on(int id) -{ - return -ENOSYS; -} - -static inline int tegra_io_rail_power_off(int id) -{ - return -ENOSYS; -} -#endif - -#endif /* __SOC_TEGRA_POWERGATE_H__ */ -- cgit v1.2.3 From 3be4beaf7c91ec9c6fefa5f11173af37113d10ae Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Tue, 20 Aug 2013 14:36:27 -0400 Subject: KEYS: verify a certificate is signed by a 'trusted' key Only public keys, with certificates signed by an existing 'trusted' key on the system trusted keyring, should be added to a trusted keyring. This patch adds support for verifying a certificate's signature. This is derived from David Howells pkcs7_request_asymmetric_key() patch. Changelog v6: - on error free key - Dmitry - validate trust only for not already trusted keys - Dmitry - formatting cleanup Changelog: - define get_system_trusted_keyring() to fix kbuild issues Signed-off-by: Mimi Zohar Signed-off-by: David Howells Acked-by: Dmitry Kasatkin --- crypto/asymmetric_keys/x509_public_key.c | 87 +++++++++++++++++++++++++++++++- include/keys/system_keyring.h | 10 +++- 2 files changed, 95 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 382ef0d2ff2e..436fbd8552fc 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -18,11 +18,61 @@ #include #include #include +#include #include #include "asymmetric_keys.h" #include "public_key.h" #include "x509_parser.h" +/* + * Find a key in the given keyring by issuer and authority. + */ +static struct key *x509_request_asymmetric_key(struct key *keyring, + const char *signer, + size_t signer_len, + const char *authority, + size_t auth_len) +{ + key_ref_t key; + char *id; + + /* Construct an identifier. */ + id = kmalloc(signer_len + 2 + auth_len + 1, GFP_KERNEL); + if (!id) + return ERR_PTR(-ENOMEM); + + memcpy(id, signer, signer_len); + id[signer_len + 0] = ':'; + id[signer_len + 1] = ' '; + memcpy(id + signer_len + 2, authority, auth_len); + id[signer_len + 2 + auth_len] = 0; + + pr_debug("Look up: \"%s\"\n", id); + + key = keyring_search(make_key_ref(keyring, 1), + &key_type_asymmetric, id); + if (IS_ERR(key)) + pr_debug("Request for module key '%s' err %ld\n", + id, PTR_ERR(key)); + kfree(id); + + if (IS_ERR(key)) { + switch (PTR_ERR(key)) { + /* Hide some search errors */ + case -EACCES: + case -ENOTDIR: + case -EAGAIN: + return ERR_PTR(-ENOKEY); + default: + return ERR_CAST(key); + } + } + + pr_devel("<==%s() = 0 [%x]\n", __func__, + key_serial(key_ref_to_ptr(key))); + return key_ref_to_ptr(key); +} + /* * Set up the signature parameters in an X.509 certificate. This involves * digesting the signed data and extracting the signature. @@ -102,6 +152,37 @@ int x509_check_signature(const struct public_key *pub, } EXPORT_SYMBOL_GPL(x509_check_signature); +/* + * Check the new certificate against the ones in the trust keyring. If one of + * those is the signing key and validates the new certificate, then mark the + * new certificate as being trusted. + * + * Return 0 if the new certificate was successfully validated, 1 if we couldn't + * find a matching parent certificate in the trusted list and an error if there + * is a matching certificate but the signature check fails. + */ +static int x509_validate_trust(struct x509_certificate *cert, + struct key *trust_keyring) +{ + const struct public_key *pk; + struct key *key; + int ret = 1; + + if (!trust_keyring) + return -EOPNOTSUPP; + + key = x509_request_asymmetric_key(trust_keyring, + cert->issuer, strlen(cert->issuer), + cert->authority, + strlen(cert->authority)); + if (!IS_ERR(key)) { + pk = key->payload.data; + ret = x509_check_signature(pk, cert); + key_put(key); + } + return ret; +} + /* * Attempt to parse a data blob for a key as an X509 certificate. */ @@ -155,9 +236,13 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) /* Check the signature on the key if it appears to be self-signed */ if (!cert->authority || strcmp(cert->fingerprint, cert->authority) == 0) { - ret = x509_check_signature(cert->pub, cert); + ret = x509_check_signature(cert->pub, cert); /* self-signed */ if (ret < 0) goto error_free_cert; + } else if (!prep->trusted) { + ret = x509_validate_trust(cert, get_system_trusted_keyring()); + if (!ret) + prep->trusted = 1; } /* Propose a description */ diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h index 8dabc399bd1d..72665eb80692 100644 --- a/include/keys/system_keyring.h +++ b/include/keys/system_keyring.h @@ -17,7 +17,15 @@ #include extern struct key *system_trusted_keyring; - +static inline struct key *get_system_trusted_keyring(void) +{ + return system_trusted_keyring; +} +#else +static inline struct key *get_system_trusted_keyring(void) +{ + return NULL; +} #endif #endif /* _KEYS_SYSTEM_KEYRING_H */ -- cgit v1.2.3 From 32c4741cb66703a3c282f41d77deff4afd93342a Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Tue, 17 Jun 2014 11:56:59 +0300 Subject: KEYS: validate certificate trust only with builtin keys Instead of allowing public keys, with certificates signed by any key on the system trusted keyring, to be added to a trusted keyring, this patch further restricts the certificates to those signed only by builtin keys on the system keyring. This patch defines a new option 'builtin' for the kernel parameter 'keys_ownerid' to allow trust validation using builtin keys. Simplified Mimi's "KEYS: define an owner trusted keyring" patch Changelog v7: - rename builtin_keys to use_builtin_keys Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- Documentation/kernel-parameters.txt | 2 +- crypto/asymmetric_keys/x509_public_key.c | 9 ++++++--- include/linux/key.h | 1 + kernel/system_keyring.c | 1 + 4 files changed, 9 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index bdb193afe176..90c12c591168 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -569,7 +569,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted. ca_keys= [KEYS] This parameter identifies a specific key(s) on the system trusted keyring to be used for certificate trust validation. - format: id: + format: { id: | builtin } ccw_timeout_log [S390] See Documentation/s390/CommonIO for details. diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index d376195e1d08..927ce755ff67 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -24,6 +24,7 @@ #include "public_key.h" #include "x509_parser.h" +static bool use_builtin_keys; static char *ca_keyid; #ifndef MODULE @@ -34,6 +35,8 @@ static int __init ca_keys_setup(char *str) if (strncmp(str, "id:", 3) == 0) ca_keyid = str; /* owner key 'id:xxxxxx' */ + else if (strcmp(str, "builtin") == 0) + use_builtin_keys = true; return 1; } @@ -180,7 +183,6 @@ EXPORT_SYMBOL_GPL(x509_check_signature); static int x509_validate_trust(struct x509_certificate *cert, struct key *trust_keyring) { - const struct public_key *pk; struct key *key; int ret = 1; @@ -195,8 +197,9 @@ static int x509_validate_trust(struct x509_certificate *cert, cert->authority, strlen(cert->authority)); if (!IS_ERR(key)) { - pk = key->payload.data; - ret = x509_check_signature(pk, cert); + if (!use_builtin_keys + || test_bit(KEY_FLAG_BUILTIN, &key->flags)) + ret = x509_check_signature(key->payload.data, cert); key_put(key); } return ret; diff --git a/include/linux/key.h b/include/linux/key.h index 017b0826642f..65316f7ae794 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -170,6 +170,7 @@ struct key { #define KEY_FLAG_INVALIDATED 7 /* set if key has been invalidated */ #define KEY_FLAG_TRUSTED 8 /* set if key is trusted */ #define KEY_FLAG_TRUSTED_ONLY 9 /* set if keyring only accepts links to trusted keys */ +#define KEY_FLAG_BUILTIN 10 /* set if key is builtin */ /* the key type and key description string * - the desc is used to match a key against search criteria diff --git a/kernel/system_keyring.c b/kernel/system_keyring.c index 52ebc70263f4..875f64e8935b 100644 --- a/kernel/system_keyring.c +++ b/kernel/system_keyring.c @@ -89,6 +89,7 @@ static __init int load_system_certificate_list(void) pr_err("Problem loading in-kernel X.509 certificate (%ld)\n", PTR_ERR(key)); } else { + set_bit(KEY_FLAG_BUILTIN, &key_ref_to_ptr(key)->flags); pr_notice("Loaded X.509 cert '%s'\n", key_ref_to_ptr(key)->description); key_ref_put(key); -- cgit v1.2.3 From 7e6a68210784dcea8e39fd9d4c9966f9c733ba09 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 27 Mar 2014 11:25:45 -0300 Subject: [media] videodev2.h: add initial support for compound controls Compound controls are controls that can be used for compound and array types. This allows for more compound data structures to be used with the control framework. The existing V4L2_CTRL_FLAG_NEXT_CTRL flag will only enumerate non-compound controls, so a new V4L2_CTRL_FLAG_NEXT_COMPOUND flag is added to enumerate compound controls. Set both flags to enumerate any control (compound or not). Compound control types will start at V4L2_CTRL_COMPOUND_TYPES. In addition, any control that uses the new 'ptr' field or the existing 'string' field will have flag V4L2_CTRL_FLAG_HAS_PAYLOAD set. While not strictly necessary, adding that flag makes life for applications a lot simpler. If the flag is not set, then the control value is set through the value or value64 fields of struct v4l2_ext_control, otherwise a pointer points to the value. Signed-off-by: Hans Verkuil Reviewed-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/videodev2.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 168ff507bf75..438c4a65c5b2 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1254,6 +1254,7 @@ struct v4l2_ext_control { __s32 value; __s64 value64; char *string; + void *ptr; }; } __attribute__ ((packed)); @@ -1278,7 +1279,10 @@ enum v4l2_ctrl_type { V4L2_CTRL_TYPE_CTRL_CLASS = 6, V4L2_CTRL_TYPE_STRING = 7, V4L2_CTRL_TYPE_BITMASK = 8, - V4L2_CTRL_TYPE_INTEGER_MENU = 9, + V4L2_CTRL_TYPE_INTEGER_MENU = 9, + + /* Compound types are >= 0x0100 */ + V4L2_CTRL_COMPOUND_TYPES = 0x0100, }; /* Used in the VIDIOC_QUERYCTRL ioctl for querying controls */ @@ -1314,9 +1318,11 @@ struct v4l2_querymenu { #define V4L2_CTRL_FLAG_SLIDER 0x0020 #define V4L2_CTRL_FLAG_WRITE_ONLY 0x0040 #define V4L2_CTRL_FLAG_VOLATILE 0x0080 +#define V4L2_CTRL_FLAG_HAS_PAYLOAD 0x0100 -/* Query flag, to be ORed with the control ID */ +/* Query flags, to be ORed with the control ID */ #define V4L2_CTRL_FLAG_NEXT_CTRL 0x80000000 +#define V4L2_CTRL_FLAG_NEXT_COMPOUND 0x40000000 /* User-class control IDs defined by V4L2 */ #define V4L2_CID_MAX_CTRLS 1024 -- cgit v1.2.3 From 5082c2417841e64df975789011e182ce99a9dacd Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 10 Jun 2014 04:14:50 -0300 Subject: [media] videodev2.h: add struct v4l2_query_ext_ctrl and VIDIOC_QUERY_EXT_CTRL Add a new struct and ioctl to extend the amount of information you can get for a control. The range is now a s64 type, and array dimensions and element size can be reported through nr_of_dims/dims/elems/elem_size. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/videodev2.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 438c4a65c5b2..0297980fe491 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1269,6 +1269,7 @@ struct v4l2_ext_controls { #define V4L2_CTRL_ID_MASK (0x0fffffff) #define V4L2_CTRL_ID2CLASS(id) ((id) & 0x0fff0000UL) #define V4L2_CTRL_DRIVER_PRIV(id) (((id) & 0xffff) >= 0x1000) +#define V4L2_CTRL_MAX_DIMS (4) enum v4l2_ctrl_type { V4L2_CTRL_TYPE_INTEGER = 1, @@ -1298,6 +1299,23 @@ struct v4l2_queryctrl { __u32 reserved[2]; }; +/* Used in the VIDIOC_QUERY_EXT_CTRL ioctl for querying extended controls */ +struct v4l2_query_ext_ctrl { + __u32 id; + __u32 type; + char name[32]; + __s64 minimum; + __s64 maximum; + __u64 step; + __s64 default_value; + __u32 flags; + __u32 elem_size; + __u32 elems; + __u32 nr_of_dims; + __u32 dims[V4L2_CTRL_MAX_DIMS]; + __u32 reserved[32]; +}; + /* Used in the VIDIOC_QUERYMENU ioctl for querying menu items */ struct v4l2_querymenu { __u32 id; @@ -2011,6 +2029,8 @@ struct v4l2_create_buffers { Never use these in applications! */ #define VIDIOC_DBG_G_CHIP_INFO _IOWR('V', 102, struct v4l2_dbg_chip_info) +#define VIDIOC_QUERY_EXT_CTRL _IOWR('V', 103, struct v4l2_query_ext_ctrl) + /* 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 d9a2547150245f34a050f744ea46542c44792652 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 12 Jun 2014 07:54:16 -0300 Subject: [media] v4l2-ctrls: add support for compound types This patch implements initial support for compound types. The changes are fairly obvious: basic support for is_ptr types, the type_is_int function is replaced by a is_int bitfield, and v4l2_query_ext_ctrl is added. Note that this patch does not yet add support for N-dimensional arrays, that comes later. So v4l2_query_ext_ctrl just sets elems to 1 and nr_of_dims and dims[] are all zero. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 224 ++++++++++++++++++++++++++--------- include/media/v4l2-ctrls.h | 23 +++- 2 files changed, 187 insertions(+), 60 deletions(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 365884b3abad..d5cd1aab970b 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1124,20 +1124,6 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, } EXPORT_SYMBOL(v4l2_ctrl_fill); -/* Helper function to determine whether the control type is compatible with - VIDIOC_G/S_CTRL. */ -static bool type_is_int(const struct v4l2_ctrl *ctrl) -{ - switch (ctrl->type) { - case V4L2_CTRL_TYPE_INTEGER64: - case V4L2_CTRL_TYPE_STRING: - /* Nope, these need v4l2_ext_control */ - return false; - default: - return true; - } -} - static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 changes) { memset(ev->reserved, 0, sizeof(ev->reserved)); @@ -1146,7 +1132,7 @@ static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 change ev->u.ctrl.changes = changes; ev->u.ctrl.type = ctrl->type; ev->u.ctrl.flags = ctrl->flags; - if (ctrl->type == V4L2_CTRL_TYPE_STRING) + if (ctrl->is_ptr) ev->u.ctrl.value64 = 0; else ev->u.ctrl.value64 = ctrl->cur.val64; @@ -1181,6 +1167,9 @@ static int cur_to_user(struct v4l2_ext_control *c, { u32 len; + if (ctrl->is_ptr && !ctrl->is_string) + return copy_to_user(c->ptr, ctrl->cur.p, ctrl->elem_size); + switch (ctrl->type) { case V4L2_CTRL_TYPE_STRING: len = strlen(ctrl->cur.string); @@ -1208,6 +1197,9 @@ static int user_to_new(struct v4l2_ext_control *c, u32 size; ctrl->is_new = 1; + if (ctrl->is_ptr && !ctrl->is_string) + return copy_from_user(ctrl->p, c->ptr, ctrl->elem_size); + switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER64: ctrl->val64 = c->value64; @@ -1242,6 +1234,9 @@ static int new_to_user(struct v4l2_ext_control *c, { u32 len; + if (ctrl->is_ptr && !ctrl->is_string) + return copy_to_user(c->ptr, ctrl->p, ctrl->elem_size); + switch (ctrl->type) { case V4L2_CTRL_TYPE_STRING: len = strlen(ctrl->string); @@ -1268,6 +1263,7 @@ static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags) if (ctrl == NULL) return; + switch (ctrl->type) { case V4L2_CTRL_TYPE_BUTTON: changed = true; @@ -1282,8 +1278,13 @@ static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags) ctrl->cur.val64 = ctrl->val64; break; default: - changed = ctrl->val != ctrl->cur.val; - ctrl->cur.val = ctrl->val; + if (ctrl->is_ptr) { + changed = memcmp(ctrl->p, ctrl->cur.p, ctrl->elem_size); + memcpy(ctrl->cur.p, ctrl->p, ctrl->elem_size); + } else { + changed = ctrl->val != ctrl->cur.val; + ctrl->cur.val = ctrl->val; + } break; } if (ch_flags & V4L2_EVENT_CTRL_CH_FLAGS) { @@ -1323,7 +1324,10 @@ static void cur_to_new(struct v4l2_ctrl *ctrl) ctrl->val64 = ctrl->cur.val64; break; default: - ctrl->val = ctrl->cur.val; + if (ctrl->is_ptr) + memcpy(ctrl->p, ctrl->cur.p, ctrl->elem_size); + else + ctrl->val = ctrl->cur.val; break; } } @@ -1536,7 +1540,7 @@ static struct v4l2_ctrl_ref *find_private_ref( VIDIOC_G/S_CTRL. */ if (V4L2_CTRL_ID2CLASS(ref->ctrl->id) == V4L2_CTRL_CLASS_USER && V4L2_CTRL_DRIVER_PRIV(ref->ctrl->id)) { - if (!type_is_int(ref->ctrl)) + if (!ref->ctrl->is_int) continue; if (id == 0) return ref; @@ -1606,8 +1610,12 @@ static int handler_new_ref(struct v4l2_ctrl_handler *hdl, u32 class_ctrl = V4L2_CTRL_ID2CLASS(id) | 1; int bucket = id % hdl->nr_of_buckets; /* which bucket to use */ - /* Automatically add the control class if it is not yet present. */ - if (id != class_ctrl && find_ref_lock(hdl, class_ctrl) == NULL) + /* + * Automatically add the control class if it is not yet present and + * the new control is not a compound control. + */ + if (ctrl->type < V4L2_CTRL_COMPOUND_TYPES && + id != class_ctrl && find_ref_lock(hdl, class_ctrl) == NULL) if (!v4l2_ctrl_new_std(hdl, NULL, class_ctrl, 0, 0, 0, 0)) return hdl->error; @@ -1668,18 +1676,28 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, const char *name, enum v4l2_ctrl_type type, s64 min, s64 max, u64 step, s64 def, + u32 elem_size, u32 flags, const char * const *qmenu, const s64 *qmenu_int, void *priv) { struct v4l2_ctrl *ctrl; - unsigned sz_extra = 0; + unsigned sz_extra; + void *data; int err; if (hdl->error) return NULL; + if (type == V4L2_CTRL_TYPE_INTEGER64) + elem_size = sizeof(s64); + else if (type == V4L2_CTRL_TYPE_STRING) + elem_size = max + 1; + else if (type < V4L2_CTRL_COMPOUND_TYPES) + elem_size = sizeof(s32); + /* Sanity checks */ if (id == 0 || name == NULL || id >= V4L2_CID_PRIVATE_BASE || + elem_size == 0 || (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) || (type == V4L2_CTRL_TYPE_INTEGER_MENU && qmenu_int == NULL)) { handler_set_err(hdl, -ERANGE); @@ -1695,12 +1713,13 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, return NULL; } + sz_extra = 0; if (type == V4L2_CTRL_TYPE_BUTTON) flags |= V4L2_CTRL_FLAG_WRITE_ONLY; else if (type == V4L2_CTRL_TYPE_CTRL_CLASS) flags |= V4L2_CTRL_FLAG_READ_ONLY; - else if (type == V4L2_CTRL_TYPE_STRING) - sz_extra += 2 * (max + 1); + else if (type == V4L2_CTRL_TYPE_STRING || type >= V4L2_CTRL_COMPOUND_TYPES) + sz_extra += 2 * elem_size; ctrl = kzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL); if (ctrl == NULL) { @@ -1719,18 +1738,28 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->minimum = min; ctrl->maximum = max; ctrl->step = step; + ctrl->default_value = def; + ctrl->is_string = type == V4L2_CTRL_TYPE_STRING; + ctrl->is_ptr = type >= V4L2_CTRL_COMPOUND_TYPES || ctrl->is_string; + ctrl->is_int = !ctrl->is_ptr && type != V4L2_CTRL_TYPE_INTEGER64; + ctrl->elem_size = elem_size; if (type == V4L2_CTRL_TYPE_MENU) ctrl->qmenu = qmenu; else if (type == V4L2_CTRL_TYPE_INTEGER_MENU) ctrl->qmenu_int = qmenu_int; ctrl->priv = priv; - ctrl->cur.val = ctrl->val = ctrl->default_value = def; + ctrl->cur.val = ctrl->val = def; + data = &ctrl->cur + 1; + + if (ctrl->is_string) { + ctrl->string = data; + ctrl->cur.string = data + elem_size; - if (ctrl->type == V4L2_CTRL_TYPE_STRING) { - ctrl->cur.string = (char *)&ctrl[1] + sz_extra - (max + 1); - ctrl->string = (char *)&ctrl[1] + sz_extra - 2 * (max + 1); if (ctrl->minimum) memset(ctrl->cur.string, ' ', ctrl->minimum); + } else if (ctrl->is_ptr) { + ctrl->p = data; + ctrl->cur.p = data + elem_size; } if (handler_new_ref(hdl, ctrl)) { kfree(ctrl); @@ -1778,7 +1807,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->id, name, type, min, max, is_menu ? cfg->menu_skip_mask : step, - def, flags, qmenu, qmenu_int, priv); + def, cfg->elem_size, + flags, qmenu, qmenu_int, priv); if (ctrl) ctrl->is_private = cfg->is_private; return ctrl; @@ -1795,13 +1825,15 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, u32 flags; v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags); - if (type == V4L2_CTRL_TYPE_MENU - || type == V4L2_CTRL_TYPE_INTEGER_MENU) { + if (type == V4L2_CTRL_TYPE_MENU || + type == V4L2_CTRL_TYPE_INTEGER_MENU || + type >= V4L2_CTRL_COMPOUND_TYPES) { handler_set_err(hdl, -EINVAL); return NULL; } return v4l2_ctrl_new(hdl, ops, id, name, type, - min, max, step, def, flags, NULL, NULL, NULL); + min, max, step, def, 0, + flags, NULL, NULL, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std); @@ -1833,7 +1865,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, id, name, type, - 0, max, mask, def, flags, qmenu, qmenu_int, NULL); + 0, max, mask, def, 0, + flags, qmenu, qmenu_int, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std_menu); @@ -1864,7 +1897,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, id, name, type, 0, max, mask, def, - flags, qmenu, NULL, NULL); + 0, flags, qmenu, NULL, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items); @@ -1888,7 +1921,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, id, name, type, - 0, max, 0, def, flags, NULL, qmenu_int, NULL); + 0, max, 0, def, 0, + flags, NULL, qmenu_int, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_int_menu); @@ -2177,9 +2211,10 @@ int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl) } EXPORT_SYMBOL(v4l2_ctrl_handler_setup); -/* Implement VIDIOC_QUERYCTRL */ -int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) +/* Implement VIDIOC_QUERY_EXT_CTRL */ +int v4l2_query_ext_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_query_ext_ctrl *qc) { + const unsigned next_flags = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND; u32 id = qc->id & V4L2_CTRL_ID_MASK; struct v4l2_ctrl_ref *ref; struct v4l2_ctrl *ctrl; @@ -2192,7 +2227,20 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) /* Try to find it */ ref = find_ref(hdl, id); - if ((qc->id & V4L2_CTRL_FLAG_NEXT_CTRL) && !list_empty(&hdl->ctrl_refs)) { + if ((qc->id & next_flags) && !list_empty(&hdl->ctrl_refs)) { + bool is_compound; + /* Match any control that is not hidden */ + unsigned mask = 1; + bool match = false; + + if ((qc->id & next_flags) == V4L2_CTRL_FLAG_NEXT_COMPOUND) { + /* Match any hidden control */ + match = true; + } else if ((qc->id & next_flags) == next_flags) { + /* Match any control, compound or not */ + mask = 0; + } + /* Find the next control with ID > qc->id */ /* Did we reach the end of the control list? */ @@ -2200,19 +2248,34 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) ref = NULL; /* Yes, so there is no next control */ } else if (ref) { /* We found a control with the given ID, so just get - the next one in the list. */ - ref = list_entry(ref->node.next, typeof(*ref), node); + the next valid one in the list. */ + list_for_each_entry_continue(ref, &hdl->ctrl_refs, node) { + is_compound = + ref->ctrl->type >= V4L2_CTRL_COMPOUND_TYPES; + if (id < ref->ctrl->id && + (is_compound & mask) == match) + break; + } + if (&ref->node == &hdl->ctrl_refs) + ref = NULL; } else { /* No control with the given ID exists, so start searching for the next largest ID. We know there is one, otherwise the first 'if' above would have been true. */ - list_for_each_entry(ref, &hdl->ctrl_refs, node) - if (id < ref->ctrl->id) + list_for_each_entry(ref, &hdl->ctrl_refs, node) { + is_compound = + ref->ctrl->type >= V4L2_CTRL_COMPOUND_TYPES; + if (id < ref->ctrl->id && + (is_compound & mask) == match) break; + } + if (&ref->node == &hdl->ctrl_refs) + ref = NULL; } } mutex_unlock(hdl->lock); + if (!ref) return -EINVAL; @@ -2223,6 +2286,12 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) else qc->id = ctrl->id; strlcpy(qc->name, ctrl->name, sizeof(qc->name)); + qc->flags = ctrl->flags; + qc->type = ctrl->type; + if (ctrl->is_ptr) + qc->flags |= V4L2_CTRL_FLAG_HAS_PAYLOAD; + qc->elem_size = ctrl->elem_size; + qc->elems = 1; qc->minimum = ctrl->minimum; qc->maximum = ctrl->maximum; qc->default_value = ctrl->default_value; @@ -2231,15 +2300,50 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) qc->step = 1; else qc->step = ctrl->step; - qc->flags = ctrl->flags; - qc->type = ctrl->type; + return 0; +} +EXPORT_SYMBOL(v4l2_query_ext_ctrl); + +/* Implement VIDIOC_QUERYCTRL */ +int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) +{ + struct v4l2_query_ext_ctrl qec = { qc->id }; + int rc; + + rc = v4l2_query_ext_ctrl(hdl, &qec); + if (rc) + return rc; + + qc->id = qec.id; + qc->type = qec.type; + qc->flags = qec.flags; + strlcpy(qc->name, qec.name, sizeof(qc->name)); + switch (qc->type) { + case V4L2_CTRL_TYPE_INTEGER: + case V4L2_CTRL_TYPE_BOOLEAN: + case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_INTEGER_MENU: + case V4L2_CTRL_TYPE_STRING: + case V4L2_CTRL_TYPE_BITMASK: + qc->minimum = qec.minimum; + qc->maximum = qec.maximum; + qc->step = qec.step; + qc->default_value = qec.default_value; + break; + default: + qc->minimum = 0; + qc->maximum = 0; + qc->step = 0; + qc->default_value = 0; + break; + } return 0; } EXPORT_SYMBOL(v4l2_queryctrl); int v4l2_subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) { - if (qc->id & V4L2_CTRL_FLAG_NEXT_CTRL) + if (qc->id & (V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND)) return -EINVAL; return v4l2_queryctrl(sd->ctrl_handler, qc); } @@ -2339,7 +2443,8 @@ EXPORT_SYMBOL(v4l2_subdev_querymenu); Find the controls in the control array and do some basic checks. */ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs, - struct v4l2_ctrl_helper *helpers) + struct v4l2_ctrl_helper *helpers, + bool get) { struct v4l2_ctrl_helper *h; bool have_clusters = false; @@ -2371,6 +2476,13 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl, have_clusters = true; if (ctrl->cluster[0] != ctrl) ref = find_ref_lock(hdl, ctrl->cluster[0]->id); + if (ctrl->is_ptr && !ctrl->is_string && c->size < ctrl->elem_size) { + if (get) { + c->size = ctrl->elem_size; + return -ENOSPC; + } + return -EFAULT; + } /* Store the ref to the master control of the cluster */ h->mref = ref; h->ctrl = ctrl; @@ -2451,7 +2563,7 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs return -ENOMEM; } - ret = prepare_ext_ctrls(hdl, cs, helpers); + ret = prepare_ext_ctrls(hdl, cs, helpers, true); cs->error_idx = cs->count; for (i = 0; !ret && i < cs->count; i++) @@ -2513,11 +2625,11 @@ static int get_ctrl(struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c) int ret = 0; int i; - /* String controls are not supported. The new_to_user() and + /* Compound controls are not supported. The new_to_user() and * cur_to_user() calls below would need to be modified not to access * userspace memory when called from get_ctrl(). */ - if (ctrl->type == V4L2_CTRL_TYPE_STRING) + if (!ctrl->is_int) return -EINVAL; if (ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY) @@ -2543,7 +2655,7 @@ int v4l2_g_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *control) struct v4l2_ext_control c; int ret; - if (ctrl == NULL || !type_is_int(ctrl)) + if (ctrl == NULL || !ctrl->is_int) return -EINVAL; ret = get_ctrl(ctrl, &c); control->value = c.value; @@ -2562,7 +2674,7 @@ s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl) struct v4l2_ext_control c; /* It's a driver bug if this happens. */ - WARN_ON(!type_is_int(ctrl)); + WARN_ON(!ctrl->is_int); c.value = 0; get_ctrl(ctrl, &c); return c.value; @@ -2698,7 +2810,7 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, if (!helpers) return -ENOMEM; } - ret = prepare_ext_ctrls(hdl, cs, helpers); + ret = prepare_ext_ctrls(hdl, cs, helpers, false); if (!ret) ret = validate_ctrls(cs, helpers, set); if (ret && set) @@ -2803,11 +2915,11 @@ static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, struct v4l2_ctrl *master = ctrl->cluster[0]; int i; - /* String controls are not supported. The user_to_new() and + /* Compound controls are not supported. The user_to_new() and * cur_to_user() calls below would need to be modified not to access * userspace memory when called from set_ctrl(). */ - if (ctrl->type == V4L2_CTRL_TYPE_STRING) + if (ctrl->is_ptr) return -EINVAL; /* Reset the 'is_new' flags of the cluster */ @@ -2849,7 +2961,7 @@ int v4l2_s_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, struct v4l2_ext_control c; int ret; - if (ctrl == NULL || !type_is_int(ctrl)) + if (ctrl == NULL || !ctrl->is_int) return -EINVAL; if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY) @@ -2873,7 +2985,7 @@ int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val) struct v4l2_ext_control c; /* It's a driver bug if this happens. */ - WARN_ON(!type_is_int(ctrl)); + WARN_ON(!ctrl->is_int); c.value = val; return set_ctrl_lock(NULL, ctrl, &c); } diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index a2e8f038a8ed..9024daebdf3b 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -73,6 +73,12 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv); * members are in 'automatic' mode or 'manual' mode. This is * used for autogain/gain type clusters. Drivers should never * set this flag directly. + * @is_int: If set, then this control has a simple integer value (i.e. it + * uses ctrl->val). + * @is_string: If set, then this control has type V4L2_CTRL_TYPE_STRING. + * @is_ptr: If set, then this control is an array and/or has type >= V4L2_CTRL_COMPOUND_TYPES + * and/or has type V4L2_CTRL_TYPE_STRING. In other words, struct + * v4l2_ext_control uses field p to point to the data. * @has_volatiles: If set, then one or more members of the cluster are volatile. * Drivers should never touch this flag. * @call_notify: If set, then call the handler's notify function whenever the @@ -90,6 +96,7 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv); * @maximum: The control's maximum value. * @default_value: The control's default value. * @step: The control's step value for non-menu controls. + * @elem_size: The size in bytes of the control. * @menu_skip_mask: The control's skip mask for menu controls. This makes it * easy to skip menu items that are not valid. If bit X is set, * then menu item X is skipped. Of course, this only works for @@ -104,7 +111,6 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv); * @cur: The control's current value. * @val: The control's new s32 value. * @val64: The control's new s64 value. - * @string: The control's new string value. * @priv: The control's private pointer. For use by the driver. It is * untouched by the control framework. Note that this pointer is * not freed when the control is deleted. Should this be needed @@ -123,6 +129,9 @@ struct v4l2_ctrl { unsigned int is_new:1; unsigned int is_private:1; unsigned int is_auto:1; + unsigned int is_int:1; + unsigned int is_string:1; + unsigned int is_ptr:1; unsigned int has_volatiles:1; unsigned int call_notify:1; unsigned int manual_mode_value:8; @@ -132,6 +141,7 @@ struct v4l2_ctrl { const char *name; enum v4l2_ctrl_type type; s64 minimum, maximum, default_value; + u32 elem_size; union { u64 step; u64 menu_skip_mask; @@ -141,17 +151,19 @@ struct v4l2_ctrl { const s64 *qmenu_int; }; unsigned long flags; + void *priv; union { s32 val; s64 val64; char *string; - } cur; + void *p; + }; union { s32 val; s64 val64; char *string; - }; - void *priv; + void *p; + } cur; }; /** struct v4l2_ctrl_ref - The control reference. @@ -212,6 +224,7 @@ struct v4l2_ctrl_handler { * @max: The control's maximum value. * @step: The control's step value for non-menu controls. * @def: The control's default value. + * @elem_size: The size in bytes of the control. * @flags: The control's flags. * @menu_skip_mask: The control's skip mask for menu controls. This makes it * easy to skip menu items that are not valid. If bit X is set, @@ -235,6 +248,7 @@ struct v4l2_ctrl_config { s64 max; u64 step; s64 def; + u32 elem_size; u32 flags; u64 menu_skip_mask; const char * const *qmenu; @@ -659,6 +673,7 @@ unsigned int v4l2_ctrl_poll(struct file *file, struct poll_table_struct *wait); /* Helpers for ioctl_ops. If hdl == NULL then they will all return -EINVAL. */ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc); +int v4l2_query_ext_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_query_ext_ctrl *qc); int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm); int v4l2_g_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *ctrl); int v4l2_s_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, -- cgit v1.2.3 From e6bee3685e732df82f48698254a36754cf15f0b0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 10 Jun 2014 04:22:06 -0300 Subject: [media] v4l2: integrate support for VIDIOC_QUERY_EXT_CTRL Add the v4l2 core plumbing for the new VIDIOC_QUERY_EXT_CTRL ioctl. Signed-off-by: Hans Verkuil Reviewed-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dev.c | 2 ++ drivers/media/v4l2-core/v4l2-ioctl.c | 33 +++++++++++++++++++++++++++++++++ drivers/media/v4l2-core/v4l2-subdev.c | 3 +++ include/media/v4l2-ioctl.h | 2 ++ 4 files changed, 40 insertions(+) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 35698aa377f5..1cfdbdd034bc 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -575,6 +575,8 @@ static void determine_valid_ioctls(struct video_device *vdev) be valid if the filehandle passed the control handler. */ if (vdev->ctrl_handler || ops->vidioc_queryctrl) set_bit(_IOC_NR(VIDIOC_QUERYCTRL), valid_ioctls); + if (vdev->ctrl_handler || ops->vidioc_query_ext_ctrl) + set_bit(_IOC_NR(VIDIOC_QUERY_EXT_CTRL), valid_ioctls); if (vdev->ctrl_handler || ops->vidioc_g_ctrl || ops->vidioc_g_ext_ctrls) set_bit(_IOC_NR(VIDIOC_G_CTRL), valid_ioctls); if (vdev->ctrl_handler || ops->vidioc_s_ctrl || ops->vidioc_s_ext_ctrls) diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 8d4a25d62e47..c38a6208d2cd 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -525,6 +525,21 @@ static void v4l_print_queryctrl(const void *arg, bool write_only) p->step, p->default_value, p->flags); } +static void v4l_print_query_ext_ctrl(const void *arg, bool write_only) +{ + const struct v4l2_query_ext_ctrl *p = arg; + + pr_cont("id=0x%x, type=%d, name=%.*s, min/max=%lld/%lld, " + "step=%lld, default=%lld, flags=0x%08x, elem_size=%u, elems=%u, " + "nr_of_dims=%u, dims=%u,%u,%u,%u,%u,%u,%u,%u\n", + p->id, p->type, (int)sizeof(p->name), p->name, + p->minimum, p->maximum, + p->step, p->default_value, p->flags, + p->elem_size, p->elems, p->nr_of_dims, + p->dims[0], p->dims[1], p->dims[2], p->dims[3], + p->dims[4], p->dims[5], p->dims[6], p->dims[7]); +} + static void v4l_print_querymenu(const void *arg, bool write_only) { const struct v4l2_querymenu *p = arg; @@ -1561,6 +1576,23 @@ static int v4l_queryctrl(const struct v4l2_ioctl_ops *ops, return -ENOTTY; } +static int v4l_query_ext_ctrl(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct video_device *vfd = video_devdata(file); + struct v4l2_query_ext_ctrl *p = arg; + struct v4l2_fh *vfh = + test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL; + + if (vfh && vfh->ctrl_handler) + return v4l2_query_ext_ctrl(vfh->ctrl_handler, p); + if (vfd->ctrl_handler) + return v4l2_query_ext_ctrl(vfd->ctrl_handler, p); + if (ops->vidioc_query_ext_ctrl) + return ops->vidioc_query_ext_ctrl(file, fh, p); + return -ENOTTY; +} + static int v4l_querymenu(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { @@ -2121,6 +2153,7 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { 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), IOCTL_INFO_FNC(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info, v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)), + IOCTL_INFO_FNC(VIDIOC_QUERY_EXT_CTRL, v4l_query_ext_ctrl, v4l_print_query_ext_ctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_query_ext_ctrl, id)), }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 058c1a6e8392..b984f33de7f4 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -139,6 +139,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_QUERYCTRL: return v4l2_queryctrl(vfh->ctrl_handler, arg); + case VIDIOC_QUERY_EXT_CTRL: + return v4l2_query_ext_ctrl(vfh->ctrl_handler, arg); + case VIDIOC_QUERYMENU: return v4l2_querymenu(vfh->ctrl_handler, arg); diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index 50cf7c110a70..53605f0f9903 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -158,6 +158,8 @@ struct v4l2_ioctl_ops { /* Control handling */ int (*vidioc_queryctrl) (struct file *file, void *fh, struct v4l2_queryctrl *a); + int (*vidioc_query_ext_ctrl) (struct file *file, void *fh, + struct v4l2_query_ext_ctrl *a); int (*vidioc_g_ctrl) (struct file *file, void *fh, struct v4l2_control *a); int (*vidioc_s_ctrl) (struct file *file, void *fh, -- cgit v1.2.3 From 1b2f121c1418249e56048d816754b479b3cb6fb3 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Wed, 25 Jun 2014 10:39:46 -0400 Subject: ftrace-graph: Remove dependency of ftrace_stop() from ftrace_graph_stop() ftrace_stop() is going away as it disables parts of function tracing that affects users that should not be affected. But ftrace_graph_stop() is built on ftrace_stop(). Here's another example of killing all of function tracing because something went wrong with function graph tracing. Instead of disabling all users of function tracing on function graph error, disable only function graph tracing. A new function is created called ftrace_graph_is_dead(). This is called in strategic paths to prevent function graph from doing more harm and allowing at least a warning to be printed before the system crashes. NOTE: ftrace_stop() is still used until all the archs are converted over to use ftrace_graph_is_dead(). After that, ftrace_stop() will be removed. Reviewed-by: Masami Hiramatsu Cc: Frederic Weisbecker Signed-off-by: Steven Rostedt --- include/linux/ftrace.h | 1 + kernel/trace/ftrace.c | 5 ----- kernel/trace/trace_functions_graph.c | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 4807a39e7ae1..18fb2c4a3f7f 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -760,6 +760,7 @@ extern char __irqentry_text_end[]; extern int register_ftrace_graph(trace_func_graph_ret_t retfunc, trace_func_graph_ent_t entryfunc); +extern bool ftrace_graph_is_dead(void); extern void ftrace_graph_stop(void); /* The current handlers in use */ diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 1776153ea6e0..8063280fd53d 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -5473,9 +5473,4 @@ void ftrace_graph_exit_task(struct task_struct *t) kfree(ret_stack); } - -void ftrace_graph_stop(void) -{ - ftrace_stop(); -} #endif diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c index 4de3e57f723c..3604690be70b 100644 --- a/kernel/trace/trace_functions_graph.c +++ b/kernel/trace/trace_functions_graph.c @@ -15,6 +15,38 @@ #include "trace.h" #include "trace_output.h" +static bool kill_ftrace_graph; + +/** + * ftrace_graph_is_dead - returns true if ftrace_graph_stop() was called + * + * ftrace_graph_stop() is called when a severe error is detected in + * the function graph tracing. This function is called by the critical + * paths of function graph to keep those paths from doing any more harm. + */ +bool ftrace_graph_is_dead(void) +{ + return kill_ftrace_graph; +} + +/** + * ftrace_graph_stop - set to permanently disable function graph tracincg + * + * In case of an error int function graph tracing, this is called + * to try to keep function graph tracing from causing any more harm. + * Usually this is pretty severe and this is called to try to at least + * get a warning out to the user. + */ +void ftrace_graph_stop(void) +{ + kill_ftrace_graph = true; + /* + * ftrace_stop() will be removed when all archs are updated to + * use ftrace_graph_is_dead() + */ + ftrace_stop(); +} + /* When set, irq functions will be ignored */ static int ftrace_graph_skip_irqs; @@ -92,6 +124,9 @@ ftrace_push_return_trace(unsigned long ret, unsigned long func, int *depth, unsigned long long calltime; int index; + if (unlikely(ftrace_graph_is_dead())) + return -EBUSY; + if (!current->ret_stack) return -EBUSY; -- cgit v1.2.3 From 0176077a813933a547b7a913377a87d615b7c108 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 27 Apr 2014 03:22:17 -0300 Subject: [media] v4l2-ctrls: create type_ops Since compound controls can have non-standard types we need to be able to do type-specific checks etc. In order to make that easy type operations are added. There are four operations: - equal: check if two values are equal - init: initialize a value - log: log the value - validate: validate a new value The v4l2_ctrl struct adds p_new and p_cur unions at the end of the struct. This union provides a standard way of accessing control types through a pointer, which greatly simplifies internal control processing. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 273 ++++++++++++++++++++++------------- drivers/media/v4l2-core/v4l2-ioctl.c | 5 +- include/media/v4l2-ctrls.h | 37 +++++ 3 files changed, 212 insertions(+), 103 deletions(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index d5cd1aab970b..09e2c3a2c5c2 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1161,6 +1161,149 @@ static void send_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 changes) v4l2_event_queue_fh(sev->fh, &ev); } +static bool std_equal(const struct v4l2_ctrl *ctrl, + union v4l2_ctrl_ptr ptr1, + union v4l2_ctrl_ptr ptr2) +{ + switch (ctrl->type) { + case V4L2_CTRL_TYPE_BUTTON: + return false; + case V4L2_CTRL_TYPE_STRING: + /* strings are always 0-terminated */ + return !strcmp(ptr1.p_char, ptr2.p_char); + case V4L2_CTRL_TYPE_INTEGER64: + return *ptr1.p_s64 == *ptr2.p_s64; + default: + if (ctrl->is_ptr) + return !memcmp(ptr1.p, ptr2.p, ctrl->elem_size); + return *ptr1.p_s32 == *ptr2.p_s32; + } +} + +static void std_init(const struct v4l2_ctrl *ctrl, + union v4l2_ctrl_ptr ptr) +{ + switch (ctrl->type) { + case V4L2_CTRL_TYPE_STRING: + memset(ptr.p_char, ' ', ctrl->minimum); + ptr.p_char[ctrl->minimum] = '\0'; + break; + case V4L2_CTRL_TYPE_INTEGER64: + *ptr.p_s64 = ctrl->default_value; + break; + case V4L2_CTRL_TYPE_INTEGER: + case V4L2_CTRL_TYPE_INTEGER_MENU: + case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_BITMASK: + case V4L2_CTRL_TYPE_BOOLEAN: + *ptr.p_s32 = ctrl->default_value; + break; + default: + break; + } +} + +static void std_log(const struct v4l2_ctrl *ctrl) +{ + union v4l2_ctrl_ptr ptr = ctrl->p_cur; + + switch (ctrl->type) { + case V4L2_CTRL_TYPE_INTEGER: + pr_cont("%d", *ptr.p_s32); + break; + case V4L2_CTRL_TYPE_BOOLEAN: + pr_cont("%s", *ptr.p_s32 ? "true" : "false"); + break; + case V4L2_CTRL_TYPE_MENU: + pr_cont("%s", ctrl->qmenu[*ptr.p_s32]); + break; + case V4L2_CTRL_TYPE_INTEGER_MENU: + pr_cont("%lld", ctrl->qmenu_int[*ptr.p_s32]); + break; + case V4L2_CTRL_TYPE_BITMASK: + pr_cont("0x%08x", *ptr.p_s32); + break; + case V4L2_CTRL_TYPE_INTEGER64: + pr_cont("%lld", *ptr.p_s64); + break; + case V4L2_CTRL_TYPE_STRING: + pr_cont("%s", ptr.p_char); + break; + default: + pr_cont("unknown type %d", ctrl->type); + break; + } +} + +/* Round towards the closest legal value */ +#define ROUND_TO_RANGE(val, offset_type, ctrl) \ +({ \ + offset_type offset; \ + val += (ctrl)->step / 2; \ + val = clamp_t(typeof(val), val, \ + (ctrl)->minimum, (ctrl)->maximum); \ + offset = (val) - (ctrl)->minimum; \ + offset = (ctrl)->step * (offset / (ctrl)->step); \ + val = (ctrl)->minimum + offset; \ + 0; \ +}) + +/* Validate a new control */ +static int std_validate(const struct v4l2_ctrl *ctrl, + union v4l2_ctrl_ptr ptr) +{ + size_t len; + + switch (ctrl->type) { + case V4L2_CTRL_TYPE_INTEGER: + return ROUND_TO_RANGE(*ptr.p_s32, u32, ctrl); + case V4L2_CTRL_TYPE_INTEGER64: + return ROUND_TO_RANGE(*ptr.p_s64, u64, ctrl); + + case V4L2_CTRL_TYPE_BOOLEAN: + *ptr.p_s32 = !!*ptr.p_s32; + return 0; + + case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_INTEGER_MENU: + if (*ptr.p_s32 < ctrl->minimum || *ptr.p_s32 > ctrl->maximum) + return -ERANGE; + if (ctrl->menu_skip_mask & (1 << *ptr.p_s32)) + return -EINVAL; + if (ctrl->type == V4L2_CTRL_TYPE_MENU && + ctrl->qmenu[*ptr.p_s32][0] == '\0') + return -EINVAL; + return 0; + + case V4L2_CTRL_TYPE_BITMASK: + *ptr.p_s32 &= ctrl->maximum; + return 0; + + case V4L2_CTRL_TYPE_BUTTON: + case V4L2_CTRL_TYPE_CTRL_CLASS: + *ptr.p_s32 = 0; + return 0; + + case V4L2_CTRL_TYPE_STRING: + len = strlen(ptr.p_char); + if (len < ctrl->minimum) + return -ERANGE; + if ((len - ctrl->minimum) % ctrl->step) + return -ERANGE; + return 0; + + default: + return -EINVAL; + } +} + +static const struct v4l2_ctrl_type_ops std_type_ops = { + .equal = std_equal, + .init = std_init, + .log = std_log, + .validate = std_validate, +}; + /* Helper function: copy the current control value back to the caller */ static int cur_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl) @@ -1344,21 +1487,7 @@ static int cluster_changed(struct v4l2_ctrl *master) if (ctrl == NULL) continue; - switch (ctrl->type) { - case V4L2_CTRL_TYPE_BUTTON: - /* Button controls are always 'different' */ - return 1; - case V4L2_CTRL_TYPE_STRING: - /* strings are always 0-terminated */ - diff = strcmp(ctrl->string, ctrl->cur.string); - break; - case V4L2_CTRL_TYPE_INTEGER64: - diff = ctrl->val64 != ctrl->cur.val64; - break; - default: - diff = ctrl->val != ctrl->cur.val; - break; - } + diff = !ctrl->type_ops->equal(ctrl, ctrl->p_cur, ctrl->p_new); } return diff; } @@ -1399,65 +1528,30 @@ static int check_range(enum v4l2_ctrl_type type, } } -/* Round towards the closest legal value */ -#define ROUND_TO_RANGE(val, offset_type, ctrl) \ -({ \ - offset_type offset; \ - val += (ctrl)->step / 2; \ - val = clamp_t(typeof(val), val, \ - (ctrl)->minimum, (ctrl)->maximum); \ - offset = (val) - (ctrl)->minimum; \ - offset = (ctrl)->step * (offset / (ctrl)->step); \ - val = (ctrl)->minimum + offset; \ - 0; \ -}) - /* Validate a new control */ static int validate_new(const struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c) { - size_t len; + union v4l2_ctrl_ptr ptr; switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER: - return ROUND_TO_RANGE(*(s32 *)&c->value, u32, ctrl); - case V4L2_CTRL_TYPE_INTEGER64: - return ROUND_TO_RANGE(*(s64 *)&c->value64, u64, ctrl); - - case V4L2_CTRL_TYPE_BOOLEAN: - c->value = !!c->value; - return 0; - - case V4L2_CTRL_TYPE_MENU: case V4L2_CTRL_TYPE_INTEGER_MENU: - if (c->value < ctrl->minimum || c->value > ctrl->maximum) - return -ERANGE; - if (ctrl->menu_skip_mask & (1 << c->value)) - return -EINVAL; - if (ctrl->type == V4L2_CTRL_TYPE_MENU && - ctrl->qmenu[c->value][0] == '\0') - return -EINVAL; - return 0; - + case V4L2_CTRL_TYPE_MENU: case V4L2_CTRL_TYPE_BITMASK: - c->value &= ctrl->maximum; - return 0; - + case V4L2_CTRL_TYPE_BOOLEAN: case V4L2_CTRL_TYPE_BUTTON: case V4L2_CTRL_TYPE_CTRL_CLASS: - c->value = 0; - return 0; + ptr.p_s32 = &c->value; + return ctrl->type_ops->validate(ctrl, ptr); - case V4L2_CTRL_TYPE_STRING: - len = strlen(c->string); - if (len < ctrl->minimum) - return -ERANGE; - if ((len - ctrl->minimum) % ctrl->step) - return -ERANGE; - return 0; + case V4L2_CTRL_TYPE_INTEGER64: + ptr.p_s64 = &c->value64; + return ctrl->type_ops->validate(ctrl, ptr); default: - return -EINVAL; + ptr.p = c->ptr; + return ctrl->type_ops->validate(ctrl, ptr); } } @@ -1674,6 +1768,7 @@ unlock: /* Add a new control */ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, + const struct v4l2_ctrl_type_ops *type_ops, u32 id, const char *name, enum v4l2_ctrl_type type, s64 min, s64 max, u64 step, s64 def, u32 elem_size, @@ -1731,6 +1826,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, INIT_LIST_HEAD(&ctrl->ev_subs); ctrl->handler = hdl; ctrl->ops = ops; + ctrl->type_ops = type_ops ? type_ops : &std_type_ops; ctrl->id = id; ctrl->name = name; ctrl->type = type; @@ -1751,16 +1847,16 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->cur.val = ctrl->val = def; data = &ctrl->cur + 1; - if (ctrl->is_string) { - ctrl->string = data; - ctrl->cur.string = data + elem_size; - - if (ctrl->minimum) - memset(ctrl->cur.string, ' ', ctrl->minimum); - } else if (ctrl->is_ptr) { - ctrl->p = data; - ctrl->cur.p = data + elem_size; + if (ctrl->is_ptr) { + ctrl->p = ctrl->p_new.p = data; + ctrl->p_cur.p = data + elem_size; + } else { + ctrl->p_new.p = &ctrl->val; + ctrl->p_cur.p = &ctrl->cur.val; } + ctrl->type_ops->init(ctrl, ctrl->p_cur); + ctrl->type_ops->init(ctrl, ctrl->p_new); + if (handler_new_ref(hdl, ctrl)) { kfree(ctrl); return NULL; @@ -1804,7 +1900,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, return NULL; } - ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->id, name, + ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->type_ops, cfg->id, name, type, min, max, is_menu ? cfg->menu_skip_mask : step, def, cfg->elem_size, @@ -1831,7 +1927,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, handler_set_err(hdl, -EINVAL); return NULL; } - return v4l2_ctrl_new(hdl, ops, id, name, type, + return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, min, max, step, def, 0, flags, NULL, NULL, NULL); } @@ -1864,7 +1960,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, handler_set_err(hdl, -EINVAL); return NULL; } - return v4l2_ctrl_new(hdl, ops, id, name, type, + return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, 0, max, mask, def, 0, flags, qmenu, qmenu_int, NULL); } @@ -1896,7 +1992,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl, handler_set_err(hdl, -EINVAL); return NULL; } - return v4l2_ctrl_new(hdl, ops, id, name, type, 0, max, mask, def, + return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, + 0, max, mask, def, 0, flags, qmenu, NULL, NULL); } @@ -1920,7 +2017,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl, handler_set_err(hdl, -EINVAL); return NULL; } - return v4l2_ctrl_new(hdl, ops, id, name, type, + return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, 0, max, 0, def, 0, flags, NULL, qmenu_int, NULL); } @@ -2104,32 +2201,8 @@ static void log_ctrl(const struct v4l2_ctrl *ctrl, pr_info("%s%s%s: ", prefix, colon, ctrl->name); - switch (ctrl->type) { - case V4L2_CTRL_TYPE_INTEGER: - pr_cont("%d", ctrl->cur.val); - break; - case V4L2_CTRL_TYPE_BOOLEAN: - pr_cont("%s", ctrl->cur.val ? "true" : "false"); - break; - case V4L2_CTRL_TYPE_MENU: - pr_cont("%s", ctrl->qmenu[ctrl->cur.val]); - break; - case V4L2_CTRL_TYPE_INTEGER_MENU: - pr_cont("%lld", ctrl->qmenu_int[ctrl->cur.val]); - break; - case V4L2_CTRL_TYPE_BITMASK: - pr_cont("0x%08x", ctrl->cur.val); - break; - case V4L2_CTRL_TYPE_INTEGER64: - pr_cont("%lld", ctrl->cur.val64); - break; - case V4L2_CTRL_TYPE_STRING: - pr_cont("%s", ctrl->cur.string); - break; - default: - pr_cont("unknown type %d", ctrl->type); - break; - } + ctrl->type_ops->log(ctrl); + if (ctrl->flags & (V4L2_CTRL_FLAG_INACTIVE | V4L2_CTRL_FLAG_GRABBED | V4L2_CTRL_FLAG_VOLATILE)) { diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index c38a6208d2cd..96bc117f66b2 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -531,13 +531,12 @@ static void v4l_print_query_ext_ctrl(const void *arg, bool write_only) pr_cont("id=0x%x, type=%d, name=%.*s, min/max=%lld/%lld, " "step=%lld, default=%lld, flags=0x%08x, elem_size=%u, elems=%u, " - "nr_of_dims=%u, dims=%u,%u,%u,%u,%u,%u,%u,%u\n", + "nr_of_dims=%u, dims=%u,%u,%u,%u\n", p->id, p->type, (int)sizeof(p->name), p->name, p->minimum, p->maximum, p->step, p->default_value, p->flags, p->elem_size, p->elems, p->nr_of_dims, - p->dims[0], p->dims[1], p->dims[2], p->dims[3], - p->dims[4], p->dims[5], p->dims[6], p->dims[7]); + p->dims[0], p->dims[1], p->dims[2], p->dims[3]); } static void v4l_print_querymenu(const void *arg, bool write_only) diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 9024daebdf3b..ddd9fdf1ac1a 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -36,6 +36,19 @@ struct v4l2_subscribed_event; struct v4l2_fh; struct poll_table_struct; +/** union v4l2_ctrl_ptr - A pointer to a control value. + * @p_s32: Pointer to a 32-bit signed value. + * @p_s64: Pointer to a 64-bit signed value. + * @p_char: Pointer to a string. + * @p: Pointer to a compound value. + */ +union v4l2_ctrl_ptr { + s32 *p_s32; + s64 *p_s64; + char *p_char; + void *p; +}; + /** struct v4l2_ctrl_ops - The control operations that the driver has to provide. * @g_volatile_ctrl: Get a new value for this control. Generally only relevant * for volatile (and usually read-only) controls such as a control @@ -54,6 +67,23 @@ struct v4l2_ctrl_ops { int (*s_ctrl)(struct v4l2_ctrl *ctrl); }; +/** struct v4l2_ctrl_type_ops - The control type operations that the driver has to provide. + * @equal: return true if both values are equal. + * @init: initialize the value. + * @log: log the value. + * @validate: validate the value. Return 0 on success and a negative value otherwise. + */ +struct v4l2_ctrl_type_ops { + bool (*equal)(const struct v4l2_ctrl *ctrl, + union v4l2_ctrl_ptr ptr1, + union v4l2_ctrl_ptr ptr2); + void (*init)(const struct v4l2_ctrl *ctrl, + union v4l2_ctrl_ptr ptr); + void (*log)(const struct v4l2_ctrl *ctrl); + int (*validate)(const struct v4l2_ctrl *ctrl, + union v4l2_ctrl_ptr ptr); +}; + typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv); /** struct v4l2_ctrl - The control structure. @@ -89,6 +119,7 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv); * value, then the whole cluster is in manual mode. Drivers should * never set this flag directly. * @ops: The control ops. + * @type_ops: The control type ops. * @id: The control ID. * @name: The control name. * @type: The control type. @@ -137,6 +168,7 @@ struct v4l2_ctrl { unsigned int manual_mode_value:8; const struct v4l2_ctrl_ops *ops; + const struct v4l2_ctrl_type_ops *type_ops; u32 id; const char *name; enum v4l2_ctrl_type type; @@ -164,6 +196,9 @@ struct v4l2_ctrl { char *string; void *p; } cur; + + union v4l2_ctrl_ptr p_new; + union v4l2_ctrl_ptr p_cur; }; /** struct v4l2_ctrl_ref - The control reference. @@ -217,6 +252,7 @@ struct v4l2_ctrl_handler { /** struct v4l2_ctrl_config - Control configuration structure. * @ops: The control ops. + * @type_ops: The control type ops. Only needed for compound controls. * @id: The control ID. * @name: The control name. * @type: The control type. @@ -241,6 +277,7 @@ struct v4l2_ctrl_handler { */ struct v4l2_ctrl_config { const struct v4l2_ctrl_ops *ops; + const struct v4l2_ctrl_type_ops *type_ops; u32 id; const char *name; enum v4l2_ctrl_type type; -- cgit v1.2.3 From 9ea1b7a4b66fddfab9e65e243b72d18371f8d9a5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 17 Jan 2014 08:25:26 -0300 Subject: [media] v4l2-ctrls: compare values only once When setting a control the control's new value is compared to the current value twice: once by new_to_cur(), once by cluster_changed(). Not a big deal when dealing with simple values, but it can be a problem when dealing with compound types or arrays. So fix this: cluster_changed() sets the has_changed flag, which is used by new_to_cur() instead of having to do another compare. Signed-off-by: Hans Verkuil Reviewed-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 17 +++++++++++------ include/media/v4l2-ctrls.h | 3 +++ 2 files changed, 14 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index e7e0beab7048..1b4d37ce4afe 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1424,8 +1424,11 @@ static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags) if (ctrl == NULL) return; - changed = !ctrl->type_ops->equal(ctrl, ctrl->p_cur, ctrl->p_new); - ptr_to_ptr(ctrl, ctrl->p_new, ctrl->p_cur); + + /* has_changed is set by cluster_changed */ + changed = ctrl->has_changed; + if (changed) + ptr_to_ptr(ctrl, ctrl->p_new, ctrl->p_cur); if (ch_flags & V4L2_EVENT_CTRL_CH_FLAGS) { /* Note: CH_FLAGS is only set for auto clusters. */ @@ -1462,17 +1465,19 @@ static void cur_to_new(struct v4l2_ctrl *ctrl) value that differs from the current value. */ static int cluster_changed(struct v4l2_ctrl *master) { - int diff = 0; + bool changed = false; int i; - for (i = 0; !diff && i < master->ncontrols; i++) { + for (i = 0; i < master->ncontrols; i++) { struct v4l2_ctrl *ctrl = master->cluster[i]; if (ctrl == NULL) continue; - diff = !ctrl->type_ops->equal(ctrl, ctrl->p_cur, ctrl->p_new); + ctrl->has_changed = !ctrl->type_ops->equal(ctrl, + ctrl->p_cur, ctrl->p_new); + changed |= ctrl->has_changed; } - return diff; + return changed; } /* Control range checking */ diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index ddd9fdf1ac1a..a38bd55909b2 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -96,6 +96,8 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv); * @is_new: Set when the user specified a new value for this control. It * is also set when called from v4l2_ctrl_handler_setup. Drivers * should never set this flag. + * @has_changed: Set when the current value differs from the new value. Drivers + * should never use this flag. * @is_private: If set, then this control is private to its handler and it * will not be added to any other handlers. Drivers can set * this flag. @@ -158,6 +160,7 @@ struct v4l2_ctrl { unsigned int done:1; unsigned int is_new:1; + unsigned int has_changed:1; unsigned int is_private:1; unsigned int is_auto:1; unsigned int is_int:1; -- cgit v1.2.3 From 2a9ec3731137f973c6289698de6566a25418b96f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 27 Apr 2014 03:38:13 -0300 Subject: [media] v4l2-ctrls: use ptrs for all but the s32 type Rather than having two unions for all types just keep 'val' and 'cur.val' and use the p_cur and p_new unions to access all others. The only reason for keeping 'val' and 'cur.val' is that it is used all over, so converting this as well would be a huge job. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-controls.txt | 2 +- drivers/media/i2c/mt9v032.c | 2 +- drivers/media/i2c/smiapp/smiapp-core.c | 4 ++-- drivers/media/pci/ivtv/ivtv-controls.c | 4 ++-- drivers/media/platform/vivi.c | 4 ++-- drivers/media/radio/si4713/si4713.c | 4 ++-- drivers/media/v4l2-core/v4l2-ctrls.c | 16 +++++++++------- drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c | 2 +- include/media/v4l2-ctrls.h | 12 ++---------- 9 files changed, 22 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/Documentation/video4linux/v4l2-controls.txt b/Documentation/video4linux/v4l2-controls.txt index 06cf3ac83631..c9ee9a7e403d 100644 --- a/Documentation/video4linux/v4l2-controls.txt +++ b/Documentation/video4linux/v4l2-controls.txt @@ -362,7 +362,7 @@ will result in a deadlock since these helpers lock the handler as well. You can also take the handler lock yourself: mutex_lock(&state->ctrl_handler.lock); - printk(KERN_INFO "String value is '%s'\n", ctrl1->cur.string); + pr_info("String value is '%s'\n", ctrl1->p_cur.p_char); printk(KERN_INFO "Integer value is '%s'\n", ctrl2->cur.val); mutex_unlock(&state->ctrl_handler.lock); diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 65e6e1a9d104..435e9e661bbb 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -667,7 +667,7 @@ static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl) break; freq = mt9v032->pdata->link_freqs[mt9v032->link_freq->val]; - mt9v032->pixel_rate->val64 = freq; + *mt9v032->pixel_rate->p_new.p_s64 = freq; mt9v032->sysclk = freq; break; diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 72350f0befc5..13411e9a0e25 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -297,8 +297,8 @@ static int smiapp_pll_update(struct smiapp_sensor *sensor) if (rval < 0) return rval; - sensor->pixel_rate_parray->cur.val64 = pll->vt_pix_clk_freq_hz; - sensor->pixel_rate_csi->cur.val64 = pll->pixel_rate_csi; + *sensor->pixel_rate_parray->p_cur.p_s64 = pll->vt_pix_clk_freq_hz; + *sensor->pixel_rate_csi->p_cur.p_s64 = pll->pixel_rate_csi; return 0; } diff --git a/drivers/media/pci/ivtv/ivtv-controls.c b/drivers/media/pci/ivtv/ivtv-controls.c index c60424601cb9..2b0ab26e11e8 100644 --- a/drivers/media/pci/ivtv/ivtv-controls.c +++ b/drivers/media/pci/ivtv/ivtv-controls.c @@ -135,8 +135,8 @@ static int ivtv_g_volatile_ctrl(struct v4l2_ctrl *ctrl) /* V4L2_CID_MPEG_VIDEO_DEC_PTS and V4L2_CID_MPEG_VIDEO_DEC_FRAME control cluster */ case V4L2_CID_MPEG_VIDEO_DEC_PTS: - return ivtv_g_pts_frame(itv, &itv->ctrl_pts->val64, - &itv->ctrl_frame->val64); + return ivtv_g_pts_frame(itv, itv->ctrl_pts->p_new.p_s64, + itv->ctrl_frame->p_new.p_s64); } return 0; } diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index cd535b38469d..7542b5dd9910 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -648,13 +648,13 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) gen_text(dev, vbuf, line++ * 16, 16, str); snprintf(str, sizeof(str), " int32 %d, int64 %lld, bitmask %08x ", dev->int32->cur.val, - dev->int64->cur.val64, + *dev->int64->p_cur.p_s64, dev->bitmask->cur.val); gen_text(dev, vbuf, line++ * 16, 16, str); snprintf(str, sizeof(str), " boolean %d, menu %s, string \"%s\" ", dev->boolean->cur.val, dev->menu->qmenu[dev->menu->cur.val], - dev->string->cur.string); + dev->string->p_cur.p_char); gen_text(dev, vbuf, line++ * 16, 16, str); snprintf(str, sizeof(str), " integer_menu %lld, value %d ", dev->int_menu->qmenu_int[dev->int_menu->cur.val], diff --git a/drivers/media/radio/si4713/si4713.c b/drivers/media/radio/si4713/si4713.c index 07d5153811e8..dbe47261b513 100644 --- a/drivers/media/radio/si4713/si4713.c +++ b/drivers/media/radio/si4713/si4713.c @@ -1098,11 +1098,11 @@ static int si4713_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_RDS_TX_PS_NAME: - ret = si4713_set_rds_ps_name(sdev, ctrl->string); + ret = si4713_set_rds_ps_name(sdev, ctrl->p_new.p_char); break; case V4L2_CID_RDS_TX_RADIO_TEXT: - ret = si4713_set_rds_radio_text(sdev, ctrl->string); + ret = si4713_set_rds_radio_text(sdev, ctrl->p_new.p_char); break; case V4L2_CID_TUNE_ANTENNA_CAPACITOR: diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 1b4d37ce4afe..4b105713e549 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1135,7 +1135,7 @@ static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 change if (ctrl->is_ptr) ev->u.ctrl.value64 = 0; else - ev->u.ctrl.value64 = ctrl->cur.val64; + ev->u.ctrl.value64 = *ctrl->p_cur.p_s64; ev->u.ctrl.minimum = ctrl->minimum; ev->u.ctrl.maximum = ctrl->maximum; if (ctrl->type == V4L2_CTRL_TYPE_MENU @@ -1801,7 +1801,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, flags |= V4L2_CTRL_FLAG_WRITE_ONLY; else if (type == V4L2_CTRL_TYPE_CTRL_CLASS) flags |= V4L2_CTRL_FLAG_READ_ONLY; - else if (type == V4L2_CTRL_TYPE_STRING || type >= V4L2_CTRL_COMPOUND_TYPES) + else if (type == V4L2_CTRL_TYPE_INTEGER64 || + type == V4L2_CTRL_TYPE_STRING || + type >= V4L2_CTRL_COMPOUND_TYPES) sz_extra += 2 * elem_size; ctrl = kzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL); @@ -1833,10 +1835,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->qmenu_int = qmenu_int; ctrl->priv = priv; ctrl->cur.val = ctrl->val = def; - data = &ctrl->cur + 1; + data = &ctrl[1]; - if (ctrl->is_ptr) { - ctrl->p = ctrl->p_new.p = data; + if (!ctrl->is_int) { + ctrl->p_new.p = data; ctrl->p_cur.p = data + elem_size; } else { ctrl->p_new.p = &ctrl->val; @@ -3103,10 +3105,10 @@ int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, ctrl->maximum = max; ctrl->step = step; ctrl->default_value = def; - c.value = ctrl->cur.val; + c.value = *ctrl->p_cur.p_s32; if (validate_new(ctrl, &c)) c.value = def; - if (c.value != ctrl->cur.val) + if (c.value != *ctrl->p_cur.p_s32) ret = set_ctrl(NULL, ctrl, &c, V4L2_EVENT_CTRL_CH_RANGE); else send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_RANGE); diff --git a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c index bb2604ead216..37f7fd090bc2 100644 --- a/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c +++ b/drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c @@ -1131,7 +1131,7 @@ static int solo_s_ctrl(struct v4l2_ctrl *ctrl) solo_motion_toggle(solo_enc, ctrl->val); return 0; case V4L2_CID_OSD_TEXT: - strcpy(solo_enc->osd_text, ctrl->string); + strcpy(solo_enc->osd_text, ctrl->p_new.p_char); err = solo_osd_print(solo_enc); return err; default: diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index a38bd55909b2..eb69c52e2f64 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -187,17 +187,9 @@ struct v4l2_ctrl { }; unsigned long flags; void *priv; - union { - s32 val; - s64 val64; - char *string; - void *p; - }; - union { + s32 val; + struct { s32 val; - s64 val64; - char *string; - void *p; } cur; union v4l2_ctrl_ptr p_new; -- cgit v1.2.3 From 20d88eef66a86989ea3cffe2a4e0d16cbf2d4563 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 12 Jun 2014 07:55:21 -0300 Subject: [media] v4l2-ctrls: prepare for array support Add dims, nr_of_dims and elems fields to the core control structures in preparation for N-dimensional array support. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 33 ++++++++++++++++++++++++--------- include/media/v4l2-ctrls.h | 8 ++++++++ 2 files changed, 32 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 4b105713e549..e6c98a705b19 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1759,18 +1759,27 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_type_ops *type_ops, u32 id, const char *name, enum v4l2_ctrl_type type, s64 min, s64 max, u64 step, s64 def, - u32 elem_size, + const u32 dims[V4L2_CTRL_MAX_DIMS], u32 elem_size, u32 flags, const char * const *qmenu, const s64 *qmenu_int, void *priv) { struct v4l2_ctrl *ctrl; unsigned sz_extra; + unsigned nr_of_dims = 0; + unsigned elems = 1; void *data; int err; if (hdl->error) return NULL; + while (dims && dims[nr_of_dims]) { + elems *= dims[nr_of_dims]; + nr_of_dims++; + if (nr_of_dims == V4L2_CTRL_MAX_DIMS) + break; + } + if (type == V4L2_CTRL_TYPE_INTEGER64) elem_size = sizeof(s64); else if (type == V4L2_CTRL_TYPE_STRING) @@ -1828,6 +1837,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->is_string = type == V4L2_CTRL_TYPE_STRING; ctrl->is_ptr = type >= V4L2_CTRL_COMPOUND_TYPES || ctrl->is_string; ctrl->is_int = !ctrl->is_ptr && type != V4L2_CTRL_TYPE_INTEGER64; + ctrl->elems = elems; + ctrl->nr_of_dims = nr_of_dims; + if (nr_of_dims) + memcpy(ctrl->dims, dims, nr_of_dims * sizeof(dims[0])); ctrl->elem_size = elem_size; if (type == V4L2_CTRL_TYPE_MENU) ctrl->qmenu = qmenu; @@ -1892,8 +1905,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->type_ops, cfg->id, name, type, min, max, - is_menu ? cfg->menu_skip_mask : step, - def, cfg->elem_size, + is_menu ? cfg->menu_skip_mask : step, def, + cfg->dims, cfg->elem_size, flags, qmenu, qmenu_int, priv); if (ctrl) ctrl->is_private = cfg->is_private; @@ -1918,7 +1931,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, - min, max, step, def, 0, + min, max, step, def, NULL, 0, flags, NULL, NULL, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std); @@ -1951,7 +1964,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, - 0, max, mask, def, 0, + 0, max, mask, def, NULL, 0, flags, qmenu, qmenu_int, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std_menu); @@ -1983,8 +1996,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, - 0, max, mask, def, - 0, flags, qmenu, NULL, NULL); + 0, max, mask, def, NULL, 0, + flags, qmenu, NULL, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items); @@ -2008,7 +2021,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, - 0, max, 0, def, 0, + 0, max, 0, def, NULL, 0, flags, NULL, qmenu_int, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_int_menu); @@ -2354,7 +2367,9 @@ int v4l2_query_ext_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_query_ext_ctr if (ctrl->is_ptr) qc->flags |= V4L2_CTRL_FLAG_HAS_PAYLOAD; qc->elem_size = ctrl->elem_size; - qc->elems = 1; + qc->elems = ctrl->elems; + qc->nr_of_dims = ctrl->nr_of_dims; + memcpy(qc->dims, ctrl->dims, qc->nr_of_dims * sizeof(qc->dims[0])); qc->minimum = ctrl->minimum; qc->maximum = ctrl->maximum; qc->default_value = ctrl->default_value; diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index eb69c52e2f64..d30da09b7b69 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -129,7 +129,10 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv); * @maximum: The control's maximum value. * @default_value: The control's default value. * @step: The control's step value for non-menu controls. + * @elems: The number of elements in the N-dimensional array. * @elem_size: The size in bytes of the control. + * @dims: The size of each dimension. + * @nr_of_dims:The number of dimensions in @dims. * @menu_skip_mask: The control's skip mask for menu controls. This makes it * easy to skip menu items that are not valid. If bit X is set, * then menu item X is skipped. Of course, this only works for @@ -176,7 +179,10 @@ struct v4l2_ctrl { const char *name; enum v4l2_ctrl_type type; s64 minimum, maximum, default_value; + u32 elems; u32 elem_size; + u32 dims[V4L2_CTRL_MAX_DIMS]; + u32 nr_of_dims; union { u64 step; u64 menu_skip_mask; @@ -255,6 +261,7 @@ struct v4l2_ctrl_handler { * @max: The control's maximum value. * @step: The control's step value for non-menu controls. * @def: The control's default value. + * @dims: The size of each dimension. * @elem_size: The size in bytes of the control. * @flags: The control's flags. * @menu_skip_mask: The control's skip mask for menu controls. This makes it @@ -280,6 +287,7 @@ struct v4l2_ctrl_config { s64 max; u64 step; s64 def; + u32 dims[V4L2_CTRL_MAX_DIMS]; u32 elem_size; u32 flags; u64 menu_skip_mask; -- cgit v1.2.3 From 998e7659150760c0f4871ee20de2ef2276e3f80a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 10 Jun 2014 07:55:00 -0300 Subject: [media] v4l2-ctrls: prepare for array support Add core support for N-dimensional arrays. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 58 ++++++++++++++++++++++++------------ include/media/v4l2-ctrls.h | 8 +++-- 2 files changed, 44 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index e6c98a705b19..6ed2d5608611 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1161,7 +1161,7 @@ static void send_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 changes) v4l2_event_queue_fh(sev->fh, &ev); } -static bool std_equal(const struct v4l2_ctrl *ctrl, +static bool std_equal(const struct v4l2_ctrl *ctrl, u32 idx, union v4l2_ctrl_ptr ptr1, union v4l2_ctrl_ptr ptr2) { @@ -1180,7 +1180,7 @@ static bool std_equal(const struct v4l2_ctrl *ctrl, } } -static void std_init(const struct v4l2_ctrl *ctrl, +static void std_init(const struct v4l2_ctrl *ctrl, u32 idx, union v4l2_ctrl_ptr ptr) { switch (ctrl->type) { @@ -1207,6 +1207,14 @@ static void std_log(const struct v4l2_ctrl *ctrl) { union v4l2_ctrl_ptr ptr = ctrl->p_cur; + if (ctrl->is_array) { + unsigned i; + + for (i = 0; i < ctrl->nr_of_dims; i++) + pr_cont("[%u]", ctrl->dims[i]); + pr_cont(" "); + } + switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER: pr_cont("%d", *ptr.p_s32); @@ -1249,7 +1257,7 @@ static void std_log(const struct v4l2_ctrl *ctrl) }) /* Validate a new control */ -static int std_validate(const struct v4l2_ctrl *ctrl, +static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, union v4l2_ctrl_ptr ptr) { size_t len; @@ -1473,8 +1481,8 @@ static int cluster_changed(struct v4l2_ctrl *master) if (ctrl == NULL) continue; - ctrl->has_changed = !ctrl->type_ops->equal(ctrl, - ctrl->p_cur, ctrl->p_new); + ctrl->has_changed = !ctrl->type_ops->equal(ctrl, 0, + ctrl->p_cur, ctrl->p_new); changed |= ctrl->has_changed; } return changed; @@ -1531,15 +1539,15 @@ static int validate_new(const struct v4l2_ctrl *ctrl, case V4L2_CTRL_TYPE_BUTTON: case V4L2_CTRL_TYPE_CTRL_CLASS: ptr.p_s32 = &c->value; - return ctrl->type_ops->validate(ctrl, ptr); + return ctrl->type_ops->validate(ctrl, 0, ptr); case V4L2_CTRL_TYPE_INTEGER64: ptr.p_s64 = &c->value64; - return ctrl->type_ops->validate(ctrl, ptr); + return ctrl->type_ops->validate(ctrl, 0, ptr); default: ptr.p = c->ptr; - return ctrl->type_ops->validate(ctrl, ptr); + return ctrl->type_ops->validate(ctrl, 0, ptr); } } @@ -1767,6 +1775,8 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, unsigned sz_extra; unsigned nr_of_dims = 0; unsigned elems = 1; + bool is_array; + unsigned tot_ctrl_size; void *data; int err; @@ -1779,6 +1789,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, if (nr_of_dims == V4L2_CTRL_MAX_DIMS) break; } + is_array = nr_of_dims > 0; if (type == V4L2_CTRL_TYPE_INTEGER64) elem_size = sizeof(s64); @@ -1786,10 +1797,11 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, elem_size = max + 1; else if (type < V4L2_CTRL_COMPOUND_TYPES) elem_size = sizeof(s32); + tot_ctrl_size = elem_size * elems; /* Sanity checks */ - if (id == 0 || name == NULL || id >= V4L2_CID_PRIVATE_BASE || - elem_size == 0 || + if (id == 0 || name == NULL || !elem_size || + id >= V4L2_CID_PRIVATE_BASE || (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) || (type == V4L2_CTRL_TYPE_INTEGER_MENU && qmenu_int == NULL)) { handler_set_err(hdl, -ERANGE); @@ -1804,6 +1816,12 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, handler_set_err(hdl, -ERANGE); return NULL; } + if (is_array && + (type == V4L2_CTRL_TYPE_BUTTON || + type == V4L2_CTRL_TYPE_CTRL_CLASS)) { + handler_set_err(hdl, -EINVAL); + return NULL; + } sz_extra = 0; if (type == V4L2_CTRL_TYPE_BUTTON) @@ -1812,8 +1830,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, flags |= V4L2_CTRL_FLAG_READ_ONLY; else if (type == V4L2_CTRL_TYPE_INTEGER64 || type == V4L2_CTRL_TYPE_STRING || - type >= V4L2_CTRL_COMPOUND_TYPES) - sz_extra += 2 * elem_size; + type >= V4L2_CTRL_COMPOUND_TYPES || + is_array) + sz_extra += 2 * tot_ctrl_size; ctrl = kzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL); if (ctrl == NULL) { @@ -1834,9 +1853,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->maximum = max; ctrl->step = step; ctrl->default_value = def; - ctrl->is_string = type == V4L2_CTRL_TYPE_STRING; - ctrl->is_ptr = type >= V4L2_CTRL_COMPOUND_TYPES || ctrl->is_string; + ctrl->is_string = !is_array && type == V4L2_CTRL_TYPE_STRING; + ctrl->is_ptr = is_array || type >= V4L2_CTRL_COMPOUND_TYPES || ctrl->is_string; ctrl->is_int = !ctrl->is_ptr && type != V4L2_CTRL_TYPE_INTEGER64; + ctrl->is_array = is_array; ctrl->elems = elems; ctrl->nr_of_dims = nr_of_dims; if (nr_of_dims) @@ -1852,13 +1872,13 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, if (!ctrl->is_int) { ctrl->p_new.p = data; - ctrl->p_cur.p = data + elem_size; + ctrl->p_cur.p = data + tot_ctrl_size; } else { ctrl->p_new.p = &ctrl->val; ctrl->p_cur.p = &ctrl->cur.val; } - ctrl->type_ops->init(ctrl, ctrl->p_cur); - ctrl->type_ops->init(ctrl, ctrl->p_new); + ctrl->type_ops->init(ctrl, 0, ctrl->p_cur); + ctrl->type_ops->init(ctrl, 0, ctrl->p_new); if (handler_new_ref(hdl, ctrl)) { kfree(ctrl); @@ -2764,7 +2784,7 @@ s64 v4l2_ctrl_g_ctrl_int64(struct v4l2_ctrl *ctrl) struct v4l2_ext_control c; /* It's a driver bug if this happens. */ - WARN_ON(ctrl->type != V4L2_CTRL_TYPE_INTEGER64); + WARN_ON(ctrl->is_ptr || ctrl->type != V4L2_CTRL_TYPE_INTEGER64); c.value = 0; get_ctrl(ctrl, &c); return c.value; @@ -3074,7 +3094,7 @@ int v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val) struct v4l2_ext_control c; /* It's a driver bug if this happens. */ - WARN_ON(ctrl->type != V4L2_CTRL_TYPE_INTEGER64); + WARN_ON(ctrl->is_ptr || ctrl->type != V4L2_CTRL_TYPE_INTEGER64); c.value64 = val; return set_ctrl_lock(NULL, ctrl, &c); } diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index d30da09b7b69..7915b1125bb5 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -74,13 +74,13 @@ struct v4l2_ctrl_ops { * @validate: validate the value. Return 0 on success and a negative value otherwise. */ struct v4l2_ctrl_type_ops { - bool (*equal)(const struct v4l2_ctrl *ctrl, + bool (*equal)(const struct v4l2_ctrl *ctrl, u32 idx, union v4l2_ctrl_ptr ptr1, union v4l2_ctrl_ptr ptr2); - void (*init)(const struct v4l2_ctrl *ctrl, + void (*init)(const struct v4l2_ctrl *ctrl, u32 idx, union v4l2_ctrl_ptr ptr); void (*log)(const struct v4l2_ctrl *ctrl); - int (*validate)(const struct v4l2_ctrl *ctrl, + int (*validate)(const struct v4l2_ctrl *ctrl, u32 idx, union v4l2_ctrl_ptr ptr); }; @@ -111,6 +111,7 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv); * @is_ptr: If set, then this control is an array and/or has type >= V4L2_CTRL_COMPOUND_TYPES * and/or has type V4L2_CTRL_TYPE_STRING. In other words, struct * v4l2_ext_control uses field p to point to the data. + * @is_array: If set, then this control contains an N-dimensional array. * @has_volatiles: If set, then one or more members of the cluster are volatile. * Drivers should never touch this flag. * @call_notify: If set, then call the handler's notify function whenever the @@ -169,6 +170,7 @@ struct v4l2_ctrl { unsigned int is_int:1; unsigned int is_string:1; unsigned int is_ptr:1; + unsigned int is_array:1; unsigned int has_volatiles:1; unsigned int call_notify:1; unsigned int manual_mode_value:8; -- cgit v1.2.3 From dda4a4d5ea245591b788b70116fb52b0d145fb33 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 10 Jun 2014 07:30:04 -0300 Subject: [media] v4l2-ctrls/videodev2.h: add u8 and u16 types These are needed by the upcoming patches for the motion detection matrices. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 45 ++++++++++++++++++++++++++++++++---- include/media/v4l2-ctrls.h | 4 ++++ include/uapi/linux/videodev2.h | 4 ++++ 3 files changed, 49 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 1086ae3f9152..adf5485e56a8 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1174,6 +1174,10 @@ static bool std_equal(const struct v4l2_ctrl *ctrl, u32 idx, return !strcmp(ptr1.p_char + idx, ptr2.p_char + idx); case V4L2_CTRL_TYPE_INTEGER64: return ptr1.p_s64[idx] == ptr2.p_s64[idx]; + case V4L2_CTRL_TYPE_U8: + return ptr1.p_u8[idx] == ptr2.p_u8[idx]; + case V4L2_CTRL_TYPE_U16: + return ptr1.p_u16[idx] == ptr2.p_u16[idx]; default: if (ctrl->is_int) return ptr1.p_s32[idx] == ptr2.p_s32[idx]; @@ -1201,6 +1205,12 @@ static void std_init(const struct v4l2_ctrl *ctrl, u32 idx, case V4L2_CTRL_TYPE_BOOLEAN: ptr.p_s32[idx] = ctrl->default_value; break; + case V4L2_CTRL_TYPE_U8: + ptr.p_u8[idx] = ctrl->default_value; + break; + case V4L2_CTRL_TYPE_U16: + ptr.p_u16[idx] = ctrl->default_value; + break; default: idx *= ctrl->elem_size; memset(ptr.p + idx, 0, ctrl->elem_size); @@ -1242,6 +1252,12 @@ static void std_log(const struct v4l2_ctrl *ctrl) case V4L2_CTRL_TYPE_STRING: pr_cont("%s", ptr.p_char); break; + case V4L2_CTRL_TYPE_U8: + pr_cont("%u", (unsigned)*ptr.p_u8); + break; + case V4L2_CTRL_TYPE_U16: + pr_cont("%u", (unsigned)*ptr.p_u16); + break; default: pr_cont("unknown type %d", ctrl->type); break; @@ -1272,6 +1288,10 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, return ROUND_TO_RANGE(ptr.p_s32[idx], u32, ctrl); case V4L2_CTRL_TYPE_INTEGER64: return ROUND_TO_RANGE(ptr.p_s64[idx], u64, ctrl); + case V4L2_CTRL_TYPE_U8: + return ROUND_TO_RANGE(ptr.p_u8[idx], u8, ctrl); + case V4L2_CTRL_TYPE_U16: + return ROUND_TO_RANGE(ptr.p_u16[idx], u16, ctrl); case V4L2_CTRL_TYPE_BOOLEAN: ptr.p_s32[idx] = !!ptr.p_s32[idx]; @@ -1502,6 +1522,8 @@ static int check_range(enum v4l2_ctrl_type type, if (step != 1 || max > 1 || min < 0) return -ERANGE; /* fall through */ + case V4L2_CTRL_TYPE_U8: + case V4L2_CTRL_TYPE_U16: case V4L2_CTRL_TYPE_INTEGER: case V4L2_CTRL_TYPE_INTEGER64: if (step == 0 || min > max || def < min || def > max) @@ -1803,12 +1825,25 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, } is_array = nr_of_dims > 0; - if (type == V4L2_CTRL_TYPE_INTEGER64) + /* Prefill elem_size for all types handled by std_type_ops */ + switch (type) { + case V4L2_CTRL_TYPE_INTEGER64: elem_size = sizeof(s64); - else if (type == V4L2_CTRL_TYPE_STRING) + break; + case V4L2_CTRL_TYPE_STRING: elem_size = max + 1; - else if (type < V4L2_CTRL_COMPOUND_TYPES) - elem_size = sizeof(s32); + break; + case V4L2_CTRL_TYPE_U8: + elem_size = sizeof(u8); + break; + case V4L2_CTRL_TYPE_U16: + elem_size = sizeof(u16); + break; + default: + if (type < V4L2_CTRL_COMPOUND_TYPES) + elem_size = sizeof(s32); + break; + } tot_ctrl_size = elem_size * elems; /* Sanity checks */ @@ -3148,6 +3183,8 @@ int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, case V4L2_CTRL_TYPE_MENU: case V4L2_CTRL_TYPE_INTEGER_MENU: case V4L2_CTRL_TYPE_BITMASK: + case V4L2_CTRL_TYPE_U8: + case V4L2_CTRL_TYPE_U16: if (ctrl->is_array) return -EINVAL; ret = check_range(ctrl->type, min, max, step, def); diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 7915b1125bb5..c630345e9fd2 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -39,12 +39,16 @@ struct poll_table_struct; /** union v4l2_ctrl_ptr - A pointer to a control value. * @p_s32: Pointer to a 32-bit signed value. * @p_s64: Pointer to a 64-bit signed value. + * @p_u8: Pointer to a 8-bit unsigned value. + * @p_u16: Pointer to a 16-bit unsigned value. * @p_char: Pointer to a string. * @p: Pointer to a compound value. */ union v4l2_ctrl_ptr { s32 *p_s32; s64 *p_s64; + u8 *p_u8; + u16 *p_u16; char *p_char; void *p; }; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 0297980fe491..632de96b42d5 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1254,6 +1254,8 @@ struct v4l2_ext_control { __s32 value; __s64 value64; char *string; + __u8 *p_u8; + __u16 *p_u16; void *ptr; }; } __attribute__ ((packed)); @@ -1284,6 +1286,8 @@ enum v4l2_ctrl_type { /* Compound types are >= 0x0100 */ V4L2_CTRL_COMPOUND_TYPES = 0x0100, + V4L2_CTRL_TYPE_U8 = 0x0100, + V4L2_CTRL_TYPE_U16 = 0x0101, }; /* Used in the VIDIOC_QUERYCTRL ioctl for querying controls */ -- cgit v1.2.3 From a77b4fc0bc328b0989f83220aba1c268e087107f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 28 Mar 2014 13:00:08 -0300 Subject: [media] v4l2-ctrls/v4l2-controls.h: add MD controls Add the 'Detect' control class and the new motion detection controls. Those controls will be used by the solo6x10 and go7007 drivers. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 27 +++++++++++++++++++++++++++ include/uapi/linux/v4l2-controls.h | 17 +++++++++++++++++ 2 files changed, 44 insertions(+) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 5aaf15e047c8..5c3b8de82e35 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -462,6 +462,13 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "RGB full range (0-255)", NULL, }; + static const char * const detect_md_mode[] = { + "Disabled", + "Global", + "Threshold Grid", + "Region Grid", + NULL, + }; switch (id) { @@ -553,6 +560,8 @@ const char * const *v4l2_ctrl_get_menu(u32 id) case V4L2_CID_DV_TX_RGB_RANGE: case V4L2_CID_DV_RX_RGB_RANGE: return dv_rgb_range; + case V4L2_CID_DETECT_MD_MODE: + return detect_md_mode; default: return NULL; @@ -874,6 +883,15 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO: return "Bandwidth, Auto"; case V4L2_CID_RF_TUNER_BANDWIDTH: return "Bandwidth"; case V4L2_CID_RF_TUNER_PLL_LOCK: return "PLL Lock"; + + /* Detection controls */ + /* Keep the order of the 'case's the same as in v4l2-controls.h! */ + case V4L2_CID_DETECT_CLASS: return "Detection Controls"; + case V4L2_CID_DETECT_MD_MODE: return "Motion Detection Mode"; + case V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD: return "MD Global Threshold"; + case V4L2_CID_DETECT_MD_THRESHOLD_GRID: return "MD Threshold Grid"; + case V4L2_CID_DETECT_MD_REGION_GRID: return "MD Region Grid"; + default: return NULL; } @@ -992,6 +1010,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_TEST_PATTERN: case V4L2_CID_TUNE_DEEMPHASIS: case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL: + case V4L2_CID_DETECT_MD_MODE: *type = V4L2_CTRL_TYPE_MENU; break; case V4L2_CID_LINK_FREQ: @@ -1018,6 +1037,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_DV_CLASS: case V4L2_CID_FM_RX_CLASS: case V4L2_CID_RF_TUNER_CLASS: + case V4L2_CID_DETECT_CLASS: *type = V4L2_CTRL_TYPE_CTRL_CLASS; /* You can neither read not write these */ *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY; @@ -1063,6 +1083,12 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, *type = V4L2_CTRL_TYPE_INTEGER64; *flags |= V4L2_CTRL_FLAG_READ_ONLY; break; + case V4L2_CID_DETECT_MD_REGION_GRID: + *type = V4L2_CTRL_TYPE_U8; + break; + case V4L2_CID_DETECT_MD_THRESHOLD_GRID: + *type = V4L2_CTRL_TYPE_U16; + break; default: *type = V4L2_CTRL_TYPE_INTEGER; break; @@ -1103,6 +1129,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_RF_TUNER_MIXER_GAIN: case V4L2_CID_RF_TUNER_IF_GAIN: case V4L2_CID_RF_TUNER_BANDWIDTH: + case V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD: *flags |= V4L2_CTRL_FLAG_SLIDER; break; case V4L2_CID_PAN_RELATIVE: diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 2ac5597f3ee1..db526d1c8309 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -61,6 +61,7 @@ #define V4L2_CTRL_CLASS_DV 0x00a00000 /* Digital Video controls */ #define V4L2_CTRL_CLASS_FM_RX 0x00a10000 /* FM Receiver controls */ #define V4L2_CTRL_CLASS_RF_TUNER 0x00a20000 /* RF tuner controls */ +#define V4L2_CTRL_CLASS_DETECT 0x00a30000 /* Detection controls */ /* User-class control IDs */ @@ -914,4 +915,20 @@ enum v4l2_deemphasis { #define V4L2_CID_RF_TUNER_IF_GAIN (V4L2_CID_RF_TUNER_CLASS_BASE + 62) #define V4L2_CID_RF_TUNER_PLL_LOCK (V4L2_CID_RF_TUNER_CLASS_BASE + 91) + +/* Detection-class control IDs defined by V4L2 */ +#define V4L2_CID_DETECT_CLASS_BASE (V4L2_CTRL_CLASS_DETECT | 0x900) +#define V4L2_CID_DETECT_CLASS (V4L2_CTRL_CLASS_DETECT | 1) + +#define V4L2_CID_DETECT_MD_MODE (V4L2_CID_DETECT_CLASS_BASE + 1) +enum v4l2_detect_md_mode { + V4L2_DETECT_MD_MODE_DISABLED = 0, + V4L2_DETECT_MD_MODE_GLOBAL = 1, + V4L2_DETECT_MD_MODE_THRESHOLD_GRID = 2, + V4L2_DETECT_MD_MODE_REGION_GRID = 3, +}; +#define V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD (V4L2_CID_DETECT_CLASS_BASE + 2) +#define V4L2_CID_DETECT_MD_THRESHOLD_GRID (V4L2_CID_DETECT_CLASS_BASE + 3) +#define V4L2_CID_DETECT_MD_REGION_GRID (V4L2_CID_DETECT_CLASS_BASE + 4) + #endif -- cgit v1.2.3 From 78ea611385f08da6e52ff2ea15f0a26e89acb3ab Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 27 Jan 2014 09:26:23 -0300 Subject: [media] v4l2: add a motion detection event Add a new MOTION_DET event to signal when motion is detected. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/videodev2.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 632de96b42d5..1477abebd35b 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1799,6 +1799,7 @@ struct v4l2_streamparm { #define V4L2_EVENT_CTRL 3 #define V4L2_EVENT_FRAME_SYNC 4 #define V4L2_EVENT_SOURCE_CHANGE 5 +#define V4L2_EVENT_MOTION_DET 6 #define V4L2_EVENT_PRIVATE_START 0x08000000 /* Payload for V4L2_EVENT_VSYNC */ @@ -1836,6 +1837,21 @@ struct v4l2_event_src_change { __u32 changes; }; +#define V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ (1 << 0) + +/** + * struct v4l2_event_motion_det - motion detection event + * @flags: if V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ is set, then the + * frame_sequence field is valid. + * @frame_sequence: the frame sequence number associated with this event. + * @region_mask: which regions detected motion. + */ +struct v4l2_event_motion_det { + __u32 flags; + __u32 frame_sequence; + __u32 region_mask; +}; + struct v4l2_event { __u32 type; union { @@ -1843,6 +1859,7 @@ struct v4l2_event { struct v4l2_event_ctrl ctrl; struct v4l2_event_frame_sync frame_sync; struct v4l2_event_src_change src_change; + struct v4l2_event_motion_det motion_det; __u8 data[64]; } u; __u32 pending; -- cgit v1.2.3 From 977ff0e4fb3460df9f3dd27de92d60786be28645 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 21 May 2014 22:07:55 -0300 Subject: [media] v4l: Add ARGB and XRGB pixel formats The existing RGB pixel formats are ill-defined in respect to their alpha bits and their meaning is driver dependent. Create new standard ARGB and XRGB variants with clearly defined meanings and make the existing variants deprecated. The new pixel formats 4CC values have been selected to match the DRM 4CCs for the same in-memory formats. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../DocBook/media/v4l/pixfmt-packed-rgb.xml | 415 ++++++++++++++++++++- include/uapi/linux/videodev2.h | 8 + 2 files changed, 403 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml b/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml index e1c4f8b4c0b3..5f1602fe5494 100644 --- a/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml +++ b/Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml @@ -130,9 +130,9 @@ colorspace V4L2_COLORSPACE_SRGB. b1 b0 - - V4L2_PIX_FMT_RGB444 - 'R444' + + V4L2_PIX_FMT_ARGB444 + 'AR12' g3 g2 @@ -152,9 +152,31 @@ colorspace V4L2_COLORSPACE_SRGB. r1 r0 - - V4L2_PIX_FMT_RGB555 - 'RGBO' + + V4L2_PIX_FMT_XRGB444 + 'XR12' + + g3 + g2 + g1 + g0 + b3 + b2 + b1 + b0 + + - + - + - + - + r3 + r2 + r1 + r0 + + + V4L2_PIX_FMT_ARGB555 + 'AR15' g2 g1 @@ -174,6 +196,28 @@ colorspace V4L2_COLORSPACE_SRGB. g4 g3 + + V4L2_PIX_FMT_XRGB555 + 'XR15' + + g2 + g1 + g0 + b4 + b3 + b2 + b1 + b0 + + - + r4 + r3 + r2 + r1 + r0 + g4 + g3 + V4L2_PIX_FMT_RGB565 'RGBP' @@ -341,9 +385,9 @@ colorspace V4L2_COLORSPACE_SRGB. b1 b0 - - V4L2_PIX_FMT_BGR32 - 'BGR4' + + V4L2_PIX_FMT_ABGR32 + 'AR24' b7 b6 @@ -381,9 +425,49 @@ colorspace V4L2_COLORSPACE_SRGB. a1 a0 - - V4L2_PIX_FMT_RGB32 - 'RGB4' + + V4L2_PIX_FMT_XBGR32 + 'XR24' + + b7 + b6 + b5 + b4 + b3 + b2 + b1 + b0 + + g7 + g6 + g5 + g4 + g3 + g2 + g1 + g0 + + r7 + r6 + r5 + r4 + r3 + r2 + r1 + r0 + + - + - + - + - + - + - + - + - + + + V4L2_PIX_FMT_ARGB32 + 'AX24' a7 a6 @@ -421,18 +505,76 @@ colorspace V4L2_COLORSPACE_SRGB. b1 b0 + + V4L2_PIX_FMT_XRGB32 + 'BX24' + + - + - + - + - + - + - + - + - + + r7 + r6 + r5 + r4 + r3 + r2 + r1 + r0 + + g7 + g6 + g5 + g4 + g3 + g2 + g1 + g0 + + b7 + b6 + b5 + b4 + b3 + b2 + b1 + b0 + - Bit 7 is the most significant bit. The value of the a = alpha -bits is undefined when reading from the driver, ignored when writing -to the driver, except when alpha blending has been negotiated for a -Video Overlay or -Video Output Overlay or when the alpha component has been configured -for a Video Capture by means of V4L2_CID_ALPHA_COMPONENT - control. + Bit 7 is the most significant bit. + + The usage and value of the alpha bits (a) in the ARGB and ABGR formats + (collectively referred to as alpha formats) depend on the device type and + hardware operation. Capture devices + (including capture queues of mem-to-mem devices) fill the alpha component in + memory. When the device outputs an alpha channel the alpha component will + have a meaningful value. Otherwise, when the device doesn't output an alpha + channel but can set the alpha bit to a user-configurable value, the V4L2_CID_ALPHA_COMPONENT + control is used to specify that alpha value, and the alpha component + of all pixels will be set to the value specified by that control. Otherwise + a corresponding format without an alpha component (XRGB or XBGR) must be + used instead of an alpha format. + + Output devices (including output queues + of mem-to-mem devices and video output overlay + devices) read the alpha component from memory. When the device processes the + alpha channel the alpha component must be filled with meaningful values by + applications. Otherwise a corresponding format without an alpha component + (XRGB or XBGR) must be used instead of an alpha format. + + The XRGB and XBGR formats contain undefined bits (-). Applications, + devices and drivers must ignore those bits, for both capture and output + devices. <constant>V4L2_PIX_FMT_BGR24</constant> 4 × 4 pixel @@ -512,6 +654,239 @@ image + Formats defined in are + deprecated and must not be used by new drivers. They are documented here for + reference. The meaning of their alpha bits (a) is ill-defined and + interpreted as in either the corresponding ARGB or XRGB format, depending on + the driver. + + + Deprecated Packed RGB Image Formats + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Identifier + Code +   + Byte 0 in memory + Byte 1 + Byte 2 + Byte 3 + + +   +   + Bit + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0 +   + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0 +   + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0 +   + 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0 + + + + + V4L2_PIX_FMT_RGB444 + 'R444' + + g3 + g2 + g1 + g0 + b3 + b2 + b1 + b0 + + a3 + a2 + a1 + a0 + r3 + r2 + r1 + r0 + + + V4L2_PIX_FMT_RGB555 + 'RGBO' + + g2 + g1 + g0 + b4 + b3 + b2 + b1 + b0 + + a + r4 + r3 + r2 + r1 + r0 + g4 + g3 + + + V4L2_PIX_FMT_BGR32 + 'BGR4' + + b7 + b6 + b5 + b4 + b3 + b2 + b1 + b0 + + g7 + g6 + g5 + g4 + g3 + g2 + g1 + g0 + + r7 + r6 + r5 + r4 + r3 + r2 + r1 + r0 + + a7 + a6 + a5 + a4 + a3 + a2 + a1 + a0 + + + V4L2_PIX_FMT_RGB32 + 'RGB4' + + a7 + a6 + a5 + a4 + a3 + a2 + a1 + a0 + + r7 + r6 + r5 + r4 + r3 + r2 + r1 + r0 + + g7 + g6 + g5 + g4 + g3 + g2 + g1 + g0 + + b7 + b6 + b5 + b4 + b3 + b2 + b1 + b0 + + + +
+ A test utility to determine which RGB formats a driver actually supports is available from the LinuxTV v4l-dvb repository. See &v4l-dvb; for access instructions. diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 1477abebd35b..a498d8b58679 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -294,7 +294,11 @@ struct v4l2_pix_format { /* RGB formats */ #define V4L2_PIX_FMT_RGB332 v4l2_fourcc('R', 'G', 'B', '1') /* 8 RGB-3-3-2 */ #define V4L2_PIX_FMT_RGB444 v4l2_fourcc('R', '4', '4', '4') /* 16 xxxxrrrr ggggbbbb */ +#define V4L2_PIX_FMT_ARGB444 v4l2_fourcc('A', 'R', '1', '2') /* 16 aaaarrrr ggggbbbb */ +#define V4L2_PIX_FMT_XRGB444 v4l2_fourcc('X', 'R', '1', '2') /* 16 xxxxrrrr ggggbbbb */ #define V4L2_PIX_FMT_RGB555 v4l2_fourcc('R', 'G', 'B', 'O') /* 16 RGB-5-5-5 */ +#define V4L2_PIX_FMT_ARGB555 v4l2_fourcc('A', 'R', '1', '5') /* 16 ARGB-1-5-5-5 */ +#define V4L2_PIX_FMT_XRGB555 v4l2_fourcc('X', 'R', '1', '5') /* 16 XRGB-1-5-5-5 */ #define V4L2_PIX_FMT_RGB565 v4l2_fourcc('R', 'G', 'B', 'P') /* 16 RGB-5-6-5 */ #define V4L2_PIX_FMT_RGB555X v4l2_fourcc('R', 'G', 'B', 'Q') /* 16 RGB-5-5-5 BE */ #define V4L2_PIX_FMT_RGB565X v4l2_fourcc('R', 'G', 'B', 'R') /* 16 RGB-5-6-5 BE */ @@ -302,7 +306,11 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_BGR24 v4l2_fourcc('B', 'G', 'R', '3') /* 24 BGR-8-8-8 */ #define V4L2_PIX_FMT_RGB24 v4l2_fourcc('R', 'G', 'B', '3') /* 24 RGB-8-8-8 */ #define V4L2_PIX_FMT_BGR32 v4l2_fourcc('B', 'G', 'R', '4') /* 32 BGR-8-8-8-8 */ +#define V4L2_PIX_FMT_ABGR32 v4l2_fourcc('A', 'R', '2', '4') /* 32 BGRA-8-8-8-8 */ +#define V4L2_PIX_FMT_XBGR32 v4l2_fourcc('X', 'R', '2', '4') /* 32 BGRX-8-8-8-8 */ #define V4L2_PIX_FMT_RGB32 v4l2_fourcc('R', 'G', 'B', '4') /* 32 RGB-8-8-8-8 */ +#define V4L2_PIX_FMT_ARGB32 v4l2_fourcc('B', 'A', '2', '4') /* 32 ARGB-8-8-8-8 */ +#define V4L2_PIX_FMT_XRGB32 v4l2_fourcc('B', 'X', '2', '4') /* 32 XRGB-8-8-8-8 */ /* Grey formats */ #define V4L2_PIX_FMT_GREY v4l2_fourcc('G', 'R', 'E', 'Y') /* 8 Greyscale */ -- cgit v1.2.3 From d52e23813672c3c72f92e7b39c7408d4b9a40a96 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 27 May 2014 09:41:05 -0300 Subject: [media] v4l: Support extending the v4l2_pix_format structure The v4l2_pix_format structure has no reserved field. It is embedded in the v4l2_framebuffer structure which has no reserved fields either, and in the v4l2_format structure which has reserved fields that were not previously required to be zeroed out by applications. To allow extending v4l2_pix_format, inline it in the v4l2_framebuffer structure, and use the priv field as a magic value to indicate that the application has set all v4l2_pix_format extended fields and zeroed all reserved fields following the v4l2_pix_format field in the v4l2_format structure. The availability of this API extension is reported to userspace through the new V4L2_CAP_EXT_PIX_FORMAT capability flag. Just checking that the priv field is still set to the magic value at [GS]_FMT return wouldn't be enough, as older kernels don't zero the priv field on return. To simplify the internal API towards drivers zero the extended fields and set the priv field to the magic value for applications not aware of the extensions. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/Makefile | 2 +- Documentation/DocBook/media/v4l/pixfmt.xml | 25 ++++++++- Documentation/DocBook/media/v4l/v4l2.xml | 8 +++ Documentation/DocBook/media/v4l/vidioc-g-fbuf.xml | 12 ++-- .../DocBook/media/v4l/vidioc-querycap.xml | 6 ++ drivers/media/parport/bw-qcam.c | 2 - drivers/media/pci/cx18/cx18-ioctl.c | 1 - drivers/media/pci/cx25821/cx25821-video.c | 3 - drivers/media/pci/ivtv/ivtv-ioctl.c | 3 - drivers/media/pci/meye/meye.c | 2 - drivers/media/pci/saa7134/saa7134-empress.c | 3 - drivers/media/pci/saa7134/saa7134-video.c | 2 - drivers/media/pci/sta2x11/sta2x11_vip.c | 1 - drivers/media/platform/coda.c | 2 - drivers/media/platform/davinci/vpif_display.c | 1 - drivers/media/platform/mem2mem_testdev.c | 1 - drivers/media/platform/omap/omap_vout.c | 2 - drivers/media/platform/sh_veu.c | 2 - drivers/media/platform/vino.c | 5 -- drivers/media/platform/vivi.c | 1 - drivers/media/usb/cx231xx/cx231xx-417.c | 2 - drivers/media/usb/cx231xx/cx231xx-video.c | 2 - drivers/media/usb/gspca/gspca.c | 8 +-- drivers/media/usb/hdpvr/hdpvr-video.c | 1 - drivers/media/usb/stkwebcam/stk-webcam.c | 2 - drivers/media/usb/tlg2300/pd-video.c | 1 - drivers/media/usb/tm6000/tm6000-video.c | 2 - drivers/media/usb/zr364xx/zr364xx.c | 3 - drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 19 +++++-- drivers/media/v4l2-core/v4l2-ioctl.c | 65 ++++++++++++++++++++-- include/uapi/linux/videodev2.h | 15 ++++- 31 files changed, 134 insertions(+), 70 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/media/Makefile b/Documentation/DocBook/media/Makefile index 639e74857968..df2962d9e11e 100644 --- a/Documentation/DocBook/media/Makefile +++ b/Documentation/DocBook/media/Makefile @@ -174,7 +174,7 @@ FILENAME = \ DOCUMENTED = \ -e "s/\(enum *\)v4l2_mpeg_cx2341x_video_\([a-z]*_spatial_filter_type\)/\1v4l2_mpeg_cx2341x_video_\2<\/link>/g" \ -e "s/\(\(enum\|struct\) *\)\(v4l2_[a-zA-Z0-9_]*\)/\1\3<\/link>/g" \ - -e "s/\(V4L2_PIX_FMT_[A-Z0-9_]\+\) /\1<\/link> /g" \ + -e "s/\(V4L2_PIX_FMT_[A-Z0-9_]\+\)\(\s\+v4l2_fourcc\)/\1<\/link>\2/g" \ -e ":a;s/\(linkend=\".*\)_\(.*\">\)/\1-\2/;ta" \ -e "s/v4l2\-mpeg\-vbi\-ITV0/v4l2-mpeg-vbi-itv0-1/g" diff --git a/Documentation/DocBook/media/v4l/pixfmt.xml b/Documentation/DocBook/media/v4l/pixfmt.xml index 91dcbc84f3f8..bb36b3829cf9 100644 --- a/Documentation/DocBook/media/v4l/pixfmt.xml +++ b/Documentation/DocBook/media/v4l/pixfmt.xml @@ -112,9 +112,28 @@ see . __u32 priv - Reserved for custom (driver defined) additional -information about formats. When not used drivers and applications must -set this field to zero. + This field indicates whether the remaining fields of the +v4l2_pix_format structure, also called the extended +fields, are valid. When set to V4L2_PIX_FMT_PRIV_MAGIC, it +indicates that the extended fields have been correctly initialized. When set to +any other value it indicates that the extended fields contain undefined values. + +Applications that wish to use the pixel format extended fields must first +ensure that the feature is supported by querying the device for the +V4L2_CAP_EXT_PIX_FORMAT +capability. If the capability isn't set the pixel format extended fields are not +supported and using the extended fields will lead to undefined results. +To use the extended fields, applications must set the +priv field to +V4L2_PIX_FMT_PRIV_MAGIC, initialize all the extended fields +and zero the unused bytes of the v4l2_format +raw_data field. +When the priv field isn't set to +V4L2_PIX_FMT_PRIV_MAGIC drivers must act as if all the +extended fields were set to zero. On return drivers must set the +priv field to +V4L2_PIX_FMT_PRIV_MAGIC and all the extended fields to +applicable values. diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml index b445161b912c..d0a48bebfa52 100644 --- a/Documentation/DocBook/media/v4l/v4l2.xml +++ b/Documentation/DocBook/media/v4l/v4l2.xml @@ -151,6 +151,14 @@ structs, ioctls) must be noted in more detail in the history chapter (compat.xml), along with the possible impact on existing drivers and applications. --> + + 3.16 + 2014-05-27 + lp + Extended &v4l2-pix-format;. + + + 3.15 2014-02-03 diff --git a/Documentation/DocBook/media/v4l/vidioc-g-fbuf.xml b/Documentation/DocBook/media/v4l/vidioc-g-fbuf.xml index 7c63815e7afd..20460730b02c 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-fbuf.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-fbuf.xml @@ -152,13 +152,10 @@ a valid base address, so applications can find the corresponding Linux framebuffer device (see ).
- &v4l2-pix-format; + struct fmt - Layout of the frame buffer. The -v4l2_pix_format structure is defined in , for clarification the fields and acceptable values - are listed below: + Layout of the frame buffer. @@ -276,9 +273,8 @@ see . __u32 priv - Reserved for additional information about custom -(driver defined) formats. When not used drivers and applications must -set this field to zero. + Reserved. Drivers and applications must set this field to +zero. diff --git a/Documentation/DocBook/media/v4l/vidioc-querycap.xml b/Documentation/DocBook/media/v4l/vidioc-querycap.xml index 370d49d6fb64..d0c5e604f014 100644 --- a/Documentation/DocBook/media/v4l/vidioc-querycap.xml +++ b/Documentation/DocBook/media/v4l/vidioc-querycap.xml @@ -300,6 +300,12 @@ modulator programming see 0x00100000 The device supports the SDR Capture interface. +
+ + V4L2_CAP_EXT_PIX_FORMAT + 0x00200000 + The device supports the &v4l2-pix-format; extended +fields. V4L2_CAP_READWRITE diff --git a/drivers/media/parport/bw-qcam.c b/drivers/media/parport/bw-qcam.c index 3c5dac4c1eab..67b9da1dc43f 100644 --- a/drivers/media/parport/bw-qcam.c +++ b/drivers/media/parport/bw-qcam.c @@ -759,7 +759,6 @@ static int qcam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f pix->sizeimage = pix->width * pix->height; /* Just a guess */ pix->colorspace = V4L2_COLORSPACE_SRGB; - pix->priv = 0; return 0; } @@ -785,7 +784,6 @@ static int qcam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format pix->sizeimage = pix->width * pix->height; /* Just a guess */ pix->colorspace = V4L2_COLORSPACE_SRGB; - pix->priv = 0; return 0; } diff --git a/drivers/media/pci/cx18/cx18-ioctl.c b/drivers/media/pci/cx18/cx18-ioctl.c index fefb2cd35838..6f2b59042b73 100644 --- a/drivers/media/pci/cx18/cx18-ioctl.c +++ b/drivers/media/pci/cx18/cx18-ioctl.c @@ -156,7 +156,6 @@ static int cx18_g_fmt_vid_cap(struct file *file, void *fh, pixfmt->height = cx->cxhdl.height; pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M; pixfmt->field = V4L2_FIELD_INTERLACED; - pixfmt->priv = 0; if (id->type == CX18_ENC_STREAM_TYPE_YUV) { pixfmt->pixelformat = s->pixelformat; pixfmt->sizeimage = s->vb_bytes_per_frame; diff --git a/drivers/media/pci/cx25821/cx25821-video.c b/drivers/media/pci/cx25821/cx25821-video.c index 8d2f1abeef77..3a419f134584 100644 --- a/drivers/media/pci/cx25821/cx25821-video.c +++ b/drivers/media/pci/cx25821/cx25821-video.c @@ -576,7 +576,6 @@ static int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.bytesperline = (chan->width * chan->fmt->depth) >> 3; f->fmt.pix.sizeimage = chan->height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - f->fmt.pix.priv = 0; return 0; } @@ -615,7 +614,6 @@ static int cx25821_vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - f->fmt.pix.priv = 0; return 0; } @@ -867,7 +865,6 @@ static int cx25821_vidioc_try_fmt_vid_out(struct file *file, void *priv, f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - f->fmt.pix.priv = 0; return 0; } diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.c b/drivers/media/pci/ivtv/ivtv-ioctl.c index b3667a00db3a..3e0cb77d5930 100644 --- a/drivers/media/pci/ivtv/ivtv-ioctl.c +++ b/drivers/media/pci/ivtv/ivtv-ioctl.c @@ -351,7 +351,6 @@ static int ivtv_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f pixfmt->height = itv->cxhdl.height; pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M; pixfmt->field = V4L2_FIELD_INTERLACED; - pixfmt->priv = 0; if (id->type == IVTV_ENC_STREAM_TYPE_YUV) { pixfmt->pixelformat = V4L2_PIX_FMT_HM12; /* YUV size is (Y=(h*720) + UV=(h*(720/2))) */ @@ -418,7 +417,6 @@ static int ivtv_g_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *f pixfmt->height = itv->main_rect.height; pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M; pixfmt->field = V4L2_FIELD_INTERLACED; - pixfmt->priv = 0; if (id->type == IVTV_DEC_STREAM_TYPE_YUV) { switch (itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) { case IVTV_YUV_MODE_INTERLACED: @@ -1384,7 +1382,6 @@ static int ivtv_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb) fb->fmt.bytesperline = fb->fmt.width; fb->fmt.colorspace = V4L2_COLORSPACE_SMPTE170M; fb->fmt.field = V4L2_FIELD_INTERLACED; - fb->fmt.priv = 0; if (fb->fmt.pixelformat != V4L2_PIX_FMT_PAL8) fb->fmt.bytesperline *= 2; if (fb->fmt.pixelformat == V4L2_PIX_FMT_RGB32 || diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c index 1a77f8dfafa5..aeae54708811 100644 --- a/drivers/media/pci/meye/meye.c +++ b/drivers/media/pci/meye/meye.c @@ -1166,7 +1166,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = 0; - f->fmt.pix.priv = 0; return 0; } @@ -1232,7 +1231,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *fh, f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = 0; - f->fmt.pix.priv = 0; return 0; } diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c index 5526ed5444fb..ef39261c1f20 100644 --- a/drivers/media/pci/saa7134/saa7134-empress.c +++ b/drivers/media/pci/saa7134/saa7134-empress.c @@ -130,7 +130,6 @@ static int empress_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets; f->fmt.pix.bytesperline = 0; - f->fmt.pix.priv = 0; return 0; } @@ -148,7 +147,6 @@ static int empress_s_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets; f->fmt.pix.bytesperline = 0; - f->fmt.pix.priv = 0; return 0; } @@ -166,7 +164,6 @@ static int empress_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets; f->fmt.pix.bytesperline = 0; - f->fmt.pix.priv = 0; return 0; } diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index d37599980768..0cfa2ca6a32a 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -1235,7 +1235,6 @@ static int saa7134_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - f->fmt.pix.priv = 0; return 0; } @@ -1315,7 +1314,6 @@ static int saa7134_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - f->fmt.pix.priv = 0; return 0; } diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index f2d8c70d35f5..365bd21301ba 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -640,7 +640,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.bytesperline = f->fmt.pix.width * 2; f->fmt.pix.sizeimage = f->fmt.pix.width * 2 * f->fmt.pix.height; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - f->fmt.pix.priv = 0; return 0; } diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index b1783791d426..54886606ed68 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -613,8 +613,6 @@ static int coda_try_fmt(struct coda_ctx *ctx, struct coda_codec *codec, BUG(); } - f->fmt.pix.priv = 0; - return 0; } diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index 877b46e8b2e4..0bd6dcb13cbc 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -648,7 +648,6 @@ static int vpif_try_fmt_vid_out(struct file *file, void *priv, pixfmt->width = common->fmt.fmt.pix.width; pixfmt->height = common->fmt.fmt.pix.height; pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height * 2; - pixfmt->priv = 0; return 0; } diff --git a/drivers/media/platform/mem2mem_testdev.c b/drivers/media/platform/mem2mem_testdev.c index 0714070ed7fa..c1b03cfd6ded 100644 --- a/drivers/media/platform/mem2mem_testdev.c +++ b/drivers/media/platform/mem2mem_testdev.c @@ -532,7 +532,6 @@ static int vidioc_try_fmt(struct v4l2_format *f, struct m2mtest_fmt *fmt) f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.priv = 0; return 0; } diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index 9a726eacb29b..2d177fa58471 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -165,7 +165,6 @@ static int omap_vout_try_format(struct v4l2_pix_format *pix) pix->pixelformat = omap_formats[ifmt].pixelformat; pix->field = V4L2_FIELD_ANY; - pix->priv = 0; switch (pix->pixelformat) { case V4L2_PIX_FMT_YUYV: @@ -1896,7 +1895,6 @@ static int __init omap_vout_setup_video_data(struct omap_vout_device *vout) pix->field = V4L2_FIELD_ANY; pix->bytesperline = pix->width * 2; pix->sizeimage = pix->bytesperline * pix->height; - pix->priv = 0; pix->colorspace = V4L2_COLORSPACE_JPEG; vout->bpp = RGB565_BPP; diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c index 744e43b480bc..8dc279d4d561 100644 --- a/drivers/media/platform/sh_veu.c +++ b/drivers/media/platform/sh_veu.c @@ -425,7 +425,6 @@ static int sh_veu_g_fmt(struct sh_veu_file *veu_file, struct v4l2_format *f) pix->bytesperline = vfmt->bytesperline; pix->sizeimage = vfmt->bytesperline * pix->height * vfmt->fmt->depth / vfmt->fmt->ydepth; - pix->priv = 0; dev_dbg(veu->dev, "%s(): type: %d, size %u @ %ux%u, fmt %x\n", __func__, f->type, pix->sizeimage, pix->width, pix->height, pix->pixelformat); @@ -473,7 +472,6 @@ static int sh_veu_try_fmt(struct v4l2_format *f, const struct sh_veu_format *fmt pix->pixelformat = fmt->fourcc; pix->colorspace = sh_veu_4cc2cspace(pix->pixelformat); - pix->priv = 0; pr_debug("%s(): type: %d, size %u\n", __func__, f->type, pix->sizeimage); diff --git a/drivers/media/platform/vino.c b/drivers/media/platform/vino.c index 470d35336119..91d44ea16f27 100644 --- a/drivers/media/platform/vino.c +++ b/drivers/media/platform/vino.c @@ -3147,7 +3147,6 @@ static int vino_try_fmt_vid_cap(struct file *file, void *__fh, pf->colorspace = vino_data_formats[tempvcs.data_format].colorspace; - pf->priv = 0; return 0; } @@ -3175,8 +3174,6 @@ static int vino_g_fmt_vid_cap(struct file *file, void *__fh, pf->colorspace = vino_data_formats[vcs->data_format].colorspace; - pf->priv = 0; - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); return 0; } @@ -3219,8 +3216,6 @@ static int vino_s_fmt_vid_cap(struct file *file, void *__fh, pf->colorspace = vino_data_formats[vcs->data_format].colorspace; - pf->priv = 0; - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); return 0; } diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index 7542b5dd9910..80333714ffa7 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -1014,7 +1014,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; else f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; - f->fmt.pix.priv = 0; return 0; } diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c index f0400e260eb7..459bb0e98971 100644 --- a/drivers/media/usb/cx231xx/cx231xx-417.c +++ b/drivers/media/usb/cx231xx/cx231xx-417.c @@ -1563,7 +1563,6 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.width = dev->ts1.width; f->fmt.pix.height = dev->ts1.height; f->fmt.pix.field = V4L2_FIELD_INTERLACED; - f->fmt.pix.priv = 0; dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d\n", dev->ts1.width, dev->ts1.height); dprintk(3, "exit vidioc_g_fmt_vid_cap()\n"); @@ -1582,7 +1581,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.sizeimage = mpeglines * mpeglinesize; f->fmt.pix.field = V4L2_FIELD_INTERLACED; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - f->fmt.pix.priv = 0; dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d\n", dev->ts1.width, dev->ts1.height); dprintk(3, "exit vidioc_try_fmt_vid_cap()\n"); diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index ae31ca2fc9a1..3b3ada6562ca 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -885,7 +885,6 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; f->fmt.pix.field = V4L2_FIELD_INTERLACED; - f->fmt.pix.priv = 0; return 0; } @@ -930,7 +929,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; f->fmt.pix.field = V4L2_FIELD_INTERLACED; - f->fmt.pix.priv = 0; return 0; } diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index 42d223239206..e8cf23c91cef 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -1109,8 +1109,8 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct gspca_dev *gspca_dev = video_drvdata(file); fmt->fmt.pix = gspca_dev->pixfmt; - /* some drivers use priv internally, zero it before giving it to - userspace */ + /* some drivers use priv internally, zero it before giving it back to + the core */ fmt->fmt.pix.priv = 0; return 0; } @@ -1146,8 +1146,8 @@ static int try_fmt_vid_cap(struct gspca_dev *gspca_dev, fmt->fmt.pix.height = h; gspca_dev->sd_desc->try_fmt(gspca_dev, fmt); } - /* some drivers use priv internally, zero it before giving it to - userspace */ + /* some drivers use priv internally, zero it before giving it back to + the core */ fmt->fmt.pix.priv = 0; return mode; /* used when s_fmt */ } diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c index dca4b65053aa..4b7653fd4b0b 100644 --- a/drivers/media/usb/hdpvr/hdpvr-video.c +++ b/drivers/media/usb/hdpvr/hdpvr-video.c @@ -1022,7 +1022,6 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *_fh, f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.sizeimage = dev->bulk_in_size; f->fmt.pix.bytesperline = 0; - f->fmt.pix.priv = 0; if (f->fmt.pix.width == 720) { /* SDTV formats */ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c index d76860b6a0b8..3588dc38db87 100644 --- a/drivers/media/usb/stkwebcam/stk-webcam.c +++ b/drivers/media/usb/stkwebcam/stk-webcam.c @@ -923,7 +923,6 @@ static int stk_vidioc_g_fmt_vid_cap(struct file *filp, pix_format->bytesperline = 2 * pix_format->width; pix_format->sizeimage = pix_format->bytesperline * pix_format->height; - pix_format->priv = 0; return 0; } @@ -967,7 +966,6 @@ static int stk_try_fmt_vid_cap(struct file *filp, fmtd->fmt.pix.bytesperline = 2 * fmtd->fmt.pix.width; fmtd->fmt.pix.sizeimage = fmtd->fmt.pix.bytesperline * fmtd->fmt.pix.height; - fmtd->fmt.pix.priv = 0; return 0; } diff --git a/drivers/media/usb/tlg2300/pd-video.c b/drivers/media/usb/tlg2300/pd-video.c index 8df668d06552..8cd7f02fcf9f 100644 --- a/drivers/media/usb/tlg2300/pd-video.c +++ b/drivers/media/usb/tlg2300/pd-video.c @@ -1321,7 +1321,6 @@ static void init_video_context(struct running_context *context) .bytesperline = 720 * 2, .sizeimage = 720 * 576 * 2, .colorspace = V4L2_COLORSPACE_SMPTE170M, - .priv = 0 }; } diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index 9bde0642ffd1..793577fc4633 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -918,7 +918,6 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, (f->fmt.pix.width * fh->fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - f->fmt.pix.priv = 0; return 0; } @@ -959,7 +958,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.width &= ~0x01; f->fmt.pix.field = field; - f->fmt.pix.priv = 0; f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c index 3b80579a82c5..5c006277b8b1 100644 --- a/drivers/media/usb/zr364xx/zr364xx.c +++ b/drivers/media/usb/zr364xx/zr364xx.c @@ -806,7 +806,6 @@ static int zr364xx_vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.bytesperline = f->fmt.pix.width * 2; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; - f->fmt.pix.priv = 0; DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__, decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name), f->fmt.pix.field); @@ -829,7 +828,6 @@ static int zr364xx_vidioc_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.bytesperline = f->fmt.pix.width * 2; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; - f->fmt.pix.priv = 0; return 0; } @@ -866,7 +864,6 @@ static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.bytesperline = f->fmt.pix.width * 2; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; - f->fmt.pix.priv = 0; cam->vb_vidq.field = f->fmt.pix.field; if (f->fmt.pix.width == 160 && f->fmt.pix.height == 120) diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index 7e2411c36419..cca6c2f76b3a 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -540,7 +540,16 @@ struct v4l2_framebuffer32 { __u32 capability; __u32 flags; compat_caddr_t base; - struct v4l2_pix_format fmt; + struct { + __u32 width; + __u32 height; + __u32 pixelformat; + __u32 field; + __u32 bytesperline; + __u32 sizeimage; + __u32 colorspace; + __u32 priv; + } fmt; }; static int get_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_framebuffer32 __user *up) @@ -550,10 +559,10 @@ static int get_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_frame if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_framebuffer32)) || get_user(tmp, &up->base) || get_user(kp->capability, &up->capability) || - get_user(kp->flags, &up->flags)) + get_user(kp->flags, &up->flags) || + copy_from_user(&kp->fmt, &up->fmt, sizeof(up->fmt))) return -EFAULT; kp->base = compat_ptr(tmp); - get_v4l2_pix_format(&kp->fmt, &up->fmt); return 0; } @@ -564,9 +573,9 @@ static int put_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_frame if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_framebuffer32)) || put_user(tmp, &up->base) || put_user(kp->capability, &up->capability) || - put_user(kp->flags, &up->flags)) + put_user(kp->flags, &up->flags) || + copy_to_user(&up->fmt, &kp->fmt, sizeof(up->fmt))) return -EFAULT; - put_v4l2_pix_format(&kp->fmt, &up->fmt); return 0; } diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 96bc117f66b2..2e630005676f 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -973,13 +973,48 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type) return -EINVAL; } +static void v4l_sanitize_format(struct v4l2_format *fmt) +{ + unsigned int offset; + + /* + * The v4l2_pix_format structure has been extended with fields that were + * not previously required to be set to zero by applications. The priv + * field, when set to a magic value, indicates the the extended fields + * are valid. Otherwise they will contain undefined values. To simplify + * the API towards drivers zero the extended fields and set the priv + * field to the magic value when the extended pixel format structure + * isn't used by applications. + */ + + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + fmt->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return; + + if (fmt->fmt.pix.priv == V4L2_PIX_FMT_PRIV_MAGIC) + return; + + fmt->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; + + offset = offsetof(struct v4l2_pix_format, priv) + + sizeof(fmt->fmt.pix.priv); + memset(((void *)&fmt->fmt.pix) + offset, 0, + sizeof(fmt->fmt.pix) - offset); +} + static int v4l_querycap(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { struct v4l2_capability *cap = (struct v4l2_capability *)arg; + int ret; cap->version = LINUX_VERSION_CODE; - return ops->vidioc_querycap(file, fh, cap); + + ret = ops->vidioc_querycap(file, fh, cap); + + cap->capabilities |= V4L2_CAP_EXT_PIX_FORMAT; + + return ret; } static int v4l_s_input(const struct v4l2_ioctl_ops *ops, @@ -1103,12 +1138,17 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR; bool is_rx = vfd->vfl_dir != VFL_DIR_TX; bool is_tx = vfd->vfl_dir != VFL_DIR_RX; + int ret; + + p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; switch (p->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_vid_cap)) break; - return ops->vidioc_g_fmt_vid_cap(file, fh, arg); + ret = ops->vidioc_g_fmt_vid_cap(file, fh, arg); + p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; + return ret; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_vid_cap_mplane)) break; @@ -1128,7 +1168,9 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, case V4L2_BUF_TYPE_VIDEO_OUTPUT: if (unlikely(!is_tx || !is_vid || !ops->vidioc_g_fmt_vid_out)) break; - return ops->vidioc_g_fmt_vid_out(file, fh, arg); + ret = ops->vidioc_g_fmt_vid_out(file, fh, arg); + p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; + return ret; case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: if (unlikely(!is_tx || !is_vid || !ops->vidioc_g_fmt_vid_out_mplane)) break; @@ -1163,6 +1205,8 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, bool is_rx = vfd->vfl_dir != VFL_DIR_TX; bool is_tx = vfd->vfl_dir != VFL_DIR_RX; + v4l_sanitize_format(p); + switch (p->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_vid_cap)) @@ -1233,6 +1277,8 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, bool is_rx = vfd->vfl_dir != VFL_DIR_TX; bool is_tx = vfd->vfl_dir != VFL_DIR_RX; + v4l_sanitize_format(p); + switch (p->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_vid_cap)) @@ -1516,7 +1562,18 @@ static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops, struct v4l2_create_buffers *create = arg; int ret = check_fmt(file, create->format.type); - return ret ? ret : ops->vidioc_create_bufs(file, fh, create); + if (ret) + return ret; + + v4l_sanitize_format(&create->format); + + ret = ops->vidioc_create_bufs(file, fh, create); + + if (create->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE || + create->format.type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + create->format.fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; + + return ret; } static int v4l_prepare_buf(const struct v4l2_ioctl_ops *ops, diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index a498d8b58679..eb3bdd33816b 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -268,6 +268,7 @@ struct v4l2_capability { #define V4L2_CAP_MODULATOR 0x00080000 /* has a modulator */ #define V4L2_CAP_SDR_CAPTURE 0x00100000 /* Is a SDR capture device */ +#define V4L2_CAP_EXT_PIX_FORMAT 0x00200000 /* Supports the extended pixel format */ #define V4L2_CAP_READWRITE 0x01000000 /* read/write systemcalls */ #define V4L2_CAP_ASYNCIO 0x02000000 /* async I/O */ @@ -448,6 +449,9 @@ struct v4l2_pix_format { #define V4L2_SDR_FMT_CU8 v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */ #define V4L2_SDR_FMT_CU16LE v4l2_fourcc('C', 'U', '1', '6') /* IQ u16le */ +/* priv field value to indicates that subsequent fields are valid. */ +#define V4L2_PIX_FMT_PRIV_MAGIC 0xfeedcafe + /* * F O R M A T E N U M E R A T I O N */ @@ -752,7 +756,16 @@ struct v4l2_framebuffer { /* FIXME: in theory we should pass something like PCI device + memory * region + offset instead of some physical address */ void *base; - struct v4l2_pix_format fmt; + struct { + __u32 width; + __u32 height; + __u32 pixelformat; + __u32 field; /* enum v4l2_field */ + __u32 bytesperline; /* for padding, zero if unused */ + __u32 sizeimage; + __u32 colorspace; /* enum v4l2_colorspace */ + __u32 priv; /* reserved field, set to 0 */ + } fmt; }; /* Flags for the 'capability' field. Read only */ #define V4L2_FBUF_CAP_EXTERNOVERLAY 0x0001 -- cgit v1.2.3 From c96fd46afb34a554406bce9784126b96ad09091e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 27 May 2014 10:12:43 -0300 Subject: [media] v4l: Add premultiplied alpha flag for pixel formats When set, the new V4L2_PIX_FMT_FLAG_PREMUL_ALPHA flag indicates that the pixel values are premultiplied by the alpha channel value. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/pixfmt.xml | 31 +++++++++++++++++++++++++++++- Documentation/DocBook/media/v4l/v4l2.xml | 2 +- drivers/media/v4l2-core/v4l2-ioctl.c | 5 +++-- include/uapi/linux/videodev2.h | 8 +++++++- 4 files changed, 41 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/pixfmt.xml b/Documentation/DocBook/media/v4l/pixfmt.xml index bb36b3829cf9..87ce7f3e7178 100644 --- a/Documentation/DocBook/media/v4l/pixfmt.xml +++ b/Documentation/DocBook/media/v4l/pixfmt.xml @@ -135,6 +135,12 @@ extended fields were set to zero. On return drivers must set the V4L2_PIX_FMT_PRIV_MAGIC and all the extended fields to applicable values. + + __u32 + flags + Flags set by the application or driver, see . + @@ -220,9 +226,15 @@ codes can be used. and the number of valid entries in the plane_fmt array.
+ + __u8 + flags + Flags set by the application or driver, see . + __u8 - reserved[11] + reserved[10] Reserved for future extensions. Should be zeroed by the application. @@ -1079,4 +1091,21 @@ concatenated to form the JPEG stream. + + + Format Flags + + &cs-def; + + + V4L2_PIX_FMT_FLAG_PREMUL_ALPHA + 0x00000001 + The color values are premultiplied by the alpha channel +value. For example, if a light blue pixel with 50% transparency was described by +RGBA values (128, 192, 255, 128), the same pixel described with premultiplied +colors would be described by RGBA values (64, 96, 128, 128) + + + +
diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml index d0a48bebfa52..f2f81f06a17b 100644 --- a/Documentation/DocBook/media/v4l/v4l2.xml +++ b/Documentation/DocBook/media/v4l/v4l2.xml @@ -155,7 +155,7 @@ applications. --> 3.16 2014-05-27 lp - Extended &v4l2-pix-format;. + Extended &v4l2-pix-format;. Added format flags. diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 2e630005676f..e0bafda89e13 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -256,7 +256,8 @@ static void v4l_print_format(const void *arg, bool write_only) pix = &p->fmt.pix; pr_cont(", width=%u, height=%u, " "pixelformat=%c%c%c%c, field=%s, " - "bytesperline=%u, sizeimage=%u, colorspace=%d\n", + "bytesperline=%u, sizeimage=%u, colorspace=%d, " + "flags %u\n", pix->width, pix->height, (pix->pixelformat & 0xff), (pix->pixelformat >> 8) & 0xff, @@ -264,7 +265,7 @@ static void v4l_print_format(const void *arg, bool write_only) (pix->pixelformat >> 24) & 0xff, prt_names(pix->field, v4l2_field_names), pix->bytesperline, pix->sizeimage, - pix->colorspace); + pix->colorspace, pix->flags); break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index eb3bdd33816b..b73e8cda7192 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -288,6 +288,7 @@ struct v4l2_pix_format { __u32 sizeimage; __u32 colorspace; /* enum v4l2_colorspace */ __u32 priv; /* private data, depends on pixelformat */ + __u32 flags; /* format flags (V4L2_PIX_FMT_FLAG_*) */ }; /* Pixel format FOURCC depth Description */ @@ -452,6 +453,9 @@ struct v4l2_pix_format { /* priv field value to indicates that subsequent fields are valid. */ #define V4L2_PIX_FMT_PRIV_MAGIC 0xfeedcafe +/* Flags */ +#define V4L2_PIX_FMT_FLAG_PREMUL_ALPHA 0x00000001 + /* * F O R M A T E N U M E R A T I O N */ @@ -1754,6 +1758,7 @@ struct v4l2_plane_pix_format { * @colorspace: enum v4l2_colorspace; supplemental to pixelformat * @plane_fmt: per-plane information * @num_planes: number of planes for this format + * @flags: format flags (V4L2_PIX_FMT_FLAG_*) */ struct v4l2_pix_format_mplane { __u32 width; @@ -1764,7 +1769,8 @@ struct v4l2_pix_format_mplane { struct v4l2_plane_pix_format plane_fmt[VIDEO_MAX_PLANES]; __u8 num_planes; - __u8 reserved[11]; + __u8 flags; + __u8 reserved[10]; } __attribute__ ((packed)); /** -- cgit v1.2.3 From 5df7f71d5cdfbcbfd7e1b68df9994609d33f7e58 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Mon, 14 Jul 2014 15:10:45 -0500 Subject: ASoC: tas2552: Support TI TAS2552 Amplifier Support the TI TAS2552 Class D amplifier. The TAS2552 is a high efficiency Class-D audio power amplifier with advanced battery current management and an integrated Class-G boost The device constantly measures the current and voltage across the load and provides a digital stream of this information. Signed-off-by: Dan Murphy Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/tas2552.txt | 26 + include/sound/tas2552-plat.h | 25 + sound/soc/codecs/Kconfig | 5 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/tas2552.c | 540 +++++++++++++++++++++ sound/soc/codecs/tas2552.h | 129 +++++ 6 files changed, 727 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/tas2552.txt create mode 100644 include/sound/tas2552-plat.h create mode 100644 sound/soc/codecs/tas2552.c create mode 100644 sound/soc/codecs/tas2552.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/sound/tas2552.txt b/Documentation/devicetree/bindings/sound/tas2552.txt new file mode 100644 index 000000000000..55e2a0af5645 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/tas2552.txt @@ -0,0 +1,26 @@ +Texas Instruments - tas2552 Codec module + +The tas2552 serial control bus communicates through I2C protocols + +Required properties: + - compatible - One of: + "ti,tas2552" - TAS2552 + - reg - I2C slave address + - supply-*: Required supply regulators are: + "vbat" battery voltage + "iovdd" I/O Voltage + "avdd" Analog DAC Voltage + +Optional properties: + - enable-gpio - gpio pin to enable/disable the device + +Example: + +tas2552: tas2552@41 { + compatible = "ti,tas2552"; + reg = <0x41>; + enable-gpio = <&gpio4 2 GPIO_ACTIVE_HIGH>; +}; + +For more product information please see the link below: +http://www.ti.com/product/TAS2552 diff --git a/include/sound/tas2552-plat.h b/include/sound/tas2552-plat.h new file mode 100644 index 000000000000..65e7627ba38e --- /dev/null +++ b/include/sound/tas2552-plat.h @@ -0,0 +1,25 @@ +/* + * TAS2552 driver platform header + * + * Copyright (C) 2014 Texas Instruments Inc. + * + * Author: Dan Murphy + * + * 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. + */ + +#ifndef TAS2552_PLAT_H +#define TAS2552_PLAT_H + +struct tas2552_platform_data { + int enable_gpio; +}; + +#endif diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index cbfa1e18f651..480881747ec5 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -91,6 +91,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_STA350 if I2C select SND_SOC_STA529 if I2C select SND_SOC_STAC9766 if SND_SOC_AC97_BUS + select SND_SOC_TAS2552 if I2C select SND_SOC_TAS5086 if I2C select SND_SOC_TLV320AIC23_I2C if I2C select SND_SOC_TLV320AIC23_SPI if SPI_MASTER @@ -513,6 +514,10 @@ config SND_SOC_STA529 config SND_SOC_STAC9766 tristate +config SND_SOC_TAS2552 + tristate "Texas Instruments TAS2552 Mono Audio amplifier" + depends on I2C + config SND_SOC_TAS5086 tristate "Texas Instruments TAS5086 speaker amplifier" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index be3377b8d73f..d79de052ef8e 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -160,6 +160,7 @@ snd-soc-wm-hubs-objs := wm_hubs.o # Amp snd-soc-max9877-objs := max9877.o snd-soc-tpa6130a2-objs := tpa6130a2.o +snd-soc-tas2552-objs := tas2552.o obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o @@ -251,6 +252,7 @@ obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o obj-$(CONFIG_SND_SOC_STA350) += snd-soc-sta350.o obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o +obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c new file mode 100644 index 000000000000..f0760af5a21e --- /dev/null +++ b/sound/soc/codecs/tas2552.c @@ -0,0 +1,540 @@ +/* + * tas2552.c - ALSA SoC Texas Instruments TAS2552 Mono Audio Amplifier + * + * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com + * + * Author: Dan Murphy + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "tas2552.h" + +static struct reg_default tas2552_reg_defs[] = { + {TAS2552_CFG_1, 0x22}, + {TAS2552_CFG_3, 0x80}, + {TAS2552_DOUT, 0x00}, + {TAS2552_OUTPUT_DATA, 0xc0}, + {TAS2552_PDM_CFG, 0x01}, + {TAS2552_PGA_GAIN, 0x00}, + {TAS2552_BOOST_PT_CTRL, 0x0f}, + {TAS2552_RESERVED_0D, 0x00}, + {TAS2552_LIMIT_RATE_HYS, 0x08}, + {TAS2552_CFG_2, 0xef}, + {TAS2552_SER_CTRL_1, 0x00}, + {TAS2552_SER_CTRL_2, 0x00}, + {TAS2552_PLL_CTRL_1, 0x10}, + {TAS2552_PLL_CTRL_2, 0x00}, + {TAS2552_PLL_CTRL_3, 0x00}, + {TAS2552_BTIP, 0x8f}, + {TAS2552_BTS_CTRL, 0x80}, + {TAS2552_LIMIT_RELEASE, 0x04}, + {TAS2552_LIMIT_INT_COUNT, 0x00}, + {TAS2552_EDGE_RATE_CTRL, 0x40}, + {TAS2552_VBAT_DATA, 0x00}, +}; + +#define TAS2552_NUM_SUPPLIES 3 +static const char *tas2552_supply_names[TAS2552_NUM_SUPPLIES] = { + "vbat", /* vbat voltage */ + "iovdd", /* I/O Voltage */ + "avdd", /* Analog DAC Voltage */ +}; + +struct tas2552_data { + struct snd_soc_codec *codec; + struct regmap *regmap; + struct i2c_client *tas2552_client; + struct regulator_bulk_data supplies[TAS2552_NUM_SUPPLIES]; + struct gpio_desc *enable_gpio; + unsigned char regs[TAS2552_VBAT_DATA]; + unsigned int mclk; +}; + +static void tas2552_sw_shutdown(struct tas2552_data *tas_data, int sw_shutdown) +{ + u8 cfg1_reg; + + if (sw_shutdown) + cfg1_reg = 0; + else + cfg1_reg = TAS2552_SWS_MASK; + + snd_soc_update_bits(tas_data->codec, TAS2552_CFG_1, + TAS2552_SWS_MASK, cfg1_reg); +} + +static int tas2552_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev); + int sample_rate, pll_clk; + int d; + u8 p, j; + + /* Turn on Class D amplifier */ + snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_CLASSD_EN_MASK, + TAS2552_CLASSD_EN); + + if (!tas2552->mclk) + return -EINVAL; + + snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0); + + if (tas2552->mclk == TAS2552_245MHZ_CLK || + tas2552->mclk == TAS2552_225MHZ_CLK) { + /* By pass the PLL configuration */ + snd_soc_update_bits(codec, TAS2552_PLL_CTRL_2, + TAS2552_PLL_BYPASS_MASK, + TAS2552_PLL_BYPASS); + } else { + /* Fill in the PLL control registers for J & D + * PLL_CLK = (.5 * freq * J.D) / 2^p + * Need to fill in J and D here based on incoming freq + */ + p = snd_soc_read(codec, TAS2552_PLL_CTRL_1); + p = (p >> 7); + sample_rate = params_rate(params); + + if (sample_rate == 48000) + pll_clk = TAS2552_245MHZ_CLK; + else if (sample_rate == 44100) + pll_clk = TAS2552_225MHZ_CLK; + else { + dev_vdbg(codec->dev, "Substream sample rate is not found %i\n", + params_rate(params)); + return -EINVAL; + } + + j = (pll_clk * 2 * (1 << p)) / tas2552->mclk; + d = (pll_clk * 2 * (1 << p)) % tas2552->mclk; + + snd_soc_update_bits(codec, TAS2552_PLL_CTRL_1, + TAS2552_PLL_J_MASK, j); + snd_soc_write(codec, TAS2552_PLL_CTRL_2, + (d >> 7) & TAS2552_PLL_D_UPPER_MASK); + snd_soc_write(codec, TAS2552_PLL_CTRL_3, + d & TAS2552_PLL_D_LOWER_MASK); + + } + + snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, + TAS2552_PLL_ENABLE); + + return 0; +} + +static int tas2552_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + u8 serial_format; + u8 serial_control_mask; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + serial_format = 0x00; + break; + case SND_SOC_DAIFMT_CBS_CFM: + serial_format = TAS2552_WORD_CLK_MASK; + break; + case SND_SOC_DAIFMT_CBM_CFS: + serial_format = TAS2552_BIT_CLK_MASK; + break; + case SND_SOC_DAIFMT_CBM_CFM: + serial_format = (TAS2552_BIT_CLK_MASK | TAS2552_WORD_CLK_MASK); + break; + default: + dev_vdbg(codec->dev, "DAI Format master is not found\n"); + return -EINVAL; + } + + serial_control_mask = TAS2552_BIT_CLK_MASK | TAS2552_WORD_CLK_MASK; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + serial_format &= TAS2552_DAIFMT_I2S_MASK; + break; + case SND_SOC_DAIFMT_DSP_A: + serial_format |= TAS2552_DAIFMT_DSP; + break; + case SND_SOC_DAIFMT_RIGHT_J: + serial_format |= TAS2552_DAIFMT_RIGHT_J; + break; + case SND_SOC_DAIFMT_LEFT_J: + serial_format |= TAS2552_DAIFMT_LEFT_J; + break; + default: + dev_vdbg(codec->dev, "DAI Format is not found\n"); + return -EINVAL; + } + + if (fmt & SND_SOC_DAIFMT_FORMAT_MASK) + serial_control_mask |= TAS2552_DATA_FORMAT_MASK; + + snd_soc_update_bits(codec, TAS2552_SER_CTRL_1, serial_control_mask, + serial_format); + + return 0; +} + +static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev); + + tas2552->mclk = freq; + + return 0; +} + +static int tas2552_mute(struct snd_soc_dai *dai, int mute) +{ + u8 cfg1_reg; + struct snd_soc_codec *codec = dai->codec; + + if (mute) + cfg1_reg = TAS2552_MUTE_MASK; + else + cfg1_reg = ~TAS2552_MUTE_MASK; + + snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE_MASK, cfg1_reg); + + return 0; +} + +#ifdef CONFIG_PM_RUNTIME +static int tas2552_runtime_suspend(struct device *dev) +{ + struct tas2552_data *tas2552 = dev_get_drvdata(dev); + + tas2552_sw_shutdown(tas2552, 0); + + if (tas2552->enable_gpio) + gpiod_set_value(tas2552->enable_gpio, 0); + + regcache_cache_only(tas2552->regmap, true); + regcache_mark_dirty(tas2552->regmap); + + return 0; +} + +static int tas2552_runtime_resume(struct device *dev) +{ + struct tas2552_data *tas2552 = dev_get_drvdata(dev); + + if (tas2552->enable_gpio) + gpiod_set_value(tas2552->enable_gpio, 1); + + tas2552_sw_shutdown(tas2552, 1); + + regcache_cache_only(tas2552->regmap, false); + regcache_sync(tas2552->regmap); + + return 0; +} +#endif + +static const struct dev_pm_ops tas2552_pm = { + SET_RUNTIME_PM_OPS(tas2552_runtime_suspend, tas2552_runtime_resume, + NULL) +}; + +static void tas2552_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + + snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0); +} + +static struct snd_soc_dai_ops tas2552_speaker_dai_ops = { + .hw_params = tas2552_hw_params, + .set_sysclk = tas2552_set_dai_sysclk, + .set_fmt = tas2552_set_dai_fmt, + .shutdown = tas2552_shutdown, + .digital_mute = tas2552_mute, +}; + +/* Formats supported by TAS2552 driver. */ +#define TAS2552_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +/* TAS2552 dai structure. */ +static struct snd_soc_dai_driver tas2552_dai[] = { + { + .name = "tas2552-amplifier", + .playback = { + .stream_name = "Speaker", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = TAS2552_FORMATS, + }, + .ops = &tas2552_speaker_dai_ops, + }, +}; + +/* + * DAC digital volumes. From -7 to 24 dB in 1 dB steps + */ +static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 24); + +static const struct snd_kcontrol_new tas2552_snd_controls[] = { + SOC_SINGLE_TLV("Speaker Driver Playback Volume", + TAS2552_PGA_GAIN, 0, 0x1f, 1, dac_tlv), +}; + +static const struct reg_default tas2552_init_regs[] = { + { TAS2552_RESERVED_0D, 0xc0 }, +}; + +static int tas2552_codec_probe(struct snd_soc_codec *codec) +{ + struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec); + int ret; + + tas2552->codec = codec; + + ret = regulator_bulk_enable(ARRAY_SIZE(tas2552->supplies), + tas2552->supplies); + + if (ret != 0) { + dev_err(codec->dev, "Failed to enable supplies: %d\n", + ret); + return ret; + } + + if (tas2552->enable_gpio) + gpiod_set_value(tas2552->enable_gpio, 1); + + ret = pm_runtime_get_sync(codec->dev); + if (ret < 0) { + dev_err(codec->dev, "Enabling device failed: %d\n", + ret); + goto probe_fail; + } + + snd_soc_write(codec, TAS2552_CFG_1, TAS2552_MUTE_MASK | + TAS2552_PLL_SRC_BCLK); + snd_soc_write(codec, TAS2552_CFG_3, TAS2552_I2S_OUT_SEL | + TAS2552_DIN_SRC_SEL_AVG_L_R | TAS2552_88_96KHZ); + snd_soc_write(codec, TAS2552_DOUT, TAS2552_PDM_DATA_I); + snd_soc_write(codec, TAS2552_OUTPUT_DATA, TAS2552_PDM_DATA_V_I | 0x8); + snd_soc_write(codec, TAS2552_PDM_CFG, TAS2552_PDM_BCLK_SEL); + snd_soc_write(codec, TAS2552_BOOST_PT_CTRL, TAS2552_APT_DELAY_200 | + TAS2552_APT_THRESH_2_1_7); + + ret = regmap_register_patch(tas2552->regmap, tas2552_init_regs, + ARRAY_SIZE(tas2552_init_regs)); + if (ret != 0) { + dev_err(codec->dev, "Failed to write init registers: %d\n", + ret); + goto patch_fail; + } + + snd_soc_write(codec, TAS2552_CFG_2, TAS2552_CLASSD_EN | + TAS2552_BOOST_EN | TAS2552_APT_EN | + TAS2552_LIM_EN); + return 0; + +patch_fail: + pm_runtime_put(codec->dev); +probe_fail: + if (tas2552->enable_gpio) + gpiod_set_value(tas2552->enable_gpio, 0); + + regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies), + tas2552->supplies); + return -EIO; +} + +static int tas2552_codec_remove(struct snd_soc_codec *codec) +{ + struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec); + + if (tas2552->enable_gpio) + gpiod_set_value(tas2552->enable_gpio, 0); + + return 0; +}; + +#ifdef CONFIG_PM +static int tas2552_suspend(struct snd_soc_codec *codec) +{ + struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec); + int ret; + + ret = regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies), + tas2552->supplies); + + if (ret != 0) + dev_err(codec->dev, "Failed to disable supplies: %d\n", + ret); + return 0; +} + +static int tas2552_resume(struct snd_soc_codec *codec) +{ + struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(tas2552->supplies), + tas2552->supplies); + + if (ret != 0) { + dev_err(codec->dev, "Failed to enable supplies: %d\n", + ret); + } + + return 0; +} +#else +#define tas2552_suspend NULL +#define tas2552_resume NULL +#endif + +static struct snd_soc_codec_driver soc_codec_dev_tas2552 = { + .probe = tas2552_codec_probe, + .remove = tas2552_codec_remove, + .suspend = tas2552_suspend, + .resume = tas2552_resume, + .controls = tas2552_snd_controls, + .num_controls = ARRAY_SIZE(tas2552_snd_controls), +}; + +static const struct regmap_config tas2552_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = TAS2552_MAX_REG, + .reg_defaults = tas2552_reg_defs, + .num_reg_defaults = ARRAY_SIZE(tas2552_reg_defs), + .cache_type = REGCACHE_RBTREE, +}; + +static int tas2552_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev; + struct tas2552_data *data; + int ret; + int i; + + dev = &client->dev; + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + data->enable_gpio = devm_gpiod_get(dev, "enable"); + if (IS_ERR(data->enable_gpio)) { + ret = PTR_ERR(data->enable_gpio); + if (ret != -ENOENT && ret != -ENOSYS) + return ret; + + data->enable_gpio = NULL; + } else { + gpiod_direction_output(data->enable_gpio, 0); + } + + data->tas2552_client = client; + data->regmap = devm_regmap_init_i2c(client, &tas2552_regmap_config); + if (IS_ERR(data->regmap)) { + ret = PTR_ERR(data->regmap); + dev_err(&client->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(data->supplies); i++) + data->supplies[i].supply = tas2552_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies), + data->supplies); + if (ret != 0) + dev_err(dev, "Failed to request supplies: %d\n", ret); + + pm_runtime_set_active(&client->dev); + pm_runtime_set_autosuspend_delay(&client->dev, 1000); + pm_runtime_use_autosuspend(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_sync_autosuspend(&client->dev); + + dev_set_drvdata(&client->dev, data); + + ret = snd_soc_register_codec(&client->dev, + &soc_codec_dev_tas2552, + tas2552_dai, ARRAY_SIZE(tas2552_dai)); + if (ret < 0) + dev_err(&client->dev, "Failed to register codec: %d\n", ret); + + return 0; +} + +static int tas2552_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id tas2552_id[] = { + { "tas2552", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tas2552_id); + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id tas2552_of_match[] = { + { .compatible = "ti,tas2552", }, + {}, +}; +MODULE_DEVICE_TABLE(of, tas2552_of_match); +#endif + +static struct i2c_driver tas2552_i2c_driver = { + .driver = { + .name = "tas2552", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(tas2552_of_match), + .pm = &tas2552_pm, + }, + .probe = tas2552_probe, + .remove = tas2552_i2c_remove, + .id_table = tas2552_id, +}; + +module_i2c_driver(tas2552_i2c_driver); + +MODULE_AUTHOR("Dan Muprhy "); +MODULE_DESCRIPTION("TAS2552 Audio amplifier driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tas2552.h b/sound/soc/codecs/tas2552.h new file mode 100644 index 000000000000..6cea8f31bf88 --- /dev/null +++ b/sound/soc/codecs/tas2552.h @@ -0,0 +1,129 @@ +/* + * tas2552.h - ALSA SoC Texas Instruments TAS2552 Mono Audio Amplifier + * + * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com + * + * Author: Dan Murphy + * + * 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. + */ + +#ifndef __TAS2552_H__ +#define __TAS2552_H__ + +/* Register Address Map */ +#define TAS2552_DEVICE_STATUS 0x00 +#define TAS2552_CFG_1 0x01 +#define TAS2552_CFG_2 0x02 +#define TAS2552_CFG_3 0x03 +#define TAS2552_DOUT 0x04 +#define TAS2552_SER_CTRL_1 0x05 +#define TAS2552_SER_CTRL_2 0x06 +#define TAS2552_OUTPUT_DATA 0x07 +#define TAS2552_PLL_CTRL_1 0x08 +#define TAS2552_PLL_CTRL_2 0x09 +#define TAS2552_PLL_CTRL_3 0x0a +#define TAS2552_BTIP 0x0b +#define TAS2552_BTS_CTRL 0x0c +#define TAS2552_RESERVED_0D 0x0d +#define TAS2552_LIMIT_RATE_HYS 0x0e +#define TAS2552_LIMIT_RELEASE 0x0f +#define TAS2552_LIMIT_INT_COUNT 0x10 +#define TAS2552_PDM_CFG 0x11 +#define TAS2552_PGA_GAIN 0x12 +#define TAS2552_EDGE_RATE_CTRL 0x13 +#define TAS2552_BOOST_PT_CTRL 0x14 +#define TAS2552_VER_NUM 0x16 +#define TAS2552_VBAT_DATA 0x19 +#define TAS2552_MAX_REG 0x20 + +/* CFG1 Register Masks */ +#define TAS2552_MUTE_MASK (1 << 2) +#define TAS2552_SWS_MASK (1 << 1) +#define TAS2552_WCLK_MASK 0x07 +#define TAS2552_CLASSD_EN_MASK (1 << 7) + +/* CFG2 Register Masks */ +#define TAS2552_CLASSD_EN (1 << 7) +#define TAS2552_BOOST_EN (1 << 6) +#define TAS2552_APT_EN (1 << 5) +#define TAS2552_PLL_ENABLE (1 << 3) +#define TAS2552_LIM_EN (1 << 2) +#define TAS2552_IVSENSE_EN (1 << 1) + +/* CFG3 Register Masks */ +#define TAS2552_WORD_CLK_MASK (1 << 7) +#define TAS2552_BIT_CLK_MASK (1 << 6) +#define TAS2552_DATA_FORMAT_MASK (0x11 << 2) + +#define TAS2552_DAIFMT_I2S_MASK 0xf3 +#define TAS2552_DAIFMT_DSP (1 << 3) +#define TAS2552_DAIFMT_RIGHT_J (1 << 4) +#define TAS2552_DAIFMT_LEFT_J (0x11 << 3) + +#define TAS2552_PLL_SRC_MCLK 0x00 +#define TAS2552_PLL_SRC_BCLK (1 << 3) +#define TAS2552_PLL_SRC_IVCLKIN (1 << 4) +#define TAS2552_PLL_SRC_1_8_FIXED (0x11 << 3) + +#define TAS2552_DIN_SRC_SEL_MUTED 0x00 +#define TAS2552_DIN_SRC_SEL_LEFT (1 << 4) +#define TAS2552_DIN_SRC_SEL_RIGHT (1 << 5) +#define TAS2552_DIN_SRC_SEL_AVG_L_R (0x11 << 4) + +#define TAS2552_PDM_IN_SEL (1 << 5) +#define TAS2552_I2S_OUT_SEL (1 << 6) +#define TAS2552_ANALOG_IN_SEL (1 << 7) + +/* CFG3 WCLK Dividers */ +#define TAS2552_8KHZ 0x00 +#define TAS2552_11_12KHZ (1 << 1) +#define TAS2552_16KHZ (1 << 2) +#define TAS2552_22_24KHZ (1 << 3) +#define TAS2552_32KHZ (1 << 4) +#define TAS2552_44_48KHZ (1 << 5) +#define TAS2552_88_96KHZ (1 << 6) +#define TAS2552_176_192KHZ (1 << 7) + +/* OUTPUT_DATA register */ +#define TAS2552_PDM_DATA_I 0x00 +#define TAS2552_PDM_DATA_V (1 << 6) +#define TAS2552_PDM_DATA_I_V (1 << 7) +#define TAS2552_PDM_DATA_V_I (0x11 << 6) + +/* PDM CFG Register */ +#define TAS2552_PDM_DATA_ES_RISE 0x4 + +#define TAS2552_PDM_PLL_CLK_SEL 0x00 +#define TAS2552_PDM_IV_CLK_SEL (1 << 1) +#define TAS2552_PDM_BCLK_SEL (1 << 2) +#define TAS2552_PDM_MCLK_SEL (1 << 3) + +/* Boost pass-through register */ +#define TAS2552_APT_DELAY_50 0x00 +#define TAS2552_APT_DELAY_75 (1 << 1) +#define TAS2552_APT_DELAY_125 (1 << 2) +#define TAS2552_APT_DELAY_200 (1 << 3) + +#define TAS2552_APT_THRESH_2_5 0x00 +#define TAS2552_APT_THRESH_1_7 (1 << 3) +#define TAS2552_APT_THRESH_1_4_1_1 (1 << 4) +#define TAS2552_APT_THRESH_2_1_7 (0x11 << 2) + +/* PLL Control Register */ +#define TAS2552_245MHZ_CLK 24576000 +#define TAS2552_225MHZ_CLK 22579200 +#define TAS2552_PLL_J_MASK 0x7f +#define TAS2552_PLL_D_UPPER_MASK 0x3f +#define TAS2552_PLL_D_LOWER_MASK 0xff +#define TAS2552_PLL_BYPASS_MASK 0x80 +#define TAS2552_PLL_BYPASS 0x80 + +#endif -- cgit v1.2.3 From 4bb7267dc41247810815e8b15f0e9fb1456c8d8c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 3 Jun 2014 18:53:25 -0300 Subject: [media] v4l: vb2: Add fatal error condition flag When a fatal error occurs that render the device unusable, the only options for a driver to signal the error condition to userspace is to set the V4L2_BUF_FLAG_ERROR flag when dequeuing buffers and to return an error from the buffer prepare handler when queuing buffers. The buffer error flag indicates a transient error and can't be used by applications to detect fatal errors. Returning an error from vb2_qbuf() is thus the only real indication that a fatal error occurred. However, this is difficult to handle for multithreaded applications that requeue buffers from a thread other than the control thread. In particular the poll() call in the control thread will not notify userspace of the error. This patch adds an explicit mechanism to report fatal errors to userspace. Drivers can call the vb2_queue_error() function to signal a fatal error. From this moment on, buffer preparation will return -EIO to userspace, and vb2_poll() will set the POLLERR flag and return immediately. The error flag is cleared when cancelling the queue, either at stream off time (through vb2_streamoff) or when releasing the queue with vb2_queue_release(). Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/videobuf2-core.c | 39 +++++++++++++++++++++++++++++--- include/media/videobuf2-core.h | 3 +++ 2 files changed, 39 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 1fc85fb964ce..a5c41cc65d1c 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1606,6 +1606,11 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) return -EINVAL; } + if (q->error) { + dprintk(1, "fatal error occurred on queue\n"); + return -EIO; + } + vb->state = VB2_BUF_STATE_PREPARING; vb->v4l2_buf.timestamp.tv_sec = 0; vb->v4l2_buf.timestamp.tv_usec = 0; @@ -1903,6 +1908,11 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) return -EINVAL; } + if (q->error) { + dprintk(1, "Queue in error state, will not wait for buffers\n"); + return -EIO; + } + if (!list_empty(&q->done_list)) { /* * Found a buffer that we were waiting for. @@ -1928,7 +1938,8 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) */ dprintk(3, "will sleep waiting for buffers\n"); ret = wait_event_interruptible(q->done_wq, - !list_empty(&q->done_list) || !q->streaming); + !list_empty(&q->done_list) || !q->streaming || + q->error); /* * We need to reevaluate both conditions again after reacquiring @@ -2125,6 +2136,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q) q->streaming = 0; q->start_streaming_called = 0; q->queued_count = 0; + q->error = 0; /* * Remove all buffers from videobuf's list... @@ -2201,6 +2213,27 @@ static int vb2_internal_streamon(struct vb2_queue *q, enum v4l2_buf_type type) return 0; } +/** + * vb2_queue_error() - signal a fatal error on the queue + * @q: videobuf2 queue + * + * Flag that a fatal unrecoverable error has occurred and wake up all processes + * waiting on the queue. Polling will now set POLLERR and queuing and dequeuing + * buffers will return -EIO. + * + * The error flag will be cleared when cancelling the queue, either from + * vb2_streamoff or vb2_queue_release. Drivers should thus not call this + * function before starting the stream, otherwise the error flag will remain set + * until the queue is released when closing the device node. + */ +void vb2_queue_error(struct vb2_queue *q) +{ + q->error = 1; + + wake_up_all(&q->done_wq); +} +EXPORT_SYMBOL_GPL(vb2_queue_error); + /** * vb2_streamon - start streaming * @q: videobuf2 queue @@ -2560,9 +2593,9 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) /* * There is nothing to wait for if no buffer has been queued and the - * queue isn't streaming. + * queue isn't streaming, or if the error flag is set. */ - if (list_empty(&q->queued_list) && !vb2_is_streaming(q)) + if ((list_empty(&q->queued_list) && !vb2_is_streaming(q)) || q->error) return res | POLLERR; if (list_empty(&q->done_list)) diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 8fab6fa0dbfb..1a262aeb1741 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -375,6 +375,7 @@ struct v4l2_fh; * @streaming: current streaming state * @start_streaming_called: start_streaming() was called successfully and we * started streaming. + * @error: a fatal error occurred on the queue * @fileio: file io emulator internal data, used only if emulator is active * @threadio: thread io internal data, used only if thread is active */ @@ -411,6 +412,7 @@ struct vb2_queue { unsigned int streaming:1; unsigned int start_streaming_called:1; + unsigned int error:1; struct vb2_fileio_data *fileio; struct vb2_threadio_data *threadio; @@ -444,6 +446,7 @@ int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b); int __must_check vb2_queue_init(struct vb2_queue *q); void vb2_queue_release(struct vb2_queue *q); +void vb2_queue_error(struct vb2_queue *q); int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b); int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb); -- cgit v1.2.3 From 0c7774abb41bd00d5836d9ba098825a40fa94133 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 17 Jul 2014 20:45:08 +0100 Subject: KEYS: Allow special keys (eg. DNS results) to be invalidated by CAP_SYS_ADMIN Special kernel keys, such as those used to hold DNS results for AFS, CIFS and NFS and those used to hold idmapper results for NFS, used to be 'invalidateable' with key_revoke(). However, since the default permissions for keys were reduced: Commit: 96b5c8fea6c0861621051290d705ec2e971963f1 KEYS: Reduce initial permissions on keys it has become impossible to do this. Add a key flag (KEY_FLAG_ROOT_CAN_INVAL) that will permit a key to be invalidated by root. This should not be used for system keyrings as the garbage collector will try and remove any invalidate key. For system keyrings, KEY_FLAG_ROOT_CAN_CLEAR can be used instead. After this, from userspace, keyctl_invalidate() and "keyctl invalidate" can be used by any possessor of CAP_SYS_ADMIN (typically root) to invalidate DNS and idmapper keys. Invalidated keys are immediately garbage collected and will be immediately rerequested if needed again. Signed-off-by: David Howells Tested-by: Steve Dickson --- fs/nfs/idmap.c | 2 ++ include/linux/key.h | 1 + net/dns_resolver/dns_query.c | 1 + security/keys/keyctl.c | 15 ++++++++++++++- 4 files changed, 18 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index 567983d2c0eb..b7458d77f511 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -282,6 +282,8 @@ static struct key *nfs_idmap_request_key(const char *name, size_t namelen, desc, "", 0, idmap); mutex_unlock(&idmap->idmap_mutex); } + if (!IS_ERR(rkey)) + set_bit(KEY_FLAG_ROOT_CAN_INVAL, &rkey->flags); kfree(desc); return rkey; diff --git a/include/linux/key.h b/include/linux/key.h index 017b0826642f..e37a4d807185 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -170,6 +170,7 @@ struct key { #define KEY_FLAG_INVALIDATED 7 /* set if key has been invalidated */ #define KEY_FLAG_TRUSTED 8 /* set if key is trusted */ #define KEY_FLAG_TRUSTED_ONLY 9 /* set if keyring only accepts links to trusted keys */ +#define KEY_FLAG_ROOT_CAN_INVAL 11 /* set if key can be invalidated by root without permission */ /* the key type and key description string * - the desc is used to match a key against search criteria diff --git a/net/dns_resolver/dns_query.c b/net/dns_resolver/dns_query.c index 9acec61f5433..9a32f55cf9b9 100644 --- a/net/dns_resolver/dns_query.c +++ b/net/dns_resolver/dns_query.c @@ -129,6 +129,7 @@ int dns_query(const char *type, const char *name, size_t namelen, } down_read(&rkey->sem); + set_bit(KEY_FLAG_ROOT_CAN_INVAL, &rkey->flags); rkey->perm |= KEY_USR_VIEW; ret = key_validate(rkey); diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index cd5bd0cef25d..609f8d326ddc 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -404,12 +404,25 @@ long keyctl_invalidate_key(key_serial_t id) key_ref = lookup_user_key(id, 0, KEY_NEED_SEARCH); if (IS_ERR(key_ref)) { ret = PTR_ERR(key_ref); + + /* Root is permitted to invalidate certain special keys */ + if (capable(CAP_SYS_ADMIN)) { + key_ref = lookup_user_key(id, 0, 0); + if (IS_ERR(key_ref)) + goto error; + if (test_bit(KEY_FLAG_ROOT_CAN_INVAL, + &key_ref_to_ptr(key_ref)->flags)) + goto invalidate; + goto error_put; + } + goto error; } +invalidate: key_invalidate(key_ref_to_ptr(key_ref)); ret = 0; - +error_put: key_ref_put(key_ref); error: kleave(" = %ld", ret); -- cgit v1.2.3 From 8ed5a4d2f7d8b296a22fed7ec2b543c0500d13be Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Mon, 2 Jun 2014 22:56:48 +0900 Subject: scsi: increase upper limit for max_sectors max_sectors in struct Scsi_Host specifies maximum number of sectors allowed in a single SCSI command. The data type of max_sectors is unsigned short, so the maximum transfer length per SCSI command is limited to less than 256MB in 4096-bytes sector size. (0xffff * 4096) This commit increases the SCSI mid level's limitation for max_sectors upto the block layer's limitation for max_hw_sectors by extending the data type of max_sectors in struct Scsi_Host and scsi_host_template, so that SCSI lower level drivers can specify more than 0xffff. Signed-off-by: Akinobu Mita Reviewed-by: Martin K. Petersen Signed-off-by: Christoph Hellwig --- include/scsi/scsi_host.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 94844fc77b97..db7d8bd2f86f 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -408,7 +408,7 @@ struct scsi_host_template { /* * Set this if the host adapter has limitations beside segment count. */ - unsigned short max_sectors; + unsigned int max_sectors; /* * DMA scatter gather segment boundary limit. A segment crossing this @@ -652,7 +652,7 @@ struct Scsi_Host { short cmd_per_lun; short unsigned int sg_tablesize; short unsigned int sg_prot_tablesize; - short unsigned int max_sectors; + unsigned int max_sectors; unsigned long dma_boundary; /* * Used to assign serial numbers to the cmds. -- cgit v1.2.3 From 65c26a0f39695ba01d9693754f27ca76cc8a3ab5 Mon Sep 17 00:00:00 2001 From: Douglas Gilbert Date: Tue, 3 Jun 2014 13:18:18 -0400 Subject: sg: relax 16 byte cdb restriction - remove the 16 byte CDB (SCSI command) length limit from the sg driver by handling longer CDBs the same way as the bsg driver. Remove comment from sg.h public interface about the cmd_len field being limited to 16 bytes. - remove some dead code caused by this change - cleanup comment block at the top of sg.h, fix urls Signed-off-by: Douglas Gilbert Reviewed-by: Mike Christie Reviewed-by: Hannes Reinecke Signed-off-by: Christoph Hellwig --- drivers/scsi/sg.c | 46 +++++++++++++++++----------- include/scsi/sg.h | 91 +++++++++++++++---------------------------------------- 2 files changed, 53 insertions(+), 84 deletions(-) (limited to 'include') diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 8f952af48322..37fb44b0074b 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -7,9 +7,7 @@ * Original driver (sg.c): * Copyright (C) 1992 Lawrence Foard * Version 2 and 3 extensions to driver: - * Copyright (C) 1998 - 2005 Douglas Gilbert - * - * Modified 19-JAN-1998 Richard Gooch Devfs support + * Copyright (C) 1998 - 2014 Douglas Gilbert * * 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 @@ -18,11 +16,11 @@ * */ -static int sg_version_num = 30534; /* 2 digits for each component */ -#define SG_VERSION_STR "3.5.34" +static int sg_version_num = 30536; /* 2 digits for each component */ +#define SG_VERSION_STR "3.5.36" /* - * D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au), notes: + * D. P. Gilbert (dgilbert@interlog.com), notes: * - scsi logging is available via SCSI_LOG_TIMEOUT macros. First * the kernel/module needs to be built with CONFIG_SCSI_LOGGING * (otherwise the macros compile to empty statements). @@ -64,7 +62,7 @@ static int sg_version_num = 30534; /* 2 digits for each component */ #ifdef CONFIG_SCSI_PROC_FS #include -static char *sg_version_date = "20061027"; +static char *sg_version_date = "20140603"; static int sg_proc_init(void); static void sg_proc_cleanup(void); @@ -74,6 +72,12 @@ static void sg_proc_cleanup(void); #define SG_MAX_DEVS 32768 +/* SG_MAX_CDB_SIZE should be 260 (spc4r37 section 3.1.30) however the type + * of sg_io_hdr::cmd_len can only represent 255. All SCSI commands greater + * than 16 bytes are "variable length" whose length is a multiple of 4 + */ +#define SG_MAX_CDB_SIZE 252 + /* * Suppose you want to calculate the formula muldiv(x,m,d)=int(x * m / d) * Then when using 32 bit integers x * m may overflow during the calculation. @@ -161,7 +165,7 @@ typedef struct sg_fd { /* holds the state of a file descriptor */ char low_dma; /* as in parent but possibly overridden to 1 */ char force_packid; /* 1 -> pack_id input to read(), 0 -> ignored */ char cmd_q; /* 1 -> allow command queuing, 0 -> don't */ - char next_cmd_len; /* 0 -> automatic (def), >0 -> use on next write() */ + unsigned char next_cmd_len; /* 0: automatic, >0: use on next write() */ char keep_orphan; /* 0 -> drop orphan (def), 1 -> keep for read() */ char mmap_called; /* 0 -> mmap() never called on this fd */ struct kref f_ref; @@ -566,7 +570,7 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) Sg_request *srp; struct sg_header old_hdr; sg_io_hdr_t *hp; - unsigned char cmnd[MAX_COMMAND_SIZE]; + unsigned char cmnd[SG_MAX_CDB_SIZE]; if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) return -ENXIO; @@ -598,12 +602,6 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) buf += SZ_SG_HEADER; __get_user(opcode, buf); if (sfp->next_cmd_len > 0) { - if (sfp->next_cmd_len > MAX_COMMAND_SIZE) { - SCSI_LOG_TIMEOUT(1, printk("sg_write: command length too long\n")); - sfp->next_cmd_len = 0; - sg_remove_request(sfp, srp); - return -EIO; - } cmd_size = sfp->next_cmd_len; sfp->next_cmd_len = 0; /* reset so only this write() effected */ } else { @@ -675,7 +673,7 @@ sg_new_write(Sg_fd *sfp, struct file *file, const char __user *buf, int k; Sg_request *srp; sg_io_hdr_t *hp; - unsigned char cmnd[MAX_COMMAND_SIZE]; + unsigned char cmnd[SG_MAX_CDB_SIZE]; int timeout; unsigned long ul_timeout; @@ -1654,15 +1652,27 @@ static int sg_start_req(Sg_request *srp, unsigned char *cmd) struct request_queue *q = sfp->parentdp->device->request_queue; struct rq_map_data *md, map_data; int rw = hp->dxfer_direction == SG_DXFER_TO_DEV ? WRITE : READ; + unsigned char *long_cmdp = NULL; SCSI_LOG_TIMEOUT(4, printk(KERN_INFO "sg_start_req: dxfer_len=%d\n", dxfer_len)); + if (hp->cmd_len > BLK_MAX_CDB) { + long_cmdp = kzalloc(hp->cmd_len, GFP_KERNEL); + if (!long_cmdp) + return -ENOMEM; + } + rq = blk_get_request(q, rw, GFP_ATOMIC); - if (!rq) + if (!rq) { + kfree(long_cmdp); return -ENOMEM; + } blk_rq_set_block_pc(rq); + + if (hp->cmd_len > BLK_MAX_CDB) + rq->cmd = long_cmdp; memcpy(rq->cmd, cmd, hp->cmd_len); rq->cmd_len = hp->cmd_len; @@ -1747,6 +1757,8 @@ static int sg_finish_rem_req(Sg_request * srp) if (srp->bio) ret = blk_rq_unmap_user(srp->bio); + if (srp->rq->cmd != srp->rq->__cmd) + kfree(srp->rq->cmd); blk_put_request(srp->rq); } diff --git a/include/scsi/sg.h b/include/scsi/sg.h index a9f3c6fc3f57..d8c0c4307fca 100644 --- a/include/scsi/sg.h +++ b/include/scsi/sg.h @@ -4,77 +4,34 @@ #include /* - History: - Started: Aug 9 by Lawrence Foard (entropy@world.std.com), to allow user - process control of SCSI devices. - Development Sponsored by Killy Corp. NY NY -Original driver (sg.h): -* Copyright (C) 1992 Lawrence Foard -Version 2 and 3 extensions to driver: -* Copyright (C) 1998 - 2006 Douglas Gilbert - - Version: 3.5.34 (20060920) - This version is for 2.6 series kernels. - - For a full changelog see http://www.torque.net/sg - -Map of SG verions to the Linux kernels in which they appear: - ---------- ---------------------------------- - original all kernels < 2.2.6 - 2.1.40 2.2.20 - 3.0.x optional version 3 sg driver for 2.2 series - 3.1.17++ 2.4.0++ - 3.5.30++ 2.6.0++ - -Major new features in SG 3.x driver (cf SG 2.x drivers) - - SG_IO ioctl() combines function if write() and read() - - new interface (sg_io_hdr_t) but still supports old interface - - scatter/gather in user space, direct IO, and mmap supported - - The normal action of this driver is to use the adapter (HBA) driver to DMA - data into kernel buffers and then use the CPU to copy the data into the - user space (vice versa for writes). That is called "indirect" IO due to - the double handling of data. There are two methods offered to remove the - redundant copy: 1) direct IO and 2) using the mmap() system call to map - the reserve buffer (this driver has one reserve buffer per fd) into the - user space. Both have their advantages. - In terms of absolute speed mmap() is faster. If speed is not a concern, - indirect IO should be fine. Read the documentation for more information. - - ** N.B. To use direct IO 'echo 1 > /proc/scsi/sg/allow_dio' or - 'echo 1 > /sys/module/sg/parameters/allow_dio' is needed. - That attribute is 0 by default. ** - - Historical note: this SCSI pass-through driver has been known as "sg" for - a decade. In broader kernel discussions "sg" is used to refer to scatter - gather techniques. The context should clarify which "sg" is referred to. - - Documentation - ============= - A web site for the SG device driver can be found at: - http://www.torque.net/sg [alternatively check the MAINTAINERS file] - The documentation for the sg version 3 driver can be found at: - http://www.torque.net/sg/p/sg_v3_ho.html - This is a rendering from DocBook source [change the extension to "sgml" - or "xml"]. There are renderings in "ps", "pdf", "rtf" and "txt" (soon). - The SG_IO ioctl is now found in other parts kernel (e.g. the block layer). - For more information see http://www.torque.net/sg/sg_io.html - - The older, version 2 documents discuss the original sg interface in detail: - http://www.torque.net/sg/p/scsi-generic.txt - http://www.torque.net/sg/p/scsi-generic_long.txt - Also available: /Documentation/scsi/scsi-generic.txt - - Utility and test programs are available at the sg web site. They are - packaged as sg3_utils (for the lk 2.4 and 2.6 series) and sg_utils - (for the lk 2.2 series). -*/ + * History: + * Started: Aug 9 by Lawrence Foard (entropy@world.std.com), to allow user + * process control of SCSI devices. + * Development Sponsored by Killy Corp. NY NY + * + * Original driver (sg.h): + * Copyright (C) 1992 Lawrence Foard + * Version 2 and 3 extensions to driver: + * Copyright (C) 1998 - 2014 Douglas Gilbert + * + * Version: 3.5.36 (20140603) + * This version is for 2.6 and 3 series kernels. + * + * Documentation + * ============= + * A web site for the SG device driver can be found at: + * http://sg.danny.cz/sg [alternatively check the MAINTAINERS file] + * The documentation for the sg version 3 driver can be found at: + * http://sg.danny.cz/sg/p/sg_v3_ho.html + * Also see: /Documentation/scsi/scsi-generic.txt + * + * For utility and test programs see: http://sg.danny.cz/sg/sg3_utils.html + */ #ifdef __KERNEL__ extern int sg_big_buff; /* for sysctl */ #endif -/* New interface introduced in the 3.x SG drivers follows */ typedef struct sg_iovec /* same structure as used by readv() Linux system */ { /* call. It defines one scatter-gather element. */ @@ -87,7 +44,7 @@ typedef struct sg_io_hdr { int interface_id; /* [i] 'S' for SCSI generic (required) */ int dxfer_direction; /* [i] data transfer direction */ - unsigned char cmd_len; /* [i] SCSI command length ( <= 16 bytes) */ + unsigned char cmd_len; /* [i] SCSI command length */ unsigned char mx_sb_len; /* [i] max length to write to sbp */ unsigned short iovec_count; /* [i] 0 implies no scatter gather */ unsigned int dxfer_len; /* [i] byte count of data transfer */ -- cgit v1.2.3 From 16070cc189c5e343696c29c8cff779e692cfcb8d Mon Sep 17 00:00:00 2001 From: Douglas Gilbert Date: Wed, 4 Jun 2014 10:58:30 -0400 Subject: sg: add SG_FLAG_Q_AT_TAIL flag When the SG_IO ioctl was copied into the block layer and later into the bsg driver, subtle differences emerged. One difference is the way injected commands are queued through the block layer (i.e. this is not SCSI device queueing nor SATA NCQ). Summarizing: - SG_IO in the block layer: blk_exec*(at_head=false) - sg SG_IO: at_head=true - bsg SG_IO: at_head=true Some time ago Boaz Harrosh introduced a sg v4 flag called BSG_FLAG_Q_AT_TAIL to override the bsg driver default. This patch does the equivalent for the sg driver. ChangeLog: Introduce SG_FLAG_Q_AT_TAIL flag to cause commands to be injected into the block layer with at_head=false. Signed-off-by: Douglas Gilbert Reviewed-by: Mike Christie Reviewed-by: Ewan D. Milne Signed-off-by: Christoph Hellwig --- drivers/scsi/sg.c | 9 +++++++-- include/scsi/sg.h | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 37fb44b0074b..32425ac61096 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -741,7 +741,7 @@ static int sg_common_write(Sg_fd * sfp, Sg_request * srp, unsigned char *cmnd, int timeout, int blocking) { - int k, data_dir; + int k, data_dir, at_head; Sg_device *sdp = sfp->parentdp; sg_io_hdr_t *hp = &srp->header; @@ -785,11 +785,16 @@ sg_common_write(Sg_fd * sfp, Sg_request * srp, break; } hp->duration = jiffies_to_msecs(jiffies); + if (hp->interface_id != '\0' && /* v3 (or later) interface */ + (SG_FLAG_Q_AT_TAIL & hp->flags)) + at_head = 0; + else + at_head = 1; srp->rq->timeout = timeout; kref_get(&sfp->f_ref); /* sg_rq_end_io() does kref_put(). */ blk_execute_rq_nowait(sdp->device->request_queue, sdp->disk, - srp->rq, 1, sg_rq_end_io); + srp->rq, at_head, sg_rq_end_io); return 0; } diff --git a/include/scsi/sg.h b/include/scsi/sg.h index d8c0c4307fca..9859355a7cf9 100644 --- a/include/scsi/sg.h +++ b/include/scsi/sg.h @@ -86,6 +86,7 @@ typedef struct sg_io_hdr #define SG_FLAG_MMAP_IO 4 /* request memory mapped IO */ #define SG_FLAG_NO_DXFER 0x10000 /* no transfer of kernel buffers to/from */ /* user space (debug indirect IO) */ +#define SG_FLAG_Q_AT_TAIL 0x10 /* default is Q_AT_HEAD */ /* following 'info' values are "or"-ed together */ #define SG_INFO_OK_MASK 0x1 -- cgit v1.2.3 From 22ffeb48b7584d6cd50f2a595ed6065d86a87459 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Tue, 3 Jun 2014 10:58:53 +0200 Subject: scsi_scan: Restrict sequential scan to 256 LUNs Sequential scan for more than 256 LUNs is very fragile as LUNs might not be numbered sequentially after that point. SAM revisions later than SCSI-3 impose a structure on LUNs larger than 256, making LUN numbers between 256 and 16384 illegal. SCSI-3, however allows for plain 64-bit numbers with no internal structure. So restrict sequential LUN scan to 256 LUNs and add a new blacklist flag 'BLIST_SCSI3LUN' to scan up to max_lun devices. Signed-off-by: Hannes Reinecke Reviewed-by: Ewan Milne Signed-off-by: Christoph Hellwig --- drivers/scsi/scsi_scan.c | 6 ++++++ include/scsi/scsi_devinfo.h | 2 ++ 2 files changed, 8 insertions(+) (limited to 'include') diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 8564bdcd2287..a02f7b0976ed 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -1234,6 +1234,12 @@ static void scsi_sequential_lun_scan(struct scsi_target *starget, if (scsi_level < SCSI_3 && !(bflags & BLIST_LARGELUN)) max_dev_lun = min(8U, max_dev_lun); + /* + * Stop scanning at 255 unless BLIST_SCSI3LUN + */ + if (!(bflags & BLIST_SCSI3LUN)) + max_dev_lun = min(256U, max_dev_lun); + /* * We have already scanned LUN 0, so start at LUN 1. Keep scanning * until we reach the max, or no LUN is found and we are not diff --git a/include/scsi/scsi_devinfo.h b/include/scsi/scsi_devinfo.h index 447d2d7466fc..8670c04e199e 100644 --- a/include/scsi/scsi_devinfo.h +++ b/include/scsi/scsi_devinfo.h @@ -32,4 +32,6 @@ #define BLIST_ATTACH_PQ3 0x1000000 /* Scan: Attach to PQ3 devices */ #define BLIST_NO_DIF 0x2000000 /* Disable T10 PI (DIF) */ #define BLIST_SKIP_VPD_PAGES 0x4000000 /* Ignore SBC-3 VPD pages */ +#define BLIST_SCSI3LUN 0x8000000 /* Scan more than 256 LUNs + for sequential scan */ #endif -- cgit v1.2.3 From 9cb78c16f5dadefd8dc5ba0ae5a2f26cd59419b3 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Wed, 25 Jun 2014 15:27:36 +0200 Subject: scsi: use 64-bit LUNs The SCSI standard defines 64-bit values for LUNs, and large arrays employing large or hierarchical LUN numbers become more and more common. So update the linux SCSI stack to use 64-bit LUN numbers. Signed-off-by: Hannes Reinecke Reviewed-by: Christoph Hellwig Reviewed-by: Ewan Milne Signed-off-by: Christoph Hellwig --- drivers/ata/libata-scsi.c | 2 +- drivers/ata/libata.h | 2 +- drivers/message/fusion/mptbase.h | 2 +- drivers/message/fusion/mptfc.c | 6 +-- drivers/message/fusion/mptscsih.c | 14 +++---- drivers/message/fusion/mptscsih.h | 4 +- drivers/message/i2o/i2o_scsi.c | 11 +++-- drivers/s390/scsi/zfcp_dbf.c | 3 +- drivers/s390/scsi/zfcp_unit.c | 4 +- drivers/scsi/53c700.c | 7 ++-- drivers/scsi/NCR5380.c | 31 ++++++++------- drivers/scsi/NCR53c406a.c | 2 +- drivers/scsi/a100u2w.c | 2 +- drivers/scsi/aacraid/linit.c | 2 +- drivers/scsi/aha152x.c | 6 +-- drivers/scsi/aic7xxx/aic79xx.h | 2 +- drivers/scsi/aic7xxx/aic79xx_osm.c | 6 +-- drivers/scsi/aic7xxx/aic79xx_proc.c | 2 +- drivers/scsi/aic7xxx/aic7xxx_osm.c | 11 ++--- drivers/scsi/aic7xxx/aic7xxx_proc.c | 2 +- drivers/scsi/arcmsr/arcmsr_hba.c | 8 ++-- drivers/scsi/arm/acornscsi.c | 14 ++++--- drivers/scsi/arm/fas216.c | 11 +++-- drivers/scsi/arm/queue.c | 3 +- drivers/scsi/atari_NCR5380.c | 62 +++++++++++++++-------------- drivers/scsi/bnx2fc/bnx2fc_io.c | 4 +- drivers/scsi/ch.c | 16 ++++---- drivers/scsi/csiostor/csio_scsi.c | 24 +++++------ drivers/scsi/dc395x.c | 40 +++++++++---------- drivers/scsi/dpt_i2o.c | 38 +++++++++--------- drivers/scsi/dpti.h | 6 +-- drivers/scsi/eata.c | 2 +- drivers/scsi/fnic/fnic_scsi.c | 4 +- drivers/scsi/g_NCR5380.c | 2 +- drivers/scsi/hpsa.c | 2 +- drivers/scsi/hptiop.c | 2 +- drivers/scsi/in2000.c | 6 +-- drivers/scsi/libiscsi.c | 8 ++-- drivers/scsi/libsas/sas_scsi_host.c | 11 ++--- drivers/scsi/lpfc/lpfc_scsi.c | 36 ++++++++--------- drivers/scsi/megaraid.c | 4 +- drivers/scsi/megaraid/mega_common.h | 2 +- drivers/scsi/megaraid/megaraid_sas_fusion.c | 4 +- drivers/scsi/mesh.c | 2 +- drivers/scsi/ncr53c8xx.c | 2 +- drivers/scsi/nsp32.c | 2 +- drivers/scsi/pcmcia/nsp_cs.c | 2 +- drivers/scsi/pcmcia/sym53c500_cs.c | 2 +- drivers/scsi/pmcraid.c | 2 +- drivers/scsi/ps3rom.c | 2 +- drivers/scsi/qla2xxx/qla_def.h | 6 +-- drivers/scsi/qla2xxx/qla_gbl.h | 16 ++++---- drivers/scsi/qla2xxx/qla_iocb.c | 11 +++-- drivers/scsi/qla2xxx/qla_isr.c | 4 +- drivers/scsi/qla2xxx/qla_mbx.c | 12 +++--- drivers/scsi/qla2xxx/qla_mr.c | 8 ++-- drivers/scsi/qla2xxx/qla_os.c | 35 ++++++++-------- drivers/scsi/qla4xxx/ql4_glbl.h | 4 +- drivers/scsi/qla4xxx/ql4_iocb.c | 2 +- drivers/scsi/qla4xxx/ql4_isr.c | 22 +++++----- drivers/scsi/qla4xxx/ql4_mbx.c | 6 +-- drivers/scsi/qla4xxx/ql4_os.c | 20 +++++----- drivers/scsi/scsi.c | 8 ++-- drivers/scsi/scsi_debug.c | 15 +++---- drivers/scsi/scsi_priv.h | 2 +- drivers/scsi/scsi_proc.c | 2 +- drivers/scsi/scsi_scan.c | 54 +++++++++---------------- drivers/scsi/scsi_sysfs.c | 14 +++---- drivers/scsi/scsi_transport_fc.c | 4 +- drivers/scsi/scsi_transport_iscsi.c | 4 +- drivers/scsi/scsi_transport_sas.c | 2 +- drivers/scsi/sg.c | 4 +- drivers/scsi/sun3_NCR5380.c | 57 +++++++++++++------------- drivers/scsi/sym53c8xx_2/sym_glue.c | 2 +- drivers/scsi/sym53c8xx_2/sym_hipd.h | 2 +- drivers/scsi/tmscsim.c | 6 +-- drivers/scsi/u14-34f.c | 10 ++--- drivers/scsi/wd33c93.c | 33 +++++++-------- drivers/staging/rts5208/rtsx.c | 4 +- drivers/target/loopback/tcm_loop.c | 2 +- drivers/target/target_core_pscsi.c | 12 +++--- drivers/usb/storage/sddr09.c | 4 +- drivers/usb/storage/usb.c | 10 +++-- include/scsi/scsi.h | 2 +- include/scsi/scsi_device.h | 22 +++++----- include/scsi/scsi_transport.h | 2 +- 86 files changed, 438 insertions(+), 429 deletions(-) (limited to 'include') diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 72691fd93948..0586f66d70fa 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3945,7 +3945,7 @@ void ata_scsi_hotplug(struct work_struct *work) * Zero. */ int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel, - unsigned int id, unsigned int lun) + unsigned int id, u64 lun) { struct ata_port *ap = ata_shost_to_port(shost); unsigned long flags; diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 45b5ab3a95d5..5f4e0cca56ec 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -144,7 +144,7 @@ extern void ata_schedule_scsi_eh(struct Scsi_Host *shost); extern void ata_scsi_dev_rescan(struct work_struct *work); extern int ata_bus_probe(struct ata_port *ap); extern int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel, - unsigned int id, unsigned int lun); + unsigned int id, u64 lun); /* libata-eh.c */ diff --git a/drivers/message/fusion/mptbase.h b/drivers/message/fusion/mptbase.h index 76c05bc24cb7..f37ea6fdc6ae 100644 --- a/drivers/message/fusion/mptbase.h +++ b/drivers/message/fusion/mptbase.h @@ -405,7 +405,7 @@ typedef struct _VirtTarget { typedef struct _VirtDevice { VirtTarget *vtarget; u8 configured_lun; - int lun; + u64 lun; } VirtDevice; /* diff --git a/drivers/message/fusion/mptfc.c b/drivers/message/fusion/mptfc.c index 02a3eefd6931..bf2a2cef562b 100644 --- a/drivers/message/fusion/mptfc.c +++ b/drivers/message/fusion/mptfc.c @@ -204,7 +204,7 @@ mptfc_block_error_handler(struct scsi_cmnd *SCpnt, || (loops > 0 && ioc->active == 0)) { spin_unlock_irqrestore(shost->host_lock, flags); dfcprintk (ioc, printk(MYIOC_s_DEBUG_FMT - "mptfc_block_error_handler.%d: %d:%d, port status is " + "mptfc_block_error_handler.%d: %d:%llu, port status is " "%x, active flag %d, deferring %s recovery.\n", ioc->name, ioc->sh->host_no, SCpnt->device->id, SCpnt->device->lun, @@ -218,7 +218,7 @@ mptfc_block_error_handler(struct scsi_cmnd *SCpnt, if (ready == DID_NO_CONNECT || !SCpnt->device->hostdata || ioc->active == 0) { dfcprintk (ioc, printk(MYIOC_s_DEBUG_FMT - "%s.%d: %d:%d, failing recovery, " + "%s.%d: %d:%llu, failing recovery, " "port state %x, active %d, vdevice %p.\n", caller, ioc->name, ioc->sh->host_no, SCpnt->device->id, SCpnt->device->lun, ready, @@ -226,7 +226,7 @@ mptfc_block_error_handler(struct scsi_cmnd *SCpnt, return FAILED; } dfcprintk (ioc, printk(MYIOC_s_DEBUG_FMT - "%s.%d: %d:%d, executing recovery.\n", caller, + "%s.%d: %d:%llu, executing recovery.\n", caller, ioc->name, ioc->sh->host_no, SCpnt->device->id, SCpnt->device->lun)); return (*func)(SCpnt); diff --git a/drivers/message/fusion/mptscsih.c b/drivers/message/fusion/mptscsih.c index 2a1c6f21af27..39e566803089 100644 --- a/drivers/message/fusion/mptscsih.c +++ b/drivers/message/fusion/mptscsih.c @@ -95,7 +95,7 @@ static void mptscsih_freeChainBuffers(MPT_ADAPTER *ioc, int req_idx); static void mptscsih_copy_sense_data(struct scsi_cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR *mf, SCSIIOReply_t *pScsiReply); int mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, - int lun, int ctx2abort, ulong timeout); + u64 lun, int ctx2abort, ulong timeout); int mptscsih_ioc_reset(MPT_ADAPTER *ioc, int post_reset); int mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply); @@ -536,7 +536,7 @@ mptscsih_info_scsiio(MPT_ADAPTER *ioc, struct scsi_cmnd *sc, SCSIIOReply_t * pSc } scsi_print_command(sc); - printk(MYIOC_s_DEBUG_FMT "\tfw_channel = %d, fw_id = %d, lun = %d\n", + printk(MYIOC_s_DEBUG_FMT "\tfw_channel = %d, fw_id = %d, lun = %llu\n", ioc->name, pScsiReply->Bus, pScsiReply->TargetID, sc->device->lun); printk(MYIOC_s_DEBUG_FMT "\trequest_len = %d, underflow = %d, " "resid = %d\n", ioc->name, scsi_bufflen(sc), sc->underflow, @@ -692,7 +692,7 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) */ if (scsi_state & MPI_SCSI_STATE_RESPONSE_INFO_VALID && pScsiReply->ResponseInfo) { - printk(MYIOC_s_NOTE_FMT "[%d:%d:%d:%d] " + printk(MYIOC_s_NOTE_FMT "[%d:%d:%d:%llu] " "FCP_ResponseInfo=%08xh\n", ioc->name, sc->device->host->host_no, sc->device->channel, sc->device->id, sc->device->lun, @@ -1155,7 +1155,7 @@ mptscsih_report_queue_full(struct scsi_cmnd *sc, SCSIIOReply_t *pScsiReply, SCSI return; ioc = hd->ioc; if (time - hd->last_queue_full > 10 * HZ) { - dprintk(ioc, printk(MYIOC_s_WARN_FMT "Device (%d:%d:%d) reported QUEUE_FULL!\n", + dprintk(ioc, printk(MYIOC_s_WARN_FMT "Device (%d:%d:%llu) reported QUEUE_FULL!\n", ioc->name, 0, sc->device->id, sc->device->lun)); hd->last_queue_full = time; } @@ -1518,7 +1518,7 @@ mptscsih_freeChainBuffers(MPT_ADAPTER *ioc, int req_idx) * **/ int -mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, int lun, +mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, u64 lun, int ctx2abort, ulong timeout) { MPT_FRAME_HDR *mf; @@ -2380,7 +2380,7 @@ mptscsih_slave_configure(struct scsi_device *sdev) vdevice = sdev->hostdata; dsprintk(ioc, printk(MYIOC_s_DEBUG_FMT - "device @ %p, channel=%d, id=%d, lun=%d\n", + "device @ %p, channel=%d, id=%d, lun=%llu\n", ioc->name, sdev, sdev->channel, sdev->id, sdev->lun)); if (ioc->bus_type == SPI) dsprintk(ioc, printk(MYIOC_s_DEBUG_FMT @@ -2971,7 +2971,7 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io) + (my_idx * MPT_SENSE_BUFFER_ALLOC)); devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT - "%s: Sending Command 0x%02x for fw_channel=%d fw_id=%d lun=%d\n", + "%s: Sending Command 0x%02x for fw_channel=%d fw_id=%d lun=%llu\n", ioc->name, __func__, cmd, io->channel, io->id, io->lun)); if (dir == MPI_SCSIIO_CONTROL_READ) diff --git a/drivers/message/fusion/mptscsih.h b/drivers/message/fusion/mptscsih.h index 99e3390807f3..e1b1a198a62a 100644 --- a/drivers/message/fusion/mptscsih.h +++ b/drivers/message/fusion/mptscsih.h @@ -98,7 +98,7 @@ typedef struct _internal_cmd { u8 cmd; /* SCSI Op Code */ u8 channel; /* bus number */ u8 id; /* SCSI ID (virtual) */ - int lun; + u64 lun; u8 flags; /* Bit Field - See above */ u8 physDiskNum; /* Phys disk number, -1 else */ u8 rsvd2; @@ -115,7 +115,7 @@ extern int mptscsih_show_info(struct seq_file *, struct Scsi_Host *); extern const char * mptscsih_info(struct Scsi_Host *SChost); extern int mptscsih_qcmd(struct scsi_cmnd *SCpnt); extern int mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, - u8 id, int lun, int ctx2abort, ulong timeout); + u8 id, u64 lun, int ctx2abort, ulong timeout); extern void mptscsih_slave_destroy(struct scsi_device *device); extern int mptscsih_slave_configure(struct scsi_device *device); extern int mptscsih_abort(struct scsi_cmnd * SCpnt); diff --git a/drivers/message/i2o/i2o_scsi.c b/drivers/message/i2o/i2o_scsi.c index 1d31d7284cbd..e7de92c67cf6 100644 --- a/drivers/message/i2o/i2o_scsi.c +++ b/drivers/message/i2o/i2o_scsi.c @@ -78,7 +78,7 @@ static unsigned int i2o_scsi_max_lun = 255; struct i2o_scsi_host { struct Scsi_Host *scsi_host; /* pointer to the SCSI host */ struct i2o_controller *iop; /* pointer to the I2O controller */ - unsigned int lun; /* lun's used for block devices */ + u64 lun; /* lun's used for block devices */ struct i2o_device *channel[0]; /* channel->i2o_dev mapping table */ }; @@ -287,9 +287,8 @@ static int i2o_scsi_probe(struct device *dev) } if (le64_to_cpu(lun) >= scsi_host->max_lun) { - osm_warn("SCSI device lun (%lu) >= max_lun of I2O host (%d)", - (long unsigned int)le64_to_cpu(lun), - scsi_host->max_lun); + osm_warn("SCSI device lun (%llu) >= max_lun of I2O host (%d)", + le64_to_cpu(lun), scsi_host->max_lun); return -EFAULT; } @@ -308,9 +307,9 @@ static int i2o_scsi_probe(struct device *dev) if (rc) goto err; - osm_info("device added (TID: %03x) channel: %d, id: %d, lun: %ld\n", + osm_info("device added (TID: %03x) channel: %d, id: %d, lun: %llu\n", i2o_dev->lct_data.tid, channel, le32_to_cpu(id), - (long unsigned int)le64_to_cpu(lun)); + le64_to_cpu(lun)); return 0; diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c index 0ca64484cfa3..5d7fbe4e907e 100644 --- a/drivers/s390/scsi/zfcp_dbf.c +++ b/drivers/s390/scsi/zfcp_dbf.c @@ -418,7 +418,8 @@ void zfcp_dbf_scsi(char *tag, struct scsi_cmnd *sc, struct zfcp_fsf_req *fsf) rec->scsi_retries = sc->retries; rec->scsi_allowed = sc->allowed; rec->scsi_id = sc->device->id; - rec->scsi_lun = sc->device->lun; + /* struct zfcp_dbf_scsi needs to be updated to handle 64bit LUNs */ + rec->scsi_lun = (u32)sc->device->lun; rec->host_scribble = (unsigned long)sc->host_scribble; memcpy(rec->scsi_opcode, sc->cmnd, diff --git a/drivers/s390/scsi/zfcp_unit.c b/drivers/s390/scsi/zfcp_unit.c index 39f5446f7216..157d3d203ba1 100644 --- a/drivers/s390/scsi/zfcp_unit.c +++ b/drivers/s390/scsi/zfcp_unit.c @@ -21,7 +21,7 @@ void zfcp_unit_scsi_scan(struct zfcp_unit *unit) { struct fc_rport *rport = unit->port->rport; - unsigned int lun; + u64 lun; lun = scsilun_to_int((struct scsi_lun *) &unit->fcp_lun); @@ -188,7 +188,7 @@ struct scsi_device *zfcp_unit_sdev(struct zfcp_unit *unit) { struct Scsi_Host *shost; struct zfcp_port *port; - unsigned int lun; + u64 lun; lun = scsilun_to_int((struct scsi_lun *) &unit->fcp_lun); port = unit->port; diff --git a/drivers/scsi/53c700.c b/drivers/scsi/53c700.c index a3adfb4357f5..fabd4be2c985 100644 --- a/drivers/scsi/53c700.c +++ b/drivers/scsi/53c700.c @@ -1005,7 +1005,7 @@ process_script_interrupt(__u32 dsps, __u32 dsp, struct scsi_cmnd *SCp, DMA_TO_DEVICE); cmnd[0] = REQUEST_SENSE; - cmnd[1] = (SCp->device->lun & 0x7) << 5; + cmnd[1] = (lun & 0x7) << 5; cmnd[2] = 0; cmnd[3] = 0; cmnd[4] = SCSI_SENSE_BUFFERSIZE; @@ -1396,7 +1396,8 @@ NCR_700_start_command(struct scsi_cmnd *SCp) struct NCR_700_Host_Parameters *hostdata = (struct NCR_700_Host_Parameters *)SCp->device->host->hostdata[0]; __u16 count = 1; /* for IDENTIFY message */ - + u8 lun = SCp->device->lun; + if(hostdata->state != NCR_700_HOST_FREE) { /* keep this inside the lock to close the race window where * the running command finishes on another CPU while we don't @@ -1415,7 +1416,7 @@ NCR_700_start_command(struct scsi_cmnd *SCp) hostdata->msgout[0] = NCR_700_identify((SCp->cmnd[0] != REQUEST_SENSE && slot->flags != NCR_700_FLAG_AUTOSENSE), - SCp->device->lun); + lun); /* for INQUIRY or REQUEST_SENSE commands, we cannot be sure * if the negotiated transfer parameters still hold, so * always renegotiate them */ diff --git a/drivers/scsi/NCR5380.c b/drivers/scsi/NCR5380.c index 93d13fc9a293..45da3c823322 100644 --- a/drivers/scsi/NCR5380.c +++ b/drivers/scsi/NCR5380.c @@ -762,7 +762,7 @@ static int __maybe_unused NCR5380_show_info(struct seq_file *m, static void lprint_Scsi_Cmnd(Scsi_Cmnd * cmd, struct seq_file *m) { - SPRINTF("scsi%d : destination target %d, lun %d\n", cmd->device->host->host_no, cmd->device->id, cmd->device->lun); + SPRINTF("scsi%d : destination target %d, lun %llu\n", cmd->device->host->host_no, cmd->device->id, cmd->device->lun); SPRINTF(" command = "); lprint_command(cmd->cmnd, m); } @@ -1039,9 +1039,10 @@ static void NCR5380_main(struct work_struct *work) for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, prev = NULL; tmp; prev = tmp, tmp = (Scsi_Cmnd *) tmp->host_scribble) { if (prev != tmp) - dprintk(NDEBUG_LISTS, "MAIN tmp=%p target=%d busy=%d lun=%d\n", tmp, tmp->device->id, hostdata->busy[tmp->device->id], tmp->device->lun); + dprintk(NDEBUG_LISTS, "MAIN tmp=%p target=%d busy=%d lun=%llu\n", tmp, tmp->device->id, hostdata->busy[tmp->device->id], tmp->device->lun); /* When we find one, remove it from the issue queue. */ - if (!(hostdata->busy[tmp->device->id] & (1 << tmp->device->lun))) { + if (!(hostdata->busy[tmp->device->id] & + (1 << (u8)(tmp->device->lun & 0xff)))) { if (prev) { REMOVE(prev, prev->host_scribble, tmp, tmp->host_scribble); prev->host_scribble = tmp->host_scribble; @@ -1057,7 +1058,7 @@ static void NCR5380_main(struct work_struct *work) * On failure, we must add the command back to the * issue queue so we can keep trying. */ - dprintk(NDEBUG_MAIN|NDEBUG_QUEUES, "scsi%d : main() : command for target %d lun %d removed from issue_queue\n", instance->host_no, tmp->device->id, tmp->device->lun); + dprintk(NDEBUG_MAIN|NDEBUG_QUEUES, "scsi%d : main() : command for target %d lun %llu removed from issue_queue\n", instance->host_no, tmp->device->id, tmp->device->lun); /* * A successful selection is defined as one that @@ -1524,7 +1525,7 @@ part2: dprintk(NDEBUG_SELECTION, "scsi%d : nexus established.\n", instance->host_no); /* XXX need to handle errors here */ hostdata->connected = cmd; - hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun); + hostdata->busy[cmd->device->id] |= (1 << (cmd->device->lun & 0xFF)); initialize_SCp(cmd); @@ -2210,14 +2211,14 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) { case LINKED_FLG_CMD_COMPLETE: /* Accept message by clearing ACK */ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - dprintk(NDEBUG_LINKED, "scsi%d : target %d lun %d linked command complete.\n", instance->host_no, cmd->device->id, cmd->device->lun); + dprintk(NDEBUG_LINKED, "scsi%d : target %d lun %llu linked command complete.\n", instance->host_no, cmd->device->id, cmd->device->lun); /* * Sanity check : A linked command should only terminate with * one of these messages if there are more linked commands * available. */ if (!cmd->next_link) { - printk("scsi%d : target %d lun %d linked command complete, no next_link\n" instance->host_no, cmd->device->id, cmd->device->lun); + printk("scsi%d : target %d lun %llu linked command complete, no next_link\n" instance->host_no, cmd->device->id, cmd->device->lun); sink = 1; do_abort(instance); return; @@ -2226,7 +2227,7 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) { /* The next command is still part of this process */ cmd->next_link->tag = cmd->tag; cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); - dprintk(NDEBUG_LINKED, "scsi%d : target %d lun %d linked request done, calling scsi_done().\n", instance->host_no, cmd->device->id, cmd->device->lun); + dprintk(NDEBUG_LINKED, "scsi%d : target %d lun %llu linked request done, calling scsi_done().\n", instance->host_no, cmd->device->id, cmd->device->lun); collect_stats(hostdata, cmd); cmd->scsi_done(cmd); cmd = hostdata->connected; @@ -2238,8 +2239,8 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) { sink = 1; NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); hostdata->connected = NULL; - dprintk(NDEBUG_QUEUES, "scsi%d : command for target %d, lun %d completed\n", instance->host_no, cmd->device->id, cmd->device->lun); - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); + dprintk(NDEBUG_QUEUES, "scsi%d : command for target %d, lun %llu completed\n", instance->host_no, cmd->device->id, cmd->device->lun); + hostdata->busy[cmd->device->id] &= ~(1 << (cmd->device->lun & 0xFF)); /* * I'm not sure what the correct thing to do here is : @@ -2304,7 +2305,7 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) { case ORDERED_QUEUE_TAG: case SIMPLE_QUEUE_TAG: cmd->device->simple_tags = 0; - hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun); + hostdata->busy[cmd->device->id] |= (1 << (cmd->device->lun & 0xFF)); break; default: break; @@ -2318,7 +2319,7 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) { hostdata->disconnected_queue; hostdata->connected = NULL; hostdata->disconnected_queue = cmd; - dprintk(NDEBUG_QUEUES, "scsi%d : command for target %d lun %d was moved from connected to" " the disconnected_queue\n", instance->host_no, cmd->device->id, cmd->device->lun); + dprintk(NDEBUG_QUEUES, "scsi%d : command for target %d lun %llu was moved from connected to" " the disconnected_queue\n", instance->host_no, cmd->device->id, cmd->device->lun); /* * Restore phase bits to 0 so an interrupted selection, * arbitration can resume. @@ -2426,7 +2427,7 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) { hostdata->last_message = msgout; NCR5380_transfer_pio(instance, &phase, &len, &data); if (msgout == ABORT) { - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); + hostdata->busy[cmd->device->id] &= ~(1 << (cmd->device->lun & 0xFF)); hostdata->connected = NULL; cmd->result = DID_ERROR << 16; collect_stats(hostdata, cmd); @@ -2562,7 +2563,7 @@ static void NCR5380_reselect(struct Scsi_Host *instance) { for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue, prev = NULL; tmp; prev = tmp, tmp = (Scsi_Cmnd *) tmp->host_scribble) - if ((target_mask == (1 << tmp->device->id)) && (lun == tmp->device->lun) + if ((target_mask == (1 << tmp->device->id)) && (lun == (u8)tmp->device->lun) ) { if (prev) { REMOVE(prev, prev->host_scribble, tmp, tmp->host_scribble); @@ -2588,7 +2589,7 @@ static void NCR5380_reselect(struct Scsi_Host *instance) { do_abort(instance); } else { hostdata->connected = tmp; - dprintk(NDEBUG_RESELECTION, "scsi%d : nexus established, target = %d, lun = %d, tag = %d\n", instance->host_no, tmp->device->id, tmp->device->lun, tmp->tag); + dprintk(NDEBUG_RESELECTION, "scsi%d : nexus established, target = %d, lun = %llu, tag = %d\n", instance->host_no, tmp->device->id, tmp->device->lun, tmp->tag); } } diff --git a/drivers/scsi/NCR53c406a.c b/drivers/scsi/NCR53c406a.c index c91888a0a23c..10c3374d759f 100644 --- a/drivers/scsi/NCR53c406a.c +++ b/drivers/scsi/NCR53c406a.c @@ -698,7 +698,7 @@ static int NCR53c406a_queue_lck(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *)) int i; VDEB(printk("NCR53c406a_queue called\n")); - DEB(printk("cmd=%02x, cmd_len=%02x, target=%02x, lun=%02x, bufflen=%d\n", SCpnt->cmnd[0], SCpnt->cmd_len, SCpnt->target, SCpnt->lun, scsi_bufflen(SCpnt))); + DEB(printk("cmd=%02x, cmd_len=%02x, target=%02x, lun=%02x, bufflen=%d\n", SCpnt->cmnd[0], SCpnt->cmd_len, SCpnt->device->target, (u8)SCpnt->device->lun, scsi_bufflen(SCpnt))); #if 0 VDEB(for (i = 0; i < SCpnt->cmd_len; i++) diff --git a/drivers/scsi/a100u2w.c b/drivers/scsi/a100u2w.c index 0163457c12bb..522570d297ca 100644 --- a/drivers/scsi/a100u2w.c +++ b/drivers/scsi/a100u2w.c @@ -891,7 +891,7 @@ static int inia100_build_scb(struct orc_host * host, struct orc_scb * scb, struc printk("max cdb length= %x\b", cmd->cmd_len); scb->cdb_len = IMAX_CDB; } - scb->ident = cmd->device->lun | DISC_ALLOW; + scb->ident = (u8)(cmd->device->lun & 0xff) | DISC_ALLOW; if (cmd->device->tagged_supported) { /* Tag Support */ scb->tag_msg = SIMPLE_QUEUE_TAG; /* Do simple tag only */ } else { diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c index 4921ed19a027..63f576c9300a 100644 --- a/drivers/scsi/aacraid/linit.c +++ b/drivers/scsi/aacraid/linit.c @@ -551,7 +551,7 @@ static int aac_eh_abort(struct scsi_cmnd* cmd) int count; int ret = FAILED; - printk(KERN_ERR "%s: Host adapter abort request (%d,%d,%d,%d)\n", + printk(KERN_ERR "%s: Host adapter abort request (%d,%d,%d,%llu)\n", AAC_DRIVERNAME, host->host_no, sdev_channel(dev), sdev_id(dev), dev->lun); switch (cmd->cmnd[0]) { diff --git a/drivers/scsi/aha152x.c b/drivers/scsi/aha152x.c index e86eb6a921fc..e77b72f78006 100644 --- a/drivers/scsi/aha152x.c +++ b/drivers/scsi/aha152x.c @@ -321,7 +321,7 @@ static LIST_HEAD(aha152x_host_list); #define CMDINFO(cmd) \ (cmd) ? ((cmd)->device->host->host_no) : -1, \ (cmd) ? ((cmd)->device->id & 0x0f) : -1, \ - (cmd) ? ((cmd)->device->lun & 0x07) : -1 + (cmd) ? ((u8)(cmd)->device->lun & 0x07) : -1 static inline void CMD_INC_RESID(struct scsi_cmnd *cmd, int inc) @@ -1602,7 +1602,7 @@ static void busfree_run(struct Scsi_Host *shpnt) #if defined(AHA152X_DEBUG) int hostno=DONE_SC->device->host->host_no; int id=DONE_SC->device->id & 0xf; - int lun=DONE_SC->device->lun & 0x7; + int lun=((u8)DONE_SC->device->lun) & 0x7; #endif Scsi_Cmnd *ptr = DONE_SC; DONE_SC=NULL; @@ -2984,7 +2984,7 @@ static void get_command(struct seq_file *m, Scsi_Cmnd * ptr) int i; SPRINTF("%p: target=%d; lun=%d; cmnd=( ", - ptr, ptr->device->id, ptr->device->lun); + ptr, ptr->device->id, (u8)ptr->device->lun); for (i = 0; i < COMMAND_SIZE(ptr->cmnd[0]); i++) SPRINTF("0x%02x ", ptr->cmnd[i]); diff --git a/drivers/scsi/aic7xxx/aic79xx.h b/drivers/scsi/aic7xxx/aic79xx.h index 113874c1284b..df2e0e5367d2 100644 --- a/drivers/scsi/aic7xxx/aic79xx.h +++ b/drivers/scsi/aic7xxx/aic79xx.h @@ -115,7 +115,7 @@ struct scb_platform_data; #endif #define AHD_BUILD_COL_IDX(target, lun) \ - (((lun) << 4) | target) + ((((u8)lun) << 4) | target) #define AHD_GET_SCB_COL_IDX(ahd, scb) \ ((SCB_GET_LUN(scb) << 4) | SCB_GET_TARGET(ahd, scb)) diff --git a/drivers/scsi/aic7xxx/aic79xx_osm.c b/drivers/scsi/aic7xxx/aic79xx_osm.c index 69d5c43a65e5..ed333669a7dc 100644 --- a/drivers/scsi/aic7xxx/aic79xx_osm.c +++ b/drivers/scsi/aic7xxx/aic79xx_osm.c @@ -2137,7 +2137,7 @@ ahd_linux_queue_cmd_complete(struct ahd_softc *ahd, struct scsi_cmnd *cmd) if (do_fallback) { printk("%s: device overrun (status %x) on %d:%d:%d\n", ahd_name(ahd), status, cmd->device->channel, - cmd->device->id, cmd->device->lun); + cmd->device->id, (u8)cmd->device->lun); } ahd_cmd_set_transaction_status(cmd, new_status); @@ -2253,13 +2253,13 @@ ahd_linux_queue_abort_cmd(struct scsi_cmnd *cmd) disconnected = TRUE; if (ahd_search_qinfifo(ahd, cmd->device->id, cmd->device->channel + 'A', - cmd->device->lun, + cmd->device->lun, pending_scb->hscb->tag, ROLE_INITIATOR, CAM_REQ_ABORTED, SEARCH_COMPLETE) > 0) { printk("%s:%d:%d:%d: Cmd aborted from QINFIFO\n", ahd_name(ahd), cmd->device->channel, - cmd->device->id, cmd->device->lun); + cmd->device->id, (u8)cmd->device->lun); retval = SUCCESS; goto done; } diff --git a/drivers/scsi/aic7xxx/aic79xx_proc.c b/drivers/scsi/aic7xxx/aic79xx_proc.c index e9778b4f7e32..27dbfccea774 100644 --- a/drivers/scsi/aic7xxx/aic79xx_proc.c +++ b/drivers/scsi/aic7xxx/aic79xx_proc.c @@ -197,7 +197,7 @@ ahd_dump_device_state(struct seq_file *m, struct scsi_device *sdev) seq_printf(m, "\tChannel %c Target %d Lun %d Settings\n", sdev->sdev_target->channel + 'A', - sdev->sdev_target->id, sdev->lun); + sdev->sdev_target->id, (u8)sdev->lun); seq_printf(m, "\t\tCommands Queued %ld\n", dev->commands_issued); seq_printf(m, "\t\tCommands Active %d\n", dev->active); diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.c b/drivers/scsi/aic7xxx/aic7xxx_osm.c index 114ff0c6e311..d2c9bf39033d 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_osm.c +++ b/drivers/scsi/aic7xxx/aic7xxx_osm.c @@ -2110,7 +2110,7 @@ ahc_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag) */ printk("%s:%d:%d:%d: Is not an active device\n", ahc_name(ahc), cmd->device->channel, cmd->device->id, - cmd->device->lun); + (u8)cmd->device->lun); retval = SUCCESS; goto no_cmd; } @@ -2118,11 +2118,11 @@ ahc_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag) if ((dev->flags & (AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED)) == 0 && ahc_search_untagged_queues(ahc, cmd, cmd->device->id, cmd->device->channel + 'A', - cmd->device->lun, + (u8)cmd->device->lun, CAM_REQ_ABORTED, SEARCH_COMPLETE) != 0) { printk("%s:%d:%d:%d: Command found on untagged queue\n", ahc_name(ahc), cmd->device->channel, cmd->device->id, - cmd->device->lun); + (u8)cmd->device->lun); retval = SUCCESS; goto done; } @@ -2188,13 +2188,14 @@ ahc_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag) SEARCH_COMPLETE) > 0) { printk("%s:%d:%d:%d: Cmd aborted from QINFIFO\n", ahc_name(ahc), cmd->device->channel, - cmd->device->id, cmd->device->lun); + cmd->device->id, (u8)cmd->device->lun); retval = SUCCESS; goto done; } } else if (ahc_search_qinfifo(ahc, cmd->device->id, cmd->device->channel + 'A', - cmd->device->lun, pending_scb->hscb->tag, + cmd->device->lun, + pending_scb->hscb->tag, ROLE_INITIATOR, /*status*/0, SEARCH_COUNT) > 0) { disconnected = FALSE; diff --git a/drivers/scsi/aic7xxx/aic7xxx_proc.c b/drivers/scsi/aic7xxx/aic7xxx_proc.c index 383a3d11652d..64eec6c07a83 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_proc.c +++ b/drivers/scsi/aic7xxx/aic7xxx_proc.c @@ -175,7 +175,7 @@ ahc_dump_device_state(struct seq_file *m, struct scsi_device *sdev) seq_printf(m, "\tChannel %c Target %d Lun %d Settings\n", sdev->sdev_target->channel + 'A', - sdev->sdev_target->id, sdev->lun); + sdev->sdev_target->id, (u8)sdev->lun); seq_printf(m, "\t\tCommands Queued %ld\n", dev->commands_issued); seq_printf(m, "\t\tCommands Active %d\n", dev->active); diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c index 652b41b4ddbd..b13764ca23fd 100644 --- a/drivers/scsi/arcmsr/arcmsr_hba.c +++ b/drivers/scsi/arcmsr/arcmsr_hba.c @@ -2335,7 +2335,7 @@ static int arcmsr_polling_hba_ccbdone(struct AdapterControlBlock *acb, " poll command abort successfully \n" , acb->host->host_no , ccb->pcmd->device->id - , ccb->pcmd->device->lun + , (u32)ccb->pcmd->device->lun , ccb); ccb->pcmd->result = DID_ABORT << 16; arcmsr_ccb_complete(ccb); @@ -2399,7 +2399,7 @@ static int arcmsr_polling_hbb_ccbdone(struct AdapterControlBlock *acb, " poll command abort successfully \n" ,acb->host->host_no ,ccb->pcmd->device->id - ,ccb->pcmd->device->lun + ,(u32)ccb->pcmd->device->lun ,ccb); ccb->pcmd->result = DID_ABORT << 16; arcmsr_ccb_complete(ccb); @@ -2456,7 +2456,7 @@ polling_hbc_ccb_retry: " poll command abort successfully \n" , acb->host->host_no , pCCB->pcmd->device->id - , pCCB->pcmd->device->lun + , (u32)pCCB->pcmd->device->lun , pCCB); pCCB->pcmd->result = DID_ABORT << 16; arcmsr_ccb_complete(pCCB); @@ -3058,7 +3058,7 @@ static int arcmsr_abort(struct scsi_cmnd *cmd) int rtn = FAILED; printk(KERN_NOTICE "arcmsr%d: abort device command of scsi id = %d lun = %d \n", - acb->host->host_no, cmd->device->id, cmd->device->lun); + acb->host->host_no, cmd->device->id, (u32)cmd->device->lun); acb->acb_flags |= ACB_F_ABORT; acb->num_aborts++; /* diff --git a/drivers/scsi/arm/acornscsi.c b/drivers/scsi/arm/acornscsi.c index 2e797a367608..d89b9b4deb3c 100644 --- a/drivers/scsi/arm/acornscsi.c +++ b/drivers/scsi/arm/acornscsi.c @@ -760,7 +760,8 @@ intr_ret_t acornscsi_kick(AS_Host *host) SCpnt->tag = SCpnt->device->current_tag; } else #endif - set_bit(SCpnt->device->id * 8 + SCpnt->device->lun, host->busyluns); + set_bit(SCpnt->device->id * 8 + + (u8)(SCpnt->device->lun & 0x07), host->busyluns); host->stats.removes += 1; @@ -863,7 +864,8 @@ static void acornscsi_done(AS_Host *host, struct scsi_cmnd **SCpntp, if (!SCpnt->scsi_done) panic("scsi%d.H: null scsi_done function in acornscsi_done", host->host->host_no); - clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, host->busyluns); + clear_bit(SCpnt->device->id * 8 + + (u8)(SCpnt->device->lun & 0x7), host->busyluns); SCpnt->scsi_done(SCpnt); } else @@ -1576,7 +1578,8 @@ void acornscsi_message(AS_Host *host) printk(KERN_NOTICE "scsi%d.%c: disabling tagged queueing\n", host->host->host_no, acornscsi_target(host)); host->SCpnt->device->simple_tags = 0; - set_bit(host->SCpnt->device->id * 8 + host->SCpnt->device->lun, host->busyluns); + set_bit(host->SCpnt->device->id * 8 + + (u8)(host->SCpnt->device->lun & 0x7), host->busyluns); break; #endif case EXTENDED_MESSAGE | (EXTENDED_SDTR << 8): @@ -2671,7 +2674,8 @@ int acornscsi_abort(struct scsi_cmnd *SCpnt) //#if (DEBUG & DEBUG_ABORT) printk("clear "); //#endif - clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, host->busyluns); + clear_bit(SCpnt->device->id * 8 + + (u8)(SCpnt->device->lun & 0x7), host->busyluns); /* * We found the command, and cleared it out. Either @@ -2853,7 +2857,7 @@ static int acornscsi_show_info(struct seq_file *m, struct Scsi_Host *instance) shost_for_each_device(scd, instance) { seq_printf(m, "Device/Lun TaggedQ Sync\n"); - seq_printf(m, " %d/%d ", scd->id, scd->lun); + seq_printf(m, " %d/%llu ", scd->id, scd->lun); if (scd->tagged_supported) seq_printf(m, "%3sabled(%3d) ", scd->simple_tags ? "en" : "dis", diff --git a/drivers/scsi/arm/fas216.c b/drivers/scsi/arm/fas216.c index b46a6f6c0eb3..71cfb1e504c4 100644 --- a/drivers/scsi/arm/fas216.c +++ b/drivers/scsi/arm/fas216.c @@ -1821,7 +1821,8 @@ static void fas216_allocate_tag(FAS216_Info *info, struct scsi_cmnd *SCpnt) SCpnt->tag = SCpnt->device->current_tag; } else #endif - set_bit(SCpnt->device->id * 8 + SCpnt->device->lun, info->busyluns); + set_bit(SCpnt->device->id * 8 + + (u8)(SCpnt->device->lun & 0x7), info->busyluns); info->stats.removes += 1; switch (SCpnt->cmnd[0]) { @@ -2171,7 +2172,8 @@ static void fas216_done(FAS216_Info *info, unsigned int result) * status. */ info->device[SCpnt->device->id].parity_check = 0; - clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, info->busyluns); + clear_bit(SCpnt->device->id * 8 + + (u8)(SCpnt->device->lun & 0x7), info->busyluns); fn = (void (*)(FAS216_Info *, struct scsi_cmnd *, unsigned int))SCpnt->host_scribble; fn(info, SCpnt, result); @@ -2398,7 +2400,8 @@ static enum res_find fas216_find_command(FAS216_Info *info, * been set. */ info->origSCpnt = NULL; - clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, info->busyluns); + clear_bit(SCpnt->device->id * 8 + + (u8)(SCpnt->device->lun & 0x7), info->busyluns); printk("waiting for execution "); res = res_success; } else @@ -3000,7 +3003,7 @@ void fas216_print_devices(FAS216_Info *info, struct seq_file *m) shost_for_each_device(scd, info->host) { dev = &info->device[scd->id]; - seq_printf(m, " %d/%d ", scd->id, scd->lun); + seq_printf(m, " %d/%llu ", scd->id, scd->lun); if (scd->tagged_supported) seq_printf(m, "%3sabled(%3d) ", scd->simple_tags ? "en" : "dis", diff --git a/drivers/scsi/arm/queue.c b/drivers/scsi/arm/queue.c index cb11ccef54e5..3441ce3ebabf 100644 --- a/drivers/scsi/arm/queue.c +++ b/drivers/scsi/arm/queue.c @@ -167,7 +167,8 @@ struct scsi_cmnd *queue_remove_exclude(Queue_t *queue, unsigned long *exclude) spin_lock_irqsave(&queue->queue_lock, flags); list_for_each(l, &queue->head) { QE_t *q = list_entry(l, QE_t, list); - if (!test_bit(q->SCpnt->device->id * 8 + q->SCpnt->device->lun, exclude)) { + if (!test_bit(q->SCpnt->device->id * 8 + + (u8)(q->SCpnt->device->lun & 0x7), exclude)) { SCpnt = __queue_remove(queue, l); break; } diff --git a/drivers/scsi/atari_NCR5380.c b/drivers/scsi/atari_NCR5380.c index 1814aa20b724..79e6f045c2a9 100644 --- a/drivers/scsi/atari_NCR5380.c +++ b/drivers/scsi/atari_NCR5380.c @@ -361,17 +361,18 @@ static void __init init_tags(void) static int is_lun_busy(Scsi_Cmnd *cmd, int should_be_tagged) { + u8 lun = cmd->device->lun; SETUP_HOSTDATA(cmd->device->host); - if (hostdata->busy[cmd->device->id] & (1 << cmd->device->lun)) + if (hostdata->busy[cmd->device->id] & (1 << lun)) return 1; if (!should_be_tagged || !setup_use_tagged_queuing || !cmd->device->tagged_supported) return 0; - if (TagAlloc[cmd->device->id][cmd->device->lun].nr_allocated >= - TagAlloc[cmd->device->id][cmd->device->lun].queue_size) { + if (TagAlloc[cmd->device->id][lun].nr_allocated >= + TagAlloc[cmd->device->id][lun].queue_size) { dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %d: no free tags\n", - H_NO(cmd), cmd->device->id, cmd->device->lun); + H_NO(cmd), cmd->device->id, lun); return 1; } return 0; @@ -385,6 +386,7 @@ static int is_lun_busy(Scsi_Cmnd *cmd, int should_be_tagged) static void cmd_get_tag(Scsi_Cmnd *cmd, int should_be_tagged) { + u8 lun = cmd->device->lun; SETUP_HOSTDATA(cmd->device->host); /* If we or the target don't support tagged queuing, allocate the LUN for @@ -393,11 +395,11 @@ static void cmd_get_tag(Scsi_Cmnd *cmd, int should_be_tagged) if (!should_be_tagged || !setup_use_tagged_queuing || !cmd->device->tagged_supported) { cmd->tag = TAG_NONE; - hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun); + hostdata->busy[cmd->device->id] |= (1 << lun); dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %d now allocated by untagged " - "command\n", H_NO(cmd), cmd->device->id, cmd->device->lun); + "command\n", H_NO(cmd), cmd->device->id, lun); } else { - TAG_ALLOC *ta = &TagAlloc[cmd->device->id][cmd->device->lun]; + TAG_ALLOC *ta = &TagAlloc[cmd->device->id][lun]; cmd->tag = find_first_zero_bit(ta->allocated, MAX_TAGS); set_bit(cmd->tag, ta->allocated); @@ -405,7 +407,7 @@ static void cmd_get_tag(Scsi_Cmnd *cmd, int should_be_tagged) dprintk(NDEBUG_TAGS, "scsi%d: using tag %d for target %d lun %d " "(now %d tags in use)\n", H_NO(cmd), cmd->tag, cmd->device->id, - cmd->device->lun, ta->nr_allocated); + lun, ta->nr_allocated); } } @@ -416,21 +418,22 @@ static void cmd_get_tag(Scsi_Cmnd *cmd, int should_be_tagged) static void cmd_free_tag(Scsi_Cmnd *cmd) { + u8 lun = cmd->device->lun; SETUP_HOSTDATA(cmd->device->host); if (cmd->tag == TAG_NONE) { - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); + hostdata->busy[cmd->device->id] &= ~(1 << lun); dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %d untagged cmd finished\n", - H_NO(cmd), cmd->device->id, cmd->device->lun); + H_NO(cmd), cmd->device->id, lun); } else if (cmd->tag >= MAX_TAGS) { printk(KERN_NOTICE "scsi%d: trying to free bad tag %d!\n", H_NO(cmd), cmd->tag); } else { - TAG_ALLOC *ta = &TagAlloc[cmd->device->id][cmd->device->lun]; + TAG_ALLOC *ta = &TagAlloc[cmd->device->id][lun]; clear_bit(cmd->tag, ta->allocated); ta->nr_allocated--; dprintk(NDEBUG_TAGS, "scsi%d: freed tag %d for target %d lun %d\n", - H_NO(cmd), cmd->tag, cmd->device->id, cmd->device->lun); + H_NO(cmd), cmd->tag, cmd->device->id, lun); } } @@ -713,7 +716,7 @@ static void lprint_Scsi_Cmnd(Scsi_Cmnd *cmd) { int i, s; unsigned char *command; - printk("scsi%d: destination target %d, lun %d\n", + printk("scsi%d: destination target %d, lun %llu\n", H_NO(cmd), cmd->device->id, cmd->device->lun); printk(KERN_CONT " command = "); command = cmd->cmnd; @@ -759,7 +762,7 @@ static void show_Scsi_Cmnd(Scsi_Cmnd *cmd, struct seq_file *m) { int i, s; unsigned char *command; - seq_printf(m, "scsi%d: destination target %d, lun %d\n", + seq_printf(m, "scsi%d: destination target %d, lun %llu\n", H_NO(cmd), cmd->device->id, cmd->device->lun); seq_printf(m, " command = "); command = cmd->cmnd; @@ -1060,12 +1063,13 @@ static void NCR5380_main(struct work_struct *work) #endif for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, prev = NULL; tmp; prev = tmp, tmp = NEXT(tmp)) { + u8 lun = tmp->device->lun; #if (NDEBUG & NDEBUG_LISTS) if (prev != tmp) - printk("MAIN tmp=%p target=%d busy=%d lun=%d\n", + printk("MAIN tmp=%p target=%d busy=%d lun=%llu\n", tmp, tmp->device->id, hostdata->busy[tmp->device->id], - tmp->device->lun); + lun); #endif /* When we find one, remove it from the issue queue. */ /* ++guenther: possible race with Falcon locking */ @@ -1073,7 +1077,7 @@ static void NCR5380_main(struct work_struct *work) #ifdef SUPPORT_TAGS !is_lun_busy( tmp, tmp->cmnd[0] != REQUEST_SENSE) #else - !(hostdata->busy[tmp->device->id] & (1 << tmp->device->lun)) + !(hostdata->busy[tmp->device->id] & (1 << lun)) #endif ) { /* ++guenther: just to be sure, this must be atomic */ @@ -1099,7 +1103,7 @@ static void NCR5380_main(struct work_struct *work) */ dprintk(NDEBUG_MAIN, "scsi%d: main(): command for target %d " "lun %d removed from issue_queue\n", - HOSTNO, tmp->device->id, tmp->device->lun); + HOSTNO, tmp->device->id, lun); /* * REQUEST SENSE commands are issued without tagged * queueing, even on SCSI-II devices because the @@ -2061,7 +2065,7 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) * accesses to this device will use the * polled-IO. */ printk(KERN_NOTICE "scsi%d: switching target %d " - "lun %d to slow handshake\n", HOSTNO, + "lun %llu to slow handshake\n", HOSTNO, cmd->device->id, cmd->device->lun); cmd->device->borken = 1; NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | @@ -2113,7 +2117,7 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) /* Accept message by clearing ACK */ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - dprintk(NDEBUG_LINKED, "scsi%d: target %d lun %d linked command " + dprintk(NDEBUG_LINKED, "scsi%d: target %d lun %llu linked command " "complete.\n", HOSTNO, cmd->device->id, cmd->device->lun); /* Enable reselect interrupts */ @@ -2125,7 +2129,7 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) */ if (!cmd->next_link) { - printk(KERN_NOTICE "scsi%d: target %d lun %d " + printk(KERN_NOTICE "scsi%d: target %d lun %llu " "linked command complete, no next_link\n", HOSTNO, cmd->device->id, cmd->device->lun); sink = 1; @@ -2138,7 +2142,7 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) * and don't free it! */ cmd->next_link->tag = cmd->tag; cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); - dprintk(NDEBUG_LINKED, "scsi%d: target %d lun %d linked request " + dprintk(NDEBUG_LINKED, "scsi%d: target %d lun %llu linked request " "done, calling scsi_done().\n", HOSTNO, cmd->device->id, cmd->device->lun); #ifdef NCR5380_STATS @@ -2155,7 +2159,7 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) /* ++guenther: possible race with Falcon locking */ falcon_dont_release++; hostdata->connected = NULL; - dprintk(NDEBUG_QUEUES, "scsi%d: command for target %d, lun %d " + dprintk(NDEBUG_QUEUES, "scsi%d: command for target %d, lun %llu " "completed\n", HOSTNO, cmd->device->id, cmd->device->lun); #ifdef SUPPORT_TAGS cmd_free_tag(cmd); @@ -2169,7 +2173,7 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) /* ++Andreas: the mid level code knows about QUEUE_FULL now. */ TAG_ALLOC *ta = &TagAlloc[cmd->device->id][cmd->device->lun]; - dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %d returned " + dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %llu returned " "QUEUE_FULL after %d commands\n", HOSTNO, cmd->device->id, cmd->device->lun, ta->nr_allocated); @@ -2267,7 +2271,7 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) cmd->device->tagged_supported = 0; hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun); cmd->tag = TAG_NONE; - dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %d rejected " + dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %llu rejected " "QUEUE_TAG message; tagged queuing " "disabled\n", HOSTNO, cmd->device->id, cmd->device->lun); @@ -2284,7 +2288,7 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) hostdata->connected = NULL; hostdata->disconnected_queue = cmd; local_irq_restore(flags); - dprintk(NDEBUG_QUEUES, "scsi%d: command for target %d lun %d was " + dprintk(NDEBUG_QUEUES, "scsi%d: command for target %d lun %llu was " "moved from connected to the " "disconnected_queue\n", HOSTNO, cmd->device->id, cmd->device->lun); @@ -2385,12 +2389,12 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) printk("\n"); } else if (tmp != EXTENDED_MESSAGE) printk(KERN_DEBUG "scsi%d: rejecting unknown " - "message %02x from target %d, lun %d\n", + "message %02x from target %d, lun %llu\n", HOSTNO, tmp, cmd->device->id, cmd->device->lun); else printk(KERN_DEBUG "scsi%d: rejecting unknown " "extended message " - "code %02x, length %d from target %d, lun %d\n", + "code %02x, length %d from target %d, lun %llu\n", HOSTNO, extended_msg[1], extended_msg[0], cmd->device->id, cmd->device->lun); @@ -2588,7 +2592,7 @@ static void NCR5380_reselect(struct Scsi_Host *instance) NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); hostdata->connected = tmp; - dprintk(NDEBUG_RESELECTION, "scsi%d: nexus established, target = %d, lun = %d, tag = %d\n", + dprintk(NDEBUG_RESELECTION, "scsi%d: nexus established, target = %d, lun = %llu, tag = %d\n", HOSTNO, tmp->device->id, tmp->device->lun, tmp->tag); falcon_dont_release--; } diff --git a/drivers/scsi/bnx2fc/bnx2fc_io.c b/drivers/scsi/bnx2fc/bnx2fc_io.c index 7bc47fc7c686..e0bb209f3af1 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_io.c +++ b/drivers/scsi/bnx2fc/bnx2fc_io.c @@ -1450,9 +1450,9 @@ static void bnx2fc_lun_reset_cmpl(struct bnx2fc_cmd *io_req) struct scsi_cmnd *sc_cmd = io_req->sc_cmd; struct bnx2fc_rport *tgt = io_req->tgt; struct bnx2fc_cmd *cmd, *tmp; - int tm_lun = sc_cmd->device->lun; + u64 tm_lun = sc_cmd->device->lun; + u64 lun; int rc = 0; - int lun; /* called with tgt_lock held */ BNX2FC_IO_DBG(io_req, "Entered bnx2fc_lun_reset_cmpl\n"); diff --git a/drivers/scsi/ch.c b/drivers/scsi/ch.c index 2a323742ce04..7e952cadbcf1 100644 --- a/drivers/scsi/ch.c +++ b/drivers/scsi/ch.c @@ -247,7 +247,7 @@ ch_read_element_status(scsi_changer *ch, u_int elem, char *data) retry: memset(cmd,0,sizeof(cmd)); cmd[0] = READ_ELEMENT_STATUS; - cmd[1] = (ch->device->lun << 5) | + cmd[1] = ((ch->device->lun & 0x7) << 5) | (ch->voltags ? 0x10 : 0) | ch_elem_to_typecode(ch,elem); cmd[2] = (elem >> 8) & 0xff; @@ -283,7 +283,7 @@ ch_init_elem(scsi_changer *ch) VPRINTK(KERN_INFO, "INITIALIZE ELEMENT STATUS, may take some time ...\n"); memset(cmd,0,sizeof(cmd)); cmd[0] = INITIALIZE_ELEMENT_STATUS; - cmd[1] = ch->device->lun << 5; + cmd[1] = (ch->device->lun & 0x7) << 5; err = ch_do_scsi(ch, cmd, NULL, 0, DMA_NONE); VPRINTK(KERN_INFO, "... finished\n"); return err; @@ -303,7 +303,7 @@ ch_readconfig(scsi_changer *ch) memset(cmd,0,sizeof(cmd)); cmd[0] = MODE_SENSE; - cmd[1] = ch->device->lun << 5; + cmd[1] = (ch->device->lun & 0x7) << 5; cmd[2] = 0x1d; cmd[4] = 255; result = ch_do_scsi(ch, cmd, buffer, 255, DMA_FROM_DEVICE); @@ -428,7 +428,7 @@ ch_position(scsi_changer *ch, u_int trans, u_int elem, int rotate) trans = ch->firsts[CHET_MT]; memset(cmd,0,sizeof(cmd)); cmd[0] = POSITION_TO_ELEMENT; - cmd[1] = ch->device->lun << 5; + cmd[1] = (ch->device->lun & 0x7) << 5; cmd[2] = (trans >> 8) & 0xff; cmd[3] = trans & 0xff; cmd[4] = (elem >> 8) & 0xff; @@ -447,7 +447,7 @@ ch_move(scsi_changer *ch, u_int trans, u_int src, u_int dest, int rotate) trans = ch->firsts[CHET_MT]; memset(cmd,0,sizeof(cmd)); cmd[0] = MOVE_MEDIUM; - cmd[1] = ch->device->lun << 5; + cmd[1] = (ch->device->lun & 0x7) << 5; cmd[2] = (trans >> 8) & 0xff; cmd[3] = trans & 0xff; cmd[4] = (src >> 8) & 0xff; @@ -470,7 +470,7 @@ ch_exchange(scsi_changer *ch, u_int trans, u_int src, trans = ch->firsts[CHET_MT]; memset(cmd,0,sizeof(cmd)); cmd[0] = EXCHANGE_MEDIUM; - cmd[1] = ch->device->lun << 5; + cmd[1] = (ch->device->lun & 0x7) << 5; cmd[2] = (trans >> 8) & 0xff; cmd[3] = trans & 0xff; cmd[4] = (src >> 8) & 0xff; @@ -518,7 +518,7 @@ ch_set_voltag(scsi_changer *ch, u_int elem, elem, tag); memset(cmd,0,sizeof(cmd)); cmd[0] = SEND_VOLUME_TAG; - cmd[1] = (ch->device->lun << 5) | + cmd[1] = ((ch->device->lun & 0x7) << 5) | ch_elem_to_typecode(ch,elem); cmd[2] = (elem >> 8) & 0xff; cmd[3] = elem & 0xff; @@ -754,7 +754,7 @@ static long ch_ioctl(struct file *file, voltag_retry: memset(ch_cmd, 0, sizeof(ch_cmd)); ch_cmd[0] = READ_ELEMENT_STATUS; - ch_cmd[1] = (ch->device->lun << 5) | + ch_cmd[1] = ((ch->device->lun & 0x7) << 5) | (ch->voltags ? 0x10 : 0) | ch_elem_to_typecode(ch,elem); ch_cmd[2] = (elem >> 8) & 0xff; diff --git a/drivers/scsi/csiostor/csio_scsi.c b/drivers/scsi/csiostor/csio_scsi.c index 7494e4bc69cc..86103c8475d8 100644 --- a/drivers/scsi/csiostor/csio_scsi.c +++ b/drivers/scsi/csiostor/csio_scsi.c @@ -1657,7 +1657,7 @@ csio_scsi_err_handler(struct csio_hw *hw, struct csio_ioreq *req) case FW_SCSI_UNDER_FLOW_ERR: csio_warn(hw, "Under-flow error,cmnd:0x%x expected" - " len:0x%x resid:0x%x lun:0x%x ssn:0x%x\n", + " len:0x%x resid:0x%x lun:0x%llx ssn:0x%x\n", cmnd->cmnd[0], scsi_bufflen(cmnd), scsi_get_resid(cmnd), cmnd->device->lun, rn->flowid); @@ -1957,7 +1957,7 @@ csio_eh_abort_handler(struct scsi_cmnd *cmnd) csio_dbg(hw, "Request to abort ioreq:%p cmd:%p cdb:%08llx" - " ssni:0x%x lun:%d iq:0x%x\n", + " ssni:0x%x lun:%llu iq:0x%x\n", ioreq, cmnd, *((uint64_t *)cmnd->cmnd), rn->flowid, cmnd->device->lun, csio_q_physiqid(hw, ioreq->iq_idx)); @@ -2015,13 +2015,13 @@ inval_scmnd: /* FW successfully aborted the request */ if (host_byte(cmnd->result) == DID_REQUEUE) { csio_info(hw, - "Aborted SCSI command to (%d:%d) serial#:0x%lx\n", + "Aborted SCSI command to (%d:%llu) serial#:0x%lx\n", cmnd->device->id, cmnd->device->lun, cmnd->serial_number); return SUCCESS; } else { csio_info(hw, - "Failed to abort SCSI command, (%d:%d) serial#:0x%lx\n", + "Failed to abort SCSI command, (%d:%llu) serial#:0x%lx\n", cmnd->device->id, cmnd->device->lun, cmnd->serial_number); return FAILED; @@ -2100,13 +2100,13 @@ csio_eh_lun_reset_handler(struct scsi_cmnd *cmnd) if (!rn) goto fail; - csio_dbg(hw, "Request to reset LUN:%d (ssni:0x%x tgtid:%d)\n", + csio_dbg(hw, "Request to reset LUN:%llu (ssni:0x%x tgtid:%d)\n", cmnd->device->lun, rn->flowid, rn->scsi_id); if (!csio_is_lnode_ready(ln)) { csio_err(hw, "LUN reset cannot be issued on non-ready" - " local node vnpi:0x%x (LUN:%d)\n", + " local node vnpi:0x%x (LUN:%llu)\n", ln->vnp_flowid, cmnd->device->lun); goto fail; } @@ -2126,7 +2126,7 @@ csio_eh_lun_reset_handler(struct scsi_cmnd *cmnd) if (fc_remote_port_chkready(rn->rport)) { csio_err(hw, "LUN reset cannot be issued on non-ready" - " remote node ssni:0x%x (LUN:%d)\n", + " remote node ssni:0x%x (LUN:%llu)\n", rn->flowid, cmnd->device->lun); goto fail; } @@ -2168,7 +2168,7 @@ csio_eh_lun_reset_handler(struct scsi_cmnd *cmnd) sld.level = CSIO_LEV_LUN; sld.lnode = ioreq->lnode; sld.rnode = ioreq->rnode; - sld.oslun = (uint64_t)cmnd->device->lun; + sld.oslun = cmnd->device->lun; spin_lock_irqsave(&hw->lock, flags); /* Kick off TM SM on the ioreq */ @@ -2190,7 +2190,7 @@ csio_eh_lun_reset_handler(struct scsi_cmnd *cmnd) /* LUN reset timed-out */ if (((struct scsi_cmnd *)csio_scsi_cmnd(ioreq)) == cmnd) { - csio_err(hw, "LUN reset (%d:%d) timed out\n", + csio_err(hw, "LUN reset (%d:%llu) timed out\n", cmnd->device->id, cmnd->device->lun); spin_lock_irq(&hw->lock); @@ -2203,7 +2203,7 @@ csio_eh_lun_reset_handler(struct scsi_cmnd *cmnd) /* LUN reset returned, check cached status */ if (cmnd->SCp.Status != FW_SUCCESS) { - csio_err(hw, "LUN reset failed (%d:%d), status: %d\n", + csio_err(hw, "LUN reset failed (%d:%llu), status: %d\n", cmnd->device->id, cmnd->device->lun, cmnd->SCp.Status); goto fail; } @@ -2223,7 +2223,7 @@ csio_eh_lun_reset_handler(struct scsi_cmnd *cmnd) /* Aborts may have timed out */ if (retval != 0) { csio_err(hw, - "Attempt to abort I/Os during LUN reset of %d" + "Attempt to abort I/Os during LUN reset of %llu" " returned %d\n", cmnd->device->lun, retval); /* Return I/Os back to active_q */ spin_lock_irq(&hw->lock); @@ -2234,7 +2234,7 @@ csio_eh_lun_reset_handler(struct scsi_cmnd *cmnd) CSIO_INC_STATS(rn, n_lun_rst); - csio_info(hw, "LUN reset occurred (%d:%d)\n", + csio_info(hw, "LUN reset occurred (%d:%llu)\n", cmnd->device->id, cmnd->device->lun); return SUCCESS; diff --git a/drivers/scsi/dc395x.c b/drivers/scsi/dc395x.c index ad7bee069d08..dff461f2338e 100644 --- a/drivers/scsi/dc395x.c +++ b/drivers/scsi/dc395x.c @@ -1087,7 +1087,7 @@ static int dc395x_queue_command_lck(struct scsi_cmnd *cmd, void (*done)(struct s struct AdapterCtlBlk *acb = (struct AdapterCtlBlk *)cmd->device->host->hostdata; dprintkdbg(DBG_0, "queue_command: (0x%p) <%02i-%i> cmnd=0x%02x\n", - cmd, cmd->device->id, cmd->device->lun, cmd->cmnd[0]); + cmd, cmd->device->id, (u8)cmd->device->lun, cmd->cmnd[0]); /* Assume BAD_TARGET; will be cleared later */ cmd->result = DID_BAD_TARGET << 16; @@ -1102,7 +1102,7 @@ static int dc395x_queue_command_lck(struct scsi_cmnd *cmd, void (*done)(struct s /* does the specified lun on the specified device exist */ if (!(acb->dcb_map[cmd->device->id] & (1 << cmd->device->lun))) { dprintkl(KERN_INFO, "queue_command: Ignore target <%02i-%i>\n", - cmd->device->id, cmd->device->lun); + cmd->device->id, (u8)cmd->device->lun); goto complete; } @@ -1111,7 +1111,7 @@ static int dc395x_queue_command_lck(struct scsi_cmnd *cmd, void (*done)(struct s if (!dcb) { /* should never happen */ dprintkl(KERN_ERR, "queue_command: No such device <%02i-%i>", - cmd->device->id, cmd->device->lun); + cmd->device->id, (u8)cmd->device->lun); goto complete; } @@ -1207,7 +1207,7 @@ static void dump_register_info(struct AdapterCtlBlk *acb, "cmnd=0x%02x <%02i-%i>\n", srb, srb->cmd, srb->cmd->cmnd[0], srb->cmd->device->id, - srb->cmd->device->lun); + (u8)srb->cmd->device->lun); printk(" sglist=%p cnt=%i idx=%i len=%zu\n", srb->segment_x, srb->sg_count, srb->sg_index, srb->total_xfer_length); @@ -1302,7 +1302,7 @@ static int __dc395x_eh_bus_reset(struct scsi_cmnd *cmd) (struct AdapterCtlBlk *)cmd->device->host->hostdata; dprintkl(KERN_INFO, "eh_bus_reset: (0%p) target=<%02i-%i> cmd=%p\n", - cmd, cmd->device->id, cmd->device->lun, cmd); + cmd, cmd->device->id, (u8)cmd->device->lun, cmd); if (timer_pending(&acb->waiting_timer)) del_timer(&acb->waiting_timer); @@ -1369,7 +1369,7 @@ static int dc395x_eh_abort(struct scsi_cmnd *cmd) struct DeviceCtlBlk *dcb; struct ScsiReqBlk *srb; dprintkl(KERN_INFO, "eh_abort: (0x%p) target=<%02i-%i> cmd=%p\n", - cmd, cmd->device->id, cmd->device->lun, cmd); + cmd, cmd->device->id, (u8)cmd->device->lun, cmd); dcb = find_dcb(acb, cmd->device->id, cmd->device->lun); if (!dcb) { @@ -1605,7 +1605,7 @@ static u8 start_scsi(struct AdapterCtlBlk* acb, struct DeviceCtlBlk* dcb, dprintkl(KERN_WARNING, "start_scsi: (0x%p) " "Out of tags target=<%02i-%i>)\n", srb->cmd, srb->cmd->device->id, - srb->cmd->device->lun); + (u8)srb->cmd->device->lun); srb->state = SRB_READY; DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_HWRESELECT); @@ -1623,7 +1623,7 @@ static u8 start_scsi(struct AdapterCtlBlk* acb, struct DeviceCtlBlk* dcb, /*polling:*/ /* Send CDB ..command block ......... */ dprintkdbg(DBG_KG, "start_scsi: (0x%p) <%02i-%i> cmnd=0x%02x tag=%i\n", - srb->cmd, srb->cmd->device->id, srb->cmd->device->lun, + srb->cmd, srb->cmd->device->id, (u8)srb->cmd->device->lun, srb->cmd->cmnd[0], srb->tag_number); if (srb->flag & AUTO_REQSENSE) { DC395x_write8(acb, TRM_S1040_SCSI_FIFO, REQUEST_SENSE); @@ -2041,7 +2041,7 @@ static void data_out_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb, u16 scsi_status = *pscsi_status; u32 d_left_counter = 0; dprintkdbg(DBG_0, "data_out_phase0: (0x%p) <%02i-%i>\n", - srb->cmd, srb->cmd->device->id, srb->cmd->device->lun); + srb->cmd, srb->cmd->device->id, (u8)srb->cmd->device->lun); /* * KG: We need to drain the buffers before we draw any conclusions! @@ -2171,7 +2171,7 @@ static void data_out_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb, u16 *pscsi_status) { dprintkdbg(DBG_0, "data_out_phase1: (0x%p) <%02i-%i>\n", - srb->cmd, srb->cmd->device->id, srb->cmd->device->lun); + srb->cmd, srb->cmd->device->id, (u8)srb->cmd->device->lun); clear_fifo(acb, "data_out_phase1"); /* do prepare before transfer when data out phase */ data_io_transfer(acb, srb, XFERDATAOUT); @@ -2183,7 +2183,7 @@ static void data_in_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb, u16 scsi_status = *pscsi_status; dprintkdbg(DBG_0, "data_in_phase0: (0x%p) <%02i-%i>\n", - srb->cmd, srb->cmd->device->id, srb->cmd->device->lun); + srb->cmd, srb->cmd->device->id, (u8)srb->cmd->device->lun); /* * KG: DataIn is much more tricky than DataOut. When the device is finished @@ -2394,7 +2394,7 @@ static void data_in_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb, u16 *pscsi_status) { dprintkdbg(DBG_0, "data_in_phase1: (0x%p) <%02i-%i>\n", - srb->cmd, srb->cmd->device->id, srb->cmd->device->lun); + srb->cmd, srb->cmd->device->id, (u8)srb->cmd->device->lun); data_io_transfer(acb, srb, XFERDATAIN); } @@ -2406,7 +2406,7 @@ static void data_io_transfer(struct AdapterCtlBlk *acb, u8 bval; dprintkdbg(DBG_0, "data_io_transfer: (0x%p) <%02i-%i> %c len=%i, sg=(%i/%i)\n", - srb->cmd, srb->cmd->device->id, srb->cmd->device->lun, + srb->cmd, srb->cmd->device->id, (u8)srb->cmd->device->lun, ((io_dir & DMACMD_DIR) ? 'r' : 'w'), srb->total_xfer_length, srb->sg_index, srb->sg_count); if (srb == acb->tmp_srb) @@ -2579,7 +2579,7 @@ static void status_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb, u16 *pscsi_status) { dprintkdbg(DBG_0, "status_phase0: (0x%p) <%02i-%i>\n", - srb->cmd, srb->cmd->device->id, srb->cmd->device->lun); + srb->cmd, srb->cmd->device->id, (u8)srb->cmd->device->lun); srb->target_status = DC395x_read8(acb, TRM_S1040_SCSI_FIFO); srb->end_message = DC395x_read8(acb, TRM_S1040_SCSI_FIFO); /* get message */ srb->state = SRB_COMPLETED; @@ -2593,7 +2593,7 @@ static void status_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb, u16 *pscsi_status) { dprintkdbg(DBG_0, "status_phase1: (0x%p) <%02i-%i>\n", - srb->cmd, srb->cmd->device->id, srb->cmd->device->lun); + srb->cmd, srb->cmd->device->id, (u8)srb->cmd->device->lun); srb->state = SRB_STATUS; DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */ DC395x_write8(acb, TRM_S1040_SCSI_COMMAND, SCMD_COMP); @@ -3318,7 +3318,7 @@ static void srb_done(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb, int ckc_only = 1; dprintkdbg(DBG_1, "srb_done: (0x%p) <%02i-%i>\n", srb->cmd, - srb->cmd->device->id, srb->cmd->device->lun); + srb->cmd->device->id, (u8)srb->cmd->device->lun); dprintkdbg(DBG_SG, "srb_done: srb=%p sg=%i(%i/%i) buf=%p\n", srb, scsi_sg_count(cmd), srb->sg_index, srb->sg_count, scsi_sgtalbe(cmd)); @@ -3498,7 +3498,7 @@ static void srb_done(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb, if (srb->total_xfer_length) dprintkdbg(DBG_KG, "srb_done: (0x%p) <%02i-%i> " "cmnd=0x%02x Missed %i bytes\n", - cmd, cmd->device->id, cmd->device->lun, + cmd, cmd->device->id, (u8)cmd->device->lun, cmd->cmnd[0], srb->total_xfer_length); } @@ -3538,7 +3538,7 @@ static void doing_srb_done(struct AdapterCtlBlk *acb, u8 did_flag, dir = p->sc_data_direction; result = MK_RES(0, did_flag, 0, 0); printk("G:%p(%02i-%i) ", p, - p->device->id, p->device->lun); + p->device->id, (u8)p->device->lun); srb_going_remove(dcb, srb); free_tag(dcb, srb); srb_free_insert(acb, srb); @@ -3568,7 +3568,7 @@ static void doing_srb_done(struct AdapterCtlBlk *acb, u8 did_flag, result = MK_RES(0, did_flag, 0, 0); printk("W:%p<%02i-%i>", p, p->device->id, - p->device->lun); + (u8)p->device->lun); srb_waiting_remove(dcb, srb); srb_free_insert(acb, srb); p->result = result; @@ -3677,7 +3677,7 @@ static void request_sense(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb, { struct scsi_cmnd *cmd = srb->cmd; dprintkdbg(DBG_1, "request_sense: (0x%p) <%02i-%i>\n", - cmd, cmd->device->id, cmd->device->lun); + cmd, cmd->device->id, (u8)cmd->device->lun); srb->flag |= AUTO_REQSENSE; srb->adapter_status = 0; diff --git a/drivers/scsi/dpt_i2o.c b/drivers/scsi/dpt_i2o.c index c0ae8fa57a3b..67283ef418ac 100644 --- a/drivers/scsi/dpt_i2o.c +++ b/drivers/scsi/dpt_i2o.c @@ -459,7 +459,7 @@ static int adpt_queue_lck(struct scsi_cmnd * cmd, void (*done) (struct scsi_cmnd * to the device structure. This should be a TEST_UNIT_READY * command from scan_scsis_single. */ - if ((pDev = adpt_find_device(pHba, (u32)cmd->device->channel, (u32)cmd->device->id, (u32)cmd->device->lun)) == NULL) { + if ((pDev = adpt_find_device(pHba, (u32)cmd->device->channel, (u32)cmd->device->id, cmd->device->lun)) == NULL) { // TODO: if any luns are at this bus, scsi id then fake a TEST_UNIT_READY and INQUIRY response // with type 7F (for all luns less than the max for this bus,id) so the lun scan will continue. cmd->result = (DID_NO_CONNECT << 16); @@ -579,8 +579,8 @@ static int adpt_show_info(struct seq_file *m, struct Scsi_Host *host) seq_printf(m," Rev: %-8.8s\n", d->pScsi_dev->rev); unit = d->pI2o_dev->lct_data.tid; - seq_printf(m, "\tTID=%d, (Channel=%d, Target=%d, Lun=%d) (%s)\n\n", - unit, (int)d->scsi_channel, (int)d->scsi_id, (int)d->scsi_lun, + seq_printf(m, "\tTID=%d, (Channel=%d, Target=%d, Lun=%llu) (%s)\n\n", + unit, (int)d->scsi_channel, (int)d->scsi_id, d->scsi_lun, scsi_device_online(d->pScsi_dev)? "online":"offline"); d = d->next_lun; } @@ -1162,7 +1162,7 @@ static void adpt_i2o_delete_hba(adpt_hba* pHba) } } -static struct adpt_device* adpt_find_device(adpt_hba* pHba, u32 chan, u32 id, u32 lun) +static struct adpt_device* adpt_find_device(adpt_hba* pHba, u32 chan, u32 id, u64 lun) { struct adpt_device* d; @@ -1462,7 +1462,7 @@ static int adpt_i2o_parse_lct(adpt_hba* pHba) i2o_lct *lct = pHba->lct; u8 bus_no = 0; s16 scsi_id; - s16 scsi_lun; + u64 scsi_lun; u32 buf[10]; // larger than 7, or 8 ... struct adpt_device* pDev; @@ -1496,7 +1496,7 @@ static int adpt_i2o_parse_lct(adpt_hba* pHba) } bus_no = buf[0]>>16; scsi_id = buf[1]; - scsi_lun = (buf[2]>>8 )&0xff; + scsi_lun = scsilun_to_int((struct scsi_lun *)&buf[2]); if(bus_no >= MAX_CHANNEL) { // Something wrong skip it printk(KERN_WARNING"%s: Channel number %d out of range \n", pHba->name, bus_no); continue; @@ -1571,7 +1571,7 @@ static int adpt_i2o_parse_lct(adpt_hba* pHba) if(adpt_i2o_query_scalar(pHba, tid, 0x8000, -1, buf, 32)>=0) { bus_no = buf[0]>>16; scsi_id = buf[1]; - scsi_lun = (buf[2]>>8 )&0xff; + scsi_lun = scsilun_to_int((struct scsi_lun *)&buf[2]); if(bus_no >= MAX_CHANNEL) { // Something wrong skip it continue; } @@ -2407,8 +2407,8 @@ static s32 adpt_i2o_to_scsi(void __iomem *reply, struct scsi_cmnd* cmd) case I2O_SCSI_DSC_COMMAND_TIMEOUT: case I2O_SCSI_DSC_NO_ADAPTER: case I2O_SCSI_DSC_RESOURCE_UNAVAILABLE: - printk(KERN_WARNING"%s: SCSI Timeout-Device (%d,%d,%d) hba status=0x%x, dev status=0x%x, cmd=0x%x\n", - pHba->name, (u32)cmd->device->channel, (u32)cmd->device->id, (u32)cmd->device->lun, hba_status, dev_status, cmd->cmnd[0]); + printk(KERN_WARNING"%s: SCSI Timeout-Device (%d,%d,%llu) hba status=0x%x, dev status=0x%x, cmd=0x%x\n", + pHba->name, (u32)cmd->device->channel, (u32)cmd->device->id, cmd->device->lun, hba_status, dev_status, cmd->cmnd[0]); cmd->result = (DID_TIME_OUT << 16); break; case I2O_SCSI_DSC_ADAPTER_BUSY: @@ -2447,8 +2447,8 @@ static s32 adpt_i2o_to_scsi(void __iomem *reply, struct scsi_cmnd* cmd) case I2O_SCSI_DSC_QUEUE_FROZEN: case I2O_SCSI_DSC_REQUEST_INVALID: default: - printk(KERN_WARNING"%s: SCSI error %0x-Device(%d,%d,%d) hba_status=0x%x, dev_status=0x%x, cmd=0x%x\n", - pHba->name, detailed_status & I2O_SCSI_DSC_MASK, (u32)cmd->device->channel, (u32)cmd->device->id, (u32)cmd->device->lun, + printk(KERN_WARNING"%s: SCSI error %0x-Device(%d,%d,%llu) hba_status=0x%x, dev_status=0x%x, cmd=0x%x\n", + pHba->name, detailed_status & I2O_SCSI_DSC_MASK, (u32)cmd->device->channel, (u32)cmd->device->id, cmd->device->lun, hba_status, dev_status, cmd->cmnd[0]); cmd->result = (DID_ERROR << 16); break; @@ -2464,8 +2464,8 @@ static s32 adpt_i2o_to_scsi(void __iomem *reply, struct scsi_cmnd* cmd) cmd->sense_buffer[2] == DATA_PROTECT ){ /* This is to handle an array failed */ cmd->result = (DID_TIME_OUT << 16); - printk(KERN_WARNING"%s: SCSI Data Protect-Device (%d,%d,%d) hba_status=0x%x, dev_status=0x%x, cmd=0x%x\n", - pHba->name, (u32)cmd->device->channel, (u32)cmd->device->id, (u32)cmd->device->lun, + printk(KERN_WARNING"%s: SCSI Data Protect-Device (%d,%d,%llu) hba_status=0x%x, dev_status=0x%x, cmd=0x%x\n", + pHba->name, (u32)cmd->device->channel, (u32)cmd->device->id, cmd->device->lun, hba_status, dev_status, cmd->cmnd[0]); } @@ -2476,8 +2476,8 @@ static s32 adpt_i2o_to_scsi(void __iomem *reply, struct scsi_cmnd* cmd) * for a limitted number of retries. */ cmd->result = (DID_TIME_OUT << 16); - printk(KERN_WARNING"%s: I2O MSG_FAIL - Device (%d,%d,%d) tid=%d, cmd=0x%x\n", - pHba->name, (u32)cmd->device->channel, (u32)cmd->device->id, (u32)cmd->device->lun, + printk(KERN_WARNING"%s: I2O MSG_FAIL - Device (%d,%d,%llu) tid=%d, cmd=0x%x\n", + pHba->name, (u32)cmd->device->channel, (u32)cmd->device->id, cmd->device->lun, ((struct adpt_device*)(cmd->device->hostdata))->tid, cmd->cmnd[0]); } @@ -2517,7 +2517,7 @@ static s32 adpt_i2o_reparse_lct(adpt_hba* pHba) i2o_lct *lct = pHba->lct; u8 bus_no = 0; s16 scsi_id; - s16 scsi_lun; + u64 scsi_lun; u32 buf[10]; // at least 8 u32's struct adpt_device* pDev = NULL; struct i2o_device* pI2o_dev = NULL; @@ -2564,7 +2564,7 @@ static s32 adpt_i2o_reparse_lct(adpt_hba* pHba) } scsi_id = buf[1]; - scsi_lun = (buf[2]>>8 )&0xff; + scsi_lun = scsilun_to_int((struct scsi_lun *)&buf[2]); pDev = pHba->channel[bus_no].device[scsi_id]; /* da lun */ while(pDev) { @@ -2633,7 +2633,7 @@ static s32 adpt_i2o_reparse_lct(adpt_hba* pHba) while(pDev) { if(pDev->scsi_lun == scsi_lun) { if(!scsi_device_online(pDev->pScsi_dev)) { - printk(KERN_WARNING"%s: Setting device (%d,%d,%d) back online\n", + printk(KERN_WARNING"%s: Setting device (%d,%d,%llu) back online\n", pHba->name,bus_no,scsi_id,scsi_lun); if (pDev->pScsi_dev) { scsi_device_set_state(pDev->pScsi_dev, SDEV_RUNNING); @@ -2665,7 +2665,7 @@ static s32 adpt_i2o_reparse_lct(adpt_hba* pHba) // in the LCT table if (pDev->state & DPTI_DEV_UNSCANNED){ pDev->state = DPTI_DEV_OFFLINE; - printk(KERN_WARNING"%s: Device (%d,%d,%d) offline\n",pHba->name,pDev->scsi_channel,pDev->scsi_id,pDev->scsi_lun); + printk(KERN_WARNING"%s: Device (%d,%d,%llu) offline\n",pHba->name,pDev->scsi_channel,pDev->scsi_id,pDev->scsi_lun); if (pDev->pScsi_dev) { scsi_device_set_state(pDev->pScsi_dev, SDEV_OFFLINE); } diff --git a/drivers/scsi/dpti.h b/drivers/scsi/dpti.h index aeb046186c84..1fa345ab8ecb 100644 --- a/drivers/scsi/dpti.h +++ b/drivers/scsi/dpti.h @@ -184,7 +184,7 @@ struct adpt_device { u32 block_size; u8 scsi_channel; u8 scsi_id; - u8 scsi_lun; + u64 scsi_lun; u8 state; u16 tid; struct i2o_device* pI2o_dev; @@ -231,7 +231,7 @@ typedef struct _adpt_hba { u32 sg_tablesize; // Scatter/Gather List Size. u8 top_scsi_channel; u8 top_scsi_id; - u8 top_scsi_lun; + u64 top_scsi_lun; u8 dma64; i2o_status_block* status_block; @@ -300,7 +300,7 @@ static s32 adpt_send_nop(adpt_hba*pHba,u32 m); static void adpt_i2o_delete_hba(adpt_hba* pHba); static void adpt_inquiry(adpt_hba* pHba); static void adpt_fail_posted_scbs(adpt_hba* pHba); -static struct adpt_device* adpt_find_device(adpt_hba* pHba, u32 chan, u32 id, u32 lun); +static struct adpt_device* adpt_find_device(adpt_hba* pHba, u32 chan, u32 id, u64 lun); static int adpt_install_hba(struct scsi_host_template* sht, struct pci_dev* pDev) ; static int adpt_i2o_online_hba(adpt_hba* pHba); static void adpt_i2o_post_wait_complete(u32, int); diff --git a/drivers/scsi/eata.c b/drivers/scsi/eata.c index ebf57364df91..0f27ac19cf01 100644 --- a/drivers/scsi/eata.c +++ b/drivers/scsi/eata.c @@ -2449,7 +2449,7 @@ static irqreturn_t ihdlr(struct Scsi_Host *shost) "target_status 0x%x, sense key 0x%x.\n", ha->board_name, SCpnt->device->channel, SCpnt->device->id, - SCpnt->device->lun, + (u8)SCpnt->device->lun, spp->target_status, SCpnt->sense_buffer[2]); ha->target_to[SCpnt->device->id][SCpnt->device->channel] = 0; diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c index ea28b5ca4c73..3f88f56582a2 100644 --- a/drivers/scsi/fnic/fnic_scsi.c +++ b/drivers/scsi/fnic/fnic_scsi.c @@ -1753,7 +1753,7 @@ int fnic_abort_cmd(struct scsi_cmnd *sc) tag = sc->request->tag; FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host, - "Abort Cmd called FCID 0x%x, LUN 0x%x TAG %x flags %x\n", + "Abort Cmd called FCID 0x%x, LUN 0x%llx TAG %x flags %x\n", rport->port_id, sc->device->lun, tag, CMD_FLAGS(sc)); CMD_FLAGS(sc) = FNIC_NO_FLAGS; @@ -2207,7 +2207,7 @@ int fnic_device_reset(struct scsi_cmnd *sc) rport = starget_to_rport(scsi_target(sc->device)); FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host, - "Device reset called FCID 0x%x, LUN 0x%x sc 0x%p\n", + "Device reset called FCID 0x%x, LUN 0x%llx sc 0x%p\n", rport->port_id, sc->device->lun, sc); if (lp->state != LPORT_ST_READY || !(lp->link_up)) diff --git a/drivers/scsi/g_NCR5380.c b/drivers/scsi/g_NCR5380.c index a1bc8ca958e1..b331272e93bc 100644 --- a/drivers/scsi/g_NCR5380.c +++ b/drivers/scsi/g_NCR5380.c @@ -768,7 +768,7 @@ static void sprint_command(struct seq_file *m, unsigned char *command) static void sprint_Scsi_Cmnd(struct seq_file *m, Scsi_Cmnd * cmd) { - PRINTP("host number %d destination target %d, lun %d\n" ANDP cmd->device->host->host_no ANDP cmd->device->id ANDP cmd->device->lun); + PRINTP("host number %d destination target %d, lun %llu\n" ANDP cmd->device->host->host_no ANDP cmd->device->id ANDP cmd->device->lun); PRINTP(" command = "); sprint_command(m, cmd->cmnd); } diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 31184b35370f..a59e1e022e37 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -4590,7 +4590,7 @@ static int hpsa_eh_abort_handler(struct scsi_cmnd *sc) return FAILED; memset(msg, 0, sizeof(msg)); - ml += sprintf(msg+ml, "ABORT REQUEST on C%d:B%d:T%d:L%d ", + ml += sprintf(msg+ml, "ABORT REQUEST on C%d:B%d:T%d:L%llu ", h->scsi_host->host_no, sc->device->channel, sc->device->id, sc->device->lun); diff --git a/drivers/scsi/hptiop.c b/drivers/scsi/hptiop.c index ee196b363d81..dedb62c21b29 100644 --- a/drivers/scsi/hptiop.c +++ b/drivers/scsi/hptiop.c @@ -1024,7 +1024,7 @@ static int hptiop_queuecommand_lck(struct scsi_cmnd *scp, _req->scp = scp; - dprintk("hptiop_queuecmd(scp=%p) %d/%d/%d/%d cdb=(%08x-%08x-%08x-%08x) " + dprintk("hptiop_queuecmd(scp=%p) %d/%d/%d/%llu cdb=(%08x-%08x-%08x-%08x) " "req_index=%d, req=%p\n", scp, host->host_no, scp->device->channel, diff --git a/drivers/scsi/in2000.c b/drivers/scsi/in2000.c index b1c4d831137d..ddf0694d87f0 100644 --- a/drivers/scsi/in2000.c +++ b/drivers/scsi/in2000.c @@ -2251,14 +2251,14 @@ static int in2000_show_info(struct seq_file *m, struct Scsi_Host *instance) seq_printf(m, "\nconnected: "); if (hd->connected) { cmd = (Scsi_Cmnd *) hd->connected; - seq_printf(m, " %d:%d(%02x)", cmd->device->id, cmd->device->lun, cmd->cmnd[0]); + seq_printf(m, " %d:%llu(%02x)", cmd->device->id, cmd->device->lun, cmd->cmnd[0]); } } if (hd->proc & PR_INPUTQ) { seq_printf(m, "\ninput_Q: "); cmd = (Scsi_Cmnd *) hd->input_Q; while (cmd) { - seq_printf(m, " %d:%d(%02x)", cmd->device->id, cmd->device->lun, cmd->cmnd[0]); + seq_printf(m, " %d:%llu(%02x)", cmd->device->id, cmd->device->lun, cmd->cmnd[0]); cmd = (Scsi_Cmnd *) cmd->host_scribble; } } @@ -2266,7 +2266,7 @@ static int in2000_show_info(struct seq_file *m, struct Scsi_Host *instance) seq_printf(m, "\ndisconnected_Q:"); cmd = (Scsi_Cmnd *) hd->disconnected_Q; while (cmd) { - seq_printf(m, " %d:%d(%02x)", cmd->device->id, cmd->device->lun, cmd->cmnd[0]); + seq_printf(m, " %d:%llu(%02x)", cmd->device->id, cmd->device->lun, cmd->cmnd[0]); cmd = (Scsi_Cmnd *) cmd->host_scribble; } } diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 3d1bc67bac9d..f2db82beb646 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -260,7 +260,7 @@ static int iscsi_check_tmf_restrictions(struct iscsi_task *task, int opcode) { struct iscsi_conn *conn = task->conn; struct iscsi_tm *tmf = &conn->tmhdr; - unsigned int hdr_lun; + u64 hdr_lun; if (conn->tmf_state == TMF_INITIAL) return 0; @@ -1859,8 +1859,7 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn, * Fail commands. session lock held and recv side suspended and xmit * thread flushed */ -static void fail_scsi_tasks(struct iscsi_conn *conn, unsigned lun, - int error) +static void fail_scsi_tasks(struct iscsi_conn *conn, u64 lun, int error) { struct iscsi_task *task; int i; @@ -2279,7 +2278,8 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc) cls_session = starget_to_session(scsi_target(sc->device)); session = cls_session->dd_data; - ISCSI_DBG_EH(session, "LU Reset [sc %p lun %u]\n", sc, sc->device->lun); + ISCSI_DBG_EH(session, "LU Reset [sc %p lun %llu]\n", sc, + sc->device->lun); mutex_lock(&session->eh_mutex); spin_lock_bh(&session->frwd_lock); diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 25d0f127424d..7d02a19419a7 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -404,7 +404,7 @@ static int sas_recover_lu(struct domain_device *dev, struct scsi_cmnd *cmd) int_to_scsilun(cmd->device->lun, &lun); - SAS_DPRINTK("eh: device %llx LUN %x has the task\n", + SAS_DPRINTK("eh: device %llx LUN %llx has the task\n", SAS_ADDR(dev->sas_addr), cmd->device->lun); @@ -490,7 +490,8 @@ static void sas_wait_eh(struct domain_device *dev) } EXPORT_SYMBOL(sas_wait_eh); -static int sas_queue_reset(struct domain_device *dev, int reset_type, int lun, int wait) +static int sas_queue_reset(struct domain_device *dev, int reset_type, + u64 lun, int wait) { struct sas_ha_struct *ha = dev->port->ha; int scheduled = 0, tries = 100; @@ -689,7 +690,7 @@ static void sas_eh_handle_sas_errors(struct Scsi_Host *shost, struct list_head * reset: tmf_resp = sas_recover_lu(task->dev, cmd); if (tmf_resp == TMF_RESP_FUNC_COMPLETE) { - SAS_DPRINTK("dev %016llx LU %x is " + SAS_DPRINTK("dev %016llx LU %llx is " "recovered\n", SAS_ADDR(task->dev), cmd->device->lun); @@ -742,7 +743,7 @@ static void sas_eh_handle_sas_errors(struct Scsi_Host *shost, struct list_head * * of effort could recover from errors. Quite * possibly the HA just disappeared. */ - SAS_DPRINTK("error from device %llx, LUN %x " + SAS_DPRINTK("error from device %llx, LUN %llx " "couldn't be recovered in any way\n", SAS_ADDR(task->dev->sas_addr), cmd->device->lun); @@ -941,7 +942,7 @@ int sas_slave_configure(struct scsi_device *scsi_dev) scsi_set_tag_type(scsi_dev, MSG_SIMPLE_TAG); scsi_activate_tcq(scsi_dev, SAS_DEF_QD); } else { - SAS_DPRINTK("device %llx, LUN %x doesn't support " + SAS_DPRINTK("device %llx, LUN %llx doesn't support " "TCQ\n", SAS_ADDR(dev->sas_addr), scsi_dev->lun); scsi_dev->tagged_supported = 0; diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 2df11daad85b..7862c5540861 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -258,7 +258,7 @@ static void lpfc_send_sdev_queuedepth_change_event(struct lpfc_hba *phba, struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, - uint32_t lun, + uint64_t lun, uint32_t old_val, uint32_t new_val) { @@ -3823,7 +3823,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, if (rsplen != 0 && rsplen != 4 && rsplen != 8) { lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, "2719 Invalid response length: " - "tgt x%x lun x%x cmnd x%x rsplen x%x\n", + "tgt x%x lun x%llx cmnd x%x rsplen x%x\n", cmnd->device->id, cmnd->device->lun, cmnd->cmnd[0], rsplen); @@ -3834,7 +3834,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, "2757 Protocol failure detected during " "processing of FCP I/O op: " - "tgt x%x lun x%x cmnd x%x rspInfo3 x%x\n", + "tgt x%x lun x%llx cmnd x%x rspInfo3 x%x\n", cmnd->device->id, cmnd->device->lun, cmnd->cmnd[0], fcprsp->rspInfo3); @@ -4045,7 +4045,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, else logit = LOG_FCP | LOG_FCP_UNDER; lpfc_printf_vlog(vport, KERN_WARNING, logit, - "9030 FCP cmd x%x failed <%d/%d> " + "9030 FCP cmd x%x failed <%d/%lld> " "status: x%x result: x%x " "sid: x%x did: x%x oxid: x%x " "Data: x%x x%x\n", @@ -4157,7 +4157,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, uint32_t *lp = (uint32_t *)cmd->sense_buffer; lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP, - "0710 Iodone <%d/%d> cmd %p, error " + "0710 Iodone <%d/%llu> cmd %p, error " "x%x SNS x%x x%x Data: x%x x%x\n", cmd->device->id, cmd->device->lun, cmd, cmd->result, *lp, *(lp + 3), cmd->retries, @@ -4390,7 +4390,7 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, static int lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, - unsigned int lun, + uint64_t lun, uint8_t task_mgmt_cmd) { struct lpfc_iocbq *piocbq; @@ -4719,12 +4719,12 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd) atomic_dec(&ndlp->cmd_pending); lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP, "3376 FCP could not issue IOCB err %x" - "FCP cmd x%x <%d/%d> " + "FCP cmd x%x <%d/%llu> " "sid: x%x did: x%x oxid: x%x " "Data: x%x x%x x%x x%x\n", err, cmnd->cmnd[0], cmnd->device ? cmnd->device->id : 0xffff, - cmnd->device ? cmnd->device->lun : 0xffff, + cmnd->device ? cmnd->device->lun : (u64) -1, vport->fc_myDID, ndlp->nlp_DID, phba->sli_rev == LPFC_SLI_REV4 ? lpfc_cmd->cur_iocbq.sli4_xritag : 0xffff, @@ -4807,7 +4807,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) spin_unlock_irqrestore(&phba->hbalock, flags); lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP, "2873 SCSI Layer I/O Abort Request IO CMPL Status " - "x%x ID %d LUN %d\n", + "x%x ID %d LUN %llu\n", SUCCESS, cmnd->device->id, cmnd->device->lun); return SUCCESS; } @@ -4924,7 +4924,7 @@ wait_for_cmpl: lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, "0748 abort handler timed out waiting " "for abortng I/O (xri:x%x) to complete: " - "ret %#x, ID %d, LUN %d\n", + "ret %#x, ID %d, LUN %llu\n", iocb->sli4_xritag, ret, cmnd->device->id, cmnd->device->lun); } @@ -4935,7 +4935,7 @@ out_unlock: out: lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP, "0749 SCSI Layer I/O Abort Request Status x%x ID %d " - "LUN %d\n", ret, cmnd->device->id, + "LUN %llu\n", ret, cmnd->device->id, cmnd->device->lun); return ret; } @@ -5047,7 +5047,7 @@ lpfc_check_fcp_rsp(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd) **/ static int lpfc_send_taskmgmt(struct lpfc_vport *vport, struct lpfc_rport_data *rdata, - unsigned tgt_id, unsigned int lun_id, + unsigned tgt_id, uint64_t lun_id, uint8_t task_mgmt_cmd) { struct lpfc_hba *phba = vport->phba; @@ -5083,7 +5083,7 @@ lpfc_send_taskmgmt(struct lpfc_vport *vport, struct lpfc_rport_data *rdata, iocbq->iocb_cmpl = lpfc_tskmgmt_def_cmpl; lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP, - "0702 Issue %s to TGT %d LUN %d " + "0702 Issue %s to TGT %d LUN %llu " "rpi x%x nlp_flag x%x Data: x%x x%x\n", lpfc_taskmgmt_name(task_mgmt_cmd), tgt_id, lun_id, pnode->nlp_rpi, pnode->nlp_flag, iocbq->sli4_xritag, @@ -5094,7 +5094,7 @@ lpfc_send_taskmgmt(struct lpfc_vport *vport, struct lpfc_rport_data *rdata, if ((status != IOCB_SUCCESS) || (iocbqrsp->iocb.ulpStatus != IOSTAT_SUCCESS)) { lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, - "0727 TMF %s to TGT %d LUN %d failed (%d, %d) " + "0727 TMF %s to TGT %d LUN %llu failed (%d, %d) " "iocb_flag x%x\n", lpfc_taskmgmt_name(task_mgmt_cmd), tgt_id, lun_id, iocbqrsp->iocb.ulpStatus, @@ -5238,7 +5238,7 @@ lpfc_device_reset_handler(struct scsi_cmnd *cmnd) struct lpfc_rport_data *rdata; struct lpfc_nodelist *pnode; unsigned tgt_id = cmnd->device->id; - unsigned int lun_id = cmnd->device->lun; + uint64_t lun_id = cmnd->device->lun; struct lpfc_scsi_event_header scsi_event; int status; @@ -5273,7 +5273,7 @@ lpfc_device_reset_handler(struct scsi_cmnd *cmnd) FCP_LUN_RESET); lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, - "0713 SCSI layer issued Device Reset (%d, %d) " + "0713 SCSI layer issued Device Reset (%d, %llu) " "return x%x\n", tgt_id, lun_id, status); /* @@ -5308,7 +5308,7 @@ lpfc_target_reset_handler(struct scsi_cmnd *cmnd) struct lpfc_rport_data *rdata; struct lpfc_nodelist *pnode; unsigned tgt_id = cmnd->device->id; - unsigned int lun_id = cmnd->device->lun; + uint64_t lun_id = cmnd->device->lun; struct lpfc_scsi_event_header scsi_event; int status; @@ -5343,7 +5343,7 @@ lpfc_target_reset_handler(struct scsi_cmnd *cmnd) FCP_TARGET_RESET); lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, - "0723 SCSI layer issued Target Reset (%d, %d) " + "0723 SCSI layer issued Target Reset (%d, %llu) " "return x%x\n", tgt_id, lun_id, status); /* diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c index b7770516f4c2..6bb2b52646b4 100644 --- a/drivers/scsi/megaraid.c +++ b/drivers/scsi/megaraid.c @@ -1941,8 +1941,8 @@ megaraid_abort_and_reset(adapter_t *adapter, Scsi_Cmnd *cmd, int aor) printk(KERN_WARNING "megaraid: %s cmd=%x \n", (aor == SCB_ABORT)? "ABORTING":"RESET", - cmd->cmnd[0], cmd->device->channel, - cmd->device->id, cmd->device->lun); + cmd->cmnd[0], cmd->device->channel, + cmd->device->id, (u32)cmd->device->lun); if(list_empty(&adapter->pending_list)) return FALSE; diff --git a/drivers/scsi/megaraid/mega_common.h b/drivers/scsi/megaraid/mega_common.h index 5ead1283a844..1d037ed52c33 100644 --- a/drivers/scsi/megaraid/mega_common.h +++ b/drivers/scsi/megaraid/mega_common.h @@ -204,7 +204,7 @@ typedef struct { #define SCP2HOSTDATA(scp) SCP2HOST(scp)->hostdata // to soft state #define SCP2CHANNEL(scp) (scp)->device->channel // to channel #define SCP2TARGET(scp) (scp)->device->id // to target -#define SCP2LUN(scp) (scp)->device->lun // to LUN +#define SCP2LUN(scp) (u32)(scp)->device->lun // to LUN // generic macro to convert scsi command and host to controller's soft state #define SCSIHOST2ADAP(host) (((caddr_t *)(host->hostdata))[0]) diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index 22600419ae9f..3ed03dfab76c 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -1690,7 +1690,7 @@ NonFastPath: MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); } io_request->RaidContext.VirtualDiskTgtId = cpu_to_le16(device_id); - io_request->LUN[1] = scmd->device->lun; + int_to_scsilun(scmd->device->lun, (struct scsi_lun *)io_request->LUN); } /** @@ -1713,7 +1713,7 @@ megasas_build_io_fusion(struct megasas_instance *instance, device_id = MEGASAS_DEV_INDEX(instance, scp); /* Zero out some fields so they don't get reused */ - io_request->LUN[1] = 0; + memset(io_request->LUN, 0x0, 8); io_request->CDB.EEDP32.PrimaryReferenceTag = 0; io_request->CDB.EEDP32.PrimaryApplicationTagMask = 0; io_request->EEDPFlags = 0; diff --git a/drivers/scsi/mesh.c b/drivers/scsi/mesh.c index e8a04ae3276a..7a6160f172ce 100644 --- a/drivers/scsi/mesh.c +++ b/drivers/scsi/mesh.c @@ -1230,7 +1230,7 @@ static void handle_msgin(struct mesh_state *ms) ms->msgphase = msg_out; } else if (code != cmd->device->lun + IDENTIFY_BASE) { printk(KERN_WARNING "mesh: lun mismatch " - "(%d != %d) on reselection from " + "(%d != %llu) on reselection from " "target %d\n", code - IDENTIFY_BASE, cmd->device->lun, ms->conn_tgt); } diff --git a/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c index 7d014b11df62..a7305ffc359d 100644 --- a/drivers/scsi/ncr53c8xx.c +++ b/drivers/scsi/ncr53c8xx.c @@ -6633,7 +6633,7 @@ static void ncr_sir_to_redo(struct ncb *np, int num, struct ccb *cp) ** patch requested size into sense command */ cp->sensecmd[0] = 0x03; - cp->sensecmd[1] = cmd->device->lun << 5; + cp->sensecmd[1] = (cmd->device->lun & 0x7) << 5; cp->sensecmd[4] = sizeof(cp->sense_buf); /* diff --git a/drivers/scsi/nsp32.c b/drivers/scsi/nsp32.c index 0665f9cfdb02..50b086aef178 100644 --- a/drivers/scsi/nsp32.c +++ b/drivers/scsi/nsp32.c @@ -915,7 +915,7 @@ static int nsp32_queuecommand_lck(struct scsi_cmnd *SCpnt, void (*done)(struct s int ret; nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND, - "enter. target: 0x%x LUN: 0x%x cmnd: 0x%x cmndlen: 0x%x " + "enter. target: 0x%x LUN: 0x%llu cmnd: 0x%x cmndlen: 0x%x " "use_sg: 0x%x reqbuf: 0x%lx reqlen: 0x%x", SCpnt->device->id, SCpnt->device->lun, SCpnt->cmnd[0], SCpnt->cmd_len, scsi_sg_count(SCpnt), scsi_sglist(SCpnt), scsi_bufflen(SCpnt)); diff --git a/drivers/scsi/pcmcia/nsp_cs.c b/drivers/scsi/pcmcia/nsp_cs.c index 987fbb1b244e..340ceff03823 100644 --- a/drivers/scsi/pcmcia/nsp_cs.c +++ b/drivers/scsi/pcmcia/nsp_cs.c @@ -195,7 +195,7 @@ static int nsp_queuecommand_lck(struct scsi_cmnd *SCpnt, nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata; nsp_dbg(NSP_DEBUG_QUEUECOMMAND, - "SCpnt=0x%p target=%d lun=%d sglist=0x%p bufflen=%d sg_count=%d", + "SCpnt=0x%p target=%d lun=%llu sglist=0x%p bufflen=%d sg_count=%d", SCpnt, target, SCpnt->device->lun, scsi_sglist(SCpnt), scsi_bufflen(SCpnt), scsi_sg_count(SCpnt)); //nsp_dbg(NSP_DEBUG_QUEUECOMMAND, "before CurrentSC=0x%p", data->CurrentSC); diff --git a/drivers/scsi/pcmcia/sym53c500_cs.c b/drivers/scsi/pcmcia/sym53c500_cs.c index f5b52731abd9..155f9573021f 100644 --- a/drivers/scsi/pcmcia/sym53c500_cs.c +++ b/drivers/scsi/pcmcia/sym53c500_cs.c @@ -558,7 +558,7 @@ SYM53C500_queue_lck(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) DEB(printk("cmd=%02x, cmd_len=%02x, target=%02x, lun=%02x, bufflen=%d\n", SCpnt->cmnd[0], SCpnt->cmd_len, SCpnt->device->id, - SCpnt->device->lun, scsi_bufflen(SCpnt))); + (u8)SCpnt->device->lun, scsi_bufflen(SCpnt))); VDEB(for (i = 0; i < SCpnt->cmd_len; i++) printk("cmd[%d]=%02x ", i, SCpnt->cmnd[i])); diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c index be8ce54f99b2..017f8b9554e5 100644 --- a/drivers/scsi/pmcraid.c +++ b/drivers/scsi/pmcraid.c @@ -237,7 +237,7 @@ static int pmcraid_slave_configure(struct scsi_device *scsi_dev) scsi_dev->host->unique_id, scsi_dev->channel, scsi_dev->id, - scsi_dev->lun); + (u8)scsi_dev->lun); if (RES_IS_GSCSI(res->cfg_entry)) { scsi_dev->allow_restart = 1; diff --git a/drivers/scsi/ps3rom.c b/drivers/scsi/ps3rom.c index e6e2a30493e6..ef23fabe3924 100644 --- a/drivers/scsi/ps3rom.c +++ b/drivers/scsi/ps3rom.c @@ -78,7 +78,7 @@ static int ps3rom_slave_configure(struct scsi_device *scsi_dev) struct ps3rom_private *priv = shost_priv(scsi_dev->host); struct ps3_storage_device *dev = priv->dev; - dev_dbg(&dev->sbd.core, "%s:%u: id %u, lun %u, channel %u\n", __func__, + dev_dbg(&dev->sbd.core, "%s:%u: id %u, lun %llu, channel %u\n", __func__, __LINE__, scsi_dev->id, scsi_dev->lun, scsi_dev->channel); /* diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index de5d0ae19d83..b64399153135 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -320,8 +320,8 @@ struct srb_iocb { * defined in tsk_mgmt_entry struct * for control_flags field in qla_fw.h. */ + uint64_t lun; uint32_t flags; - uint32_t lun; uint32_t data; struct completion comp; __le16 comp_status; @@ -2529,8 +2529,8 @@ struct isp_operations { void (*disable_intrs) (struct qla_hw_data *); int (*abort_command) (srb_t *); - int (*target_reset) (struct fc_port *, unsigned int, int); - int (*lun_reset) (struct fc_port *, unsigned int, int); + int (*target_reset) (struct fc_port *, uint64_t, int); + int (*lun_reset) (struct fc_port *, uint64_t, int); int (*fabric_login) (struct scsi_qla_host *, uint16_t, uint8_t, uint8_t, uint8_t, uint16_t *, uint8_t); int (*fabric_logout) (struct scsi_qla_host *, uint16_t, uint8_t, diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index d48dea8fab1b..9ef0781b12da 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -212,7 +212,7 @@ extern void qla2x00_build_scsi_iocbs_64(srb_t *, cmd_entry_t *, uint16_t); extern int qla2x00_start_scsi(srb_t *sp); extern int qla24xx_start_scsi(srb_t *sp); int qla2x00_marker(struct scsi_qla_host *, struct req_que *, struct rsp_que *, - uint16_t, uint16_t, uint8_t); + uint16_t, uint64_t, uint8_t); extern int qla2x00_start_sp(srb_t *); extern int qla24xx_dif_start_scsi(srb_t *); extern int qla2x00_start_bidir(srb_t *, struct scsi_qla_host *, uint32_t); @@ -262,10 +262,10 @@ extern int qla2x00_abort_command(srb_t *); extern int -qla2x00_abort_target(struct fc_port *, unsigned int, int); +qla2x00_abort_target(struct fc_port *, uint64_t, int); extern int -qla2x00_lun_reset(struct fc_port *, unsigned int, int); +qla2x00_lun_reset(struct fc_port *, uint64_t, int); extern int qla2x00_get_adapter_id(scsi_qla_host_t *, uint16_t *, uint8_t *, uint8_t *, @@ -339,12 +339,12 @@ qla24xx_get_isp_stats(scsi_qla_host_t *, struct link_statistics *, extern int qla24xx_abort_command(srb_t *); extern int qla24xx_async_abort_command(srb_t *); extern int -qla24xx_abort_target(struct fc_port *, unsigned int, int); +qla24xx_abort_target(struct fc_port *, uint64_t, int); extern int -qla24xx_lun_reset(struct fc_port *, unsigned int, int); +qla24xx_lun_reset(struct fc_port *, uint64_t, int); extern int qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *, unsigned int, - unsigned int, enum nexus_wait_type); + uint64_t, enum nexus_wait_type); extern int qla2x00_system_error(scsi_qla_host_t *); @@ -617,8 +617,8 @@ extern char *qlafx00_fw_version_str(struct scsi_qla_host *, char *); extern irqreturn_t qlafx00_intr_handler(int, void *); extern void qlafx00_enable_intrs(struct qla_hw_data *); extern void qlafx00_disable_intrs(struct qla_hw_data *); -extern int qlafx00_abort_target(fc_port_t *, unsigned int, int); -extern int qlafx00_lun_reset(fc_port_t *, unsigned int, int); +extern int qlafx00_abort_target(fc_port_t *, uint64_t, int); +extern int qlafx00_lun_reset(fc_port_t *, uint64_t, int); extern int qlafx00_start_scsi(srb_t *); extern int qlafx00_abort_isp(scsi_qla_host_t *); extern int qlafx00_iospace_config(struct qla_hw_data *); diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index 760931529592..150529d98db4 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -520,7 +520,7 @@ qla2x00_start_iocbs(struct scsi_qla_host *vha, struct req_que *req) static int __qla2x00_marker(struct scsi_qla_host *vha, struct req_que *req, struct rsp_que *rsp, uint16_t loop_id, - uint16_t lun, uint8_t type) + uint64_t lun, uint8_t type) { mrk_entry_t *mrk; struct mrk_entry_24xx *mrk24 = NULL; @@ -543,14 +543,13 @@ __qla2x00_marker(struct scsi_qla_host *vha, struct req_que *req, if (IS_FWI2_CAPABLE(ha)) { mrk24 = (struct mrk_entry_24xx *) mrk; mrk24->nport_handle = cpu_to_le16(loop_id); - mrk24->lun[1] = LSB(lun); - mrk24->lun[2] = MSB(lun); + int_to_scsilun(lun, (struct scsi_lun *)&mrk24->lun); host_to_fcp_swap(mrk24->lun, sizeof(mrk24->lun)); mrk24->vp_index = vha->vp_idx; mrk24->handle = MAKE_HANDLE(req->id, mrk24->handle); } else { SET_TARGET_ID(ha, mrk->target, loop_id); - mrk->lun = cpu_to_le16(lun); + mrk->lun = cpu_to_le16((uint16_t)lun); } } wmb(); @@ -562,7 +561,7 @@ __qla2x00_marker(struct scsi_qla_host *vha, struct req_que *req, int qla2x00_marker(struct scsi_qla_host *vha, struct req_que *req, - struct rsp_que *rsp, uint16_t loop_id, uint16_t lun, + struct rsp_que *rsp, uint16_t loop_id, uint64_t lun, uint8_t type) { int ret; @@ -2047,7 +2046,7 @@ static void qla24xx_tm_iocb(srb_t *sp, struct tsk_mgmt_entry *tsk) { uint32_t flags; - unsigned int lun; + uint64_t lun; struct fc_port *fcport = sp->fcport; scsi_qla_host_t *vha = fcport->vha; struct qla_hw_data *ha = vha->hw; diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index a56825c73c31..550a4a31f51a 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -1659,7 +1659,7 @@ qla2x00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t par_sense_len, if (sense_len) { ql_dbg(ql_dbg_io + ql_dbg_buffer, vha, 0x301c, - "Check condition Sense data, nexus%ld:%d:%d cmd=%p.\n", + "Check condition Sense data, nexus%ld:%d:%llu cmd=%p.\n", sp->fcport->vha->host_no, cp->device->id, cp->device->lun, cp); ql_dump_buffer(ql_dbg_io + ql_dbg_buffer, vha, 0x302b, @@ -2281,7 +2281,7 @@ check_scsi_status: out: if (logit) ql_dbg(ql_dbg_io, fcport->vha, 0x3022, - "FCP command status: 0x%x-0x%x (0x%x) nexus=%ld:%d:%d " + "FCP command status: 0x%x-0x%x (0x%x) nexus=%ld:%d:%llu " "portid=%02x%02x%02x oxid=0x%x cdb=%10phN len=0x%x " "rsp_info=0x%x resid=0x%x fw_resid=0x%x.\n", comp_status, scsi_status, res, vha->host_no, diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index 1c33a77db5c2..d9aafc003be2 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -947,7 +947,7 @@ qla2x00_abort_command(srb_t *sp) } int -qla2x00_abort_target(struct fc_port *fcport, unsigned int l, int tag) +qla2x00_abort_target(struct fc_port *fcport, uint64_t l, int tag) { int rval, rval2; mbx_cmd_t mc; @@ -1000,7 +1000,7 @@ qla2x00_abort_target(struct fc_port *fcport, unsigned int l, int tag) } int -qla2x00_lun_reset(struct fc_port *fcport, unsigned int l, int tag) +qla2x00_lun_reset(struct fc_port *fcport, uint64_t l, int tag) { int rval, rval2; mbx_cmd_t mc; @@ -1022,7 +1022,7 @@ qla2x00_lun_reset(struct fc_port *fcport, unsigned int l, int tag) mcp->mb[1] = fcport->loop_id; else mcp->mb[1] = fcport->loop_id << 8; - mcp->mb[2] = l; + mcp->mb[2] = (u32)l; mcp->mb[3] = 0; mcp->mb[9] = vha->vp_idx; @@ -2666,7 +2666,7 @@ struct tsk_mgmt_cmd { static int __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport, - unsigned int l, int tag) + uint64_t l, int tag) { int rval, rval2; struct tsk_mgmt_cmd *tsk; @@ -2760,7 +2760,7 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport, } int -qla24xx_abort_target(struct fc_port *fcport, unsigned int l, int tag) +qla24xx_abort_target(struct fc_port *fcport, uint64_t l, int tag) { struct qla_hw_data *ha = fcport->vha->hw; @@ -2771,7 +2771,7 @@ qla24xx_abort_target(struct fc_port *fcport, unsigned int l, int tag) } int -qla24xx_lun_reset(struct fc_port *fcport, unsigned int l, int tag) +qla24xx_lun_reset(struct fc_port *fcport, uint64_t l, int tag) { struct qla_hw_data *ha = fcport->vha->hw; diff --git a/drivers/scsi/qla2xxx/qla_mr.c b/drivers/scsi/qla2xxx/qla_mr.c index abeb3901498b..4775baa8b6a0 100644 --- a/drivers/scsi/qla2xxx/qla_mr.c +++ b/drivers/scsi/qla2xxx/qla_mr.c @@ -726,13 +726,13 @@ qlafx00_disable_intrs(struct qla_hw_data *ha) } int -qlafx00_abort_target(fc_port_t *fcport, unsigned int l, int tag) +qlafx00_abort_target(fc_port_t *fcport, uint64_t l, int tag) { return qla2x00_async_tm_cmd(fcport, TCF_TARGET_RESET, l, tag); } int -qlafx00_lun_reset(fc_port_t *fcport, unsigned int l, int tag) +qlafx00_lun_reset(fc_port_t *fcport, uint64_t l, int tag) { return qla2x00_async_tm_cmd(fcport, TCF_LUN_RESET, l, tag); } @@ -2159,7 +2159,7 @@ qlafx00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t par_sense_len, if (sense_len) { ql_dbg(ql_dbg_io + ql_dbg_buffer, vha, 0x3039, - "Check condition Sense data, nexus%ld:%d:%d cmd=%p.\n", + "Check condition Sense data, nexus%ld:%d:%llu cmd=%p.\n", sp->fcport->vha->host_no, cp->device->id, cp->device->lun, cp); ql_dump_buffer(ql_dbg_io + ql_dbg_buffer, vha, 0x3049, @@ -2524,7 +2524,7 @@ check_scsi_status: if (logit) ql_dbg(ql_dbg_io, fcport->vha, 0x3058, - "FCP command status: 0x%x-0x%x (0x%x) nexus=%ld:%d:%d " + "FCP command status: 0x%x-0x%x (0x%x) nexus=%ld:%d:%llu " "tgt_id: 0x%x lscsi_status: 0x%x cdb=%10phN len=0x%x " "rsp_info=0x%x resid=0x%x fw_resid=0x%x sense_len=0x%x, " "par_sense_len=0x%x, rsp_info_len=0x%x\n", diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 5269aee1df3b..b22c75305b44 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -920,7 +920,8 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) scsi_qla_host_t *vha = shost_priv(cmd->device->host); srb_t *sp; int ret; - unsigned int id, lun; + unsigned int id; + uint64_t lun; unsigned long flags; int rval, wait = 0; struct qla_hw_data *ha = vha->hw; @@ -944,7 +945,7 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) } ql_dbg(ql_dbg_taskm, vha, 0x8002, - "Aborting from RISC nexus=%ld:%d:%d sp=%p cmd=%p\n", + "Aborting from RISC nexus=%ld:%d:%llu sp=%p cmd=%p\n", vha->host_no, id, lun, sp, cmd); /* Get a reference to the sp and drop the lock.*/ @@ -995,7 +996,7 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) } ql_log(ql_log_info, vha, 0x801c, - "Abort command issued nexus=%ld:%d:%d -- %d %x.\n", + "Abort command issued nexus=%ld:%d:%llu -- %d %x.\n", vha->host_no, id, lun, wait, ret); return ret; @@ -1003,7 +1004,7 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) int qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *vha, unsigned int t, - unsigned int l, enum nexus_wait_type type) + uint64_t l, enum nexus_wait_type type) { int cnt, match, status; unsigned long flags; @@ -1060,7 +1061,7 @@ static char *reset_errors[] = { static int __qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type, - struct scsi_cmnd *cmd, int (*do_reset)(struct fc_port *, unsigned int, int)) + struct scsi_cmnd *cmd, int (*do_reset)(struct fc_port *, uint64_t, int)) { scsi_qla_host_t *vha = shost_priv(cmd->device->host); fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata; @@ -1075,7 +1076,7 @@ __qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type, return err; ql_log(ql_log_info, vha, 0x8009, - "%s RESET ISSUED nexus=%ld:%d:%d cmd=%p.\n", name, vha->host_no, + "%s RESET ISSUED nexus=%ld:%d:%llu cmd=%p.\n", name, vha->host_no, cmd->device->id, cmd->device->lun, cmd); err = 0; @@ -1100,14 +1101,14 @@ __qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type, } ql_log(ql_log_info, vha, 0x800e, - "%s RESET SUCCEEDED nexus:%ld:%d:%d cmd=%p.\n", name, + "%s RESET SUCCEEDED nexus:%ld:%d:%llu cmd=%p.\n", name, vha->host_no, cmd->device->id, cmd->device->lun, cmd); return SUCCESS; eh_reset_failed: ql_log(ql_log_info, vha, 0x800f, - "%s RESET FAILED: %s nexus=%ld:%d:%d cmd=%p.\n", name, + "%s RESET FAILED: %s nexus=%ld:%d:%llu cmd=%p.\n", name, reset_errors[err], vha->host_no, cmd->device->id, cmd->device->lun, cmd); return FAILED; @@ -1154,7 +1155,8 @@ qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd) scsi_qla_host_t *vha = shost_priv(cmd->device->host); fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata; int ret = FAILED; - unsigned int id, lun; + unsigned int id; + uint64_t lun; id = cmd->device->id; lun = cmd->device->lun; @@ -1169,7 +1171,7 @@ qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd) ret = FAILED; ql_log(ql_log_info, vha, 0x8012, - "BUS RESET ISSUED nexus=%ld:%d:%d.\n", vha->host_no, id, lun); + "BUS RESET ISSUED nexus=%ld:%d:%llu.\n", vha->host_no, id, lun); if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) { ql_log(ql_log_fatal, vha, 0x8013, @@ -1193,7 +1195,7 @@ qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd) eh_bus_reset_done: ql_log(ql_log_warn, vha, 0x802b, - "BUS RESET %s nexus=%ld:%d:%d.\n", + "BUS RESET %s nexus=%ld:%d:%llu.\n", (ret == FAILED) ? "FAILED" : "SUCCEEDED", vha->host_no, id, lun); return ret; @@ -1220,14 +1222,15 @@ qla2xxx_eh_host_reset(struct scsi_cmnd *cmd) scsi_qla_host_t *vha = shost_priv(cmd->device->host); struct qla_hw_data *ha = vha->hw; int ret = FAILED; - unsigned int id, lun; + unsigned int id; + uint64_t lun; scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); id = cmd->device->id; lun = cmd->device->lun; ql_log(ql_log_info, vha, 0x8018, - "ADAPTER RESET ISSUED nexus=%ld:%d:%d.\n", vha->host_no, id, lun); + "ADAPTER RESET ISSUED nexus=%ld:%d:%llu.\n", vha->host_no, id, lun); /* * No point in issuing another reset if one is active. Also do not @@ -1273,7 +1276,7 @@ qla2xxx_eh_host_reset(struct scsi_cmnd *cmd) eh_host_reset_lock: ql_log(ql_log_info, vha, 0x8017, - "ADAPTER RESET %s nexus=%ld:%d:%d.\n", + "ADAPTER RESET %s nexus=%ld:%d:%llu.\n", (ret == FAILED) ? "FAILED" : "SUCCEEDED", vha->host_no, id, lun); return ret; @@ -1409,7 +1412,7 @@ static void qla2x00_handle_queue_full(struct scsi_device *sdev, int qdepth) return; ql_dbg(ql_dbg_io, fcport->vha, 0x3029, - "Queue depth adjusted-down to %d for nexus=%ld:%d:%d.\n", + "Queue depth adjusted-down to %d for nexus=%ld:%d:%llu.\n", sdev->queue_depth, fcport->vha->host_no, sdev->id, sdev->lun); } @@ -1432,7 +1435,7 @@ static void qla2x00_adjust_sdev_qdepth_up(struct scsi_device *sdev, int qdepth) scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, qdepth); ql_dbg(ql_dbg_io, vha, 0x302a, - "Queue depth adjusted-up to %d for nexus=%ld:%d:%d.\n", + "Queue depth adjusted-up to %d for nexus=%ld:%d:%llu.\n", sdev->queue_depth, fcport->vha->host_no, sdev->id, sdev->lun); } diff --git a/drivers/scsi/qla4xxx/ql4_glbl.h b/drivers/scsi/qla4xxx/ql4_glbl.h index 5f58b451327e..2559144f5475 100644 --- a/drivers/scsi/qla4xxx/ql4_glbl.h +++ b/drivers/scsi/qla4xxx/ql4_glbl.h @@ -23,7 +23,7 @@ void qla4xxx_process_aen(struct scsi_qla_host *ha, uint8_t process_aen); int qla4xxx_get_dhcp_ip_address(struct scsi_qla_host *ha); int qla4xxx_abort_task(struct scsi_qla_host *ha, struct srb *srb); int qla4xxx_reset_lun(struct scsi_qla_host *ha, struct ddb_entry *ddb_entry, - int lun); + uint64_t lun); int qla4xxx_reset_target(struct scsi_qla_host *ha, struct ddb_entry *ddb_entry); int qla4xxx_get_flash(struct scsi_qla_host *ha, dma_addr_t dma_addr, @@ -76,7 +76,7 @@ int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, uint32_t fw_ddb_index, uint32_t state, uint32_t conn_error); void qla4xxx_dump_buffer(void *b, uint32_t size); int qla4xxx_send_marker_iocb(struct scsi_qla_host *ha, - struct ddb_entry *ddb_entry, int lun, uint16_t mrkr_mod); + struct ddb_entry *ddb_entry, uint64_t lun, uint16_t mrkr_mod); int qla4xxx_set_flash(struct scsi_qla_host *ha, dma_addr_t dma_addr, uint32_t offset, uint32_t length, uint32_t options); int qla4xxx_mailbox_command(struct scsi_qla_host *ha, uint8_t inCount, diff --git a/drivers/scsi/qla4xxx/ql4_iocb.c b/drivers/scsi/qla4xxx/ql4_iocb.c index e5697ab144d2..08ab6dac226d 100644 --- a/drivers/scsi/qla4xxx/ql4_iocb.c +++ b/drivers/scsi/qla4xxx/ql4_iocb.c @@ -83,7 +83,7 @@ static int qla4xxx_get_req_pkt(struct scsi_qla_host *ha, * This routine issues a marker IOCB. **/ int qla4xxx_send_marker_iocb(struct scsi_qla_host *ha, - struct ddb_entry *ddb_entry, int lun, uint16_t mrkr_mod) + struct ddb_entry *ddb_entry, uint64_t lun, uint16_t mrkr_mod) { struct qla4_marker_entry *marker_entry; unsigned long flags = 0; diff --git a/drivers/scsi/qla4xxx/ql4_isr.c b/drivers/scsi/qla4xxx/ql4_isr.c index 081b6b78d2c6..4f9c0f2be89d 100644 --- a/drivers/scsi/qla4xxx/ql4_isr.c +++ b/drivers/scsi/qla4xxx/ql4_isr.c @@ -26,7 +26,7 @@ static void qla4xxx_copy_sense(struct scsi_qla_host *ha, memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE); sense_len = le16_to_cpu(sts_entry->senseDataByteCnt); if (sense_len == 0) { - DEBUG2(ql4_printk(KERN_INFO, ha, "scsi%ld:%d:%d:%d: %s:" + DEBUG2(ql4_printk(KERN_INFO, ha, "scsi%ld:%d:%d:%llu: %s:" " sense len 0\n", ha->host_no, cmd->device->channel, cmd->device->id, cmd->device->lun, __func__)); @@ -43,7 +43,7 @@ static void qla4xxx_copy_sense(struct scsi_qla_host *ha, sense_len = min_t(uint16_t, sense_len, IOCB_MAX_SENSEDATA_LEN); memcpy(cmd->sense_buffer, sts_entry->senseData, sense_len); - DEBUG2(printk(KERN_INFO "scsi%ld:%d:%d:%d: %s: sense key = %x, " + DEBUG2(printk(KERN_INFO "scsi%ld:%d:%d:%llu: %s: sense key = %x, " "ASL= %02x, ASC/ASCQ = %02x/%02x\n", ha->host_no, cmd->device->channel, cmd->device->id, cmd->device->lun, __func__, @@ -169,7 +169,7 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha, cmd->result = DID_ERROR << 16; - DEBUG2(printk("scsi%ld:%d:%d:%d: %s: " + DEBUG2(printk("scsi%ld:%d:%d:%llu: %s: " "Mid-layer Data underrun0, " "xferlen = 0x%x, " "residual = 0x%x\n", ha->host_no, @@ -197,7 +197,7 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha, break; case SCS_RESET_OCCURRED: - DEBUG2(printk("scsi%ld:%d:%d:%d: %s: Device RESET occurred\n", + DEBUG2(printk("scsi%ld:%d:%d:%llu: %s: Device RESET occurred\n", ha->host_no, cmd->device->channel, cmd->device->id, cmd->device->lun, __func__)); @@ -205,7 +205,7 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha, break; case SCS_ABORTED: - DEBUG2(printk("scsi%ld:%d:%d:%d: %s: Abort occurred\n", + DEBUG2(printk("scsi%ld:%d:%d:%llu: %s: Abort occurred\n", ha->host_no, cmd->device->channel, cmd->device->id, cmd->device->lun, __func__)); @@ -213,7 +213,7 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha, break; case SCS_TIMEOUT: - DEBUG2(printk(KERN_INFO "scsi%ld:%d:%d:%d: Timeout\n", + DEBUG2(printk(KERN_INFO "scsi%ld:%d:%d:%llu: Timeout\n", ha->host_no, cmd->device->channel, cmd->device->id, cmd->device->lun)); @@ -232,7 +232,7 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha, case SCS_DATA_OVERRUN: if ((sts_entry->iscsiFlags & ISCSI_FLAG_RESIDUAL_OVER) || (sts_entry->completionStatus == SCS_DATA_OVERRUN)) { - DEBUG2(printk("scsi%ld:%d:%d:%d: %s: " "Data overrun\n", + DEBUG2(printk("scsi%ld:%d:%d:%llu: %s: " "Data overrun\n", ha->host_no, cmd->device->channel, cmd->device->id, cmd->device->lun, __func__)); @@ -259,7 +259,7 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha, if (!scsi_status && (scsi_bufflen(cmd) - residual) < cmd->underflow) { DEBUG2(ql4_printk(KERN_INFO, ha, - "scsi%ld:%d:%d:%d: %s: Mid-layer Data underrun, xferlen = 0x%x,residual = 0x%x\n", + "scsi%ld:%d:%d:%llu: %s: Mid-layer Data underrun, xferlen = 0x%x,residual = 0x%x\n", ha->host_no, cmd->device->channel, cmd->device->id, @@ -291,7 +291,7 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha, */ DEBUG2(ql4_printk(KERN_INFO, ha, - "scsi%ld:%d:%d:%d: %s: Dropped frame(s) detected (0x%x of 0x%x bytes).\n", + "scsi%ld:%d:%d:%llu: %s: Dropped frame(s) detected (0x%x of 0x%x bytes).\n", ha->host_no, cmd->device->channel, cmd->device->id, @@ -313,7 +313,7 @@ check_scsi_status: case SCS_DEVICE_LOGGED_OUT: case SCS_DEVICE_UNAVAILABLE: - DEBUG2(printk(KERN_INFO "scsi%ld:%d:%d:%d: SCS_DEVICE " + DEBUG2(printk(KERN_INFO "scsi%ld:%d:%d:%llu: SCS_DEVICE " "state: 0x%x\n", ha->host_no, cmd->device->channel, cmd->device->id, cmd->device->lun, sts_entry->completionStatus)); @@ -333,7 +333,7 @@ check_scsi_status: * SCSI Mid-Layer handles device queue full */ cmd->result = DID_OK << 16 | sts_entry->scsiStatus; - DEBUG2(printk("scsi%ld:%d:%d: %s: QUEUE FULL detected " + DEBUG2(printk("scsi%ld:%d:%llu: %s: QUEUE FULL detected " "compl=%02x, scsi=%02x, state=%02x, iFlags=%02x," " iResp=%02x\n", ha->host_no, cmd->device->id, cmd->device->lun, __func__, diff --git a/drivers/scsi/qla4xxx/ql4_mbx.c b/drivers/scsi/qla4xxx/ql4_mbx.c index 0a3312c6dd6d..fdfae79924ac 100644 --- a/drivers/scsi/qla4xxx/ql4_mbx.c +++ b/drivers/scsi/qla4xxx/ql4_mbx.c @@ -1205,7 +1205,7 @@ int qla4xxx_abort_task(struct scsi_qla_host *ha, struct srb *srb) if (mbox_sts[0] != MBOX_STS_COMMAND_COMPLETE) { status = QLA_ERROR; - DEBUG2(printk(KERN_WARNING "scsi%ld:%d:%d: abort task FAILED: " + DEBUG2(printk(KERN_WARNING "scsi%ld:%d:%llu: abort task FAILED: " "mbx0=%04X, mb1=%04X, mb2=%04X, mb3=%04X, mb4=%04X\n", ha->host_no, cmd->device->id, cmd->device->lun, mbox_sts[0], mbox_sts[1], mbox_sts[2], mbox_sts[3], mbox_sts[4])); @@ -1225,14 +1225,14 @@ int qla4xxx_abort_task(struct scsi_qla_host *ha, struct srb *srb) * are valid before calling this routine. **/ int qla4xxx_reset_lun(struct scsi_qla_host * ha, struct ddb_entry * ddb_entry, - int lun) + uint64_t lun) { uint32_t mbox_cmd[MBOX_REG_COUNT]; uint32_t mbox_sts[MBOX_REG_COUNT]; uint32_t scsi_lun[2]; int status = QLA_SUCCESS; - DEBUG2(printk("scsi%ld:%d:%d: lun reset issued\n", ha->host_no, + DEBUG2(printk("scsi%ld:%d:%llu: lun reset issued\n", ha->host_no, ddb_entry->fw_ddb_index, lun)); /* diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index 320206376206..c5d9564d455c 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -9223,20 +9223,20 @@ static int qla4xxx_eh_abort(struct scsi_cmnd *cmd) { struct scsi_qla_host *ha = to_qla_host(cmd->device->host); unsigned int id = cmd->device->id; - unsigned int lun = cmd->device->lun; + uint64_t lun = cmd->device->lun; unsigned long flags; struct srb *srb = NULL; int ret = SUCCESS; int wait = 0; - ql4_printk(KERN_INFO, ha, "scsi%ld:%d:%d: Abort command issued cmd=%p, cdb=0x%x\n", + ql4_printk(KERN_INFO, ha, "scsi%ld:%d:%llu: Abort command issued cmd=%p, cdb=0x%x\n", ha->host_no, id, lun, cmd, cmd->cmnd[0]); spin_lock_irqsave(&ha->hardware_lock, flags); srb = (struct srb *) CMD_SP(cmd); if (!srb) { spin_unlock_irqrestore(&ha->hardware_lock, flags); - ql4_printk(KERN_INFO, ha, "scsi%ld:%d:%d: Specified command has already completed.\n", + ql4_printk(KERN_INFO, ha, "scsi%ld:%d:%llu: Specified command has already completed.\n", ha->host_no, id, lun); return SUCCESS; } @@ -9244,11 +9244,11 @@ static int qla4xxx_eh_abort(struct scsi_cmnd *cmd) spin_unlock_irqrestore(&ha->hardware_lock, flags); if (qla4xxx_abort_task(ha, srb) != QLA_SUCCESS) { - DEBUG3(printk("scsi%ld:%d:%d: Abort_task mbx failed.\n", + DEBUG3(printk("scsi%ld:%d:%llu: Abort_task mbx failed.\n", ha->host_no, id, lun)); ret = FAILED; } else { - DEBUG3(printk("scsi%ld:%d:%d: Abort_task mbx success.\n", + DEBUG3(printk("scsi%ld:%d:%llu: Abort_task mbx success.\n", ha->host_no, id, lun)); wait = 1; } @@ -9258,14 +9258,14 @@ static int qla4xxx_eh_abort(struct scsi_cmnd *cmd) /* Wait for command to complete */ if (wait) { if (!qla4xxx_eh_wait_on_command(ha, cmd)) { - DEBUG2(printk("scsi%ld:%d:%d: Abort handler timed out\n", + DEBUG2(printk("scsi%ld:%d:%llu: Abort handler timed out\n", ha->host_no, id, lun)); ret = FAILED; } } ql4_printk(KERN_INFO, ha, - "scsi%ld:%d:%d: Abort command - %s\n", + "scsi%ld:%d:%llu: Abort command - %s\n", ha->host_no, id, lun, (ret == SUCCESS) ? "succeeded" : "failed"); return ret; @@ -9293,7 +9293,7 @@ static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd) ret = FAILED; ql4_printk(KERN_INFO, ha, - "scsi%ld:%d:%d:%d: DEVICE RESET ISSUED.\n", ha->host_no, + "scsi%ld:%d:%d:%llu: DEVICE RESET ISSUED.\n", ha->host_no, cmd->device->channel, cmd->device->id, cmd->device->lun); DEBUG2(printk(KERN_INFO @@ -9323,7 +9323,7 @@ static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd) goto eh_dev_reset_done; ql4_printk(KERN_INFO, ha, - "scsi(%ld:%d:%d:%d): DEVICE RESET SUCCEEDED.\n", + "scsi(%ld:%d:%d:%llu): DEVICE RESET SUCCEEDED.\n", ha->host_no, cmd->device->channel, cmd->device->id, cmd->device->lun); @@ -9440,7 +9440,7 @@ static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd) } ql4_printk(KERN_INFO, ha, - "scsi(%ld:%d:%d:%d): HOST RESET ISSUED.\n", ha->host_no, + "scsi(%ld:%d:%d:%llu): HOST RESET ISSUED.\n", ha->host_no, cmd->device->channel, cmd->device->id, cmd->device->lun); if (qla4xxx_wait_for_hba_online(ha) != QLA_SUCCESS) { diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 53b8b94e6c84..bff351b526cf 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -1291,7 +1291,7 @@ EXPORT_SYMBOL(__starget_for_each_device); * really want to use scsi_device_lookup_by_target instead. **/ struct scsi_device *__scsi_device_lookup_by_target(struct scsi_target *starget, - uint lun) + u64 lun) { struct scsi_device *sdev; @@ -1316,7 +1316,7 @@ EXPORT_SYMBOL(__scsi_device_lookup_by_target); * needs to be released with scsi_device_put once you're done with it. **/ struct scsi_device *scsi_device_lookup_by_target(struct scsi_target *starget, - uint lun) + u64 lun) { struct scsi_device *sdev; struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); @@ -1349,7 +1349,7 @@ EXPORT_SYMBOL(scsi_device_lookup_by_target); * really want to use scsi_device_lookup instead. **/ struct scsi_device *__scsi_device_lookup(struct Scsi_Host *shost, - uint channel, uint id, uint lun) + uint channel, uint id, u64 lun) { struct scsi_device *sdev; @@ -1375,7 +1375,7 @@ EXPORT_SYMBOL(__scsi_device_lookup); * needs to be released with scsi_device_put once you're done with it. **/ struct scsi_device *scsi_device_lookup(struct Scsi_Host *shost, - uint channel, uint id, uint lun) + uint channel, uint id, u64 lun) { struct scsi_device *sdev; unsigned long flags; diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index c4ad52c2ec69..6ed43fd19a22 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -228,9 +228,9 @@ struct sdebug_dev_info { unsigned char sense_buff[SDEBUG_SENSE_LEN]; /* weak nexus */ unsigned int channel; unsigned int target; - unsigned int lun; + u64 lun; struct sdebug_host_info *sdbg_host; - unsigned int wlun; + u64 wlun; char reset; char stopped; char used; @@ -2278,7 +2278,8 @@ static int resp_report_luns(struct scsi_cmnd * scp, struct sdebug_dev_info * devip) { unsigned int alloc_len; - int lun_cnt, i, upper, num, n, wlun, lun; + int lun_cnt, i, upper, num, n; + u64 wlun, lun; unsigned char *cmd = (unsigned char *)scp->cmnd; int select_report = (int)cmd[2]; struct scsi_lun *one_lun; @@ -2462,7 +2463,7 @@ static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev) static int scsi_debug_slave_alloc(struct scsi_device *sdp) { if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) - printk(KERN_INFO "scsi_debug: slave_alloc <%u %u %u %u>\n", + printk(KERN_INFO "scsi_debug: slave_alloc <%u %u %u %llu>\n", sdp->host->host_no, sdp->channel, sdp->id, sdp->lun); queue_flag_set_unlocked(QUEUE_FLAG_BIDI, sdp->request_queue); return 0; @@ -2473,7 +2474,7 @@ static int scsi_debug_slave_configure(struct scsi_device *sdp) struct sdebug_dev_info *devip; if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) - printk(KERN_INFO "scsi_debug: slave_configure <%u %u %u %u>\n", + printk(KERN_INFO "scsi_debug: slave_configure <%u %u %u %llu>\n", sdp->host->host_no, sdp->channel, sdp->id, sdp->lun); if (sdp->host->max_cmd_len != SCSI_DEBUG_MAX_CMD_LEN) sdp->host->max_cmd_len = SCSI_DEBUG_MAX_CMD_LEN; @@ -2496,7 +2497,7 @@ static void scsi_debug_slave_destroy(struct scsi_device *sdp) (struct sdebug_dev_info *)sdp->hostdata; if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) - printk(KERN_INFO "scsi_debug: slave_destroy <%u %u %u %u>\n", + printk(KERN_INFO "scsi_debug: slave_destroy <%u %u %u %llu>\n", sdp->host->host_no, sdp->channel, sdp->id, sdp->lun); if (devip) { /* make this slot available for re-use */ @@ -2708,7 +2709,7 @@ static int schedule_resp(struct scsi_cmnd * cmnd, if (scsi_result) { struct scsi_device * sdp = cmnd->device; - printk(KERN_INFO "scsi_debug: <%u %u %u %u> " + printk(KERN_INFO "scsi_debug: <%u %u %u %llu> " "non-zero result=0x%x\n", sdp->host->host_no, sdp->channel, sdp->id, sdp->lun, scsi_result); } diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index 48e5b657e79f..a45d1c2eb414 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -115,7 +115,7 @@ extern void scsi_exit_procfs(void); extern char scsi_scan_type[]; extern int scsi_complete_async_scans(void); extern int scsi_scan_host_selected(struct Scsi_Host *, unsigned int, - unsigned int, unsigned int, int); + unsigned int, u64, int); extern void scsi_forget_host(struct Scsi_Host *); extern void scsi_rescan_device(struct device *); diff --git a/drivers/scsi/scsi_proc.c b/drivers/scsi/scsi_proc.c index 86f0c5d5c116..6fcefa2da503 100644 --- a/drivers/scsi/scsi_proc.c +++ b/drivers/scsi/scsi_proc.c @@ -185,7 +185,7 @@ static int proc_print_scsidevice(struct device *dev, void *data) sdev = to_scsi_device(dev); seq_printf(s, - "Host: scsi%d Channel: %02d Id: %02d Lun: %02d\n Vendor: ", + "Host: scsi%d Channel: %02d Id: %02d Lun: %02llu\n Vendor: ", sdev->host->host_no, sdev->channel, sdev->id, sdev->lun); for (i = 0; i < 8; i++) { if (sdev->vendor[i] >= 0x20) diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index a02f7b0976ed..1a71547832f3 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -220,7 +220,7 @@ static void scsi_unlock_floptical(struct scsi_device *sdev, * scsi_Device pointer, or NULL on failure. **/ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget, - unsigned int lun, void *hostdata) + u64 lun, void *hostdata) { struct scsi_device *sdev; int display_failure_msg = 1, ret; @@ -1028,7 +1028,7 @@ static unsigned char *scsi_inq_str(unsigned char *buf, unsigned char *inq, * SCSI_SCAN_LUN_PRESENT: a new scsi_device was allocated and initialized **/ static int scsi_probe_and_add_lun(struct scsi_target *starget, - uint lun, int *bflagsp, + u64 lun, int *bflagsp, struct scsi_device **sdevp, int rescan, void *hostdata) { @@ -1181,7 +1181,8 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget, static void scsi_sequential_lun_scan(struct scsi_target *starget, int bflags, int scsi_level, int rescan) { - unsigned int sparse_lun, lun, max_dev_lun; + uint max_dev_lun; + u64 sparse_lun, lun; struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: Sequential scan of" @@ -1271,10 +1272,10 @@ static void scsi_sequential_lun_scan(struct scsi_target *starget, * Given a struct scsi_lun of: 0a 04 0b 03 00 00 00 00, this function returns * the integer: 0x0b030a04 **/ -int scsilun_to_int(struct scsi_lun *scsilun) +u64 scsilun_to_int(struct scsi_lun *scsilun) { int i; - unsigned int lun; + u64 lun; lun = 0; for (i = 0; i < sizeof(lun); i += 2) @@ -1302,7 +1303,7 @@ EXPORT_SYMBOL(scsilun_to_int); * scsi_lun of : struct scsi_lun of: 0a 04 0b 03 00 00 00 00 * **/ -void int_to_scsilun(unsigned int lun, struct scsi_lun *scsilun) +void int_to_scsilun(u64 lun, struct scsi_lun *scsilun) { int i; @@ -1342,7 +1343,7 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags, char devname[64]; unsigned char scsi_cmd[MAX_COMMAND_SIZE]; unsigned int length; - unsigned int lun; + u64 lun; unsigned int num_luns; unsigned int retries; int result; @@ -1485,25 +1486,8 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags, for (lunp = &lun_data[1]; lunp <= &lun_data[num_luns]; lunp++) { lun = scsilun_to_int(lunp); - /* - * Check if the unused part of lunp is non-zero, and so - * does not fit in lun. - */ - if (memcmp(&lunp->scsi_lun[sizeof(lun)], "\0\0\0\0", 4)) { - int i; - - /* - * Output an error displaying the LUN in byte order, - * this differs from what linux would print for the - * integer LUN value. - */ - printk(KERN_WARNING "scsi: %s lun 0x", devname); - data = (char *)lunp->scsi_lun; - for (i = 0; i < sizeof(struct scsi_lun); i++) - printk("%02x", data[i]); - printk(" has a LUN larger than currently supported.\n"); - } else if (lun > sdev->host->max_lun) { - printk(KERN_WARNING "scsi: %s lun%d has a LUN larger" + if (lun > sdev->host->max_lun) { + printk(KERN_WARNING "scsi: %s lun%llu has a LUN larger" " than allowed by the host adapter\n", devname, lun); } else { @@ -1517,8 +1501,8 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags, */ sdev_printk(KERN_ERR, sdev, "Unexpected response" - " from lun %d while scanning, scan" - " aborted\n", lun); + " from lun %llu while scanning, scan" + " aborted\n", (unsigned long long)lun); break; } } @@ -1537,7 +1521,7 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags, } struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel, - uint id, uint lun, void *hostdata) + uint id, u64 lun, void *hostdata) { struct scsi_device *sdev = ERR_PTR(-ENODEV); struct device *parent = &shost->shost_gendev; @@ -1573,7 +1557,7 @@ struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel, EXPORT_SYMBOL(__scsi_add_device); int scsi_add_device(struct Scsi_Host *host, uint channel, - uint target, uint lun) + uint target, u64 lun) { struct scsi_device *sdev = __scsi_add_device(host, channel, target, lun, NULL); @@ -1602,7 +1586,7 @@ void scsi_rescan_device(struct device *dev) EXPORT_SYMBOL(scsi_rescan_device); static void __scsi_scan_target(struct device *parent, unsigned int channel, - unsigned int id, unsigned int lun, int rescan) + unsigned int id, u64 lun, int rescan) { struct Scsi_Host *shost = dev_to_shost(parent); int bflags = 0; @@ -1670,7 +1654,7 @@ static void __scsi_scan_target(struct device *parent, unsigned int channel, * sequential scan of LUNs on the target id. **/ void scsi_scan_target(struct device *parent, unsigned int channel, - unsigned int id, unsigned int lun, int rescan) + unsigned int id, u64 lun, int rescan) { struct Scsi_Host *shost = dev_to_shost(parent); @@ -1690,7 +1674,7 @@ void scsi_scan_target(struct device *parent, unsigned int channel, EXPORT_SYMBOL(scsi_scan_target); static void scsi_scan_channel(struct Scsi_Host *shost, unsigned int channel, - unsigned int id, unsigned int lun, int rescan) + unsigned int id, u64 lun, int rescan) { uint order_id; @@ -1721,10 +1705,10 @@ static void scsi_scan_channel(struct Scsi_Host *shost, unsigned int channel, } int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel, - unsigned int id, unsigned int lun, int rescan) + unsigned int id, u64 lun, int rescan) { SCSI_LOG_SCAN_BUS(3, shost_printk (KERN_INFO, shost, - "%s: <%u:%u:%u>\n", + "%s: <%u:%u:%llu>\n", __func__, channel, id, lun)); if (((channel != SCAN_WILD_CARD) && (channel > shost->max_channel)) || diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 074e8cc30955..5f36788705ba 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -80,7 +80,7 @@ const char *scsi_host_state_name(enum scsi_host_state state) return name; } -static int check_set(unsigned int *val, char *src) +static int check_set(unsigned long long *val, char *src) { char *last; @@ -90,7 +90,7 @@ static int check_set(unsigned int *val, char *src) /* * Doesn't check for int overflow */ - *val = simple_strtoul(src, &last, 0); + *val = simple_strtoull(src, &last, 0); if (*last != '\0') return 1; } @@ -99,11 +99,11 @@ static int check_set(unsigned int *val, char *src) static int scsi_scan(struct Scsi_Host *shost, const char *str) { - char s1[15], s2[15], s3[15], junk; - unsigned int channel, id, lun; + char s1[15], s2[15], s3[17], junk; + unsigned long long channel, id, lun; int res; - res = sscanf(str, "%10s %10s %10s %c", s1, s2, s3, &junk); + res = sscanf(str, "%10s %10s %16s %c", s1, s2, s3, &junk); if (res != 3) return -EINVAL; if (check_set(&channel, s1)) @@ -1230,13 +1230,13 @@ void scsi_sysfs_device_initialize(struct scsi_device *sdev) device_initialize(&sdev->sdev_gendev); sdev->sdev_gendev.bus = &scsi_bus_type; sdev->sdev_gendev.type = &scsi_dev_type; - dev_set_name(&sdev->sdev_gendev, "%d:%d:%d:%d", + dev_set_name(&sdev->sdev_gendev, "%d:%d:%d:%llu", sdev->host->host_no, sdev->channel, sdev->id, sdev->lun); device_initialize(&sdev->sdev_dev); sdev->sdev_dev.parent = get_device(&sdev->sdev_gendev); sdev->sdev_dev.class = &sdev_class; - dev_set_name(&sdev->sdev_dev, "%d:%d:%d:%d", + dev_set_name(&sdev->sdev_dev, "%d:%d:%d:%llu", sdev->host->host_no, sdev->channel, sdev->id, sdev->lun); sdev->scsi_level = starget->scsi_level; transport_setup_device(&sdev->sdev_gendev); diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 521f5838594b..8365705c231d 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -2089,7 +2089,7 @@ fc_timed_out(struct scsi_cmnd *scmd) * on the rport. */ static void -fc_user_scan_tgt(struct Scsi_Host *shost, uint channel, uint id, uint lun) +fc_user_scan_tgt(struct Scsi_Host *shost, uint channel, uint id, u64 lun) { struct fc_rport *rport; unsigned long flags; @@ -2121,7 +2121,7 @@ fc_user_scan_tgt(struct Scsi_Host *shost, uint channel, uint id, uint lun) * object as the parent. */ static int -fc_user_scan(struct Scsi_Host *shost, uint channel, uint id, uint lun) +fc_user_scan(struct Scsi_Host *shost, uint channel, uint id, u64 lun) { uint chlo, chhi; uint tgtlo, tgthi; diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 0102a2d70dd8..126bf260500f 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -1780,7 +1780,7 @@ EXPORT_SYMBOL_GPL(iscsi_scan_finished); struct iscsi_scan_data { unsigned int channel; unsigned int id; - unsigned int lun; + u64 lun; }; static int iscsi_user_scan_session(struct device *dev, void *data) @@ -1827,7 +1827,7 @@ user_scan_exit: } static int iscsi_user_scan(struct Scsi_Host *shost, uint channel, - uint id, uint lun) + uint id, u64 lun) { struct iscsi_scan_data scan_data; diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index c341f855fadc..9a058194b9bd 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -1705,7 +1705,7 @@ EXPORT_SYMBOL(scsi_is_sas_rphy); */ static int sas_user_scan(struct Scsi_Host *shost, uint channel, - uint id, uint lun) + uint id, u64 lun) { struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); struct sas_rphy *rphy; diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 2e01a9dd26fa..1de183d72598 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -2542,7 +2542,7 @@ static int sg_proc_seq_show_dev(struct seq_file *s, void *v) seq_puts(s, "-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\n"); else { scsidp = sdp->device; - seq_printf(s, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", + seq_printf(s, "%d\t%d\t%d\t%llu\t%d\t%d\t%d\t%d\t%d\n", scsidp->host->host_no, scsidp->channel, scsidp->id, scsidp->lun, (int) scsidp->type, 1, @@ -2671,7 +2671,7 @@ static int sg_proc_seq_show_debug(struct seq_file *s, void *v) else if (sdp->device) { struct scsi_device *scsidp = sdp->device; - seq_printf(s, "%d:%d:%d:%d em=%d", + seq_printf(s, "%d:%d:%d:%llu em=%d", scsidp->host->host_no, scsidp->channel, scsidp->id, scsidp->lun, diff --git a/drivers/scsi/sun3_NCR5380.c b/drivers/scsi/sun3_NCR5380.c index 88220794cc98..1a2367a1b1f2 100644 --- a/drivers/scsi/sun3_NCR5380.c +++ b/drivers/scsi/sun3_NCR5380.c @@ -355,17 +355,18 @@ static void __init init_tags( void ) static int is_lun_busy(struct scsi_cmnd *cmd, int should_be_tagged) { + u8 lun = cmd->device->lun; SETUP_HOSTDATA(cmd->device->host); - if (hostdata->busy[cmd->device->id] & (1 << cmd->device->lun)) + if (hostdata->busy[cmd->device->id] & (1 << lun)) return( 1 ); if (!should_be_tagged || !setup_use_tagged_queuing || !cmd->device->tagged_supported) return( 0 ); - if (TagAlloc[cmd->device->id][cmd->device->lun].nr_allocated >= - TagAlloc[cmd->device->id][cmd->device->lun].queue_size ) { + if (TagAlloc[cmd->device->id][lun].nr_allocated >= + TagAlloc[cmd->device->id][lun].queue_size ) { dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %d: no free tags\n", - H_NO(cmd), cmd->device->id, cmd->device->lun ); + H_NO(cmd), cmd->device->id, lun ); return( 1 ); } return( 0 ); @@ -379,6 +380,7 @@ static int is_lun_busy(struct scsi_cmnd *cmd, int should_be_tagged) static void cmd_get_tag(struct scsi_cmnd *cmd, int should_be_tagged) { + u8 lun = cmd->device->lun; SETUP_HOSTDATA(cmd->device->host); /* If we or the target don't support tagged queuing, allocate the LUN for @@ -387,19 +389,19 @@ static void cmd_get_tag(struct scsi_cmnd *cmd, int should_be_tagged) if (!should_be_tagged || !setup_use_tagged_queuing || !cmd->device->tagged_supported) { cmd->tag = TAG_NONE; - hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun); + hostdata->busy[cmd->device->id] |= (1 << lun); dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %d now allocated by untagged " - "command\n", H_NO(cmd), cmd->device->id, cmd->device->lun ); + "command\n", H_NO(cmd), cmd->device->id, lun ); } else { - TAG_ALLOC *ta = &TagAlloc[cmd->device->id][cmd->device->lun]; + TAG_ALLOC *ta = &TagAlloc[cmd->device->id][lun]; cmd->tag = find_first_zero_bit( &ta->allocated, MAX_TAGS ); set_bit( cmd->tag, &ta->allocated ); ta->nr_allocated++; dprintk(NDEBUG_TAGS, "scsi%d: using tag %d for target %d lun %d " "(now %d tags in use)\n", - H_NO(cmd), cmd->tag, cmd->device->id, cmd->device->lun, + H_NO(cmd), cmd->tag, cmd->device->id, lun, ta->nr_allocated ); } } @@ -411,23 +413,24 @@ static void cmd_get_tag(struct scsi_cmnd *cmd, int should_be_tagged) static void cmd_free_tag(struct scsi_cmnd *cmd) { + u8 lun = cmd->device->lun; SETUP_HOSTDATA(cmd->device->host); if (cmd->tag == TAG_NONE) { - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); + hostdata->busy[cmd->device->id] &= ~(1 << lun); dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %d untagged cmd finished\n", - H_NO(cmd), cmd->device->id, cmd->device->lun ); + H_NO(cmd), cmd->device->id, lun ); } else if (cmd->tag >= MAX_TAGS) { printk(KERN_NOTICE "scsi%d: trying to free bad tag %d!\n", H_NO(cmd), cmd->tag ); } else { - TAG_ALLOC *ta = &TagAlloc[cmd->device->id][cmd->device->lun]; + TAG_ALLOC *ta = &TagAlloc[cmd->device->id][lun]; clear_bit( cmd->tag, &ta->allocated ); ta->nr_allocated--; dprintk(NDEBUG_TAGS, "scsi%d: freed tag %d for target %d lun %d\n", - H_NO(cmd), cmd->tag, cmd->device->id, cmd->device->lun ); + H_NO(cmd), cmd->tag, cmd->device->id, lun ); } } @@ -659,7 +662,7 @@ static void lprint_Scsi_Cmnd(Scsi_Cmnd *cmd) { int i, s; unsigned char *command; - printk("scsi%d: destination target %d, lun %d\n", + printk("scsi%d: destination target %d, lun %llu\n", H_NO(cmd), cmd->device->id, cmd->device->lun); printk(KERN_CONT " command = "); command = cmd->cmnd; @@ -705,7 +708,7 @@ static void show_Scsi_Cmnd(Scsi_Cmnd *cmd, struct seq_file *m) { int i, s; unsigned char *command; - seq_printf(m, "scsi%d: destination target %d, lun %d\n", + seq_printf(m, "scsi%d: destination target %d, lun %llu\n", H_NO(cmd), cmd->device->id, cmd->device->lun); seq_printf(m, " command = "); command = cmd->cmnd; @@ -1007,7 +1010,7 @@ static void NCR5380_main (struct work_struct *bl) prev = NULL; tmp; prev = tmp, tmp = NEXT(tmp) ) { if (prev != tmp) - dprintk(NDEBUG_LISTS, "MAIN tmp=%p target=%d busy=%d lun=%d\n", tmp, tmp->device->id, hostdata->busy[tmp->device->id], tmp->device->lun); + dprintk(NDEBUG_LISTS, "MAIN tmp=%p target=%d busy=%d lun=%llu\n", tmp, tmp->device->id, hostdata->busy[tmp->device->id], tmp->device->lun); /* When we find one, remove it from the issue queue. */ /* ++guenther: possible race with Falcon locking */ if ( @@ -1038,7 +1041,7 @@ static void NCR5380_main (struct work_struct *bl) * issue queue so we can keep trying. */ dprintk(NDEBUG_MAIN, "scsi%d: main(): command for target %d " - "lun %d removed from issue_queue\n", + "lun %llu removed from issue_queue\n", HOSTNO, tmp->device->id, tmp->device->lun); /* * REQUEST SENSE commands are issued without tagged @@ -2020,7 +2023,7 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance) * accesses to this device will use the * polled-IO. */ printk(KERN_NOTICE "scsi%d: switching target %d " - "lun %d to slow handshake\n", HOSTNO, + "lun %llu to slow handshake\n", HOSTNO, cmd->device->id, cmd->device->lun); cmd->device->borken = 1; NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | @@ -2078,7 +2081,7 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance) /* Accept message by clearing ACK */ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - dprintk(NDEBUG_LINKED, "scsi%d: target %d lun %d linked command " + dprintk(NDEBUG_LINKED, "scsi%d: target %d lun %llu linked command " "complete.\n", HOSTNO, cmd->device->id, cmd->device->lun); /* Enable reselect interrupts */ @@ -2090,7 +2093,7 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance) */ if (!cmd->next_link) { - printk(KERN_NOTICE "scsi%d: target %d lun %d " + printk(KERN_NOTICE "scsi%d: target %d lun %llu " "linked command complete, no next_link\n", HOSTNO, cmd->device->id, cmd->device->lun); sink = 1; @@ -2103,7 +2106,7 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance) * and don't free it! */ cmd->next_link->tag = cmd->tag; cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); - dprintk(NDEBUG_LINKED, "scsi%d: target %d lun %d linked request " + dprintk(NDEBUG_LINKED, "scsi%d: target %d lun %llu linked request " "done, calling scsi_done().\n", HOSTNO, cmd->device->id, cmd->device->lun); #ifdef NCR5380_STATS @@ -2118,7 +2121,7 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance) /* Accept message by clearing ACK */ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); hostdata->connected = NULL; - dprintk(NDEBUG_QUEUES, "scsi%d: command for target %d, lun %d " + dprintk(NDEBUG_QUEUES, "scsi%d: command for target %d, lun %llu " "completed\n", HOSTNO, cmd->device->id, cmd->device->lun); #ifdef SUPPORT_TAGS cmd_free_tag( cmd ); @@ -2132,7 +2135,7 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance) /* ++Andreas: the mid level code knows about QUEUE_FULL now. */ TAG_ALLOC *ta = &TagAlloc[cmd->device->id][cmd->device->lun]; - dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %d returned " + dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %llu returned " "QUEUE_FULL after %d commands\n", HOSTNO, cmd->device->id, cmd->device->lun, ta->nr_allocated); @@ -2228,7 +2231,7 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance) cmd->device->tagged_supported = 0; hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun); cmd->tag = TAG_NONE; - dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %d rejected " + dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %llu rejected " "QUEUE_TAG message; tagged queuing " "disabled\n", HOSTNO, cmd->device->id, cmd->device->lun); @@ -2245,7 +2248,7 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance) hostdata->connected = NULL; hostdata->disconnected_queue = cmd; local_irq_restore(flags); - dprintk(NDEBUG_QUEUES, "scsi%d: command for target %d lun %d was " + dprintk(NDEBUG_QUEUES, "scsi%d: command for target %d lun %llu was " "moved from connected to the " "disconnected_queue\n", HOSTNO, cmd->device->id, cmd->device->lun); @@ -2349,12 +2352,12 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance) printk("\n"); } else if (tmp != EXTENDED_MESSAGE) printk(KERN_DEBUG "scsi%d: rejecting unknown " - "message %02x from target %d, lun %d\n", + "message %02x from target %d, lun %llu\n", HOSTNO, tmp, cmd->device->id, cmd->device->lun); else printk(KERN_DEBUG "scsi%d: rejecting unknown " "extended message " - "code %02x, length %d from target %d, lun %d\n", + "code %02x, length %d from target %d, lun %llu\n", HOSTNO, extended_msg[1], extended_msg[0], cmd->device->id, cmd->device->lun); @@ -2576,7 +2579,7 @@ static void NCR5380_reselect (struct Scsi_Host *instance) #endif hostdata->connected = tmp; - dprintk(NDEBUG_RESELECTION, "scsi%d: nexus established, target = %d, lun = %d, tag = %d\n", + dprintk(NDEBUG_RESELECTION, "scsi%d: nexus established, target = %d, lun = %llu, tag = %d\n", HOSTNO, tmp->device->id, tmp->device->lun, tmp->tag); } diff --git a/drivers/scsi/sym53c8xx_2/sym_glue.c b/drivers/scsi/sym53c8xx_2/sym_glue.c index 6d3ee1ab6362..e59e6f96b725 100644 --- a/drivers/scsi/sym53c8xx_2/sym_glue.c +++ b/drivers/scsi/sym53c8xx_2/sym_glue.c @@ -851,7 +851,7 @@ static void sym53c8xx_slave_destroy(struct scsi_device *sdev) * so let's try to stop all on-going I/O. */ starget_printk(KERN_WARNING, tp->starget, - "Removing busy LCB (%d)\n", sdev->lun); + "Removing busy LCB (%d)\n", (u8)sdev->lun); sym_reset_scsi_bus(np, 1); } diff --git a/drivers/scsi/sym53c8xx_2/sym_hipd.h b/drivers/scsi/sym53c8xx_2/sym_hipd.h index 5a80cbac3f92..a141b1758033 100644 --- a/drivers/scsi/sym53c8xx_2/sym_hipd.h +++ b/drivers/scsi/sym53c8xx_2/sym_hipd.h @@ -581,7 +581,7 @@ struct sym_pmc { #define sym_lp(tp, lun) (!lun) ? (tp)->lun0p : NULL #else #define sym_lp(tp, lun) \ - (!lun) ? (tp)->lun0p : (tp)->lunmp ? (tp)->lunmp[(lun)] : NULL + (!lun) ? (tp)->lun0p : (tp)->lunmp ? (tp)->lunmp[((u8)lun)] : NULL #endif /* diff --git a/drivers/scsi/tmscsim.c b/drivers/scsi/tmscsim.c index b006cf789ba1..764575726c85 100644 --- a/drivers/scsi/tmscsim.c +++ b/drivers/scsi/tmscsim.c @@ -621,7 +621,7 @@ dc390_StartSCSI( struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_sr { dc390_freetag (pDCB, pSRB); DEBUG0(printk ("DC390: Interrupt during Start SCSI (target %02i-%02i)\n", - scmd->device->id, scmd->device->lun)); + scmd->device->id, (u8)scmd->device->lun)); pSRB->SRBState = SRB_READY; //DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); pACB->SelLost++; @@ -1726,7 +1726,7 @@ dc390_SRBdone( struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_srb* } else { SET_RES_DRV(pcmd->result, DRIVER_SENSE); //pSRB->ScsiCmdLen = (u8) (pSRB->Segment1[0] >> 8); - DEBUG0 (printk ("DC390: RETRY (%02x), target %02i-%02i\n", pcmd->cmnd[0], pcmd->device->id, pcmd->device->lun)); + DEBUG0 (printk ("DC390: RETRY (%02x), target %02i-%02i\n", pcmd->cmnd[0], pcmd->device->id, (u8)pcmd->device->lun)); pSRB->TotalXferredLen = 0; SET_RES_DID(pcmd->result, DID_SOFT_ERROR); } @@ -1746,7 +1746,7 @@ dc390_SRBdone( struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_srb* else if (status == SAM_STAT_TASK_SET_FULL) { scsi_track_queue_full(pcmd->device, pDCB->GoingSRBCnt - 1); - DEBUG0 (printk ("DC390: RETRY (%02x), target %02i-%02i\n", pcmd->cmnd[0], pcmd->device->id, pcmd->device->lun)); + DEBUG0 (printk ("DC390: RETRY (%02x), target %02i-%02i\n", pcmd->cmnd[0], pcmd->device->id, (u8)pcmd->device->lun)); pSRB->TotalXferredLen = 0; SET_RES_DID(pcmd->result, DID_SOFT_ERROR); } diff --git a/drivers/scsi/u14-34f.c b/drivers/scsi/u14-34f.c index 5a03bb3bcfef..4e76fe863fc4 100644 --- a/drivers/scsi/u14-34f.c +++ b/drivers/scsi/u14-34f.c @@ -1285,14 +1285,14 @@ static int u14_34f_queuecommand_lck(struct scsi_cmnd *SCpnt, void (*done)(struct cpp->cpp_index = i; SCpnt->host_scribble = (unsigned char *) &cpp->cpp_index; - if (do_trace) printk("%s: qcomm, mbox %d, target %d.%d:%d.\n", + if (do_trace) printk("%s: qcomm, mbox %d, target %d.%d:%llu.\n", BN(j), i, SCpnt->device->channel, SCpnt->device->id, - SCpnt->device->lun); + (u8)SCpnt->device->lun); cpp->opcode = OP_SCSI; cpp->channel = SCpnt->device->channel; cpp->target = SCpnt->device->id; - cpp->lun = SCpnt->device->lun; + cpp->lun = (u8)SCpnt->device->lun; cpp->SCpnt = SCpnt; cpp->cdb_len = SCpnt->cmd_len; memcpy(cpp->cdb, SCpnt->cmnd, SCpnt->cmd_len); @@ -1663,10 +1663,10 @@ static int reorder(unsigned int j, unsigned long cursec, if (link_statistics && (overlap || !(flushcount % link_statistics))) for (n = 0; n < n_ready; n++) { k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt; - printk("%s %d.%d:%d mb %d fc %d nr %d sec %ld ns %u"\ + printk("%s %d.%d:%llu mb %d fc %d nr %d sec %ld ns %u"\ " cur %ld s:%c r:%c rev:%c in:%c ov:%c xd %d.\n", (ihdlr ? "ihdlr" : "qcomm"), SCpnt->channel, SCpnt->target, - SCpnt->lun, k, flushcount, n_ready, + (u8)SCpnt->lun, k, flushcount, n_ready, blk_rq_pos(SCpnt->request), blk_rq_sectors(SCpnt->request), cursec, YESNO(s), YESNO(r), YESNO(rev), YESNO(input_only), YESNO(overlap), cpp->xdir); diff --git a/drivers/scsi/wd33c93.c b/drivers/scsi/wd33c93.c index 41883a87931d..c0506de4f3b6 100644 --- a/drivers/scsi/wd33c93.c +++ b/drivers/scsi/wd33c93.c @@ -502,7 +502,8 @@ wd33c93_execute(struct Scsi_Host *instance) cmd = (struct scsi_cmnd *) hostdata->input_Q; prev = NULL; while (cmd) { - if (!(hostdata->busy[cmd->device->id] & (1 << cmd->device->lun))) + if (!(hostdata->busy[cmd->device->id] & + (1 << (cmd->device->lun & 0xff)))) break; prev = cmd; cmd = (struct scsi_cmnd *) cmd->host_scribble; @@ -593,10 +594,10 @@ wd33c93_execute(struct Scsi_Host *instance) write_wd33c93(regs, WD_SOURCE_ID, ((cmd->SCp.phase) ? SRCID_ER : 0)); - write_wd33c93(regs, WD_TARGET_LUN, cmd->device->lun); + write_wd33c93(regs, WD_TARGET_LUN, (u8)cmd->device->lun); write_wd33c93(regs, WD_SYNCHRONOUS_TRANSFER, hostdata->sync_xfer[cmd->device->id]); - hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun); + hostdata->busy[cmd->device->id] |= (1 << (cmd->device->lun & 0xFF)); if ((hostdata->level2 == L2_NONE) || (hostdata->sync_stat[cmd->device->id] == SS_UNSET)) { @@ -862,7 +863,7 @@ wd33c93_intr(struct Scsi_Host *instance) } cmd->result = DID_NO_CONNECT << 16; - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); + hostdata->busy[cmd->device->id] &= ~(1 << (cmd->device->lun & 0xff)); hostdata->state = S_UNCONNECTED; cmd->scsi_done(cmd); @@ -895,7 +896,7 @@ wd33c93_intr(struct Scsi_Host *instance) /* construct an IDENTIFY message with correct disconnect bit */ - hostdata->outgoing_msg[0] = (0x80 | 0x00 | cmd->device->lun); + hostdata->outgoing_msg[0] = IDENTIFY(0, cmd->device->lun); if (cmd->SCp.phase) hostdata->outgoing_msg[0] |= 0x40; @@ -1179,7 +1180,7 @@ wd33c93_intr(struct Scsi_Host *instance) lun = read_wd33c93(regs, WD_TARGET_LUN); DB(DB_INTR, printk(":%d.%d", cmd->SCp.Status, lun)) hostdata->connected = NULL; - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); + hostdata->busy[cmd->device->id] &= ~(1 << (cmd->device->lun & 0xff)); hostdata->state = S_UNCONNECTED; if (cmd->SCp.Status == ILLEGAL_STATUS_BYTE) cmd->SCp.Status = lun; @@ -1268,7 +1269,7 @@ wd33c93_intr(struct Scsi_Host *instance) } DB(DB_INTR, printk("UNEXP_DISC")) hostdata->connected = NULL; - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); + hostdata->busy[cmd->device->id] &= ~(1 << (cmd->device->lun & 0xff)); hostdata->state = S_UNCONNECTED; if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD) cmd->result = @@ -1300,7 +1301,7 @@ wd33c93_intr(struct Scsi_Host *instance) switch (hostdata->state) { case S_PRE_CMP_DISC: hostdata->connected = NULL; - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); + hostdata->busy[cmd->device->id] &= ~(1 << (cmd->device->lun & 0xff)); hostdata->state = S_UNCONNECTED; DB(DB_INTR, printk(":%d", cmd->SCp.Status)) if (cmd->cmnd[0] == REQUEST_SENSE @@ -1353,7 +1354,7 @@ wd33c93_intr(struct Scsi_Host *instance) if (hostdata->selecting) { cmd = (struct scsi_cmnd *) hostdata->selecting; hostdata->selecting = NULL; - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); + hostdata->busy[cmd->device->id] &= ~(1 << (cmd->device->lun & 0xff)); cmd->host_scribble = (uchar *) hostdata->input_Q; hostdata->input_Q = cmd; @@ -1365,7 +1366,7 @@ wd33c93_intr(struct Scsi_Host *instance) if (cmd) { if (phs == 0x00) { hostdata->busy[cmd->device->id] &= - ~(1 << cmd->device->lun); + ~(1 << (cmd->device->lun & 0xff)); cmd->host_scribble = (uchar *) hostdata->input_Q; hostdata->input_Q = cmd; @@ -1448,7 +1449,7 @@ wd33c93_intr(struct Scsi_Host *instance) cmd = (struct scsi_cmnd *) hostdata->disconnected_Q; patch = NULL; while (cmd) { - if (id == cmd->device->id && lun == cmd->device->lun) + if (id == cmd->device->id && lun == (u8)cmd->device->lun) break; patch = cmd; cmd = (struct scsi_cmnd *) cmd->host_scribble; @@ -1459,7 +1460,7 @@ wd33c93_intr(struct Scsi_Host *instance) if (!cmd) { printk ("---TROUBLE: target %d.%d not in disconnect queue---", - id, lun); + id, (u8)lun); spin_unlock_irqrestore(&hostdata->lock, flags); return; } @@ -1705,7 +1706,7 @@ wd33c93_abort(struct scsi_cmnd * cmd) sr = read_wd33c93(regs, WD_SCSI_STATUS); printk("asr=%02x, sr=%02x.", asr, sr); - hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); + hostdata->busy[cmd->device->id] &= ~(1 << (cmd->device->lun & 0xff)); hostdata->connected = NULL; hostdata->state = S_UNCONNECTED; cmd->result = DID_ABORT << 16; @@ -2169,7 +2170,7 @@ wd33c93_show_info(struct seq_file *m, struct Scsi_Host *instance) seq_printf(m, "\nconnected: "); if (hd->connected) { cmd = (struct scsi_cmnd *) hd->connected; - seq_printf(m, " %d:%d(%02x)", + seq_printf(m, " %d:%llu(%02x)", cmd->device->id, cmd->device->lun, cmd->cmnd[0]); } } @@ -2177,7 +2178,7 @@ wd33c93_show_info(struct seq_file *m, struct Scsi_Host *instance) seq_printf(m, "\ninput_Q: "); cmd = (struct scsi_cmnd *) hd->input_Q; while (cmd) { - seq_printf(m, " %d:%d(%02x)", + seq_printf(m, " %d:%llu(%02x)", cmd->device->id, cmd->device->lun, cmd->cmnd[0]); cmd = (struct scsi_cmnd *) cmd->host_scribble; } @@ -2186,7 +2187,7 @@ wd33c93_show_info(struct seq_file *m, struct Scsi_Host *instance) seq_printf(m, "\ndisconnected_Q:"); cmd = (struct scsi_cmnd *) hd->disconnected_Q; while (cmd) { - seq_printf(m, " %d:%d(%02x)", + seq_printf(m, " %d:%llu(%02x)", cmd->device->id, cmd->device->lun, cmd->cmnd[0]); cmd = (struct scsi_cmnd *) cmd->host_scribble; } diff --git a/drivers/staging/rts5208/rtsx.c b/drivers/staging/rts5208/rtsx.c index c0a0e6010372..77020905a73a 100644 --- a/drivers/staging/rts5208/rtsx.c +++ b/drivers/staging/rts5208/rtsx.c @@ -465,14 +465,14 @@ static int rtsx_control_thread(void *__dev) else if (chip->srb->device->id) { dev_err(&dev->pci->dev, "Bad target number (%d:%d)\n", chip->srb->device->id, - chip->srb->device->lun); + (u8)chip->srb->device->lun); chip->srb->result = DID_BAD_TARGET << 16; } else if (chip->srb->device->lun > chip->max_lun) { dev_err(&dev->pci->dev, "Bad LUN (%d:%d)\n", chip->srb->device->id, - chip->srb->device->lun); + (u8)chip->srb->device->lun); chip->srb->result = DID_BAD_TARGET << 16; } diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c index 8c64b8776a96..340de9d92b15 100644 --- a/drivers/target/loopback/tcm_loop.c +++ b/drivers/target/loopback/tcm_loop.c @@ -252,7 +252,7 @@ static int tcm_loop_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc) { struct tcm_loop_cmd *tl_cmd; - pr_debug("tcm_loop_queuecommand() %d:%d:%d:%d got CDB: 0x%02x" + pr_debug("tcm_loop_queuecommand() %d:%d:%d:%llu got CDB: 0x%02x" " scsi_buf_len: %u\n", sc->device->host->host_no, sc->device->id, sc->device->channel, sc->device->lun, sc->cmnd[0], scsi_bufflen(sc)); diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c index 94d00df28f39..943b1dbe859a 100644 --- a/drivers/target/target_core_pscsi.c +++ b/drivers/target/target_core_pscsi.c @@ -312,7 +312,7 @@ static int pscsi_add_device_to_list(struct se_device *dev, if (!sd->queue_depth) { sd->queue_depth = PSCSI_DEFAULT_QUEUEDEPTH; - pr_err("Set broken SCSI Device %d:%d:%d" + pr_err("Set broken SCSI Device %d:%d:%llu" " queue_depth to %d\n", sd->channel, sd->id, sd->lun, sd->queue_depth); } @@ -375,7 +375,7 @@ static int pscsi_create_type_disk(struct se_device *dev, struct scsi_device *sd) int ret; if (scsi_device_get(sd)) { - pr_err("scsi_device_get() failed for %d:%d:%d:%d\n", + pr_err("scsi_device_get() failed for %d:%d:%d:%llu\n", sh->host_no, sd->channel, sd->id, sd->lun); spin_unlock_irq(sh->host_lock); return -EIO; @@ -401,7 +401,7 @@ static int pscsi_create_type_disk(struct se_device *dev, struct scsi_device *sd) return ret; } - pr_debug("CORE_PSCSI[%d] - Added TYPE_DISK for %d:%d:%d:%d\n", + pr_debug("CORE_PSCSI[%d] - Added TYPE_DISK for %d:%d:%d:%llu\n", phv->phv_host_id, sh->host_no, sd->channel, sd->id, sd->lun); return 0; } @@ -417,7 +417,7 @@ static int pscsi_create_type_rom(struct se_device *dev, struct scsi_device *sd) int ret; if (scsi_device_get(sd)) { - pr_err("scsi_device_get() failed for %d:%d:%d:%d\n", + pr_err("scsi_device_get() failed for %d:%d:%d:%llu\n", sh->host_no, sd->channel, sd->id, sd->lun); spin_unlock_irq(sh->host_lock); return -EIO; @@ -429,7 +429,7 @@ static int pscsi_create_type_rom(struct se_device *dev, struct scsi_device *sd) scsi_device_put(sd); return ret; } - pr_debug("CORE_PSCSI[%d] - Added Type: %s for %d:%d:%d:%d\n", + pr_debug("CORE_PSCSI[%d] - Added Type: %s for %d:%d:%d:%llu\n", phv->phv_host_id, scsi_device_type(sd->type), sh->host_no, sd->channel, sd->id, sd->lun); @@ -452,7 +452,7 @@ static int pscsi_create_type_other(struct se_device *dev, if (ret) return ret; - pr_debug("CORE_PSCSI[%d] - Added Type: %s for %d:%d:%d:%d\n", + pr_debug("CORE_PSCSI[%d] - Added Type: %s for %d:%d:%d:%llu\n", phv->phv_host_id, scsi_device_type(sd->type), sh->host_no, sd->channel, sd->id, sd->lun); return 0; diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c index 073a2c32ccc4..38a4504ce450 100644 --- a/drivers/usb/storage/sddr09.c +++ b/drivers/usb/storage/sddr09.c @@ -1498,7 +1498,7 @@ static int dpcm_transport(struct scsi_cmnd *srb, struct us_data *us) { int ret; - usb_stor_dbg(us, "LUN=%d\n", srb->device->lun); + usb_stor_dbg(us, "LUN=%d\n", (u8)srb->device->lun); switch (srb->device->lun) { case 0: @@ -1524,7 +1524,7 @@ static int dpcm_transport(struct scsi_cmnd *srb, struct us_data *us) break; default: - usb_stor_dbg(us, "Invalid LUN %d\n", srb->device->lun); + usb_stor_dbg(us, "Invalid LUN %d\n", (u8)srb->device->lun); ret = USB_STOR_TRANSPORT_ERROR; break; } diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index f1c96261a501..cedb29252a92 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -347,14 +347,16 @@ static int usb_stor_control_thread(void * __us) */ else if (us->srb->device->id && !(us->fflags & US_FL_SCM_MULT_TARG)) { - usb_stor_dbg(us, "Bad target number (%d:%d)\n", - us->srb->device->id, us->srb->device->lun); + usb_stor_dbg(us, "Bad target number (%d:%llu)\n", + us->srb->device->id, + us->srb->device->lun); us->srb->result = DID_BAD_TARGET << 16; } else if (us->srb->device->lun > us->max_lun) { - usb_stor_dbg(us, "Bad LUN (%d:%d)\n", - us->srb->device->id, us->srb->device->lun); + usb_stor_dbg(us, "Bad LUN (%d:%llu)\n", + us->srb->device->id, + us->srb->device->lun); us->srb->result = DID_BAD_TARGET << 16; } diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h index 0a4edfe8af51..91e2e4215ba0 100644 --- a/include/scsi/scsi.h +++ b/include/scsi/scsi.h @@ -385,7 +385,7 @@ struct scsi_lun { #define SCSI_W_LUN_ACCESS_CONTROL (SCSI_W_LUN_BASE + 2) #define SCSI_W_LUN_TARGET_LOG_PAGE (SCSI_W_LUN_BASE + 3) -static inline int scsi_is_wlun(unsigned int lun) +static inline int scsi_is_wlun(u64 lun) { return (lun & 0xff00) == SCSI_W_LUN_BASE; } diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 27ab31017f09..9aa38f7b303b 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -98,8 +98,8 @@ struct scsi_device { unsigned long last_queue_ramp_up; /* last queue ramp up time */ - unsigned int id, lun, channel; - + unsigned int id, channel; + u64 lun; unsigned int manufacturer; /* Manufacturer of device, for using * vendor-specific cmd's */ unsigned sector_size; /* size in bytes */ @@ -321,9 +321,9 @@ static inline struct scsi_target *scsi_target(struct scsi_device *sdev) dev_printk(prefix, &(starget)->dev, fmt, ##a) extern struct scsi_device *__scsi_add_device(struct Scsi_Host *, - uint, uint, uint, void *hostdata); + uint, uint, u64, void *hostdata); extern int scsi_add_device(struct Scsi_Host *host, uint channel, - uint target, uint lun); + uint target, u64 lun); extern int scsi_register_device_handler(struct scsi_device_handler *scsi_dh); extern void scsi_remove_device(struct scsi_device *); extern int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh); @@ -332,13 +332,13 @@ void scsi_attach_vpd(struct scsi_device *sdev); extern int scsi_device_get(struct scsi_device *); extern void scsi_device_put(struct scsi_device *); extern struct scsi_device *scsi_device_lookup(struct Scsi_Host *, - uint, uint, uint); + uint, uint, u64); extern struct scsi_device *__scsi_device_lookup(struct Scsi_Host *, - uint, uint, uint); + uint, uint, u64); extern struct scsi_device *scsi_device_lookup_by_target(struct scsi_target *, - uint); + u64); extern struct scsi_device *__scsi_device_lookup_by_target(struct scsi_target *, - uint); + u64); extern void starget_for_each_device(struct scsi_target *, void *, void (*fn)(struct scsi_device *, void *)); extern void __starget_for_each_device(struct scsi_target *, void *, @@ -411,13 +411,13 @@ extern void scsi_device_resume(struct scsi_device *sdev); extern void scsi_target_quiesce(struct scsi_target *); extern void scsi_target_resume(struct scsi_target *); extern void scsi_scan_target(struct device *parent, unsigned int channel, - unsigned int id, unsigned int lun, int rescan); + unsigned int id, u64 lun, int rescan); extern void scsi_target_reap(struct scsi_target *); extern void scsi_target_block(struct device *); extern void scsi_target_unblock(struct device *, enum scsi_device_state); extern void scsi_remove_target(struct device *); -extern void int_to_scsilun(unsigned int, struct scsi_lun *); -extern int scsilun_to_int(struct scsi_lun *); +extern void int_to_scsilun(u64, struct scsi_lun *); +extern u64 scsilun_to_int(struct scsi_lun *); extern const char *scsi_device_state_name(enum scsi_device_state); extern int scsi_is_sdev_device(const struct device *); extern int scsi_is_target_device(const struct device *); diff --git a/include/scsi/scsi_transport.h b/include/scsi/scsi_transport.h index af244f4bba53..81292392adbc 100644 --- a/include/scsi/scsi_transport.h +++ b/include/scsi/scsi_transport.h @@ -35,7 +35,7 @@ struct scsi_transport_template { /* * If set, called from sysfs and legacy procfs rescanning code. */ - int (*user_scan)(struct Scsi_Host *, uint, uint, uint); + int (*user_scan)(struct Scsi_Host *, uint, uint, u64); /* The size of the specific transport attribute structure (a * space of this size will be left at the end of the -- cgit v1.2.3 From b4210b810e5040f10a30ba56de6c3faab5c49345 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Wed, 25 Jun 2014 15:27:37 +0200 Subject: Add module param type 'ullong' Some driver might want to pass in an 64-bit value, so introduce a module param type 'ullong'. Signed-off-by: Hannes Reinecke Reviewed-by: Christoph Hellwig Reviewed-by: Ewan Milne Acked-by: Rusty Russell Signed-off-by: Christoph Hellwig --- include/linux/moduleparam.h | 5 +++++ kernel/params.c | 1 + 2 files changed, 6 insertions(+) (limited to 'include') diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h index b1990c5524e1..494f99e852da 100644 --- a/include/linux/moduleparam.h +++ b/include/linux/moduleparam.h @@ -381,6 +381,11 @@ extern int param_set_ulong(const char *val, const struct kernel_param *kp); extern int param_get_ulong(char *buffer, const struct kernel_param *kp); #define param_check_ulong(name, p) __param_check(name, p, unsigned long) +extern struct kernel_param_ops param_ops_ullong; +extern int param_set_ullong(const char *val, const struct kernel_param *kp); +extern int param_get_ullong(char *buffer, const struct kernel_param *kp); +#define param_check_ullong(name, p) __param_check(name, p, unsigned long long) + extern struct kernel_param_ops param_ops_charp; extern int param_set_charp(const char *val, const struct kernel_param *kp); extern int param_get_charp(char *buffer, const struct kernel_param *kp); diff --git a/kernel/params.c b/kernel/params.c index 1e52ca233fd9..34f527023794 100644 --- a/kernel/params.c +++ b/kernel/params.c @@ -256,6 +256,7 @@ STANDARD_PARAM_DEF(int, int, "%i", kstrtoint); STANDARD_PARAM_DEF(uint, unsigned int, "%u", kstrtouint); STANDARD_PARAM_DEF(long, long, "%li", kstrtol); STANDARD_PARAM_DEF(ulong, unsigned long, "%lu", kstrtoul); +STANDARD_PARAM_DEF(ullong, unsigned long long, "%llu", kstrtoull); int param_set_charp(const char *val, const struct kernel_param *kp) { -- cgit v1.2.3 From 1abf635d2f3332641570e1913e317073834a055f Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Wed, 25 Jun 2014 15:27:38 +0200 Subject: scsi: use 64-bit value for 'max_luns' Now that we're using 64-bit LUNs internally we need to increase the size of max_luns to 64 bits, too. Signed-off-by: Hannes Reinecke Reviewed-by: Christoph Hellwig Reviewed-by: Ewan Milne Signed-off-by: Christoph Hellwig --- drivers/message/i2o/i2o_scsi.c | 2 +- drivers/scsi/advansys.c | 2 +- drivers/scsi/cxgbi/libcxgbi.c | 2 +- drivers/scsi/cxgbi/libcxgbi.h | 2 +- drivers/scsi/dc395x.c | 2 +- drivers/scsi/eata.c | 2 +- drivers/scsi/ibmvscsi/ibmvfc.c | 4 ++-- drivers/scsi/lpfc/lpfc_attr.c | 10 +++++++++- drivers/scsi/megaraid.c | 2 +- drivers/scsi/mpt3sas/mpt3sas_scsih.c | 4 ++-- drivers/scsi/qla2xxx/qla_gbl.h | 2 +- drivers/scsi/qla2xxx/qla_os.c | 6 +++--- drivers/scsi/scsi_scan.c | 6 +++--- include/scsi/scsi_host.h | 6 +++--- 14 files changed, 30 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/message/i2o/i2o_scsi.c b/drivers/message/i2o/i2o_scsi.c index e7de92c67cf6..8152e9fa9d95 100644 --- a/drivers/message/i2o/i2o_scsi.c +++ b/drivers/message/i2o/i2o_scsi.c @@ -287,7 +287,7 @@ static int i2o_scsi_probe(struct device *dev) } if (le64_to_cpu(lun) >= scsi_host->max_lun) { - osm_warn("SCSI device lun (%llu) >= max_lun of I2O host (%d)", + osm_warn("SCSI device lun (%llu) >= max_lun of I2O host (%llu)", le64_to_cpu(lun), scsi_host->max_lun); return -EFAULT; } diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c index d8145888e66a..e716d0aef194 100644 --- a/drivers/scsi/advansys.c +++ b/drivers/scsi/advansys.c @@ -3345,7 +3345,7 @@ static void asc_prt_driver_conf(struct seq_file *m, struct Scsi_Host *shost) shost->host_no); seq_printf(m, - " host_busy %u, max_id %u, max_lun %u, max_channel %u\n", + " host_busy %u, max_id %u, max_lun %llu, max_channel %u\n", shost->host_busy, shost->max_id, shost->max_lun, shost->max_channel); diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c index b44c1cff3114..dc812069046c 100644 --- a/drivers/scsi/cxgbi/libcxgbi.c +++ b/drivers/scsi/cxgbi/libcxgbi.c @@ -245,7 +245,7 @@ void cxgbi_hbas_remove(struct cxgbi_device *cdev) } EXPORT_SYMBOL_GPL(cxgbi_hbas_remove); -int cxgbi_hbas_add(struct cxgbi_device *cdev, unsigned int max_lun, +int cxgbi_hbas_add(struct cxgbi_device *cdev, u64 max_lun, unsigned int max_id, struct scsi_host_template *sht, struct scsi_transport_template *stt) { diff --git a/drivers/scsi/cxgbi/libcxgbi.h b/drivers/scsi/cxgbi/libcxgbi.h index 8135f04671af..538d7a64e138 100644 --- a/drivers/scsi/cxgbi/libcxgbi.h +++ b/drivers/scsi/cxgbi/libcxgbi.h @@ -692,7 +692,7 @@ struct cxgbi_device *cxgbi_device_register(unsigned int, unsigned int); void cxgbi_device_unregister(struct cxgbi_device *); void cxgbi_device_unregister_all(unsigned int flag); struct cxgbi_device *cxgbi_device_find_by_lldev(void *); -int cxgbi_hbas_add(struct cxgbi_device *, unsigned int, unsigned int, +int cxgbi_hbas_add(struct cxgbi_device *, u64, unsigned int, struct scsi_host_template *, struct scsi_transport_template *); void cxgbi_hbas_remove(struct cxgbi_device *); diff --git a/drivers/scsi/dc395x.c b/drivers/scsi/dc395x.c index dff461f2338e..0c6be0a17f53 100644 --- a/drivers/scsi/dc395x.c +++ b/drivers/scsi/dc395x.c @@ -4638,7 +4638,7 @@ static int dc395x_show_info(struct seq_file *m, struct Scsi_Host *host) SPRINTF("irq_level 0x%04x, ", acb->irq_level); SPRINTF(" SelTimeout %ims\n", (1638 * acb->sel_timeout) / 1000); - SPRINTF("MaxID %i, MaxLUN %i, ", host->max_id, host->max_lun); + SPRINTF("MaxID %i, MaxLUN %llu, ", host->max_id, host->max_lun); SPRINTF("AdapterID %i\n", host->this_id); SPRINTF("tag_max_num %i", acb->tag_max_num); diff --git a/drivers/scsi/eata.c b/drivers/scsi/eata.c index 0f27ac19cf01..03372cff38f3 100644 --- a/drivers/scsi/eata.c +++ b/drivers/scsi/eata.c @@ -1399,7 +1399,7 @@ static int port_detect(unsigned long port_base, unsigned int j, if (shost->max_id > 8 || shost->max_lun > 8) printk - ("%s: wide SCSI support enabled, max_id %u, max_lun %u.\n", + ("%s: wide SCSI support enabled, max_id %u, max_lun %llu.\n", ha->board_name, shost->max_id, shost->max_lun); for (i = 0; i <= shost->max_channel; i++) diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c index 8dd47689d584..6eb47b683569 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc.c @@ -46,7 +46,7 @@ static unsigned int init_timeout = IBMVFC_INIT_TIMEOUT; static unsigned int default_timeout = IBMVFC_DEFAULT_TIMEOUT; -static unsigned int max_lun = IBMVFC_MAX_LUN; +static u64 max_lun = IBMVFC_MAX_LUN; static unsigned int max_targets = IBMVFC_MAX_TARGETS; static unsigned int max_requests = IBMVFC_MAX_REQUESTS_DEFAULT; static unsigned int disc_threads = IBMVFC_MAX_DISC_THREADS; @@ -71,7 +71,7 @@ MODULE_PARM_DESC(default_timeout, module_param_named(max_requests, max_requests, uint, S_IRUGO); MODULE_PARM_DESC(max_requests, "Maximum requests for this adapter. " "[Default=" __stringify(IBMVFC_MAX_REQUESTS_DEFAULT) "]"); -module_param_named(max_lun, max_lun, uint, S_IRUGO); +module_param_named(max_lun, max_lun, ullong, S_IRUGO); MODULE_PARM_DESC(max_lun, "Maximum allowed LUN. " "[Default=" __stringify(IBMVFC_MAX_LUN) "]"); module_param_named(max_targets, max_targets, uint, S_IRUGO); diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 1d7a5c34ee8c..6eed9e76a166 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -1998,6 +1998,14 @@ lpfc_vport_param_show(name)\ lpfc_vport_param_init(name, defval, minval, maxval)\ static DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL) +#define LPFC_VPORT_ULL_ATTR_R(name, defval, minval, maxval, desc) \ +static uint64_t lpfc_##name = defval;\ +module_param(lpfc_##name, ullong, S_IRUGO);\ +MODULE_PARM_DESC(lpfc_##name, desc);\ +lpfc_vport_param_show(name)\ +lpfc_vport_param_init(name, defval, minval, maxval)\ +static DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL) + #define LPFC_VPORT_ATTR_RW(name, defval, minval, maxval, desc) \ static uint lpfc_##name = defval;\ module_param(lpfc_##name, uint, S_IRUGO);\ @@ -4596,7 +4604,7 @@ LPFC_VPORT_ATTR(discovery_threads, 32, 1, 64, "Maximum number of ELS commands " # Value range is [0,65535]. Default value is 255. # NOTE: The SCSI layer might probe all allowed LUN on some old targets. */ -LPFC_VPORT_ATTR_R(max_luns, 255, 0, 65535, "Maximum allowed LUN ID"); +LPFC_VPORT_ULL_ATTR_R(max_luns, 255, 0, 65535, "Maximum allowed LUN ID"); /* # lpfc_poll_tmo: .Milliseconds driver will wait between polling FCP ring. diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c index 6bb2b52646b4..ac5d94cfd52f 100644 --- a/drivers/scsi/megaraid.c +++ b/drivers/scsi/megaraid.c @@ -1860,7 +1860,7 @@ megaraid_info(struct Scsi_Host *host) "LSI Logic MegaRAID %s %d commands %d targs %d chans %d luns", adapter->fw_version, adapter->product_info.max_commands, adapter->host->max_id, adapter->host->max_channel, - adapter->host->max_lun); + (u32)adapter->host->max_lun); return buffer; } diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 18e713db1d32..0274ac3c533b 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -112,8 +112,8 @@ MODULE_PARM_DESC(missing_delay, " device missing delay , io missing delay"); /* scsi-mid layer global parmeter is max_report_luns, which is 511 */ #define MPT3SAS_MAX_LUN (16895) -static int max_lun = MPT3SAS_MAX_LUN; -module_param(max_lun, int, 0); +static u64 max_lun = MPT3SAS_MAX_LUN; +module_param(max_lun, ullong, 0); MODULE_PARM_DESC(max_lun, " max lun, default=16895 "); diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index 9ef0781b12da..d646540db3ac 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -113,7 +113,7 @@ extern int ql2xenabledif; extern int ql2xenablehba_err_chk; extern int ql2xtargetreset; extern int ql2xdontresethba; -extern unsigned int ql2xmaxlun; +extern uint64_t ql2xmaxlun; extern int ql2xmdcapmask; extern int ql2xmdenable; diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index b22c75305b44..be9698d920c2 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -202,8 +202,8 @@ MODULE_PARM_DESC(ql2xdontresethba, " 0 (Default) -- Reset on failure.\n" " 1 -- Do not reset on failure.\n"); -uint ql2xmaxlun = MAX_LUNS; -module_param(ql2xmaxlun, uint, S_IRUGO); +uint64_t ql2xmaxlun = MAX_LUNS; +module_param(ql2xmaxlun, ullong, S_IRUGO); MODULE_PARM_DESC(ql2xmaxlun, "Defines the maximum LU number to register with the SCSI " "midlayer. Default is 65535."); @@ -2676,7 +2676,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ql_dbg(ql_dbg_init, base_vha, 0x0033, "max_id=%d this_id=%d " "cmd_per_len=%d unique_id=%d max_cmd_len=%d max_channel=%d " - "max_lun=%d transportt=%p, vendor_id=%llu.\n", host->max_id, + "max_lun=%llu transportt=%p, vendor_id=%llu.\n", host->max_id, host->this_id, host->cmd_per_lun, host->unique_id, host->max_cmd_len, host->max_channel, host->max_lun, host->transportt, sht->vendor_id); diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 1a71547832f3..fa57a046f025 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -81,11 +81,11 @@ static const char *scsi_null_device_strs = "nullnullnullnull"; #define MAX_SCSI_LUNS 512 -static unsigned int max_scsi_luns = MAX_SCSI_LUNS; +static u64 max_scsi_luns = MAX_SCSI_LUNS; -module_param_named(max_luns, max_scsi_luns, uint, S_IRUGO|S_IWUSR); +module_param_named(max_luns, max_scsi_luns, ullong, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(max_luns, - "last scsi LUN (should be between 1 and 2^32-1)"); + "last scsi LUN (should be between 1 and 2^64-1)"); #ifdef CONFIG_SCSI_SCAN_ASYNC #define SCSI_SCAN_TYPE_DEFAULT "async" diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index db7d8bd2f86f..abb695882fed 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -623,11 +623,11 @@ struct Scsi_Host { * These three parameters can be used to allow for wide scsi, * and for host adapters that support multiple busses * The first two should be set to 1 more than the actual max id - * or lun (i.e. 8 for normal systems). + * or lun (e.g. 8 for SCSI parallel systems). */ - unsigned int max_id; - unsigned int max_lun; unsigned int max_channel; + unsigned int max_id; + u64 max_lun; /* * This is a unique identifier that must be assigned so that we -- cgit v1.2.3 From f6667938cfceefd8afe6355ceb6497dce4883ae9 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 15 Apr 2014 12:26:51 +0200 Subject: libsrp: removal Remove the libsrp module which was only used by the now removed ibmvstgt driver. Signed-off-by: Christoph Hellwig Reviewed-by: Paolo Bonzini Reviewed-by: Hannes Reinecke --- drivers/scsi/Kconfig | 10 -- drivers/scsi/Makefile | 1 - drivers/scsi/libsrp.c | 447 -------------------------------------------------- include/scsi/libsrp.h | 78 --------- 4 files changed, 536 deletions(-) delete mode 100644 drivers/scsi/libsrp.c delete mode 100644 include/scsi/libsrp.h (limited to 'include') diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 10b3cc885d9c..67d1ac342451 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1722,16 +1722,6 @@ config SCSI_PM8001 This driver supports PMC-Sierra PCIE SAS/SATA 8x6G SPC 8001 chip based host adapters. -config SCSI_SRP - tristate "SCSI RDMA Protocol helper library" - depends on SCSI && PCI - select SCSI_TGT - help - If you wish to use SRP target drivers, say Y. - - To compile this driver as a module, choose M here: the - module will be called libsrp. - config SCSI_BFA_FC tristate "Brocade BFA Fibre Channel Support" depends on PCI && SCSI diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index aacad2f077ea..e069e95a2f96 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -127,7 +127,6 @@ obj-$(CONFIG_SCSI_LASI700) += 53c700.o lasi700.o obj-$(CONFIG_SCSI_SNI_53C710) += 53c700.o sni_53c710.o obj-$(CONFIG_SCSI_NSP32) += nsp32.o obj-$(CONFIG_SCSI_IPR) += ipr.o -obj-$(CONFIG_SCSI_SRP) += libsrp.o obj-$(CONFIG_SCSI_IBMVSCSI) += ibmvscsi/ obj-$(CONFIG_SCSI_IBMVFC) += ibmvscsi/ obj-$(CONFIG_SCSI_HPTIOP) += hptiop.o diff --git a/drivers/scsi/libsrp.c b/drivers/scsi/libsrp.c deleted file mode 100644 index 0707ecdbaa32..000000000000 --- a/drivers/scsi/libsrp.c +++ /dev/null @@ -1,447 +0,0 @@ -/* - * SCSI RDMA Protocol lib functions - * - * Copyright (C) 2006 FUJITA Tomonori - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -enum srp_task_attributes { - SRP_SIMPLE_TASK = 0, - SRP_HEAD_TASK = 1, - SRP_ORDERED_TASK = 2, - SRP_ACA_TASK = 4 -}; - -/* tmp - will replace with SCSI logging stuff */ -#define eprintk(fmt, args...) \ -do { \ - printk("%s(%d) " fmt, __func__, __LINE__, ##args); \ -} while (0) -/* #define dprintk eprintk */ -#define dprintk(fmt, args...) - -static int srp_iu_pool_alloc(struct srp_queue *q, size_t max, - struct srp_buf **ring) -{ - int i; - struct iu_entry *iue; - - q->pool = kcalloc(max, sizeof(struct iu_entry *), GFP_KERNEL); - if (!q->pool) - return -ENOMEM; - q->items = kcalloc(max, sizeof(struct iu_entry), GFP_KERNEL); - if (!q->items) - goto free_pool; - - spin_lock_init(&q->lock); - kfifo_init(&q->queue, (void *) q->pool, max * sizeof(void *)); - - for (i = 0, iue = q->items; i < max; i++) { - kfifo_in(&q->queue, (void *) &iue, sizeof(void *)); - iue->sbuf = ring[i]; - iue++; - } - return 0; - - kfree(q->items); -free_pool: - kfree(q->pool); - return -ENOMEM; -} - -static void srp_iu_pool_free(struct srp_queue *q) -{ - kfree(q->items); - kfree(q->pool); -} - -static struct srp_buf **srp_ring_alloc(struct device *dev, - size_t max, size_t size) -{ - int i; - struct srp_buf **ring; - - ring = kcalloc(max, sizeof(struct srp_buf *), GFP_KERNEL); - if (!ring) - return NULL; - - for (i = 0; i < max; i++) { - ring[i] = kzalloc(sizeof(struct srp_buf), GFP_KERNEL); - if (!ring[i]) - goto out; - ring[i]->buf = dma_alloc_coherent(dev, size, &ring[i]->dma, - GFP_KERNEL); - if (!ring[i]->buf) - goto out; - } - return ring; - -out: - for (i = 0; i < max && ring[i]; i++) { - if (ring[i]->buf) - dma_free_coherent(dev, size, ring[i]->buf, ring[i]->dma); - kfree(ring[i]); - } - kfree(ring); - - return NULL; -} - -static void srp_ring_free(struct device *dev, struct srp_buf **ring, size_t max, - size_t size) -{ - int i; - - for (i = 0; i < max; i++) { - dma_free_coherent(dev, size, ring[i]->buf, ring[i]->dma); - kfree(ring[i]); - } - kfree(ring); -} - -int srp_target_alloc(struct srp_target *target, struct device *dev, - size_t nr, size_t iu_size) -{ - int err; - - spin_lock_init(&target->lock); - INIT_LIST_HEAD(&target->cmd_queue); - - target->dev = dev; - dev_set_drvdata(target->dev, target); - - target->srp_iu_size = iu_size; - target->rx_ring_size = nr; - target->rx_ring = srp_ring_alloc(target->dev, nr, iu_size); - if (!target->rx_ring) - return -ENOMEM; - err = srp_iu_pool_alloc(&target->iu_queue, nr, target->rx_ring); - if (err) - goto free_ring; - - return 0; - -free_ring: - srp_ring_free(target->dev, target->rx_ring, nr, iu_size); - return -ENOMEM; -} -EXPORT_SYMBOL_GPL(srp_target_alloc); - -void srp_target_free(struct srp_target *target) -{ - srp_ring_free(target->dev, target->rx_ring, target->rx_ring_size, - target->srp_iu_size); - srp_iu_pool_free(&target->iu_queue); -} -EXPORT_SYMBOL_GPL(srp_target_free); - -struct iu_entry *srp_iu_get(struct srp_target *target) -{ - struct iu_entry *iue = NULL; - - if (kfifo_out_locked(&target->iu_queue.queue, (void *) &iue, - sizeof(void *), &target->iu_queue.lock) != sizeof(void *)) { - WARN_ONCE(1, "unexpected fifo state"); - return NULL; - } - if (!iue) - return iue; - iue->target = target; - INIT_LIST_HEAD(&iue->ilist); - iue->flags = 0; - return iue; -} -EXPORT_SYMBOL_GPL(srp_iu_get); - -void srp_iu_put(struct iu_entry *iue) -{ - kfifo_in_locked(&iue->target->iu_queue.queue, (void *) &iue, - sizeof(void *), &iue->target->iu_queue.lock); -} -EXPORT_SYMBOL_GPL(srp_iu_put); - -static int srp_direct_data(struct scsi_cmnd *sc, struct srp_direct_buf *md, - enum dma_data_direction dir, srp_rdma_t rdma_io, - int dma_map, int ext_desc) -{ - struct iu_entry *iue = NULL; - struct scatterlist *sg = NULL; - int err, nsg = 0, len; - - if (dma_map) { - iue = (struct iu_entry *) sc->SCp.ptr; - sg = scsi_sglist(sc); - - dprintk("%p %u %u %d\n", iue, scsi_bufflen(sc), - md->len, scsi_sg_count(sc)); - - nsg = dma_map_sg(iue->target->dev, sg, scsi_sg_count(sc), - DMA_BIDIRECTIONAL); - if (!nsg) { - printk("fail to map %p %d\n", iue, scsi_sg_count(sc)); - return 0; - } - len = min(scsi_bufflen(sc), md->len); - } else - len = md->len; - - err = rdma_io(sc, sg, nsg, md, 1, dir, len); - - if (dma_map) - dma_unmap_sg(iue->target->dev, sg, nsg, DMA_BIDIRECTIONAL); - - return err; -} - -static int srp_indirect_data(struct scsi_cmnd *sc, struct srp_cmd *cmd, - struct srp_indirect_buf *id, - enum dma_data_direction dir, srp_rdma_t rdma_io, - int dma_map, int ext_desc) -{ - struct iu_entry *iue = NULL; - struct srp_direct_buf *md = NULL; - struct scatterlist dummy, *sg = NULL; - dma_addr_t token = 0; - int err = 0; - int nmd, nsg = 0, len; - - if (dma_map || ext_desc) { - iue = (struct iu_entry *) sc->SCp.ptr; - sg = scsi_sglist(sc); - - dprintk("%p %u %u %d %d\n", - iue, scsi_bufflen(sc), id->len, - cmd->data_in_desc_cnt, cmd->data_out_desc_cnt); - } - - nmd = id->table_desc.len / sizeof(struct srp_direct_buf); - - if ((dir == DMA_FROM_DEVICE && nmd == cmd->data_in_desc_cnt) || - (dir == DMA_TO_DEVICE && nmd == cmd->data_out_desc_cnt)) { - md = &id->desc_list[0]; - goto rdma; - } - - if (ext_desc && dma_map) { - md = dma_alloc_coherent(iue->target->dev, id->table_desc.len, - &token, GFP_KERNEL); - if (!md) { - eprintk("Can't get dma memory %u\n", id->table_desc.len); - return -ENOMEM; - } - - sg_init_one(&dummy, md, id->table_desc.len); - sg_dma_address(&dummy) = token; - sg_dma_len(&dummy) = id->table_desc.len; - err = rdma_io(sc, &dummy, 1, &id->table_desc, 1, DMA_TO_DEVICE, - id->table_desc.len); - if (err) { - eprintk("Error copying indirect table %d\n", err); - goto free_mem; - } - } else { - eprintk("This command uses external indirect buffer\n"); - return -EINVAL; - } - -rdma: - if (dma_map) { - nsg = dma_map_sg(iue->target->dev, sg, scsi_sg_count(sc), - DMA_BIDIRECTIONAL); - if (!nsg) { - eprintk("fail to map %p %d\n", iue, scsi_sg_count(sc)); - err = -EIO; - goto free_mem; - } - len = min(scsi_bufflen(sc), id->len); - } else - len = id->len; - - err = rdma_io(sc, sg, nsg, md, nmd, dir, len); - - if (dma_map) - dma_unmap_sg(iue->target->dev, sg, nsg, DMA_BIDIRECTIONAL); - -free_mem: - if (token && dma_map) - dma_free_coherent(iue->target->dev, id->table_desc.len, md, token); - - return err; -} - -static int data_out_desc_size(struct srp_cmd *cmd) -{ - int size = 0; - u8 fmt = cmd->buf_fmt >> 4; - - switch (fmt) { - case SRP_NO_DATA_DESC: - break; - case SRP_DATA_DESC_DIRECT: - size = sizeof(struct srp_direct_buf); - break; - case SRP_DATA_DESC_INDIRECT: - size = sizeof(struct srp_indirect_buf) + - sizeof(struct srp_direct_buf) * cmd->data_out_desc_cnt; - break; - default: - eprintk("client error. Invalid data_out_format %x\n", fmt); - break; - } - return size; -} - -/* - * TODO: this can be called multiple times for a single command if it - * has very long data. - */ -int srp_transfer_data(struct scsi_cmnd *sc, struct srp_cmd *cmd, - srp_rdma_t rdma_io, int dma_map, int ext_desc) -{ - struct srp_direct_buf *md; - struct srp_indirect_buf *id; - enum dma_data_direction dir; - int offset, err = 0; - u8 format; - - offset = cmd->add_cdb_len & ~3; - - dir = srp_cmd_direction(cmd); - if (dir == DMA_FROM_DEVICE) - offset += data_out_desc_size(cmd); - - if (dir == DMA_TO_DEVICE) - format = cmd->buf_fmt >> 4; - else - format = cmd->buf_fmt & ((1U << 4) - 1); - - switch (format) { - case SRP_NO_DATA_DESC: - break; - case SRP_DATA_DESC_DIRECT: - md = (struct srp_direct_buf *) - (cmd->add_data + offset); - err = srp_direct_data(sc, md, dir, rdma_io, dma_map, ext_desc); - break; - case SRP_DATA_DESC_INDIRECT: - id = (struct srp_indirect_buf *) - (cmd->add_data + offset); - err = srp_indirect_data(sc, cmd, id, dir, rdma_io, dma_map, - ext_desc); - break; - default: - eprintk("Unknown format %d %x\n", dir, format); - err = -EINVAL; - } - - return err; -} -EXPORT_SYMBOL_GPL(srp_transfer_data); - -static int vscsis_data_length(struct srp_cmd *cmd, enum dma_data_direction dir) -{ - struct srp_direct_buf *md; - struct srp_indirect_buf *id; - int len = 0, offset = cmd->add_cdb_len & ~3; - u8 fmt; - - if (dir == DMA_TO_DEVICE) - fmt = cmd->buf_fmt >> 4; - else { - fmt = cmd->buf_fmt & ((1U << 4) - 1); - offset += data_out_desc_size(cmd); - } - - switch (fmt) { - case SRP_NO_DATA_DESC: - break; - case SRP_DATA_DESC_DIRECT: - md = (struct srp_direct_buf *) (cmd->add_data + offset); - len = md->len; - break; - case SRP_DATA_DESC_INDIRECT: - id = (struct srp_indirect_buf *) (cmd->add_data + offset); - len = id->len; - break; - default: - eprintk("invalid data format %x\n", fmt); - break; - } - return len; -} - -int srp_cmd_queue(struct Scsi_Host *shost, struct srp_cmd *cmd, void *info, - u64 itn_id, u64 addr) -{ - enum dma_data_direction dir; - struct scsi_cmnd *sc; - int tag, len, err; - - switch (cmd->task_attr) { - case SRP_SIMPLE_TASK: - tag = MSG_SIMPLE_TAG; - break; - case SRP_ORDERED_TASK: - tag = MSG_ORDERED_TAG; - break; - case SRP_HEAD_TASK: - tag = MSG_HEAD_TAG; - break; - default: - eprintk("Task attribute %d not supported\n", cmd->task_attr); - tag = MSG_ORDERED_TAG; - } - - dir = srp_cmd_direction(cmd); - len = vscsis_data_length(cmd, dir); - - dprintk("%p %x %lx %d %d %d %llx\n", info, cmd->cdb[0], - cmd->lun, dir, len, tag, (unsigned long long) cmd->tag); - - sc = scsi_host_get_command(shost, dir, GFP_KERNEL); - if (!sc) - return -ENOMEM; - - sc->SCp.ptr = info; - memcpy(sc->cmnd, cmd->cdb, MAX_COMMAND_SIZE); - sc->sdb.length = len; - sc->sdb.table.sgl = (void *) (unsigned long) addr; - sc->tag = tag; - err = scsi_tgt_queue_command(sc, itn_id, (struct scsi_lun *)&cmd->lun, - cmd->tag); - if (err) - scsi_host_put_command(shost, sc); - - return err; -} -EXPORT_SYMBOL_GPL(srp_cmd_queue); - -MODULE_DESCRIPTION("SCSI RDMA Protocol lib functions"); -MODULE_AUTHOR("FUJITA Tomonori"); -MODULE_LICENSE("GPL"); diff --git a/include/scsi/libsrp.h b/include/scsi/libsrp.h deleted file mode 100644 index f4105c91af53..000000000000 --- a/include/scsi/libsrp.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef __LIBSRP_H__ -#define __LIBSRP_H__ - -#include -#include -#include -#include -#include - -enum iue_flags { - V_DIOVER, - V_WRITE, - V_LINKED, - V_FLYING, -}; - -struct srp_buf { - dma_addr_t dma; - void *buf; -}; - -struct srp_queue { - void *pool; - void *items; - struct kfifo queue; - spinlock_t lock; -}; - -struct srp_target { - struct Scsi_Host *shost; - struct device *dev; - - spinlock_t lock; - struct list_head cmd_queue; - - size_t srp_iu_size; - struct srp_queue iu_queue; - size_t rx_ring_size; - struct srp_buf **rx_ring; - - void *ldata; -}; - -struct iu_entry { - struct srp_target *target; - - struct list_head ilist; - dma_addr_t remote_token; - unsigned long flags; - - struct srp_buf *sbuf; -}; - -typedef int (srp_rdma_t)(struct scsi_cmnd *, struct scatterlist *, int, - struct srp_direct_buf *, int, - enum dma_data_direction, unsigned int); -extern int srp_target_alloc(struct srp_target *, struct device *, size_t, size_t); -extern void srp_target_free(struct srp_target *); - -extern struct iu_entry *srp_iu_get(struct srp_target *); -extern void srp_iu_put(struct iu_entry *); - -extern int srp_cmd_queue(struct Scsi_Host *, struct srp_cmd *, void *, u64, u64); -extern int srp_transfer_data(struct scsi_cmnd *, struct srp_cmd *, - srp_rdma_t, int, int); - - -static inline struct srp_target *host_to_srp_target(struct Scsi_Host *host) -{ - return (struct srp_target *) host->hostdata; -} - -static inline int srp_cmd_direction(struct srp_cmd *cmd) -{ - return (cmd->buf_fmt >> 4) ? DMA_TO_DEVICE : DMA_FROM_DEVICE; -} - -#endif -- cgit v1.2.3 From 066465251303c2a4ba489596f1ecda279711273d Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 15 Apr 2014 12:26:52 +0200 Subject: tgt: removal Now that the ibmvstgt driver as the only user of scsi_tgt is gone, the scsi_tgt kernel module, the CONFIG_SCSI_TGT, CONFIG_SCSI_SRP_TGT_ATTRS and CONFIG_SCSI_FC_TGT_ATTRS kbuild variable, the scsi_host_template transfer_response method are no longer needed. [hch: minor updates to the current tree, changelog update] Signed-off-by: Bart Van Assche Signed-off-by: Christoph Hellwig Reviewed-by: Paolo Bonzini Reviewed-by: Hannes Reinecke --- drivers/scsi/Kconfig | 7 - drivers/scsi/Makefile | 3 - drivers/scsi/scsi_tgt_if.c | 399 ----------------- drivers/scsi/scsi_tgt_lib.c | 661 ----------------------------- drivers/scsi/scsi_tgt_priv.h | 32 -- drivers/scsi/scsi_transport_fc.c | 12 - drivers/scsi/scsi_transport_fc_internal.h | 26 -- drivers/scsi/scsi_transport_srp.c | 18 - drivers/scsi/scsi_transport_srp_internal.h | 25 -- include/scsi/scsi_host.h | 21 - include/scsi/scsi_tgt.h | 21 - include/scsi/scsi_tgt_if.h | 108 ----- 12 files changed, 1333 deletions(-) delete mode 100644 drivers/scsi/scsi_tgt_if.c delete mode 100644 drivers/scsi/scsi_tgt_lib.c delete mode 100644 drivers/scsi/scsi_tgt_priv.h delete mode 100644 drivers/scsi/scsi_transport_fc_internal.h delete mode 100644 drivers/scsi/scsi_transport_srp_internal.h delete mode 100644 include/scsi/scsi_tgt.h delete mode 100644 include/scsi/scsi_tgt_if.h (limited to 'include') diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 67d1ac342451..2f85035e6e97 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -304,13 +304,6 @@ config SCSI_SRP_ATTRS If you wish to export transport-specific information about each attached SRP device to sysfs, say Y. -config SCSI_SRP_TGT_ATTRS - bool "SCSI target support for SRP Transport Attributes" - depends on SCSI_SRP_ATTRS - depends on SCSI_TGT = y || SCSI_TGT = SCSI_SRP_ATTRS - help - If you want to use SCSI target mode drivers enable this option. - endmenu menuconfig SCSI_LOWLEVEL diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index e069e95a2f96..5f0d299b0093 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -20,7 +20,6 @@ CFLAGS_gdth.o = # -DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ -DGDTH_STATISTICS obj-$(CONFIG_PCMCIA) += pcmcia/ obj-$(CONFIG_SCSI) += scsi_mod.o -obj-$(CONFIG_SCSI_TGT) += scsi_tgt.o obj-$(CONFIG_RAID_ATTRS) += raid_class.o @@ -171,8 +170,6 @@ scsi_mod-$(CONFIG_PM) += scsi_pm.o hv_storvsc-y := storvsc_drv.o -scsi_tgt-y += scsi_tgt_lib.o scsi_tgt_if.o - sd_mod-objs := sd.o sd_mod-$(CONFIG_BLK_DEV_INTEGRITY) += sd_dif.o diff --git a/drivers/scsi/scsi_tgt_if.c b/drivers/scsi/scsi_tgt_if.c deleted file mode 100644 index 6209110f295d..000000000000 --- a/drivers/scsi/scsi_tgt_if.c +++ /dev/null @@ -1,399 +0,0 @@ -/* - * SCSI target kernel/user interface functions - * - * Copyright (C) 2005 FUJITA Tomonori - * Copyright (C) 2005 Mike Christie - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "scsi_tgt_priv.h" - -#if TGT_RING_SIZE < PAGE_SIZE -# define TGT_RING_SIZE PAGE_SIZE -#endif - -#define TGT_RING_PAGES (TGT_RING_SIZE >> PAGE_SHIFT) -#define TGT_EVENT_PER_PAGE (PAGE_SIZE / sizeof(struct tgt_event)) -#define TGT_MAX_EVENTS (TGT_EVENT_PER_PAGE * TGT_RING_PAGES) - -struct tgt_ring { - u32 tr_idx; - unsigned long tr_pages[TGT_RING_PAGES]; - spinlock_t tr_lock; -}; - -/* tx_ring : kernel->user, rx_ring : user->kernel */ -static struct tgt_ring tx_ring, rx_ring; -static DECLARE_WAIT_QUEUE_HEAD(tgt_poll_wait); - -static inline void tgt_ring_idx_inc(struct tgt_ring *ring) -{ - if (ring->tr_idx == TGT_MAX_EVENTS - 1) - ring->tr_idx = 0; - else - ring->tr_idx++; -} - -static struct tgt_event *tgt_head_event(struct tgt_ring *ring, u32 idx) -{ - u32 pidx, off; - - pidx = idx / TGT_EVENT_PER_PAGE; - off = idx % TGT_EVENT_PER_PAGE; - - return (struct tgt_event *) - (ring->tr_pages[pidx] + sizeof(struct tgt_event) * off); -} - -static int tgt_uspace_send_event(u32 type, struct tgt_event *p) -{ - struct tgt_event *ev; - struct tgt_ring *ring = &tx_ring; - unsigned long flags; - int err = 0; - - spin_lock_irqsave(&ring->tr_lock, flags); - - ev = tgt_head_event(ring, ring->tr_idx); - if (!ev->hdr.status) - tgt_ring_idx_inc(ring); - else - err = -BUSY; - - spin_unlock_irqrestore(&ring->tr_lock, flags); - - if (err) - return err; - - memcpy(ev, p, sizeof(*ev)); - ev->hdr.type = type; - mb(); - ev->hdr.status = 1; - - flush_dcache_page(virt_to_page(ev)); - - wake_up_interruptible(&tgt_poll_wait); - - return 0; -} - -int scsi_tgt_uspace_send_cmd(struct scsi_cmnd *cmd, u64 itn_id, - struct scsi_lun *lun, u64 tag) -{ - struct Scsi_Host *shost = scsi_tgt_cmd_to_host(cmd); - struct tgt_event ev; - int err; - - memset(&ev, 0, sizeof(ev)); - ev.p.cmd_req.host_no = shost->host_no; - ev.p.cmd_req.itn_id = itn_id; - ev.p.cmd_req.data_len = scsi_bufflen(cmd); - memcpy(ev.p.cmd_req.scb, cmd->cmnd, sizeof(ev.p.cmd_req.scb)); - memcpy(ev.p.cmd_req.lun, lun, sizeof(ev.p.cmd_req.lun)); - ev.p.cmd_req.attribute = cmd->tag; - ev.p.cmd_req.tag = tag; - - dprintk("%p %d %u %x %llx\n", cmd, shost->host_no, - ev.p.cmd_req.data_len, cmd->tag, - (unsigned long long) ev.p.cmd_req.tag); - - err = tgt_uspace_send_event(TGT_KEVENT_CMD_REQ, &ev); - if (err) - eprintk("tx buf is full, could not send\n"); - - return err; -} - -int scsi_tgt_uspace_send_status(struct scsi_cmnd *cmd, u64 itn_id, u64 tag) -{ - struct Scsi_Host *shost = scsi_tgt_cmd_to_host(cmd); - struct tgt_event ev; - int err; - - memset(&ev, 0, sizeof(ev)); - ev.p.cmd_done.host_no = shost->host_no; - ev.p.cmd_done.itn_id = itn_id; - ev.p.cmd_done.tag = tag; - ev.p.cmd_done.result = cmd->result; - - dprintk("%p %d %llu %u %x\n", cmd, shost->host_no, - (unsigned long long) ev.p.cmd_req.tag, - ev.p.cmd_req.data_len, cmd->tag); - - err = tgt_uspace_send_event(TGT_KEVENT_CMD_DONE, &ev); - if (err) - eprintk("tx buf is full, could not send\n"); - - return err; -} - -int scsi_tgt_uspace_send_tsk_mgmt(int host_no, u64 itn_id, int function, - u64 tag, struct scsi_lun *scsilun, void *data) -{ - struct tgt_event ev; - int err; - - memset(&ev, 0, sizeof(ev)); - ev.p.tsk_mgmt_req.host_no = host_no; - ev.p.tsk_mgmt_req.itn_id = itn_id; - ev.p.tsk_mgmt_req.function = function; - ev.p.tsk_mgmt_req.tag = tag; - memcpy(ev.p.tsk_mgmt_req.lun, scsilun, sizeof(ev.p.tsk_mgmt_req.lun)); - ev.p.tsk_mgmt_req.mid = (u64) (unsigned long) data; - - dprintk("%d %x %llx %llx\n", host_no, function, (unsigned long long) tag, - (unsigned long long) ev.p.tsk_mgmt_req.mid); - - err = tgt_uspace_send_event(TGT_KEVENT_TSK_MGMT_REQ, &ev); - if (err) - eprintk("tx buf is full, could not send\n"); - - return err; -} - -int scsi_tgt_uspace_send_it_nexus_request(int host_no, u64 itn_id, - int function, char *initiator_id) -{ - struct tgt_event ev; - int err; - - memset(&ev, 0, sizeof(ev)); - ev.p.it_nexus_req.host_no = host_no; - ev.p.it_nexus_req.function = function; - ev.p.it_nexus_req.itn_id = itn_id; - if (initiator_id) - strncpy(ev.p.it_nexus_req.initiator_id, initiator_id, - sizeof(ev.p.it_nexus_req.initiator_id)); - - dprintk("%d %x %llx\n", host_no, function, (unsigned long long)itn_id); - - err = tgt_uspace_send_event(TGT_KEVENT_IT_NEXUS_REQ, &ev); - if (err) - eprintk("tx buf is full, could not send\n"); - - return err; -} - -static int event_recv_msg(struct tgt_event *ev) -{ - int err = 0; - - switch (ev->hdr.type) { - case TGT_UEVENT_CMD_RSP: - err = scsi_tgt_kspace_exec(ev->p.cmd_rsp.host_no, - ev->p.cmd_rsp.itn_id, - ev->p.cmd_rsp.result, - ev->p.cmd_rsp.tag, - ev->p.cmd_rsp.uaddr, - ev->p.cmd_rsp.len, - ev->p.cmd_rsp.sense_uaddr, - ev->p.cmd_rsp.sense_len, - ev->p.cmd_rsp.rw); - break; - case TGT_UEVENT_TSK_MGMT_RSP: - err = scsi_tgt_kspace_tsk_mgmt(ev->p.tsk_mgmt_rsp.host_no, - ev->p.tsk_mgmt_rsp.itn_id, - ev->p.tsk_mgmt_rsp.mid, - ev->p.tsk_mgmt_rsp.result); - break; - case TGT_UEVENT_IT_NEXUS_RSP: - err = scsi_tgt_kspace_it_nexus_rsp(ev->p.it_nexus_rsp.host_no, - ev->p.it_nexus_rsp.itn_id, - ev->p.it_nexus_rsp.result); - break; - default: - eprintk("unknown type %d\n", ev->hdr.type); - err = -EINVAL; - } - - return err; -} - -static ssize_t tgt_write(struct file *file, const char __user * buffer, - size_t count, loff_t * ppos) -{ - struct tgt_event *ev; - struct tgt_ring *ring = &rx_ring; - - while (1) { - ev = tgt_head_event(ring, ring->tr_idx); - /* do we need this? */ - flush_dcache_page(virt_to_page(ev)); - - if (!ev->hdr.status) - break; - - tgt_ring_idx_inc(ring); - event_recv_msg(ev); - ev->hdr.status = 0; - }; - - return count; -} - -static unsigned int tgt_poll(struct file * file, struct poll_table_struct *wait) -{ - struct tgt_event *ev; - struct tgt_ring *ring = &tx_ring; - unsigned long flags; - unsigned int mask = 0; - u32 idx; - - poll_wait(file, &tgt_poll_wait, wait); - - spin_lock_irqsave(&ring->tr_lock, flags); - - idx = ring->tr_idx ? ring->tr_idx - 1 : TGT_MAX_EVENTS - 1; - ev = tgt_head_event(ring, idx); - if (ev->hdr.status) - mask |= POLLIN | POLLRDNORM; - - spin_unlock_irqrestore(&ring->tr_lock, flags); - - return mask; -} - -static int uspace_ring_map(struct vm_area_struct *vma, unsigned long addr, - struct tgt_ring *ring) -{ - int i, err; - - for (i = 0; i < TGT_RING_PAGES; i++) { - struct page *page = virt_to_page(ring->tr_pages[i]); - err = vm_insert_page(vma, addr, page); - if (err) - return err; - addr += PAGE_SIZE; - } - - return 0; -} - -static int tgt_mmap(struct file *filp, struct vm_area_struct *vma) -{ - unsigned long addr; - int err; - - if (vma->vm_pgoff) - return -EINVAL; - - if (vma->vm_end - vma->vm_start != TGT_RING_SIZE * 2) { - eprintk("mmap size must be %lu, not %lu \n", - TGT_RING_SIZE * 2, vma->vm_end - vma->vm_start); - return -EINVAL; - } - - addr = vma->vm_start; - err = uspace_ring_map(vma, addr, &tx_ring); - if (err) - return err; - err = uspace_ring_map(vma, addr + TGT_RING_SIZE, &rx_ring); - - return err; -} - -static int tgt_open(struct inode *inode, struct file *file) -{ - tx_ring.tr_idx = rx_ring.tr_idx = 0; - - return 0; -} - -static const struct file_operations tgt_fops = { - .owner = THIS_MODULE, - .open = tgt_open, - .poll = tgt_poll, - .write = tgt_write, - .mmap = tgt_mmap, - .llseek = noop_llseek, -}; - -static struct miscdevice tgt_miscdev = { - .minor = MISC_DYNAMIC_MINOR, - .name = "tgt", - .fops = &tgt_fops, -}; - -static void tgt_ring_exit(struct tgt_ring *ring) -{ - int i; - - for (i = 0; i < TGT_RING_PAGES; i++) - free_page(ring->tr_pages[i]); -} - -static int tgt_ring_init(struct tgt_ring *ring) -{ - int i; - - spin_lock_init(&ring->tr_lock); - - for (i = 0; i < TGT_RING_PAGES; i++) { - ring->tr_pages[i] = get_zeroed_page(GFP_KERNEL); - if (!ring->tr_pages[i]) { - eprintk("out of memory\n"); - return -ENOMEM; - } - } - - return 0; -} - -void scsi_tgt_if_exit(void) -{ - tgt_ring_exit(&tx_ring); - tgt_ring_exit(&rx_ring); - misc_deregister(&tgt_miscdev); -} - -int scsi_tgt_if_init(void) -{ - int err; - - err = tgt_ring_init(&tx_ring); - if (err) - return err; - - err = tgt_ring_init(&rx_ring); - if (err) - goto free_tx_ring; - - err = misc_register(&tgt_miscdev); - if (err) - goto free_rx_ring; - - return 0; -free_rx_ring: - tgt_ring_exit(&rx_ring); -free_tx_ring: - tgt_ring_exit(&tx_ring); - - return err; -} diff --git a/drivers/scsi/scsi_tgt_lib.c b/drivers/scsi/scsi_tgt_lib.c deleted file mode 100644 index e51add05fb8d..000000000000 --- a/drivers/scsi/scsi_tgt_lib.c +++ /dev/null @@ -1,661 +0,0 @@ -/* - * SCSI target lib functions - * - * Copyright (C) 2005 Mike Christie - * Copyright (C) 2005 FUJITA Tomonori - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "scsi_tgt_priv.h" - -static struct workqueue_struct *scsi_tgtd; -static struct kmem_cache *scsi_tgt_cmd_cache; - -/* - * TODO: this struct will be killed when the block layer supports large bios - * and James's work struct code is in - */ -struct scsi_tgt_cmd { - /* TODO replace work with James b's code */ - struct work_struct work; - /* TODO fix limits of some drivers */ - struct bio *bio; - - struct list_head hash_list; - struct request *rq; - u64 itn_id; - u64 tag; -}; - -#define TGT_HASH_ORDER 4 -#define cmd_hashfn(tag) hash_long((unsigned long) (tag), TGT_HASH_ORDER) - -struct scsi_tgt_queuedata { - struct Scsi_Host *shost; - struct list_head cmd_hash[1 << TGT_HASH_ORDER]; - spinlock_t cmd_hash_lock; -}; - -/* - * Function: scsi_host_get_command() - * - * Purpose: Allocate and setup a scsi command block and blk request - * - * Arguments: shost - scsi host - * data_dir - dma data dir - * gfp_mask- allocator flags - * - * Returns: The allocated scsi command structure. - * - * This should be called by target LLDs to get a command. - */ -struct scsi_cmnd *scsi_host_get_command(struct Scsi_Host *shost, - enum dma_data_direction data_dir, - gfp_t gfp_mask) -{ - int write = (data_dir == DMA_TO_DEVICE); - struct request *rq; - struct scsi_cmnd *cmd; - struct scsi_tgt_cmd *tcmd; - - /* Bail if we can't get a reference to the device */ - if (!get_device(&shost->shost_gendev)) - return NULL; - - tcmd = kmem_cache_alloc(scsi_tgt_cmd_cache, GFP_ATOMIC); - if (!tcmd) - goto put_dev; - - /* - * The blk helpers are used to the READ/WRITE requests - * transferring data from a initiator point of view. Since - * we are in target mode we want the opposite. - */ - rq = blk_get_request(shost->uspace_req_q, !write, gfp_mask); - if (!rq) - goto free_tcmd; - - cmd = __scsi_get_command(shost, gfp_mask); - if (!cmd) - goto release_rq; - - cmd->sc_data_direction = data_dir; - cmd->jiffies_at_alloc = jiffies; - cmd->request = rq; - - cmd->cmnd = rq->cmd; - - rq->special = cmd; - rq->cmd_type = REQ_TYPE_SPECIAL; - rq->cmd_flags |= REQ_TYPE_BLOCK_PC; - rq->end_io_data = tcmd; - - tcmd->rq = rq; - - return cmd; - -release_rq: - blk_put_request(rq); -free_tcmd: - kmem_cache_free(scsi_tgt_cmd_cache, tcmd); -put_dev: - put_device(&shost->shost_gendev); - return NULL; - -} -EXPORT_SYMBOL_GPL(scsi_host_get_command); - -/* - * Function: scsi_host_put_command() - * - * Purpose: Free a scsi command block - * - * Arguments: shost - scsi host - * cmd - command block to free - * - * Returns: Nothing. - * - * Notes: The command must not belong to any lists. - */ -void scsi_host_put_command(struct Scsi_Host *shost, struct scsi_cmnd *cmd) -{ - struct request_queue *q = shost->uspace_req_q; - struct request *rq = cmd->request; - struct scsi_tgt_cmd *tcmd = rq->end_io_data; - unsigned long flags; - - kmem_cache_free(scsi_tgt_cmd_cache, tcmd); - - spin_lock_irqsave(q->queue_lock, flags); - __blk_put_request(q, rq); - spin_unlock_irqrestore(q->queue_lock, flags); - - __scsi_put_command(shost, cmd); - put_device(&shost->shost_gendev); -} -EXPORT_SYMBOL_GPL(scsi_host_put_command); - -static void cmd_hashlist_del(struct scsi_cmnd *cmd) -{ - struct request_queue *q = cmd->request->q; - struct scsi_tgt_queuedata *qdata = q->queuedata; - unsigned long flags; - struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data; - - spin_lock_irqsave(&qdata->cmd_hash_lock, flags); - list_del(&tcmd->hash_list); - spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags); -} - -static void scsi_unmap_user_pages(struct scsi_tgt_cmd *tcmd) -{ - blk_rq_unmap_user(tcmd->bio); -} - -static void scsi_tgt_cmd_destroy(struct work_struct *work) -{ - struct scsi_tgt_cmd *tcmd = - container_of(work, struct scsi_tgt_cmd, work); - struct scsi_cmnd *cmd = tcmd->rq->special; - - dprintk("cmd %p %d %u\n", cmd, cmd->sc_data_direction, - rq_data_dir(cmd->request)); - scsi_unmap_user_pages(tcmd); - tcmd->rq->bio = NULL; - scsi_host_put_command(scsi_tgt_cmd_to_host(cmd), cmd); -} - -static void init_scsi_tgt_cmd(struct request *rq, struct scsi_tgt_cmd *tcmd, - u64 itn_id, u64 tag) -{ - struct scsi_tgt_queuedata *qdata = rq->q->queuedata; - unsigned long flags; - struct list_head *head; - - tcmd->itn_id = itn_id; - tcmd->tag = tag; - tcmd->bio = NULL; - INIT_WORK(&tcmd->work, scsi_tgt_cmd_destroy); - spin_lock_irqsave(&qdata->cmd_hash_lock, flags); - head = &qdata->cmd_hash[cmd_hashfn(tag)]; - list_add(&tcmd->hash_list, head); - spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags); -} - -/* - * scsi_tgt_alloc_queue - setup queue used for message passing - * shost: scsi host - * - * This should be called by the LLD after host allocation. - * And will be released when the host is released. - */ -int scsi_tgt_alloc_queue(struct Scsi_Host *shost) -{ - struct scsi_tgt_queuedata *queuedata; - struct request_queue *q; - int err, i; - - /* - * Do we need to send a netlink event or should uspace - * just respond to the hotplug event? - */ - q = __scsi_alloc_queue(shost, NULL); - if (!q) - return -ENOMEM; - - queuedata = kzalloc(sizeof(*queuedata), GFP_KERNEL); - if (!queuedata) { - err = -ENOMEM; - goto cleanup_queue; - } - queuedata->shost = shost; - q->queuedata = queuedata; - - /* - * this is a silly hack. We should probably just queue as many - * command as is recvd to userspace. uspace can then make - * sure we do not overload the HBA - */ - q->nr_requests = shost->can_queue; - /* - * We currently only support software LLDs so this does - * not matter for now. Do we need this for the cards we support? - * If so we should make it a host template value. - */ - blk_queue_dma_alignment(q, 0); - shost->uspace_req_q = q; - - for (i = 0; i < ARRAY_SIZE(queuedata->cmd_hash); i++) - INIT_LIST_HEAD(&queuedata->cmd_hash[i]); - spin_lock_init(&queuedata->cmd_hash_lock); - - return 0; - -cleanup_queue: - blk_cleanup_queue(q); - return err; -} -EXPORT_SYMBOL_GPL(scsi_tgt_alloc_queue); - -void scsi_tgt_free_queue(struct Scsi_Host *shost) -{ - int i; - unsigned long flags; - struct request_queue *q = shost->uspace_req_q; - struct scsi_cmnd *cmd; - struct scsi_tgt_queuedata *qdata = q->queuedata; - struct scsi_tgt_cmd *tcmd, *n; - LIST_HEAD(cmds); - - spin_lock_irqsave(&qdata->cmd_hash_lock, flags); - - for (i = 0; i < ARRAY_SIZE(qdata->cmd_hash); i++) { - list_for_each_entry_safe(tcmd, n, &qdata->cmd_hash[i], - hash_list) - list_move(&tcmd->hash_list, &cmds); - } - - spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags); - - while (!list_empty(&cmds)) { - tcmd = list_entry(cmds.next, struct scsi_tgt_cmd, hash_list); - list_del(&tcmd->hash_list); - cmd = tcmd->rq->special; - - shost->hostt->eh_abort_handler(cmd); - scsi_tgt_cmd_destroy(&tcmd->work); - } -} -EXPORT_SYMBOL_GPL(scsi_tgt_free_queue); - -struct Scsi_Host *scsi_tgt_cmd_to_host(struct scsi_cmnd *cmd) -{ - struct scsi_tgt_queuedata *queue = cmd->request->q->queuedata; - return queue->shost; -} -EXPORT_SYMBOL_GPL(scsi_tgt_cmd_to_host); - -/* - * scsi_tgt_queue_command - queue command for userspace processing - * @cmd: scsi command - * @scsilun: scsi lun - * @tag: unique value to identify this command for tmf - */ -int scsi_tgt_queue_command(struct scsi_cmnd *cmd, u64 itn_id, - struct scsi_lun *scsilun, u64 tag) -{ - struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data; - int err; - - init_scsi_tgt_cmd(cmd->request, tcmd, itn_id, tag); - err = scsi_tgt_uspace_send_cmd(cmd, itn_id, scsilun, tag); - if (err) - cmd_hashlist_del(cmd); - - return err; -} -EXPORT_SYMBOL_GPL(scsi_tgt_queue_command); - -/* - * This is run from a interrupt handler normally and the unmap - * needs process context so we must queue - */ -static void scsi_tgt_cmd_done(struct scsi_cmnd *cmd) -{ - struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data; - - dprintk("cmd %p %u\n", cmd, rq_data_dir(cmd->request)); - - scsi_tgt_uspace_send_status(cmd, tcmd->itn_id, tcmd->tag); - - scsi_release_buffers(cmd); - - queue_work(scsi_tgtd, &tcmd->work); -} - -static int scsi_tgt_transfer_response(struct scsi_cmnd *cmd) -{ - struct Scsi_Host *shost = scsi_tgt_cmd_to_host(cmd); - int err; - - dprintk("cmd %p %u\n", cmd, rq_data_dir(cmd->request)); - - err = shost->hostt->transfer_response(cmd, scsi_tgt_cmd_done); - switch (err) { - case SCSI_MLQUEUE_HOST_BUSY: - case SCSI_MLQUEUE_DEVICE_BUSY: - return -EAGAIN; - } - return 0; -} - -/* TODO: test this crap and replace bio_map_user with new interface maybe */ -static int scsi_map_user_pages(struct scsi_tgt_cmd *tcmd, struct scsi_cmnd *cmd, - unsigned long uaddr, unsigned int len, int rw) -{ - struct request_queue *q = cmd->request->q; - struct request *rq = cmd->request; - int err; - - dprintk("%lx %u\n", uaddr, len); - err = blk_rq_map_user(q, rq, NULL, (void *)uaddr, len, GFP_KERNEL); - if (err) { - /* - * TODO: need to fixup sg_tablesize, max_segment_size, - * max_sectors, etc for modern HW and software drivers - * where this value is bogus. - * - * TODO2: we can alloc a reserve buffer of max size - * we can handle and do the slow copy path for really large - * IO. - */ - eprintk("Could not handle request of size %u.\n", len); - return err; - } - - tcmd->bio = rq->bio; - err = scsi_init_io(cmd, GFP_KERNEL); - if (err) { - scsi_release_buffers(cmd); - goto unmap_rq; - } - /* - * we use REQ_TYPE_BLOCK_PC so scsi_init_io doesn't set the - * length for us. - */ - cmd->sdb.length = blk_rq_bytes(rq); - - return 0; - -unmap_rq: - scsi_unmap_user_pages(tcmd); - return err; -} - -static int scsi_tgt_copy_sense(struct scsi_cmnd *cmd, unsigned long uaddr, - unsigned len) -{ - char __user *p = (char __user *) uaddr; - - if (copy_from_user(cmd->sense_buffer, p, - min_t(unsigned, SCSI_SENSE_BUFFERSIZE, len))) { - printk(KERN_ERR "Could not copy the sense buffer\n"); - return -EIO; - } - return 0; -} - -static int scsi_tgt_abort_cmd(struct Scsi_Host *shost, struct scsi_cmnd *cmd) -{ - struct scsi_tgt_cmd *tcmd; - int err; - - err = shost->hostt->eh_abort_handler(cmd); - if (err) - eprintk("fail to abort %p\n", cmd); - - tcmd = cmd->request->end_io_data; - scsi_tgt_cmd_destroy(&tcmd->work); - return err; -} - -static struct request *tgt_cmd_hash_lookup(struct request_queue *q, u64 tag) -{ - struct scsi_tgt_queuedata *qdata = q->queuedata; - struct request *rq = NULL; - struct list_head *head; - struct scsi_tgt_cmd *tcmd; - unsigned long flags; - - head = &qdata->cmd_hash[cmd_hashfn(tag)]; - spin_lock_irqsave(&qdata->cmd_hash_lock, flags); - list_for_each_entry(tcmd, head, hash_list) { - if (tcmd->tag == tag) { - rq = tcmd->rq; - list_del(&tcmd->hash_list); - break; - } - } - spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags); - - return rq; -} - -int scsi_tgt_kspace_exec(int host_no, u64 itn_id, int result, u64 tag, - unsigned long uaddr, u32 len, unsigned long sense_uaddr, - u32 sense_len, u8 rw) -{ - struct Scsi_Host *shost; - struct scsi_cmnd *cmd; - struct request *rq; - struct scsi_tgt_cmd *tcmd; - int err = 0; - - dprintk("%d %llu %d %u %lx %u\n", host_no, (unsigned long long) tag, - result, len, uaddr, rw); - - /* TODO: replace with a O(1) alg */ - shost = scsi_host_lookup(host_no); - if (!shost) { - printk(KERN_ERR "Could not find host no %d\n", host_no); - return -EINVAL; - } - - if (!shost->uspace_req_q) { - printk(KERN_ERR "Not target scsi host %d\n", host_no); - goto done; - } - - rq = tgt_cmd_hash_lookup(shost->uspace_req_q, tag); - if (!rq) { - printk(KERN_ERR "Could not find tag %llu\n", - (unsigned long long) tag); - err = -EINVAL; - goto done; - } - cmd = rq->special; - - dprintk("cmd %p scb %x result %d len %d bufflen %u %u %x\n", - cmd, cmd->cmnd[0], result, len, scsi_bufflen(cmd), - rq_data_dir(rq), cmd->cmnd[0]); - - if (result == TASK_ABORTED) { - scsi_tgt_abort_cmd(shost, cmd); - goto done; - } - /* - * store the userspace values here, the working values are - * in the request_* values - */ - tcmd = cmd->request->end_io_data; - cmd->result = result; - - if (cmd->result == SAM_STAT_CHECK_CONDITION) - scsi_tgt_copy_sense(cmd, sense_uaddr, sense_len); - - if (len) { - err = scsi_map_user_pages(rq->end_io_data, cmd, uaddr, len, rw); - if (err) { - /* - * user-space daemon bugs or OOM - * TODO: we can do better for OOM. - */ - struct scsi_tgt_queuedata *qdata; - struct list_head *head; - unsigned long flags; - - eprintk("cmd %p ret %d uaddr %lx len %d rw %d\n", - cmd, err, uaddr, len, rw); - - qdata = shost->uspace_req_q->queuedata; - head = &qdata->cmd_hash[cmd_hashfn(tcmd->tag)]; - - spin_lock_irqsave(&qdata->cmd_hash_lock, flags); - list_add(&tcmd->hash_list, head); - spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags); - - goto done; - } - } - err = scsi_tgt_transfer_response(cmd); -done: - scsi_host_put(shost); - return err; -} - -int scsi_tgt_tsk_mgmt_request(struct Scsi_Host *shost, u64 itn_id, - int function, u64 tag, struct scsi_lun *scsilun, - void *data) -{ - int err; - - /* TODO: need to retry if this fails. */ - err = scsi_tgt_uspace_send_tsk_mgmt(shost->host_no, itn_id, - function, tag, scsilun, data); - if (err < 0) - eprintk("The task management request lost!\n"); - return err; -} -EXPORT_SYMBOL_GPL(scsi_tgt_tsk_mgmt_request); - -int scsi_tgt_kspace_tsk_mgmt(int host_no, u64 itn_id, u64 mid, int result) -{ - struct Scsi_Host *shost; - int err = -EINVAL; - - dprintk("%d %d %llx\n", host_no, result, (unsigned long long) mid); - - shost = scsi_host_lookup(host_no); - if (!shost) { - printk(KERN_ERR "Could not find host no %d\n", host_no); - return err; - } - - if (!shost->uspace_req_q) { - printk(KERN_ERR "Not target scsi host %d\n", host_no); - goto done; - } - - err = shost->transportt->tsk_mgmt_response(shost, itn_id, mid, result); -done: - scsi_host_put(shost); - return err; -} - -int scsi_tgt_it_nexus_create(struct Scsi_Host *shost, u64 itn_id, - char *initiator) -{ - int err; - - /* TODO: need to retry if this fails. */ - err = scsi_tgt_uspace_send_it_nexus_request(shost->host_no, itn_id, 0, - initiator); - if (err < 0) - eprintk("The i_t_neuxs request lost, %d %llx!\n", - shost->host_no, (unsigned long long)itn_id); - return err; -} -EXPORT_SYMBOL_GPL(scsi_tgt_it_nexus_create); - -int scsi_tgt_it_nexus_destroy(struct Scsi_Host *shost, u64 itn_id) -{ - int err; - - /* TODO: need to retry if this fails. */ - err = scsi_tgt_uspace_send_it_nexus_request(shost->host_no, - itn_id, 1, NULL); - if (err < 0) - eprintk("The i_t_neuxs request lost, %d %llx!\n", - shost->host_no, (unsigned long long)itn_id); - return err; -} -EXPORT_SYMBOL_GPL(scsi_tgt_it_nexus_destroy); - -int scsi_tgt_kspace_it_nexus_rsp(int host_no, u64 itn_id, int result) -{ - struct Scsi_Host *shost; - int err = -EINVAL; - - dprintk("%d %d%llx\n", host_no, result, (unsigned long long)itn_id); - - shost = scsi_host_lookup(host_no); - if (!shost) { - printk(KERN_ERR "Could not find host no %d\n", host_no); - return err; - } - - if (!shost->uspace_req_q) { - printk(KERN_ERR "Not target scsi host %d\n", host_no); - goto done; - } - - err = shost->transportt->it_nexus_response(shost, itn_id, result); -done: - scsi_host_put(shost); - return err; -} - -static int __init scsi_tgt_init(void) -{ - int err; - - scsi_tgt_cmd_cache = KMEM_CACHE(scsi_tgt_cmd, 0); - if (!scsi_tgt_cmd_cache) - return -ENOMEM; - - scsi_tgtd = alloc_workqueue("scsi_tgtd", 0, 1); - if (!scsi_tgtd) { - err = -ENOMEM; - goto free_kmemcache; - } - - err = scsi_tgt_if_init(); - if (err) - goto destroy_wq; - - return 0; - -destroy_wq: - destroy_workqueue(scsi_tgtd); -free_kmemcache: - kmem_cache_destroy(scsi_tgt_cmd_cache); - return err; -} - -static void __exit scsi_tgt_exit(void) -{ - destroy_workqueue(scsi_tgtd); - scsi_tgt_if_exit(); - kmem_cache_destroy(scsi_tgt_cmd_cache); -} - -module_init(scsi_tgt_init); -module_exit(scsi_tgt_exit); - -MODULE_DESCRIPTION("SCSI target core"); -MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/scsi_tgt_priv.h b/drivers/scsi/scsi_tgt_priv.h deleted file mode 100644 index fe4c62177f78..000000000000 --- a/drivers/scsi/scsi_tgt_priv.h +++ /dev/null @@ -1,32 +0,0 @@ -struct scsi_cmnd; -struct scsi_lun; -struct Scsi_Host; -struct task_struct; - -/* tmp - will replace with SCSI logging stuff */ -#define eprintk(fmt, args...) \ -do { \ - printk("%s(%d) " fmt, __func__, __LINE__, ##args); \ -} while (0) - -#define dprintk(fmt, args...) -/* #define dprintk eprintk */ - -extern void scsi_tgt_if_exit(void); -extern int scsi_tgt_if_init(void); - -extern int scsi_tgt_uspace_send_cmd(struct scsi_cmnd *cmd, u64 it_nexus_id, - struct scsi_lun *lun, u64 tag); -extern int scsi_tgt_uspace_send_status(struct scsi_cmnd *cmd, u64 it_nexus_id, - u64 tag); -extern int scsi_tgt_kspace_exec(int host_no, u64 it_nexus_id, int result, u64 tag, - unsigned long uaddr, u32 len, - unsigned long sense_uaddr, u32 sense_len, u8 rw); -extern int scsi_tgt_uspace_send_tsk_mgmt(int host_no, u64 it_nexus_id, - int function, u64 tag, - struct scsi_lun *scsilun, void *data); -extern int scsi_tgt_kspace_tsk_mgmt(int host_no, u64 it_nexus_id, - u64 mid, int result); -extern int scsi_tgt_uspace_send_it_nexus_request(int host_no, u64 it_nexus_id, - int function, char *initiator); -extern int scsi_tgt_kspace_it_nexus_rsp(int host_no, u64 it_nexus_id, int result); diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 8365705c231d..4b0f167e8c21 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -39,7 +39,6 @@ #include #include #include "scsi_priv.h" -#include "scsi_transport_fc_internal.h" static int fc_queue_work(struct Scsi_Host *, struct work_struct *); static void fc_vport_sched_delete(struct work_struct *work); @@ -3008,10 +3007,6 @@ fc_remote_port_delete(struct fc_rport *rport) spin_unlock_irqrestore(shost->host_lock, flags); - if (rport->roles & FC_PORT_ROLE_FCP_INITIATOR && - shost->active_mode & MODE_TARGET) - fc_tgt_it_nexus_destroy(shost, (unsigned long)rport); - scsi_target_block(&rport->dev); /* see if we need to kill io faster than waiting for device loss */ @@ -3052,7 +3047,6 @@ fc_remote_port_rolechg(struct fc_rport *rport, u32 roles) struct fc_host_attrs *fc_host = shost_to_fc_host(shost); unsigned long flags; int create = 0; - int ret; spin_lock_irqsave(shost->host_lock, flags); if (roles & FC_PORT_ROLE_FCP_TARGET) { @@ -3061,12 +3055,6 @@ fc_remote_port_rolechg(struct fc_rport *rport, u32 roles) create = 1; } else if (!(rport->roles & FC_PORT_ROLE_FCP_TARGET)) create = 1; - } else if (shost->active_mode & MODE_TARGET) { - ret = fc_tgt_it_nexus_create(shost, (unsigned long)rport, - (char *)&rport->node_name); - if (ret) - printk(KERN_ERR "FC Remore Port tgt nexus failed %d\n", - ret); } rport->roles = roles; diff --git a/drivers/scsi/scsi_transport_fc_internal.h b/drivers/scsi/scsi_transport_fc_internal.h deleted file mode 100644 index e7bfbe751c1f..000000000000 --- a/drivers/scsi/scsi_transport_fc_internal.h +++ /dev/null @@ -1,26 +0,0 @@ -#include - -#ifdef CONFIG_SCSI_FC_TGT_ATTRS -static inline int fc_tgt_it_nexus_create(struct Scsi_Host *shost, u64 itn_id, - char *initiator) -{ - return scsi_tgt_it_nexus_create(shost, itn_id, initiator); -} - -static inline int fc_tgt_it_nexus_destroy(struct Scsi_Host *shost, u64 itn_id) -{ - return scsi_tgt_it_nexus_destroy(shost, itn_id); -} -#else -static inline int fc_tgt_it_nexus_create(struct Scsi_Host *shost, u64 itn_id, - char *initiator) -{ - return 0; -} - -static inline int fc_tgt_it_nexus_destroy(struct Scsi_Host *shost, u64 itn_id) -{ - return 0; -} - -#endif diff --git a/drivers/scsi/scsi_transport_srp.c b/drivers/scsi/scsi_transport_srp.c index 13e898332e45..43fea2219f83 100644 --- a/drivers/scsi/scsi_transport_srp.c +++ b/drivers/scsi/scsi_transport_srp.c @@ -33,7 +33,6 @@ #include #include #include "scsi_priv.h" -#include "scsi_transport_srp_internal.h" struct srp_host_attrs { atomic_t next_port_id; @@ -746,18 +745,6 @@ struct srp_rport *srp_rport_add(struct Scsi_Host *shost, return ERR_PTR(ret); } - if (shost->active_mode & MODE_TARGET && - ids->roles == SRP_RPORT_ROLE_INITIATOR) { - ret = srp_tgt_it_nexus_create(shost, (unsigned long)rport, - rport->port_id); - if (ret) { - device_del(&rport->dev); - transport_destroy_device(&rport->dev); - put_device(&rport->dev); - return ERR_PTR(ret); - } - } - transport_add_device(&rport->dev); transport_configure_device(&rport->dev); @@ -774,11 +761,6 @@ EXPORT_SYMBOL_GPL(srp_rport_add); void srp_rport_del(struct srp_rport *rport) { struct device *dev = &rport->dev; - struct Scsi_Host *shost = dev_to_shost(dev->parent); - - if (shost->active_mode & MODE_TARGET && - rport->roles == SRP_RPORT_ROLE_INITIATOR) - srp_tgt_it_nexus_destroy(shost, (unsigned long)rport); transport_remove_device(dev); device_del(dev); diff --git a/drivers/scsi/scsi_transport_srp_internal.h b/drivers/scsi/scsi_transport_srp_internal.h deleted file mode 100644 index 8a79747f9f3d..000000000000 --- a/drivers/scsi/scsi_transport_srp_internal.h +++ /dev/null @@ -1,25 +0,0 @@ -#include - -#ifdef CONFIG_SCSI_SRP_TGT_ATTRS -static inline int srp_tgt_it_nexus_create(struct Scsi_Host *shost, u64 itn_id, - char *initiator) -{ - return scsi_tgt_it_nexus_create(shost, itn_id, initiator); -} - -static inline int srp_tgt_it_nexus_destroy(struct Scsi_Host *shost, u64 itn_id) -{ - return scsi_tgt_it_nexus_destroy(shost, itn_id); -} - -#else -static inline int srp_tgt_it_nexus_create(struct Scsi_Host *shost, u64 itn_id, - char *initiator) -{ - return 0; -} -static inline int srp_tgt_it_nexus_destroy(struct Scsi_Host *shost, u64 itn_id) -{ - return 0; -} -#endif diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index abb695882fed..f7adfe0a6dc8 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -131,27 +131,6 @@ struct scsi_host_template { */ int (* queuecommand)(struct Scsi_Host *, struct scsi_cmnd *); - /* - * The transfer functions are used to queue a scsi command to - * the LLD. When the driver is finished processing the command - * the done callback is invoked. - * - * This is called to inform the LLD to transfer - * scsi_bufflen(cmd) bytes. scsi_sg_count(cmd) speciefies the - * number of scatterlist entried in the command and - * scsi_sglist(cmd) returns the scatterlist. - * - * return values: see queuecommand - * - * If the LLD accepts the cmd, it should set the result to an - * appropriate value when completed before calling the done function. - * - * STATUS: REQUIRED FOR TARGET DRIVERS - */ - /* TODO: rename */ - int (* transfer_response)(struct scsi_cmnd *, - void (*done)(struct scsi_cmnd *)); - /* * This is an error handling strategy routine. You don't need to * define one of these if you don't want to - there is a default diff --git a/include/scsi/scsi_tgt.h b/include/scsi/scsi_tgt.h deleted file mode 100644 index d0fefb96158f..000000000000 --- a/include/scsi/scsi_tgt.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * SCSI target definitions - */ - -#include - -struct Scsi_Host; -struct scsi_cmnd; -struct scsi_lun; - -extern struct Scsi_Host *scsi_tgt_cmd_to_host(struct scsi_cmnd *); -extern int scsi_tgt_alloc_queue(struct Scsi_Host *); -extern void scsi_tgt_free_queue(struct Scsi_Host *); -extern int scsi_tgt_queue_command(struct scsi_cmnd *, u64, struct scsi_lun *, u64); -extern int scsi_tgt_tsk_mgmt_request(struct Scsi_Host *, u64, int, u64, - struct scsi_lun *, void *); -extern struct scsi_cmnd *scsi_host_get_command(struct Scsi_Host *, - enum dma_data_direction, gfp_t); -extern void scsi_host_put_command(struct Scsi_Host *, struct scsi_cmnd *); -extern int scsi_tgt_it_nexus_create(struct Scsi_Host *, u64, char *); -extern int scsi_tgt_it_nexus_destroy(struct Scsi_Host *, u64); diff --git a/include/scsi/scsi_tgt_if.h b/include/scsi/scsi_tgt_if.h deleted file mode 100644 index f2ee7c238a45..000000000000 --- a/include/scsi/scsi_tgt_if.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * SCSI target kernel/user interface - * - * Copyright (C) 2005 FUJITA Tomonori - * Copyright (C) 2005 Mike Christie - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ -#ifndef __SCSI_TARGET_IF_H -#define __SCSI_TARGET_IF_H - -/* user -> kernel */ -#define TGT_UEVENT_CMD_RSP 0x0001 -#define TGT_UEVENT_IT_NEXUS_RSP 0x0002 -#define TGT_UEVENT_TSK_MGMT_RSP 0x0003 - -/* kernel -> user */ -#define TGT_KEVENT_CMD_REQ 0x1001 -#define TGT_KEVENT_CMD_DONE 0x1002 -#define TGT_KEVENT_IT_NEXUS_REQ 0x1003 -#define TGT_KEVENT_TSK_MGMT_REQ 0x1004 - -struct tgt_event_hdr { - uint16_t version; - uint16_t status; - uint16_t type; - uint16_t len; -} __attribute__ ((aligned (sizeof(uint64_t)))); - -struct tgt_event { - struct tgt_event_hdr hdr; - - union { - /* user-> kernel */ - struct { - int host_no; - int result; - aligned_u64 itn_id; - aligned_u64 tag; - aligned_u64 uaddr; - aligned_u64 sense_uaddr; - uint32_t len; - uint32_t sense_len; - uint8_t rw; - } cmd_rsp; - struct { - int host_no; - int result; - aligned_u64 itn_id; - aligned_u64 mid; - } tsk_mgmt_rsp; - struct { - __s32 host_no; - __s32 result; - aligned_u64 itn_id; - __u32 function; - } it_nexus_rsp; - - /* kernel -> user */ - struct { - int host_no; - uint32_t data_len; - aligned_u64 itn_id; - uint8_t scb[16]; - uint8_t lun[8]; - int attribute; - aligned_u64 tag; - } cmd_req; - struct { - int host_no; - int result; - aligned_u64 itn_id; - aligned_u64 tag; - } cmd_done; - struct { - int host_no; - int function; - aligned_u64 itn_id; - aligned_u64 tag; - uint8_t lun[8]; - aligned_u64 mid; - } tsk_mgmt_req; - struct { - __s32 host_no; - __u32 function; - aligned_u64 itn_id; - __u32 max_cmds; - __u8 initiator_id[16]; - } it_nexus_req; - } p; -} __attribute__ ((aligned (sizeof(uint64_t)))); - -#define TGT_RING_SIZE (1UL << 16) - -#endif -- cgit v1.2.3 From f1bea55d5afa371c311b61946c58b2cd4e78fb2d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 15 Apr 2014 12:26:54 +0200 Subject: scsi: remove various exports that were only used by scsi_tgt Signed-off-by: Christoph Hellwig Reviewed-by: Paolo Bonzini Reviewed-by: Hannes Reinecke --- drivers/scsi/scsi.c | 10 +++------- drivers/scsi/scsi_lib.c | 6 ++---- include/scsi/scsi_cmnd.h | 3 --- include/scsi/scsi_host.h | 2 -- 4 files changed, 5 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index ce5b4e5e7a5a..a76d76df4209 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -235,7 +235,8 @@ fail: * Description: allocate a struct scsi_cmd from host's slab, recycling from the * host's free_list if necessary. */ -struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *shost, gfp_t gfp_mask) +static struct scsi_cmnd * +__scsi_get_command(struct Scsi_Host *shost, gfp_t gfp_mask) { struct scsi_cmnd *cmd = scsi_host_alloc_command(shost, gfp_mask); @@ -265,7 +266,6 @@ struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *shost, gfp_t gfp_mask) return cmd; } -EXPORT_SYMBOL_GPL(__scsi_get_command); /** * scsi_get_command - Allocate and setup a scsi command block @@ -291,14 +291,13 @@ struct scsi_cmnd *scsi_get_command(struct scsi_device *dev, gfp_t gfp_mask) cmd->jiffies_at_alloc = jiffies; return cmd; } -EXPORT_SYMBOL(scsi_get_command); /** * __scsi_put_command - Free a struct scsi_cmnd * @shost: dev->host * @cmd: Command to free */ -void __scsi_put_command(struct Scsi_Host *shost, struct scsi_cmnd *cmd) +static void __scsi_put_command(struct Scsi_Host *shost, struct scsi_cmnd *cmd) { unsigned long flags; @@ -314,7 +313,6 @@ void __scsi_put_command(struct Scsi_Host *shost, struct scsi_cmnd *cmd) if (likely(cmd != NULL)) scsi_host_free_command(shost, cmd); } -EXPORT_SYMBOL(__scsi_put_command); /** * scsi_put_command - Free a scsi command block @@ -338,7 +336,6 @@ void scsi_put_command(struct scsi_cmnd *cmd) __scsi_put_command(cmd->device->host, cmd); } -EXPORT_SYMBOL(scsi_put_command); static struct scsi_host_cmd_pool * scsi_find_host_cmd_pool(struct Scsi_Host *shost) @@ -801,7 +798,6 @@ void scsi_finish_command(struct scsi_cmnd *cmd) } scsi_io_completion(cmd, good_bytes); } -EXPORT_SYMBOL(scsi_finish_command); /** * scsi_adjust_queue_depth - Let low level drivers change a device's queue depth diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 093e63207dc6..db7b7f2c14b8 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -579,7 +579,7 @@ static void scsi_free_sgtable(struct scsi_data_buffer *sdb) * the __init_io() function. Primarily this would involve * the scatter-gather table. */ -void scsi_release_buffers(struct scsi_cmnd *cmd) +static void scsi_release_buffers(struct scsi_cmnd *cmd) { if (cmd->sdb.table.nents) scsi_free_sgtable(&cmd->sdb); @@ -589,7 +589,6 @@ void scsi_release_buffers(struct scsi_cmnd *cmd) if (scsi_prot_sg_count(cmd)) scsi_free_sgtable(cmd->prot_sdb); } -EXPORT_SYMBOL(scsi_release_buffers); static void scsi_release_bidi_buffers(struct scsi_cmnd *cmd) { @@ -1609,7 +1608,7 @@ out_delay: blk_delay_queue(q, SCSI_QUEUE_DELAY); } -u64 scsi_calculate_bounce_limit(struct Scsi_Host *shost) +static u64 scsi_calculate_bounce_limit(struct Scsi_Host *shost) { struct device *host_dev; u64 bounce_limit = 0xffffffff; @@ -1629,7 +1628,6 @@ u64 scsi_calculate_bounce_limit(struct Scsi_Host *shost) return bounce_limit; } -EXPORT_SYMBOL(scsi_calculate_bounce_limit); struct request_queue *__scsi_alloc_queue(struct Scsi_Host *shost, request_fn_proc *request_fn) diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h index e0ae71098144..73f349044941 100644 --- a/include/scsi/scsi_cmnd.h +++ b/include/scsi/scsi_cmnd.h @@ -150,9 +150,7 @@ static inline struct scsi_driver *scsi_cmd_to_driver(struct scsi_cmnd *cmd) } extern struct scsi_cmnd *scsi_get_command(struct scsi_device *, gfp_t); -extern struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *, gfp_t); extern void scsi_put_command(struct scsi_cmnd *); -extern void __scsi_put_command(struct Scsi_Host *, struct scsi_cmnd *); extern void scsi_finish_command(struct scsi_cmnd *cmd); extern void *scsi_kmap_atomic_sg(struct scatterlist *sg, int sg_count, @@ -160,7 +158,6 @@ extern void *scsi_kmap_atomic_sg(struct scatterlist *sg, int sg_count, extern void scsi_kunmap_atomic_sg(void *virt); extern int scsi_init_io(struct scsi_cmnd *cmd, gfp_t gfp_mask); -extern void scsi_release_buffers(struct scsi_cmnd *cmd); extern int scsi_dma_map(struct scsi_cmnd *cmd); extern void scsi_dma_unmap(struct scsi_cmnd *cmd); diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index f7adfe0a6dc8..b2bc5198b7fb 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -795,8 +795,6 @@ extern struct Scsi_Host *scsi_host_lookup(unsigned short); extern const char *scsi_host_state_name(enum scsi_host_state); extern void scsi_cmd_get_serial(struct Scsi_Host *, struct scsi_cmnd *); -extern u64 scsi_calculate_bounce_limit(struct Scsi_Host *); - static inline int __must_check scsi_add_host(struct Scsi_Host *host, struct device *dev) { -- cgit v1.2.3 From 3868cf8ea70a57fc3f927872d8296f287ce4b96a Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 28 Jun 2014 11:58:42 +0200 Subject: scsi: restructure command initialization for TYPE_FS requests We should call the device handler prep_fn for all TYPE_FS requests, not just simple read/write calls that are handled by the disk driver. Restructure the common I/O code to call the prep_fn handler and zero out the CDB, and just leave the call to scsi_init_io to the ULDs. Signed-off-by: Christoph Hellwig Reviewed-by: Martin K. Petersen Reviewed-by: Hannes Reinecke --- drivers/scsi/scsi_lib.c | 22 ++++++++++++---------- drivers/scsi/sd.c | 2 +- drivers/scsi/sr.c | 3 +-- include/scsi/scsi_driver.h | 1 - 4 files changed, 14 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 150d7bb675a1..2afb96b801cb 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1082,11 +1082,10 @@ int scsi_setup_blk_pc_cmnd(struct scsi_device *sdev, struct request *req) EXPORT_SYMBOL(scsi_setup_blk_pc_cmnd); /* - * Setup a REQ_TYPE_FS command. These are simple read/write request - * from filesystems that still need to be translated to SCSI CDBs from - * the ULD. + * Setup a REQ_TYPE_FS command. These are simple request from filesystems + * that still need to be translated to SCSI CDBs from the ULD. */ -int scsi_setup_fs_cmnd(struct scsi_device *sdev, struct request *req) +static int scsi_setup_fs_cmnd(struct scsi_device *sdev, struct request *req) { struct scsi_cmnd *cmd = req->special; @@ -1098,9 +1097,8 @@ int scsi_setup_fs_cmnd(struct scsi_device *sdev, struct request *req) } memset(cmd->cmnd, 0, BLK_MAX_CDB); - return scsi_init_io(cmd, GFP_ATOMIC); + return scsi_cmd_to_driver(cmd)->init_command(cmd); } -EXPORT_SYMBOL(scsi_setup_fs_cmnd); static int scsi_prep_state_check(struct scsi_device *sdev, struct request *req) @@ -1205,12 +1203,16 @@ static int scsi_prep_fn(struct request_queue *q, struct request *req) goto out; } - if (req->cmd_type == REQ_TYPE_FS) - ret = scsi_cmd_to_driver(cmd)->init_command(cmd); - else if (req->cmd_type == REQ_TYPE_BLOCK_PC) + switch (req->cmd_type) { + case REQ_TYPE_FS: + ret = scsi_setup_fs_cmnd(sdev, req); + break; + case REQ_TYPE_BLOCK_PC: ret = scsi_setup_blk_pc_cmnd(sdev, req); - else + break; + default: ret = BLKPREP_KILL; + } out: return scsi_prep_return(q, req, ret); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 87566b51fcf7..1b166c7ea891 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -894,7 +894,7 @@ static int sd_init_command(struct scsi_cmnd *SCpnt) ret = scsi_setup_flush_cmnd(sdp, rq); goto out; } - ret = scsi_setup_fs_cmnd(sdp, rq); + ret = scsi_init_io(SCpnt, GFP_ATOMIC); if (ret != BLKPREP_OK) goto out; SCpnt = rq->special; diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index a7ea27c01286..9feeb3766d7d 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -385,10 +385,9 @@ static int sr_init_command(struct scsi_cmnd *SCpnt) int block = 0, this_count, s_size; struct scsi_cd *cd; struct request *rq = SCpnt->request; - struct scsi_device *sdp = SCpnt->device; int ret; - ret = scsi_setup_fs_cmnd(sdp, rq); + ret = scsi_init_io(SCpnt, GFP_ATOMIC); if (ret != BLKPREP_OK) goto out; SCpnt = rq->special; diff --git a/include/scsi/scsi_driver.h b/include/scsi/scsi_driver.h index 36c4114ed9bc..009d2ae91a52 100644 --- a/include/scsi/scsi_driver.h +++ b/include/scsi/scsi_driver.h @@ -30,6 +30,5 @@ extern int scsi_register_interface(struct class_interface *); class_interface_unregister(intf) int scsi_setup_blk_pc_cmnd(struct scsi_device *sdev, struct request *req); -int scsi_setup_fs_cmnd(struct scsi_device *sdev, struct request *req); #endif /* _SCSI_SCSI_DRIVER_H */ -- cgit v1.2.3 From ae4b884fc6316b3190be19448cea24b020c1cad6 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 15 Jul 2014 12:59:36 -0400 Subject: nfsd: silence sparse warning about accessing credentials sparse says: fs/nfsd/auth.c:31:38: warning: incorrect type in argument 1 (different address spaces) fs/nfsd/auth.c:31:38: expected struct cred const *cred fs/nfsd/auth.c:31:38: got struct cred const [noderef] *real_cred Add a new accessor for the ->real_cred and use that to fetch the pointer. Accessing current->real_cred directly is actually quite safe since we know that they can't go away so this is mostly a cosmetic fixup to silence sparse. Signed-off-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfsd/auth.c | 2 +- include/linux/cred.h | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/fs/nfsd/auth.c b/fs/nfsd/auth.c index 72f44823adbb..9d46a0bdd9f9 100644 --- a/fs/nfsd/auth.c +++ b/fs/nfsd/auth.c @@ -28,7 +28,7 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp) validate_process_creds(); /* discard any old override before preparing the new set */ - revert_creds(get_cred(current->real_cred)); + revert_creds(get_cred(current_real_cred())); new = prepare_creds(); if (!new) return -ENOMEM; diff --git a/include/linux/cred.h b/include/linux/cred.h index f61d6c8f5ef3..b2d0820837c4 100644 --- a/include/linux/cred.h +++ b/include/linux/cred.h @@ -258,6 +258,15 @@ static inline void put_cred(const struct cred *_cred) #define current_cred() \ rcu_dereference_protected(current->cred, 1) +/** + * current_real_cred - Access the current task's objective credentials + * + * Access the objective credentials of the current task. RCU-safe, + * since nobody else can modify it. + */ +#define current_real_cred() \ + rcu_dereference_protected(current->real_cred, 1) + /** * __task_cred - Access a task's objective credentials * @task: The task to query -- cgit v1.2.3 From 4f1e57657548d7afb4a6b62097765282f3b03c6e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 28 Jun 2014 12:36:28 +0200 Subject: scsi: mark scsi_setup_blk_pc_cmnd static Signed-off-by: Christoph Hellwig Reviewed-by: Martin K. Petersen Reviewed-by: Hannes Reinecke --- drivers/scsi/scsi_lib.c | 3 +-- include/scsi/scsi_driver.h | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 4e15a3a5ad29..85cf0ef843f6 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1047,7 +1047,7 @@ static struct scsi_cmnd *scsi_get_cmd_from_req(struct scsi_device *sdev, return cmd; } -int scsi_setup_blk_pc_cmnd(struct scsi_device *sdev, struct request *req) +static int scsi_setup_blk_pc_cmnd(struct scsi_device *sdev, struct request *req) { struct scsi_cmnd *cmd = req->special; @@ -1072,7 +1072,6 @@ int scsi_setup_blk_pc_cmnd(struct scsi_device *sdev, struct request *req) cmd->allowed = req->retries; return BLKPREP_OK; } -EXPORT_SYMBOL(scsi_setup_blk_pc_cmnd); /* * Setup a REQ_TYPE_FS command. These are simple request from filesystems diff --git a/include/scsi/scsi_driver.h b/include/scsi/scsi_driver.h index 009d2ae91a52..c2b759809d8a 100644 --- a/include/scsi/scsi_driver.h +++ b/include/scsi/scsi_driver.h @@ -29,6 +29,4 @@ extern int scsi_register_interface(struct class_interface *); #define scsi_unregister_interface(intf) \ class_interface_unregister(intf) -int scsi_setup_blk_pc_cmnd(struct scsi_device *sdev, struct request *req); - #endif /* _SCSI_SCSI_DRIVER_H */ -- cgit v1.2.3 From e34c4db8fe5c8d47753057b2409f9b8388f1896e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 26 Feb 2014 18:22:05 -0300 Subject: [media] videodev2.h: add V4L2_FIELD_HAS_T_OR_B macro Add a macro to test if the field consists of a single top or bottom field. Anyone who needs to work with fields as opposed to frame will need this. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/videodev2.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index b73e8cda7192..1f1a65c1fe8e 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -124,6 +124,10 @@ enum v4l2_field { (field) == V4L2_FIELD_INTERLACED_BT ||\ (field) == V4L2_FIELD_SEQ_TB ||\ (field) == V4L2_FIELD_SEQ_BT) +#define V4L2_FIELD_HAS_T_OR_B(field) \ + ((field) == V4L2_FIELD_BOTTOM ||\ + (field) == V4L2_FIELD_TOP ||\ + (field) == V4L2_FIELD_ALTERNATE) enum v4l2_buf_type { V4L2_BUF_TYPE_VIDEO_CAPTURE = 1, -- cgit v1.2.3 From c2659479f7865fb538493089bce3dd3d2abf90b0 Mon Sep 17 00:00:00 2001 From: Anish Bhatt Date: Wed, 16 Jul 2014 22:32:39 -0700 Subject: Update setapp/getapp prototypes in dcbnl_rtnl_ops to return int instead of u8 v2: fixed issue with checking return of dcbnl_rtnl_ops->getapp() Signed-off-by: Anish Bhatt Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c | 4 ++-- drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.c | 21 ++++++--------------- drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c | 6 +++--- drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c | 4 ++-- include/net/dcbnl.h | 4 ++-- net/dcb/dcbnl.c | 8 +++++++- 6 files changed, 22 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c index 51a952c51cb1..fb26bc4c42a1 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c @@ -2303,8 +2303,8 @@ static int bnx2x_set_admin_app_up(struct bnx2x *bp, u8 idtype, u16 idval, u8 up) return 0; } -static u8 bnx2x_dcbnl_set_app_up(struct net_device *netdev, u8 idtype, - u16 idval, u8 up) +static int bnx2x_dcbnl_set_app_up(struct net_device *netdev, u8 idtype, + u16 idval, u8 up) { struct bnx2x *bp = netdev_priv(netdev); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.c index a8b1073e6373..0d3a9df5be36 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.c @@ -648,26 +648,17 @@ static int __cxgb4_getapp(struct net_device *dev, u8 app_idtype, u16 app_id, } /* Return the Application User Priority Map associated with the specified - * Application ID. Since this routine is prototyped to return "u8" we can't - * return errors ... + * Application ID. */ -static u8 cxgb4_getapp(struct net_device *dev, u8 app_idtype, u16 app_id) +static int cxgb4_getapp(struct net_device *dev, u8 app_idtype, u16 app_id) { - int result = __cxgb4_getapp(dev, app_idtype, app_id, 0); - - if (result < 0) - result = 0; - - return result; + return __cxgb4_getapp(dev, app_idtype, app_id, 0); } -/* Write a new Application User Priority Map for the specified Application ID. - * This routine is prototyped to return "u8" but other instantiations of the - * DCB NetLink Operations "setapp" routines return negative errnos for errors. - * We follow their lead. +/* Write a new Application User Priority Map for the specified Application ID */ -static u8 cxgb4_setapp(struct net_device *dev, u8 app_idtype, u16 app_id, - u8 app_prio) +static int cxgb4_setapp(struct net_device *dev, u8 app_idtype, u16 app_id, + u8 app_prio) { struct fw_port_cmd pcmd; struct port_info *pi = netdev2pinfo(dev); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c index 5172b6b12c09..ea1c1ab926e2 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c @@ -495,10 +495,10 @@ static void ixgbe_dcbnl_setpfcstate(struct net_device *netdev, u8 state) * @id: id is either ether type or TCP/UDP port number * * Returns : on success, returns a non-zero 802.1p user priority bitmap - * otherwise returns 0 as the invalid user priority bitmap to indicate an + * otherwise returns -EINVAL as the invalid user priority bitmap to indicate an * error. */ -static u8 ixgbe_dcbnl_getapp(struct net_device *netdev, u8 idtype, u16 id) +static int ixgbe_dcbnl_getapp(struct net_device *netdev, u8 idtype, u16 id) { struct ixgbe_adapter *adapter = netdev_priv(netdev); struct dcb_app app = { @@ -507,7 +507,7 @@ static u8 ixgbe_dcbnl_getapp(struct net_device *netdev, u8 idtype, u16 id) }; if (!(adapter->dcbx_cap & DCB_CAP_DCBX_VER_CEE)) - return 0; + return -EINVAL; return dcb_getapp(netdev, &app); } diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c index 561cb11ca58c..a72bcddf160a 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c @@ -926,7 +926,7 @@ static int qlcnic_dcb_get_num_tcs(struct net_device *netdev, int attr, u8 *num) } } -static u8 qlcnic_dcb_get_app(struct net_device *netdev, u8 idtype, u16 id) +static int qlcnic_dcb_get_app(struct net_device *netdev, u8 idtype, u16 id) { struct qlcnic_adapter *adapter = netdev_priv(netdev); struct dcb_app app = { @@ -935,7 +935,7 @@ static u8 qlcnic_dcb_get_app(struct net_device *netdev, u8 idtype, u16 id) }; if (!test_bit(QLCNIC_DCB_STATE, &adapter->dcb->state)) - return 0; + return -EINVAL; return dcb_getapp(netdev, &app); } diff --git a/include/net/dcbnl.h b/include/net/dcbnl.h index a975edf21b22..597b88a94332 100644 --- a/include/net/dcbnl.h +++ b/include/net/dcbnl.h @@ -81,8 +81,8 @@ struct dcbnl_rtnl_ops { void (*setbcncfg)(struct net_device *, int, u32); void (*getbcnrp)(struct net_device *, int, u8 *); void (*setbcnrp)(struct net_device *, int, u8); - u8 (*setapp)(struct net_device *, u8, u16, u8); - u8 (*getapp)(struct net_device *, u8, u16); + int (*setapp)(struct net_device *, u8, u16, u8); + int (*getapp)(struct net_device *, u8, u16); u8 (*getfeatcfg)(struct net_device *, int, u8 *); u8 (*setfeatcfg)(struct net_device *, int, u8); diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c index f8b98d89c285..c34af7a1d2d4 100644 --- a/net/dcb/dcbnl.c +++ b/net/dcb/dcbnl.c @@ -471,7 +471,11 @@ static int dcbnl_getapp(struct net_device *netdev, struct nlmsghdr *nlh, id = nla_get_u16(app_tb[DCB_APP_ATTR_ID]); if (netdev->dcbnl_ops->getapp) { - up = netdev->dcbnl_ops->getapp(netdev, idtype, id); + ret = netdev->dcbnl_ops->getapp(netdev, idtype, id); + if (ret < 0) + return ret; + else + up = ret; } else { struct dcb_app app = { .selector = idtype, @@ -538,6 +542,8 @@ static int dcbnl_setapp(struct net_device *netdev, struct nlmsghdr *nlh, if (netdev->dcbnl_ops->setapp) { ret = netdev->dcbnl_ops->setapp(netdev, idtype, id, up); + if (ret < 0) + return ret; } else { struct dcb_app app; app.selector = idtype; -- cgit v1.2.3 From 2b1987a9f1e6250c962ca13820d3c69817879266 Mon Sep 17 00:00:00 2001 From: Brian W Hart Date: Fri, 27 Jun 2014 16:09:39 -0500 Subject: cpufreq: make table sentinel macros unsigned to match use MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 5eeaf1f18973 (cpufreq: Fix build error on some platforms that use cpufreq_for_each_*) moved function cpufreq_next_valid() to a public header. Warnings are now generated when objects including that header are built with -Wsign-compare (as an out-of-tree module might be): .../include/linux/cpufreq.h: In function ‘cpufreq_next_valid’: .../include/linux/cpufreq.h:519:27: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] while ((*pos)->frequency != CPUFREQ_TABLE_END) ^ .../include/linux/cpufreq.h:520:25: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] if ((*pos)->frequency != CPUFREQ_ENTRY_INVALID) ^ Constants CPUFREQ_ENTRY_INVALID and CPUFREQ_TABLE_END are signed, but are used with unsigned member 'frequency' of cpufreq_frequency_table. Update the macro definitions to be explicitly unsigned to match their use. This also corrects potentially wrong behavior of clk_rate_table_iter() if unsigned long is wider than usigned int. Fixes: 5eeaf1f18973 (cpufreq: Fix build error on some platforms that use cpufreq_for_each_*) Signed-off-by: Brian W Hart Reviewed-by: Simon Horman Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- include/linux/cpufreq.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index ec4112d257bc..8f8ae95c6e27 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -482,8 +482,8 @@ extern struct cpufreq_governor cpufreq_gov_conservative; *********************************************************************/ /* Special Values of .frequency field */ -#define CPUFREQ_ENTRY_INVALID ~0 -#define CPUFREQ_TABLE_END ~1 +#define CPUFREQ_ENTRY_INVALID ~0u +#define CPUFREQ_TABLE_END ~1u /* Special Values of .flags field */ #define CPUFREQ_BOOST_FREQ (1 << 0) -- cgit v1.2.3 From b1261c86fe238cc0da3f5dc837a38a0c39f3e7c4 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 14 Jul 2014 14:26:14 +0300 Subject: serial: 8250: introduce up_to_u8250p() helper It helps to cast struct uart_port to struct uart_8250_port at runtime. Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 57 +++++++++++++------------------------ drivers/tty/serial/8250/8250_dw.c | 8 ++++-- drivers/tty/serial/8250/8250_fsl.c | 3 +- drivers/tty/serial/8250/8250_pci.c | 6 ++-- include/linux/serial_8250.h | 5 ++++ 5 files changed, 32 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 7a91c6d1eb7d..0da01458816e 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -439,8 +439,7 @@ static int exar_handle_irq(struct uart_port *port); static void set_io_from_upio(struct uart_port *p) { - struct uart_8250_port *up = - container_of(p, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(p); up->dl_read = default_serial_dl_read; up->dl_write = default_serial_dl_write; @@ -1277,8 +1276,7 @@ static inline void __stop_tx(struct uart_8250_port *p) static void serial8250_stop_tx(struct uart_port *port) { - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(port); __stop_tx(up); @@ -1293,8 +1291,7 @@ static void serial8250_stop_tx(struct uart_port *port) static void serial8250_start_tx(struct uart_port *port) { - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(port); if (up->dma && !serial8250_tx_dma(up)) { return; @@ -1322,8 +1319,7 @@ static void serial8250_start_tx(struct uart_port *port) static void serial8250_stop_rx(struct uart_port *port) { - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(port); up->ier &= ~UART_IER_RLSI; up->port.read_status_mask &= ~UART_LSR_DR; @@ -1332,8 +1328,7 @@ static void serial8250_stop_rx(struct uart_port *port) static void serial8250_enable_ms(struct uart_port *port) { - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(port); /* no MSR capabilities */ if (up->bugs & UART_BUG_NOMSR) @@ -1499,8 +1494,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) { unsigned char status; unsigned long flags; - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(port); int dma_err = 0; if (iir & UART_IIR_NO_INT) @@ -1785,8 +1779,7 @@ static void serial8250_backup_timeout(unsigned long data) static unsigned int serial8250_tx_empty(struct uart_port *port) { - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(port); unsigned long flags; unsigned int lsr; @@ -1800,8 +1793,7 @@ static unsigned int serial8250_tx_empty(struct uart_port *port) static unsigned int serial8250_get_mctrl(struct uart_port *port) { - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(port); unsigned int status; unsigned int ret; @@ -1821,8 +1813,7 @@ static unsigned int serial8250_get_mctrl(struct uart_port *port) static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl) { - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(port); unsigned char mcr = 0; if (mctrl & TIOCM_RTS) @@ -1843,8 +1834,7 @@ static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl) static void serial8250_break_ctl(struct uart_port *port, int break_state) { - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(port); unsigned long flags; spin_lock_irqsave(&port->lock, flags); @@ -1911,8 +1901,7 @@ static void serial8250_put_poll_char(struct uart_port *port, unsigned char c) { unsigned int ier; - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(port); /* * First save the IER then disable the interrupts @@ -1941,8 +1930,7 @@ static void serial8250_put_poll_char(struct uart_port *port, static int serial8250_startup(struct uart_port *port) { - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(port); unsigned long flags; unsigned char lsr, iir; int retval; @@ -2194,8 +2182,7 @@ dont_test_tx_en: static void serial8250_shutdown(struct uart_port *port) { - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(port); unsigned long flags; /* @@ -2268,8 +2255,7 @@ void serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(port); unsigned char cval, fcr = 0; unsigned long flags; unsigned int baud, quot; @@ -2498,8 +2484,7 @@ serial8250_set_ldisc(struct uart_port *port, int new) void serial8250_do_pm(struct uart_port *port, unsigned int state, unsigned int oldstate) { - struct uart_8250_port *p = - container_of(port, struct uart_8250_port, port); + struct uart_8250_port *p = up_to_u8250p(port); serial8250_set_sleep(p, state != 0); } @@ -2630,8 +2615,7 @@ static void serial8250_release_rsa_resource(struct uart_8250_port *up) static void serial8250_release_port(struct uart_port *port) { - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(port); serial8250_release_std_resource(up); if (port->type == PORT_RSA) @@ -2640,8 +2624,7 @@ static void serial8250_release_port(struct uart_port *port) static int serial8250_request_port(struct uart_port *port) { - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(port); int ret; if (port->type == PORT_8250_CIR) @@ -2659,8 +2642,7 @@ static int serial8250_request_port(struct uart_port *port) static void serial8250_config_port(struct uart_port *port, int flags) { - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(port); int probeflags = PROBE_ANY; int ret; @@ -2859,8 +2841,7 @@ serial8250_register_ports(struct uart_driver *drv, struct device *dev) static void serial8250_console_putchar(struct uart_port *port, int ch) { - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(port); wait_for_xmitr(up, UART_LSR_THRE); serial_port_out(port, UART_TX, ch); diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index c531fa42f838..affdcb192aed 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -84,8 +84,9 @@ static inline int dw8250_modify_msr(struct uart_port *p, int offset, int value) static void dw8250_force_idle(struct uart_port *p) { - serial8250_clear_and_reinit_fifos(container_of - (p, struct uart_8250_port, port)); + struct uart_8250_port *up = up_to_u8250p(p); + + serial8250_clear_and_reinit_fifos(up); (void)p->serial_in(p, UART_RX); } @@ -255,6 +256,7 @@ static int dw8250_probe_of(struct uart_port *p, struct dw8250_data *data) { struct device_node *np = p->dev->of_node; + struct uart_8250_port *up = up_to_u8250p(p); u32 val; bool has_ucv = true; @@ -287,7 +289,7 @@ static int dw8250_probe_of(struct uart_port *p, } } if (has_ucv) - dw8250_setup_port(container_of(p, struct uart_8250_port, port)); + dw8250_setup_port(up); if (!of_property_read_u32(np, "reg-shift", &val)) p->regshift = val; diff --git a/drivers/tty/serial/8250/8250_fsl.c b/drivers/tty/serial/8250/8250_fsl.c index f4d3c47b88e8..c0533a57ec53 100644 --- a/drivers/tty/serial/8250/8250_fsl.c +++ b/drivers/tty/serial/8250/8250_fsl.c @@ -28,8 +28,7 @@ int fsl8250_handle_irq(struct uart_port *port) unsigned char lsr, orig_lsr; unsigned long flags; unsigned int iir; - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(port); spin_lock_irqsave(&up->port.lock, flags); diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 33137b3ba94d..61830b1792eb 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1581,8 +1581,7 @@ static int skip_tx_en_setup(struct serial_private *priv, static void kt_handle_break(struct uart_port *p) { - struct uart_8250_port *up = - container_of(p, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(p); /* * On receipt of a BI, serial device in Intel ME (Intel * management engine) needs to have its fifos cleared for sane @@ -1593,8 +1592,7 @@ static void kt_handle_break(struct uart_port *p) static unsigned int kt_serial_in(struct uart_port *p, int offset) { - struct uart_8250_port *up = - container_of(p, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(p); unsigned int val; /* diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index af47a8af6024..730ab4b3d686 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -100,6 +100,11 @@ struct uart_8250_port { void (*dl_write)(struct uart_8250_port *, int); }; +static inline struct uart_8250_port *up_to_u8250p(struct uart_port *up) +{ + return container_of(up, struct uart_8250_port, port); +} + int serial8250_register_8250_port(struct uart_8250_port *); void serial8250_unregister_port(int line); void serial8250_suspend_port(int line); -- cgit v1.2.3 From 266dcff03eed0050b6af11aaf2a61ab837d7ba3f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 16 Jul 2014 01:19:34 +0000 Subject: Serial: allow port drivers to have a default attribute group Some serial drivers (like 8250), want to add sysfs files. We need to do so in a race-free way, so allow any port to be able to specify an attribute group that should be added at device creation time. Signed-off-by: Greg Kroah-Hartman Signed-off-by: Yoshihiro YUNOMAE Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 24 +++++++++++++++++------- include/linux/serial_core.h | 2 ++ 2 files changed, 19 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index b70095e55df6..61529a84c3fc 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2564,12 +2564,6 @@ static const struct attribute_group tty_dev_attr_group = { .attrs = tty_dev_attrs, }; -static const struct attribute_group *tty_dev_attr_groups[] = { - &tty_dev_attr_group, - NULL - }; - - /** * uart_add_one_port - attach a driver-defined port structure * @drv: pointer to the uart low level driver structure for this port @@ -2586,6 +2580,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) struct tty_port *port; int ret = 0; struct device *tty_dev; + int num_groups; BUG_ON(in_interrupt()); @@ -2619,12 +2614,26 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) uart_configure_port(drv, state, uport); + num_groups = 2; + if (uport->attr_group) + num_groups++; + + uport->tty_groups = kcalloc(num_groups, sizeof(**uport->tty_groups), + GFP_KERNEL); + if (!uport->tty_groups) { + ret = -ENOMEM; + goto out; + } + uport->tty_groups[0] = &tty_dev_attr_group; + if (uport->attr_group) + uport->tty_groups[1] = uport->attr_group; + /* * Register the port whether it's detected or not. This allows * setserial to be used to alter this port's parameters. */ tty_dev = tty_port_register_device_attr(port, drv->tty_driver, - uport->line, uport->dev, port, tty_dev_attr_groups); + uport->line, uport->dev, port, uport->tty_groups); if (likely(!IS_ERR(tty_dev))) { device_set_wakeup_capable(tty_dev, 1); } else { @@ -2703,6 +2712,7 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport) */ if (uport->type != PORT_UNKNOWN) uport->ops->release_port(uport); + kfree(uport->tty_groups); /* * Indicate that there isn't a port here anymore. diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 5bbb809ee197..cf3a1e789bf5 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -199,6 +199,8 @@ struct uart_port { unsigned char suspended; unsigned char irq_wake; unsigned char unused[2]; + struct attribute_group *attr_group; /* port specific attributes */ + const struct attribute_group **tty_groups; /* all attributes (serial core use only) */ void *private_data; /* generic platform data pointer */ }; -- cgit v1.2.3 From aef9a7bd9b676f797dd5cefd43deb30d36b976a9 Mon Sep 17 00:00:00 2001 From: Yoshihiro YUNOMAE Date: Wed, 16 Jul 2014 01:19:36 +0000 Subject: serial/uart/8250: Add tunable RX interrupt trigger I/F of FIFO buffers Add tunable RX interrupt trigger I/F of FIFO buffers. Serial devices are used as not only message communication devices but control or sending communication devices. For the latter uses, normally small data will be exchanged, so user applications want to receive data unit as soon as possible for real-time tendency. If we have a sensor which sends a 1 byte data each time and must control a device based on the sensor feedback, the RX interrupt should be triggered for each data. According to HW specification of serial UART devices, RX interrupt trigger can be changed, but the trigger is hard-coded. For example, RX interrupt trigger in 16550A can be set to 1, 4, 8, or 14 bytes for HW, but current driver sets the trigger to only 8bytes. This patch makes some devices change RX interrupt trigger from userland. - Read current setting # cat /sys/class/tty/ttyS0/rx_trig_bytes 8 - Write user setting # echo 1 > /sys/class/tty/ttyS0/rx_trig_bytes # cat /sys/class/tty/ttyS0/rx_trig_bytes 1 - 16550A and Tegra (1, 4, 8, or 14 bytes) - 16650V2 (8, 16, 24, or 28 bytes) - 16654 (8, 16, 56, or 60 bytes) - 16750 (1, 16, 32, or 56 bytes) Changes in V9: - Use attr_group instead of dev_spec_attr_group of uart_port structure Changes in V8: - Divide this patch from V7's patch based on Greg's comment Changes in V7: - Add Documentation - Change I/F name from rx_int_trig to rx_trig_bytes because the name rx_int_trig is hard to understand how users specify the value Changes in V6: - Move FCR_RX_TRIG_* definition in 8250.h to include/uapi/linux/serial_reg.h, rename those to UART_FCR_R_TRIG_*, and use UART_FCR_TRIGGER_MASK to UART_FCR_R_TRIG_BITS() - Change following function names: convert_fcr2val() => fcr_get_rxtrig_bytes() convert_val2rxtrig() => bytes_to_fcr_rxtrig() - Fix typo in serial8250_do_set_termios() - Delete the verbose error message pr_info() in bytes_to_fcr_rxtrig() - Rename *rx_int_trig/rx_trig* to *rxtrig* for several functions or variables (but UI remains rx_int_trig) - Change the meaningless variable name 'val' to 'bytes' following functions: fcr_get_rxtrig_bytes(), bytes_to_fcr_rxtrig(), do_set_rxtrig(), do_serial8250_set_rxtrig(), and serial8250_set_attr_rxtrig() - Use up->fcr in order to get rxtrig_bytes instead of rx_trig_raw in fcr_get_rxtrig_bytes() - Use conf_type->rxtrig_bytes[0] instead of switch statement for support check in register_dev_spec_attr_grp() - Delete the checking whether a user changed FCR or not when minimum buffer is needed in serial8250_do_set_termios() Changes in V5.1: - Fix FCR_RX_TRIG_MAX_STATE definition Changes in V5: - Support Tegra, 16650V2, 16654, and 16750 - Store default FCR value to up->fcr when the port is first created - Add rx_trig_byte[] in uart_config[] for each device and use rx_trig_byte[] in convert_fcr2val() and convert_val2rxtrig() Changes in V4: - Introduce fifo_bug flag in uart_8250_port structure This is enabled only when parity is enabled and UART_BUG_PARITY is enabled for up->bugs. If this flag is enabled, user cannot set RX trigger. - Return -EOPNOTSUPP when it does not support device at convert_fcr2val() and at convert_val2rxtrig() - Set the nearest lower RX trigger when users input a meaningless value at convert_val2rxtrig() - Check whether p->fcr is existing at serial8250_clear_and_reinit_fifos() - Set fcr = up->fcr in the begging of serial8250_do_set_termios() Changes in V3: - Change I/F from ioctl(2) to sysfs(rx_int_trig) Changed in V2: - Use _IOW for TIOCSFIFORTRIG definition - Pass the interrupt trigger value itself Signed-off-by: Yoshihiro YUNOMAE Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-tty | 16 ++++ drivers/tty/serial/8250/8250.h | 2 + drivers/tty/serial/8250/8250_core.c | 173 ++++++++++++++++++++++++++++++++---- include/linux/serial_8250.h | 2 + include/uapi/linux/serial_reg.h | 5 ++ 5 files changed, 183 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-tty b/Documentation/ABI/testing/sysfs-tty index ad22fb0ee765..9eb3c2b6b040 100644 --- a/Documentation/ABI/testing/sysfs-tty +++ b/Documentation/ABI/testing/sysfs-tty @@ -138,3 +138,19 @@ Description: These sysfs values expose the TIOCGSERIAL interface via sysfs rather than via ioctls. + +What: /sys/class/tty/ttyS0/rx_trig_bytes +Date: May 2014 +Contact: Yoshihiro YUNOMAE +Description: + Shows current RX interrupt trigger bytes or sets the + user specified value to change it for the FIFO buffer. + Users can show or set this value regardless of opening the + serial device file or not. + + The RX trigger can be set one of four kinds of values for UART + serials. When users input a meaning less value to this I/F, + the RX trigger is changed to the nearest lower value for the + device specification. For example, when user sets 7bytes on + 16550A, which has 1/4/8/14 bytes trigger, the RX trigger is + automatically changed to 4 bytes. diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 1ebf8538b4fa..1b08c918cd51 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -12,6 +12,7 @@ */ #include +#include #include struct uart_8250_dma { @@ -60,6 +61,7 @@ struct serial8250_config { unsigned short fifo_size; unsigned short tx_loadsz; unsigned char fcr; + unsigned char rxtrig_bytes[UART_FCR_R_TRIG_MAX_STATE]; unsigned int flags; }; diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 0da01458816e..1d42dba6121d 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include #include @@ -161,6 +160,7 @@ static const struct serial8250_config uart_config[] = { .fifo_size = 16, .tx_loadsz = 16, .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .rxtrig_bytes = {1, 4, 8, 14}, .flags = UART_CAP_FIFO, }, [PORT_CIRRUS] = { @@ -180,6 +180,7 @@ static const struct serial8250_config uart_config[] = { .tx_loadsz = 16, .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 | UART_FCR_T_TRIG_00, + .rxtrig_bytes = {8, 16, 24, 28}, .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, }, [PORT_16750] = { @@ -188,6 +189,7 @@ static const struct serial8250_config uart_config[] = { .tx_loadsz = 64, .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 | UART_FCR7_64BYTE, + .rxtrig_bytes = {1, 16, 32, 56}, .flags = UART_CAP_FIFO | UART_CAP_SLEEP | UART_CAP_AFE, }, [PORT_STARTECH] = { @@ -209,6 +211,7 @@ static const struct serial8250_config uart_config[] = { .tx_loadsz = 32, .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 | UART_FCR_T_TRIG_10, + .rxtrig_bytes = {8, 16, 56, 60}, .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, }, [PORT_16850] = { @@ -266,6 +269,7 @@ static const struct serial8250_config uart_config[] = { .tx_loadsz = 8, .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 | UART_FCR_T_TRIG_01, + .rxtrig_bytes = {1, 4, 8, 14}, .flags = UART_CAP_FIFO | UART_CAP_RTOIE, }, [PORT_XR17D15X] = { @@ -530,11 +534,8 @@ static void serial8250_clear_fifos(struct uart_8250_port *p) void serial8250_clear_and_reinit_fifos(struct uart_8250_port *p) { - unsigned char fcr; - serial8250_clear_fifos(p); - fcr = uart_config[p->port.type].fcr; - serial_out(p, UART_FCR, fcr); + serial_out(p, UART_FCR, p->fcr); } EXPORT_SYMBOL_GPL(serial8250_clear_and_reinit_fifos); @@ -2256,10 +2257,9 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { struct uart_8250_port *up = up_to_u8250p(port); - unsigned char cval, fcr = 0; + unsigned char cval; unsigned long flags; unsigned int baud, quot; - int fifo_bug = 0; switch (termios->c_cflag & CSIZE) { case CS5: @@ -2282,7 +2282,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, if (termios->c_cflag & PARENB) { cval |= UART_LCR_PARITY; if (up->bugs & UART_BUG_PARITY) - fifo_bug = 1; + up->fifo_bug = true; } if (!(termios->c_cflag & PARODD)) cval |= UART_LCR_EPAR; @@ -2306,10 +2306,10 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, quot++; if (up->capabilities & UART_CAP_FIFO && port->fifosize > 1) { - fcr = uart_config[port->type].fcr; - if ((baud < 2400 && !up->dma) || fifo_bug) { - fcr &= ~UART_FCR_TRIGGER_MASK; - fcr |= UART_FCR_TRIGGER_1; + /* NOTE: If fifo_bug is not set, a user can set RX_trigger. */ + if ((baud < 2400 && !up->dma) || up->fifo_bug) { + up->fcr &= ~UART_FCR_TRIGGER_MASK; + up->fcr |= UART_FCR_TRIGGER_1; } } @@ -2442,15 +2442,15 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, * is written without DLAB set, this mode will be disabled. */ if (port->type == PORT_16750) - serial_port_out(port, UART_FCR, fcr); + serial_port_out(port, UART_FCR, up->fcr); serial_port_out(port, UART_LCR, cval); /* reset DLAB */ up->lcr = cval; /* Save LCR */ if (port->type != PORT_16750) { /* emulated UARTs (Lucent Venus 167x) need two steps */ - if (fcr & UART_FCR_ENABLE_FIFO) + if (up->fcr & UART_FCR_ENABLE_FIFO) serial_port_out(port, UART_FCR, UART_FCR_ENABLE_FIFO); - serial_port_out(port, UART_FCR, fcr); /* set fcr */ + serial_port_out(port, UART_FCR, up->fcr); /* set fcr */ } serial8250_set_mctrl(port, port->mctrl); spin_unlock_irqrestore(&port->lock, flags); @@ -2640,6 +2640,146 @@ static int serial8250_request_port(struct uart_port *port) return ret; } +static int fcr_get_rxtrig_bytes(struct uart_8250_port *up) +{ + const struct serial8250_config *conf_type = &uart_config[up->port.type]; + unsigned char bytes; + + bytes = conf_type->rxtrig_bytes[UART_FCR_R_TRIG_BITS(up->fcr)]; + + return bytes ? bytes : -EOPNOTSUPP; +} + +static int bytes_to_fcr_rxtrig(struct uart_8250_port *up, unsigned char bytes) +{ + const struct serial8250_config *conf_type = &uart_config[up->port.type]; + int i; + + if (!conf_type->rxtrig_bytes[UART_FCR_R_TRIG_BITS(UART_FCR_R_TRIG_00)]) + return -EOPNOTSUPP; + + for (i = 1; i < UART_FCR_R_TRIG_MAX_STATE; i++) { + if (bytes < conf_type->rxtrig_bytes[i]) + /* Use the nearest lower value */ + return (--i) << UART_FCR_R_TRIG_SHIFT; + } + + return UART_FCR_R_TRIG_11; +} + +static int do_get_rxtrig(struct tty_port *port) +{ + struct uart_state *state = container_of(port, struct uart_state, port); + struct uart_port *uport = state->uart_port; + struct uart_8250_port *up = + container_of(uport, struct uart_8250_port, port); + + if (!(up->capabilities & UART_CAP_FIFO) || uport->fifosize <= 1) + return -EINVAL; + + return fcr_get_rxtrig_bytes(up); +} + +static int do_serial8250_get_rxtrig(struct tty_port *port) +{ + int rxtrig_bytes; + + mutex_lock(&port->mutex); + rxtrig_bytes = do_get_rxtrig(port); + mutex_unlock(&port->mutex); + + return rxtrig_bytes; +} + +static ssize_t serial8250_get_attr_rx_trig_bytes(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tty_port *port = dev_get_drvdata(dev); + int rxtrig_bytes; + + rxtrig_bytes = do_serial8250_get_rxtrig(port); + if (rxtrig_bytes < 0) + return rxtrig_bytes; + + return snprintf(buf, PAGE_SIZE, "%d\n", rxtrig_bytes); +} + +static int do_set_rxtrig(struct tty_port *port, unsigned char bytes) +{ + struct uart_state *state = container_of(port, struct uart_state, port); + struct uart_port *uport = state->uart_port; + struct uart_8250_port *up = + container_of(uport, struct uart_8250_port, port); + int rxtrig; + + if (!(up->capabilities & UART_CAP_FIFO) || uport->fifosize <= 1 || + up->fifo_bug) + return -EINVAL; + + rxtrig = bytes_to_fcr_rxtrig(up, bytes); + if (rxtrig < 0) + return rxtrig; + + serial8250_clear_fifos(up); + up->fcr &= ~UART_FCR_TRIGGER_MASK; + up->fcr |= (unsigned char)rxtrig; + serial_out(up, UART_FCR, up->fcr); + return 0; +} + +static int do_serial8250_set_rxtrig(struct tty_port *port, unsigned char bytes) +{ + int ret; + + mutex_lock(&port->mutex); + ret = do_set_rxtrig(port, bytes); + mutex_unlock(&port->mutex); + + return ret; +} + +static ssize_t serial8250_set_attr_rx_trig_bytes(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct tty_port *port = dev_get_drvdata(dev); + unsigned char bytes; + int ret; + + if (!count) + return -EINVAL; + + ret = kstrtou8(buf, 10, &bytes); + if (ret < 0) + return ret; + + ret = do_serial8250_set_rxtrig(port, bytes); + if (ret < 0) + return ret; + + return count; +} + +static DEVICE_ATTR(rx_trig_bytes, S_IRUSR | S_IWUSR | S_IRGRP, + serial8250_get_attr_rx_trig_bytes, + serial8250_set_attr_rx_trig_bytes); + +static struct attribute *serial8250_dev_attrs[] = { + &dev_attr_rx_trig_bytes.attr, + NULL, + }; + +static struct attribute_group serial8250_dev_attr_group = { + .attrs = serial8250_dev_attrs, + }; + +static void register_dev_spec_attr_grp(struct uart_8250_port *up) +{ + const struct serial8250_config *conf_type = &uart_config[up->port.type]; + + if (conf_type->rxtrig_bytes[0]) + up->port.attr_group = &serial8250_dev_attr_group; +} + static void serial8250_config_port(struct uart_port *port, int flags) { struct uart_8250_port *up = up_to_u8250p(port); @@ -2687,6 +2827,9 @@ static void serial8250_config_port(struct uart_port *port, int flags) if ((port->type == PORT_XR17V35X) || (port->type == PORT_XR17D15X)) port->handle_irq = exar_handle_irq; + + register_dev_spec_attr_grp(up); + up->fcr = uart_config[up->port.type].fcr; } static int diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index 730ab4b3d686..f93649e22c43 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -74,8 +74,10 @@ struct uart_8250_port { struct list_head list; /* ports on this IRQ */ unsigned short capabilities; /* port capabilities */ unsigned short bugs; /* port bugs */ + bool fifo_bug; /* min RX trigger if enabled */ unsigned int tx_loadsz; /* transmit fifo load size */ unsigned char acr; + unsigned char fcr; unsigned char ier; unsigned char lcr; unsigned char mcr; diff --git a/include/uapi/linux/serial_reg.h b/include/uapi/linux/serial_reg.h index 99b47058816a..df6c9ab6b0cd 100644 --- a/include/uapi/linux/serial_reg.h +++ b/include/uapi/linux/serial_reg.h @@ -88,6 +88,11 @@ #define UART_FCR6_T_TRIGGER_30 0x30 /* Mask for transmit trigger set at 30 */ #define UART_FCR7_64BYTE 0x20 /* Go into 64 byte mode (TI16C750) */ +#define UART_FCR_R_TRIG_SHIFT 6 +#define UART_FCR_R_TRIG_BITS(x) \ + (((x) & UART_FCR_TRIGGER_MASK) >> UART_FCR_R_TRIG_SHIFT) +#define UART_FCR_R_TRIG_MAX_STATE 4 + #define UART_LCR 3 /* Out: Line Control Register */ /* * Note: if the word length is 5 bits (UART_LCR_WLEN5), then setting -- cgit v1.2.3 From 75f2a4ead5d5890ada9c2663a70fb58613c0d9f2 Mon Sep 17 00:00:00 2001 From: Himangi Saraogi Date: Thu, 17 Jul 2014 02:27:52 +0530 Subject: devres: Add devm_kasprintf and devm_kvasprintf API devm_kasprintf() and devm_kvasprintf() are the managed counterparts for kasprintf() and kvasprintf(). Signed-off-by: Himangi Saraogi Acked-by: Julia Lawall Acked-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- drivers/base/devres.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/device.h | 4 ++++ 2 files changed, 59 insertions(+) (limited to 'include') diff --git a/drivers/base/devres.c b/drivers/base/devres.c index 52302946770f..69d9b0c89a01 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -816,6 +816,61 @@ char *devm_kstrdup(struct device *dev, const char *s, gfp_t gfp) } EXPORT_SYMBOL_GPL(devm_kstrdup); +/** + * devm_kvasprintf - Allocate resource managed space + * for the formatted string. + * @dev: Device to allocate memory for + * @gfp: the GFP mask used in the devm_kmalloc() call when + * allocating memory + * @fmt: the formatted string to duplicate + * @ap: the list of tokens to be placed in the formatted string + * RETURNS: + * Pointer to allocated string on success, NULL on failure. + */ +char *devm_kvasprintf(struct device *dev, gfp_t gfp, const char *fmt, + va_list ap) +{ + unsigned int len; + char *p; + va_list aq; + + va_copy(aq, ap); + len = vsnprintf(NULL, 0, fmt, aq); + va_end(aq); + + p = devm_kmalloc(dev, len+1, gfp); + if (!p) + return NULL; + + vsnprintf(p, len+1, fmt, ap); + + return p; +} +EXPORT_SYMBOL(devm_kvasprintf); + +/** + * devm_kasprintf - Allocate resource managed space + * and copy an existing formatted string into that + * @dev: Device to allocate memory for + * @gfp: the GFP mask used in the devm_kmalloc() call when + * allocating memory + * @fmt: the string to duplicate + * RETURNS: + * Pointer to allocated string on success, NULL on failure. + */ +char *devm_kasprintf(struct device *dev, gfp_t gfp, const char *fmt, ...) +{ + va_list ap; + char *p; + + va_start(ap, fmt); + p = devm_kvasprintf(dev, gfp, fmt, ap); + va_end(ap); + + return p; +} +EXPORT_SYMBOL_GPL(devm_kasprintf); + /** * devm_kfree - Resource-managed kfree * @dev: Device this memory belongs to diff --git a/include/linux/device.h b/include/linux/device.h index af424acd393d..34a24ce30620 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -605,6 +605,10 @@ extern int devres_release_group(struct device *dev, void *id); /* managed devm_k.alloc/kfree for device drivers */ extern void *devm_kmalloc(struct device *dev, size_t size, gfp_t gfp); +extern char *devm_kvasprintf(struct device *dev, gfp_t gfp, const char *fmt, + va_list ap); +extern char *devm_kasprintf(struct device *dev, gfp_t gfp, + const char *fmt, ...); static inline void *devm_kzalloc(struct device *dev, size_t size, gfp_t gfp) { return devm_kmalloc(dev, size, gfp | __GFP_ZERO); -- cgit v1.2.3 From ac36187b373ff6be495c7b68ccea5eb0fe928442 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Tue, 20 May 2014 20:43:49 +0400 Subject: ARM: i.MX1 clk: Add devicetree support This patch adds devicetree support CCM module for i.MX1 (MC9328MX1) CPUs. Signed-off-by: Alexander Shiyan Signed-off-by: Shawn Guo --- .../devicetree/bindings/clock/imx1-clock.txt | 26 ++++ arch/arm/mach-imx/clk-imx1.c | 153 +++++++++++---------- include/dt-bindings/clock/imx1-clock.h | 40 ++++++ 3 files changed, 148 insertions(+), 71 deletions(-) create mode 100644 Documentation/devicetree/bindings/clock/imx1-clock.txt create mode 100644 include/dt-bindings/clock/imx1-clock.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/imx1-clock.txt b/Documentation/devicetree/bindings/clock/imx1-clock.txt new file mode 100644 index 000000000000..b7adf4e3ea98 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/imx1-clock.txt @@ -0,0 +1,26 @@ +* Clock bindings for Freescale i.MX1 CPUs + +Required properties: +- compatible: Should be "fsl,imx1-ccm". +- reg: Address and length of the register set. +- #clock-cells: Should be <1>. + +The clock consumer should specify the desired clock by having the clock +ID in its "clocks" phandle cell. See include/dt-bindings/clock/imx1-clock.h +for the full list of i.MX1 clock IDs. + +Examples: + clks: ccm@0021b000 { + #clock-cells = <1>; + compatible = "fsl,imx1-ccm"; + reg = <0x0021b000 0x1000>; + }; + + pwm: pwm@00208000 { + #pwm-cells = <2>; + compatible = "fsl,imx1-pwm"; + reg = <0x00208000 0x1000>; + interrupts = <34>; + clocks = <&clks IMX1_CLK_DUMMY>, <&clks IMX1_CLK_PER1>; + clock-names = "ipg", "per"; + }; diff --git a/arch/arm/mach-imx/clk-imx1.c b/arch/arm/mach-imx/clk-imx1.c index 7f739be3de2c..e9c391b32092 100644 --- a/arch/arm/mach-imx/clk-imx1.c +++ b/arch/arm/mach-imx/clk-imx1.c @@ -15,100 +15,111 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include -#include #include -#include #include +#include #include +#include +#include +#include +#include #include "clk.h" #include "common.h" #include "hardware.h" -/* CCM register addresses */ -#define IO_ADDR_CCM(off) (MX1_IO_ADDRESS(MX1_CCM_BASE_ADDR + (off))) - -#define CCM_CSCR IO_ADDR_CCM(0x0) -#define CCM_MPCTL0 IO_ADDR_CCM(0x4) -#define CCM_SPCTL0 IO_ADDR_CCM(0xc) -#define CCM_PCDR IO_ADDR_CCM(0x20) - -/* SCM register addresses */ -#define IO_ADDR_SCM(off) (MX1_IO_ADDRESS(MX1_SCM_BASE_ADDR + (off))) - -#define SCM_GCCR IO_ADDR_SCM(0xc) - static const char *prem_sel_clks[] = { "clk32_premult", "clk16m", }; static const char *clko_sel_clks[] = { "per1", "hclk", "clk48m", "clk16m", "prem", "fclk", }; -enum imx1_clks { - dummy, clk32, clk16m_ext, clk16m, clk32_premult, prem, mpll, mpll_gate, - spll, spll_gate, mcu, fclk, hclk, clk48m, per1, per2, per3, clko, - uart3_gate, ssi2_gate, brom_gate, dma_gate, csi_gate, mma_gate, - usbd_gate, clk_max -}; +static struct clk *clk[IMX1_CLK_MAX]; +static struct clk_onecell_data clk_data; -static struct clk *clk[clk_max]; +static void __iomem *ccm __initdata; +#define CCM_CSCR (ccm + 0x0000) +#define CCM_MPCTL0 (ccm + 0x0004) +#define CCM_SPCTL0 (ccm + 0x000c) +#define CCM_PCDR (ccm + 0x0020) +#define SCM_GCCR (ccm + 0x0810) -int __init mx1_clocks_init(unsigned long fref) +static void __init _mx1_clocks_init(unsigned long fref) { - int i; - - clk[dummy] = imx_clk_fixed("dummy", 0); - clk[clk32] = imx_clk_fixed("clk32", fref); - clk[clk16m_ext] = imx_clk_fixed("clk16m_ext", 16000000); - clk[clk16m] = imx_clk_gate("clk16m", "clk16m_ext", CCM_CSCR, 17); - clk[clk32_premult] = imx_clk_fixed_factor("clk32_premult", "clk32", 512, 1); - clk[prem] = imx_clk_mux("prem", CCM_CSCR, 16, 1, prem_sel_clks, - ARRAY_SIZE(prem_sel_clks)); - clk[mpll] = imx_clk_pllv1("mpll", "clk32_premult", CCM_MPCTL0); - clk[mpll_gate] = imx_clk_gate("mpll_gate", "mpll", CCM_CSCR, 0); - clk[spll] = imx_clk_pllv1("spll", "prem", CCM_SPCTL0); - clk[spll_gate] = imx_clk_gate("spll_gate", "spll", CCM_CSCR, 1); - clk[mcu] = imx_clk_divider("mcu", "clk32_premult", CCM_CSCR, 15, 1); - clk[fclk] = imx_clk_divider("fclk", "mpll_gate", CCM_CSCR, 15, 1); - clk[hclk] = imx_clk_divider("hclk", "spll_gate", CCM_CSCR, 10, 4); - clk[clk48m] = imx_clk_divider("clk48m", "spll_gate", CCM_CSCR, 26, 3); - clk[per1] = imx_clk_divider("per1", "spll_gate", CCM_PCDR, 0, 4); - clk[per2] = imx_clk_divider("per2", "spll_gate", CCM_PCDR, 4, 4); - clk[per3] = imx_clk_divider("per3", "spll_gate", CCM_PCDR, 16, 7); - clk[clko] = imx_clk_mux("clko", CCM_CSCR, 29, 3, clko_sel_clks, - ARRAY_SIZE(clko_sel_clks)); - clk[uart3_gate] = imx_clk_gate("uart3_gate", "hclk", SCM_GCCR, 6); - clk[ssi2_gate] = imx_clk_gate("ssi2_gate", "hclk", SCM_GCCR, 5); - clk[brom_gate] = imx_clk_gate("brom_gate", "hclk", SCM_GCCR, 4); - clk[dma_gate] = imx_clk_gate("dma_gate", "hclk", SCM_GCCR, 3); - clk[csi_gate] = imx_clk_gate("csi_gate", "hclk", SCM_GCCR, 2); - clk[mma_gate] = imx_clk_gate("mma_gate", "hclk", SCM_GCCR, 1); - clk[usbd_gate] = imx_clk_gate("usbd_gate", "clk48m", SCM_GCCR, 0); + unsigned i; + + clk[IMX1_CLK_DUMMY] = imx_clk_fixed("dummy", 0); + clk[IMX1_CLK_CLK32] = imx_obtain_fixed_clock("clk32", fref); + clk[IMX1_CLK_CLK16M_EXT] = imx_clk_fixed("clk16m_ext", 16000000); + clk[IMX1_CLK_CLK16M] = imx_clk_gate("clk16m", "clk16m_ext", CCM_CSCR, 17); + clk[IMX1_CLK_CLK32_PREMULT] = imx_clk_fixed_factor("clk32_premult", "clk32", 512, 1); + clk[IMX1_CLK_PREM] = imx_clk_mux("prem", CCM_CSCR, 16, 1, prem_sel_clks, ARRAY_SIZE(prem_sel_clks)); + clk[IMX1_CLK_MPLL] = imx_clk_pllv1("mpll", "clk32_premult", CCM_MPCTL0); + clk[IMX1_CLK_MPLL_GATE] = imx_clk_gate("mpll_gate", "mpll", CCM_CSCR, 0); + clk[IMX1_CLK_SPLL] = imx_clk_pllv1("spll", "prem", CCM_SPCTL0); + clk[IMX1_CLK_SPLL_GATE] = imx_clk_gate("spll_gate", "spll", CCM_CSCR, 1); + clk[IMX1_CLK_MCU] = imx_clk_divider("mcu", "clk32_premult", CCM_CSCR, 15, 1); + clk[IMX1_CLK_FCLK] = imx_clk_divider("fclk", "mpll_gate", CCM_CSCR, 15, 1); + clk[IMX1_CLK_HCLK] = imx_clk_divider("hclk", "spll_gate", CCM_CSCR, 10, 4); + clk[IMX1_CLK_CLK48M] = imx_clk_divider("clk48m", "spll_gate", CCM_CSCR, 26, 3); + clk[IMX1_CLK_PER1] = imx_clk_divider("per1", "spll_gate", CCM_PCDR, 0, 4); + clk[IMX1_CLK_PER2] = imx_clk_divider("per2", "spll_gate", CCM_PCDR, 4, 4); + clk[IMX1_CLK_PER3] = imx_clk_divider("per3", "spll_gate", CCM_PCDR, 16, 7); + clk[IMX1_CLK_CLKO] = imx_clk_mux("clko", CCM_CSCR, 29, 3, clko_sel_clks, ARRAY_SIZE(clko_sel_clks)); + clk[IMX1_CLK_UART3_GATE] = imx_clk_gate("uart3_gate", "hclk", SCM_GCCR, 6); + clk[IMX1_CLK_SSI2_GATE] = imx_clk_gate("ssi2_gate", "hclk", SCM_GCCR, 5); + clk[IMX1_CLK_BROM_GATE] = imx_clk_gate("brom_gate", "hclk", SCM_GCCR, 4); + clk[IMX1_CLK_DMA_GATE] = imx_clk_gate("dma_gate", "hclk", SCM_GCCR, 3); + clk[IMX1_CLK_CSI_GATE] = imx_clk_gate("csi_gate", "hclk", SCM_GCCR, 2); + clk[IMX1_CLK_MMA_GATE] = imx_clk_gate("mma_gate", "hclk", SCM_GCCR, 1); + clk[IMX1_CLK_USBD_GATE] = imx_clk_gate("usbd_gate", "clk48m", SCM_GCCR, 0); for (i = 0; i < ARRAY_SIZE(clk); i++) if (IS_ERR(clk[i])) pr_err("imx1 clk %d: register failed with %ld\n", i, PTR_ERR(clk[i])); - clk_register_clkdev(clk[dma_gate], "ahb", "imx1-dma"); - clk_register_clkdev(clk[hclk], "ipg", "imx1-dma"); - clk_register_clkdev(clk[per1], "per", "imx-gpt.0"); - clk_register_clkdev(clk[hclk], "ipg", "imx-gpt.0"); - clk_register_clkdev(clk[per1], "per", "imx1-uart.0"); - clk_register_clkdev(clk[hclk], "ipg", "imx1-uart.0"); - clk_register_clkdev(clk[per1], "per", "imx1-uart.1"); - clk_register_clkdev(clk[hclk], "ipg", "imx1-uart.1"); - clk_register_clkdev(clk[per1], "per", "imx1-uart.2"); - clk_register_clkdev(clk[uart3_gate], "ipg", "imx1-uart.2"); - clk_register_clkdev(clk[hclk], NULL, "imx1-i2c.0"); - clk_register_clkdev(clk[per2], "per", "imx1-cspi.0"); - clk_register_clkdev(clk[dummy], "ipg", "imx1-cspi.0"); - clk_register_clkdev(clk[per2], "per", "imx1-cspi.1"); - clk_register_clkdev(clk[dummy], "ipg", "imx1-cspi.1"); - clk_register_clkdev(clk[per2], "per", "imx1-fb.0"); - clk_register_clkdev(clk[dummy], "ipg", "imx1-fb.0"); - clk_register_clkdev(clk[dummy], "ahb", "imx1-fb.0"); + clk_register_clkdev(clk[IMX1_CLK_PER1], "per", "imx-gpt.0"); + clk_register_clkdev(clk[IMX1_CLK_HCLK], "ipg", "imx-gpt.0"); +} + +int __init mx1_clocks_init(unsigned long fref) +{ + ccm = MX1_IO_ADDRESS(MX1_CCM_BASE_ADDR); + + _mx1_clocks_init(fref); + + clk_register_clkdev(clk[IMX1_CLK_DMA_GATE], "ahb", "imx1-dma"); + clk_register_clkdev(clk[IMX1_CLK_HCLK], "ipg", "imx1-dma"); + clk_register_clkdev(clk[IMX1_CLK_PER1], "per", "imx1-uart.0"); + clk_register_clkdev(clk[IMX1_CLK_HCLK], "ipg", "imx1-uart.0"); + clk_register_clkdev(clk[IMX1_CLK_PER1], "per", "imx1-uart.1"); + clk_register_clkdev(clk[IMX1_CLK_HCLK], "ipg", "imx1-uart.1"); + clk_register_clkdev(clk[IMX1_CLK_PER1], "per", "imx1-uart.2"); + clk_register_clkdev(clk[IMX1_CLK_UART3_GATE], "ipg", "imx1-uart.2"); + clk_register_clkdev(clk[IMX1_CLK_HCLK], NULL, "imx1-i2c.0"); + clk_register_clkdev(clk[IMX1_CLK_PER2], "per", "imx1-cspi.0"); + clk_register_clkdev(clk[IMX1_CLK_DUMMY], "ipg", "imx1-cspi.0"); + clk_register_clkdev(clk[IMX1_CLK_PER2], "per", "imx1-cspi.1"); + clk_register_clkdev(clk[IMX1_CLK_DUMMY], "ipg", "imx1-cspi.1"); + clk_register_clkdev(clk[IMX1_CLK_PER2], "per", "imx1-fb.0"); + clk_register_clkdev(clk[IMX1_CLK_DUMMY], "ipg", "imx1-fb.0"); + clk_register_clkdev(clk[IMX1_CLK_DUMMY], "ahb", "imx1-fb.0"); mxc_timer_init(MX1_IO_ADDRESS(MX1_TIM1_BASE_ADDR), MX1_TIM1_INT); return 0; } + +static void __init mx1_clocks_init_dt(struct device_node *np) +{ + ccm = of_iomap(np, 0); + BUG_ON(!ccm); + + _mx1_clocks_init(32768); + + clk_data.clks = clk; + clk_data.clk_num = ARRAY_SIZE(clk); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + + mxc_timer_init_dt(of_find_compatible_node(NULL, NULL, "fsl,imx1-gpt")); +} +CLK_OF_DECLARE(imx1_ccm, "fsl,imx1-ccm", mx1_clocks_init_dt); diff --git a/include/dt-bindings/clock/imx1-clock.h b/include/dt-bindings/clock/imx1-clock.h new file mode 100644 index 000000000000..607bf01a31dd --- /dev/null +++ b/include/dt-bindings/clock/imx1-clock.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2014 Alexander Shiyan + * + * 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 __DT_BINDINGS_CLOCK_IMX1_H +#define __DT_BINDINGS_CLOCK_IMX1_H + +#define IMX1_CLK_DUMMY 0 +#define IMX1_CLK_CLK32 1 +#define IMX1_CLK_CLK16M_EXT 2 +#define IMX1_CLK_CLK16M 3 +#define IMX1_CLK_CLK32_PREMULT 4 +#define IMX1_CLK_PREM 5 +#define IMX1_CLK_MPLL 6 +#define IMX1_CLK_MPLL_GATE 7 +#define IMX1_CLK_SPLL 8 +#define IMX1_CLK_SPLL_GATE 9 +#define IMX1_CLK_MCU 10 +#define IMX1_CLK_FCLK 11 +#define IMX1_CLK_HCLK 12 +#define IMX1_CLK_CLK48M 13 +#define IMX1_CLK_PER1 14 +#define IMX1_CLK_PER2 15 +#define IMX1_CLK_PER3 16 +#define IMX1_CLK_CLKO 17 +#define IMX1_CLK_UART3_GATE 18 +#define IMX1_CLK_SSI2_GATE 19 +#define IMX1_CLK_BROM_GATE 20 +#define IMX1_CLK_DMA_GATE 21 +#define IMX1_CLK_CSI_GATE 22 +#define IMX1_CLK_MMA_GATE 23 +#define IMX1_CLK_USBD_GATE 24 +#define IMX1_CLK_MAX 25 + +#endif -- cgit v1.2.3 From e1b243772d455f3b25b410a15a5677a9e74ffa37 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Tue, 20 May 2014 20:43:51 +0400 Subject: ARM: i.MX: Remove registration helper for i.MX1 USB UDC imx_udc driver was removed from the kernel of about 10 months ago. This patch removes a registration helper for this driver and orphaned driver header. Signed-off-by: Alexander Shiyan Signed-off-by: Shawn Guo --- arch/arm/mach-imx/devices/Kconfig | 3 -- arch/arm/mach-imx/devices/Makefile | 1 - arch/arm/mach-imx/devices/devices-common.h | 16 ------ arch/arm/mach-imx/devices/platform-imx_udc.c | 75 ---------------------------- include/linux/platform_data/usb-imx_udc.h | 23 --------- 5 files changed, 118 deletions(-) delete mode 100644 arch/arm/mach-imx/devices/platform-imx_udc.c delete mode 100644 include/linux/platform_data/usb-imx_udc.h (limited to 'include') diff --git a/arch/arm/mach-imx/devices/Kconfig b/arch/arm/mach-imx/devices/Kconfig index 2d260a5a307c..dd7e0b45fb18 100644 --- a/arch/arm/mach-imx/devices/Kconfig +++ b/arch/arm/mach-imx/devices/Kconfig @@ -43,9 +43,6 @@ config IMX_HAVE_PLATFORM_IMX_SSI config IMX_HAVE_PLATFORM_IMX_UART bool -config IMX_HAVE_PLATFORM_IMX_UDC - bool - config IMX_HAVE_PLATFORM_IPU_CORE bool diff --git a/arch/arm/mach-imx/devices/Makefile b/arch/arm/mach-imx/devices/Makefile index 1cbc14cd80d1..6bb144dd680e 100644 --- a/arch/arm/mach-imx/devices/Makefile +++ b/arch/arm/mach-imx/devices/Makefile @@ -16,7 +16,6 @@ obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_KEYPAD) += platform-imx-keypad.o obj-$(CONFIG_IMX_HAVE_PLATFORM_PATA_IMX) += platform-pata_imx.o obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_SSI) += platform-imx-ssi.o obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_UART) += platform-imx-uart.o -obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_UDC) += platform-imx_udc.o obj-$(CONFIG_IMX_HAVE_PLATFORM_IPU_CORE) += platform-ipu-core.o obj-$(CONFIG_IMX_HAVE_PLATFORM_MX1_CAMERA) += platform-mx1-camera.o obj-$(CONFIG_IMX_HAVE_PLATFORM_MX2_CAMERA) += platform-mx2-camera.o diff --git a/arch/arm/mach-imx/devices/devices-common.h b/arch/arm/mach-imx/devices/devices-common.h index 61352a80bb59..69bafc884dfa 100644 --- a/arch/arm/mach-imx/devices/devices-common.h +++ b/arch/arm/mach-imx/devices/devices-common.h @@ -176,22 +176,6 @@ struct platform_device *__init imx_add_imx_uart_1irq( const struct imx_imx_uart_1irq_data *data, const struct imxuart_platform_data *pdata); -#include -struct imx_imx_udc_data { - resource_size_t iobase; - resource_size_t iosize; - resource_size_t irq0; - resource_size_t irq1; - resource_size_t irq2; - resource_size_t irq3; - resource_size_t irq4; - resource_size_t irq5; - resource_size_t irq6; -}; -struct platform_device *__init imx_add_imx_udc( - const struct imx_imx_udc_data *data, - const struct imxusb_platform_data *pdata); - #include #include struct imx_ipu_core_data { diff --git a/arch/arm/mach-imx/devices/platform-imx_udc.c b/arch/arm/mach-imx/devices/platform-imx_udc.c deleted file mode 100644 index 5ced7e4e2c71..000000000000 --- a/arch/arm/mach-imx/devices/platform-imx_udc.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2010 Pengutronix - * Uwe Kleine-Koenig - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License version 2 as published by the - * Free Software Foundation. - */ -#include "../hardware.h" -#include "devices-common.h" - -#define imx_imx_udc_data_entry_single(soc, _size) \ - { \ - .iobase = soc ## _USBD_BASE_ADDR, \ - .iosize = _size, \ - .irq0 = soc ## _INT_USBD0, \ - .irq1 = soc ## _INT_USBD1, \ - .irq2 = soc ## _INT_USBD2, \ - .irq3 = soc ## _INT_USBD3, \ - .irq4 = soc ## _INT_USBD4, \ - .irq5 = soc ## _INT_USBD5, \ - .irq6 = soc ## _INT_USBD6, \ - } - -#define imx_imx_udc_data_entry(soc, _size) \ - [_id] = imx_imx_udc_data_entry_single(soc, _size) - -#ifdef CONFIG_SOC_IMX1 -const struct imx_imx_udc_data imx1_imx_udc_data __initconst = - imx_imx_udc_data_entry_single(MX1, SZ_4K); -#endif /* ifdef CONFIG_SOC_IMX1 */ - -struct platform_device *__init imx_add_imx_udc( - const struct imx_imx_udc_data *data, - const struct imxusb_platform_data *pdata) -{ - struct resource res[] = { - { - .start = data->iobase, - .end = data->iobase + data->iosize - 1, - .flags = IORESOURCE_MEM, - }, { - .start = data->irq0, - .end = data->irq0, - .flags = IORESOURCE_IRQ, - }, { - .start = data->irq1, - .end = data->irq1, - .flags = IORESOURCE_IRQ, - }, { - .start = data->irq2, - .end = data->irq2, - .flags = IORESOURCE_IRQ, - }, { - .start = data->irq3, - .end = data->irq3, - .flags = IORESOURCE_IRQ, - }, { - .start = data->irq4, - .end = data->irq4, - .flags = IORESOURCE_IRQ, - }, { - .start = data->irq5, - .end = data->irq5, - .flags = IORESOURCE_IRQ, - }, { - .start = data->irq6, - .end = data->irq6, - .flags = IORESOURCE_IRQ, - }, - }; - - return imx_add_platform_device("imx_udc", 0, - res, ARRAY_SIZE(res), pdata, sizeof(*pdata)); -} diff --git a/include/linux/platform_data/usb-imx_udc.h b/include/linux/platform_data/usb-imx_udc.h deleted file mode 100644 index be273371f34a..000000000000 --- a/include/linux/platform_data/usb-imx_udc.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2008 Darius Augulis - * - * 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. - */ - -#ifndef __ASM_ARCH_MXC_USB -#define __ASM_ARCH_MXC_USB - -struct imxusb_platform_data { - int (*init)(struct device *); - void (*exit)(struct device *); -}; - -#endif /* __ASM_ARCH_MXC_USB */ -- cgit v1.2.3 From 641dfe8b73e81aa38cbbeab72a480462a4987963 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 19 May 2014 20:41:52 +0800 Subject: ARM: imx: move EHCI platform defines out of platform_data header The platform_data header usb-ehci-mxc.h has a lot of stuff used by only IMX platform code. They shouldn't be really in this header but a IMX platform local header. Create ehci.h and move these stuff into it. Signed-off-by: Shawn Guo --- arch/arm/mach-imx/ehci-imx25.c | 1 + arch/arm/mach-imx/ehci-imx27.c | 1 + arch/arm/mach-imx/ehci-imx31.c | 1 + arch/arm/mach-imx/ehci-imx35.c | 1 + arch/arm/mach-imx/ehci-imx5.c | 1 + arch/arm/mach-imx/ehci.h | 50 +++++++++++++++++++++++++++++ arch/arm/mach-imx/mach-armadillo5x0.c | 1 + arch/arm/mach-imx/mach-cpuimx27.c | 1 + arch/arm/mach-imx/mach-cpuimx35.c | 1 + arch/arm/mach-imx/mach-eukrea_cpuimx25.c | 1 + arch/arm/mach-imx/mach-imx27_visstrim_m10.c | 1 + arch/arm/mach-imx/mach-mx25_3ds.c | 1 + arch/arm/mach-imx/mach-mx27_3ds.c | 1 + arch/arm/mach-imx/mach-mx31_3ds.c | 1 + arch/arm/mach-imx/mach-mx31lilly.c | 1 + arch/arm/mach-imx/mach-mx31lite.c | 1 + arch/arm/mach-imx/mach-mx31moboard.c | 1 + arch/arm/mach-imx/mach-mx35_3ds.c | 1 + arch/arm/mach-imx/mach-pca100.c | 1 + arch/arm/mach-imx/mach-pcm037.c | 1 + arch/arm/mach-imx/mach-pcm038.c | 1 + arch/arm/mach-imx/mach-pcm043.c | 1 + arch/arm/mach-imx/mach-vpr200.c | 1 + arch/arm/mach-imx/mx31moboard-devboard.c | 1 + arch/arm/mach-imx/mx31moboard-marxbot.c | 1 + arch/arm/mach-imx/mx31moboard-smartbot.c | 1 + include/linux/platform_data/usb-ehci-mxc.h | 46 -------------------------- 27 files changed, 75 insertions(+), 46 deletions(-) create mode 100644 arch/arm/mach-imx/ehci.h (limited to 'include') diff --git a/arch/arm/mach-imx/ehci-imx25.c b/arch/arm/mach-imx/ehci-imx25.c index 134c190e3003..42a5a3d14c5f 100644 --- a/arch/arm/mach-imx/ehci-imx25.c +++ b/arch/arm/mach-imx/ehci-imx25.c @@ -17,6 +17,7 @@ #include #include +#include "ehci.h" #include "hardware.h" #define USBCTRL_OTGBASE_OFFSET 0x600 diff --git a/arch/arm/mach-imx/ehci-imx27.c b/arch/arm/mach-imx/ehci-imx27.c index 448d9115539d..c56974346c16 100644 --- a/arch/arm/mach-imx/ehci-imx27.c +++ b/arch/arm/mach-imx/ehci-imx27.c @@ -17,6 +17,7 @@ #include #include +#include "ehci.h" #include "hardware.h" #define USBCTRL_OTGBASE_OFFSET 0x600 diff --git a/arch/arm/mach-imx/ehci-imx31.c b/arch/arm/mach-imx/ehci-imx31.c index 05de4e1e39d7..bede21d9b981 100644 --- a/arch/arm/mach-imx/ehci-imx31.c +++ b/arch/arm/mach-imx/ehci-imx31.c @@ -17,6 +17,7 @@ #include #include +#include "ehci.h" #include "hardware.h" #define USBCTRL_OTGBASE_OFFSET 0x600 diff --git a/arch/arm/mach-imx/ehci-imx35.c b/arch/arm/mach-imx/ehci-imx35.c index 554e7cccff53..f424a543755c 100644 --- a/arch/arm/mach-imx/ehci-imx35.c +++ b/arch/arm/mach-imx/ehci-imx35.c @@ -17,6 +17,7 @@ #include #include +#include "ehci.h" #include "hardware.h" #define USBCTRL_OTGBASE_OFFSET 0x600 diff --git a/arch/arm/mach-imx/ehci-imx5.c b/arch/arm/mach-imx/ehci-imx5.c index e49710b10c68..74bfdd970bfe 100644 --- a/arch/arm/mach-imx/ehci-imx5.c +++ b/arch/arm/mach-imx/ehci-imx5.c @@ -17,6 +17,7 @@ #include #include +#include "ehci.h" #include "hardware.h" #define MXC_OTG_OFFSET 0 diff --git a/arch/arm/mach-imx/ehci.h b/arch/arm/mach-imx/ehci.h new file mode 100644 index 000000000000..4f42ca380b26 --- /dev/null +++ b/arch/arm/mach-imx/ehci.h @@ -0,0 +1,50 @@ +#ifndef __MACH_IMX_EHCI_H +#define __MACH_IMX_EHCI_H + +/* values for portsc field */ +#define MXC_EHCI_PHY_LOW_POWER_SUSPEND (1 << 23) +#define MXC_EHCI_FORCE_FS (1 << 24) +#define MXC_EHCI_UTMI_8BIT (0 << 28) +#define MXC_EHCI_UTMI_16BIT (1 << 28) +#define MXC_EHCI_SERIAL (1 << 29) +#define MXC_EHCI_MODE_UTMI (0 << 30) +#define MXC_EHCI_MODE_PHILIPS (1 << 30) +#define MXC_EHCI_MODE_ULPI (2 << 30) +#define MXC_EHCI_MODE_SERIAL (3 << 30) + +/* values for flags field */ +#define MXC_EHCI_INTERFACE_DIFF_UNI (0 << 0) +#define MXC_EHCI_INTERFACE_DIFF_BI (1 << 0) +#define MXC_EHCI_INTERFACE_SINGLE_UNI (2 << 0) +#define MXC_EHCI_INTERFACE_SINGLE_BI (3 << 0) +#define MXC_EHCI_INTERFACE_MASK (0xf) + +#define MXC_EHCI_POWER_PINS_ENABLED (1 << 5) +#define MXC_EHCI_PWR_PIN_ACTIVE_HIGH (1 << 6) +#define MXC_EHCI_OC_PIN_ACTIVE_LOW (1 << 7) +#define MXC_EHCI_TTL_ENABLED (1 << 8) + +#define MXC_EHCI_INTERNAL_PHY (1 << 9) +#define MXC_EHCI_IPPUE_DOWN (1 << 10) +#define MXC_EHCI_IPPUE_UP (1 << 11) +#define MXC_EHCI_WAKEUP_ENABLED (1 << 12) +#define MXC_EHCI_ITC_NO_THRESHOLD (1 << 13) + +#define MXC_USBCTRL_OFFSET 0 +#define MXC_USB_PHY_CTR_FUNC_OFFSET 0x8 +#define MXC_USB_PHY_CTR_FUNC2_OFFSET 0xc +#define MXC_USBH2CTRL_OFFSET 0x14 + +#define MX5_USBOTHER_REGS_OFFSET 0x800 + +/* USB_PHY_CTRL_FUNC2*/ +#define MX5_USB_UTMI_PHYCTRL1_PLLDIV_MASK 0x3 +#define MX5_USB_UTMI_PHYCTRL1_PLLDIV_SHIFT 0 + +int mx51_initialize_usb_hw(int port, unsigned int flags); +int mx25_initialize_usb_hw(int port, unsigned int flags); +int mx31_initialize_usb_hw(int port, unsigned int flags); +int mx35_initialize_usb_hw(int port, unsigned int flags); +int mx27_initialize_usb_hw(int port, unsigned int flags); + +#endif /* __MACH_IMX_EHCI_H */ diff --git a/arch/arm/mach-imx/mach-armadillo5x0.c b/arch/arm/mach-imx/mach-armadillo5x0.c index 39406b7e3228..a7e9bd26a552 100644 --- a/arch/arm/mach-imx/mach-armadillo5x0.c +++ b/arch/arm/mach-imx/mach-armadillo5x0.c @@ -50,6 +50,7 @@ #include "common.h" #include "devices-imx31.h" #include "crmregs-imx3.h" +#include "ehci.h" #include "hardware.h" #include "iomux-mx3.h" #include "ulpi.h" diff --git a/arch/arm/mach-imx/mach-cpuimx27.c b/arch/arm/mach-imx/mach-cpuimx27.c index 75b7b6aa2720..e6d4b9929571 100644 --- a/arch/arm/mach-imx/mach-cpuimx27.c +++ b/arch/arm/mach-imx/mach-cpuimx27.c @@ -36,6 +36,7 @@ #include "common.h" #include "devices-imx27.h" +#include "ehci.h" #include "eukrea-baseboards.h" #include "hardware.h" #include "iomux-mx27.h" diff --git a/arch/arm/mach-imx/mach-cpuimx35.c b/arch/arm/mach-imx/mach-cpuimx35.c index 1ffa27169045..62a6e02f4763 100644 --- a/arch/arm/mach-imx/mach-cpuimx35.c +++ b/arch/arm/mach-imx/mach-cpuimx35.c @@ -39,6 +39,7 @@ #include "common.h" #include "devices-imx35.h" +#include "ehci.h" #include "eukrea-baseboards.h" #include "hardware.h" #include "iomux-mx35.h" diff --git a/arch/arm/mach-imx/mach-eukrea_cpuimx25.c b/arch/arm/mach-imx/mach-eukrea_cpuimx25.c index e978dda1434c..b2ee6e009fe4 100644 --- a/arch/arm/mach-imx/mach-eukrea_cpuimx25.c +++ b/arch/arm/mach-imx/mach-eukrea_cpuimx25.c @@ -35,6 +35,7 @@ #include "common.h" #include "devices-imx25.h" +#include "ehci.h" #include "eukrea-baseboards.h" #include "hardware.h" #include "iomux-mx25.h" diff --git a/arch/arm/mach-imx/mach-imx27_visstrim_m10.c b/arch/arm/mach-imx/mach-imx27_visstrim_m10.c index b61bd8ed5568..ede2bdbb5dd5 100644 --- a/arch/arm/mach-imx/mach-imx27_visstrim_m10.c +++ b/arch/arm/mach-imx/mach-imx27_visstrim_m10.c @@ -43,6 +43,7 @@ #include "common.h" #include "devices-imx27.h" +#include "ehci.h" #include "hardware.h" #include "iomux-mx27.h" diff --git a/arch/arm/mach-imx/mach-mx25_3ds.c b/arch/arm/mach-imx/mach-mx25_3ds.c index ea1fa199c148..0d01e367b062 100644 --- a/arch/arm/mach-imx/mach-mx25_3ds.c +++ b/arch/arm/mach-imx/mach-mx25_3ds.c @@ -39,6 +39,7 @@ #include "common.h" #include "devices-imx25.h" +#include "ehci.h" #include "hardware.h" #include "iomux-mx25.h" #include "mx25.h" diff --git a/arch/arm/mach-imx/mach-mx27_3ds.c b/arch/arm/mach-imx/mach-mx27_3ds.c index 435a5428a678..9ef4640f3660 100644 --- a/arch/arm/mach-imx/mach-mx27_3ds.c +++ b/arch/arm/mach-imx/mach-mx27_3ds.c @@ -40,6 +40,7 @@ #include "3ds_debugboard.h" #include "common.h" #include "devices-imx27.h" +#include "ehci.h" #include "hardware.h" #include "iomux-mx27.h" #include "ulpi.h" diff --git a/arch/arm/mach-imx/mach-mx31_3ds.c b/arch/arm/mach-imx/mach-mx31_3ds.c index 4217871a9653..453f41a2c5a9 100644 --- a/arch/arm/mach-imx/mach-mx31_3ds.c +++ b/arch/arm/mach-imx/mach-mx31_3ds.c @@ -40,6 +40,7 @@ #include "3ds_debugboard.h" #include "common.h" #include "devices-imx31.h" +#include "ehci.h" #include "hardware.h" #include "iomux-mx3.h" #include "ulpi.h" diff --git a/arch/arm/mach-imx/mach-mx31lilly.c b/arch/arm/mach-imx/mach-mx31lilly.c index eee042fa2768..e9549a3c0223 100644 --- a/arch/arm/mach-imx/mach-mx31lilly.c +++ b/arch/arm/mach-imx/mach-mx31lilly.c @@ -45,6 +45,7 @@ #include "board-mx31lilly.h" #include "common.h" #include "devices-imx31.h" +#include "ehci.h" #include "hardware.h" #include "iomux-mx3.h" #include "ulpi.h" diff --git a/arch/arm/mach-imx/mach-mx31lite.c b/arch/arm/mach-imx/mach-mx31lite.c index fa15d0b6118d..57eac6f45fab 100644 --- a/arch/arm/mach-imx/mach-mx31lite.c +++ b/arch/arm/mach-imx/mach-mx31lite.c @@ -42,6 +42,7 @@ #include "board-mx31lite.h" #include "common.h" #include "devices-imx31.h" +#include "ehci.h" #include "hardware.h" #include "iomux-mx3.h" #include "ulpi.h" diff --git a/arch/arm/mach-imx/mach-mx31moboard.c b/arch/arm/mach-imx/mach-mx31moboard.c index 08730f238449..6bed57040973 100644 --- a/arch/arm/mach-imx/mach-mx31moboard.c +++ b/arch/arm/mach-imx/mach-mx31moboard.c @@ -47,6 +47,7 @@ #include "board-mx31moboard.h" #include "common.h" #include "devices-imx31.h" +#include "ehci.h" #include "hardware.h" #include "iomux-mx3.h" #include "ulpi.h" diff --git a/arch/arm/mach-imx/mach-mx35_3ds.c b/arch/arm/mach-imx/mach-mx35_3ds.c index 4e8b184d773b..72cd77d21f63 100644 --- a/arch/arm/mach-imx/mach-mx35_3ds.c +++ b/arch/arm/mach-imx/mach-mx35_3ds.c @@ -50,6 +50,7 @@ #include "3ds_debugboard.h" #include "common.h" #include "devices-imx35.h" +#include "ehci.h" #include "hardware.h" #include "iomux-mx35.h" diff --git a/arch/arm/mach-imx/mach-pca100.c b/arch/arm/mach-imx/mach-pca100.c index 12212378c672..2d1c50bd8bdf 100644 --- a/arch/arm/mach-imx/mach-pca100.c +++ b/arch/arm/mach-imx/mach-pca100.c @@ -36,6 +36,7 @@ #include "common.h" #include "devices-imx27.h" +#include "ehci.h" #include "hardware.h" #include "iomux-mx27.h" #include "ulpi.h" diff --git a/arch/arm/mach-imx/mach-pcm037.c b/arch/arm/mach-imx/mach-pcm037.c index 81b8affb9448..8eb1570f7851 100644 --- a/arch/arm/mach-imx/mach-pcm037.c +++ b/arch/arm/mach-imx/mach-pcm037.c @@ -45,6 +45,7 @@ #include "common.h" #include "devices-imx31.h" +#include "ehci.h" #include "hardware.h" #include "iomux-mx3.h" #include "pcm037.h" diff --git a/arch/arm/mach-imx/mach-pcm038.c b/arch/arm/mach-imx/mach-pcm038.c index 6c56fb5553c7..ee862ad6b6fc 100644 --- a/arch/arm/mach-imx/mach-pcm038.c +++ b/arch/arm/mach-imx/mach-pcm038.c @@ -36,6 +36,7 @@ #include "board-pcm038.h" #include "common.h" #include "devices-imx27.h" +#include "ehci.h" #include "hardware.h" #include "iomux-mx27.h" #include "ulpi.h" diff --git a/arch/arm/mach-imx/mach-pcm043.c b/arch/arm/mach-imx/mach-pcm043.c index c62b5d261345..b623bcaca76c 100644 --- a/arch/arm/mach-imx/mach-pcm043.c +++ b/arch/arm/mach-imx/mach-pcm043.c @@ -35,6 +35,7 @@ #include "common.h" #include "devices-imx35.h" +#include "ehci.h" #include "hardware.h" #include "iomux-mx35.h" #include "ulpi.h" diff --git a/arch/arm/mach-imx/mach-vpr200.c b/arch/arm/mach-imx/mach-vpr200.c index 872b3c6ba408..97836e94451c 100644 --- a/arch/arm/mach-imx/mach-vpr200.c +++ b/arch/arm/mach-imx/mach-vpr200.c @@ -34,6 +34,7 @@ #include "common.h" #include "devices-imx35.h" +#include "ehci.h" #include "hardware.h" #include "iomux-mx35.h" diff --git a/arch/arm/mach-imx/mx31moboard-devboard.c b/arch/arm/mach-imx/mx31moboard-devboard.c index 52d5b1574721..1e4ea1640a2a 100644 --- a/arch/arm/mach-imx/mx31moboard-devboard.c +++ b/arch/arm/mach-imx/mx31moboard-devboard.c @@ -24,6 +24,7 @@ #include "common.h" #include "devices-imx31.h" +#include "ehci.h" #include "hardware.h" #include "iomux-mx3.h" #include "ulpi.h" diff --git a/arch/arm/mach-imx/mx31moboard-marxbot.c b/arch/arm/mach-imx/mx31moboard-marxbot.c index a4f43e90f3c1..699d01a4fef8 100644 --- a/arch/arm/mach-imx/mx31moboard-marxbot.c +++ b/arch/arm/mach-imx/mx31moboard-marxbot.c @@ -28,6 +28,7 @@ #include "common.h" #include "devices-imx31.h" +#include "ehci.h" #include "hardware.h" #include "iomux-mx3.h" #include "ulpi.h" diff --git a/arch/arm/mach-imx/mx31moboard-smartbot.c b/arch/arm/mach-imx/mx31moboard-smartbot.c index 04ae45dbfaa7..4b3d66eb8d34 100644 --- a/arch/arm/mach-imx/mx31moboard-smartbot.c +++ b/arch/arm/mach-imx/mx31moboard-smartbot.c @@ -28,6 +28,7 @@ #include "board-mx31moboard.h" #include "common.h" #include "devices-imx31.h" +#include "ehci.h" #include "hardware.h" #include "iomux-mx3.h" #include "ulpi.h" diff --git a/include/linux/platform_data/usb-ehci-mxc.h b/include/linux/platform_data/usb-ehci-mxc.h index 7eb9d1329671..157e71f79f99 100644 --- a/include/linux/platform_data/usb-ehci-mxc.h +++ b/include/linux/platform_data/usb-ehci-mxc.h @@ -1,46 +1,6 @@ #ifndef __INCLUDE_ASM_ARCH_MXC_EHCI_H #define __INCLUDE_ASM_ARCH_MXC_EHCI_H -/* values for portsc field */ -#define MXC_EHCI_PHY_LOW_POWER_SUSPEND (1 << 23) -#define MXC_EHCI_FORCE_FS (1 << 24) -#define MXC_EHCI_UTMI_8BIT (0 << 28) -#define MXC_EHCI_UTMI_16BIT (1 << 28) -#define MXC_EHCI_SERIAL (1 << 29) -#define MXC_EHCI_MODE_UTMI (0 << 30) -#define MXC_EHCI_MODE_PHILIPS (1 << 30) -#define MXC_EHCI_MODE_ULPI (2 << 30) -#define MXC_EHCI_MODE_SERIAL (3 << 30) - -/* values for flags field */ -#define MXC_EHCI_INTERFACE_DIFF_UNI (0 << 0) -#define MXC_EHCI_INTERFACE_DIFF_BI (1 << 0) -#define MXC_EHCI_INTERFACE_SINGLE_UNI (2 << 0) -#define MXC_EHCI_INTERFACE_SINGLE_BI (3 << 0) -#define MXC_EHCI_INTERFACE_MASK (0xf) - -#define MXC_EHCI_POWER_PINS_ENABLED (1 << 5) -#define MXC_EHCI_PWR_PIN_ACTIVE_HIGH (1 << 6) -#define MXC_EHCI_OC_PIN_ACTIVE_LOW (1 << 7) -#define MXC_EHCI_TTL_ENABLED (1 << 8) - -#define MXC_EHCI_INTERNAL_PHY (1 << 9) -#define MXC_EHCI_IPPUE_DOWN (1 << 10) -#define MXC_EHCI_IPPUE_UP (1 << 11) -#define MXC_EHCI_WAKEUP_ENABLED (1 << 12) -#define MXC_EHCI_ITC_NO_THRESHOLD (1 << 13) - -#define MXC_USBCTRL_OFFSET 0 -#define MXC_USB_PHY_CTR_FUNC_OFFSET 0x8 -#define MXC_USB_PHY_CTR_FUNC2_OFFSET 0xc -#define MXC_USBH2CTRL_OFFSET 0x14 - -#define MX5_USBOTHER_REGS_OFFSET 0x800 - -/* USB_PHY_CTRL_FUNC2*/ -#define MX5_USB_UTMI_PHYCTRL1_PLLDIV_MASK 0x3 -#define MX5_USB_UTMI_PHYCTRL1_PLLDIV_SHIFT 0 - struct mxc_usbh_platform_data { int (*init)(struct platform_device *pdev); int (*exit)(struct platform_device *pdev); @@ -49,11 +9,5 @@ struct mxc_usbh_platform_data { struct usb_phy *otg; }; -int mx51_initialize_usb_hw(int port, unsigned int flags); -int mx25_initialize_usb_hw(int port, unsigned int flags); -int mx31_initialize_usb_hw(int port, unsigned int flags); -int mx35_initialize_usb_hw(int port, unsigned int flags); -int mx27_initialize_usb_hw(int port, unsigned int flags); - #endif /* __INCLUDE_ASM_ARCH_MXC_EHCI_H */ -- cgit v1.2.3 From d2d2e54d6655d78e619bfa22e186d82ca5d6e880 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sun, 15 Jun 2014 19:35:10 +0800 Subject: ARM: imx6qdl: switch to use macro for clock ID Instead of using enum for clock ID, let's switch imx6qdl clock driver to use macro. In this case, device tree can reuse these macros to improve readability. Signed-off-by: Shawn Guo --- .../devicetree/bindings/clock/imx6q-clock.txt | 220 +-------- arch/arm/mach-imx/clk-imx6q.c | 529 ++++++++++----------- include/dt-bindings/clock/imx6qdl-clock.h | 224 +++++++++ 3 files changed, 476 insertions(+), 497 deletions(-) create mode 100644 include/dt-bindings/clock/imx6qdl-clock.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/imx6q-clock.txt b/Documentation/devicetree/bindings/clock/imx6q-clock.txt index 90ec91fe5ce0..9252912a5b0e 100644 --- a/Documentation/devicetree/bindings/clock/imx6q-clock.txt +++ b/Documentation/devicetree/bindings/clock/imx6q-clock.txt @@ -7,223 +7,13 @@ Required properties: - #clock-cells: Should be <1> The clock consumer should specify the desired clock by having the clock -ID in its "clocks" phandle cell. The following is a full list of i.MX6Q -clocks and IDs. - - Clock ID - --------------------------- - dummy 0 - ckil 1 - ckih 2 - osc 3 - pll2_pfd0_352m 4 - pll2_pfd1_594m 5 - pll2_pfd2_396m 6 - pll3_pfd0_720m 7 - pll3_pfd1_540m 8 - pll3_pfd2_508m 9 - pll3_pfd3_454m 10 - pll2_198m 11 - pll3_120m 12 - pll3_80m 13 - pll3_60m 14 - twd 15 - step 16 - pll1_sw 17 - periph_pre 18 - periph2_pre 19 - periph_clk2_sel 20 - periph2_clk2_sel 21 - axi_sel 22 - esai_sel 23 - asrc_sel 24 - spdif_sel 25 - gpu2d_axi 26 - gpu3d_axi 27 - gpu2d_core_sel 28 - gpu3d_core_sel 29 - gpu3d_shader_sel 30 - ipu1_sel 31 - ipu2_sel 32 - ldb_di0_sel 33 - ldb_di1_sel 34 - ipu1_di0_pre_sel 35 - ipu1_di1_pre_sel 36 - ipu2_di0_pre_sel 37 - ipu2_di1_pre_sel 38 - ipu1_di0_sel 39 - ipu1_di1_sel 40 - ipu2_di0_sel 41 - ipu2_di1_sel 42 - hsi_tx_sel 43 - pcie_axi_sel 44 - ssi1_sel 45 - ssi2_sel 46 - ssi3_sel 47 - usdhc1_sel 48 - usdhc2_sel 49 - usdhc3_sel 50 - usdhc4_sel 51 - enfc_sel 52 - emi_sel 53 - emi_slow_sel 54 - vdo_axi_sel 55 - vpu_axi_sel 56 - cko1_sel 57 - periph 58 - periph2 59 - periph_clk2 60 - periph2_clk2 61 - ipg 62 - ipg_per 63 - esai_pred 64 - esai_podf 65 - asrc_pred 66 - asrc_podf 67 - spdif_pred 68 - spdif_podf 69 - can_root 70 - ecspi_root 71 - gpu2d_core_podf 72 - gpu3d_core_podf 73 - gpu3d_shader 74 - ipu1_podf 75 - ipu2_podf 76 - ldb_di0_podf 77 - ldb_di1_podf 78 - ipu1_di0_pre 79 - ipu1_di1_pre 80 - ipu2_di0_pre 81 - ipu2_di1_pre 82 - hsi_tx_podf 83 - ssi1_pred 84 - ssi1_podf 85 - ssi2_pred 86 - ssi2_podf 87 - ssi3_pred 88 - ssi3_podf 89 - uart_serial_podf 90 - usdhc1_podf 91 - usdhc2_podf 92 - usdhc3_podf 93 - usdhc4_podf 94 - enfc_pred 95 - enfc_podf 96 - emi_podf 97 - emi_slow_podf 98 - vpu_axi_podf 99 - cko1_podf 100 - axi 101 - mmdc_ch0_axi_podf 102 - mmdc_ch1_axi_podf 103 - arm 104 - ahb 105 - apbh_dma 106 - asrc 107 - can1_ipg 108 - can1_serial 109 - can2_ipg 110 - can2_serial 111 - ecspi1 112 - ecspi2 113 - ecspi3 114 - ecspi4 115 - ecspi5 116 - enet 117 - esai 118 - gpt_ipg 119 - gpt_ipg_per 120 - gpu2d_core 121 - gpu3d_core 122 - hdmi_iahb 123 - hdmi_isfr 124 - i2c1 125 - i2c2 126 - i2c3 127 - iim 128 - enfc 129 - ipu1 130 - ipu1_di0 131 - ipu1_di1 132 - ipu2 133 - ipu2_di0 134 - ldb_di0 135 - ldb_di1 136 - ipu2_di1 137 - hsi_tx 138 - mlb 139 - mmdc_ch0_axi 140 - mmdc_ch1_axi 141 - ocram 142 - openvg_axi 143 - pcie_axi 144 - pwm1 145 - pwm2 146 - pwm3 147 - pwm4 148 - per1_bch 149 - gpmi_bch_apb 150 - gpmi_bch 151 - gpmi_io 152 - gpmi_apb 153 - sata 154 - sdma 155 - spba 156 - ssi1 157 - ssi2 158 - ssi3 159 - uart_ipg 160 - uart_serial 161 - usboh3 162 - usdhc1 163 - usdhc2 164 - usdhc3 165 - usdhc4 166 - vdo_axi 167 - vpu_axi 168 - cko1 169 - pll1_sys 170 - pll2_bus 171 - pll3_usb_otg 172 - pll4_audio 173 - pll5_video 174 - pll8_mlb 175 - pll7_usb_host 176 - pll6_enet 177 - ssi1_ipg 178 - ssi2_ipg 179 - ssi3_ipg 180 - rom 181 - usbphy1 182 - usbphy2 183 - ldb_di0_div_3_5 184 - ldb_di1_div_3_5 185 - sata_ref 186 - sata_ref_100m 187 - pcie_ref 188 - pcie_ref_125m 189 - enet_ref 190 - usbphy1_gate 191 - usbphy2_gate 192 - pll4_post_div 193 - pll5_post_div 194 - pll5_video_div 195 - eim_slow 196 - spdif 197 - cko2_sel 198 - cko2_podf 199 - cko2 200 - cko 201 - vdoa 202 - pll4_audio_div 203 - lvds1_sel 204 - lvds2_sel 205 - lvds1_gate 206 - lvds2_gate 207 - esai_ahb 208 +ID in its "clocks" phandle cell. See include/dt-bindings/clock/imx6qdl-clock.h +for the full list of i.MX6 Quad and DualLite clock IDs. Examples: +#include + clks: ccm@020c4000 { compatible = "fsl,imx6q-ccm"; reg = <0x020c4000 0x4000>; @@ -235,7 +25,7 @@ uart1: serial@02020000 { compatible = "fsl,imx6q-uart", "fsl,imx21-uart"; reg = <0x02020000 0x4000>; interrupts = <0 26 0x04>; - clocks = <&clks 160>, <&clks 161>; + clocks = <&clks IMX6QDL_CLK_UART_IPG>, <&clks IMX6QDL_CLK_UART_SERIAL>; clock-names = "ipg", "per"; status = "disabled"; }; diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c index 56c897ef454b..8a827acdde70 100644 --- a/arch/arm/mach-imx/clk-imx6q.c +++ b/arch/arm/mach-imx/clk-imx6q.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "clk.h" #include "common.h" @@ -73,48 +74,13 @@ static const char *lvds_sels[] = { "pcie_ref_125m", "sata_ref_100m", }; -enum mx6q_clks { - dummy, ckil, ckih, osc, pll2_pfd0_352m, pll2_pfd1_594m, pll2_pfd2_396m, - pll3_pfd0_720m, pll3_pfd1_540m, pll3_pfd2_508m, pll3_pfd3_454m, - pll2_198m, pll3_120m, pll3_80m, pll3_60m, twd, step, pll1_sw, - periph_pre, periph2_pre, periph_clk2_sel, periph2_clk2_sel, axi_sel, - esai_sel, asrc_sel, spdif_sel, gpu2d_axi, gpu3d_axi, gpu2d_core_sel, - gpu3d_core_sel, gpu3d_shader_sel, ipu1_sel, ipu2_sel, ldb_di0_sel, - ldb_di1_sel, ipu1_di0_pre_sel, ipu1_di1_pre_sel, ipu2_di0_pre_sel, - ipu2_di1_pre_sel, ipu1_di0_sel, ipu1_di1_sel, ipu2_di0_sel, - ipu2_di1_sel, hsi_tx_sel, pcie_axi_sel, ssi1_sel, ssi2_sel, ssi3_sel, - usdhc1_sel, usdhc2_sel, usdhc3_sel, usdhc4_sel, enfc_sel, emi_sel, - emi_slow_sel, vdo_axi_sel, vpu_axi_sel, cko1_sel, periph, periph2, - periph_clk2, periph2_clk2, ipg, ipg_per, esai_pred, esai_podf, - asrc_pred, asrc_podf, spdif_pred, spdif_podf, can_root, ecspi_root, - gpu2d_core_podf, gpu3d_core_podf, gpu3d_shader, ipu1_podf, ipu2_podf, - ldb_di0_podf, ldb_di1_podf, ipu1_di0_pre, ipu1_di1_pre, ipu2_di0_pre, - ipu2_di1_pre, hsi_tx_podf, ssi1_pred, ssi1_podf, ssi2_pred, ssi2_podf, - ssi3_pred, ssi3_podf, uart_serial_podf, usdhc1_podf, usdhc2_podf, - usdhc3_podf, usdhc4_podf, enfc_pred, enfc_podf, emi_podf, - emi_slow_podf, vpu_axi_podf, cko1_podf, axi, mmdc_ch0_axi_podf, - mmdc_ch1_axi_podf, arm, ahb, apbh_dma, asrc, can1_ipg, can1_serial, - can2_ipg, can2_serial, ecspi1, ecspi2, ecspi3, ecspi4, ecspi5, enet, - esai, gpt_ipg, gpt_ipg_per, gpu2d_core, gpu3d_core, hdmi_iahb, - hdmi_isfr, i2c1, i2c2, i2c3, iim, enfc, ipu1, ipu1_di0, ipu1_di1, ipu2, - ipu2_di0, ldb_di0, ldb_di1, ipu2_di1, hsi_tx, mlb, mmdc_ch0_axi, - mmdc_ch1_axi, ocram, openvg_axi, pcie_axi, pwm1, pwm2, pwm3, pwm4, per1_bch, - gpmi_bch_apb, gpmi_bch, gpmi_io, gpmi_apb, sata, sdma, spba, ssi1, - ssi2, ssi3, uart_ipg, uart_serial, usboh3, usdhc1, usdhc2, usdhc3, - usdhc4, vdo_axi, vpu_axi, cko1, pll1_sys, pll2_bus, pll3_usb_otg, - pll4_audio, pll5_video, pll8_mlb, pll7_usb_host, pll6_enet, ssi1_ipg, - ssi2_ipg, ssi3_ipg, rom, usbphy1, usbphy2, ldb_di0_div_3_5, ldb_di1_div_3_5, - sata_ref, sata_ref_100m, pcie_ref, pcie_ref_125m, enet_ref, usbphy1_gate, - usbphy2_gate, pll4_post_div, pll5_post_div, pll5_video_div, eim_slow, - spdif, cko2_sel, cko2_podf, cko2, cko, vdoa, pll4_audio_div, - lvds1_sel, lvds2_sel, lvds1_gate, lvds2_gate, esai_ahb, clk_max -}; - -static struct clk *clk[clk_max]; +static struct clk *clk[IMX6QDL_CLK_END]; static struct clk_onecell_data clk_data; -static enum mx6q_clks const clks_init_on[] __initconst = { - mmdc_ch0_axi, rom, arm, +static unsigned int const clks_init_on[] __initconst = { + IMX6QDL_CLK_MMDC_CH0_AXI, + IMX6QDL_CLK_ROM, + IMX6QDL_CLK_ARM, }; static struct clk_div_table clk_enet_ref_table[] = { @@ -149,10 +115,10 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) int i; int ret; - clk[dummy] = imx_clk_fixed("dummy", 0); - clk[ckil] = imx_obtain_fixed_clock("ckil", 0); - clk[ckih] = imx_obtain_fixed_clock("ckih1", 0); - clk[osc] = imx_obtain_fixed_clock("osc", 0); + clk[IMX6QDL_CLK_DUMMY] = imx_clk_fixed("dummy", 0); + clk[IMX6QDL_CLK_CKIL] = imx_obtain_fixed_clock("ckil", 0); + clk[IMX6QDL_CLK_CKIH] = imx_obtain_fixed_clock("ckih1", 0); + clk[IMX6QDL_CLK_OSC] = imx_obtain_fixed_clock("osc", 0); np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop"); base = of_iomap(np, 0); @@ -166,14 +132,14 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) video_div_table[2].div = 1; }; - /* type name parent_name base div_mask */ - clk[pll1_sys] = imx_clk_pllv3(IMX_PLLV3_SYS, "pll1_sys", "osc", base, 0x7f); - clk[pll2_bus] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_bus", "osc", base + 0x30, 0x1); - clk[pll3_usb_otg] = imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc", base + 0x10, 0x3); - clk[pll4_audio] = imx_clk_pllv3(IMX_PLLV3_AV, "pll4_audio", "osc", base + 0x70, 0x7f); - clk[pll5_video] = imx_clk_pllv3(IMX_PLLV3_AV, "pll5_video", "osc", base + 0xa0, 0x7f); - clk[pll6_enet] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll6_enet", "osc", base + 0xe0, 0x3); - clk[pll7_usb_host] = imx_clk_pllv3(IMX_PLLV3_USB, "pll7_usb_host","osc", base + 0x20, 0x3); + /* type name parent_name base div_mask */ + clk[IMX6QDL_CLK_PLL1_SYS] = imx_clk_pllv3(IMX_PLLV3_SYS, "pll1_sys", "osc", base, 0x7f); + clk[IMX6QDL_CLK_PLL2_BUS] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_bus", "osc", base + 0x30, 0x1); + clk[IMX6QDL_CLK_PLL3_USB_OTG] = imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc", base + 0x10, 0x3); + clk[IMX6QDL_CLK_PLL4_AUDIO] = imx_clk_pllv3(IMX_PLLV3_AV, "pll4_audio", "osc", base + 0x70, 0x7f); + clk[IMX6QDL_CLK_PLL5_VIDEO] = imx_clk_pllv3(IMX_PLLV3_AV, "pll5_video", "osc", base + 0xa0, 0x7f); + clk[IMX6QDL_CLK_PLL6_ENET] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll6_enet", "osc", base + 0xe0, 0x3); + clk[IMX6QDL_CLK_PLL7_USB_HOST] = imx_clk_pllv3(IMX_PLLV3_USB, "pll7_usb_host","osc", base + 0x20, 0x3); /* * Bit 20 is the reserved and read-only bit, we do this only for: @@ -181,28 +147,28 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) * - Keep refcount when do usbphy clk_enable/disable, in that case, * the clk framework may need to enable/disable usbphy's parent */ - clk[usbphy1] = imx_clk_gate("usbphy1", "pll3_usb_otg", base + 0x10, 20); - clk[usbphy2] = imx_clk_gate("usbphy2", "pll7_usb_host", base + 0x20, 20); + clk[IMX6QDL_CLK_USBPHY1] = imx_clk_gate("usbphy1", "pll3_usb_otg", base + 0x10, 20); + clk[IMX6QDL_CLK_USBPHY2] = imx_clk_gate("usbphy2", "pll7_usb_host", base + 0x20, 20); /* * usbphy*_gate needs to be on after system boots up, and software * never needs to control it anymore. */ - clk[usbphy1_gate] = imx_clk_gate("usbphy1_gate", "dummy", base + 0x10, 6); - clk[usbphy2_gate] = imx_clk_gate("usbphy2_gate", "dummy", base + 0x20, 6); + clk[IMX6QDL_CLK_USBPHY1_GATE] = imx_clk_gate("usbphy1_gate", "dummy", base + 0x10, 6); + clk[IMX6QDL_CLK_USBPHY2_GATE] = imx_clk_gate("usbphy2_gate", "dummy", base + 0x20, 6); - clk[sata_ref] = imx_clk_fixed_factor("sata_ref", "pll6_enet", 1, 5); - clk[pcie_ref] = imx_clk_fixed_factor("pcie_ref", "pll6_enet", 1, 4); + clk[IMX6QDL_CLK_SATA_REF] = imx_clk_fixed_factor("sata_ref", "pll6_enet", 1, 5); + clk[IMX6QDL_CLK_PCIE_REF] = imx_clk_fixed_factor("pcie_ref", "pll6_enet", 1, 4); - clk[sata_ref_100m] = imx_clk_gate("sata_ref_100m", "sata_ref", base + 0xe0, 20); - clk[pcie_ref_125m] = imx_clk_gate("pcie_ref_125m", "pcie_ref", base + 0xe0, 19); + clk[IMX6QDL_CLK_SATA_REF_100M] = imx_clk_gate("sata_ref_100m", "sata_ref", base + 0xe0, 20); + clk[IMX6QDL_CLK_PCIE_REF_125M] = imx_clk_gate("pcie_ref_125m", "pcie_ref", base + 0xe0, 19); - clk[enet_ref] = clk_register_divider_table(NULL, "enet_ref", "pll6_enet", 0, + clk[IMX6QDL_CLK_ENET_REF] = clk_register_divider_table(NULL, "enet_ref", "pll6_enet", 0, base + 0xe0, 0, 2, 0, clk_enet_ref_table, &imx_ccm_lock); - clk[lvds1_sel] = imx_clk_mux("lvds1_sel", base + 0x160, 0, 5, lvds_sels, ARRAY_SIZE(lvds_sels)); - clk[lvds2_sel] = imx_clk_mux("lvds2_sel", base + 0x160, 5, 5, lvds_sels, ARRAY_SIZE(lvds_sels)); + clk[IMX6QDL_CLK_LVDS1_SEL] = imx_clk_mux("lvds1_sel", base + 0x160, 0, 5, lvds_sels, ARRAY_SIZE(lvds_sels)); + clk[IMX6QDL_CLK_LVDS2_SEL] = imx_clk_mux("lvds2_sel", base + 0x160, 5, 5, lvds_sels, ARRAY_SIZE(lvds_sels)); /* * lvds1_gate and lvds2_gate are pseudo-gates. Both can be @@ -210,29 +176,29 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) * the "output_enable" bit as a gate, even though it's really just * enabling clock output. */ - clk[lvds1_gate] = imx_clk_gate("lvds1_gate", "lvds1_sel", base + 0x160, 10); - clk[lvds2_gate] = imx_clk_gate("lvds2_gate", "lvds2_sel", base + 0x160, 11); - - /* name parent_name reg idx */ - clk[pll2_pfd0_352m] = imx_clk_pfd("pll2_pfd0_352m", "pll2_bus", base + 0x100, 0); - clk[pll2_pfd1_594m] = imx_clk_pfd("pll2_pfd1_594m", "pll2_bus", base + 0x100, 1); - clk[pll2_pfd2_396m] = imx_clk_pfd("pll2_pfd2_396m", "pll2_bus", base + 0x100, 2); - clk[pll3_pfd0_720m] = imx_clk_pfd("pll3_pfd0_720m", "pll3_usb_otg", base + 0xf0, 0); - clk[pll3_pfd1_540m] = imx_clk_pfd("pll3_pfd1_540m", "pll3_usb_otg", base + 0xf0, 1); - clk[pll3_pfd2_508m] = imx_clk_pfd("pll3_pfd2_508m", "pll3_usb_otg", base + 0xf0, 2); - clk[pll3_pfd3_454m] = imx_clk_pfd("pll3_pfd3_454m", "pll3_usb_otg", base + 0xf0, 3); - - /* name parent_name mult div */ - clk[pll2_198m] = imx_clk_fixed_factor("pll2_198m", "pll2_pfd2_396m", 1, 2); - clk[pll3_120m] = imx_clk_fixed_factor("pll3_120m", "pll3_usb_otg", 1, 4); - clk[pll3_80m] = imx_clk_fixed_factor("pll3_80m", "pll3_usb_otg", 1, 6); - clk[pll3_60m] = imx_clk_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8); - clk[twd] = imx_clk_fixed_factor("twd", "arm", 1, 2); - - clk[pll4_post_div] = clk_register_divider_table(NULL, "pll4_post_div", "pll4_audio", CLK_SET_RATE_PARENT, base + 0x70, 19, 2, 0, post_div_table, &imx_ccm_lock); - clk[pll4_audio_div] = clk_register_divider(NULL, "pll4_audio_div", "pll4_post_div", CLK_SET_RATE_PARENT, base + 0x170, 15, 1, 0, &imx_ccm_lock); - clk[pll5_post_div] = clk_register_divider_table(NULL, "pll5_post_div", "pll5_video", CLK_SET_RATE_PARENT, base + 0xa0, 19, 2, 0, post_div_table, &imx_ccm_lock); - clk[pll5_video_div] = clk_register_divider_table(NULL, "pll5_video_div", "pll5_post_div", CLK_SET_RATE_PARENT, base + 0x170, 30, 2, 0, video_div_table, &imx_ccm_lock); + clk[IMX6QDL_CLK_LVDS1_GATE] = imx_clk_gate("lvds1_gate", "lvds1_sel", base + 0x160, 10); + clk[IMX6QDL_CLK_LVDS2_GATE] = imx_clk_gate("lvds2_gate", "lvds2_sel", base + 0x160, 11); + + /* name parent_name reg idx */ + clk[IMX6QDL_CLK_PLL2_PFD0_352M] = imx_clk_pfd("pll2_pfd0_352m", "pll2_bus", base + 0x100, 0); + clk[IMX6QDL_CLK_PLL2_PFD1_594M] = imx_clk_pfd("pll2_pfd1_594m", "pll2_bus", base + 0x100, 1); + clk[IMX6QDL_CLK_PLL2_PFD2_396M] = imx_clk_pfd("pll2_pfd2_396m", "pll2_bus", base + 0x100, 2); + clk[IMX6QDL_CLK_PLL3_PFD0_720M] = imx_clk_pfd("pll3_pfd0_720m", "pll3_usb_otg", base + 0xf0, 0); + clk[IMX6QDL_CLK_PLL3_PFD1_540M] = imx_clk_pfd("pll3_pfd1_540m", "pll3_usb_otg", base + 0xf0, 1); + clk[IMX6QDL_CLK_PLL3_PFD2_508M] = imx_clk_pfd("pll3_pfd2_508m", "pll3_usb_otg", base + 0xf0, 2); + clk[IMX6QDL_CLK_PLL3_PFD3_454M] = imx_clk_pfd("pll3_pfd3_454m", "pll3_usb_otg", base + 0xf0, 3); + + /* name parent_name mult div */ + clk[IMX6QDL_CLK_PLL2_198M] = imx_clk_fixed_factor("pll2_198m", "pll2_pfd2_396m", 1, 2); + clk[IMX6QDL_CLK_PLL3_120M] = imx_clk_fixed_factor("pll3_120m", "pll3_usb_otg", 1, 4); + clk[IMX6QDL_CLK_PLL3_80M] = imx_clk_fixed_factor("pll3_80m", "pll3_usb_otg", 1, 6); + clk[IMX6QDL_CLK_PLL3_60M] = imx_clk_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8); + clk[IMX6QDL_CLK_TWD] = imx_clk_fixed_factor("twd", "arm", 1, 2); + + clk[IMX6QDL_CLK_PLL4_POST_DIV] = clk_register_divider_table(NULL, "pll4_post_div", "pll4_audio", CLK_SET_RATE_PARENT, base + 0x70, 19, 2, 0, post_div_table, &imx_ccm_lock); + clk[IMX6QDL_CLK_PLL4_AUDIO_DIV] = clk_register_divider(NULL, "pll4_audio_div", "pll4_post_div", CLK_SET_RATE_PARENT, base + 0x170, 15, 1, 0, &imx_ccm_lock); + clk[IMX6QDL_CLK_PLL5_POST_DIV] = clk_register_divider_table(NULL, "pll5_post_div", "pll5_video", CLK_SET_RATE_PARENT, base + 0xa0, 19, 2, 0, post_div_table, &imx_ccm_lock); + clk[IMX6QDL_CLK_PLL5_VIDEO_DIV] = clk_register_divider_table(NULL, "pll5_video_div", "pll5_post_div", CLK_SET_RATE_PARENT, base + 0x170, 30, 2, 0, video_div_table, &imx_ccm_lock); np = ccm_node; base = of_iomap(np, 0); @@ -240,198 +206,197 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) imx6q_pm_set_ccm_base(base); - /* name reg shift width parent_names num_parents */ - clk[step] = imx_clk_mux("step", base + 0xc, 8, 1, step_sels, ARRAY_SIZE(step_sels)); - clk[pll1_sw] = imx_clk_mux("pll1_sw", base + 0xc, 2, 1, pll1_sw_sels, ARRAY_SIZE(pll1_sw_sels)); - clk[periph_pre] = imx_clk_mux("periph_pre", base + 0x18, 18, 2, periph_pre_sels, ARRAY_SIZE(periph_pre_sels)); - clk[periph2_pre] = imx_clk_mux("periph2_pre", base + 0x18, 21, 2, periph_pre_sels, ARRAY_SIZE(periph_pre_sels)); - clk[periph_clk2_sel] = imx_clk_mux("periph_clk2_sel", base + 0x18, 12, 2, periph_clk2_sels, ARRAY_SIZE(periph_clk2_sels)); - clk[periph2_clk2_sel] = imx_clk_mux("periph2_clk2_sel", base + 0x18, 20, 1, periph2_clk2_sels, ARRAY_SIZE(periph2_clk2_sels)); - clk[axi_sel] = imx_clk_mux("axi_sel", base + 0x14, 6, 2, axi_sels, ARRAY_SIZE(axi_sels)); - clk[esai_sel] = imx_clk_mux("esai_sel", base + 0x20, 19, 2, audio_sels, ARRAY_SIZE(audio_sels)); - clk[asrc_sel] = imx_clk_mux("asrc_sel", base + 0x30, 7, 2, audio_sels, ARRAY_SIZE(audio_sels)); - clk[spdif_sel] = imx_clk_mux("spdif_sel", base + 0x30, 20, 2, audio_sels, ARRAY_SIZE(audio_sels)); - clk[gpu2d_axi] = imx_clk_mux("gpu2d_axi", base + 0x18, 0, 1, gpu_axi_sels, ARRAY_SIZE(gpu_axi_sels)); - clk[gpu3d_axi] = imx_clk_mux("gpu3d_axi", base + 0x18, 1, 1, gpu_axi_sels, ARRAY_SIZE(gpu_axi_sels)); - clk[gpu2d_core_sel] = imx_clk_mux("gpu2d_core_sel", base + 0x18, 16, 2, gpu2d_core_sels, ARRAY_SIZE(gpu2d_core_sels)); - clk[gpu3d_core_sel] = imx_clk_mux("gpu3d_core_sel", base + 0x18, 4, 2, gpu3d_core_sels, ARRAY_SIZE(gpu3d_core_sels)); - clk[gpu3d_shader_sel] = imx_clk_mux("gpu3d_shader_sel", base + 0x18, 8, 2, gpu3d_shader_sels, ARRAY_SIZE(gpu3d_shader_sels)); - clk[ipu1_sel] = imx_clk_mux("ipu1_sel", base + 0x3c, 9, 2, ipu_sels, ARRAY_SIZE(ipu_sels)); - clk[ipu2_sel] = imx_clk_mux("ipu2_sel", base + 0x3c, 14, 2, ipu_sels, ARRAY_SIZE(ipu_sels)); - clk[ldb_di0_sel] = imx_clk_mux_flags("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT); - clk[ldb_di1_sel] = imx_clk_mux_flags("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT); - clk[ipu1_di0_pre_sel] = imx_clk_mux_flags("ipu1_di0_pre_sel", base + 0x34, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); - clk[ipu1_di1_pre_sel] = imx_clk_mux_flags("ipu1_di1_pre_sel", base + 0x34, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); - clk[ipu2_di0_pre_sel] = imx_clk_mux_flags("ipu2_di0_pre_sel", base + 0x38, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); - clk[ipu2_di1_pre_sel] = imx_clk_mux_flags("ipu2_di1_pre_sel", base + 0x38, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); - clk[ipu1_di0_sel] = imx_clk_mux_flags("ipu1_di0_sel", base + 0x34, 0, 3, ipu1_di0_sels, ARRAY_SIZE(ipu1_di0_sels), CLK_SET_RATE_PARENT); - clk[ipu1_di1_sel] = imx_clk_mux_flags("ipu1_di1_sel", base + 0x34, 9, 3, ipu1_di1_sels, ARRAY_SIZE(ipu1_di1_sels), CLK_SET_RATE_PARENT); - clk[ipu2_di0_sel] = imx_clk_mux_flags("ipu2_di0_sel", base + 0x38, 0, 3, ipu2_di0_sels, ARRAY_SIZE(ipu2_di0_sels), CLK_SET_RATE_PARENT); - clk[ipu2_di1_sel] = imx_clk_mux_flags("ipu2_di1_sel", base + 0x38, 9, 3, ipu2_di1_sels, ARRAY_SIZE(ipu2_di1_sels), CLK_SET_RATE_PARENT); - clk[hsi_tx_sel] = imx_clk_mux("hsi_tx_sel", base + 0x30, 28, 1, hsi_tx_sels, ARRAY_SIZE(hsi_tx_sels)); - clk[pcie_axi_sel] = imx_clk_mux("pcie_axi_sel", base + 0x18, 10, 1, pcie_axi_sels, ARRAY_SIZE(pcie_axi_sels)); - clk[ssi1_sel] = imx_clk_fixup_mux("ssi1_sel", base + 0x1c, 10, 2, ssi_sels, ARRAY_SIZE(ssi_sels), imx_cscmr1_fixup); - clk[ssi2_sel] = imx_clk_fixup_mux("ssi2_sel", base + 0x1c, 12, 2, ssi_sels, ARRAY_SIZE(ssi_sels), imx_cscmr1_fixup); - clk[ssi3_sel] = imx_clk_fixup_mux("ssi3_sel", base + 0x1c, 14, 2, ssi_sels, ARRAY_SIZE(ssi_sels), imx_cscmr1_fixup); - clk[usdhc1_sel] = imx_clk_fixup_mux("usdhc1_sel", base + 0x1c, 16, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels), imx_cscmr1_fixup); - clk[usdhc2_sel] = imx_clk_fixup_mux("usdhc2_sel", base + 0x1c, 17, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels), imx_cscmr1_fixup); - clk[usdhc3_sel] = imx_clk_fixup_mux("usdhc3_sel", base + 0x1c, 18, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels), imx_cscmr1_fixup); - clk[usdhc4_sel] = imx_clk_fixup_mux("usdhc4_sel", base + 0x1c, 19, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels), imx_cscmr1_fixup); - clk[enfc_sel] = imx_clk_mux("enfc_sel", base + 0x2c, 16, 2, enfc_sels, ARRAY_SIZE(enfc_sels)); - clk[emi_sel] = imx_clk_fixup_mux("emi_sel", base + 0x1c, 27, 2, emi_sels, ARRAY_SIZE(emi_sels), imx_cscmr1_fixup); - clk[emi_slow_sel] = imx_clk_fixup_mux("emi_slow_sel", base + 0x1c, 29, 2, emi_slow_sels, ARRAY_SIZE(emi_slow_sels), imx_cscmr1_fixup); - clk[vdo_axi_sel] = imx_clk_mux("vdo_axi_sel", base + 0x18, 11, 1, vdo_axi_sels, ARRAY_SIZE(vdo_axi_sels)); - clk[vpu_axi_sel] = imx_clk_mux("vpu_axi_sel", base + 0x18, 14, 2, vpu_axi_sels, ARRAY_SIZE(vpu_axi_sels)); - clk[cko1_sel] = imx_clk_mux("cko1_sel", base + 0x60, 0, 4, cko1_sels, ARRAY_SIZE(cko1_sels)); - clk[cko2_sel] = imx_clk_mux("cko2_sel", base + 0x60, 16, 5, cko2_sels, ARRAY_SIZE(cko2_sels)); - clk[cko] = imx_clk_mux("cko", base + 0x60, 8, 1, cko_sels, ARRAY_SIZE(cko_sels)); - - /* name reg shift width busy: reg, shift parent_names num_parents */ - clk[periph] = imx_clk_busy_mux("periph", base + 0x14, 25, 1, base + 0x48, 5, periph_sels, ARRAY_SIZE(periph_sels)); - clk[periph2] = imx_clk_busy_mux("periph2", base + 0x14, 26, 1, base + 0x48, 3, periph2_sels, ARRAY_SIZE(periph2_sels)); - - /* name parent_name reg shift width */ - clk[periph_clk2] = imx_clk_divider("periph_clk2", "periph_clk2_sel", base + 0x14, 27, 3); - clk[periph2_clk2] = imx_clk_divider("periph2_clk2", "periph2_clk2_sel", base + 0x14, 0, 3); - clk[ipg] = imx_clk_divider("ipg", "ahb", base + 0x14, 8, 2); - clk[ipg_per] = imx_clk_fixup_divider("ipg_per", "ipg", base + 0x1c, 0, 6, imx_cscmr1_fixup); - clk[esai_pred] = imx_clk_divider("esai_pred", "esai_sel", base + 0x28, 9, 3); - clk[esai_podf] = imx_clk_divider("esai_podf", "esai_pred", base + 0x28, 25, 3); - clk[asrc_pred] = imx_clk_divider("asrc_pred", "asrc_sel", base + 0x30, 12, 3); - clk[asrc_podf] = imx_clk_divider("asrc_podf", "asrc_pred", base + 0x30, 9, 3); - clk[spdif_pred] = imx_clk_divider("spdif_pred", "spdif_sel", base + 0x30, 25, 3); - clk[spdif_podf] = imx_clk_divider("spdif_podf", "spdif_pred", base + 0x30, 22, 3); - clk[can_root] = imx_clk_divider("can_root", "pll3_60m", base + 0x20, 2, 6); - clk[ecspi_root] = imx_clk_divider("ecspi_root", "pll3_60m", base + 0x38, 19, 6); - clk[gpu2d_core_podf] = imx_clk_divider("gpu2d_core_podf", "gpu2d_core_sel", base + 0x18, 23, 3); - clk[gpu3d_core_podf] = imx_clk_divider("gpu3d_core_podf", "gpu3d_core_sel", base + 0x18, 26, 3); - clk[gpu3d_shader] = imx_clk_divider("gpu3d_shader", "gpu3d_shader_sel", base + 0x18, 29, 3); - clk[ipu1_podf] = imx_clk_divider("ipu1_podf", "ipu1_sel", base + 0x3c, 11, 3); - clk[ipu2_podf] = imx_clk_divider("ipu2_podf", "ipu2_sel", base + 0x3c, 16, 3); - clk[ldb_di0_div_3_5] = imx_clk_fixed_factor("ldb_di0_div_3_5", "ldb_di0_sel", 2, 7); - clk[ldb_di0_podf] = imx_clk_divider_flags("ldb_di0_podf", "ldb_di0_div_3_5", base + 0x20, 10, 1, 0); - clk[ldb_di1_div_3_5] = imx_clk_fixed_factor("ldb_di1_div_3_5", "ldb_di1_sel", 2, 7); - clk[ldb_di1_podf] = imx_clk_divider_flags("ldb_di1_podf", "ldb_di1_div_3_5", base + 0x20, 11, 1, 0); - clk[ipu1_di0_pre] = imx_clk_divider("ipu1_di0_pre", "ipu1_di0_pre_sel", base + 0x34, 3, 3); - clk[ipu1_di1_pre] = imx_clk_divider("ipu1_di1_pre", "ipu1_di1_pre_sel", base + 0x34, 12, 3); - clk[ipu2_di0_pre] = imx_clk_divider("ipu2_di0_pre", "ipu2_di0_pre_sel", base + 0x38, 3, 3); - clk[ipu2_di1_pre] = imx_clk_divider("ipu2_di1_pre", "ipu2_di1_pre_sel", base + 0x38, 12, 3); - clk[hsi_tx_podf] = imx_clk_divider("hsi_tx_podf", "hsi_tx_sel", base + 0x30, 29, 3); - clk[ssi1_pred] = imx_clk_divider("ssi1_pred", "ssi1_sel", base + 0x28, 6, 3); - clk[ssi1_podf] = imx_clk_divider("ssi1_podf", "ssi1_pred", base + 0x28, 0, 6); - clk[ssi2_pred] = imx_clk_divider("ssi2_pred", "ssi2_sel", base + 0x2c, 6, 3); - clk[ssi2_podf] = imx_clk_divider("ssi2_podf", "ssi2_pred", base + 0x2c, 0, 6); - clk[ssi3_pred] = imx_clk_divider("ssi3_pred", "ssi3_sel", base + 0x28, 22, 3); - clk[ssi3_podf] = imx_clk_divider("ssi3_podf", "ssi3_pred", base + 0x28, 16, 6); - clk[uart_serial_podf] = imx_clk_divider("uart_serial_podf", "pll3_80m", base + 0x24, 0, 6); - clk[usdhc1_podf] = imx_clk_divider("usdhc1_podf", "usdhc1_sel", base + 0x24, 11, 3); - clk[usdhc2_podf] = imx_clk_divider("usdhc2_podf", "usdhc2_sel", base + 0x24, 16, 3); - clk[usdhc3_podf] = imx_clk_divider("usdhc3_podf", "usdhc3_sel", base + 0x24, 19, 3); - clk[usdhc4_podf] = imx_clk_divider("usdhc4_podf", "usdhc4_sel", base + 0x24, 22, 3); - clk[enfc_pred] = imx_clk_divider("enfc_pred", "enfc_sel", base + 0x2c, 18, 3); - clk[enfc_podf] = imx_clk_divider("enfc_podf", "enfc_pred", base + 0x2c, 21, 6); - clk[emi_podf] = imx_clk_fixup_divider("emi_podf", "emi_sel", base + 0x1c, 20, 3, imx_cscmr1_fixup); - clk[emi_slow_podf] = imx_clk_fixup_divider("emi_slow_podf", "emi_slow_sel", base + 0x1c, 23, 3, imx_cscmr1_fixup); - clk[vpu_axi_podf] = imx_clk_divider("vpu_axi_podf", "vpu_axi_sel", base + 0x24, 25, 3); - clk[cko1_podf] = imx_clk_divider("cko1_podf", "cko1_sel", base + 0x60, 4, 3); - clk[cko2_podf] = imx_clk_divider("cko2_podf", "cko2_sel", base + 0x60, 21, 3); - - /* name parent_name reg shift width busy: reg, shift */ - clk[axi] = imx_clk_busy_divider("axi", "axi_sel", base + 0x14, 16, 3, base + 0x48, 0); - clk[mmdc_ch0_axi_podf] = imx_clk_busy_divider("mmdc_ch0_axi_podf", "periph", base + 0x14, 19, 3, base + 0x48, 4); - clk[mmdc_ch1_axi_podf] = imx_clk_busy_divider("mmdc_ch1_axi_podf", "periph2", base + 0x14, 3, 3, base + 0x48, 2); - clk[arm] = imx_clk_busy_divider("arm", "pll1_sw", base + 0x10, 0, 3, base + 0x48, 16); - clk[ahb] = imx_clk_busy_divider("ahb", "periph", base + 0x14, 10, 3, base + 0x48, 1); - - /* name parent_name reg shift */ - clk[apbh_dma] = imx_clk_gate2("apbh_dma", "usdhc3", base + 0x68, 4); - clk[asrc] = imx_clk_gate2("asrc", "asrc_podf", base + 0x68, 6); - clk[can1_ipg] = imx_clk_gate2("can1_ipg", "ipg", base + 0x68, 14); - clk[can1_serial] = imx_clk_gate2("can1_serial", "can_root", base + 0x68, 16); - clk[can2_ipg] = imx_clk_gate2("can2_ipg", "ipg", base + 0x68, 18); - clk[can2_serial] = imx_clk_gate2("can2_serial", "can_root", base + 0x68, 20); - clk[ecspi1] = imx_clk_gate2("ecspi1", "ecspi_root", base + 0x6c, 0); - clk[ecspi2] = imx_clk_gate2("ecspi2", "ecspi_root", base + 0x6c, 2); - clk[ecspi3] = imx_clk_gate2("ecspi3", "ecspi_root", base + 0x6c, 4); - clk[ecspi4] = imx_clk_gate2("ecspi4", "ecspi_root", base + 0x6c, 6); + /* name reg shift width parent_names num_parents */ + clk[IMX6QDL_CLK_STEP] = imx_clk_mux("step", base + 0xc, 8, 1, step_sels, ARRAY_SIZE(step_sels)); + clk[IMX6QDL_CLK_PLL1_SW] = imx_clk_mux("pll1_sw", base + 0xc, 2, 1, pll1_sw_sels, ARRAY_SIZE(pll1_sw_sels)); + clk[IMX6QDL_CLK_PERIPH_PRE] = imx_clk_mux("periph_pre", base + 0x18, 18, 2, periph_pre_sels, ARRAY_SIZE(periph_pre_sels)); + clk[IMX6QDL_CLK_PERIPH2_PRE] = imx_clk_mux("periph2_pre", base + 0x18, 21, 2, periph_pre_sels, ARRAY_SIZE(periph_pre_sels)); + clk[IMX6QDL_CLK_PERIPH_CLK2_SEL] = imx_clk_mux("periph_clk2_sel", base + 0x18, 12, 2, periph_clk2_sels, ARRAY_SIZE(periph_clk2_sels)); + clk[IMX6QDL_CLK_PERIPH2_CLK2_SEL] = imx_clk_mux("periph2_clk2_sel", base + 0x18, 20, 1, periph2_clk2_sels, ARRAY_SIZE(periph2_clk2_sels)); + clk[IMX6QDL_CLK_AXI_SEL] = imx_clk_mux("axi_sel", base + 0x14, 6, 2, axi_sels, ARRAY_SIZE(axi_sels)); + clk[IMX6QDL_CLK_ESAI_SEL] = imx_clk_mux("esai_sel", base + 0x20, 19, 2, audio_sels, ARRAY_SIZE(audio_sels)); + clk[IMX6QDL_CLK_ASRC_SEL] = imx_clk_mux("asrc_sel", base + 0x30, 7, 2, audio_sels, ARRAY_SIZE(audio_sels)); + clk[IMX6QDL_CLK_SPDIF_SEL] = imx_clk_mux("spdif_sel", base + 0x30, 20, 2, audio_sels, ARRAY_SIZE(audio_sels)); + clk[IMX6QDL_CLK_GPU2D_AXI] = imx_clk_mux("gpu2d_axi", base + 0x18, 0, 1, gpu_axi_sels, ARRAY_SIZE(gpu_axi_sels)); + clk[IMX6QDL_CLK_GPU3D_AXI] = imx_clk_mux("gpu3d_axi", base + 0x18, 1, 1, gpu_axi_sels, ARRAY_SIZE(gpu_axi_sels)); + clk[IMX6QDL_CLK_GPU2D_CORE_SEL] = imx_clk_mux("gpu2d_core_sel", base + 0x18, 16, 2, gpu2d_core_sels, ARRAY_SIZE(gpu2d_core_sels)); + clk[IMX6QDL_CLK_GPU3D_CORE_SEL] = imx_clk_mux("gpu3d_core_sel", base + 0x18, 4, 2, gpu3d_core_sels, ARRAY_SIZE(gpu3d_core_sels)); + clk[IMX6QDL_CLK_GPU3D_SHADER_SEL] = imx_clk_mux("gpu3d_shader_sel", base + 0x18, 8, 2, gpu3d_shader_sels, ARRAY_SIZE(gpu3d_shader_sels)); + clk[IMX6QDL_CLK_IPU1_SEL] = imx_clk_mux("ipu1_sel", base + 0x3c, 9, 2, ipu_sels, ARRAY_SIZE(ipu_sels)); + clk[IMX6QDL_CLK_IPU2_SEL] = imx_clk_mux("ipu2_sel", base + 0x3c, 14, 2, ipu_sels, ARRAY_SIZE(ipu_sels)); + clk[IMX6QDL_CLK_LDB_DI0_SEL] = imx_clk_mux_flags("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT); + clk[IMX6QDL_CLK_LDB_DI1_SEL] = imx_clk_mux_flags("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT); + clk[IMX6QDL_CLK_IPU1_DI0_PRE_SEL] = imx_clk_mux_flags("ipu1_di0_pre_sel", base + 0x34, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); + clk[IMX6QDL_CLK_IPU1_DI1_PRE_SEL] = imx_clk_mux_flags("ipu1_di1_pre_sel", base + 0x34, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); + clk[IMX6QDL_CLK_IPU2_DI0_PRE_SEL] = imx_clk_mux_flags("ipu2_di0_pre_sel", base + 0x38, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); + clk[IMX6QDL_CLK_IPU2_DI1_PRE_SEL] = imx_clk_mux_flags("ipu2_di1_pre_sel", base + 0x38, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); + clk[IMX6QDL_CLK_IPU1_DI0_SEL] = imx_clk_mux_flags("ipu1_di0_sel", base + 0x34, 0, 3, ipu1_di0_sels, ARRAY_SIZE(ipu1_di0_sels), CLK_SET_RATE_PARENT); + clk[IMX6QDL_CLK_IPU1_DI1_SEL] = imx_clk_mux_flags("ipu1_di1_sel", base + 0x34, 9, 3, ipu1_di1_sels, ARRAY_SIZE(ipu1_di1_sels), CLK_SET_RATE_PARENT); + clk[IMX6QDL_CLK_IPU2_DI0_SEL] = imx_clk_mux_flags("ipu2_di0_sel", base + 0x38, 0, 3, ipu2_di0_sels, ARRAY_SIZE(ipu2_di0_sels), CLK_SET_RATE_PARENT); + clk[IMX6QDL_CLK_IPU2_DI1_SEL] = imx_clk_mux_flags("ipu2_di1_sel", base + 0x38, 9, 3, ipu2_di1_sels, ARRAY_SIZE(ipu2_di1_sels), CLK_SET_RATE_PARENT); + clk[IMX6QDL_CLK_HSI_TX_SEL] = imx_clk_mux("hsi_tx_sel", base + 0x30, 28, 1, hsi_tx_sels, ARRAY_SIZE(hsi_tx_sels)); + clk[IMX6QDL_CLK_PCIE_AXI_SEL] = imx_clk_mux("pcie_axi_sel", base + 0x18, 10, 1, pcie_axi_sels, ARRAY_SIZE(pcie_axi_sels)); + clk[IMX6QDL_CLK_SSI1_SEL] = imx_clk_fixup_mux("ssi1_sel", base + 0x1c, 10, 2, ssi_sels, ARRAY_SIZE(ssi_sels), imx_cscmr1_fixup); + clk[IMX6QDL_CLK_SSI2_SEL] = imx_clk_fixup_mux("ssi2_sel", base + 0x1c, 12, 2, ssi_sels, ARRAY_SIZE(ssi_sels), imx_cscmr1_fixup); + clk[IMX6QDL_CLK_SSI3_SEL] = imx_clk_fixup_mux("ssi3_sel", base + 0x1c, 14, 2, ssi_sels, ARRAY_SIZE(ssi_sels), imx_cscmr1_fixup); + clk[IMX6QDL_CLK_USDHC1_SEL] = imx_clk_fixup_mux("usdhc1_sel", base + 0x1c, 16, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels), imx_cscmr1_fixup); + clk[IMX6QDL_CLK_USDHC2_SEL] = imx_clk_fixup_mux("usdhc2_sel", base + 0x1c, 17, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels), imx_cscmr1_fixup); + clk[IMX6QDL_CLK_USDHC3_SEL] = imx_clk_fixup_mux("usdhc3_sel", base + 0x1c, 18, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels), imx_cscmr1_fixup); + clk[IMX6QDL_CLK_USDHC4_SEL] = imx_clk_fixup_mux("usdhc4_sel", base + 0x1c, 19, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels), imx_cscmr1_fixup); + clk[IMX6QDL_CLK_ENFC_SEL] = imx_clk_mux("enfc_sel", base + 0x2c, 16, 2, enfc_sels, ARRAY_SIZE(enfc_sels)); + clk[IMX6QDL_CLK_EMI_SEL] = imx_clk_fixup_mux("emi_sel", base + 0x1c, 27, 2, emi_sels, ARRAY_SIZE(emi_sels), imx_cscmr1_fixup); + clk[IMX6QDL_CLK_EMI_SLOW_SEL] = imx_clk_fixup_mux("emi_slow_sel", base + 0x1c, 29, 2, emi_slow_sels, ARRAY_SIZE(emi_slow_sels), imx_cscmr1_fixup); + clk[IMX6QDL_CLK_VDO_AXI_SEL] = imx_clk_mux("vdo_axi_sel", base + 0x18, 11, 1, vdo_axi_sels, ARRAY_SIZE(vdo_axi_sels)); + clk[IMX6QDL_CLK_VPU_AXI_SEL] = imx_clk_mux("vpu_axi_sel", base + 0x18, 14, 2, vpu_axi_sels, ARRAY_SIZE(vpu_axi_sels)); + clk[IMX6QDL_CLK_CKO1_SEL] = imx_clk_mux("cko1_sel", base + 0x60, 0, 4, cko1_sels, ARRAY_SIZE(cko1_sels)); + clk[IMX6QDL_CLK_CKO2_SEL] = imx_clk_mux("cko2_sel", base + 0x60, 16, 5, cko2_sels, ARRAY_SIZE(cko2_sels)); + clk[IMX6QDL_CLK_CKO] = imx_clk_mux("cko", base + 0x60, 8, 1, cko_sels, ARRAY_SIZE(cko_sels)); + + /* name reg shift width busy: reg, shift parent_names num_parents */ + clk[IMX6QDL_CLK_PERIPH] = imx_clk_busy_mux("periph", base + 0x14, 25, 1, base + 0x48, 5, periph_sels, ARRAY_SIZE(periph_sels)); + clk[IMX6QDL_CLK_PERIPH2] = imx_clk_busy_mux("periph2", base + 0x14, 26, 1, base + 0x48, 3, periph2_sels, ARRAY_SIZE(periph2_sels)); + + /* name parent_name reg shift width */ + clk[IMX6QDL_CLK_PERIPH_CLK2] = imx_clk_divider("periph_clk2", "periph_clk2_sel", base + 0x14, 27, 3); + clk[IMX6QDL_CLK_PERIPH2_CLK2] = imx_clk_divider("periph2_clk2", "periph2_clk2_sel", base + 0x14, 0, 3); + clk[IMX6QDL_CLK_IPG] = imx_clk_divider("ipg", "ahb", base + 0x14, 8, 2); + clk[IMX6QDL_CLK_IPG_PER] = imx_clk_fixup_divider("ipg_per", "ipg", base + 0x1c, 0, 6, imx_cscmr1_fixup); + clk[IMX6QDL_CLK_ESAI_PRED] = imx_clk_divider("esai_pred", "esai_sel", base + 0x28, 9, 3); + clk[IMX6QDL_CLK_ESAI_PODF] = imx_clk_divider("esai_podf", "esai_pred", base + 0x28, 25, 3); + clk[IMX6QDL_CLK_ASRC_PRED] = imx_clk_divider("asrc_pred", "asrc_sel", base + 0x30, 12, 3); + clk[IMX6QDL_CLK_ASRC_PODF] = imx_clk_divider("asrc_podf", "asrc_pred", base + 0x30, 9, 3); + clk[IMX6QDL_CLK_SPDIF_PRED] = imx_clk_divider("spdif_pred", "spdif_sel", base + 0x30, 25, 3); + clk[IMX6QDL_CLK_SPDIF_PODF] = imx_clk_divider("spdif_podf", "spdif_pred", base + 0x30, 22, 3); + clk[IMX6QDL_CLK_CAN_ROOT] = imx_clk_divider("can_root", "pll3_60m", base + 0x20, 2, 6); + clk[IMX6QDL_CLK_ECSPI_ROOT] = imx_clk_divider("ecspi_root", "pll3_60m", base + 0x38, 19, 6); + clk[IMX6QDL_CLK_GPU2D_CORE_PODF] = imx_clk_divider("gpu2d_core_podf", "gpu2d_core_sel", base + 0x18, 23, 3); + clk[IMX6QDL_CLK_GPU3D_CORE_PODF] = imx_clk_divider("gpu3d_core_podf", "gpu3d_core_sel", base + 0x18, 26, 3); + clk[IMX6QDL_CLK_GPU3D_SHADER] = imx_clk_divider("gpu3d_shader", "gpu3d_shader_sel", base + 0x18, 29, 3); + clk[IMX6QDL_CLK_IPU1_PODF] = imx_clk_divider("ipu1_podf", "ipu1_sel", base + 0x3c, 11, 3); + clk[IMX6QDL_CLK_IPU2_PODF] = imx_clk_divider("ipu2_podf", "ipu2_sel", base + 0x3c, 16, 3); + clk[IMX6QDL_CLK_LDB_DI0_DIV_3_5] = imx_clk_fixed_factor("ldb_di0_div_3_5", "ldb_di0_sel", 2, 7); + clk[IMX6QDL_CLK_LDB_DI0_PODF] = imx_clk_divider_flags("ldb_di0_podf", "ldb_di0_div_3_5", base + 0x20, 10, 1, 0); + clk[IMX6QDL_CLK_LDB_DI1_DIV_3_5] = imx_clk_fixed_factor("ldb_di1_div_3_5", "ldb_di1_sel", 2, 7); + clk[IMX6QDL_CLK_LDB_DI1_PODF] = imx_clk_divider_flags("ldb_di1_podf", "ldb_di1_div_3_5", base + 0x20, 11, 1, 0); + clk[IMX6QDL_CLK_IPU1_DI0_PRE] = imx_clk_divider("ipu1_di0_pre", "ipu1_di0_pre_sel", base + 0x34, 3, 3); + clk[IMX6QDL_CLK_IPU1_DI1_PRE] = imx_clk_divider("ipu1_di1_pre", "ipu1_di1_pre_sel", base + 0x34, 12, 3); + clk[IMX6QDL_CLK_IPU2_DI0_PRE] = imx_clk_divider("ipu2_di0_pre", "ipu2_di0_pre_sel", base + 0x38, 3, 3); + clk[IMX6QDL_CLK_IPU2_DI1_PRE] = imx_clk_divider("ipu2_di1_pre", "ipu2_di1_pre_sel", base + 0x38, 12, 3); + clk[IMX6QDL_CLK_HSI_TX_PODF] = imx_clk_divider("hsi_tx_podf", "hsi_tx_sel", base + 0x30, 29, 3); + clk[IMX6QDL_CLK_SSI1_PRED] = imx_clk_divider("ssi1_pred", "ssi1_sel", base + 0x28, 6, 3); + clk[IMX6QDL_CLK_SSI1_PODF] = imx_clk_divider("ssi1_podf", "ssi1_pred", base + 0x28, 0, 6); + clk[IMX6QDL_CLK_SSI2_PRED] = imx_clk_divider("ssi2_pred", "ssi2_sel", base + 0x2c, 6, 3); + clk[IMX6QDL_CLK_SSI2_PODF] = imx_clk_divider("ssi2_podf", "ssi2_pred", base + 0x2c, 0, 6); + clk[IMX6QDL_CLK_SSI3_PRED] = imx_clk_divider("ssi3_pred", "ssi3_sel", base + 0x28, 22, 3); + clk[IMX6QDL_CLK_SSI3_PODF] = imx_clk_divider("ssi3_podf", "ssi3_pred", base + 0x28, 16, 6); + clk[IMX6QDL_CLK_UART_SERIAL_PODF] = imx_clk_divider("uart_serial_podf", "pll3_80m", base + 0x24, 0, 6); + clk[IMX6QDL_CLK_USDHC1_PODF] = imx_clk_divider("usdhc1_podf", "usdhc1_sel", base + 0x24, 11, 3); + clk[IMX6QDL_CLK_USDHC2_PODF] = imx_clk_divider("usdhc2_podf", "usdhc2_sel", base + 0x24, 16, 3); + clk[IMX6QDL_CLK_USDHC3_PODF] = imx_clk_divider("usdhc3_podf", "usdhc3_sel", base + 0x24, 19, 3); + clk[IMX6QDL_CLK_USDHC4_PODF] = imx_clk_divider("usdhc4_podf", "usdhc4_sel", base + 0x24, 22, 3); + clk[IMX6QDL_CLK_ENFC_PRED] = imx_clk_divider("enfc_pred", "enfc_sel", base + 0x2c, 18, 3); + clk[IMX6QDL_CLK_ENFC_PODF] = imx_clk_divider("enfc_podf", "enfc_pred", base + 0x2c, 21, 6); + clk[IMX6QDL_CLK_EMI_PODF] = imx_clk_fixup_divider("emi_podf", "emi_sel", base + 0x1c, 20, 3, imx_cscmr1_fixup); + clk[IMX6QDL_CLK_EMI_SLOW_PODF] = imx_clk_fixup_divider("emi_slow_podf", "emi_slow_sel", base + 0x1c, 23, 3, imx_cscmr1_fixup); + clk[IMX6QDL_CLK_VPU_AXI_PODF] = imx_clk_divider("vpu_axi_podf", "vpu_axi_sel", base + 0x24, 25, 3); + clk[IMX6QDL_CLK_CKO1_PODF] = imx_clk_divider("cko1_podf", "cko1_sel", base + 0x60, 4, 3); + clk[IMX6QDL_CLK_CKO2_PODF] = imx_clk_divider("cko2_podf", "cko2_sel", base + 0x60, 21, 3); + + /* name parent_name reg shift width busy: reg, shift */ + clk[IMX6QDL_CLK_AXI] = imx_clk_busy_divider("axi", "axi_sel", base + 0x14, 16, 3, base + 0x48, 0); + clk[IMX6QDL_CLK_MMDC_CH0_AXI_PODF] = imx_clk_busy_divider("mmdc_ch0_axi_podf", "periph", base + 0x14, 19, 3, base + 0x48, 4); + clk[IMX6QDL_CLK_MMDC_CH1_AXI_PODF] = imx_clk_busy_divider("mmdc_ch1_axi_podf", "periph2", base + 0x14, 3, 3, base + 0x48, 2); + clk[IMX6QDL_CLK_ARM] = imx_clk_busy_divider("arm", "pll1_sw", base + 0x10, 0, 3, base + 0x48, 16); + clk[IMX6QDL_CLK_AHB] = imx_clk_busy_divider("ahb", "periph", base + 0x14, 10, 3, base + 0x48, 1); + + /* name parent_name reg shift */ + clk[IMX6QDL_CLK_APBH_DMA] = imx_clk_gate2("apbh_dma", "usdhc3", base + 0x68, 4); + clk[IMX6QDL_CLK_ASRC] = imx_clk_gate2("asrc", "asrc_podf", base + 0x68, 6); + clk[IMX6QDL_CLK_CAN1_IPG] = imx_clk_gate2("can1_ipg", "ipg", base + 0x68, 14); + clk[IMX6QDL_CLK_CAN1_SERIAL] = imx_clk_gate2("can1_serial", "can_root", base + 0x68, 16); + clk[IMX6QDL_CLK_CAN2_IPG] = imx_clk_gate2("can2_ipg", "ipg", base + 0x68, 18); + clk[IMX6QDL_CLK_CAN2_SERIAL] = imx_clk_gate2("can2_serial", "can_root", base + 0x68, 20); + clk[IMX6QDL_CLK_ECSPI1] = imx_clk_gate2("ecspi1", "ecspi_root", base + 0x6c, 0); + clk[IMX6QDL_CLK_ECSPI2] = imx_clk_gate2("ecspi2", "ecspi_root", base + 0x6c, 2); + clk[IMX6QDL_CLK_ECSPI3] = imx_clk_gate2("ecspi3", "ecspi_root", base + 0x6c, 4); + clk[IMX6QDL_CLK_ECSPI4] = imx_clk_gate2("ecspi4", "ecspi_root", base + 0x6c, 6); if (cpu_is_imx6dl()) - /* ecspi5 is replaced with i2c4 on imx6dl & imx6s */ - clk[ecspi5] = imx_clk_gate2("i2c4", "ipg_per", base + 0x6c, 8); + clk[IMX6DL_CLK_I2C4] = imx_clk_gate2("i2c4", "ipg_per", base + 0x6c, 8); else - clk[ecspi5] = imx_clk_gate2("ecspi5", "ecspi_root", base + 0x6c, 8); - clk[enet] = imx_clk_gate2("enet", "ipg", base + 0x6c, 10); - clk[esai] = imx_clk_gate2_shared("esai", "esai_podf", base + 0x6c, 16, &share_count_esai); - clk[esai_ahb] = imx_clk_gate2_shared("esai_ahb", "ahb", base + 0x6c, 16, &share_count_esai); - clk[gpt_ipg] = imx_clk_gate2("gpt_ipg", "ipg", base + 0x6c, 20); - clk[gpt_ipg_per] = imx_clk_gate2("gpt_ipg_per", "ipg_per", base + 0x6c, 22); + clk[IMX6Q_CLK_ECSPI5] = imx_clk_gate2("ecspi5", "ecspi_root", base + 0x6c, 8); + clk[IMX6QDL_CLK_ENET] = imx_clk_gate2("enet", "ipg", base + 0x6c, 10); + clk[IMX6QDL_CLK_ESAI] = imx_clk_gate2_shared("esai", "esai_podf", base + 0x6c, 16, &share_count_esai); + clk[IMX6QDL_CLK_ESAI_AHB] = imx_clk_gate2_shared("esai_ahb", "ahb", base + 0x6c, 16, &share_count_esai); + clk[IMX6QDL_CLK_GPT_IPG] = imx_clk_gate2("gpt_ipg", "ipg", base + 0x6c, 20); + clk[IMX6QDL_CLK_GPT_IPG_PER] = imx_clk_gate2("gpt_ipg_per", "ipg_per", base + 0x6c, 22); if (cpu_is_imx6dl()) /* * The multiplexer and divider of imx6q clock gpu3d_shader get * redefined/reused as gpu2d_core_sel and gpu2d_core_podf on imx6dl. */ - clk[gpu2d_core] = imx_clk_gate2("gpu2d_core", "gpu3d_shader", base + 0x6c, 24); + clk[IMX6QDL_CLK_GPU2D_CORE] = imx_clk_gate2("gpu2d_core", "gpu3d_shader", base + 0x6c, 24); else - clk[gpu2d_core] = imx_clk_gate2("gpu2d_core", "gpu2d_core_podf", base + 0x6c, 24); - clk[gpu3d_core] = imx_clk_gate2("gpu3d_core", "gpu3d_core_podf", base + 0x6c, 26); - clk[hdmi_iahb] = imx_clk_gate2("hdmi_iahb", "ahb", base + 0x70, 0); - clk[hdmi_isfr] = imx_clk_gate2("hdmi_isfr", "pll3_pfd1_540m", base + 0x70, 4); - clk[i2c1] = imx_clk_gate2("i2c1", "ipg_per", base + 0x70, 6); - clk[i2c2] = imx_clk_gate2("i2c2", "ipg_per", base + 0x70, 8); - clk[i2c3] = imx_clk_gate2("i2c3", "ipg_per", base + 0x70, 10); - clk[iim] = imx_clk_gate2("iim", "ipg", base + 0x70, 12); - clk[enfc] = imx_clk_gate2("enfc", "enfc_podf", base + 0x70, 14); - clk[vdoa] = imx_clk_gate2("vdoa", "vdo_axi", base + 0x70, 26); - clk[ipu1] = imx_clk_gate2("ipu1", "ipu1_podf", base + 0x74, 0); - clk[ipu1_di0] = imx_clk_gate2("ipu1_di0", "ipu1_di0_sel", base + 0x74, 2); - clk[ipu1_di1] = imx_clk_gate2("ipu1_di1", "ipu1_di1_sel", base + 0x74, 4); - clk[ipu2] = imx_clk_gate2("ipu2", "ipu2_podf", base + 0x74, 6); - clk[ipu2_di0] = imx_clk_gate2("ipu2_di0", "ipu2_di0_sel", base + 0x74, 8); - clk[ldb_di0] = imx_clk_gate2("ldb_di0", "ldb_di0_podf", base + 0x74, 12); - clk[ldb_di1] = imx_clk_gate2("ldb_di1", "ldb_di1_podf", base + 0x74, 14); - clk[ipu2_di1] = imx_clk_gate2("ipu2_di1", "ipu2_di1_sel", base + 0x74, 10); - clk[hsi_tx] = imx_clk_gate2("hsi_tx", "hsi_tx_podf", base + 0x74, 16); + clk[IMX6QDL_CLK_GPU2D_CORE] = imx_clk_gate2("gpu2d_core", "gpu2d_core_podf", base + 0x6c, 24); + clk[IMX6QDL_CLK_GPU3D_CORE] = imx_clk_gate2("gpu3d_core", "gpu3d_core_podf", base + 0x6c, 26); + clk[IMX6QDL_CLK_HDMI_IAHB] = imx_clk_gate2("hdmi_iahb", "ahb", base + 0x70, 0); + clk[IMX6QDL_CLK_HDMI_ISFR] = imx_clk_gate2("hdmi_isfr", "pll3_pfd1_540m", base + 0x70, 4); + clk[IMX6QDL_CLK_I2C1] = imx_clk_gate2("i2c1", "ipg_per", base + 0x70, 6); + clk[IMX6QDL_CLK_I2C2] = imx_clk_gate2("i2c2", "ipg_per", base + 0x70, 8); + clk[IMX6QDL_CLK_I2C3] = imx_clk_gate2("i2c3", "ipg_per", base + 0x70, 10); + clk[IMX6QDL_CLK_IIM] = imx_clk_gate2("iim", "ipg", base + 0x70, 12); + clk[IMX6QDL_CLK_ENFC] = imx_clk_gate2("enfc", "enfc_podf", base + 0x70, 14); + clk[IMX6QDL_CLK_VDOA] = imx_clk_gate2("vdoa", "vdo_axi", base + 0x70, 26); + clk[IMX6QDL_CLK_IPU1] = imx_clk_gate2("ipu1", "ipu1_podf", base + 0x74, 0); + clk[IMX6QDL_CLK_IPU1_DI0] = imx_clk_gate2("ipu1_di0", "ipu1_di0_sel", base + 0x74, 2); + clk[IMX6QDL_CLK_IPU1_DI1] = imx_clk_gate2("ipu1_di1", "ipu1_di1_sel", base + 0x74, 4); + clk[IMX6QDL_CLK_IPU2] = imx_clk_gate2("ipu2", "ipu2_podf", base + 0x74, 6); + clk[IMX6QDL_CLK_IPU2_DI0] = imx_clk_gate2("ipu2_di0", "ipu2_di0_sel", base + 0x74, 8); + clk[IMX6QDL_CLK_LDB_DI0] = imx_clk_gate2("ldb_di0", "ldb_di0_podf", base + 0x74, 12); + clk[IMX6QDL_CLK_LDB_DI1] = imx_clk_gate2("ldb_di1", "ldb_di1_podf", base + 0x74, 14); + clk[IMX6QDL_CLK_IPU2_DI1] = imx_clk_gate2("ipu2_di1", "ipu2_di1_sel", base + 0x74, 10); + clk[IMX6QDL_CLK_HSI_TX] = imx_clk_gate2("hsi_tx", "hsi_tx_podf", base + 0x74, 16); if (cpu_is_imx6dl()) /* * The multiplexer and divider of the imx6q clock gpu2d get * redefined/reused as mlb_sys_sel and mlb_sys_clk_podf on imx6dl. */ - clk[mlb] = imx_clk_gate2("mlb", "gpu2d_core_podf", base + 0x74, 18); + clk[IMX6QDL_CLK_MLB] = imx_clk_gate2("mlb", "gpu2d_core_podf", base + 0x74, 18); else - clk[mlb] = imx_clk_gate2("mlb", "axi", base + 0x74, 18); - clk[mmdc_ch0_axi] = imx_clk_gate2("mmdc_ch0_axi", "mmdc_ch0_axi_podf", base + 0x74, 20); - clk[mmdc_ch1_axi] = imx_clk_gate2("mmdc_ch1_axi", "mmdc_ch1_axi_podf", base + 0x74, 22); - clk[ocram] = imx_clk_gate2("ocram", "ahb", base + 0x74, 28); - clk[openvg_axi] = imx_clk_gate2("openvg_axi", "axi", base + 0x74, 30); - clk[pcie_axi] = imx_clk_gate2("pcie_axi", "pcie_axi_sel", base + 0x78, 0); - clk[per1_bch] = imx_clk_gate2("per1_bch", "usdhc3", base + 0x78, 12); - clk[pwm1] = imx_clk_gate2("pwm1", "ipg_per", base + 0x78, 16); - clk[pwm2] = imx_clk_gate2("pwm2", "ipg_per", base + 0x78, 18); - clk[pwm3] = imx_clk_gate2("pwm3", "ipg_per", base + 0x78, 20); - clk[pwm4] = imx_clk_gate2("pwm4", "ipg_per", base + 0x78, 22); - clk[gpmi_bch_apb] = imx_clk_gate2("gpmi_bch_apb", "usdhc3", base + 0x78, 24); - clk[gpmi_bch] = imx_clk_gate2("gpmi_bch", "usdhc4", base + 0x78, 26); - clk[gpmi_io] = imx_clk_gate2("gpmi_io", "enfc", base + 0x78, 28); - clk[gpmi_apb] = imx_clk_gate2("gpmi_apb", "usdhc3", base + 0x78, 30); - clk[rom] = imx_clk_gate2("rom", "ahb", base + 0x7c, 0); - clk[sata] = imx_clk_gate2("sata", "ipg", base + 0x7c, 4); - clk[sdma] = imx_clk_gate2("sdma", "ahb", base + 0x7c, 6); - clk[spba] = imx_clk_gate2("spba", "ipg", base + 0x7c, 12); - clk[spdif] = imx_clk_gate2("spdif", "spdif_podf", base + 0x7c, 14); - clk[ssi1_ipg] = imx_clk_gate2("ssi1_ipg", "ipg", base + 0x7c, 18); - clk[ssi2_ipg] = imx_clk_gate2("ssi2_ipg", "ipg", base + 0x7c, 20); - clk[ssi3_ipg] = imx_clk_gate2("ssi3_ipg", "ipg", base + 0x7c, 22); - clk[uart_ipg] = imx_clk_gate2("uart_ipg", "ipg", base + 0x7c, 24); - clk[uart_serial] = imx_clk_gate2("uart_serial", "uart_serial_podf", base + 0x7c, 26); - clk[usboh3] = imx_clk_gate2("usboh3", "ipg", base + 0x80, 0); - clk[usdhc1] = imx_clk_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2); - clk[usdhc2] = imx_clk_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4); - clk[usdhc3] = imx_clk_gate2("usdhc3", "usdhc3_podf", base + 0x80, 6); - clk[usdhc4] = imx_clk_gate2("usdhc4", "usdhc4_podf", base + 0x80, 8); - clk[eim_slow] = imx_clk_gate2("eim_slow", "emi_slow_podf", base + 0x80, 10); - clk[vdo_axi] = imx_clk_gate2("vdo_axi", "vdo_axi_sel", base + 0x80, 12); - clk[vpu_axi] = imx_clk_gate2("vpu_axi", "vpu_axi_podf", base + 0x80, 14); - clk[cko1] = imx_clk_gate("cko1", "cko1_podf", base + 0x60, 7); - clk[cko2] = imx_clk_gate("cko2", "cko2_podf", base + 0x60, 24); + clk[IMX6QDL_CLK_MLB] = imx_clk_gate2("mlb", "axi", base + 0x74, 18); + clk[IMX6QDL_CLK_MMDC_CH0_AXI] = imx_clk_gate2("mmdc_ch0_axi", "mmdc_ch0_axi_podf", base + 0x74, 20); + clk[IMX6QDL_CLK_MMDC_CH1_AXI] = imx_clk_gate2("mmdc_ch1_axi", "mmdc_ch1_axi_podf", base + 0x74, 22); + clk[IMX6QDL_CLK_OCRAM] = imx_clk_gate2("ocram", "ahb", base + 0x74, 28); + clk[IMX6QDL_CLK_OPENVG_AXI] = imx_clk_gate2("openvg_axi", "axi", base + 0x74, 30); + clk[IMX6QDL_CLK_PCIE_AXI] = imx_clk_gate2("pcie_axi", "pcie_axi_sel", base + 0x78, 0); + clk[IMX6QDL_CLK_PER1_BCH] = imx_clk_gate2("per1_bch", "usdhc3", base + 0x78, 12); + clk[IMX6QDL_CLK_PWM1] = imx_clk_gate2("pwm1", "ipg_per", base + 0x78, 16); + clk[IMX6QDL_CLK_PWM2] = imx_clk_gate2("pwm2", "ipg_per", base + 0x78, 18); + clk[IMX6QDL_CLK_PWM3] = imx_clk_gate2("pwm3", "ipg_per", base + 0x78, 20); + clk[IMX6QDL_CLK_PWM4] = imx_clk_gate2("pwm4", "ipg_per", base + 0x78, 22); + clk[IMX6QDL_CLK_GPMI_BCH_APB] = imx_clk_gate2("gpmi_bch_apb", "usdhc3", base + 0x78, 24); + clk[IMX6QDL_CLK_GPMI_BCH] = imx_clk_gate2("gpmi_bch", "usdhc4", base + 0x78, 26); + clk[IMX6QDL_CLK_GPMI_IO] = imx_clk_gate2("gpmi_io", "enfc", base + 0x78, 28); + clk[IMX6QDL_CLK_GPMI_APB] = imx_clk_gate2("gpmi_apb", "usdhc3", base + 0x78, 30); + clk[IMX6QDL_CLK_ROM] = imx_clk_gate2("rom", "ahb", base + 0x7c, 0); + clk[IMX6QDL_CLK_SATA] = imx_clk_gate2("sata", "ipg", base + 0x7c, 4); + clk[IMX6QDL_CLK_SDMA] = imx_clk_gate2("sdma", "ahb", base + 0x7c, 6); + clk[IMX6QDL_CLK_SPBA] = imx_clk_gate2("spba", "ipg", base + 0x7c, 12); + clk[IMX6QDL_CLK_SPDIF] = imx_clk_gate2("spdif", "spdif_podf", base + 0x7c, 14); + clk[IMX6QDL_CLK_SSI1_IPG] = imx_clk_gate2("ssi1_ipg", "ipg", base + 0x7c, 18); + clk[IMX6QDL_CLK_SSI2_IPG] = imx_clk_gate2("ssi2_ipg", "ipg", base + 0x7c, 20); + clk[IMX6QDL_CLK_SSI3_IPG] = imx_clk_gate2("ssi3_ipg", "ipg", base + 0x7c, 22); + clk[IMX6QDL_CLK_UART_IPG] = imx_clk_gate2("uart_ipg", "ipg", base + 0x7c, 24); + clk[IMX6QDL_CLK_UART_SERIAL] = imx_clk_gate2("uart_serial", "uart_serial_podf", base + 0x7c, 26); + clk[IMX6QDL_CLK_USBOH3] = imx_clk_gate2("usboh3", "ipg", base + 0x80, 0); + clk[IMX6QDL_CLK_USDHC1] = imx_clk_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2); + clk[IMX6QDL_CLK_USDHC2] = imx_clk_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4); + clk[IMX6QDL_CLK_USDHC3] = imx_clk_gate2("usdhc3", "usdhc3_podf", base + 0x80, 6); + clk[IMX6QDL_CLK_USDHC4] = imx_clk_gate2("usdhc4", "usdhc4_podf", base + 0x80, 8); + clk[IMX6QDL_CLK_EIM_SLOW] = imx_clk_gate2("eim_slow", "emi_slow_podf", base + 0x80, 10); + clk[IMX6QDL_CLK_VDO_AXI] = imx_clk_gate2("vdo_axi", "vdo_axi_sel", base + 0x80, 12); + clk[IMX6QDL_CLK_VPU_AXI] = imx_clk_gate2("vpu_axi", "vpu_axi_podf", base + 0x80, 14); + clk[IMX6QDL_CLK_CKO1] = imx_clk_gate("cko1", "cko1_podf", base + 0x60, 7); + clk[IMX6QDL_CLK_CKO2] = imx_clk_gate("cko2", "cko2_podf", base + 0x60, 24); imx_check_clocks(clk, ARRAY_SIZE(clk)); @@ -439,54 +404,54 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk_data.clk_num = ARRAY_SIZE(clk); of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); - clk_register_clkdev(clk[enet_ref], "enet_ref", NULL); + clk_register_clkdev(clk[IMX6QDL_CLK_ENET_REF], "enet_ref", NULL); if ((imx_get_soc_revision() != IMX_CHIP_REVISION_1_0) || cpu_is_imx6dl()) { - clk_set_parent(clk[ldb_di0_sel], clk[pll5_video_div]); - clk_set_parent(clk[ldb_di1_sel], clk[pll5_video_div]); + clk_set_parent(clk[IMX6QDL_CLK_LDB_DI0_SEL], clk[IMX6QDL_CLK_PLL5_VIDEO_DIV]); + clk_set_parent(clk[IMX6QDL_CLK_LDB_DI1_SEL], clk[IMX6QDL_CLK_PLL5_VIDEO_DIV]); } - clk_set_parent(clk[ipu1_di0_pre_sel], clk[pll5_video_div]); - clk_set_parent(clk[ipu1_di1_pre_sel], clk[pll5_video_div]); - clk_set_parent(clk[ipu2_di0_pre_sel], clk[pll5_video_div]); - clk_set_parent(clk[ipu2_di1_pre_sel], clk[pll5_video_div]); - clk_set_parent(clk[ipu1_di0_sel], clk[ipu1_di0_pre]); - clk_set_parent(clk[ipu1_di1_sel], clk[ipu1_di1_pre]); - clk_set_parent(clk[ipu2_di0_sel], clk[ipu2_di0_pre]); - clk_set_parent(clk[ipu2_di1_sel], clk[ipu2_di1_pre]); + clk_set_parent(clk[IMX6QDL_CLK_IPU1_DI0_PRE_SEL], clk[IMX6QDL_CLK_PLL5_VIDEO_DIV]); + clk_set_parent(clk[IMX6QDL_CLK_IPU1_DI1_PRE_SEL], clk[IMX6QDL_CLK_PLL5_VIDEO_DIV]); + clk_set_parent(clk[IMX6QDL_CLK_IPU2_DI0_PRE_SEL], clk[IMX6QDL_CLK_PLL5_VIDEO_DIV]); + clk_set_parent(clk[IMX6QDL_CLK_IPU2_DI1_PRE_SEL], clk[IMX6QDL_CLK_PLL5_VIDEO_DIV]); + clk_set_parent(clk[IMX6QDL_CLK_IPU1_DI0_SEL], clk[IMX6QDL_CLK_IPU1_DI0_PRE]); + clk_set_parent(clk[IMX6QDL_CLK_IPU1_DI1_SEL], clk[IMX6QDL_CLK_IPU1_DI1_PRE]); + clk_set_parent(clk[IMX6QDL_CLK_IPU2_DI0_SEL], clk[IMX6QDL_CLK_IPU2_DI0_PRE]); + clk_set_parent(clk[IMX6QDL_CLK_IPU2_DI1_SEL], clk[IMX6QDL_CLK_IPU2_DI1_PRE]); /* * The gpmi needs 100MHz frequency in the EDO/Sync mode, * We can not get the 100MHz from the pll2_pfd0_352m. * So choose pll2_pfd2_396m as enfc_sel's parent. */ - clk_set_parent(clk[enfc_sel], clk[pll2_pfd2_396m]); + clk_set_parent(clk[IMX6QDL_CLK_ENFC_SEL], clk[IMX6QDL_CLK_PLL2_PFD2_396M]); for (i = 0; i < ARRAY_SIZE(clks_init_on); i++) clk_prepare_enable(clk[clks_init_on[i]]); if (IS_ENABLED(CONFIG_USB_MXS_PHY)) { - clk_prepare_enable(clk[usbphy1_gate]); - clk_prepare_enable(clk[usbphy2_gate]); + clk_prepare_enable(clk[IMX6QDL_CLK_USBPHY1_GATE]); + clk_prepare_enable(clk[IMX6QDL_CLK_USBPHY2_GATE]); } /* * Let's initially set up CLKO with OSC24M, since this configuration * is widely used by imx6q board designs to clock audio codec. */ - ret = clk_set_parent(clk[cko2_sel], clk[osc]); + ret = clk_set_parent(clk[IMX6QDL_CLK_CKO2_SEL], clk[IMX6QDL_CLK_OSC]); if (!ret) - ret = clk_set_parent(clk[cko], clk[cko2]); + ret = clk_set_parent(clk[IMX6QDL_CLK_CKO], clk[IMX6QDL_CLK_CKO2]); if (ret) pr_warn("failed to set up CLKO: %d\n", ret); /* Audio-related clocks configuration */ - clk_set_parent(clk[spdif_sel], clk[pll3_pfd3_454m]); + clk_set_parent(clk[IMX6QDL_CLK_SPDIF_SEL], clk[IMX6QDL_CLK_PLL3_PFD3_454M]); /* All existing boards with PCIe use LVDS1 */ if (IS_ENABLED(CONFIG_PCI_IMX6)) - clk_set_parent(clk[lvds1_sel], clk[sata_ref_100m]); + clk_set_parent(clk[IMX6QDL_CLK_LVDS1_SEL], clk[IMX6QDL_CLK_SATA_REF_100M]); /* Set initial power mode */ imx6q_set_lpm(WAIT_CLOCKED); diff --git a/include/dt-bindings/clock/imx6qdl-clock.h b/include/dt-bindings/clock/imx6qdl-clock.h new file mode 100644 index 000000000000..654151e24288 --- /dev/null +++ b/include/dt-bindings/clock/imx6qdl-clock.h @@ -0,0 +1,224 @@ +/* + * Copyright 2014 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __DT_BINDINGS_CLOCK_IMX6QDL_H +#define __DT_BINDINGS_CLOCK_IMX6QDL_H + +#define IMX6QDL_CLK_DUMMY 0 +#define IMX6QDL_CLK_CKIL 1 +#define IMX6QDL_CLK_CKIH 2 +#define IMX6QDL_CLK_OSC 3 +#define IMX6QDL_CLK_PLL2_PFD0_352M 4 +#define IMX6QDL_CLK_PLL2_PFD1_594M 5 +#define IMX6QDL_CLK_PLL2_PFD2_396M 6 +#define IMX6QDL_CLK_PLL3_PFD0_720M 7 +#define IMX6QDL_CLK_PLL3_PFD1_540M 8 +#define IMX6QDL_CLK_PLL3_PFD2_508M 9 +#define IMX6QDL_CLK_PLL3_PFD3_454M 10 +#define IMX6QDL_CLK_PLL2_198M 11 +#define IMX6QDL_CLK_PLL3_120M 12 +#define IMX6QDL_CLK_PLL3_80M 13 +#define IMX6QDL_CLK_PLL3_60M 14 +#define IMX6QDL_CLK_TWD 15 +#define IMX6QDL_CLK_STEP 16 +#define IMX6QDL_CLK_PLL1_SW 17 +#define IMX6QDL_CLK_PERIPH_PRE 18 +#define IMX6QDL_CLK_PERIPH2_PRE 19 +#define IMX6QDL_CLK_PERIPH_CLK2_SEL 20 +#define IMX6QDL_CLK_PERIPH2_CLK2_SEL 21 +#define IMX6QDL_CLK_AXI_SEL 22 +#define IMX6QDL_CLK_ESAI_SEL 23 +#define IMX6QDL_CLK_ASRC_SEL 24 +#define IMX6QDL_CLK_SPDIF_SEL 25 +#define IMX6QDL_CLK_GPU2D_AXI 26 +#define IMX6QDL_CLK_GPU3D_AXI 27 +#define IMX6QDL_CLK_GPU2D_CORE_SEL 28 +#define IMX6QDL_CLK_GPU3D_CORE_SEL 29 +#define IMX6QDL_CLK_GPU3D_SHADER_SEL 30 +#define IMX6QDL_CLK_IPU1_SEL 31 +#define IMX6QDL_CLK_IPU2_SEL 32 +#define IMX6QDL_CLK_LDB_DI0_SEL 33 +#define IMX6QDL_CLK_LDB_DI1_SEL 34 +#define IMX6QDL_CLK_IPU1_DI0_PRE_SEL 35 +#define IMX6QDL_CLK_IPU1_DI1_PRE_SEL 36 +#define IMX6QDL_CLK_IPU2_DI0_PRE_SEL 37 +#define IMX6QDL_CLK_IPU2_DI1_PRE_SEL 38 +#define IMX6QDL_CLK_IPU1_DI0_SEL 39 +#define IMX6QDL_CLK_IPU1_DI1_SEL 40 +#define IMX6QDL_CLK_IPU2_DI0_SEL 41 +#define IMX6QDL_CLK_IPU2_DI1_SEL 42 +#define IMX6QDL_CLK_HSI_TX_SEL 43 +#define IMX6QDL_CLK_PCIE_AXI_SEL 44 +#define IMX6QDL_CLK_SSI1_SEL 45 +#define IMX6QDL_CLK_SSI2_SEL 46 +#define IMX6QDL_CLK_SSI3_SEL 47 +#define IMX6QDL_CLK_USDHC1_SEL 48 +#define IMX6QDL_CLK_USDHC2_SEL 49 +#define IMX6QDL_CLK_USDHC3_SEL 50 +#define IMX6QDL_CLK_USDHC4_SEL 51 +#define IMX6QDL_CLK_ENFC_SEL 52 +#define IMX6QDL_CLK_EMI_SEL 53 +#define IMX6QDL_CLK_EMI_SLOW_SEL 54 +#define IMX6QDL_CLK_VDO_AXI_SEL 55 +#define IMX6QDL_CLK_VPU_AXI_SEL 56 +#define IMX6QDL_CLK_CKO1_SEL 57 +#define IMX6QDL_CLK_PERIPH 58 +#define IMX6QDL_CLK_PERIPH2 59 +#define IMX6QDL_CLK_PERIPH_CLK2 60 +#define IMX6QDL_CLK_PERIPH2_CLK2 61 +#define IMX6QDL_CLK_IPG 62 +#define IMX6QDL_CLK_IPG_PER 63 +#define IMX6QDL_CLK_ESAI_PRED 64 +#define IMX6QDL_CLK_ESAI_PODF 65 +#define IMX6QDL_CLK_ASRC_PRED 66 +#define IMX6QDL_CLK_ASRC_PODF 67 +#define IMX6QDL_CLK_SPDIF_PRED 68 +#define IMX6QDL_CLK_SPDIF_PODF 69 +#define IMX6QDL_CLK_CAN_ROOT 70 +#define IMX6QDL_CLK_ECSPI_ROOT 71 +#define IMX6QDL_CLK_GPU2D_CORE_PODF 72 +#define IMX6QDL_CLK_GPU3D_CORE_PODF 73 +#define IMX6QDL_CLK_GPU3D_SHADER 74 +#define IMX6QDL_CLK_IPU1_PODF 75 +#define IMX6QDL_CLK_IPU2_PODF 76 +#define IMX6QDL_CLK_LDB_DI0_PODF 77 +#define IMX6QDL_CLK_LDB_DI1_PODF 78 +#define IMX6QDL_CLK_IPU1_DI0_PRE 79 +#define IMX6QDL_CLK_IPU1_DI1_PRE 80 +#define IMX6QDL_CLK_IPU2_DI0_PRE 81 +#define IMX6QDL_CLK_IPU2_DI1_PRE 82 +#define IMX6QDL_CLK_HSI_TX_PODF 83 +#define IMX6QDL_CLK_SSI1_PRED 84 +#define IMX6QDL_CLK_SSI1_PODF 85 +#define IMX6QDL_CLK_SSI2_PRED 86 +#define IMX6QDL_CLK_SSI2_PODF 87 +#define IMX6QDL_CLK_SSI3_PRED 88 +#define IMX6QDL_CLK_SSI3_PODF 89 +#define IMX6QDL_CLK_UART_SERIAL_PODF 90 +#define IMX6QDL_CLK_USDHC1_PODF 91 +#define IMX6QDL_CLK_USDHC2_PODF 92 +#define IMX6QDL_CLK_USDHC3_PODF 93 +#define IMX6QDL_CLK_USDHC4_PODF 94 +#define IMX6QDL_CLK_ENFC_PRED 95 +#define IMX6QDL_CLK_ENFC_PODF 96 +#define IMX6QDL_CLK_EMI_PODF 97 +#define IMX6QDL_CLK_EMI_SLOW_PODF 98 +#define IMX6QDL_CLK_VPU_AXI_PODF 99 +#define IMX6QDL_CLK_CKO1_PODF 100 +#define IMX6QDL_CLK_AXI 101 +#define IMX6QDL_CLK_MMDC_CH0_AXI_PODF 102 +#define IMX6QDL_CLK_MMDC_CH1_AXI_PODF 103 +#define IMX6QDL_CLK_ARM 104 +#define IMX6QDL_CLK_AHB 105 +#define IMX6QDL_CLK_APBH_DMA 106 +#define IMX6QDL_CLK_ASRC 107 +#define IMX6QDL_CLK_CAN1_IPG 108 +#define IMX6QDL_CLK_CAN1_SERIAL 109 +#define IMX6QDL_CLK_CAN2_IPG 110 +#define IMX6QDL_CLK_CAN2_SERIAL 111 +#define IMX6QDL_CLK_ECSPI1 112 +#define IMX6QDL_CLK_ECSPI2 113 +#define IMX6QDL_CLK_ECSPI3 114 +#define IMX6QDL_CLK_ECSPI4 115 +#define IMX6Q_CLK_ECSPI5 116 +#define IMX6DL_CLK_I2C4 116 +#define IMX6QDL_CLK_ENET 117 +#define IMX6QDL_CLK_ESAI 118 +#define IMX6QDL_CLK_GPT_IPG 119 +#define IMX6QDL_CLK_GPT_IPG_PER 120 +#define IMX6QDL_CLK_GPU2D_CORE 121 +#define IMX6QDL_CLK_GPU3D_CORE 122 +#define IMX6QDL_CLK_HDMI_IAHB 123 +#define IMX6QDL_CLK_HDMI_ISFR 124 +#define IMX6QDL_CLK_I2C1 125 +#define IMX6QDL_CLK_I2C2 126 +#define IMX6QDL_CLK_I2C3 127 +#define IMX6QDL_CLK_IIM 128 +#define IMX6QDL_CLK_ENFC 129 +#define IMX6QDL_CLK_IPU1 130 +#define IMX6QDL_CLK_IPU1_DI0 131 +#define IMX6QDL_CLK_IPU1_DI1 132 +#define IMX6QDL_CLK_IPU2 133 +#define IMX6QDL_CLK_IPU2_DI0 134 +#define IMX6QDL_CLK_LDB_DI0 135 +#define IMX6QDL_CLK_LDB_DI1 136 +#define IMX6QDL_CLK_IPU2_DI1 137 +#define IMX6QDL_CLK_HSI_TX 138 +#define IMX6QDL_CLK_MLB 139 +#define IMX6QDL_CLK_MMDC_CH0_AXI 140 +#define IMX6QDL_CLK_MMDC_CH1_AXI 141 +#define IMX6QDL_CLK_OCRAM 142 +#define IMX6QDL_CLK_OPENVG_AXI 143 +#define IMX6QDL_CLK_PCIE_AXI 144 +#define IMX6QDL_CLK_PWM1 145 +#define IMX6QDL_CLK_PWM2 146 +#define IMX6QDL_CLK_PWM3 147 +#define IMX6QDL_CLK_PWM4 148 +#define IMX6QDL_CLK_PER1_BCH 149 +#define IMX6QDL_CLK_GPMI_BCH_APB 150 +#define IMX6QDL_CLK_GPMI_BCH 151 +#define IMX6QDL_CLK_GPMI_IO 152 +#define IMX6QDL_CLK_GPMI_APB 153 +#define IMX6QDL_CLK_SATA 154 +#define IMX6QDL_CLK_SDMA 155 +#define IMX6QDL_CLK_SPBA 156 +#define IMX6QDL_CLK_SSI1 157 +#define IMX6QDL_CLK_SSI2 158 +#define IMX6QDL_CLK_SSI3 159 +#define IMX6QDL_CLK_UART_IPG 160 +#define IMX6QDL_CLK_UART_SERIAL 161 +#define IMX6QDL_CLK_USBOH3 162 +#define IMX6QDL_CLK_USDHC1 163 +#define IMX6QDL_CLK_USDHC2 164 +#define IMX6QDL_CLK_USDHC3 165 +#define IMX6QDL_CLK_USDHC4 166 +#define IMX6QDL_CLK_VDO_AXI 167 +#define IMX6QDL_CLK_VPU_AXI 168 +#define IMX6QDL_CLK_CKO1 169 +#define IMX6QDL_CLK_PLL1_SYS 170 +#define IMX6QDL_CLK_PLL2_BUS 171 +#define IMX6QDL_CLK_PLL3_USB_OTG 172 +#define IMX6QDL_CLK_PLL4_AUDIO 173 +#define IMX6QDL_CLK_PLL5_VIDEO 174 +#define IMX6QDL_CLK_PLL8_MLB 175 +#define IMX6QDL_CLK_PLL7_USB_HOST 176 +#define IMX6QDL_CLK_PLL6_ENET 177 +#define IMX6QDL_CLK_SSI1_IPG 178 +#define IMX6QDL_CLK_SSI2_IPG 179 +#define IMX6QDL_CLK_SSI3_IPG 180 +#define IMX6QDL_CLK_ROM 181 +#define IMX6QDL_CLK_USBPHY1 182 +#define IMX6QDL_CLK_USBPHY2 183 +#define IMX6QDL_CLK_LDB_DI0_DIV_3_5 184 +#define IMX6QDL_CLK_LDB_DI1_DIV_3_5 185 +#define IMX6QDL_CLK_SATA_REF 186 +#define IMX6QDL_CLK_SATA_REF_100M 187 +#define IMX6QDL_CLK_PCIE_REF 188 +#define IMX6QDL_CLK_PCIE_REF_125M 189 +#define IMX6QDL_CLK_ENET_REF 190 +#define IMX6QDL_CLK_USBPHY1_GATE 191 +#define IMX6QDL_CLK_USBPHY2_GATE 192 +#define IMX6QDL_CLK_PLL4_POST_DIV 193 +#define IMX6QDL_CLK_PLL5_POST_DIV 194 +#define IMX6QDL_CLK_PLL5_VIDEO_DIV 195 +#define IMX6QDL_CLK_EIM_SLOW 196 +#define IMX6QDL_CLK_SPDIF 197 +#define IMX6QDL_CLK_CKO2_SEL 198 +#define IMX6QDL_CLK_CKO2_PODF 199 +#define IMX6QDL_CLK_CKO2 200 +#define IMX6QDL_CLK_CKO 201 +#define IMX6QDL_CLK_VDOA 202 +#define IMX6QDL_CLK_PLL4_AUDIO_DIV 203 +#define IMX6QDL_CLK_LVDS1_SEL 204 +#define IMX6QDL_CLK_LVDS2_SEL 205 +#define IMX6QDL_CLK_LVDS1_GATE 206 +#define IMX6QDL_CLK_LVDS2_GATE 207 +#define IMX6QDL_CLK_ESAI_AHB 208 +#define IMX6QDL_CLK_END 209 + +#endif /* __DT_BINDINGS_CLOCK_IMX6QDL_H */ -- cgit v1.2.3 From 35bcaf00de20911dd58ed2ea9848aa7e1d969394 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sun, 22 Jun 2014 17:17:09 +0400 Subject: ARM: i.MX21 clk: Add devicetree support This patch adds devicetree support CCM module for i.MX21 CPUs. Signed-off-by: Alexander Shiyan Signed-off-by: Shawn Guo --- .../devicetree/bindings/clock/imx21-clock.txt | 28 +++ arch/arm/mach-imx/clk-imx21.c | 259 +++++++++++---------- include/dt-bindings/clock/imx21-clock.h | 80 +++++++ 3 files changed, 244 insertions(+), 123 deletions(-) create mode 100644 Documentation/devicetree/bindings/clock/imx21-clock.txt create mode 100644 include/dt-bindings/clock/imx21-clock.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/imx21-clock.txt b/Documentation/devicetree/bindings/clock/imx21-clock.txt new file mode 100644 index 000000000000..c3b0db437c48 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/imx21-clock.txt @@ -0,0 +1,28 @@ +* Clock bindings for Freescale i.MX21 + +Required properties: +- compatible : Should be "fsl,imx21-ccm". +- reg : Address and length of the register set. +- interrupts : Should contain CCM interrupt. +- #clock-cells: Should be <1>. + +The clock consumer should specify the desired clock by having the clock +ID in its "clocks" phandle cell. See include/dt-bindings/clock/imx21-clock.h +for the full list of i.MX21 clock IDs. + +Examples: + clks: ccm@10027000{ + compatible = "fsl,imx21-ccm"; + reg = <0x10027000 0x800>; + #clock-cells = <1>; + }; + + uart1: serial@1000a000 { + compatible = "fsl,imx21-uart"; + reg = <0x1000a000 0x1000>; + interrupts = <20>; + clocks = <&clks IMX21_CLK_UART1_IPG_GATE>, + <&clks IMX21_CLK_PER1>; + clock-names = "ipg", "per"; + status = "disabled"; + }; diff --git a/arch/arm/mach-imx/clk-imx21.c b/arch/arm/mach-imx/clk-imx21.c index 990713597de7..6b36738b170d 100644 --- a/arch/arm/mach-imx/clk-imx21.c +++ b/arch/arm/mach-imx/clk-imx21.c @@ -12,149 +12,162 @@ #include #include #include +#include +#include +#include #include "clk.h" #include "common.h" #include "hardware.h" -#define IO_ADDR_CCM(off) (MX21_IO_ADDRESS(MX21_CCM_BASE_ADDR + (off))) +static void __iomem *ccm __initdata; /* Register offsets */ -#define CCM_CSCR IO_ADDR_CCM(0x0) -#define CCM_MPCTL0 IO_ADDR_CCM(0x4) -#define CCM_SPCTL0 IO_ADDR_CCM(0xc) -#define CCM_PCDR0 IO_ADDR_CCM(0x18) -#define CCM_PCDR1 IO_ADDR_CCM(0x1c) -#define CCM_PCCR0 IO_ADDR_CCM(0x20) -#define CCM_PCCR1 IO_ADDR_CCM(0x24) +#define CCM_CSCR (ccm + 0x00) +#define CCM_MPCTL0 (ccm + 0x04) +#define CCM_SPCTL0 (ccm + 0x0c) +#define CCM_PCDR0 (ccm + 0x18) +#define CCM_PCDR1 (ccm + 0x1c) +#define CCM_PCCR0 (ccm + 0x20) +#define CCM_PCCR1 (ccm + 0x24) static const char *mpll_osc_sel_clks[] = { "ckih_gate", "ckih_div1p5", }; static const char *mpll_sel_clks[] = { "fpm_gate", "mpll_osc_sel", }; static const char *spll_sel_clks[] = { "fpm_gate", "mpll_osc_sel", }; static const char *ssi_sel_clks[] = { "spll_gate", "mpll_gate", }; -enum imx21_clks { - dummy, ckil, ckih, fpm, ckih_div1p5, mpll_gate, spll_gate, fpm_gate, - ckih_gate, mpll_osc_sel, ipg, hclk, mpll_sel, spll_sel, ssi1_sel, - ssi2_sel, usb_div, fclk, mpll, spll, nfc_div, ssi1_div, ssi2_div, per1, - per2, per3, per4, uart1_ipg_gate, uart2_ipg_gate, uart3_ipg_gate, - uart4_ipg_gate, cspi1_ipg_gate, cspi2_ipg_gate, ssi1_gate, ssi2_gate, - sdhc1_ipg_gate, sdhc2_ipg_gate, gpio_gate, i2c_gate, dma_gate, usb_gate, - emma_gate, ssi2_baud_gate, ssi1_baud_gate, lcdc_ipg_gate, nfc_gate, - lcdc_hclk_gate, per4_gate, bmi_gate, usb_hclk_gate, slcdc_gate, - slcdc_hclk_gate, emma_hclk_gate, brom_gate, dma_hclk_gate, - csi_hclk_gate, cspi3_ipg_gate, wdog_gate, gpt1_ipg_gate, gpt2_ipg_gate, - gpt3_ipg_gate, pwm_ipg_gate, rtc_gate, kpp_gate, owire_gate, clk_max -}; - -static struct clk *clk[clk_max]; +static struct clk *clk[IMX21_CLK_MAX]; +static struct clk_onecell_data clk_data; -int __init mx21_clocks_init(unsigned long lref, unsigned long href) +static void __init _mx21_clocks_init(unsigned long lref, unsigned long href) { - clk[dummy] = imx_clk_fixed("dummy", 0); - clk[ckil] = imx_clk_fixed("ckil", lref); - clk[ckih] = imx_clk_fixed("ckih", href); - clk[fpm] = imx_clk_fixed_factor("fpm", "ckil", 512, 1); - clk[ckih_div1p5] = imx_clk_fixed_factor("ckih_div1p5", "ckih_gate", 2, 3); - - clk[mpll_gate] = imx_clk_gate("mpll_gate", "mpll", CCM_CSCR, 0); - clk[spll_gate] = imx_clk_gate("spll_gate", "spll", CCM_CSCR, 1); - clk[fpm_gate] = imx_clk_gate("fpm_gate", "fpm", CCM_CSCR, 2); - clk[ckih_gate] = imx_clk_gate_dis("ckih_gate", "ckih", CCM_CSCR, 3); - clk[mpll_osc_sel] = imx_clk_mux("mpll_osc_sel", CCM_CSCR, 4, 1, mpll_osc_sel_clks, ARRAY_SIZE(mpll_osc_sel_clks)); - clk[ipg] = imx_clk_divider("ipg", "hclk", CCM_CSCR, 9, 1); - clk[hclk] = imx_clk_divider("hclk", "fclk", CCM_CSCR, 10, 4); - clk[mpll_sel] = imx_clk_mux("mpll_sel", CCM_CSCR, 16, 1, mpll_sel_clks, ARRAY_SIZE(mpll_sel_clks)); - clk[spll_sel] = imx_clk_mux("spll_sel", CCM_CSCR, 17, 1, spll_sel_clks, ARRAY_SIZE(spll_sel_clks)); - clk[ssi1_sel] = imx_clk_mux("ssi1_sel", CCM_CSCR, 19, 1, ssi_sel_clks, ARRAY_SIZE(ssi_sel_clks)); - clk[ssi2_sel] = imx_clk_mux("ssi2_sel", CCM_CSCR, 20, 1, ssi_sel_clks, ARRAY_SIZE(ssi_sel_clks)); - clk[usb_div] = imx_clk_divider("usb_div", "spll_gate", CCM_CSCR, 26, 3); - clk[fclk] = imx_clk_divider("fclk", "mpll_gate", CCM_CSCR, 29, 3); - - clk[mpll] = imx_clk_pllv1("mpll", "mpll_sel", CCM_MPCTL0); - - clk[spll] = imx_clk_pllv1("spll", "spll_sel", CCM_SPCTL0); - - clk[nfc_div] = imx_clk_divider("nfc_div", "fclk", CCM_PCDR0, 12, 4); - clk[ssi1_div] = imx_clk_divider("ssi1_div", "ssi1_sel", CCM_PCDR0, 16, 6); - clk[ssi2_div] = imx_clk_divider("ssi2_div", "ssi2_sel", CCM_PCDR0, 26, 6); - - clk[per1] = imx_clk_divider("per1", "mpll_gate", CCM_PCDR1, 0, 6); - clk[per2] = imx_clk_divider("per2", "mpll_gate", CCM_PCDR1, 8, 6); - clk[per3] = imx_clk_divider("per3", "mpll_gate", CCM_PCDR1, 16, 6); - clk[per4] = imx_clk_divider("per4", "mpll_gate", CCM_PCDR1, 24, 6); - - clk[uart1_ipg_gate] = imx_clk_gate("uart1_ipg_gate", "ipg", CCM_PCCR0, 0); - clk[uart2_ipg_gate] = imx_clk_gate("uart2_ipg_gate", "ipg", CCM_PCCR0, 1); - clk[uart3_ipg_gate] = imx_clk_gate("uart3_ipg_gate", "ipg", CCM_PCCR0, 2); - clk[uart4_ipg_gate] = imx_clk_gate("uart4_ipg_gate", "ipg", CCM_PCCR0, 3); - clk[cspi1_ipg_gate] = imx_clk_gate("cspi1_ipg_gate", "ipg", CCM_PCCR0, 4); - clk[cspi2_ipg_gate] = imx_clk_gate("cspi2_ipg_gate", "ipg", CCM_PCCR0, 5); - clk[ssi1_gate] = imx_clk_gate("ssi1_gate", "ipg", CCM_PCCR0, 6); - clk[ssi2_gate] = imx_clk_gate("ssi2_gate", "ipg", CCM_PCCR0, 7); - clk[sdhc1_ipg_gate] = imx_clk_gate("sdhc1_ipg_gate", "ipg", CCM_PCCR0, 9); - clk[sdhc2_ipg_gate] = imx_clk_gate("sdhc2_ipg_gate", "ipg", CCM_PCCR0, 10); - clk[gpio_gate] = imx_clk_gate("gpio_gate", "ipg", CCM_PCCR0, 11); - clk[i2c_gate] = imx_clk_gate("i2c_gate", "ipg", CCM_PCCR0, 12); - clk[dma_gate] = imx_clk_gate("dma_gate", "ipg", CCM_PCCR0, 13); - clk[usb_gate] = imx_clk_gate("usb_gate", "usb_div", CCM_PCCR0, 14); - clk[emma_gate] = imx_clk_gate("emma_gate", "ipg", CCM_PCCR0, 15); - clk[ssi2_baud_gate] = imx_clk_gate("ssi2_baud_gate", "ipg", CCM_PCCR0, 16); - clk[ssi1_baud_gate] = imx_clk_gate("ssi1_baud_gate", "ipg", CCM_PCCR0, 17); - clk[lcdc_ipg_gate] = imx_clk_gate("lcdc_ipg_gate", "ipg", CCM_PCCR0, 18); - clk[nfc_gate] = imx_clk_gate("nfc_gate", "nfc_div", CCM_PCCR0, 19); - clk[slcdc_hclk_gate] = imx_clk_gate("slcdc_hclk_gate", "hclk", CCM_PCCR0, 21); - clk[per4_gate] = imx_clk_gate("per4_gate", "per4", CCM_PCCR0, 22); - clk[bmi_gate] = imx_clk_gate("bmi_gate", "hclk", CCM_PCCR0, 23); - clk[usb_hclk_gate] = imx_clk_gate("usb_hclk_gate", "hclk", CCM_PCCR0, 24); - clk[slcdc_gate] = imx_clk_gate("slcdc_gate", "hclk", CCM_PCCR0, 25); - clk[lcdc_hclk_gate] = imx_clk_gate("lcdc_hclk_gate", "hclk", CCM_PCCR0, 26); - clk[emma_hclk_gate] = imx_clk_gate("emma_hclk_gate", "hclk", CCM_PCCR0, 27); - clk[brom_gate] = imx_clk_gate("brom_gate", "hclk", CCM_PCCR0, 28); - clk[dma_hclk_gate] = imx_clk_gate("dma_hclk_gate", "hclk", CCM_PCCR0, 30); - clk[csi_hclk_gate] = imx_clk_gate("csi_hclk_gate", "hclk", CCM_PCCR0, 31); - - clk[cspi3_ipg_gate] = imx_clk_gate("cspi3_ipg_gate", "ipg", CCM_PCCR1, 23); - clk[wdog_gate] = imx_clk_gate("wdog_gate", "ipg", CCM_PCCR1, 24); - clk[gpt1_ipg_gate] = imx_clk_gate("gpt1_ipg_gate", "ipg", CCM_PCCR1, 25); - clk[gpt2_ipg_gate] = imx_clk_gate("gpt2_ipg_gate", "ipg", CCM_PCCR1, 26); - clk[gpt3_ipg_gate] = imx_clk_gate("gpt3_ipg_gate", "ipg", CCM_PCCR1, 27); - clk[pwm_ipg_gate] = imx_clk_gate("pwm_ipg_gate", "ipg", CCM_PCCR1, 28); - clk[rtc_gate] = imx_clk_gate("rtc_gate", "ipg", CCM_PCCR1, 29); - clk[kpp_gate] = imx_clk_gate("kpp_gate", "ipg", CCM_PCCR1, 30); - clk[owire_gate] = imx_clk_gate("owire_gate", "ipg", CCM_PCCR1, 31); + BUG_ON(!ccm); + + clk[IMX21_CLK_DUMMY] = imx_clk_fixed("dummy", 0); + clk[IMX21_CLK_CKIL] = imx_obtain_fixed_clock("ckil", lref); + clk[IMX21_CLK_CKIH] = imx_obtain_fixed_clock("ckih", href); + clk[IMX21_CLK_FPM] = imx_clk_fixed_factor("fpm", "ckil", 512, 1); + clk[IMX21_CLK_CKIH_DIV1P5] = imx_clk_fixed_factor("ckih_div1p5", "ckih_gate", 2, 3); + + clk[IMX21_CLK_MPLL_GATE] = imx_clk_gate("mpll_gate", "mpll", CCM_CSCR, 0); + clk[IMX21_CLK_SPLL_GATE] = imx_clk_gate("spll_gate", "spll", CCM_CSCR, 1); + clk[IMX21_CLK_FPM_GATE] = imx_clk_gate("fpm_gate", "fpm", CCM_CSCR, 2); + clk[IMX21_CLK_CKIH_GATE] = imx_clk_gate_dis("ckih_gate", "ckih", CCM_CSCR, 3); + clk[IMX21_CLK_MPLL_OSC_SEL] = imx_clk_mux("mpll_osc_sel", CCM_CSCR, 4, 1, mpll_osc_sel_clks, ARRAY_SIZE(mpll_osc_sel_clks)); + clk[IMX21_CLK_IPG] = imx_clk_divider("ipg", "hclk", CCM_CSCR, 9, 1); + clk[IMX21_CLK_HCLK] = imx_clk_divider("hclk", "fclk", CCM_CSCR, 10, 4); + clk[IMX21_CLK_MPLL_SEL] = imx_clk_mux("mpll_sel", CCM_CSCR, 16, 1, mpll_sel_clks, ARRAY_SIZE(mpll_sel_clks)); + clk[IMX21_CLK_SPLL_SEL] = imx_clk_mux("spll_sel", CCM_CSCR, 17, 1, spll_sel_clks, ARRAY_SIZE(spll_sel_clks)); + clk[IMX21_CLK_SSI1_SEL] = imx_clk_mux("ssi1_sel", CCM_CSCR, 19, 1, ssi_sel_clks, ARRAY_SIZE(ssi_sel_clks)); + clk[IMX21_CLK_SSI2_SEL] = imx_clk_mux("ssi2_sel", CCM_CSCR, 20, 1, ssi_sel_clks, ARRAY_SIZE(ssi_sel_clks)); + clk[IMX21_CLK_USB_DIV] = imx_clk_divider("usb_div", "spll_gate", CCM_CSCR, 26, 3); + clk[IMX21_CLK_FCLK] = imx_clk_divider("fclk", "mpll_gate", CCM_CSCR, 29, 3); + + clk[IMX21_CLK_MPLL] = imx_clk_pllv1("mpll", "mpll_sel", CCM_MPCTL0); + + clk[IMX21_CLK_SPLL] = imx_clk_pllv1("spll", "spll_sel", CCM_SPCTL0); + + clk[IMX21_CLK_NFC_DIV] = imx_clk_divider("nfc_div", "fclk", CCM_PCDR0, 12, 4); + clk[IMX21_CLK_SSI1_DIV] = imx_clk_divider("ssi1_div", "ssi1_sel", CCM_PCDR0, 16, 6); + clk[IMX21_CLK_SSI2_DIV] = imx_clk_divider("ssi2_div", "ssi2_sel", CCM_PCDR0, 26, 6); + + clk[IMX21_CLK_PER1] = imx_clk_divider("per1", "mpll_gate", CCM_PCDR1, 0, 6); + clk[IMX21_CLK_PER2] = imx_clk_divider("per2", "mpll_gate", CCM_PCDR1, 8, 6); + clk[IMX21_CLK_PER3] = imx_clk_divider("per3", "mpll_gate", CCM_PCDR1, 16, 6); + clk[IMX21_CLK_PER4] = imx_clk_divider("per4", "mpll_gate", CCM_PCDR1, 24, 6); + + clk[IMX21_CLK_UART1_IPG_GATE] = imx_clk_gate("uart1_ipg_gate", "ipg", CCM_PCCR0, 0); + clk[IMX21_CLK_UART2_IPG_GATE] = imx_clk_gate("uart2_ipg_gate", "ipg", CCM_PCCR0, 1); + clk[IMX21_CLK_UART3_IPG_GATE] = imx_clk_gate("uart3_ipg_gate", "ipg", CCM_PCCR0, 2); + clk[IMX21_CLK_UART4_IPG_GATE] = imx_clk_gate("uart4_ipg_gate", "ipg", CCM_PCCR0, 3); + clk[IMX21_CLK_CSPI1_IPG_GATE] = imx_clk_gate("cspi1_ipg_gate", "ipg", CCM_PCCR0, 4); + clk[IMX21_CLK_CSPI2_IPG_GATE] = imx_clk_gate("cspi2_ipg_gate", "ipg", CCM_PCCR0, 5); + clk[IMX21_CLK_SSI1_GATE] = imx_clk_gate("ssi1_gate", "ipg", CCM_PCCR0, 6); + clk[IMX21_CLK_SSI2_GATE] = imx_clk_gate("ssi2_gate", "ipg", CCM_PCCR0, 7); + clk[IMX21_CLK_SDHC1_IPG_GATE] = imx_clk_gate("sdhc1_ipg_gate", "ipg", CCM_PCCR0, 9); + clk[IMX21_CLK_SDHC2_IPG_GATE] = imx_clk_gate("sdhc2_ipg_gate", "ipg", CCM_PCCR0, 10); + clk[IMX21_CLK_GPIO_GATE] = imx_clk_gate("gpio_gate", "ipg", CCM_PCCR0, 11); + clk[IMX21_CLK_I2C_GATE] = imx_clk_gate("i2c_gate", "ipg", CCM_PCCR0, 12); + clk[IMX21_CLK_DMA_GATE] = imx_clk_gate("dma_gate", "ipg", CCM_PCCR0, 13); + clk[IMX21_CLK_USB_GATE] = imx_clk_gate("usb_gate", "usb_div", CCM_PCCR0, 14); + clk[IMX21_CLK_EMMA_GATE] = imx_clk_gate("emma_gate", "ipg", CCM_PCCR0, 15); + clk[IMX21_CLK_SSI2_BAUD_GATE] = imx_clk_gate("ssi2_baud_gate", "ipg", CCM_PCCR0, 16); + clk[IMX21_CLK_SSI1_BAUD_GATE] = imx_clk_gate("ssi1_baud_gate", "ipg", CCM_PCCR0, 17); + clk[IMX21_CLK_LCDC_IPG_GATE] = imx_clk_gate("lcdc_ipg_gate", "ipg", CCM_PCCR0, 18); + clk[IMX21_CLK_NFC_GATE] = imx_clk_gate("nfc_gate", "nfc_div", CCM_PCCR0, 19); + clk[IMX21_CLK_SLCDC_HCLK_GATE] = imx_clk_gate("slcdc_hclk_gate", "hclk", CCM_PCCR0, 21); + clk[IMX21_CLK_PER4_GATE] = imx_clk_gate("per4_gate", "per4", CCM_PCCR0, 22); + clk[IMX21_CLK_BMI_GATE] = imx_clk_gate("bmi_gate", "hclk", CCM_PCCR0, 23); + clk[IMX21_CLK_USB_HCLK_GATE] = imx_clk_gate("usb_hclk_gate", "hclk", CCM_PCCR0, 24); + clk[IMX21_CLK_SLCDC_GATE] = imx_clk_gate("slcdc_gate", "hclk", CCM_PCCR0, 25); + clk[IMX21_CLK_LCDC_HCLK_GATE] = imx_clk_gate("lcdc_hclk_gate", "hclk", CCM_PCCR0, 26); + clk[IMX21_CLK_EMMA_HCLK_GATE] = imx_clk_gate("emma_hclk_gate", "hclk", CCM_PCCR0, 27); + clk[IMX21_CLK_BROM_GATE] = imx_clk_gate("brom_gate", "hclk", CCM_PCCR0, 28); + clk[IMX21_CLK_DMA_HCLK_GATE] = imx_clk_gate("dma_hclk_gate", "hclk", CCM_PCCR0, 30); + clk[IMX21_CLK_CSI_HCLK_GATE] = imx_clk_gate("csi_hclk_gate", "hclk", CCM_PCCR0, 31); + + clk[IMX21_CLK_CSPI3_IPG_GATE] = imx_clk_gate("cspi3_ipg_gate", "ipg", CCM_PCCR1, 23); + clk[IMX21_CLK_WDOG_GATE] = imx_clk_gate("wdog_gate", "ipg", CCM_PCCR1, 24); + clk[IMX21_CLK_GPT1_IPG_GATE] = imx_clk_gate("gpt1_ipg_gate", "ipg", CCM_PCCR1, 25); + clk[IMX21_CLK_GPT2_IPG_GATE] = imx_clk_gate("gpt2_ipg_gate", "ipg", CCM_PCCR1, 26); + clk[IMX21_CLK_GPT3_IPG_GATE] = imx_clk_gate("gpt3_ipg_gate", "ipg", CCM_PCCR1, 27); + clk[IMX21_CLK_PWM_IPG_GATE] = imx_clk_gate("pwm_ipg_gate", "ipg", CCM_PCCR1, 28); + clk[IMX21_CLK_RTC_GATE] = imx_clk_gate("rtc_gate", "ipg", CCM_PCCR1, 29); + clk[IMX21_CLK_KPP_GATE] = imx_clk_gate("kpp_gate", "ipg", CCM_PCCR1, 30); + clk[IMX21_CLK_OWIRE_GATE] = imx_clk_gate("owire_gate", "ipg", CCM_PCCR1, 31); imx_check_clocks(clk, ARRAY_SIZE(clk)); +} - clk_register_clkdev(clk[per1], "per", "imx21-uart.0"); - clk_register_clkdev(clk[uart1_ipg_gate], "ipg", "imx21-uart.0"); - clk_register_clkdev(clk[per1], "per", "imx21-uart.1"); - clk_register_clkdev(clk[uart2_ipg_gate], "ipg", "imx21-uart.1"); - clk_register_clkdev(clk[per1], "per", "imx21-uart.2"); - clk_register_clkdev(clk[uart3_ipg_gate], "ipg", "imx21-uart.2"); - clk_register_clkdev(clk[per1], "per", "imx21-uart.3"); - clk_register_clkdev(clk[uart4_ipg_gate], "ipg", "imx21-uart.3"); - clk_register_clkdev(clk[gpt1_ipg_gate], "ipg", "imx-gpt.0"); - clk_register_clkdev(clk[per1], "per", "imx-gpt.0"); - clk_register_clkdev(clk[per2], "per", "imx21-cspi.0"); - clk_register_clkdev(clk[cspi1_ipg_gate], "ipg", "imx21-cspi.0"); - clk_register_clkdev(clk[per2], "per", "imx21-cspi.1"); - clk_register_clkdev(clk[cspi2_ipg_gate], "ipg", "imx21-cspi.1"); - clk_register_clkdev(clk[per2], "per", "imx21-cspi.2"); - clk_register_clkdev(clk[cspi3_ipg_gate], "ipg", "imx21-cspi.2"); - clk_register_clkdev(clk[per3], "per", "imx21-fb.0"); - clk_register_clkdev(clk[lcdc_ipg_gate], "ipg", "imx21-fb.0"); - clk_register_clkdev(clk[lcdc_hclk_gate], "ahb", "imx21-fb.0"); - clk_register_clkdev(clk[usb_gate], "per", "imx21-hcd.0"); - clk_register_clkdev(clk[usb_hclk_gate], "ahb", "imx21-hcd.0"); - clk_register_clkdev(clk[nfc_gate], NULL, "imx21-nand.0"); - clk_register_clkdev(clk[dma_hclk_gate], "ahb", "imx21-dma"); - clk_register_clkdev(clk[dma_gate], "ipg", "imx21-dma"); - clk_register_clkdev(clk[wdog_gate], NULL, "imx2-wdt.0"); - clk_register_clkdev(clk[i2c_gate], NULL, "imx21-i2c.0"); - clk_register_clkdev(clk[owire_gate], NULL, "mxc_w1.0"); +int __init mx21_clocks_init(unsigned long lref, unsigned long href) +{ + ccm = ioremap(MX21_CCM_BASE_ADDR, SZ_2K); + + _mx21_clocks_init(lref, href); + + clk_register_clkdev(clk[IMX21_CLK_PER1], "per", "imx21-uart.0"); + clk_register_clkdev(clk[IMX21_CLK_UART1_IPG_GATE], "ipg", "imx21-uart.0"); + clk_register_clkdev(clk[IMX21_CLK_PER1], "per", "imx21-uart.1"); + clk_register_clkdev(clk[IMX21_CLK_UART2_IPG_GATE], "ipg", "imx21-uart.1"); + clk_register_clkdev(clk[IMX21_CLK_PER1], "per", "imx21-uart.2"); + clk_register_clkdev(clk[IMX21_CLK_UART3_IPG_GATE], "ipg", "imx21-uart.2"); + clk_register_clkdev(clk[IMX21_CLK_PER1], "per", "imx21-uart.3"); + clk_register_clkdev(clk[IMX21_CLK_UART4_IPG_GATE], "ipg", "imx21-uart.3"); + clk_register_clkdev(clk[IMX21_CLK_GPT1_IPG_GATE], "ipg", "imx-gpt.0"); + clk_register_clkdev(clk[IMX21_CLK_PER1], "per", "imx-gpt.0"); + clk_register_clkdev(clk[IMX21_CLK_PER2], "per", "imx21-cspi.0"); + clk_register_clkdev(clk[IMX21_CLK_CSPI1_IPG_GATE], "ipg", "imx21-cspi.0"); + clk_register_clkdev(clk[IMX21_CLK_PER2], "per", "imx21-cspi.1"); + clk_register_clkdev(clk[IMX21_CLK_CSPI2_IPG_GATE], "ipg", "imx21-cspi.1"); + clk_register_clkdev(clk[IMX21_CLK_PER2], "per", "imx21-cspi.2"); + clk_register_clkdev(clk[IMX21_CLK_CSPI3_IPG_GATE], "ipg", "imx21-cspi.2"); + clk_register_clkdev(clk[IMX21_CLK_PER3], "per", "imx21-fb.0"); + clk_register_clkdev(clk[IMX21_CLK_LCDC_IPG_GATE], "ipg", "imx21-fb.0"); + clk_register_clkdev(clk[IMX21_CLK_LCDC_HCLK_GATE], "ahb", "imx21-fb.0"); + clk_register_clkdev(clk[IMX21_CLK_USB_GATE], "per", "imx21-hcd.0"); + clk_register_clkdev(clk[IMX21_CLK_USB_HCLK_GATE], "ahb", "imx21-hcd.0"); + clk_register_clkdev(clk[IMX21_CLK_NFC_GATE], NULL, "imx21-nand.0"); + clk_register_clkdev(clk[IMX21_CLK_DMA_HCLK_GATE], "ahb", "imx21-dma"); + clk_register_clkdev(clk[IMX21_CLK_DMA_GATE], "ipg", "imx21-dma"); + clk_register_clkdev(clk[IMX21_CLK_WDOG_GATE], NULL, "imx2-wdt.0"); + clk_register_clkdev(clk[IMX21_CLK_I2C_GATE], NULL, "imx21-i2c.0"); + clk_register_clkdev(clk[IMX21_CLK_OWIRE_GATE], NULL, "mxc_w1.0"); mxc_timer_init(MX21_IO_ADDRESS(MX21_GPT1_BASE_ADDR), MX21_INT_GPT1); return 0; } + +static void __init mx21_clocks_init_dt(struct device_node *np) +{ + ccm = of_iomap(np, 0); + + _mx21_clocks_init(32768, 26000000); + + clk_data.clks = clk; + clk_data.clk_num = ARRAY_SIZE(clk); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + + mxc_timer_init_dt(of_find_compatible_node(NULL, NULL, "fsl,imx1-gpt")); +} +CLK_OF_DECLARE(imx27_ccm, "fsl,imx21-ccm", mx21_clocks_init_dt); diff --git a/include/dt-bindings/clock/imx21-clock.h b/include/dt-bindings/clock/imx21-clock.h new file mode 100644 index 000000000000..b13596cf51b2 --- /dev/null +++ b/include/dt-bindings/clock/imx21-clock.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2014 Alexander Shiyan + * + * 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 __DT_BINDINGS_CLOCK_IMX21_H +#define __DT_BINDINGS_CLOCK_IMX21_H + +#define IMX21_CLK_DUMMY 0 +#define IMX21_CLK_CKIL 1 +#define IMX21_CLK_CKIH 2 +#define IMX21_CLK_FPM 3 +#define IMX21_CLK_CKIH_DIV1P5 4 +#define IMX21_CLK_MPLL_GATE 5 +#define IMX21_CLK_SPLL_GATE 6 +#define IMX21_CLK_FPM_GATE 7 +#define IMX21_CLK_CKIH_GATE 8 +#define IMX21_CLK_MPLL_OSC_SEL 9 +#define IMX21_CLK_IPG 10 +#define IMX21_CLK_HCLK 11 +#define IMX21_CLK_MPLL_SEL 12 +#define IMX21_CLK_SPLL_SEL 13 +#define IMX21_CLK_SSI1_SEL 14 +#define IMX21_CLK_SSI2_SEL 15 +#define IMX21_CLK_USB_DIV 16 +#define IMX21_CLK_FCLK 17 +#define IMX21_CLK_MPLL 18 +#define IMX21_CLK_SPLL 19 +#define IMX21_CLK_NFC_DIV 20 +#define IMX21_CLK_SSI1_DIV 21 +#define IMX21_CLK_SSI2_DIV 22 +#define IMX21_CLK_PER1 23 +#define IMX21_CLK_PER2 24 +#define IMX21_CLK_PER3 25 +#define IMX21_CLK_PER4 26 +#define IMX21_CLK_UART1_IPG_GATE 27 +#define IMX21_CLK_UART2_IPG_GATE 28 +#define IMX21_CLK_UART3_IPG_GATE 29 +#define IMX21_CLK_UART4_IPG_GATE 30 +#define IMX21_CLK_CSPI1_IPG_GATE 31 +#define IMX21_CLK_CSPI2_IPG_GATE 32 +#define IMX21_CLK_SSI1_GATE 33 +#define IMX21_CLK_SSI2_GATE 34 +#define IMX21_CLK_SDHC1_IPG_GATE 35 +#define IMX21_CLK_SDHC2_IPG_GATE 36 +#define IMX21_CLK_GPIO_GATE 37 +#define IMX21_CLK_I2C_GATE 38 +#define IMX21_CLK_DMA_GATE 39 +#define IMX21_CLK_USB_GATE 40 +#define IMX21_CLK_EMMA_GATE 41 +#define IMX21_CLK_SSI2_BAUD_GATE 42 +#define IMX21_CLK_SSI1_BAUD_GATE 43 +#define IMX21_CLK_LCDC_IPG_GATE 44 +#define IMX21_CLK_NFC_GATE 45 +#define IMX21_CLK_LCDC_HCLK_GATE 46 +#define IMX21_CLK_PER4_GATE 47 +#define IMX21_CLK_BMI_GATE 48 +#define IMX21_CLK_USB_HCLK_GATE 49 +#define IMX21_CLK_SLCDC_GATE 50 +#define IMX21_CLK_SLCDC_HCLK_GATE 51 +#define IMX21_CLK_EMMA_HCLK_GATE 52 +#define IMX21_CLK_BROM_GATE 53 +#define IMX21_CLK_DMA_HCLK_GATE 54 +#define IMX21_CLK_CSI_HCLK_GATE 55 +#define IMX21_CLK_CSPI3_IPG_GATE 56 +#define IMX21_CLK_WDOG_GATE 57 +#define IMX21_CLK_GPT1_IPG_GATE 58 +#define IMX21_CLK_GPT2_IPG_GATE 59 +#define IMX21_CLK_GPT3_IPG_GATE 60 +#define IMX21_CLK_PWM_IPG_GATE 61 +#define IMX21_CLK_RTC_GATE 62 +#define IMX21_CLK_KPP_GATE 63 +#define IMX21_CLK_OWIRE_GATE 64 +#define IMX21_CLK_MAX 65 + +#endif -- cgit v1.2.3 From 35e3bc535d0437ca5f32985a294703ce48c75d88 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 5 Jul 2014 08:51:38 +0400 Subject: ARM: i.MX: Remove i.MX1 camera support i.MX1 camera driver has been removed by the commit 90b055898e. This patch removes remaining support files for this camera. Signed-off-by: Alexander Shiyan Signed-off-by: Shawn Guo --- arch/arm/mach-imx/Makefile | 3 -- arch/arm/mach-imx/devices/Kconfig | 3 -- arch/arm/mach-imx/devices/Makefile | 1 - arch/arm/mach-imx/devices/devices-common.h | 10 ------ arch/arm/mach-imx/devices/platform-mx1-camera.c | 42 ------------------------- arch/arm/mach-imx/mx1-camera-fiq-ksym.c | 18 ----------- arch/arm/mach-imx/mx1-camera-fiq.S | 35 --------------------- include/linux/platform_data/camera-mx1.h | 35 --------------------- 8 files changed, 147 deletions(-) delete mode 100644 arch/arm/mach-imx/devices/platform-mx1-camera.c delete mode 100644 arch/arm/mach-imx/mx1-camera-fiq-ksym.c delete mode 100644 arch/arm/mach-imx/mx1-camera-fiq.S delete mode 100644 include/linux/platform_data/camera-mx1.h (limited to 'include') diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile index a3d9712567c9..d1c1d38f3741 100644 --- a/arch/arm/mach-imx/Makefile +++ b/arch/arm/mach-imx/Makefile @@ -40,9 +40,6 @@ obj-y += ssi-fiq.o obj-y += ssi-fiq-ksym.o endif -# Support for CMOS sensor interface -obj-$(CONFIG_MX1_VIDEO) += mx1-camera-fiq.o mx1-camera-fiq-ksym.o - # i.MX1 based machines obj-$(CONFIG_ARCH_MX1ADS) += mach-mx1ads.o obj-$(CONFIG_MACH_SCB9328) += mach-scb9328.o diff --git a/arch/arm/mach-imx/devices/Kconfig b/arch/arm/mach-imx/devices/Kconfig index 119f699649a9..73a1dce6cac1 100644 --- a/arch/arm/mach-imx/devices/Kconfig +++ b/arch/arm/mach-imx/devices/Kconfig @@ -45,9 +45,6 @@ config IMX_HAVE_PLATFORM_IMX_UART config IMX_HAVE_PLATFORM_IPU_CORE bool -config IMX_HAVE_PLATFORM_MX1_CAMERA - bool - config IMX_HAVE_PLATFORM_MX2_CAMERA bool diff --git a/arch/arm/mach-imx/devices/Makefile b/arch/arm/mach-imx/devices/Makefile index 6bb144dd680e..8fdb12b4ca7e 100644 --- a/arch/arm/mach-imx/devices/Makefile +++ b/arch/arm/mach-imx/devices/Makefile @@ -17,7 +17,6 @@ obj-$(CONFIG_IMX_HAVE_PLATFORM_PATA_IMX) += platform-pata_imx.o obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_SSI) += platform-imx-ssi.o obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_UART) += platform-imx-uart.o obj-$(CONFIG_IMX_HAVE_PLATFORM_IPU_CORE) += platform-ipu-core.o -obj-$(CONFIG_IMX_HAVE_PLATFORM_MX1_CAMERA) += platform-mx1-camera.o obj-$(CONFIG_IMX_HAVE_PLATFORM_MX2_CAMERA) += platform-mx2-camera.o obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_EHCI) += platform-mxc-ehci.o obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_MMC) += platform-mxc-mmc.o diff --git a/arch/arm/mach-imx/devices/devices-common.h b/arch/arm/mach-imx/devices/devices-common.h index 69bafc884dfa..67f7fb13050d 100644 --- a/arch/arm/mach-imx/devices/devices-common.h +++ b/arch/arm/mach-imx/devices/devices-common.h @@ -192,16 +192,6 @@ struct platform_device *__init imx_add_mx3_sdc_fb( const struct imx_ipu_core_data *data, struct mx3fb_platform_data *pdata); -#include -struct imx_mx1_camera_data { - resource_size_t iobase; - resource_size_t iosize; - resource_size_t irq; -}; -struct platform_device *__init imx_add_mx1_camera( - const struct imx_mx1_camera_data *data, - const struct mx1_camera_pdata *pdata); - #include struct imx_mx2_camera_data { const char *devid; diff --git a/arch/arm/mach-imx/devices/platform-mx1-camera.c b/arch/arm/mach-imx/devices/platform-mx1-camera.c deleted file mode 100644 index 2c6788131080..000000000000 --- a/arch/arm/mach-imx/devices/platform-mx1-camera.c +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2010 Pengutronix - * Uwe Kleine-Koenig - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License version 2 as published by the - * Free Software Foundation. - */ -#include "../hardware.h" -#include "devices-common.h" - -#define imx_mx1_camera_data_entry_single(soc, _size) \ - { \ - .iobase = soc ## _CSI ## _BASE_ADDR, \ - .iosize = _size, \ - .irq = soc ## _INT_CSI, \ - } - -#ifdef CONFIG_SOC_IMX1 -const struct imx_mx1_camera_data imx1_mx1_camera_data __initconst = - imx_mx1_camera_data_entry_single(MX1, 10); -#endif /* ifdef CONFIG_SOC_IMX1 */ - -struct platform_device *__init imx_add_mx1_camera( - const struct imx_mx1_camera_data *data, - const struct mx1_camera_pdata *pdata) -{ - struct resource res[] = { - { - .start = data->iobase, - .end = data->iobase + data->iosize - 1, - .flags = IORESOURCE_MEM, - }, { - .start = data->irq, - .end = data->irq, - .flags = IORESOURCE_IRQ, - }, - }; - return imx_add_platform_device_dmamask("mx1-camera", 0, - res, ARRAY_SIZE(res), - pdata, sizeof(*pdata), DMA_BIT_MASK(32)); -} diff --git a/arch/arm/mach-imx/mx1-camera-fiq-ksym.c b/arch/arm/mach-imx/mx1-camera-fiq-ksym.c deleted file mode 100644 index fb38436ca67f..000000000000 --- a/arch/arm/mach-imx/mx1-camera-fiq-ksym.c +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Exported ksyms of ARCH_MX1 - * - * Copyright (C) 2008, Darius Augulis - * - * 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 - -/* IMX camera FIQ handler */ -EXPORT_SYMBOL(mx1_camera_sof_fiq_start); -EXPORT_SYMBOL(mx1_camera_sof_fiq_end); diff --git a/arch/arm/mach-imx/mx1-camera-fiq.S b/arch/arm/mach-imx/mx1-camera-fiq.S deleted file mode 100644 index 9c69aa65bf17..000000000000 --- a/arch/arm/mach-imx/mx1-camera-fiq.S +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2008 Paulius Zaleckas - * - * Based on linux/arch/arm/lib/floppydma.S - * Copyright (C) 1995, 1996 Russell King - * - * 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 - - .text - .global mx1_camera_sof_fiq_end - .global mx1_camera_sof_fiq_start -mx1_camera_sof_fiq_start: - @ enable dma - ldr r12, [r9] - orr r12, r12, #0x00000001 - str r12, [r9] - @ unmask DMA interrupt - ldr r12, [r8] - bic r12, r12, r13 - str r12, [r8] - @ disable SOF interrupt - ldr r12, [r10] - bic r12, r12, #0x00010000 - str r12, [r10] - @ clear SOF flag - mov r12, #0x00010000 - str r12, [r11] - @ return from FIQ - subs pc, lr, #4 -mx1_camera_sof_fiq_end: diff --git a/include/linux/platform_data/camera-mx1.h b/include/linux/platform_data/camera-mx1.h deleted file mode 100644 index 4fd6c70314b4..000000000000 --- a/include/linux/platform_data/camera-mx1.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * mx1_camera.h - i.MX1/i.MXL camera driver header file - * - * Copyright (c) 2008, Paulius Zaleckas - * Copyright (C) 2009, Darius Augulis - * - * Based on PXA camera.h file: - * Copyright (C) 2003, Intel Corporation - * Copyright (C) 2008, 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 __ASM_ARCH_CAMERA_H_ -#define __ASM_ARCH_CAMERA_H_ - -#define MX1_CAMERA_DATA_HIGH 1 -#define MX1_CAMERA_PCLK_RISING 2 -#define MX1_CAMERA_VSYNC_HIGH 4 - -extern unsigned char mx1_camera_sof_fiq_start, mx1_camera_sof_fiq_end; - -/** - * struct mx1_camera_pdata - i.MX1/i.MXL camera platform data - * @mclk_10khz: master clock frequency in 10kHz units - * @flags: MX1 camera platform flags - */ -struct mx1_camera_pdata { - unsigned long mclk_10khz; - unsigned long flags; -}; - -#endif /* __ASM_ARCH_CAMERA_H_ */ -- cgit v1.2.3 From e8e3faa0391a81a40a9add37d90bcdfbd9a5b942 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 5 Jul 2014 09:36:06 +0400 Subject: ARM: i.MX27 clk: Introduce DT include for clock provider Use clock defines in order to make devicetrees more human readable. Signed-off-by: Alexander Shiyan Signed-off-by: Shawn Guo --- .../devicetree/bindings/clock/imx27-clock.txt | 127 +------- arch/arm/mach-imx/clk-imx27.c | 347 ++++++++++----------- include/dt-bindings/clock/imx27-clock.h | 107 +++++++ 3 files changed, 285 insertions(+), 296 deletions(-) create mode 100644 include/dt-bindings/clock/imx27-clock.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/imx27-clock.txt b/Documentation/devicetree/bindings/clock/imx27-clock.txt index 6bc9fd2c6631..cc05de9ec393 100644 --- a/Documentation/devicetree/bindings/clock/imx27-clock.txt +++ b/Documentation/devicetree/bindings/clock/imx27-clock.txt @@ -7,117 +7,22 @@ Required properties: - #clock-cells: Should be <1> The clock consumer should specify the desired clock by having the clock -ID in its "clocks" phandle cell. The following is a full list of i.MX27 -clocks and IDs. - - Clock ID - ----------------------- - dummy 0 - ckih 1 - ckil 2 - mpll 3 - spll 4 - mpll_main2 5 - ahb 6 - ipg 7 - nfc_div 8 - per1_div 9 - per2_div 10 - per3_div 11 - per4_div 12 - vpu_sel 13 - vpu_div 14 - usb_div 15 - cpu_sel 16 - clko_sel 17 - cpu_div 18 - clko_div 19 - ssi1_sel 20 - ssi2_sel 21 - ssi1_div 22 - ssi2_div 23 - clko_en 24 - ssi2_ipg_gate 25 - ssi1_ipg_gate 26 - slcdc_ipg_gate 27 - sdhc3_ipg_gate 28 - sdhc2_ipg_gate 29 - sdhc1_ipg_gate 30 - scc_ipg_gate 31 - sahara_ipg_gate 32 - rtc_ipg_gate 33 - pwm_ipg_gate 34 - owire_ipg_gate 35 - lcdc_ipg_gate 36 - kpp_ipg_gate 37 - iim_ipg_gate 38 - i2c2_ipg_gate 39 - i2c1_ipg_gate 40 - gpt6_ipg_gate 41 - gpt5_ipg_gate 42 - gpt4_ipg_gate 43 - gpt3_ipg_gate 44 - gpt2_ipg_gate 45 - gpt1_ipg_gate 46 - gpio_ipg_gate 47 - fec_ipg_gate 48 - emma_ipg_gate 49 - dma_ipg_gate 50 - cspi3_ipg_gate 51 - cspi2_ipg_gate 52 - cspi1_ipg_gate 53 - nfc_baud_gate 54 - ssi2_baud_gate 55 - ssi1_baud_gate 56 - vpu_baud_gate 57 - per4_gate 58 - per3_gate 59 - per2_gate 60 - per1_gate 61 - usb_ahb_gate 62 - slcdc_ahb_gate 63 - sahara_ahb_gate 64 - lcdc_ahb_gate 65 - vpu_ahb_gate 66 - fec_ahb_gate 67 - emma_ahb_gate 68 - emi_ahb_gate 69 - dma_ahb_gate 70 - csi_ahb_gate 71 - brom_ahb_gate 72 - ata_ahb_gate 73 - wdog_ipg_gate 74 - usb_ipg_gate 75 - uart6_ipg_gate 76 - uart5_ipg_gate 77 - uart4_ipg_gate 78 - uart3_ipg_gate 79 - uart2_ipg_gate 80 - uart1_ipg_gate 81 - ckih_div1p5 82 - fpm 83 - mpll_osc_sel 84 - mpll_sel 85 - spll_gate 86 - mshc_div 87 - rtic_ipg_gate 88 - mshc_ipg_gate 89 - rtic_ahb_gate 90 - mshc_baud_gate 91 +ID in its "clocks" phandle cell. See include/dt-bindings/clock/imx27-clock.h +for the full list of i.MX27 clock IDs. Examples: + clks: ccm@10027000{ + compatible = "fsl,imx27-ccm"; + reg = <0x10027000 0x1000>; + #clock-cells = <1>; + }; -clks: ccm@10027000{ - compatible = "fsl,imx27-ccm"; - reg = <0x10027000 0x1000>; - #clock-cells = <1>; -}; - -uart1: serial@1000a000 { - compatible = "fsl,imx27-uart", "fsl,imx21-uart"; - reg = <0x1000a000 0x1000>; - interrupts = <20>; - clocks = <&clks 81>, <&clks 61>; - clock-names = "ipg", "per"; - status = "disabled"; -}; + uart1: serial@1000a000 { + compatible = "fsl,imx27-uart", "fsl,imx21-uart"; + reg = <0x1000a000 0x1000>; + interrupts = <20>; + clocks = <&clks IMX27_CLK_UART1_IPG_GATE>, + <&clks IMX27_CLK_PER1_GATE>; + clock-names = "ipg", "per"; + status = "disabled"; + }; diff --git a/arch/arm/mach-imx/clk-imx27.c b/arch/arm/mach-imx/clk-imx27.c index d76aa5f13260..ef7001c190a7 100644 --- a/arch/arm/mach-imx/clk-imx27.c +++ b/arch/arm/mach-imx/clk-imx27.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "clk.h" #include "common.h" @@ -63,147 +64,123 @@ static const char *clko_sel_clks[] = { static const char *ssi_sel_clks[] = { "spll_gate", "mpll", }; -enum mx27_clks { - dummy, ckih, ckil, mpll, spll, mpll_main2, ahb, ipg, nfc_div, per1_div, - per2_div, per3_div, per4_div, vpu_sel, vpu_div, usb_div, cpu_sel, - clko_sel, cpu_div, clko_div, ssi1_sel, ssi2_sel, ssi1_div, ssi2_div, - clko_en, ssi2_ipg_gate, ssi1_ipg_gate, slcdc_ipg_gate, sdhc3_ipg_gate, - sdhc2_ipg_gate, sdhc1_ipg_gate, scc_ipg_gate, sahara_ipg_gate, - rtc_ipg_gate, pwm_ipg_gate, owire_ipg_gate, lcdc_ipg_gate, - kpp_ipg_gate, iim_ipg_gate, i2c2_ipg_gate, i2c1_ipg_gate, - gpt6_ipg_gate, gpt5_ipg_gate, gpt4_ipg_gate, gpt3_ipg_gate, - gpt2_ipg_gate, gpt1_ipg_gate, gpio_ipg_gate, fec_ipg_gate, - emma_ipg_gate, dma_ipg_gate, cspi3_ipg_gate, cspi2_ipg_gate, - cspi1_ipg_gate, nfc_baud_gate, ssi2_baud_gate, ssi1_baud_gate, - vpu_baud_gate, per4_gate, per3_gate, per2_gate, per1_gate, - usb_ahb_gate, slcdc_ahb_gate, sahara_ahb_gate, lcdc_ahb_gate, - vpu_ahb_gate, fec_ahb_gate, emma_ahb_gate, emi_ahb_gate, dma_ahb_gate, - csi_ahb_gate, brom_ahb_gate, ata_ahb_gate, wdog_ipg_gate, usb_ipg_gate, - uart6_ipg_gate, uart5_ipg_gate, uart4_ipg_gate, uart3_ipg_gate, - uart2_ipg_gate, uart1_ipg_gate, ckih_div1p5, fpm, mpll_osc_sel, - mpll_sel, spll_gate, mshc_div, rtic_ipg_gate, mshc_ipg_gate, - rtic_ahb_gate, mshc_baud_gate, clk_max -}; - -static struct clk *clk[clk_max]; +static struct clk *clk[IMX27_CLK_MAX]; static struct clk_onecell_data clk_data; static void __init _mx27_clocks_init(unsigned long fref) { BUG_ON(!ccm); - clk[dummy] = imx_clk_fixed("dummy", 0); - clk[ckih] = imx_clk_fixed("ckih", fref); - clk[ckil] = imx_clk_fixed("ckil", 32768); - clk[fpm] = imx_clk_fixed_factor("fpm", "ckil", 1024, 1); - clk[ckih_div1p5] = imx_clk_fixed_factor("ckih_div1p5", "ckih", 2, 3); - - clk[mpll_osc_sel] = imx_clk_mux("mpll_osc_sel", CCM_CSCR, 4, 1, - mpll_osc_sel_clks, - ARRAY_SIZE(mpll_osc_sel_clks)); - clk[mpll_sel] = imx_clk_mux("mpll_sel", CCM_CSCR, 16, 1, mpll_sel_clks, - ARRAY_SIZE(mpll_sel_clks)); - clk[mpll] = imx_clk_pllv1("mpll", "mpll_sel", CCM_MPCTL0); - clk[spll] = imx_clk_pllv1("spll", "ckih", CCM_SPCTL0); - clk[spll_gate] = imx_clk_gate("spll_gate", "spll", CCM_CSCR, 1); - clk[mpll_main2] = imx_clk_fixed_factor("mpll_main2", "mpll", 2, 3); + clk[IMX27_CLK_DUMMY] = imx_clk_fixed("dummy", 0); + clk[IMX27_CLK_CKIH] = imx_clk_fixed("ckih", fref); + clk[IMX27_CLK_CKIL] = imx_clk_fixed("ckil", 32768); + clk[IMX27_CLK_FPM] = imx_clk_fixed_factor("fpm", "ckil", 1024, 1); + clk[IMX27_CLK_CKIH_DIV1P5] = imx_clk_fixed_factor("ckih_div1p5", "ckih", 2, 3); + clk[IMX27_CLK_MPLL_OSC_SEL] = imx_clk_mux("mpll_osc_sel", CCM_CSCR, 4, 1, mpll_osc_sel_clks, ARRAY_SIZE(mpll_osc_sel_clks)); + clk[IMX27_CLK_MPLL_SEL] = imx_clk_mux("mpll_sel", CCM_CSCR, 16, 1, mpll_sel_clks, ARRAY_SIZE(mpll_sel_clks)); + clk[IMX27_CLK_MPLL] = imx_clk_pllv1("mpll", "mpll_sel", CCM_MPCTL0); + clk[IMX27_CLK_SPLL] = imx_clk_pllv1("spll", "ckih", CCM_SPCTL0); + clk[IMX27_CLK_SPLL_GATE] = imx_clk_gate("spll_gate", "spll", CCM_CSCR, 1); + clk[IMX27_CLK_MPLL_MAIN2] = imx_clk_fixed_factor("mpll_main2", "mpll", 2, 3); if (mx27_revision() >= IMX_CHIP_REVISION_2_0) { - clk[ahb] = imx_clk_divider("ahb", "mpll_main2", CCM_CSCR, 8, 2); - clk[ipg] = imx_clk_fixed_factor("ipg", "ahb", 1, 2); + clk[IMX27_CLK_AHB] = imx_clk_divider("ahb", "mpll_main2", CCM_CSCR, 8, 2); + clk[IMX27_CLK_IPG] = imx_clk_fixed_factor("ipg", "ahb", 1, 2); } else { - clk[ahb] = imx_clk_divider("ahb", "mpll_main2", CCM_CSCR, 9, 4); - clk[ipg] = imx_clk_divider("ipg", "ahb", CCM_CSCR, 8, 1); + clk[IMX27_CLK_AHB] = imx_clk_divider("ahb", "mpll_main2", CCM_CSCR, 9, 4); + clk[IMX27_CLK_IPG] = imx_clk_divider("ipg", "ahb", CCM_CSCR, 8, 1); } - clk[mshc_div] = imx_clk_divider("mshc_div", "ahb", CCM_PCDR0, 0, 6); - clk[nfc_div] = imx_clk_divider("nfc_div", "ahb", CCM_PCDR0, 6, 4); - clk[per1_div] = imx_clk_divider("per1_div", "mpll_main2", CCM_PCDR1, 0, 6); - clk[per2_div] = imx_clk_divider("per2_div", "mpll_main2", CCM_PCDR1, 8, 6); - clk[per3_div] = imx_clk_divider("per3_div", "mpll_main2", CCM_PCDR1, 16, 6); - clk[per4_div] = imx_clk_divider("per4_div", "mpll_main2", CCM_PCDR1, 24, 6); - clk[vpu_sel] = imx_clk_mux("vpu_sel", CCM_CSCR, 21, 1, vpu_sel_clks, ARRAY_SIZE(vpu_sel_clks)); - clk[vpu_div] = imx_clk_divider("vpu_div", "vpu_sel", CCM_PCDR0, 10, 6); - clk[usb_div] = imx_clk_divider("usb_div", "spll_gate", CCM_CSCR, 28, 3); - clk[cpu_sel] = imx_clk_mux("cpu_sel", CCM_CSCR, 15, 1, cpu_sel_clks, ARRAY_SIZE(cpu_sel_clks)); - clk[clko_sel] = imx_clk_mux("clko_sel", CCM_CCSR, 0, 5, clko_sel_clks, ARRAY_SIZE(clko_sel_clks)); + clk[IMX27_CLK_MSHC_DIV] = imx_clk_divider("mshc_div", "ahb", CCM_PCDR0, 0, 6); + clk[IMX27_CLK_NFC_DIV] = imx_clk_divider("nfc_div", "ahb", CCM_PCDR0, 6, 4); + clk[IMX27_CLK_PER1_DIV] = imx_clk_divider("per1_div", "mpll_main2", CCM_PCDR1, 0, 6); + clk[IMX27_CLK_PER2_DIV] = imx_clk_divider("per2_div", "mpll_main2", CCM_PCDR1, 8, 6); + clk[IMX27_CLK_PER3_DIV] = imx_clk_divider("per3_div", "mpll_main2", CCM_PCDR1, 16, 6); + clk[IMX27_CLK_PER4_DIV] = imx_clk_divider("per4_div", "mpll_main2", CCM_PCDR1, 24, 6); + clk[IMX27_CLK_VPU_SEL] = imx_clk_mux("vpu_sel", CCM_CSCR, 21, 1, vpu_sel_clks, ARRAY_SIZE(vpu_sel_clks)); + clk[IMX27_CLK_VPU_DIV] = imx_clk_divider("vpu_div", "vpu_sel", CCM_PCDR0, 10, 6); + clk[IMX27_CLK_USB_DIV] = imx_clk_divider("usb_div", "spll_gate", CCM_CSCR, 28, 3); + clk[IMX27_CLK_CPU_SEL] = imx_clk_mux("cpu_sel", CCM_CSCR, 15, 1, cpu_sel_clks, ARRAY_SIZE(cpu_sel_clks)); + clk[IMX27_CLK_CLKO_SEL] = imx_clk_mux("clko_sel", CCM_CCSR, 0, 5, clko_sel_clks, ARRAY_SIZE(clko_sel_clks)); + if (mx27_revision() >= IMX_CHIP_REVISION_2_0) - clk[cpu_div] = imx_clk_divider("cpu_div", "cpu_sel", CCM_CSCR, 12, 2); + clk[IMX27_CLK_CPU_DIV] = imx_clk_divider("cpu_div", "cpu_sel", CCM_CSCR, 12, 2); else - clk[cpu_div] = imx_clk_divider("cpu_div", "cpu_sel", CCM_CSCR, 13, 3); - clk[clko_div] = imx_clk_divider("clko_div", "clko_sel", CCM_PCDR0, 22, 3); - clk[ssi1_sel] = imx_clk_mux("ssi1_sel", CCM_CSCR, 22, 1, ssi_sel_clks, ARRAY_SIZE(ssi_sel_clks)); - clk[ssi2_sel] = imx_clk_mux("ssi2_sel", CCM_CSCR, 23, 1, ssi_sel_clks, ARRAY_SIZE(ssi_sel_clks)); - clk[ssi1_div] = imx_clk_divider("ssi1_div", "ssi1_sel", CCM_PCDR0, 16, 6); - clk[ssi2_div] = imx_clk_divider("ssi2_div", "ssi2_sel", CCM_PCDR0, 26, 6); - clk[clko_en] = imx_clk_gate("clko_en", "clko_div", CCM_PCCR0, 0); - clk[ssi2_ipg_gate] = imx_clk_gate("ssi2_ipg_gate", "ipg", CCM_PCCR0, 0); - clk[ssi1_ipg_gate] = imx_clk_gate("ssi1_ipg_gate", "ipg", CCM_PCCR0, 1); - clk[slcdc_ipg_gate] = imx_clk_gate("slcdc_ipg_gate", "ipg", CCM_PCCR0, 2); - clk[sdhc3_ipg_gate] = imx_clk_gate("sdhc3_ipg_gate", "ipg", CCM_PCCR0, 3); - clk[sdhc2_ipg_gate] = imx_clk_gate("sdhc2_ipg_gate", "ipg", CCM_PCCR0, 4); - clk[sdhc1_ipg_gate] = imx_clk_gate("sdhc1_ipg_gate", "ipg", CCM_PCCR0, 5); - clk[scc_ipg_gate] = imx_clk_gate("scc_ipg_gate", "ipg", CCM_PCCR0, 6); - clk[sahara_ipg_gate] = imx_clk_gate("sahara_ipg_gate", "ipg", CCM_PCCR0, 7); - clk[rtic_ipg_gate] = imx_clk_gate("rtic_ipg_gate", "ipg", CCM_PCCR0, 8); - clk[rtc_ipg_gate] = imx_clk_gate("rtc_ipg_gate", "ipg", CCM_PCCR0, 9); - clk[pwm_ipg_gate] = imx_clk_gate("pwm_ipg_gate", "ipg", CCM_PCCR0, 11); - clk[owire_ipg_gate] = imx_clk_gate("owire_ipg_gate", "ipg", CCM_PCCR0, 12); - clk[mshc_ipg_gate] = imx_clk_gate("mshc_ipg_gate", "ipg", CCM_PCCR0, 13); - clk[lcdc_ipg_gate] = imx_clk_gate("lcdc_ipg_gate", "ipg", CCM_PCCR0, 14); - clk[kpp_ipg_gate] = imx_clk_gate("kpp_ipg_gate", "ipg", CCM_PCCR0, 15); - clk[iim_ipg_gate] = imx_clk_gate("iim_ipg_gate", "ipg", CCM_PCCR0, 16); - clk[i2c2_ipg_gate] = imx_clk_gate("i2c2_ipg_gate", "ipg", CCM_PCCR0, 17); - clk[i2c1_ipg_gate] = imx_clk_gate("i2c1_ipg_gate", "ipg", CCM_PCCR0, 18); - clk[gpt6_ipg_gate] = imx_clk_gate("gpt6_ipg_gate", "ipg", CCM_PCCR0, 19); - clk[gpt5_ipg_gate] = imx_clk_gate("gpt5_ipg_gate", "ipg", CCM_PCCR0, 20); - clk[gpt4_ipg_gate] = imx_clk_gate("gpt4_ipg_gate", "ipg", CCM_PCCR0, 21); - clk[gpt3_ipg_gate] = imx_clk_gate("gpt3_ipg_gate", "ipg", CCM_PCCR0, 22); - clk[gpt2_ipg_gate] = imx_clk_gate("gpt2_ipg_gate", "ipg", CCM_PCCR0, 23); - clk[gpt1_ipg_gate] = imx_clk_gate("gpt1_ipg_gate", "ipg", CCM_PCCR0, 24); - clk[gpio_ipg_gate] = imx_clk_gate("gpio_ipg_gate", "ipg", CCM_PCCR0, 25); - clk[fec_ipg_gate] = imx_clk_gate("fec_ipg_gate", "ipg", CCM_PCCR0, 26); - clk[emma_ipg_gate] = imx_clk_gate("emma_ipg_gate", "ipg", CCM_PCCR0, 27); - clk[dma_ipg_gate] = imx_clk_gate("dma_ipg_gate", "ipg", CCM_PCCR0, 28); - clk[cspi3_ipg_gate] = imx_clk_gate("cspi3_ipg_gate", "ipg", CCM_PCCR0, 29); - clk[cspi2_ipg_gate] = imx_clk_gate("cspi2_ipg_gate", "ipg", CCM_PCCR0, 30); - clk[cspi1_ipg_gate] = imx_clk_gate("cspi1_ipg_gate", "ipg", CCM_PCCR0, 31); - clk[mshc_baud_gate] = imx_clk_gate("mshc_baud_gate", "mshc_div", CCM_PCCR1, 2); - clk[nfc_baud_gate] = imx_clk_gate("nfc_baud_gate", "nfc_div", CCM_PCCR1, 3); - clk[ssi2_baud_gate] = imx_clk_gate("ssi2_baud_gate", "ssi2_div", CCM_PCCR1, 4); - clk[ssi1_baud_gate] = imx_clk_gate("ssi1_baud_gate", "ssi1_div", CCM_PCCR1, 5); - clk[vpu_baud_gate] = imx_clk_gate("vpu_baud_gate", "vpu_div", CCM_PCCR1, 6); - clk[per4_gate] = imx_clk_gate("per4_gate", "per4_div", CCM_PCCR1, 7); - clk[per3_gate] = imx_clk_gate("per3_gate", "per3_div", CCM_PCCR1, 8); - clk[per2_gate] = imx_clk_gate("per2_gate", "per2_div", CCM_PCCR1, 9); - clk[per1_gate] = imx_clk_gate("per1_gate", "per1_div", CCM_PCCR1, 10); - clk[usb_ahb_gate] = imx_clk_gate("usb_ahb_gate", "ahb", CCM_PCCR1, 11); - clk[slcdc_ahb_gate] = imx_clk_gate("slcdc_ahb_gate", "ahb", CCM_PCCR1, 12); - clk[sahara_ahb_gate] = imx_clk_gate("sahara_ahb_gate", "ahb", CCM_PCCR1, 13); - clk[rtic_ahb_gate] = imx_clk_gate("rtic_ahb_gate", "ahb", CCM_PCCR1, 14); - clk[lcdc_ahb_gate] = imx_clk_gate("lcdc_ahb_gate", "ahb", CCM_PCCR1, 15); - clk[vpu_ahb_gate] = imx_clk_gate("vpu_ahb_gate", "ahb", CCM_PCCR1, 16); - clk[fec_ahb_gate] = imx_clk_gate("fec_ahb_gate", "ahb", CCM_PCCR1, 17); - clk[emma_ahb_gate] = imx_clk_gate("emma_ahb_gate", "ahb", CCM_PCCR1, 18); - clk[emi_ahb_gate] = imx_clk_gate("emi_ahb_gate", "ahb", CCM_PCCR1, 19); - clk[dma_ahb_gate] = imx_clk_gate("dma_ahb_gate", "ahb", CCM_PCCR1, 20); - clk[csi_ahb_gate] = imx_clk_gate("csi_ahb_gate", "ahb", CCM_PCCR1, 21); - clk[brom_ahb_gate] = imx_clk_gate("brom_ahb_gate", "ahb", CCM_PCCR1, 22); - clk[ata_ahb_gate] = imx_clk_gate("ata_ahb_gate", "ahb", CCM_PCCR1, 23); - clk[wdog_ipg_gate] = imx_clk_gate("wdog_ipg_gate", "ipg", CCM_PCCR1, 24); - clk[usb_ipg_gate] = imx_clk_gate("usb_ipg_gate", "ipg", CCM_PCCR1, 25); - clk[uart6_ipg_gate] = imx_clk_gate("uart6_ipg_gate", "ipg", CCM_PCCR1, 26); - clk[uart5_ipg_gate] = imx_clk_gate("uart5_ipg_gate", "ipg", CCM_PCCR1, 27); - clk[uart4_ipg_gate] = imx_clk_gate("uart4_ipg_gate", "ipg", CCM_PCCR1, 28); - clk[uart3_ipg_gate] = imx_clk_gate("uart3_ipg_gate", "ipg", CCM_PCCR1, 29); - clk[uart2_ipg_gate] = imx_clk_gate("uart2_ipg_gate", "ipg", CCM_PCCR1, 30); - clk[uart1_ipg_gate] = imx_clk_gate("uart1_ipg_gate", "ipg", CCM_PCCR1, 31); + clk[IMX27_CLK_CPU_DIV] = imx_clk_divider("cpu_div", "cpu_sel", CCM_CSCR, 13, 3); + + clk[IMX27_CLK_CLKO_DIV] = imx_clk_divider("clko_div", "clko_sel", CCM_PCDR0, 22, 3); + clk[IMX27_CLK_SSI1_SEL] = imx_clk_mux("ssi1_sel", CCM_CSCR, 22, 1, ssi_sel_clks, ARRAY_SIZE(ssi_sel_clks)); + clk[IMX27_CLK_SSI2_SEL] = imx_clk_mux("ssi2_sel", CCM_CSCR, 23, 1, ssi_sel_clks, ARRAY_SIZE(ssi_sel_clks)); + clk[IMX27_CLK_SSI1_DIV] = imx_clk_divider("ssi1_div", "ssi1_sel", CCM_PCDR0, 16, 6); + clk[IMX27_CLK_SSI2_DIV] = imx_clk_divider("ssi2_div", "ssi2_sel", CCM_PCDR0, 26, 6); + clk[IMX27_CLK_CLKO_EN] = imx_clk_gate("clko_en", "clko_div", CCM_PCCR0, 0); + clk[IMX27_CLK_SSI2_IPG_GATE] = imx_clk_gate("ssi2_ipg_gate", "ipg", CCM_PCCR0, 0); + clk[IMX27_CLK_SSI1_IPG_GATE] = imx_clk_gate("ssi1_ipg_gate", "ipg", CCM_PCCR0, 1); + clk[IMX27_CLK_SLCDC_IPG_GATE] = imx_clk_gate("slcdc_ipg_gate", "ipg", CCM_PCCR0, 2); + clk[IMX27_CLK_SDHC3_IPG_GATE] = imx_clk_gate("sdhc3_ipg_gate", "ipg", CCM_PCCR0, 3); + clk[IMX27_CLK_SDHC2_IPG_GATE] = imx_clk_gate("sdhc2_ipg_gate", "ipg", CCM_PCCR0, 4); + clk[IMX27_CLK_SDHC1_IPG_GATE] = imx_clk_gate("sdhc1_ipg_gate", "ipg", CCM_PCCR0, 5); + clk[IMX27_CLK_SCC_IPG_GATE] = imx_clk_gate("scc_ipg_gate", "ipg", CCM_PCCR0, 6); + clk[IMX27_CLK_SAHARA_IPG_GATE] = imx_clk_gate("sahara_ipg_gate", "ipg", CCM_PCCR0, 7); + clk[IMX27_CLK_RTIC_IPG_GATE] = imx_clk_gate("rtic_ipg_gate", "ipg", CCM_PCCR0, 8); + clk[IMX27_CLK_RTC_IPG_GATE] = imx_clk_gate("rtc_ipg_gate", "ipg", CCM_PCCR0, 9); + clk[IMX27_CLK_PWM_IPG_GATE] = imx_clk_gate("pwm_ipg_gate", "ipg", CCM_PCCR0, 11); + clk[IMX27_CLK_OWIRE_IPG_GATE] = imx_clk_gate("owire_ipg_gate", "ipg", CCM_PCCR0, 12); + clk[IMX27_CLK_MSHC_IPG_GATE] = imx_clk_gate("mshc_ipg_gate", "ipg", CCM_PCCR0, 13); + clk[IMX27_CLK_LCDC_IPG_GATE] = imx_clk_gate("lcdc_ipg_gate", "ipg", CCM_PCCR0, 14); + clk[IMX27_CLK_KPP_IPG_GATE] = imx_clk_gate("kpp_ipg_gate", "ipg", CCM_PCCR0, 15); + clk[IMX27_CLK_IIM_IPG_GATE] = imx_clk_gate("iim_ipg_gate", "ipg", CCM_PCCR0, 16); + clk[IMX27_CLK_I2C2_IPG_GATE] = imx_clk_gate("i2c2_ipg_gate", "ipg", CCM_PCCR0, 17); + clk[IMX27_CLK_I2C1_IPG_GATE] = imx_clk_gate("i2c1_ipg_gate", "ipg", CCM_PCCR0, 18); + clk[IMX27_CLK_GPT6_IPG_GATE] = imx_clk_gate("gpt6_ipg_gate", "ipg", CCM_PCCR0, 19); + clk[IMX27_CLK_GPT5_IPG_GATE] = imx_clk_gate("gpt5_ipg_gate", "ipg", CCM_PCCR0, 20); + clk[IMX27_CLK_GPT4_IPG_GATE] = imx_clk_gate("gpt4_ipg_gate", "ipg", CCM_PCCR0, 21); + clk[IMX27_CLK_GPT3_IPG_GATE] = imx_clk_gate("gpt3_ipg_gate", "ipg", CCM_PCCR0, 22); + clk[IMX27_CLK_GPT2_IPG_GATE] = imx_clk_gate("gpt2_ipg_gate", "ipg", CCM_PCCR0, 23); + clk[IMX27_CLK_GPT1_IPG_GATE] = imx_clk_gate("gpt1_ipg_gate", "ipg", CCM_PCCR0, 24); + clk[IMX27_CLK_GPIO_IPG_GATE] = imx_clk_gate("gpio_ipg_gate", "ipg", CCM_PCCR0, 25); + clk[IMX27_CLK_FEC_IPG_GATE] = imx_clk_gate("fec_ipg_gate", "ipg", CCM_PCCR0, 26); + clk[IMX27_CLK_EMMA_IPG_GATE] = imx_clk_gate("emma_ipg_gate", "ipg", CCM_PCCR0, 27); + clk[IMX27_CLK_DMA_IPG_GATE] = imx_clk_gate("dma_ipg_gate", "ipg", CCM_PCCR0, 28); + clk[IMX27_CLK_CSPI3_IPG_GATE] = imx_clk_gate("cspi3_ipg_gate", "ipg", CCM_PCCR0, 29); + clk[IMX27_CLK_CSPI2_IPG_GATE] = imx_clk_gate("cspi2_ipg_gate", "ipg", CCM_PCCR0, 30); + clk[IMX27_CLK_CSPI1_IPG_GATE] = imx_clk_gate("cspi1_ipg_gate", "ipg", CCM_PCCR0, 31); + clk[IMX27_CLK_MSHC_BAUD_GATE] = imx_clk_gate("mshc_baud_gate", "mshc_div", CCM_PCCR1, 2); + clk[IMX27_CLK_NFC_BAUD_GATE] = imx_clk_gate("nfc_baud_gate", "nfc_div", CCM_PCCR1, 3); + clk[IMX27_CLK_SSI2_BAUD_GATE] = imx_clk_gate("ssi2_baud_gate", "ssi2_div", CCM_PCCR1, 4); + clk[IMX27_CLK_SSI1_BAUD_GATE] = imx_clk_gate("ssi1_baud_gate", "ssi1_div", CCM_PCCR1, 5); + clk[IMX27_CLK_VPU_BAUD_GATE] = imx_clk_gate("vpu_baud_gate", "vpu_div", CCM_PCCR1, 6); + clk[IMX27_CLK_PER4_GATE] = imx_clk_gate("per4_gate", "per4_div", CCM_PCCR1, 7); + clk[IMX27_CLK_PER3_GATE] = imx_clk_gate("per3_gate", "per3_div", CCM_PCCR1, 8); + clk[IMX27_CLK_PER2_GATE] = imx_clk_gate("per2_gate", "per2_div", CCM_PCCR1, 9); + clk[IMX27_CLK_PER1_GATE] = imx_clk_gate("per1_gate", "per1_div", CCM_PCCR1, 10); + clk[IMX27_CLK_USB_AHB_GATE] = imx_clk_gate("usb_ahb_gate", "ahb", CCM_PCCR1, 11); + clk[IMX27_CLK_SLCDC_AHB_GATE] = imx_clk_gate("slcdc_ahb_gate", "ahb", CCM_PCCR1, 12); + clk[IMX27_CLK_SAHARA_AHB_GATE] = imx_clk_gate("sahara_ahb_gate", "ahb", CCM_PCCR1, 13); + clk[IMX27_CLK_RTIC_AHB_GATE] = imx_clk_gate("rtic_ahb_gate", "ahb", CCM_PCCR1, 14); + clk[IMX27_CLK_LCDC_AHB_GATE] = imx_clk_gate("lcdc_ahb_gate", "ahb", CCM_PCCR1, 15); + clk[IMX27_CLK_VPU_AHB_GATE] = imx_clk_gate("vpu_ahb_gate", "ahb", CCM_PCCR1, 16); + clk[IMX27_CLK_FEC_AHB_GATE] = imx_clk_gate("fec_ahb_gate", "ahb", CCM_PCCR1, 17); + clk[IMX27_CLK_EMMA_AHB_GATE] = imx_clk_gate("emma_ahb_gate", "ahb", CCM_PCCR1, 18); + clk[IMX27_CLK_EMI_AHB_GATE] = imx_clk_gate("emi_ahb_gate", "ahb", CCM_PCCR1, 19); + clk[IMX27_CLK_DMA_AHB_GATE] = imx_clk_gate("dma_ahb_gate", "ahb", CCM_PCCR1, 20); + clk[IMX27_CLK_CSI_AHB_GATE] = imx_clk_gate("csi_ahb_gate", "ahb", CCM_PCCR1, 21); + clk[IMX27_CLK_BROM_AHB_GATE] = imx_clk_gate("brom_ahb_gate", "ahb", CCM_PCCR1, 22); + clk[IMX27_CLK_ATA_AHB_GATE] = imx_clk_gate("ata_ahb_gate", "ahb", CCM_PCCR1, 23); + clk[IMX27_CLK_WDOG_IPG_GATE] = imx_clk_gate("wdog_ipg_gate", "ipg", CCM_PCCR1, 24); + clk[IMX27_CLK_USB_IPG_GATE] = imx_clk_gate("usb_ipg_gate", "ipg", CCM_PCCR1, 25); + clk[IMX27_CLK_UART6_IPG_GATE] = imx_clk_gate("uart6_ipg_gate", "ipg", CCM_PCCR1, 26); + clk[IMX27_CLK_UART5_IPG_GATE] = imx_clk_gate("uart5_ipg_gate", "ipg", CCM_PCCR1, 27); + clk[IMX27_CLK_UART4_IPG_GATE] = imx_clk_gate("uart4_ipg_gate", "ipg", CCM_PCCR1, 28); + clk[IMX27_CLK_UART3_IPG_GATE] = imx_clk_gate("uart3_ipg_gate", "ipg", CCM_PCCR1, 29); + clk[IMX27_CLK_UART2_IPG_GATE] = imx_clk_gate("uart2_ipg_gate", "ipg", CCM_PCCR1, 30); + clk[IMX27_CLK_UART1_IPG_GATE] = imx_clk_gate("uart1_ipg_gate", "ipg", CCM_PCCR1, 31); imx_check_clocks(clk, ARRAY_SIZE(clk)); - clk_register_clkdev(clk[cpu_div], NULL, "cpu0"); + clk_register_clkdev(clk[IMX27_CLK_CPU_DIV], NULL, "cpu0"); - clk_prepare_enable(clk[emi_ahb_gate]); + clk_prepare_enable(clk[IMX27_CLK_EMI_AHB_GATE]); imx_print_silicon_rev("i.MX27", mx27_revision()); } @@ -214,67 +191,67 @@ int __init mx27_clocks_init(unsigned long fref) _mx27_clocks_init(fref); - clk_register_clkdev(clk[uart1_ipg_gate], "ipg", "imx21-uart.0"); - clk_register_clkdev(clk[per1_gate], "per", "imx21-uart.0"); - clk_register_clkdev(clk[uart2_ipg_gate], "ipg", "imx21-uart.1"); - clk_register_clkdev(clk[per1_gate], "per", "imx21-uart.1"); - clk_register_clkdev(clk[uart3_ipg_gate], "ipg", "imx21-uart.2"); - clk_register_clkdev(clk[per1_gate], "per", "imx21-uart.2"); - clk_register_clkdev(clk[uart4_ipg_gate], "ipg", "imx21-uart.3"); - clk_register_clkdev(clk[per1_gate], "per", "imx21-uart.3"); - clk_register_clkdev(clk[uart5_ipg_gate], "ipg", "imx21-uart.4"); - clk_register_clkdev(clk[per1_gate], "per", "imx21-uart.4"); - clk_register_clkdev(clk[uart6_ipg_gate], "ipg", "imx21-uart.5"); - clk_register_clkdev(clk[per1_gate], "per", "imx21-uart.5"); - clk_register_clkdev(clk[gpt1_ipg_gate], "ipg", "imx-gpt.0"); - clk_register_clkdev(clk[per1_gate], "per", "imx-gpt.0"); - clk_register_clkdev(clk[per2_gate], "per", "imx21-mmc.0"); - clk_register_clkdev(clk[sdhc1_ipg_gate], "ipg", "imx21-mmc.0"); - clk_register_clkdev(clk[per2_gate], "per", "imx21-mmc.1"); - clk_register_clkdev(clk[sdhc2_ipg_gate], "ipg", "imx21-mmc.1"); - clk_register_clkdev(clk[per2_gate], "per", "imx21-mmc.2"); - clk_register_clkdev(clk[sdhc2_ipg_gate], "ipg", "imx21-mmc.2"); - clk_register_clkdev(clk[per2_gate], "per", "imx27-cspi.0"); - clk_register_clkdev(clk[cspi1_ipg_gate], "ipg", "imx27-cspi.0"); - clk_register_clkdev(clk[per2_gate], "per", "imx27-cspi.1"); - clk_register_clkdev(clk[cspi2_ipg_gate], "ipg", "imx27-cspi.1"); - clk_register_clkdev(clk[per2_gate], "per", "imx27-cspi.2"); - clk_register_clkdev(clk[cspi3_ipg_gate], "ipg", "imx27-cspi.2"); - clk_register_clkdev(clk[per3_gate], "per", "imx21-fb.0"); - clk_register_clkdev(clk[lcdc_ipg_gate], "ipg", "imx21-fb.0"); - clk_register_clkdev(clk[lcdc_ahb_gate], "ahb", "imx21-fb.0"); - clk_register_clkdev(clk[csi_ahb_gate], "ahb", "imx27-camera.0"); - clk_register_clkdev(clk[per4_gate], "per", "imx27-camera.0"); - clk_register_clkdev(clk[usb_div], "per", "imx-udc-mx27"); - clk_register_clkdev(clk[usb_ipg_gate], "ipg", "imx-udc-mx27"); - clk_register_clkdev(clk[usb_ahb_gate], "ahb", "imx-udc-mx27"); - clk_register_clkdev(clk[usb_div], "per", "mxc-ehci.0"); - clk_register_clkdev(clk[usb_ipg_gate], "ipg", "mxc-ehci.0"); - clk_register_clkdev(clk[usb_ahb_gate], "ahb", "mxc-ehci.0"); - clk_register_clkdev(clk[usb_div], "per", "mxc-ehci.1"); - clk_register_clkdev(clk[usb_ipg_gate], "ipg", "mxc-ehci.1"); - clk_register_clkdev(clk[usb_ahb_gate], "ahb", "mxc-ehci.1"); - clk_register_clkdev(clk[usb_div], "per", "mxc-ehci.2"); - clk_register_clkdev(clk[usb_ipg_gate], "ipg", "mxc-ehci.2"); - clk_register_clkdev(clk[usb_ahb_gate], "ahb", "mxc-ehci.2"); - clk_register_clkdev(clk[ssi1_ipg_gate], NULL, "imx-ssi.0"); - clk_register_clkdev(clk[ssi2_ipg_gate], NULL, "imx-ssi.1"); - clk_register_clkdev(clk[nfc_baud_gate], NULL, "imx27-nand.0"); - clk_register_clkdev(clk[vpu_baud_gate], "per", "coda-imx27.0"); - clk_register_clkdev(clk[vpu_ahb_gate], "ahb", "coda-imx27.0"); - clk_register_clkdev(clk[dma_ahb_gate], "ahb", "imx27-dma"); - clk_register_clkdev(clk[dma_ipg_gate], "ipg", "imx27-dma"); - clk_register_clkdev(clk[fec_ipg_gate], "ipg", "imx27-fec.0"); - clk_register_clkdev(clk[fec_ahb_gate], "ahb", "imx27-fec.0"); - clk_register_clkdev(clk[wdog_ipg_gate], NULL, "imx2-wdt.0"); - clk_register_clkdev(clk[i2c1_ipg_gate], NULL, "imx21-i2c.0"); - clk_register_clkdev(clk[i2c2_ipg_gate], NULL, "imx21-i2c.1"); - clk_register_clkdev(clk[owire_ipg_gate], NULL, "mxc_w1.0"); - clk_register_clkdev(clk[kpp_ipg_gate], NULL, "imx-keypad"); - clk_register_clkdev(clk[emma_ahb_gate], "emma-ahb", "imx27-camera.0"); - clk_register_clkdev(clk[emma_ipg_gate], "emma-ipg", "imx27-camera.0"); - clk_register_clkdev(clk[emma_ahb_gate], "ahb", "m2m-emmaprp.0"); - clk_register_clkdev(clk[emma_ipg_gate], "ipg", "m2m-emmaprp.0"); + clk_register_clkdev(clk[IMX27_CLK_UART1_IPG_GATE], "ipg", "imx21-uart.0"); + clk_register_clkdev(clk[IMX27_CLK_PER1_GATE], "per", "imx21-uart.0"); + clk_register_clkdev(clk[IMX27_CLK_UART2_IPG_GATE], "ipg", "imx21-uart.1"); + clk_register_clkdev(clk[IMX27_CLK_PER1_GATE], "per", "imx21-uart.1"); + clk_register_clkdev(clk[IMX27_CLK_UART3_IPG_GATE], "ipg", "imx21-uart.2"); + clk_register_clkdev(clk[IMX27_CLK_PER1_GATE], "per", "imx21-uart.2"); + clk_register_clkdev(clk[IMX27_CLK_UART4_IPG_GATE], "ipg", "imx21-uart.3"); + clk_register_clkdev(clk[IMX27_CLK_PER1_GATE], "per", "imx21-uart.3"); + clk_register_clkdev(clk[IMX27_CLK_UART5_IPG_GATE], "ipg", "imx21-uart.4"); + clk_register_clkdev(clk[IMX27_CLK_PER1_GATE], "per", "imx21-uart.4"); + clk_register_clkdev(clk[IMX27_CLK_UART6_IPG_GATE], "ipg", "imx21-uart.5"); + clk_register_clkdev(clk[IMX27_CLK_PER1_GATE], "per", "imx21-uart.5"); + clk_register_clkdev(clk[IMX27_CLK_GPT1_IPG_GATE], "ipg", "imx-gpt.0"); + clk_register_clkdev(clk[IMX27_CLK_PER1_GATE], "per", "imx-gpt.0"); + clk_register_clkdev(clk[IMX27_CLK_PER2_GATE], "per", "imx21-mmc.0"); + clk_register_clkdev(clk[IMX27_CLK_SDHC1_IPG_GATE], "ipg", "imx21-mmc.0"); + clk_register_clkdev(clk[IMX27_CLK_PER2_GATE], "per", "imx21-mmc.1"); + clk_register_clkdev(clk[IMX27_CLK_SDHC2_IPG_GATE], "ipg", "imx21-mmc.1"); + clk_register_clkdev(clk[IMX27_CLK_PER2_GATE], "per", "imx21-mmc.2"); + clk_register_clkdev(clk[IMX27_CLK_SDHC2_IPG_GATE], "ipg", "imx21-mmc.2"); + clk_register_clkdev(clk[IMX27_CLK_PER2_GATE], "per", "imx27-cspi.0"); + clk_register_clkdev(clk[IMX27_CLK_CSPI1_IPG_GATE], "ipg", "imx27-cspi.0"); + clk_register_clkdev(clk[IMX27_CLK_PER2_GATE], "per", "imx27-cspi.1"); + clk_register_clkdev(clk[IMX27_CLK_CSPI2_IPG_GATE], "ipg", "imx27-cspi.1"); + clk_register_clkdev(clk[IMX27_CLK_PER2_GATE], "per", "imx27-cspi.2"); + clk_register_clkdev(clk[IMX27_CLK_CSPI3_IPG_GATE], "ipg", "imx27-cspi.2"); + clk_register_clkdev(clk[IMX27_CLK_PER3_GATE], "per", "imx21-fb.0"); + clk_register_clkdev(clk[IMX27_CLK_LCDC_IPG_GATE], "ipg", "imx21-fb.0"); + clk_register_clkdev(clk[IMX27_CLK_LCDC_AHB_GATE], "ahb", "imx21-fb.0"); + clk_register_clkdev(clk[IMX27_CLK_CSI_AHB_GATE], "ahb", "imx27-camera.0"); + clk_register_clkdev(clk[IMX27_CLK_PER4_GATE], "per", "imx27-camera.0"); + clk_register_clkdev(clk[IMX27_CLK_USB_DIV], "per", "imx-udc-mx27"); + clk_register_clkdev(clk[IMX27_CLK_USB_IPG_GATE], "ipg", "imx-udc-mx27"); + clk_register_clkdev(clk[IMX27_CLK_USB_AHB_GATE], "ahb", "imx-udc-mx27"); + clk_register_clkdev(clk[IMX27_CLK_USB_DIV], "per", "mxc-ehci.0"); + clk_register_clkdev(clk[IMX27_CLK_USB_IPG_GATE], "ipg", "mxc-ehci.0"); + clk_register_clkdev(clk[IMX27_CLK_USB_AHB_GATE], "ahb", "mxc-ehci.0"); + clk_register_clkdev(clk[IMX27_CLK_USB_DIV], "per", "mxc-ehci.1"); + clk_register_clkdev(clk[IMX27_CLK_USB_IPG_GATE], "ipg", "mxc-ehci.1"); + clk_register_clkdev(clk[IMX27_CLK_USB_AHB_GATE], "ahb", "mxc-ehci.1"); + clk_register_clkdev(clk[IMX27_CLK_USB_DIV], "per", "mxc-ehci.2"); + clk_register_clkdev(clk[IMX27_CLK_USB_IPG_GATE], "ipg", "mxc-ehci.2"); + clk_register_clkdev(clk[IMX27_CLK_USB_AHB_GATE], "ahb", "mxc-ehci.2"); + clk_register_clkdev(clk[IMX27_CLK_SSI1_IPG_GATE], NULL, "imx-ssi.0"); + clk_register_clkdev(clk[IMX27_CLK_SSI2_IPG_GATE], NULL, "imx-ssi.1"); + clk_register_clkdev(clk[IMX27_CLK_NFC_BAUD_GATE], NULL, "imx27-nand.0"); + clk_register_clkdev(clk[IMX27_CLK_VPU_BAUD_GATE], "per", "coda-imx27.0"); + clk_register_clkdev(clk[IMX27_CLK_VPU_AHB_GATE], "ahb", "coda-imx27.0"); + clk_register_clkdev(clk[IMX27_CLK_DMA_AHB_GATE], "ahb", "imx27-dma"); + clk_register_clkdev(clk[IMX27_CLK_DMA_IPG_GATE], "ipg", "imx27-dma"); + clk_register_clkdev(clk[IMX27_CLK_FEC_IPG_GATE], "ipg", "imx27-fec.0"); + clk_register_clkdev(clk[IMX27_CLK_FEC_AHB_GATE], "ahb", "imx27-fec.0"); + clk_register_clkdev(clk[IMX27_CLK_WDOG_IPG_GATE], NULL, "imx2-wdt.0"); + clk_register_clkdev(clk[IMX27_CLK_I2C1_IPG_GATE], NULL, "imx21-i2c.0"); + clk_register_clkdev(clk[IMX27_CLK_I2C2_IPG_GATE], NULL, "imx21-i2c.1"); + clk_register_clkdev(clk[IMX27_CLK_OWIRE_IPG_GATE], NULL, "mxc_w1.0"); + clk_register_clkdev(clk[IMX27_CLK_KPP_IPG_GATE], NULL, "imx-keypad"); + clk_register_clkdev(clk[IMX27_CLK_EMMA_AHB_GATE], "emma-ahb", "imx27-camera.0"); + clk_register_clkdev(clk[IMX27_CLK_EMMA_IPG_GATE], "emma-ipg", "imx27-camera.0"); + clk_register_clkdev(clk[IMX27_CLK_EMMA_AHB_GATE], "ahb", "m2m-emmaprp.0"); + clk_register_clkdev(clk[IMX27_CLK_EMMA_IPG_GATE], "ipg", "m2m-emmaprp.0"); mxc_timer_init(MX27_IO_ADDRESS(MX27_GPT1_BASE_ADDR), MX27_INT_GPT1); diff --git a/include/dt-bindings/clock/imx27-clock.h b/include/dt-bindings/clock/imx27-clock.h new file mode 100644 index 000000000000..6b642d4d94c0 --- /dev/null +++ b/include/dt-bindings/clock/imx27-clock.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2014 Alexander Shiyan + * + * 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 __DT_BINDINGS_CLOCK_IMX27_H +#define __DT_BINDINGS_CLOCK_IMX27_H + +#define IMX27_CLK_DUMMY 0 +#define IMX27_CLK_CKIH 1 +#define IMX27_CLK_CKIL 2 +#define IMX27_CLK_MPLL 3 +#define IMX27_CLK_SPLL 4 +#define IMX27_CLK_MPLL_MAIN2 5 +#define IMX27_CLK_AHB 6 +#define IMX27_CLK_IPG 7 +#define IMX27_CLK_NFC_DIV 8 +#define IMX27_CLK_PER1_DIV 9 +#define IMX27_CLK_PER2_DIV 10 +#define IMX27_CLK_PER3_DIV 11 +#define IMX27_CLK_PER4_DIV 12 +#define IMX27_CLK_VPU_SEL 13 +#define IMX27_CLK_VPU_DIV 14 +#define IMX27_CLK_USB_DIV 15 +#define IMX27_CLK_CPU_SEL 16 +#define IMX27_CLK_CLKO_SEL 17 +#define IMX27_CLK_CPU_DIV 18 +#define IMX27_CLK_CLKO_DIV 19 +#define IMX27_CLK_SSI1_SEL 20 +#define IMX27_CLK_SSI2_SEL 21 +#define IMX27_CLK_SSI1_DIV 22 +#define IMX27_CLK_SSI2_DIV 23 +#define IMX27_CLK_CLKO_EN 24 +#define IMX27_CLK_SSI2_IPG_GATE 25 +#define IMX27_CLK_SSI1_IPG_GATE 26 +#define IMX27_CLK_SLCDC_IPG_GATE 27 +#define IMX27_CLK_SDHC3_IPG_GATE 28 +#define IMX27_CLK_SDHC2_IPG_GATE 29 +#define IMX27_CLK_SDHC1_IPG_GATE 30 +#define IMX27_CLK_SCC_IPG_GATE 31 +#define IMX27_CLK_SAHARA_IPG_GATE 32 +#define IMX27_CLK_RTC_IPG_GATE 33 +#define IMX27_CLK_PWM_IPG_GATE 34 +#define IMX27_CLK_OWIRE_IPG_GATE 35 +#define IMX27_CLK_LCDC_IPG_GATE 36 +#define IMX27_CLK_KPP_IPG_GATE 37 +#define IMX27_CLK_IIM_IPG_GATE 38 +#define IMX27_CLK_I2C2_IPG_GATE 39 +#define IMX27_CLK_I2C1_IPG_GATE 40 +#define IMX27_CLK_GPT6_IPG_GATE 41 +#define IMX27_CLK_GPT5_IPG_GATE 42 +#define IMX27_CLK_GPT4_IPG_GATE 43 +#define IMX27_CLK_GPT3_IPG_GATE 44 +#define IMX27_CLK_GPT2_IPG_GATE 45 +#define IMX27_CLK_GPT1_IPG_GATE 46 +#define IMX27_CLK_GPIO_IPG_GATE 47 +#define IMX27_CLK_FEC_IPG_GATE 48 +#define IMX27_CLK_EMMA_IPG_GATE 49 +#define IMX27_CLK_DMA_IPG_GATE 50 +#define IMX27_CLK_CSPI3_IPG_GATE 51 +#define IMX27_CLK_CSPI2_IPG_GATE 52 +#define IMX27_CLK_CSPI1_IPG_GATE 53 +#define IMX27_CLK_NFC_BAUD_GATE 54 +#define IMX27_CLK_SSI2_BAUD_GATE 55 +#define IMX27_CLK_SSI1_BAUD_GATE 56 +#define IMX27_CLK_VPU_BAUD_GATE 57 +#define IMX27_CLK_PER4_GATE 58 +#define IMX27_CLK_PER3_GATE 59 +#define IMX27_CLK_PER2_GATE 60 +#define IMX27_CLK_PER1_GATE 61 +#define IMX27_CLK_USB_AHB_GATE 62 +#define IMX27_CLK_SLCDC_AHB_GATE 63 +#define IMX27_CLK_SAHARA_AHB_GATE 64 +#define IMX27_CLK_LCDC_AHB_GATE 65 +#define IMX27_CLK_VPU_AHB_GATE 66 +#define IMX27_CLK_FEC_AHB_GATE 67 +#define IMX27_CLK_EMMA_AHB_GATE 68 +#define IMX27_CLK_EMI_AHB_GATE 69 +#define IMX27_CLK_DMA_AHB_GATE 70 +#define IMX27_CLK_CSI_AHB_GATE 71 +#define IMX27_CLK_BROM_AHB_GATE 72 +#define IMX27_CLK_ATA_AHB_GATE 73 +#define IMX27_CLK_WDOG_IPG_GATE 74 +#define IMX27_CLK_USB_IPG_GATE 75 +#define IMX27_CLK_UART6_IPG_GATE 76 +#define IMX27_CLK_UART5_IPG_GATE 77 +#define IMX27_CLK_UART4_IPG_GATE 78 +#define IMX27_CLK_UART3_IPG_GATE 79 +#define IMX27_CLK_UART2_IPG_GATE 80 +#define IMX27_CLK_UART1_IPG_GATE 81 +#define IMX27_CLK_CKIH_DIV1P5 82 +#define IMX27_CLK_FPM 83 +#define IMX27_CLK_MPLL_OSC_SEL 84 +#define IMX27_CLK_MPLL_SEL 85 +#define IMX27_CLK_SPLL_GATE 86 +#define IMX27_CLK_MSHC_DIV 87 +#define IMX27_CLK_RTIC_IPG_GATE 88 +#define IMX27_CLK_MSHC_IPG_GATE 89 +#define IMX27_CLK_RTIC_AHB_GATE 90 +#define IMX27_CLK_MSHC_BAUD_GATE 91 +#define IMX27_CLK_MAX 92 + +#endif -- cgit v1.2.3 From b4de531716eb42fd074b28118bc51cef28f06e9a Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 5 Jul 2014 09:36:09 +0400 Subject: ARM: i.MX27 clk: Add 26 MHz oscillator circuit clock gate This patch adds missing 26 MHz oscillator circuit clock gate support. Signed-off-by: Alexander Shiyan Signed-off-by: Shawn Guo --- arch/arm/mach-imx/clk-imx27.c | 11 ++++++----- include/dt-bindings/clock/imx27-clock.h | 3 ++- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-imx/clk-imx27.c b/arch/arm/mach-imx/clk-imx27.c index fcfb81b881af..07bc32cbcdd9 100644 --- a/arch/arm/mach-imx/clk-imx27.c +++ b/arch/arm/mach-imx/clk-imx27.c @@ -27,10 +27,10 @@ static void __iomem *ccm __initdata; static const char *vpu_sel_clks[] = { "spll", "mpll_main2", }; static const char *cpu_sel_clks[] = { "mpll_main2", "mpll", }; static const char *mpll_sel_clks[] = { "fpm", "mpll_osc_sel", }; -static const char *mpll_osc_sel_clks[] = { "ckih", "ckih_div1p5", }; +static const char *mpll_osc_sel_clks[] = { "ckih_gate", "ckih_div1p5", }; static const char *clko_sel_clks[] = { - "ckil", "fpm", "ckih", "ckih", - "ckih", "mpll", "spll", "cpu_div", + "ckil", "fpm", "ckih_gate", "ckih_gate", + "ckih_gate", "mpll", "spll", "cpu_div", "ahb", "ipg", "per1_div", "per2_div", "per3_div", "per4_div", "ssi1_div", "ssi2_div", "nfc_div", "mshc_div", "vpu_div", "60m", @@ -50,11 +50,12 @@ static void __init _mx27_clocks_init(unsigned long fref) clk[IMX27_CLK_CKIH] = imx_clk_fixed("ckih", fref); clk[IMX27_CLK_CKIL] = imx_clk_fixed("ckil", 32768); clk[IMX27_CLK_FPM] = imx_clk_fixed_factor("fpm", "ckil", 1024, 1); - clk[IMX27_CLK_CKIH_DIV1P5] = imx_clk_fixed_factor("ckih_div1p5", "ckih", 2, 3); + clk[IMX27_CLK_CKIH_DIV1P5] = imx_clk_fixed_factor("ckih_div1p5", "ckih_gate", 2, 3); + clk[IMX27_CLK_CKIH_GATE] = imx_clk_gate_dis("ckih_gate", "ckih", CCM_CSCR, 3); clk[IMX27_CLK_MPLL_OSC_SEL] = imx_clk_mux("mpll_osc_sel", CCM_CSCR, 4, 1, mpll_osc_sel_clks, ARRAY_SIZE(mpll_osc_sel_clks)); clk[IMX27_CLK_MPLL_SEL] = imx_clk_mux("mpll_sel", CCM_CSCR, 16, 1, mpll_sel_clks, ARRAY_SIZE(mpll_sel_clks)); clk[IMX27_CLK_MPLL] = imx_clk_pllv1("mpll", "mpll_sel", CCM_MPCTL0); - clk[IMX27_CLK_SPLL] = imx_clk_pllv1("spll", "ckih", CCM_SPCTL0); + clk[IMX27_CLK_SPLL] = imx_clk_pllv1("spll", "ckih_gate", CCM_SPCTL0); clk[IMX27_CLK_SPLL_GATE] = imx_clk_gate("spll_gate", "spll", CCM_CSCR, 1); clk[IMX27_CLK_MPLL_MAIN2] = imx_clk_fixed_factor("mpll_main2", "mpll", 2, 3); diff --git a/include/dt-bindings/clock/imx27-clock.h b/include/dt-bindings/clock/imx27-clock.h index 6b642d4d94c0..148b053e54ec 100644 --- a/include/dt-bindings/clock/imx27-clock.h +++ b/include/dt-bindings/clock/imx27-clock.h @@ -102,6 +102,7 @@ #define IMX27_CLK_MSHC_IPG_GATE 89 #define IMX27_CLK_RTIC_AHB_GATE 90 #define IMX27_CLK_MSHC_BAUD_GATE 91 -#define IMX27_CLK_MAX 92 +#define IMX27_CLK_CKIH_GATE 92 +#define IMX27_CLK_MAX 93 #endif -- cgit v1.2.3 From 4349c4298f676815bf7ad146cf37e76843054783 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Tue, 15 Jul 2014 14:56:19 +0200 Subject: ARM: imx: clk-vf610: fix FlexCAN clock gating Extend the clock control for FlexCAN with the second gate which enable the clocks in the Clock Divider (CCM_CSCDR2) register too. Signed-off-by: Stefan Agner Signed-off-by: Shawn Guo --- arch/arm/mach-imx/clk-vf610.c | 6 ++++-- include/dt-bindings/clock/vf610-clock.h | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-imx/clk-vf610.c b/arch/arm/mach-imx/clk-vf610.c index 2435bc4612eb..f60d6d569ce3 100644 --- a/arch/arm/mach-imx/clk-vf610.c +++ b/arch/arm/mach-imx/clk-vf610.c @@ -295,8 +295,10 @@ static void __init vf610_clocks_init(struct device_node *ccm_node) clk[VF610_CLK_ASRC] = imx_clk_gate2("asrc", "ipg_bus", CCM_CCGR4, CCM_CCGRx_CGn(1)); - clk[VF610_CLK_FLEXCAN0] = imx_clk_gate2("flexcan0", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(0)); - clk[VF610_CLK_FLEXCAN1] = imx_clk_gate2("flexcan1", "ipg_bus", CCM_CCGR9, CCM_CCGRx_CGn(4)); + clk[VF610_CLK_FLEXCAN0_EN] = imx_clk_gate("flexcan0_en", "ipg_bus", CCM_CSCDR2, 11); + clk[VF610_CLK_FLEXCAN0] = imx_clk_gate2("flexcan0", "flexcan0_en", CCM_CCGR0, CCM_CCGRx_CGn(0)); + clk[VF610_CLK_FLEXCAN1_EN] = imx_clk_gate("flexcan1_en", "ipg_bus", CCM_CSCDR2, 12); + clk[VF610_CLK_FLEXCAN1] = imx_clk_gate2("flexcan1", "flexcan1_en", CCM_CCGR9, CCM_CCGRx_CGn(4)); clk[VF610_CLK_DMAMUX0] = imx_clk_gate2("dmamux0", "platform_bus", CCM_CCGR0, CCM_CCGRx_CGn(4)); clk[VF610_CLK_DMAMUX1] = imx_clk_gate2("dmamux1", "platform_bus", CCM_CCGR0, CCM_CCGRx_CGn(5)); diff --git a/include/dt-bindings/clock/vf610-clock.h b/include/dt-bindings/clock/vf610-clock.h index a91602951d3d..00953d9484cb 100644 --- a/include/dt-bindings/clock/vf610-clock.h +++ b/include/dt-bindings/clock/vf610-clock.h @@ -164,6 +164,8 @@ #define VF610_CLK_DMAMUX1 151 #define VF610_CLK_DMAMUX2 152 #define VF610_CLK_DMAMUX3 153 -#define VF610_CLK_END 154 +#define VF610_CLK_FLEXCAN0_EN 154 +#define VF610_CLK_FLEXCAN1_EN 155 +#define VF610_CLK_END 156 #endif /* __DT_BINDINGS_CLOCK_VF610_H */ -- cgit v1.2.3 From 5442e9fbd7c23172a1c9bc736629cd123a9923f0 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Wed, 16 Jul 2014 01:54:54 +0400 Subject: timerfd: Implement timerfd_ioctl method to restore timerfd_ctx::ticks, v3 The read() of timerfd files allows to fetch the number of timer ticks while there is no way to set it back from userspace. To restore the timer's state as it was at checkpoint moment we need a path to bring @ticks back. Initially I thought about writing ticks back via write() interface but it seems such API is somehow obscure. Instead implement timerfd_ioctl() method with TFD_IOC_SET_TICKS command which allows to adjust @ticks into non-zero value waking up the waiters. I wrapped code with CONFIG_CHECKPOINT_RESTORE which can be dropped off if there users except c/r camp appear. v2 (by akpm@): - Use define timerfd_ioctl NULL for non c/r config v3: - Use copy_from_user for @ticks fetching since not all arch support get_user for 8 byte argument Signed-off-by: Cyrill Gorcunov Cc: Andrew Morton Cc: Michael Kerrisk Cc: Andrey Vagin Cc: Arnd Bergmann Cc: Christopher Covington Cc: Pavel Emelyanov Cc: Vladimir Davydov Link: http://lkml.kernel.org/r/20140715215703.285617923@openvz.org Signed-off-by: Thomas Gleixner --- fs/timerfd.c | 37 +++++++++++++++++++++++++++++++++++++ include/linux/timerfd.h | 5 +++++ 2 files changed, 42 insertions(+) (limited to 'include') diff --git a/fs/timerfd.c b/fs/timerfd.c index 77183f047f65..709603cac9e6 100644 --- a/fs/timerfd.c +++ b/fs/timerfd.c @@ -315,12 +315,49 @@ static int timerfd_show(struct seq_file *m, struct file *file) #define timerfd_show NULL #endif +#ifdef CONFIG_CHECKPOINT_RESTORE +static long timerfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct timerfd_ctx *ctx = file->private_data; + int ret = 0; + + switch (cmd) { + case TFD_IOC_SET_TICKS: { + u64 ticks; + + if (copy_from_user(&ticks, (u64 __user *)arg, sizeof(ticks))) + return -EFAULT; + if (!ticks) + return -EINVAL; + + spin_lock_irq(&ctx->wqh.lock); + if (!timerfd_canceled(ctx)) { + ctx->ticks = ticks; + if (ticks) + wake_up_locked(&ctx->wqh); + } else + ret = -ECANCELED; + spin_unlock_irq(&ctx->wqh.lock); + break; + } + default: + ret = -ENOTTY; + break; + } + + return ret; +} +#else +#define timerfd_ioctl NULL +#endif + static const struct file_operations timerfd_fops = { .release = timerfd_release, .poll = timerfd_poll, .read = timerfd_read, .llseek = noop_llseek, .show_fdinfo = timerfd_show, + .unlocked_ioctl = timerfd_ioctl, }; static int timerfd_fget(int fd, struct fd *p) diff --git a/include/linux/timerfd.h b/include/linux/timerfd.h index d3b57fa12225..bd36ce431e32 100644 --- a/include/linux/timerfd.h +++ b/include/linux/timerfd.h @@ -11,6 +11,9 @@ /* For O_CLOEXEC and O_NONBLOCK */ #include +/* For _IO helpers */ +#include + /* * CAREFUL: Check include/asm-generic/fcntl.h when defining * new flags, since they might collide with O_* ones. We want @@ -29,4 +32,6 @@ /* Flags for timerfd_settime. */ #define TFD_SETTIME_FLAGS (TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET) +#define TFD_IOC_SET_TICKS _IOW('T', 0, u64) + #endif /* _LINUX_TIMERFD_H */ -- cgit v1.2.3 From 3c45ddf823d679a820adddd53b52c6699c9a05ac Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 16 Jul 2014 15:38:32 -0400 Subject: svcrdma: Select NFSv4.1 backchannel transport based on forward channel The current code always selects XPRT_TRANSPORT_BC_TCP for the back channel, even when the forward channel was not TCP (eg, RDMA). When a 4.1 mount is attempted with RDMA, the server panics in the TCP BC code when trying to send CB_NULL. Instead, construct the transport protocol number from the forward channel transport or'd with XPRT_TRANSPORT_BC. Transports that do not support bi-directional RPC will not have registered a "BC" transport, causing create_backchannel_client() to fail immediately. Fixes: https://bugzilla.linux-nfs.org/show_bug.cgi?id=265 Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4callback.c | 3 ++- include/linux/sunrpc/svc_xprt.h | 1 + net/sunrpc/svcsock.c | 2 ++ net/sunrpc/xprt.c | 2 +- net/sunrpc/xprtrdma/svc_rdma_transport.c | 1 + 5 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index a88a93e09d69..564d72304613 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -689,7 +689,8 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c clp->cl_cb_session = ses; args.bc_xprt = conn->cb_xprt; args.prognumber = clp->cl_cb_session->se_cb_prog; - args.protocol = XPRT_TRANSPORT_BC_TCP; + args.protocol = conn->cb_xprt->xpt_class->xcl_ident | + XPRT_TRANSPORT_BC; args.authflavor = ses->se_cb_sec.flavor; } /* Create RPC client */ diff --git a/include/linux/sunrpc/svc_xprt.h b/include/linux/sunrpc/svc_xprt.h index 7235040a19b2..5d9d6f84b382 100644 --- a/include/linux/sunrpc/svc_xprt.h +++ b/include/linux/sunrpc/svc_xprt.h @@ -33,6 +33,7 @@ struct svc_xprt_class { struct svc_xprt_ops *xcl_ops; struct list_head xcl_list; u32 xcl_max_payload; + int xcl_ident; }; /* diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index b507cd327d9b..b2437ee93657 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -692,6 +692,7 @@ static struct svc_xprt_class svc_udp_class = { .xcl_owner = THIS_MODULE, .xcl_ops = &svc_udp_ops, .xcl_max_payload = RPCSVC_MAXPAYLOAD_UDP, + .xcl_ident = XPRT_TRANSPORT_UDP, }; static void svc_udp_init(struct svc_sock *svsk, struct svc_serv *serv) @@ -1292,6 +1293,7 @@ static struct svc_xprt_class svc_tcp_class = { .xcl_owner = THIS_MODULE, .xcl_ops = &svc_tcp_ops, .xcl_max_payload = RPCSVC_MAXPAYLOAD_TCP, + .xcl_ident = XPRT_TRANSPORT_TCP, }; void svc_init_xprt_sock(void) diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index c3b2b3369e52..51c63165073c 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -1306,7 +1306,7 @@ struct rpc_xprt *xprt_create_transport(struct xprt_create *args) } } spin_unlock(&xprt_list_lock); - printk(KERN_ERR "RPC: transport (%d) not supported\n", args->ident); + dprintk("RPC: transport (%d) not supported\n", args->ident); return ERR_PTR(-EIO); found: diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index e7323fbbd348..06a5d9235107 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -92,6 +92,7 @@ struct svc_xprt_class svc_rdma_class = { .xcl_owner = THIS_MODULE, .xcl_ops = &svc_rdma_ops, .xcl_max_payload = RPCSVC_MAXPAYLOAD_TCP, + .xcl_ident = XPRT_TRANSPORT_RDMA, }; struct svc_rdma_op_ctxt *svc_rdma_get_context(struct svcxprt_rdma *xprt) -- cgit v1.2.3 From 6ad59343ecd72dd3f83c4db3bcddbb0beabb4c4c Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Tue, 15 Jul 2014 16:18:57 +0200 Subject: ssb: extract power info from SPROM revs 4 and 5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is needed to properly handle early 802.11n devices like BCM4321. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/ssb/pci.c | 45 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/ssb/ssb_regs.h | 37 ++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) (limited to 'include') diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c index 6318364be590..0f28c08fcb3c 100644 --- a/drivers/ssb/pci.c +++ b/drivers/ssb/pci.c @@ -470,7 +470,15 @@ static void sprom_extract_r458(struct ssb_sprom *out, const u16 *in) static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in) { + static const u16 pwr_info_offset[] = { + SSB_SPROM4_PWR_INFO_CORE0, SSB_SPROM4_PWR_INFO_CORE1, + SSB_SPROM4_PWR_INFO_CORE2, SSB_SPROM4_PWR_INFO_CORE3 + }; u16 il0mac_offset; + int i; + + BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) != + ARRAY_SIZE(out->core_pwr_info)); if (out->revision == 4) il0mac_offset = SSB_SPROM4_IL0MAC; @@ -543,6 +551,43 @@ static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in) SSB_SPROM4_AGAIN3, SSB_SPROM4_AGAIN3_SHIFT); + /* Extract cores power info info */ + for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) { + u16 o = pwr_info_offset[i]; + + SPEX(core_pwr_info[i].itssi_2g, o + SSB_SPROM4_2G_MAXP_ITSSI, + SSB_SPROM4_2G_ITSSI, SSB_SPROM4_2G_ITSSI_SHIFT); + SPEX(core_pwr_info[i].maxpwr_2g, o + SSB_SPROM4_2G_MAXP_ITSSI, + SSB_SPROM4_2G_MAXP, 0); + + SPEX(core_pwr_info[i].pa_2g[0], o + SSB_SPROM4_2G_PA_0, ~0, 0); + SPEX(core_pwr_info[i].pa_2g[1], o + SSB_SPROM4_2G_PA_1, ~0, 0); + SPEX(core_pwr_info[i].pa_2g[2], o + SSB_SPROM4_2G_PA_2, ~0, 0); + SPEX(core_pwr_info[i].pa_2g[3], o + SSB_SPROM4_2G_PA_3, ~0, 0); + + SPEX(core_pwr_info[i].itssi_5g, o + SSB_SPROM4_5G_MAXP_ITSSI, + SSB_SPROM4_5G_ITSSI, SSB_SPROM4_5G_ITSSI_SHIFT); + SPEX(core_pwr_info[i].maxpwr_5g, o + SSB_SPROM4_5G_MAXP_ITSSI, + SSB_SPROM4_5G_MAXP, 0); + SPEX(core_pwr_info[i].maxpwr_5gh, o + SSB_SPROM4_5GHL_MAXP, + SSB_SPROM4_5GH_MAXP, 0); + SPEX(core_pwr_info[i].maxpwr_5gl, o + SSB_SPROM4_5GHL_MAXP, + SSB_SPROM4_5GL_MAXP, SSB_SPROM4_5GL_MAXP_SHIFT); + + SPEX(core_pwr_info[i].pa_5gl[0], o + SSB_SPROM4_5GL_PA_0, ~0, 0); + SPEX(core_pwr_info[i].pa_5gl[1], o + SSB_SPROM4_5GL_PA_1, ~0, 0); + SPEX(core_pwr_info[i].pa_5gl[2], o + SSB_SPROM4_5GL_PA_2, ~0, 0); + SPEX(core_pwr_info[i].pa_5gl[3], o + SSB_SPROM4_5GL_PA_3, ~0, 0); + SPEX(core_pwr_info[i].pa_5g[0], o + SSB_SPROM4_5G_PA_0, ~0, 0); + SPEX(core_pwr_info[i].pa_5g[1], o + SSB_SPROM4_5G_PA_1, ~0, 0); + SPEX(core_pwr_info[i].pa_5g[2], o + SSB_SPROM4_5G_PA_2, ~0, 0); + SPEX(core_pwr_info[i].pa_5g[3], o + SSB_SPROM4_5G_PA_3, ~0, 0); + SPEX(core_pwr_info[i].pa_5gh[0], o + SSB_SPROM4_5GH_PA_0, ~0, 0); + SPEX(core_pwr_info[i].pa_5gh[1], o + SSB_SPROM4_5GH_PA_1, ~0, 0); + SPEX(core_pwr_info[i].pa_5gh[2], o + SSB_SPROM4_5GH_PA_2, ~0, 0); + SPEX(core_pwr_info[i].pa_5gh[3], o + SSB_SPROM4_5GH_PA_3, ~0, 0); + } + sprom_extract_r458(out, in); /* TODO - get remaining rev 4 stuff needed */ diff --git a/include/linux/ssb/ssb_regs.h b/include/linux/ssb/ssb_regs.h index f9f931c89e3e..f7b9100686c3 100644 --- a/include/linux/ssb/ssb_regs.h +++ b/include/linux/ssb/ssb_regs.h @@ -345,6 +345,43 @@ #define SSB_SPROM4_TXPID5GH2_SHIFT 0 #define SSB_SPROM4_TXPID5GH3 0xFF00 #define SSB_SPROM4_TXPID5GH3_SHIFT 8 + +/* There are 4 blocks with power info sharing the same layout */ +#define SSB_SPROM4_PWR_INFO_CORE0 0x0080 +#define SSB_SPROM4_PWR_INFO_CORE1 0x00AE +#define SSB_SPROM4_PWR_INFO_CORE2 0x00DC +#define SSB_SPROM4_PWR_INFO_CORE3 0x010A + +#define SSB_SPROM4_2G_MAXP_ITSSI 0x00 /* 2 GHz ITSSI and 2 GHz Max Power */ +#define SSB_SPROM4_2G_MAXP 0x00FF +#define SSB_SPROM4_2G_ITSSI 0xFF00 +#define SSB_SPROM4_2G_ITSSI_SHIFT 8 +#define SSB_SPROM4_2G_PA_0 0x02 /* 2 GHz power amp */ +#define SSB_SPROM4_2G_PA_1 0x04 +#define SSB_SPROM4_2G_PA_2 0x06 +#define SSB_SPROM4_2G_PA_3 0x08 +#define SSB_SPROM4_5G_MAXP_ITSSI 0x0A /* 5 GHz ITSSI and 5.3 GHz Max Power */ +#define SSB_SPROM4_5G_MAXP 0x00FF +#define SSB_SPROM4_5G_ITSSI 0xFF00 +#define SSB_SPROM4_5G_ITSSI_SHIFT 8 +#define SSB_SPROM4_5GHL_MAXP 0x0C /* 5.2 GHz and 5.8 GHz Max Power */ +#define SSB_SPROM4_5GH_MAXP 0x00FF +#define SSB_SPROM4_5GL_MAXP 0xFF00 +#define SSB_SPROM4_5GL_MAXP_SHIFT 8 +#define SSB_SPROM4_5G_PA_0 0x0E /* 5.3 GHz power amp */ +#define SSB_SPROM4_5G_PA_1 0x10 +#define SSB_SPROM4_5G_PA_2 0x12 +#define SSB_SPROM4_5G_PA_3 0x14 +#define SSB_SPROM4_5GL_PA_0 0x16 /* 5.2 GHz power amp */ +#define SSB_SPROM4_5GL_PA_1 0x18 +#define SSB_SPROM4_5GL_PA_2 0x1A +#define SSB_SPROM4_5GL_PA_3 0x1C +#define SSB_SPROM4_5GH_PA_0 0x1E /* 5.8 GHz power amp */ +#define SSB_SPROM4_5GH_PA_1 0x20 +#define SSB_SPROM4_5GH_PA_2 0x22 +#define SSB_SPROM4_5GH_PA_3 0x24 + +/* TODO: Make it deprecated */ #define SSB_SPROM4_MAXP_BG 0x0080 /* Max Power BG in path 1 */ #define SSB_SPROM4_MAXP_BG_MASK 0x00FF /* Mask for Max Power BG */ #define SSB_SPROM4_ITSSI_BG 0xFF00 /* Mask for path 1 itssi_bg */ -- cgit v1.2.3 From d1d3799fcb1037357b54be44e796a6253484268e Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Tue, 15 Jul 2014 19:44:28 +0200 Subject: bcma: add support for BCM43217 found in Tenda W322E (14e4:43a9) 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/driver_chipcommon_pmu.c | 1 + drivers/bcma/host_pci.c | 1 + drivers/bcma/sprom.c | 1 + include/linux/bcma/bcma.h | 1 + 4 files changed, 4 insertions(+) (limited to 'include') diff --git a/drivers/bcma/driver_chipcommon_pmu.c b/drivers/bcma/driver_chipcommon_pmu.c index 5081a8c439cc..bb694e2e9f32 100644 --- a/drivers/bcma/driver_chipcommon_pmu.c +++ b/drivers/bcma/driver_chipcommon_pmu.c @@ -603,6 +603,7 @@ void bcma_pmu_spuravoid_pllupdate(struct bcma_drv_cc *cc, int spuravoid) tmp = BCMA_CC_PMU_CTL_PLL_UPD | BCMA_CC_PMU_CTL_NOILPONW; break; + case BCMA_CHIP_ID_BCM43217: case BCMA_CHIP_ID_BCM43227: case BCMA_CHIP_ID_BCM43228: case BCMA_CHIP_ID_BCM43428: diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c index e333305363aa..3cf725a49dc1 100644 --- a/drivers/bcma/host_pci.c +++ b/drivers/bcma/host_pci.c @@ -279,6 +279,7 @@ static const struct pci_device_id bcma_pci_bridge_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4358) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4359) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4365) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43a9) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4727) }, { 0, }, }; diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c index a9dfb1ac138d..97bb38e9ed65 100644 --- a/drivers/bcma/sprom.c +++ b/drivers/bcma/sprom.c @@ -534,6 +534,7 @@ static bool bcma_sprom_onchip_available(struct bcma_bus *bus) /* for these chips OTP is always available */ present = true; break; + case BCMA_CHIP_ID_BCM43217: case BCMA_CHIP_ID_BCM43227: case BCMA_CHIP_ID_BCM43228: case BCMA_CHIP_ID_BCM43428: diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 452286a38b2b..7cb2344741cf 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -158,6 +158,7 @@ struct bcma_host_ops { /* Chip IDs of PCIe devices */ #define BCMA_CHIP_ID_BCM4313 0x4313 #define BCMA_CHIP_ID_BCM43142 43142 +#define BCMA_CHIP_ID_BCM43217 43217 #define BCMA_CHIP_ID_BCM43224 43224 #define BCMA_PKG_ID_BCM43224_FAB_CSM 0x8 #define BCMA_PKG_ID_BCM43224_FAB_SMIC 0xa -- cgit v1.2.3 From c2cb2c4cf1a089501242a1701b589d2ad5eb0448 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Thu, 17 Jul 2014 19:31:05 +0200 Subject: b43: use one shared function for setting MAC frequency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By the way add few chipsets that were tracked with "wl" dumps. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 39 ++++++++++++++++++++++++++++++++++++++ drivers/net/wireless/b43/main.h | 1 + drivers/net/wireless/b43/phy_lcn.c | 35 +--------------------------------- drivers/net/wireless/b43/phy_n.c | 7 +------ include/linux/bcma/bcma.h | 1 + 5 files changed, 43 insertions(+), 40 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 3dcd3aa38608..3e127be06bfb 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -2964,6 +2964,45 @@ void b43_mac_phy_clock_set(struct b43_wldev *dev, bool on) } } +/* brcms_b_switch_macfreq */ +void b43_mac_switch_freq(struct b43_wldev *dev, u8 spurmode) +{ + u16 chip_id = dev->dev->chip_id; + + if (chip_id == BCMA_CHIP_ID_BCM43217 || + chip_id == BCMA_CHIP_ID_BCM43222 || + chip_id == BCMA_CHIP_ID_BCM43224 || + chip_id == BCMA_CHIP_ID_BCM43225 || + chip_id == BCMA_CHIP_ID_BCM43227 || + chip_id == BCMA_CHIP_ID_BCM43228) { + switch (spurmode) { + case 2: /* 126 Mhz */ + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x2082); + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8); + break; + case 1: /* 123 Mhz */ + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x5341); + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8); + break; + default: /* 120 Mhz */ + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x8889); + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8); + break; + } + } else if (dev->phy.type == B43_PHYTYPE_LCN) { + switch (spurmode) { + case 1: /* 82 Mhz */ + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x7CE0); + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC); + break; + default: /* 80 Mhz */ + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0xCCCD); + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC); + break; + } + } +} + static void b43_adjust_opmode(struct b43_wldev *dev) { struct b43_wl *wl = dev->wl; diff --git a/drivers/net/wireless/b43/main.h b/drivers/net/wireless/b43/main.h index f476fc337d64..9f22e4b4c132 100644 --- a/drivers/net/wireless/b43/main.h +++ b/drivers/net/wireless/b43/main.h @@ -99,6 +99,7 @@ void b43_power_saving_ctl_bits(struct b43_wldev *dev, unsigned int ps_flags); void b43_mac_suspend(struct b43_wldev *dev); void b43_mac_enable(struct b43_wldev *dev); void b43_mac_phy_clock_set(struct b43_wldev *dev, bool on); +void b43_mac_switch_freq(struct b43_wldev *dev, u8 spurmode); struct b43_request_fw_context; diff --git a/drivers/net/wireless/b43/phy_lcn.c b/drivers/net/wireless/b43/phy_lcn.c index 0bafa3b17035..e76bbdf3247e 100644 --- a/drivers/net/wireless/b43/phy_lcn.c +++ b/drivers/net/wireless/b43/phy_lcn.c @@ -54,39 +54,6 @@ enum lcn_sense_type { B43_SENSE_VBAT, }; -/* In theory it's PHY common function, move if needed */ -/* brcms_b_switch_macfreq */ -static void b43_phy_switch_macfreq(struct b43_wldev *dev, u8 spurmode) -{ - if (dev->dev->chip_id == 43224 || dev->dev->chip_id == 43225) { - switch (spurmode) { - case 2: /* 126 Mhz */ - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x2082); - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8); - break; - case 1: /* 123 Mhz */ - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x5341); - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8); - break; - default: /* 120 Mhz */ - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x8889); - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8); - break; - } - } else if (dev->phy.type == B43_PHYTYPE_LCN) { - switch (spurmode) { - case 1: /* 82 Mhz */ - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x7CE0); - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC); - break; - default: /* 80 Mhz */ - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0xCCCD); - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC); - break; - } - } -} - /************************************************** * Radio 2064. **************************************************/ @@ -609,7 +576,7 @@ static void b43_phy_lcn_txrx_spur_avoidance_mode(struct b43_wldev *dev, b43_phy_write(dev, 0x93b, ((0 << 13) + 23)); b43_phy_write(dev, 0x93c, ((0 << 13) + 1989)); } - b43_phy_switch_macfreq(dev, enable); + b43_mac_switch_freq(dev, enable); } /************************************************** diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index ef1acaec7027..0f0c1306b0ad 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -6113,12 +6113,7 @@ static void b43_nphy_channel_setup(struct b43_wldev *dev, b43_nphy_pmu_spur_avoid(dev, avoid); - if (dev->dev->chip_id == 43222 || dev->dev->chip_id == 43224 || - dev->dev->chip_id == 43225) { - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, - avoid ? 0x5341 : 0x8889); - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8); - } + b43_mac_switch_freq(dev, avoid); if (dev->phy.rev == 3 || dev->phy.rev == 4) ; /* TODO: reset PLL */ diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 7cb2344741cf..969af0f2bdf9 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -159,6 +159,7 @@ struct bcma_host_ops { #define BCMA_CHIP_ID_BCM4313 0x4313 #define BCMA_CHIP_ID_BCM43142 43142 #define BCMA_CHIP_ID_BCM43217 43217 +#define BCMA_CHIP_ID_BCM43222 43222 #define BCMA_CHIP_ID_BCM43224 43224 #define BCMA_PKG_ID_BCM43224_FAB_CSM 0x8 #define BCMA_PKG_ID_BCM43224_FAB_SMIC 0xa -- cgit v1.2.3 From 6a09d17bb66a533c165be81e8a4c3557f68e1a3b Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 18 Jul 2014 18:56:34 +0100 Subject: KEYS: Provide a generic instantiation function Provide a generic instantiation function for key types that use the preparse hook. This makes it easier to prereserve key quota before keyrings get locked to retain the new key. Signed-off-by: David Howells Acked-by: Steve Dickson Acked-by: Jeff Layton Reviewed-by: Sage Weil --- crypto/asymmetric_keys/asymmetric_type.c | 25 +------------------------ include/linux/key-type.h | 2 ++ security/keys/key.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index b77eb5304788..c1fe0fcee8e3 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -163,29 +163,6 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep) kfree(prep->description); } -/* - * Instantiate a asymmetric_key defined key. The key was preparsed, so we just - * have to transfer the data here. - */ -static int asymmetric_key_instantiate(struct key *key, struct key_preparsed_payload *prep) -{ - int ret; - - pr_devel("==>%s()\n", __func__); - - ret = key_payload_reserve(key, prep->quotalen); - if (ret == 0) { - key->type_data.p[0] = prep->type_data[0]; - key->type_data.p[1] = prep->type_data[1]; - key->payload.data = prep->payload; - prep->type_data[0] = NULL; - prep->type_data[1] = NULL; - prep->payload = NULL; - } - pr_devel("<==%s() = %d\n", __func__, ret); - return ret; -} - /* * dispose of the data dangling from the corpse of a asymmetric key */ @@ -205,7 +182,7 @@ struct key_type key_type_asymmetric = { .name = "asymmetric", .preparse = asymmetric_key_preparse, .free_preparse = asymmetric_key_free_preparse, - .instantiate = asymmetric_key_instantiate, + .instantiate = generic_key_instantiate, .match = asymmetric_key_match, .destroy = asymmetric_key_destroy, .describe = asymmetric_key_describe, diff --git a/include/linux/key-type.h b/include/linux/key-type.h index a74c3a84dfdd..88503dca2a57 100644 --- a/include/linux/key-type.h +++ b/include/linux/key-type.h @@ -159,5 +159,7 @@ static inline int key_negate_and_link(struct key *key, return key_reject_and_link(key, timeout, ENOKEY, keyring, instkey); } +extern int generic_key_instantiate(struct key *key, struct key_preparsed_payload *prep); + #endif /* CONFIG_KEYS */ #endif /* _LINUX_KEY_TYPE_H */ diff --git a/security/keys/key.c b/security/keys/key.c index 2048a110e7f1..7c9acbf106b6 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -1023,6 +1023,36 @@ void key_invalidate(struct key *key) } EXPORT_SYMBOL(key_invalidate); +/** + * generic_key_instantiate - Simple instantiation of a key from preparsed data + * @key: The key to be instantiated + * @prep: The preparsed data to load. + * + * Instantiate a key from preparsed data. We assume we can just copy the data + * in directly and clear the old pointers. + * + * This can be pointed to directly by the key type instantiate op pointer. + */ +int generic_key_instantiate(struct key *key, struct key_preparsed_payload *prep) +{ + int ret; + + pr_devel("==>%s()\n", __func__); + + ret = key_payload_reserve(key, prep->quotalen); + if (ret == 0) { + key->type_data.p[0] = prep->type_data[0]; + key->type_data.p[1] = prep->type_data[1]; + rcu_assign_keypointer(key, prep->payload); + prep->type_data[0] = NULL; + prep->type_data[1] = NULL; + prep->payload = NULL; + } + pr_devel("<==%s() = %d\n", __func__, ret); + return ret; +} +EXPORT_SYMBOL(generic_key_instantiate); + /** * register_key_type - Register a type of key. * @ktype: The new key type. -- cgit v1.2.3 From 0ef1b9e0cfd98f91b2341d581ea9424eb4ba3aa7 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Wed, 25 Jun 2014 11:25:19 -0400 Subject: ftrace: Remove ftrace_start/stop() There are no more kernel users of ftrace_stop() and ftrace_start(). Remove them. Reviewed-by: Masami Hiramatsu Signed-off-by: Steven Rostedt --- include/linux/ftrace.h | 28 ---------------------------- 1 file changed, 28 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 18fb2c4a3f7f..b7333794554f 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -143,32 +143,6 @@ enum ftrace_tracing_type_t { /* Current tracing type, default is FTRACE_TYPE_ENTER */ extern enum ftrace_tracing_type_t ftrace_tracing_type; -/** - * ftrace_stop - stop function tracer. - * - * A quick way to stop the function tracer. Note this an on off switch, - * it is not something that is recursive like preempt_disable. - * This does not disable the calling of mcount, it only stops the - * calling of functions from mcount. - */ -static inline void ftrace_stop(void) -{ - function_trace_stop = 1; -} - -/** - * ftrace_start - start the function tracer. - * - * This function is the inverse of ftrace_stop. This does not enable - * the function tracing if the function tracer is disabled. This only - * sets the function tracer flag to continue calling the functions - * from mcount. - */ -static inline void ftrace_start(void) -{ - function_trace_stop = 0; -} - /* * The ftrace_ops must be a static and should also * be read_mostly. These functions do modify read_mostly variables @@ -245,8 +219,6 @@ static inline int ftrace_nr_registered_ops(void) } static inline void clear_ftrace_function(void) { } static inline void ftrace_kill(void) { } -static inline void ftrace_stop(void) { } -static inline void ftrace_start(void) { } #endif /* CONFIG_FUNCTION_TRACER */ #ifdef CONFIG_STACK_TRACER -- cgit v1.2.3 From 7544256aa20356e506b0d179f9b6abc661847e2f Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Wed, 25 Jun 2014 13:26:59 -0400 Subject: ftrace: Remove check for HAVE_FUNCTION_TRACE_MCOUNT_TEST function_trace_stop is no longer used to disable function tracing. This means that archs are no longer limited if it does not support checking this variable in the mcount trampoline. No need to use the list_func for archs that do not support this obsolete method. Acked-by: James Hogan Reviewed-by: Masami Hiramatsu Signed-off-by: Steven Rostedt --- Documentation/trace/ftrace-design.txt | 26 -------------------------- include/linux/ftrace.h | 3 +-- 2 files changed, 1 insertion(+), 28 deletions(-) (limited to 'include') diff --git a/Documentation/trace/ftrace-design.txt b/Documentation/trace/ftrace-design.txt index 3f669b9e8852..dd5f916b351d 100644 --- a/Documentation/trace/ftrace-design.txt +++ b/Documentation/trace/ftrace-design.txt @@ -102,30 +102,6 @@ extern void mcount(void); EXPORT_SYMBOL(mcount); -HAVE_FUNCTION_TRACE_MCOUNT_TEST -------------------------------- - -This is an optional optimization for the normal case when tracing is turned off -in the system. If you do not enable this Kconfig option, the common ftrace -code will take care of doing the checking for you. - -To support this feature, you only need to check the function_trace_stop -variable in the mcount function. If it is non-zero, there is no tracing to be -done at all, so you can return. - -This additional pseudo code would simply be: -void mcount(void) -{ - /* save any bare state needed in order to do initial checking */ - -+ if (function_trace_stop) -+ return; - - extern void (*ftrace_trace_function)(unsigned long, unsigned long); - if (ftrace_trace_function != ftrace_stub) -... - - HAVE_FUNCTION_GRAPH_TRACER -------------------------- @@ -328,8 +304,6 @@ void mcount(void) void ftrace_caller(void) { - /* implement HAVE_FUNCTION_TRACE_MCOUNT_TEST if you desire */ - /* save all state needed by the ABI (see paragraph above) */ unsigned long frompc = ...; diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index b7333794554f..c800906235e1 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -33,8 +33,7 @@ * features, then it must call an indirect function that * does. Or at least does enough to prevent any unwelcomed side effects. */ -#if !defined(CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST) || \ - !ARCH_SUPPORTS_FTRACE_OPS +#if !ARCH_SUPPORTS_FTRACE_OPS # define FTRACE_FORCE_LIST_FUNC 1 #else # define FTRACE_FORCE_LIST_FUNC 0 -- cgit v1.2.3 From 3a636388bae8390d23f31e061c0c6fdc14525786 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Thu, 26 Jun 2014 11:24:52 -0400 Subject: tracing: Remove function_trace_stop and HAVE_FUNCTION_TRACE_MCOUNT_TEST All users of function_trace_stop and HAVE_FUNCTION_TRACE_MCOUNT_TEST have been removed. We can safely remove them from the kernel. Reviewed-by: Masami Hiramatsu Signed-off-by: Steven Rostedt --- include/linux/ftrace.h | 2 -- kernel/trace/Kconfig | 5 ----- kernel/trace/ftrace.c | 3 --- 3 files changed, 10 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index c800906235e1..7a5b7b97e539 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -129,8 +129,6 @@ struct ftrace_ops { #endif }; -extern int function_trace_stop; - /* * Type of the current tracing. */ diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index d4409356f40d..a5da09c899dd 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig @@ -29,11 +29,6 @@ config HAVE_FUNCTION_GRAPH_FP_TEST help See Documentation/trace/ftrace-design.txt -config HAVE_FUNCTION_TRACE_MCOUNT_TEST - bool - help - See Documentation/trace/ftrace-design.txt - config HAVE_DYNAMIC_FTRACE bool help diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 70abf97d6e84..4c61f28a08e0 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -80,9 +80,6 @@ static struct ftrace_ops ftrace_list_end __read_mostly = { int ftrace_enabled __read_mostly; static int last_ftrace_enabled; -/* Quick disabling of function tracer. */ -int function_trace_stop __read_mostly; - /* Current function tracing op */ struct ftrace_ops *function_trace_op __read_mostly = &ftrace_list_end; /* What to set function_trace_op to */ -- cgit v1.2.3 From 48dc92b9fc3926844257316e75ba11eb5c742b2c Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 25 Jun 2014 16:08:24 -0700 Subject: seccomp: add "seccomp" syscall This adds the new "seccomp" syscall with both an "operation" and "flags" parameter for future expansion. The third argument is a pointer value, used with the SECCOMP_SET_MODE_FILTER operation. Currently, flags must be 0. This is functionally equivalent to prctl(PR_SET_SECCOMP, ...). In addition to the TSYNC flag later in this patch series, there is a non-zero chance that this syscall could be used for configuring a fixed argument area for seccomp-tracer-aware processes to pass syscall arguments in the future. Hence, the use of "seccomp" not simply "seccomp_add_filter" for this syscall. Additionally, this syscall uses operation, flags, and user pointer for arguments because strictly passing arguments via a user pointer would mean seccomp itself would be unable to trivially filter the seccomp syscall itself. Signed-off-by: Kees Cook Reviewed-by: Oleg Nesterov Reviewed-by: Andy Lutomirski --- arch/Kconfig | 1 + arch/x86/syscalls/syscall_32.tbl | 1 + arch/x86/syscalls/syscall_64.tbl | 1 + include/linux/syscalls.h | 2 ++ include/uapi/asm-generic/unistd.h | 4 ++- include/uapi/linux/seccomp.h | 4 +++ kernel/seccomp.c | 55 +++++++++++++++++++++++++++++++++++---- kernel/sys_ni.c | 3 +++ 8 files changed, 65 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/arch/Kconfig b/arch/Kconfig index 97ff872c7acc..0eae9df35b88 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -321,6 +321,7 @@ config HAVE_ARCH_SECCOMP_FILTER - secure_computing is called from a ptrace_event()-safe context - secure_computing return value is checked and a return value of -1 results in the system call being skipped immediately. + - seccomp syscall wired up config SECCOMP_FILTER def_bool y diff --git a/arch/x86/syscalls/syscall_32.tbl b/arch/x86/syscalls/syscall_32.tbl index d6b867921612..7527eac24122 100644 --- a/arch/x86/syscalls/syscall_32.tbl +++ b/arch/x86/syscalls/syscall_32.tbl @@ -360,3 +360,4 @@ 351 i386 sched_setattr sys_sched_setattr 352 i386 sched_getattr sys_sched_getattr 353 i386 renameat2 sys_renameat2 +354 i386 seccomp sys_seccomp diff --git a/arch/x86/syscalls/syscall_64.tbl b/arch/x86/syscalls/syscall_64.tbl index ec255a1646d2..16272a6c12b7 100644 --- a/arch/x86/syscalls/syscall_64.tbl +++ b/arch/x86/syscalls/syscall_64.tbl @@ -323,6 +323,7 @@ 314 common sched_setattr sys_sched_setattr 315 common sched_getattr sys_sched_getattr 316 common renameat2 sys_renameat2 +317 common seccomp sys_seccomp # # x32-specific system call numbers start at 512 to avoid cache impact diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index b0881a0ed322..1713977ee26f 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -866,4 +866,6 @@ asmlinkage long sys_process_vm_writev(pid_t pid, asmlinkage long sys_kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2); asmlinkage long sys_finit_module(int fd, const char __user *uargs, int flags); +asmlinkage long sys_seccomp(unsigned int op, unsigned int flags, + const char __user *uargs); #endif diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h index 333640608087..65acbf0e2867 100644 --- a/include/uapi/asm-generic/unistd.h +++ b/include/uapi/asm-generic/unistd.h @@ -699,9 +699,11 @@ __SYSCALL(__NR_sched_setattr, sys_sched_setattr) __SYSCALL(__NR_sched_getattr, sys_sched_getattr) #define __NR_renameat2 276 __SYSCALL(__NR_renameat2, sys_renameat2) +#define __NR_seccomp 277 +__SYSCALL(__NR_seccomp, sys_seccomp) #undef __NR_syscalls -#define __NR_syscalls 277 +#define __NR_syscalls 278 /* * All syscalls below here should go away really, diff --git a/include/uapi/linux/seccomp.h b/include/uapi/linux/seccomp.h index ac2dc9f72973..b258878ba754 100644 --- a/include/uapi/linux/seccomp.h +++ b/include/uapi/linux/seccomp.h @@ -10,6 +10,10 @@ #define SECCOMP_MODE_STRICT 1 /* uses hard-coded filter. */ #define SECCOMP_MODE_FILTER 2 /* uses user-supplied filter. */ +/* Valid operations for seccomp syscall. */ +#define SECCOMP_SET_MODE_STRICT 0 +#define SECCOMP_SET_MODE_FILTER 1 + /* * All BPF programs must return a 32-bit value. * The bottom 16-bits are for optional return data. diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 05cac2c2eca1..f0652578af75 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -18,6 +18,7 @@ #include #include #include +#include /* #define SECCOMP_DEBUG 1 */ @@ -314,7 +315,7 @@ free_prog: * * Returns 0 on success and non-zero otherwise. */ -static long seccomp_attach_user_filter(char __user *user_filter) +static long seccomp_attach_user_filter(const char __user *user_filter) { struct sock_fprog fprog; long ret = -EFAULT; @@ -517,6 +518,7 @@ out: #ifdef CONFIG_SECCOMP_FILTER /** * seccomp_set_mode_filter: internal function for setting seccomp filter + * @flags: flags to change filter behavior * @filter: struct sock_fprog containing filter * * This function may be called repeatedly to install additional filters. @@ -527,11 +529,16 @@ out: * * Returns 0 on success or -EINVAL on failure. */ -static long seccomp_set_mode_filter(char __user *filter) +static long seccomp_set_mode_filter(unsigned int flags, + const char __user *filter) { const unsigned long seccomp_mode = SECCOMP_MODE_FILTER; long ret = -EINVAL; + /* Validate flags. */ + if (flags != 0) + goto out; + if (!seccomp_may_assign_mode(seccomp_mode)) goto out; @@ -544,12 +551,35 @@ out: return ret; } #else -static inline long seccomp_set_mode_filter(char __user *filter) +static inline long seccomp_set_mode_filter(unsigned int flags, + const char __user *filter) { return -EINVAL; } #endif +/* Common entry point for both prctl and syscall. */ +static long do_seccomp(unsigned int op, unsigned int flags, + const char __user *uargs) +{ + switch (op) { + case SECCOMP_SET_MODE_STRICT: + if (flags != 0 || uargs != NULL) + return -EINVAL; + return seccomp_set_mode_strict(); + case SECCOMP_SET_MODE_FILTER: + return seccomp_set_mode_filter(flags, uargs); + default: + return -EINVAL; + } +} + +SYSCALL_DEFINE3(seccomp, unsigned int, op, unsigned int, flags, + const char __user *, uargs) +{ + return do_seccomp(op, flags, uargs); +} + /** * prctl_set_seccomp: configures current->seccomp.mode * @seccomp_mode: requested mode to use @@ -559,12 +589,27 @@ static inline long seccomp_set_mode_filter(char __user *filter) */ long prctl_set_seccomp(unsigned long seccomp_mode, char __user *filter) { + unsigned int op; + char __user *uargs; + switch (seccomp_mode) { case SECCOMP_MODE_STRICT: - return seccomp_set_mode_strict(); + op = SECCOMP_SET_MODE_STRICT; + /* + * Setting strict mode through prctl always ignored filter, + * so make sure it is always NULL here to pass the internal + * check in do_seccomp(). + */ + uargs = NULL; + break; case SECCOMP_MODE_FILTER: - return seccomp_set_mode_filter(filter); + op = SECCOMP_SET_MODE_FILTER; + uargs = filter; + break; default: return -EINVAL; } + + /* prctl interface doesn't have flags, so they are always zero. */ + return do_seccomp(op, 0, uargs); } diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index 36441b51b5df..2904a2105914 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c @@ -213,3 +213,6 @@ cond_syscall(compat_sys_open_by_handle_at); /* compare kernel pointers */ cond_syscall(sys_kcmp); + +/* operate on Secure Computing state */ +cond_syscall(sys_seccomp); -- cgit v1.2.3 From 1d4457f99928a968767f6405b4a1f50845aa15fd Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 21 May 2014 15:23:46 -0700 Subject: sched: move no_new_privs into new atomic flags Since seccomp transitions between threads requires updates to the no_new_privs flag to be atomic, the flag must be part of an atomic flag set. This moves the nnp flag into a separate task field, and introduces accessors. Signed-off-by: Kees Cook Reviewed-by: Oleg Nesterov Reviewed-by: Andy Lutomirski --- fs/exec.c | 4 ++-- include/linux/sched.h | 18 +++++++++++++++--- kernel/seccomp.c | 2 +- kernel/sys.c | 4 ++-- security/apparmor/domain.c | 4 ++-- 5 files changed, 22 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/fs/exec.c b/fs/exec.c index a3d33fe592d6..0f5c272410f6 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1234,7 +1234,7 @@ static void check_unsafe_exec(struct linux_binprm *bprm) * This isn't strictly necessary, but it makes it harder for LSMs to * mess up. */ - if (current->no_new_privs) + if (task_no_new_privs(current)) bprm->unsafe |= LSM_UNSAFE_NO_NEW_PRIVS; t = p; @@ -1272,7 +1272,7 @@ int prepare_binprm(struct linux_binprm *bprm) bprm->cred->egid = current_egid(); if (!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) && - !current->no_new_privs && + !task_no_new_privs(current) && kuid_has_mapping(bprm->cred->user_ns, inode->i_uid) && kgid_has_mapping(bprm->cred->user_ns, inode->i_gid)) { /* Set-uid? */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 306f4f0c987a..0fd19055bb64 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1307,13 +1307,12 @@ struct task_struct { * execve */ unsigned in_iowait:1; - /* task may not gain privileges */ - unsigned no_new_privs:1; - /* Revert to default priority/policy when forking */ unsigned sched_reset_on_fork:1; unsigned sched_contributes_to_load:1; + unsigned long atomic_flags; /* Flags needing atomic access. */ + pid_t pid; pid_t tgid; @@ -1967,6 +1966,19 @@ static inline void memalloc_noio_restore(unsigned int flags) current->flags = (current->flags & ~PF_MEMALLOC_NOIO) | flags; } +/* Per-process atomic flags. */ +#define PFA_NO_NEW_PRIVS 0x00000001 /* May not gain new privileges. */ + +static inline bool task_no_new_privs(struct task_struct *p) +{ + return test_bit(PFA_NO_NEW_PRIVS, &p->atomic_flags); +} + +static inline void task_set_no_new_privs(struct task_struct *p) +{ + set_bit(PFA_NO_NEW_PRIVS, &p->atomic_flags); +} + /* * task->jobctl flags */ diff --git a/kernel/seccomp.c b/kernel/seccomp.c index f0652578af75..d2596136b0d1 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -241,7 +241,7 @@ static long seccomp_attach_filter(struct sock_fprog *fprog) * This avoids scenarios where unprivileged tasks can affect the * behavior of privileged children. */ - if (!current->no_new_privs && + if (!task_no_new_privs(current) && security_capable_noaudit(current_cred(), current_user_ns(), CAP_SYS_ADMIN) != 0) return -EACCES; diff --git a/kernel/sys.c b/kernel/sys.c index 66a751ebf9d9..ce8129192a26 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -1990,12 +1990,12 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, if (arg2 != 1 || arg3 || arg4 || arg5) return -EINVAL; - current->no_new_privs = 1; + task_set_no_new_privs(current); break; case PR_GET_NO_NEW_PRIVS: if (arg2 || arg3 || arg4 || arg5) return -EINVAL; - return current->no_new_privs ? 1 : 0; + return task_no_new_privs(current) ? 1 : 0; case PR_GET_THP_DISABLE: if (arg2 || arg3 || arg4 || arg5) return -EINVAL; diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 452567d3a08e..d97cba3e3849 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -621,7 +621,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) * There is no exception for unconfined as change_hat is not * available. */ - if (current->no_new_privs) + if (task_no_new_privs(current)) return -EPERM; /* released below */ @@ -776,7 +776,7 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, * no_new_privs is set because this aways results in a reduction * of permissions. */ - if (current->no_new_privs && !unconfined(profile)) { + if (task_no_new_privs(current) && !unconfined(profile)) { put_cred(cred); return -EPERM; } -- cgit v1.2.3 From dbd952127d11bb44a4ea30b08cc60531b6a23d71 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 27 Jun 2014 15:18:48 -0700 Subject: seccomp: introduce writer locking Normally, task_struct.seccomp.filter is only ever read or modified by the task that owns it (current). This property aids in fast access during system call filtering as read access is lockless. Updating the pointer from another task, however, opens up race conditions. To allow cross-thread filter pointer updates, writes to the seccomp fields are now protected by the sighand spinlock (which is shared by all threads in the thread group). Read access remains lockless because pointer updates themselves are atomic. However, writes (or cloning) often entail additional checking (like maximum instruction counts) which require locking to perform safely. In the case of cloning threads, the child is invisible to the system until it enters the task list. To make sure a child can't be cloned from a thread and left in a prior state, seccomp duplication is additionally moved under the sighand lock. Then parent and child are certain have the same seccomp state when they exit the lock. Based on patches by Will Drewry and David Drysdale. Signed-off-by: Kees Cook Reviewed-by: Oleg Nesterov Reviewed-by: Andy Lutomirski --- include/linux/seccomp.h | 6 +++--- kernel/fork.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- kernel/seccomp.c | 16 +++++++++++++++- 3 files changed, 66 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h index 4054b0994071..9ff98b4bfe2e 100644 --- a/include/linux/seccomp.h +++ b/include/linux/seccomp.h @@ -14,11 +14,11 @@ struct seccomp_filter; * * @mode: indicates one of the valid values above for controlled * system calls available to a process. - * @filter: The metadata and ruleset for determining what system calls - * are allowed for a task. + * @filter: must always point to a valid seccomp-filter or NULL as it is + * accessed without locking during system call entry. * * @filter must only be accessed from the context of current as there - * is no locking. + * is no read locking. */ struct seccomp { int mode; diff --git a/kernel/fork.c b/kernel/fork.c index 6a13c46cd87d..ed4bc339c9dc 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -315,6 +315,15 @@ static struct task_struct *dup_task_struct(struct task_struct *orig) goto free_ti; tsk->stack = ti; +#ifdef CONFIG_SECCOMP + /* + * We must handle setting up seccomp filters once we're under + * the sighand lock in case orig has changed between now and + * then. Until then, filter must be NULL to avoid messing up + * the usage counts on the error path calling free_task. + */ + tsk->seccomp.filter = NULL; +#endif setup_thread_stack(tsk, orig); clear_user_return_notifier(tsk); @@ -1081,6 +1090,39 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) return 0; } +static void copy_seccomp(struct task_struct *p) +{ +#ifdef CONFIG_SECCOMP + /* + * Must be called with sighand->lock held, which is common to + * all threads in the group. Holding cred_guard_mutex is not + * needed because this new task is not yet running and cannot + * be racing exec. + */ + BUG_ON(!spin_is_locked(¤t->sighand->siglock)); + + /* Ref-count the new filter user, and assign it. */ + get_seccomp_filter(current); + p->seccomp = current->seccomp; + + /* + * Explicitly enable no_new_privs here in case it got set + * between the task_struct being duplicated and holding the + * sighand lock. The seccomp state and nnp must be in sync. + */ + if (task_no_new_privs(current)) + task_set_no_new_privs(p); + + /* + * If the parent gained a seccomp mode after copying thread + * flags and between before we held the sighand lock, we have + * to manually enable the seccomp thread flag here. + */ + if (p->seccomp.mode != SECCOMP_MODE_DISABLED) + set_tsk_thread_flag(p, TIF_SECCOMP); +#endif +} + SYSCALL_DEFINE1(set_tid_address, int __user *, tidptr) { current->clear_child_tid = tidptr; @@ -1196,7 +1238,6 @@ static struct task_struct *copy_process(unsigned long clone_flags, goto fork_out; ftrace_graph_init_task(p); - get_seccomp_filter(p); rt_mutex_init_task(p); @@ -1436,6 +1477,12 @@ static struct task_struct *copy_process(unsigned long clone_flags, spin_lock(¤t->sighand->siglock); + /* + * Copy seccomp details explicitly here, in case they were changed + * before holding sighand lock. + */ + copy_seccomp(p); + /* * Process group and session signals need to be delivered to just the * parent before the fork or both the parent and the child after the diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 58125160417c..d5543e787e4e 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -199,6 +199,8 @@ static u32 seccomp_run_filters(int syscall) static inline bool seccomp_may_assign_mode(unsigned long seccomp_mode) { + BUG_ON(!spin_is_locked(¤t->sighand->siglock)); + if (current->seccomp.mode && current->seccomp.mode != seccomp_mode) return false; @@ -207,6 +209,8 @@ static inline bool seccomp_may_assign_mode(unsigned long seccomp_mode) static inline void seccomp_assign_mode(unsigned long seccomp_mode) { + BUG_ON(!spin_is_locked(¤t->sighand->siglock)); + current->seccomp.mode = seccomp_mode; set_tsk_thread_flag(current, TIF_SECCOMP); } @@ -332,6 +336,8 @@ out: * @flags: flags to change filter behavior * @filter: seccomp filter to add to the current process * + * Caller must be holding current->sighand->siglock lock. + * * Returns 0 on success, -ve on error. */ static long seccomp_attach_filter(unsigned int flags, @@ -340,6 +346,8 @@ static long seccomp_attach_filter(unsigned int flags, unsigned long total_insns; struct seccomp_filter *walker; + BUG_ON(!spin_is_locked(¤t->sighand->siglock)); + /* Validate resulting filter length. */ total_insns = filter->prog->len; for (walker = current->seccomp.filter; walker; walker = walker->prev) @@ -529,6 +537,8 @@ static long seccomp_set_mode_strict(void) const unsigned long seccomp_mode = SECCOMP_MODE_STRICT; long ret = -EINVAL; + spin_lock_irq(¤t->sighand->siglock); + if (!seccomp_may_assign_mode(seccomp_mode)) goto out; @@ -539,6 +549,7 @@ static long seccomp_set_mode_strict(void) ret = 0; out: + spin_unlock_irq(¤t->sighand->siglock); return ret; } @@ -566,13 +577,15 @@ static long seccomp_set_mode_filter(unsigned int flags, /* Validate flags. */ if (flags != 0) - goto out; + return -EINVAL; /* Prepare the new filter before holding any locks. */ prepared = seccomp_prepare_user_filter(filter); if (IS_ERR(prepared)) return PTR_ERR(prepared); + spin_lock_irq(¤t->sighand->siglock); + if (!seccomp_may_assign_mode(seccomp_mode)) goto out; @@ -584,6 +597,7 @@ static long seccomp_set_mode_filter(unsigned int flags, seccomp_assign_mode(seccomp_mode); out: + spin_unlock_irq(¤t->sighand->siglock); seccomp_filter_free(prepared); return ret; } -- cgit v1.2.3 From c2e1f2e30daa551db3c670c0ccfeab20a540b9e1 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 5 Jun 2014 00:23:17 -0700 Subject: seccomp: implement SECCOMP_FILTER_FLAG_TSYNC Applying restrictive seccomp filter programs to large or diverse codebases often requires handling threads which may be started early in the process lifetime (e.g., by code that is linked in). While it is possible to apply permissive programs prior to process start up, it is difficult to further restrict the kernel ABI to those threads after that point. This change adds a new seccomp syscall flag to SECCOMP_SET_MODE_FILTER for synchronizing thread group seccomp filters at filter installation time. When calling seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, filter) an attempt will be made to synchronize all threads in current's threadgroup to its new seccomp filter program. This is possible iff all threads are using a filter that is an ancestor to the filter current is attempting to synchronize to. NULL filters (where the task is running as SECCOMP_MODE_NONE) are also treated as ancestors allowing threads to be transitioned into SECCOMP_MODE_FILTER. If prctrl(PR_SET_NO_NEW_PRIVS, ...) has been set on the calling thread, no_new_privs will be set for all synchronized threads too. On success, 0 is returned. On failure, the pid of one of the failing threads will be returned and no filters will have been applied. The race conditions against another thread are: - requesting TSYNC (already handled by sighand lock) - performing a clone (already handled by sighand lock) - changing its filter (already handled by sighand lock) - calling exec (handled by cred_guard_mutex) The clone case is assisted by the fact that new threads will have their seccomp state duplicated from their parent before appearing on the tasklist. Holding cred_guard_mutex means that seccomp filters cannot be assigned while in the middle of another thread's exec (potentially bypassing no_new_privs or similar). The call to de_thread() may kill threads waiting for the mutex. Changes across threads to the filter pointer includes a barrier. Based on patches by Will Drewry. Suggested-by: Julien Tinnes Signed-off-by: Kees Cook Reviewed-by: Oleg Nesterov Reviewed-by: Andy Lutomirski --- fs/exec.c | 2 +- include/linux/seccomp.h | 2 + include/uapi/linux/seccomp.h | 3 + kernel/seccomp.c | 135 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 140 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/fs/exec.c b/fs/exec.c index 0f5c272410f6..ab1f1200ce5d 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1216,7 +1216,7 @@ EXPORT_SYMBOL(install_exec_creds); /* * determine how safe it is to execute the proposed program * - the caller must hold ->cred_guard_mutex to protect against - * PTRACE_ATTACH + * PTRACE_ATTACH or seccomp thread-sync */ static void check_unsafe_exec(struct linux_binprm *bprm) { diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h index 9ff98b4bfe2e..5d586a45a319 100644 --- a/include/linux/seccomp.h +++ b/include/linux/seccomp.h @@ -3,6 +3,8 @@ #include +#define SECCOMP_FILTER_FLAG_MASK (SECCOMP_FILTER_FLAG_TSYNC) + #ifdef CONFIG_SECCOMP #include diff --git a/include/uapi/linux/seccomp.h b/include/uapi/linux/seccomp.h index b258878ba754..0f238a43ff1e 100644 --- a/include/uapi/linux/seccomp.h +++ b/include/uapi/linux/seccomp.h @@ -14,6 +14,9 @@ #define SECCOMP_SET_MODE_STRICT 0 #define SECCOMP_SET_MODE_FILTER 1 +/* Valid flags for SECCOMP_SET_MODE_FILTER */ +#define SECCOMP_FILTER_FLAG_TSYNC 1 + /* * All BPF programs must return a 32-bit value. * The bottom 16-bits are for optional return data. diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 9065d2c79c56..74f460179171 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -26,6 +26,7 @@ #ifdef CONFIG_SECCOMP_FILTER #include #include +#include #include #include #include @@ -225,6 +226,114 @@ static inline void seccomp_assign_mode(struct task_struct *task, } #ifdef CONFIG_SECCOMP_FILTER +/* Returns 1 if the parent is an ancestor of the child. */ +static int is_ancestor(struct seccomp_filter *parent, + struct seccomp_filter *child) +{ + /* NULL is the root ancestor. */ + if (parent == NULL) + return 1; + for (; child; child = child->prev) + if (child == parent) + return 1; + return 0; +} + +/** + * seccomp_can_sync_threads: checks if all threads can be synchronized + * + * Expects sighand and cred_guard_mutex locks to be held. + * + * Returns 0 on success, -ve on error, or the pid of a thread which was + * either not in the correct seccomp mode or it did not have an ancestral + * seccomp filter. + */ +static inline pid_t seccomp_can_sync_threads(void) +{ + struct task_struct *thread, *caller; + + BUG_ON(!mutex_is_locked(¤t->signal->cred_guard_mutex)); + BUG_ON(!spin_is_locked(¤t->sighand->siglock)); + + /* Validate all threads being eligible for synchronization. */ + caller = current; + for_each_thread(caller, thread) { + pid_t failed; + + /* Skip current, since it is initiating the sync. */ + if (thread == caller) + continue; + + if (thread->seccomp.mode == SECCOMP_MODE_DISABLED || + (thread->seccomp.mode == SECCOMP_MODE_FILTER && + is_ancestor(thread->seccomp.filter, + caller->seccomp.filter))) + continue; + + /* Return the first thread that cannot be synchronized. */ + failed = task_pid_vnr(thread); + /* If the pid cannot be resolved, then return -ESRCH */ + if (unlikely(WARN_ON(failed == 0))) + failed = -ESRCH; + return failed; + } + + return 0; +} + +/** + * seccomp_sync_threads: sets all threads to use current's filter + * + * Expects sighand and cred_guard_mutex locks to be held, and for + * seccomp_can_sync_threads() to have returned success already + * without dropping the locks. + * + */ +static inline void seccomp_sync_threads(void) +{ + struct task_struct *thread, *caller; + + BUG_ON(!mutex_is_locked(¤t->signal->cred_guard_mutex)); + BUG_ON(!spin_is_locked(¤t->sighand->siglock)); + + /* Synchronize all threads. */ + caller = current; + for_each_thread(caller, thread) { + /* Skip current, since it needs no changes. */ + if (thread == caller) + continue; + + /* Get a task reference for the new leaf node. */ + get_seccomp_filter(caller); + /* + * Drop the task reference to the shared ancestor since + * current's path will hold a reference. (This also + * allows a put before the assignment.) + */ + put_seccomp_filter(thread); + smp_store_release(&thread->seccomp.filter, + caller->seccomp.filter); + /* + * Opt the other thread into seccomp if needed. + * As threads are considered to be trust-realm + * equivalent (see ptrace_may_access), it is safe to + * allow one thread to transition the other. + */ + if (thread->seccomp.mode == SECCOMP_MODE_DISABLED) { + /* + * Don't let an unprivileged task work around + * the no_new_privs restriction by creating + * a thread that sets it up, enters seccomp, + * then dies. + */ + if (task_no_new_privs(caller)) + task_set_no_new_privs(thread); + + seccomp_assign_mode(thread, SECCOMP_MODE_FILTER); + } + } +} + /** * seccomp_prepare_filter: Prepares a seccomp filter for use. * @fprog: BPF program to install @@ -364,6 +473,15 @@ static long seccomp_attach_filter(unsigned int flags, if (total_insns > MAX_INSNS_PER_PATH) return -ENOMEM; + /* If thread sync has been requested, check that it is possible. */ + if (flags & SECCOMP_FILTER_FLAG_TSYNC) { + int ret; + + ret = seccomp_can_sync_threads(); + if (ret) + return ret; + } + /* * If there is an existing filter, make it the prev and don't drop its * task reference. @@ -371,6 +489,10 @@ static long seccomp_attach_filter(unsigned int flags, filter->prev = current->seccomp.filter; current->seccomp.filter = filter; + /* Now that the new filter is in place, synchronize to all threads. */ + if (flags & SECCOMP_FILTER_FLAG_TSYNC) + seccomp_sync_threads(); + return 0; } @@ -590,7 +712,7 @@ static long seccomp_set_mode_filter(unsigned int flags, long ret = -EINVAL; /* Validate flags. */ - if (flags != 0) + if (flags & ~SECCOMP_FILTER_FLAG_MASK) return -EINVAL; /* Prepare the new filter before holding any locks. */ @@ -598,6 +720,14 @@ static long seccomp_set_mode_filter(unsigned int flags, if (IS_ERR(prepared)) return PTR_ERR(prepared); + /* + * Make sure we cannot change seccomp or nnp state via TSYNC + * while another thread is in the middle of calling exec. + */ + if (flags & SECCOMP_FILTER_FLAG_TSYNC && + mutex_lock_killable(¤t->signal->cred_guard_mutex)) + goto out_free; + spin_lock_irq(¤t->sighand->siglock); if (!seccomp_may_assign_mode(seccomp_mode)) @@ -612,6 +742,9 @@ static long seccomp_set_mode_filter(unsigned int flags, seccomp_assign_mode(current, seccomp_mode); out: spin_unlock_irq(¤t->sighand->siglock); + if (flags & SECCOMP_FILTER_FLAG_TSYNC) + mutex_unlock(¤t->signal->cred_guard_mutex); +out_free: seccomp_filter_free(prepared); return ret; } -- cgit v1.2.3 From 841586082d84639e206a08f93b29d3fce7769c08 Mon Sep 17 00:00:00 2001 From: Mateusz Krawczuk Date: Sat, 28 Dec 2013 18:09:14 +0100 Subject: clk: samsung: Add clock driver for S5PV210 and compatible SoCs This patch adds new, Common Clock Framework-based clock driver for Samsung S5PV210 and compatible SoCs. The driver is just added, without enabling it yet. Signed-off-by: Mateusz Krawczuk Signed-off-by: Kyungmin Park [t.figa: Added support for other SoC variants and clock output. Fixed remaining minor issues.] Signed-off-by: Tomasz Figa Signed-off-by: Kukjin Kim --- .../bindings/clock/samsung,s5pv210-clock.txt | 78 ++ drivers/clk/samsung/Makefile | 1 + drivers/clk/samsung/clk-s5pv210.c | 976 +++++++++++++++++++++ include/dt-bindings/clock/s5pv210.h | 239 +++++ 4 files changed, 1294 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/samsung,s5pv210-clock.txt create mode 100644 drivers/clk/samsung/clk-s5pv210.c create mode 100644 include/dt-bindings/clock/s5pv210.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/samsung,s5pv210-clock.txt b/Documentation/devicetree/bindings/clock/samsung,s5pv210-clock.txt new file mode 100644 index 000000000000..effd9401c133 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/samsung,s5pv210-clock.txt @@ -0,0 +1,78 @@ +* Samsung S5P6442/S5PC110/S5PV210 Clock Controller + +Samsung S5P6442, S5PC110 and S5PV210 SoCs contain integrated clock +controller, which generates and supplies clock to various controllers +within the SoC. + +Required Properties: + +- compatible: should be one of following: + - "samsung,s5pv210-clock" : for clock controller of Samsung + S5PC110/S5PV210 SoCs, + - "samsung,s5p6442-clock" : for clock controller of Samsung + S5P6442 SoC. + +- reg: physical base address of the controller and length of memory mapped + region. + +- #clock-cells: should be 1. + +All available clocks are defined as preprocessor macros in +dt-bindings/clock/s5pv210.h header and can be used in device tree sources. + +External clocks: + +There are several clocks that are generated outside the SoC. It is expected +that they are defined using standard clock bindings with following +clock-output-names: + - "xxti": external crystal oscillator connected to XXTI and XXTO pins of +the SoC, + - "xusbxti": external crystal oscillator connected to XUSBXTI and XUSBXTO +pins of the SoC, + +A subset of above clocks available on given board shall be specified in +board device tree, including the system base clock, as selected by XOM[0] +pin of the SoC. Refer to generic fixed rate clock bindings +documentation[1] for more information how to specify these clocks. + +[1] Documentation/devicetree/bindings/clock/fixed-clock.txt + +Example: Clock controller node: + + clock: clock-controller@7e00f000 { + compatible = "samsung,s5pv210-clock"; + reg = <0x7e00f000 0x1000>; + #clock-cells = <1>; + }; + +Example: Required external clocks: + + xxti: clock-xxti { + compatible = "fixed-clock"; + clock-output-names = "xxti"; + clock-frequency = <24000000>; + #clock-cells = <0>; + }; + + xusbxti: clock-xusbxti { + compatible = "fixed-clock"; + clock-output-names = "xusbxti"; + clock-frequency = <24000000>; + #clock-cells = <0>; + }; + +Example: UART controller node that consumes the clock generated by the clock + controller (refer to the standard clock bindings for information about + "clocks" and "clock-names" properties): + + uart0: serial@e2900000 { + compatible = "samsung,s5pv210-uart"; + reg = <0xe2900000 0x400>; + interrupt-parent = <&vic1>; + interrupts = <10>; + clock-names = "uart", "clk_uart_baud0", + "clk_uart_baud1"; + clocks = <&clocks UART0>, <&clocks UART0>, + <&clocks SCLK_UART0>; + status = "disabled"; + }; diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index 69e81773164e..49d6ce1ea107 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_S3C2410_COMMON_DCLK)+= clk-s3c2410-dclk.o obj-$(CONFIG_S3C2412_COMMON_CLK)+= clk-s3c2412.o obj-$(CONFIG_S3C2443_COMMON_CLK)+= clk-s3c2443.o obj-$(CONFIG_ARCH_S3C64XX) += clk-s3c64xx.o +obj-$(CONFIG_ARCH_S5PV210) += clk-s5pv210.o diff --git a/drivers/clk/samsung/clk-s5pv210.c b/drivers/clk/samsung/clk-s5pv210.c new file mode 100644 index 000000000000..509779ad2e3e --- /dev/null +++ b/drivers/clk/samsung/clk-s5pv210.c @@ -0,0 +1,976 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * Author: Mateusz Krawczuk + * + * Based on clock drivers for S3C64xx and Exynos4 SoCs. + * + * 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. + * + * Common Clock Framework support for all S5PC110/S5PV210 SoCs. + */ + +#include +#include +#include +#include +#include +#include + +#include "clk.h" +#include "clk-pll.h" + +#include + +/* S5PC110/S5PV210 clock controller register offsets */ +#define APLL_LOCK 0x0000 +#define MPLL_LOCK 0x0008 +#define EPLL_LOCK 0x0010 +#define VPLL_LOCK 0x0020 +#define APLL_CON0 0x0100 +#define APLL_CON1 0x0104 +#define MPLL_CON 0x0108 +#define EPLL_CON0 0x0110 +#define EPLL_CON1 0x0114 +#define VPLL_CON 0x0120 +#define CLK_SRC0 0x0200 +#define CLK_SRC1 0x0204 +#define CLK_SRC2 0x0208 +#define CLK_SRC3 0x020c +#define CLK_SRC4 0x0210 +#define CLK_SRC5 0x0214 +#define CLK_SRC6 0x0218 +#define CLK_SRC_MASK0 0x0280 +#define CLK_SRC_MASK1 0x0284 +#define CLK_DIV0 0x0300 +#define CLK_DIV1 0x0304 +#define CLK_DIV2 0x0308 +#define CLK_DIV3 0x030c +#define CLK_DIV4 0x0310 +#define CLK_DIV5 0x0314 +#define CLK_DIV6 0x0318 +#define CLK_DIV7 0x031c +#define CLK_GATE_MAIN0 0x0400 +#define CLK_GATE_MAIN1 0x0404 +#define CLK_GATE_MAIN2 0x0408 +#define CLK_GATE_PERI0 0x0420 +#define CLK_GATE_PERI1 0x0424 +#define CLK_GATE_SCLK0 0x0440 +#define CLK_GATE_SCLK1 0x0444 +#define CLK_GATE_IP0 0x0460 +#define CLK_GATE_IP1 0x0464 +#define CLK_GATE_IP2 0x0468 +#define CLK_GATE_IP3 0x046c +#define CLK_GATE_IP4 0x0470 +#define CLK_GATE_BLOCK 0x0480 +#define CLK_GATE_IP5 0x0484 +#define CLK_OUT 0x0500 +#define MISC 0xe000 +#define OM_STAT 0xe100 + +/* IDs of PLLs available on S5PV210/S5P6442 SoCs */ +enum { + apll, + mpll, + epll, + vpll, +}; + +/* IDs of external clocks (used for legacy boards) */ +enum { + xxti, + xusbxti, +}; + +static void __iomem *reg_base; + +#ifdef CONFIG_PM_SLEEP +static struct samsung_clk_reg_dump *s5pv210_clk_dump; + +/* List of registers that need to be preserved across suspend/resume. */ +static unsigned long s5pv210_clk_regs[] __initdata = { + CLK_SRC0, + CLK_SRC1, + CLK_SRC2, + CLK_SRC3, + CLK_SRC4, + CLK_SRC5, + CLK_SRC6, + CLK_SRC_MASK0, + CLK_SRC_MASK1, + CLK_DIV0, + CLK_DIV1, + CLK_DIV2, + CLK_DIV3, + CLK_DIV4, + CLK_DIV5, + CLK_DIV6, + CLK_DIV7, + CLK_GATE_MAIN0, + CLK_GATE_MAIN1, + CLK_GATE_MAIN2, + CLK_GATE_PERI0, + CLK_GATE_PERI1, + CLK_GATE_SCLK0, + CLK_GATE_SCLK1, + CLK_GATE_IP0, + CLK_GATE_IP1, + CLK_GATE_IP2, + CLK_GATE_IP3, + CLK_GATE_IP4, + CLK_GATE_IP5, + CLK_GATE_BLOCK, + APLL_LOCK, + MPLL_LOCK, + EPLL_LOCK, + VPLL_LOCK, + APLL_CON0, + APLL_CON1, + MPLL_CON, + EPLL_CON0, + EPLL_CON1, + VPLL_CON, + CLK_OUT, +}; + +static int s5pv210_clk_suspend(void) +{ + samsung_clk_save(reg_base, s5pv210_clk_dump, + ARRAY_SIZE(s5pv210_clk_regs)); + return 0; +} + +static void s5pv210_clk_resume(void) +{ + samsung_clk_restore(reg_base, s5pv210_clk_dump, + ARRAY_SIZE(s5pv210_clk_regs)); +} + +static struct syscore_ops s5pv210_clk_syscore_ops = { + .suspend = s5pv210_clk_suspend, + .resume = s5pv210_clk_resume, +}; + +static void s5pv210_clk_sleep_init(void) +{ + s5pv210_clk_dump = + samsung_clk_alloc_reg_dump(s5pv210_clk_regs, + ARRAY_SIZE(s5pv210_clk_regs)); + if (!s5pv210_clk_dump) { + pr_warn("%s: Failed to allocate sleep save data\n", __func__); + return; + } + + register_syscore_ops(&s5pv210_clk_syscore_ops); +} +#else +static inline void s5pv210_clk_sleep_init(void) { } +#endif + +/* Mux parent lists. */ +static const char *fin_pll_p[] __initconst = { + "xxti", + "xusbxti" +}; + +static const char *mout_apll_p[] __initconst = { + "fin_pll", + "fout_apll" +}; + +static const char *mout_mpll_p[] __initconst = { + "fin_pll", + "fout_mpll" +}; + +static const char *mout_epll_p[] __initconst = { + "fin_pll", + "fout_epll" +}; + +static const char *mout_vpllsrc_p[] __initconst = { + "fin_pll", + "sclk_hdmi27m" +}; + +static const char *mout_vpll_p[] __initconst = { + "mout_vpllsrc", + "fout_vpll" +}; + +static const char *mout_group1_p[] __initconst = { + "dout_a2m", + "mout_mpll", + "mout_epll", + "mout_vpll" +}; + +static const char *mout_group2_p[] __initconst = { + "xxti", + "xusbxti", + "sclk_hdmi27m", + "sclk_usbphy0", + "sclk_usbphy1", + "sclk_hdmiphy", + "mout_mpll", + "mout_epll", + "mout_vpll", +}; + +static const char *mout_audio0_p[] __initconst = { + "xxti", + "pcmcdclk0", + "sclk_hdmi27m", + "sclk_usbphy0", + "sclk_usbphy1", + "sclk_hdmiphy", + "mout_mpll", + "mout_epll", + "mout_vpll", +}; + +static const char *mout_audio1_p[] __initconst = { + "i2scdclk1", + "pcmcdclk1", + "sclk_hdmi27m", + "sclk_usbphy0", + "sclk_usbphy1", + "sclk_hdmiphy", + "mout_mpll", + "mout_epll", + "mout_vpll", +}; + +static const char *mout_audio2_p[] __initconst = { + "i2scdclk2", + "pcmcdclk2", + "sclk_hdmi27m", + "sclk_usbphy0", + "sclk_usbphy1", + "sclk_hdmiphy", + "mout_mpll", + "mout_epll", + "mout_vpll", +}; + +static const char *mout_spdif_p[] __initconst = { + "dout_audio0", + "dout_audio1", + "dout_audio3", +}; + +static const char *mout_group3_p[] __initconst = { + "mout_apll", + "mout_mpll" +}; + +static const char *mout_group4_p[] __initconst = { + "mout_mpll", + "dout_a2m" +}; + +static const char *mout_flash_p[] __initconst = { + "dout_hclkd", + "dout_hclkp" +}; + +static const char *mout_dac_p[] __initconst = { + "mout_vpll", + "sclk_hdmiphy" +}; + +static const char *mout_hdmi_p[] __initconst = { + "sclk_hdmiphy", + "dout_tblk" +}; + +static const char *mout_mixer_p[] __initconst = { + "mout_dac", + "mout_hdmi" +}; + +static const char *mout_vpll_6442_p[] __initconst = { + "fin_pll", + "fout_vpll" +}; + +static const char *mout_mixer_6442_p[] __initconst = { + "mout_vpll", + "dout_mixer" +}; + +static const char *mout_d0sync_6442_p[] __initconst = { + "mout_dsys", + "div_apll" +}; + +static const char *mout_d1sync_6442_p[] __initconst = { + "mout_psys", + "div_apll" +}; + +static const char *mout_group2_6442_p[] __initconst = { + "fin_pll", + "none", + "none", + "sclk_usbphy0", + "none", + "none", + "mout_mpll", + "mout_epll", + "mout_vpll", +}; + +static const char *mout_audio0_6442_p[] __initconst = { + "fin_pll", + "pcmcdclk0", + "none", + "sclk_usbphy0", + "none", + "none", + "mout_mpll", + "mout_epll", + "mout_vpll", +}; + +static const char *mout_audio1_6442_p[] __initconst = { + "i2scdclk1", + "pcmcdclk1", + "none", + "sclk_usbphy0", + "none", + "none", + "mout_mpll", + "mout_epll", + "mout_vpll", + "fin_pll", +}; + +static const char *mout_clksel_p[] __initconst = { + "fout_apll_clkout", + "fout_mpll_clkout", + "fout_epll", + "fout_vpll", + "sclk_usbphy0", + "sclk_usbphy1", + "sclk_hdmiphy", + "rtc", + "rtc_tick", + "dout_hclkm", + "dout_pclkm", + "dout_hclkd", + "dout_pclkd", + "dout_hclkp", + "dout_pclkp", + "dout_apll_clkout", + "dout_hpm", + "xxti", + "xusbxti", + "div_dclk" +}; + +static const char *mout_clksel_6442_p[] __initconst = { + "fout_apll_clkout", + "fout_mpll_clkout", + "fout_epll", + "fout_vpll", + "sclk_usbphy0", + "none", + "none", + "rtc", + "rtc_tick", + "none", + "none", + "dout_hclkd", + "dout_pclkd", + "dout_hclkp", + "dout_pclkp", + "dout_apll_clkout", + "none", + "fin_pll", + "none", + "div_dclk" +}; + +static const char *mout_clkout_p[] __initconst = { + "dout_clkout", + "none", + "xxti", + "xusbxti" +}; + +/* Common fixed factor clocks. */ +static struct samsung_fixed_factor_clock ffactor_clks[] __initdata = { + FFACTOR(FOUT_APLL_CLKOUT, "fout_apll_clkout", "fout_apll", 1, 4, 0), + FFACTOR(FOUT_MPLL_CLKOUT, "fout_mpll_clkout", "fout_mpll", 1, 2, 0), + FFACTOR(DOUT_APLL_CLKOUT, "dout_apll_clkout", "dout_apll", 1, 4, 0), +}; + +/* PLL input mux (fin_pll), which needs to be registered before PLLs. */ +static struct samsung_mux_clock early_mux_clks[] __initdata = { + MUX_F(FIN_PLL, "fin_pll", fin_pll_p, OM_STAT, 0, 1, + CLK_MUX_READ_ONLY, 0), +}; + +/* Common clock muxes. */ +static struct samsung_mux_clock mux_clks[] __initdata = { + MUX(MOUT_FLASH, "mout_flash", mout_flash_p, CLK_SRC0, 28, 1), + MUX(MOUT_PSYS, "mout_psys", mout_group4_p, CLK_SRC0, 24, 1), + MUX(MOUT_DSYS, "mout_dsys", mout_group4_p, CLK_SRC0, 20, 1), + MUX(MOUT_MSYS, "mout_msys", mout_group3_p, CLK_SRC0, 16, 1), + MUX(MOUT_EPLL, "mout_epll", mout_epll_p, CLK_SRC0, 8, 1), + MUX(MOUT_MPLL, "mout_mpll", mout_mpll_p, CLK_SRC0, 4, 1), + MUX(MOUT_APLL, "mout_apll", mout_apll_p, CLK_SRC0, 0, 1), + + MUX(MOUT_CLKOUT, "mout_clkout", mout_clkout_p, MISC, 8, 2), +}; + +/* S5PV210-specific clock muxes. */ +static struct samsung_mux_clock s5pv210_mux_clks[] __initdata = { + MUX(MOUT_VPLL, "mout_vpll", mout_vpll_p, CLK_SRC0, 12, 1), + + MUX(MOUT_VPLLSRC, "mout_vpllsrc", mout_vpllsrc_p, CLK_SRC1, 28, 1), + MUX(MOUT_CSIS, "mout_csis", mout_group2_p, CLK_SRC1, 24, 4), + MUX(MOUT_FIMD, "mout_fimd", mout_group2_p, CLK_SRC1, 20, 4), + MUX(MOUT_CAM1, "mout_cam1", mout_group2_p, CLK_SRC1, 16, 4), + MUX(MOUT_CAM0, "mout_cam0", mout_group2_p, CLK_SRC1, 12, 4), + MUX(MOUT_DAC, "mout_dac", mout_dac_p, CLK_SRC1, 8, 1), + MUX(MOUT_MIXER, "mout_mixer", mout_mixer_p, CLK_SRC1, 4, 1), + MUX(MOUT_HDMI, "mout_hdmi", mout_hdmi_p, CLK_SRC1, 0, 1), + + MUX(MOUT_G2D, "mout_g2d", mout_group1_p, CLK_SRC2, 8, 2), + MUX(MOUT_MFC, "mout_mfc", mout_group1_p, CLK_SRC2, 4, 2), + MUX(MOUT_G3D, "mout_g3d", mout_group1_p, CLK_SRC2, 0, 2), + + MUX(MOUT_FIMC2, "mout_fimc2", mout_group2_p, CLK_SRC3, 20, 4), + MUX(MOUT_FIMC1, "mout_fimc1", mout_group2_p, CLK_SRC3, 16, 4), + MUX(MOUT_FIMC0, "mout_fimc0", mout_group2_p, CLK_SRC3, 12, 4), + + MUX(MOUT_UART3, "mout_uart3", mout_group2_p, CLK_SRC4, 28, 4), + MUX(MOUT_UART2, "mout_uart2", mout_group2_p, CLK_SRC4, 24, 4), + MUX(MOUT_UART1, "mout_uart1", mout_group2_p, CLK_SRC4, 20, 4), + MUX(MOUT_UART0, "mout_uart0", mout_group2_p, CLK_SRC4, 16, 4), + MUX(MOUT_MMC3, "mout_mmc3", mout_group2_p, CLK_SRC4, 12, 4), + MUX(MOUT_MMC2, "mout_mmc2", mout_group2_p, CLK_SRC4, 8, 4), + MUX(MOUT_MMC1, "mout_mmc1", mout_group2_p, CLK_SRC4, 4, 4), + MUX(MOUT_MMC0, "mout_mmc0", mout_group2_p, CLK_SRC4, 0, 4), + + MUX(MOUT_PWM, "mout_pwm", mout_group2_p, CLK_SRC5, 12, 4), + MUX(MOUT_SPI1, "mout_spi1", mout_group2_p, CLK_SRC5, 4, 4), + MUX(MOUT_SPI0, "mout_spi0", mout_group2_p, CLK_SRC5, 0, 4), + + MUX(MOUT_DMC0, "mout_dmc0", mout_group1_p, CLK_SRC6, 24, 2), + MUX(MOUT_PWI, "mout_pwi", mout_group2_p, CLK_SRC6, 20, 4), + MUX(MOUT_HPM, "mout_hpm", mout_group3_p, CLK_SRC6, 16, 1), + MUX(MOUT_SPDIF, "mout_spdif", mout_spdif_p, CLK_SRC6, 12, 2), + MUX(MOUT_AUDIO2, "mout_audio2", mout_audio2_p, CLK_SRC6, 8, 4), + MUX(MOUT_AUDIO1, "mout_audio1", mout_audio1_p, CLK_SRC6, 4, 4), + MUX(MOUT_AUDIO0, "mout_audio0", mout_audio0_p, CLK_SRC6, 0, 4), + + MUX(MOUT_CLKSEL, "mout_clksel", mout_clksel_p, CLK_OUT, 12, 5), +}; + +/* S5P6442-specific clock muxes. */ +static struct samsung_mux_clock s5p6442_mux_clks[] __initdata = { + MUX(MOUT_VPLL, "mout_vpll", mout_vpll_6442_p, CLK_SRC0, 12, 1), + + MUX(MOUT_FIMD, "mout_fimd", mout_group2_6442_p, CLK_SRC1, 20, 4), + MUX(MOUT_CAM1, "mout_cam1", mout_group2_6442_p, CLK_SRC1, 16, 4), + MUX(MOUT_CAM0, "mout_cam0", mout_group2_6442_p, CLK_SRC1, 12, 4), + MUX(MOUT_MIXER, "mout_mixer", mout_mixer_6442_p, CLK_SRC1, 4, 1), + + MUX(MOUT_D0SYNC, "mout_d0sync", mout_d0sync_6442_p, CLK_SRC2, 28, 1), + MUX(MOUT_D1SYNC, "mout_d1sync", mout_d1sync_6442_p, CLK_SRC2, 24, 1), + + MUX(MOUT_FIMC2, "mout_fimc2", mout_group2_6442_p, CLK_SRC3, 20, 4), + MUX(MOUT_FIMC1, "mout_fimc1", mout_group2_6442_p, CLK_SRC3, 16, 4), + MUX(MOUT_FIMC0, "mout_fimc0", mout_group2_6442_p, CLK_SRC3, 12, 4), + + MUX(MOUT_UART2, "mout_uart2", mout_group2_6442_p, CLK_SRC4, 24, 4), + MUX(MOUT_UART1, "mout_uart1", mout_group2_6442_p, CLK_SRC4, 20, 4), + MUX(MOUT_UART0, "mout_uart0", mout_group2_6442_p, CLK_SRC4, 16, 4), + MUX(MOUT_MMC2, "mout_mmc2", mout_group2_6442_p, CLK_SRC4, 8, 4), + MUX(MOUT_MMC1, "mout_mmc1", mout_group2_6442_p, CLK_SRC4, 4, 4), + MUX(MOUT_MMC0, "mout_mmc0", mout_group2_6442_p, CLK_SRC4, 0, 4), + + MUX(MOUT_PWM, "mout_pwm", mout_group2_6442_p, CLK_SRC5, 12, 4), + MUX(MOUT_SPI0, "mout_spi0", mout_group2_6442_p, CLK_SRC5, 0, 4), + + MUX(MOUT_AUDIO1, "mout_audio1", mout_audio1_6442_p, CLK_SRC6, 4, 4), + MUX(MOUT_AUDIO0, "mout_audio0", mout_audio0_6442_p, CLK_SRC6, 0, 4), + + MUX(MOUT_CLKSEL, "mout_clksel", mout_clksel_6442_p, CLK_OUT, 12, 5), +}; + +/* + * Common fixed rate clocks generated outside the SoC. + * NOTE: Needed only to support legacy board files. + */ +static struct samsung_fixed_rate_clock ext_clks[] __initdata = { + [xxti] = FRATE(0, "xxti", NULL, CLK_IS_ROOT, 0), + [xusbxti] = FRATE(0, "xusbxti", NULL, CLK_IS_ROOT, 0), +}; + +/* S5PV210-specific fixed rate clocks generated inside the SoC. */ +static struct samsung_fixed_rate_clock s5pv210_frate_clks[] __initdata = { + FRATE(SCLK_HDMI27M, "sclk_hdmi27m", NULL, CLK_IS_ROOT, 27000000), + FRATE(SCLK_HDMIPHY, "sclk_hdmiphy", NULL, CLK_IS_ROOT, 27000000), + FRATE(SCLK_USBPHY0, "sclk_usbphy0", NULL, CLK_IS_ROOT, 48000000), + FRATE(SCLK_USBPHY1, "sclk_usbphy1", NULL, CLK_IS_ROOT, 48000000), +}; + +/* S5P6442-specific fixed rate clocks generated inside the SoC. */ +static struct samsung_fixed_rate_clock s5p6442_frate_clks[] __initdata = { + FRATE(SCLK_USBPHY0, "sclk_usbphy0", NULL, CLK_IS_ROOT, 30000000), +}; + +/* Common clock dividers. */ +static struct samsung_div_clock div_clks[] __initdata = { + DIV(DOUT_PCLKP, "dout_pclkp", "dout_hclkp", CLK_DIV0, 28, 3), + DIV(DOUT_PCLKD, "dout_pclkd", "dout_hclkd", CLK_DIV0, 20, 3), + DIV(DOUT_A2M, "dout_a2m", "mout_apll", CLK_DIV0, 4, 3), + DIV(DOUT_APLL, "dout_apll", "mout_msys", CLK_DIV0, 0, 3), + + DIV(DOUT_FIMD, "dout_fimd", "mout_fimd", CLK_DIV1, 20, 4), + DIV(DOUT_CAM1, "dout_cam1", "mout_cam1", CLK_DIV1, 16, 4), + DIV(DOUT_CAM0, "dout_cam0", "mout_cam0", CLK_DIV1, 12, 4), + + DIV(DOUT_FIMC2, "dout_fimc2", "mout_fimc2", CLK_DIV3, 20, 4), + DIV(DOUT_FIMC1, "dout_fimc1", "mout_fimc1", CLK_DIV3, 16, 4), + DIV(DOUT_FIMC0, "dout_fimc0", "mout_fimc0", CLK_DIV3, 12, 4), + + DIV(DOUT_UART2, "dout_uart2", "mout_uart2", CLK_DIV4, 24, 4), + DIV(DOUT_UART1, "dout_uart1", "mout_uart1", CLK_DIV4, 20, 4), + DIV(DOUT_UART0, "dout_uart0", "mout_uart0", CLK_DIV4, 16, 4), + DIV(DOUT_MMC2, "dout_mmc2", "mout_mmc2", CLK_DIV4, 8, 4), + DIV(DOUT_MMC1, "dout_mmc1", "mout_mmc1", CLK_DIV4, 4, 4), + DIV(DOUT_MMC0, "dout_mmc0", "mout_mmc0", CLK_DIV4, 0, 4), + + DIV(DOUT_PWM, "dout_pwm", "mout_pwm", CLK_DIV5, 12, 4), + DIV(DOUT_SPI0, "dout_spi0", "mout_spi0", CLK_DIV5, 0, 4), + + DIV(DOUT_FLASH, "dout_flash", "mout_flash", CLK_DIV6, 12, 3), + DIV(DOUT_AUDIO1, "dout_audio1", "mout_audio1", CLK_DIV6, 4, 4), + DIV(DOUT_AUDIO0, "dout_audio0", "mout_audio0", CLK_DIV6, 0, 4), + + DIV(DOUT_CLKOUT, "dout_clkout", "mout_clksel", CLK_OUT, 20, 4), +}; + +/* S5PV210-specific clock dividers. */ +static struct samsung_div_clock s5pv210_div_clks[] __initdata = { + DIV(DOUT_HCLKP, "dout_hclkp", "mout_psys", CLK_DIV0, 24, 4), + DIV(DOUT_HCLKD, "dout_hclkd", "mout_dsys", CLK_DIV0, 16, 4), + DIV(DOUT_PCLKM, "dout_pclkm", "dout_hclkm", CLK_DIV0, 12, 3), + DIV(DOUT_HCLKM, "dout_hclkm", "dout_apll", CLK_DIV0, 8, 3), + + DIV(DOUT_CSIS, "dout_csis", "mout_csis", CLK_DIV1, 28, 4), + DIV(DOUT_TBLK, "dout_tblk", "mout_vpll", CLK_DIV1, 0, 4), + + DIV(DOUT_G2D, "dout_g2d", "mout_g2d", CLK_DIV2, 8, 4), + DIV(DOUT_MFC, "dout_mfc", "mout_mfc", CLK_DIV2, 4, 4), + DIV(DOUT_G3D, "dout_g3d", "mout_g3d", CLK_DIV2, 0, 4), + + DIV(DOUT_UART3, "dout_uart3", "mout_uart3", CLK_DIV4, 28, 4), + DIV(DOUT_MMC3, "dout_mmc3", "mout_mmc3", CLK_DIV4, 12, 4), + + DIV(DOUT_SPI1, "dout_spi1", "mout_spi1", CLK_DIV5, 4, 4), + + DIV(DOUT_DMC0, "dout_dmc0", "mout_dmc0", CLK_DIV6, 28, 4), + DIV(DOUT_PWI, "dout_pwi", "mout_pwi", CLK_DIV6, 24, 4), + DIV(DOUT_HPM, "dout_hpm", "dout_copy", CLK_DIV6, 20, 3), + DIV(DOUT_COPY, "dout_copy", "mout_hpm", CLK_DIV6, 16, 3), + DIV(DOUT_AUDIO2, "dout_audio2", "mout_audio2", CLK_DIV6, 8, 4), + + DIV(DOUT_DPM, "dout_dpm", "dout_pclkp", CLK_DIV7, 8, 7), + DIV(DOUT_DVSEM, "dout_dvsem", "dout_pclkp", CLK_DIV7, 0, 7), +}; + +/* S5P6442-specific clock dividers. */ +static struct samsung_div_clock s5p6442_div_clks[] __initdata = { + DIV(DOUT_HCLKP, "dout_hclkp", "mout_d1sync", CLK_DIV0, 24, 4), + DIV(DOUT_HCLKD, "dout_hclkd", "mout_d0sync", CLK_DIV0, 16, 4), + + DIV(DOUT_MIXER, "dout_mixer", "mout_vpll", CLK_DIV1, 0, 4), +}; + +/* Common clock gates. */ +static struct samsung_gate_clock gate_clks[] __initdata = { + GATE(CLK_ROTATOR, "rotator", "dout_hclkd", CLK_GATE_IP0, 29, 0, 0), + GATE(CLK_FIMC2, "fimc2", "dout_hclkd", CLK_GATE_IP0, 26, 0, 0), + GATE(CLK_FIMC1, "fimc1", "dout_hclkd", CLK_GATE_IP0, 25, 0, 0), + GATE(CLK_FIMC0, "fimc0", "dout_hclkd", CLK_GATE_IP0, 24, 0, 0), + GATE(CLK_PDMA0, "pdma0", "dout_hclkp", CLK_GATE_IP0, 3, 0, 0), + GATE(CLK_MDMA, "mdma", "dout_hclkd", CLK_GATE_IP0, 2, 0, 0), + + GATE(CLK_SROMC, "sromc", "dout_hclkp", CLK_GATE_IP1, 26, 0, 0), + GATE(CLK_NANDXL, "nandxl", "dout_hclkp", CLK_GATE_IP1, 24, 0, 0), + GATE(CLK_USB_OTG, "usb_otg", "dout_hclkp", CLK_GATE_IP1, 16, 0, 0), + GATE(CLK_TVENC, "tvenc", "dout_hclkd", CLK_GATE_IP1, 10, 0, 0), + GATE(CLK_MIXER, "mixer", "dout_hclkd", CLK_GATE_IP1, 9, 0, 0), + GATE(CLK_VP, "vp", "dout_hclkd", CLK_GATE_IP1, 8, 0, 0), + GATE(CLK_FIMD, "fimd", "dout_hclkd", CLK_GATE_IP1, 0, 0, 0), + + GATE(CLK_HSMMC2, "hsmmc2", "dout_hclkp", CLK_GATE_IP2, 18, 0, 0), + GATE(CLK_HSMMC1, "hsmmc1", "dout_hclkp", CLK_GATE_IP2, 17, 0, 0), + GATE(CLK_HSMMC0, "hsmmc0", "dout_hclkp", CLK_GATE_IP2, 16, 0, 0), + GATE(CLK_MODEMIF, "modemif", "dout_hclkp", CLK_GATE_IP2, 9, 0, 0), + GATE(CLK_SECSS, "secss", "dout_hclkp", CLK_GATE_IP2, 0, 0, 0), + + GATE(CLK_PCM1, "pcm1", "dout_pclkp", CLK_GATE_IP3, 29, 0, 0), + GATE(CLK_PCM0, "pcm0", "dout_pclkp", CLK_GATE_IP3, 28, 0, 0), + GATE(CLK_TSADC, "tsadc", "dout_pclkp", CLK_GATE_IP3, 24, 0, 0), + GATE(CLK_PWM, "pwm", "dout_pclkp", CLK_GATE_IP3, 23, 0, 0), + GATE(CLK_WDT, "watchdog", "dout_pclkp", CLK_GATE_IP3, 22, 0, 0), + GATE(CLK_KEYIF, "keyif", "dout_pclkp", CLK_GATE_IP3, 21, 0, 0), + GATE(CLK_UART2, "uart2", "dout_pclkp", CLK_GATE_IP3, 19, 0, 0), + GATE(CLK_UART1, "uart1", "dout_pclkp", CLK_GATE_IP3, 18, 0, 0), + GATE(CLK_UART0, "uart0", "dout_pclkp", CLK_GATE_IP3, 17, 0, 0), + GATE(CLK_SYSTIMER, "systimer", "dout_pclkp", CLK_GATE_IP3, 16, 0, 0), + GATE(CLK_RTC, "rtc", "dout_pclkp", CLK_GATE_IP3, 15, 0, 0), + GATE(CLK_SPI0, "spi0", "dout_pclkp", CLK_GATE_IP3, 12, 0, 0), + GATE(CLK_I2C2, "i2c2", "dout_pclkp", CLK_GATE_IP3, 9, 0, 0), + GATE(CLK_I2C0, "i2c0", "dout_pclkp", CLK_GATE_IP3, 7, 0, 0), + GATE(CLK_I2S1, "i2s1", "dout_pclkp", CLK_GATE_IP3, 5, 0, 0), + GATE(CLK_I2S0, "i2s0", "dout_pclkp", CLK_GATE_IP3, 4, 0, 0), + + GATE(CLK_SECKEY, "seckey", "dout_pclkp", CLK_GATE_IP4, 3, 0, 0), + GATE(CLK_CHIPID, "chipid", "dout_pclkp", CLK_GATE_IP4, 0, 0, 0), + + GATE(SCLK_AUDIO1, "sclk_audio1", "dout_audio1", CLK_SRC_MASK0, 25, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_AUDIO0, "sclk_audio0", "dout_audio0", CLK_SRC_MASK0, 24, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_PWM, "sclk_pwm", "dout_pwm", CLK_SRC_MASK0, 19, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_SPI0, "sclk_spi0", "dout_spi0", CLK_SRC_MASK0, 16, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_UART2, "sclk_uart2", "dout_uart2", CLK_SRC_MASK0, 14, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_UART1, "sclk_uart1", "dout_uart1", CLK_SRC_MASK0, 13, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_UART0, "sclk_uart0", "dout_uart0", CLK_SRC_MASK0, 12, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_MMC2, "sclk_mmc2", "dout_mmc2", CLK_SRC_MASK0, 10, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_MMC1, "sclk_mmc1", "dout_mmc1", CLK_SRC_MASK0, 9, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_MMC0, "sclk_mmc0", "dout_mmc0", CLK_SRC_MASK0, 8, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_FIMD, "sclk_fimd", "dout_fimd", CLK_SRC_MASK0, 5, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_CAM1, "sclk_cam1", "dout_cam1", CLK_SRC_MASK0, 4, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_CAM0, "sclk_cam0", "dout_cam0", CLK_SRC_MASK0, 3, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_MIXER, "sclk_mixer", "mout_mixer", CLK_SRC_MASK0, 1, + CLK_SET_RATE_PARENT, 0), + + GATE(SCLK_FIMC2, "sclk_fimc2", "dout_fimc2", CLK_SRC_MASK1, 4, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_FIMC1, "sclk_fimc1", "dout_fimc1", CLK_SRC_MASK1, 3, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_FIMC0, "sclk_fimc0", "dout_fimc0", CLK_SRC_MASK1, 2, + CLK_SET_RATE_PARENT, 0), +}; + +/* S5PV210-specific clock gates. */ +static struct samsung_gate_clock s5pv210_gate_clks[] __initdata = { + GATE(CLK_CSIS, "clk_csis", "dout_hclkd", CLK_GATE_IP0, 31, 0, 0), + GATE(CLK_MFC, "mfc", "dout_hclkm", CLK_GATE_IP0, 16, 0, 0), + GATE(CLK_G2D, "g2d", "dout_hclkd", CLK_GATE_IP0, 12, 0, 0), + GATE(CLK_G3D, "g3d", "dout_hclkm", CLK_GATE_IP0, 8, 0, 0), + GATE(CLK_IMEM, "imem", "dout_hclkm", CLK_GATE_IP0, 5, 0, 0), + GATE(CLK_PDMA1, "pdma1", "dout_hclkp", CLK_GATE_IP0, 4, 0, 0), + + GATE(CLK_NFCON, "nfcon", "dout_hclkp", CLK_GATE_IP1, 28, 0, 0), + GATE(CLK_CFCON, "cfcon", "dout_hclkp", CLK_GATE_IP1, 25, 0, 0), + GATE(CLK_USB_HOST, "usb_host", "dout_hclkp", CLK_GATE_IP1, 17, 0, 0), + GATE(CLK_HDMI, "hdmi", "dout_hclkd", CLK_GATE_IP1, 11, 0, 0), + GATE(CLK_DSIM, "dsim", "dout_pclkd", CLK_GATE_IP1, 2, 0, 0), + + GATE(CLK_TZIC3, "tzic3", "dout_hclkm", CLK_GATE_IP2, 31, 0, 0), + GATE(CLK_TZIC2, "tzic2", "dout_hclkm", CLK_GATE_IP2, 30, 0, 0), + GATE(CLK_TZIC1, "tzic1", "dout_hclkm", CLK_GATE_IP2, 29, 0, 0), + GATE(CLK_TZIC0, "tzic0", "dout_hclkm", CLK_GATE_IP2, 28, 0, 0), + GATE(CLK_TSI, "tsi", "dout_hclkd", CLK_GATE_IP2, 20, 0, 0), + GATE(CLK_HSMMC3, "hsmmc3", "dout_hclkp", CLK_GATE_IP2, 19, 0, 0), + GATE(CLK_JTAG, "jtag", "dout_hclkp", CLK_GATE_IP2, 11, 0, 0), + GATE(CLK_CORESIGHT, "coresight", "dout_pclkp", CLK_GATE_IP2, 8, 0, 0), + GATE(CLK_SDM, "sdm", "dout_pclkm", CLK_GATE_IP2, 1, 0, 0), + + GATE(CLK_PCM2, "pcm2", "dout_pclkp", CLK_GATE_IP3, 30, 0, 0), + GATE(CLK_UART3, "uart3", "dout_pclkp", CLK_GATE_IP3, 20, 0, 0), + GATE(CLK_SPI1, "spi1", "dout_pclkp", CLK_GATE_IP3, 13, 0, 0), + GATE(CLK_I2C_HDMI_PHY, "i2c_hdmi_phy", "dout_pclkd", + CLK_GATE_IP3, 11, 0, 0), + GATE(CLK_I2C1, "i2c1", "dout_pclkd", CLK_GATE_IP3, 10, 0, 0), + GATE(CLK_I2S2, "i2s2", "dout_pclkp", CLK_GATE_IP3, 6, 0, 0), + GATE(CLK_AC97, "ac97", "dout_pclkp", CLK_GATE_IP3, 1, 0, 0), + GATE(CLK_SPDIF, "spdif", "dout_pclkp", CLK_GATE_IP3, 0, 0, 0), + + GATE(CLK_TZPC3, "tzpc.3", "dout_pclkd", CLK_GATE_IP4, 8, 0, 0), + GATE(CLK_TZPC2, "tzpc.2", "dout_pclkd", CLK_GATE_IP4, 7, 0, 0), + GATE(CLK_TZPC1, "tzpc.1", "dout_pclkp", CLK_GATE_IP4, 6, 0, 0), + GATE(CLK_TZPC0, "tzpc.0", "dout_pclkm", CLK_GATE_IP4, 5, 0, 0), + GATE(CLK_IEM_APC, "iem_apc", "dout_pclkp", CLK_GATE_IP4, 2, 0, 0), + GATE(CLK_IEM_IEC, "iem_iec", "dout_pclkp", CLK_GATE_IP4, 1, 0, 0), + + GATE(CLK_JPEG, "jpeg", "dout_hclkd", CLK_GATE_IP5, 29, 0, 0), + + GATE(SCLK_SPDIF, "sclk_spdif", "mout_spdif", CLK_SRC_MASK0, 27, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_AUDIO2, "sclk_audio2", "dout_audio2", CLK_SRC_MASK0, 26, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_SPI1, "sclk_spi1", "dout_spi1", CLK_SRC_MASK0, 17, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_UART3, "sclk_uart3", "dout_uart3", CLK_SRC_MASK0, 15, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_MMC3, "sclk_mmc3", "dout_mmc3", CLK_SRC_MASK0, 11, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_CSIS, "sclk_csis", "dout_csis", CLK_SRC_MASK0, 6, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_DAC, "sclk_dac", "mout_dac", CLK_SRC_MASK0, 2, + CLK_SET_RATE_PARENT, 0), + GATE(SCLK_HDMI, "sclk_hdmi", "mout_hdmi", CLK_SRC_MASK0, 0, + CLK_SET_RATE_PARENT, 0), +}; + +/* S5P6442-specific clock gates. */ +static struct samsung_gate_clock s5p6442_gate_clks[] __initdata = { + GATE(CLK_JPEG, "jpeg", "dout_hclkd", CLK_GATE_IP0, 28, 0, 0), + GATE(CLK_MFC, "mfc", "dout_hclkd", CLK_GATE_IP0, 16, 0, 0), + GATE(CLK_G2D, "g2d", "dout_hclkd", CLK_GATE_IP0, 12, 0, 0), + GATE(CLK_G3D, "g3d", "dout_hclkd", CLK_GATE_IP0, 8, 0, 0), + GATE(CLK_IMEM, "imem", "dout_hclkd", CLK_GATE_IP0, 5, 0, 0), + + GATE(CLK_ETB, "etb", "dout_hclkd", CLK_GATE_IP1, 31, 0, 0), + GATE(CLK_ETM, "etm", "dout_hclkd", CLK_GATE_IP1, 30, 0, 0), + + GATE(CLK_I2C1, "i2c1", "dout_pclkp", CLK_GATE_IP3, 8, 0, 0), + + GATE(SCLK_DAC, "sclk_dac", "mout_vpll", CLK_SRC_MASK0, 2, + CLK_SET_RATE_PARENT, 0), +}; + +/* + * Clock aliases for legacy clkdev look-up. + * NOTE: Needed only to support legacy board files. + */ +static struct samsung_clock_alias s5pv210_aliases[] = { + ALIAS(CLK_FIMC0, "s5pv210-fimc.0", "fimc"), + ALIAS(CLK_FIMC1, "s5pv210-fimc.1", "fimc"), + ALIAS(CLK_FIMC2, "s5pv210-fimc.2", "fimc"), + ALIAS(SCLK_FIMC0, "s5pv210-fimc.0", "sclk_fimc"), + ALIAS(SCLK_FIMC1, "s5pv210-fimc.1", "sclk_fimc"), + ALIAS(SCLK_FIMC2, "s5pv210-fimc.2", "sclk_fimc"), + ALIAS(DOUT_APLL, NULL, "armclk"), + ALIAS(DOUT_HCLKM, NULL, "hclk_msys"), + ALIAS(MOUT_DMC0, NULL, "sclk_dmc0"), + ALIAS(CLK_UART0, "s5pv210-uart.0", "uart"), + ALIAS(CLK_UART1, "s5pv210-uart.1", "uart"), + ALIAS(CLK_UART2, "s5pv210-uart.2", "uart"), + ALIAS(CLK_UART3, "s5pv210-uart.3", "uart"), + ALIAS(CLK_UART0, "s5pv210-uart.0", "clk_uart_baud0"), + ALIAS(CLK_UART1, "s5pv210-uart.1", "clk_uart_baud0"), + ALIAS(CLK_UART2, "s5pv210-uart.2", "clk_uart_baud0"), + ALIAS(CLK_UART3, "s5pv210-uart.3", "clk_uart_baud0"), + ALIAS(SCLK_UART0, "s5pv210-uart.0", "clk_uart_baud1"), + ALIAS(SCLK_UART1, "s5pv210-uart.1", "clk_uart_baud1"), + ALIAS(SCLK_UART2, "s5pv210-uart.2", "clk_uart_baud1"), + ALIAS(SCLK_UART3, "s5pv210-uart.3", "clk_uart_baud1"), + ALIAS(CLK_HSMMC0, "s3c-sdhci.0", "hsmmc"), + ALIAS(CLK_HSMMC1, "s3c-sdhci.1", "hsmmc"), + ALIAS(CLK_HSMMC2, "s3c-sdhci.2", "hsmmc"), + ALIAS(CLK_HSMMC3, "s3c-sdhci.3", "hsmmc"), + ALIAS(CLK_HSMMC0, "s3c-sdhci.0", "mmc_busclk.0"), + ALIAS(CLK_HSMMC1, "s3c-sdhci.1", "mmc_busclk.0"), + ALIAS(CLK_HSMMC2, "s3c-sdhci.2", "mmc_busclk.0"), + ALIAS(CLK_HSMMC3, "s3c-sdhci.3", "mmc_busclk.0"), + ALIAS(SCLK_MMC0, "s3c-sdhci.0", "mmc_busclk.2"), + ALIAS(SCLK_MMC1, "s3c-sdhci.1", "mmc_busclk.2"), + ALIAS(SCLK_MMC2, "s3c-sdhci.2", "mmc_busclk.2"), + ALIAS(SCLK_MMC3, "s3c-sdhci.3", "mmc_busclk.2"), + ALIAS(CLK_SPI0, "s5pv210-spi.0", "spi_busclk0"), + ALIAS(CLK_SPI1, "s5pv210-spi.1", "spi_busclk0"), + ALIAS(SCLK_SPI0, "s5pv210-spi.0", "spi_busclk1"), + ALIAS(SCLK_SPI1, "s5pv210-spi.1", "spi_busclk1"), + ALIAS(CLK_PDMA0, "dma-pl330.0", "apb_pclk"), + ALIAS(CLK_PDMA1, "dma-pl330.1", "apb_pclk"), + ALIAS(CLK_PWM, NULL, "timers"), + ALIAS(CLK_NANDXL, "s5pc110-onenand", "gate"), + ALIAS(CLK_JPEG, NULL, "jpeg"), + ALIAS(CLK_MFC, "s5p-mfc", "mfc"), + ALIAS(CLK_TVENC, "s5p-sdo", "dac"), + ALIAS(CLK_MIXER, "s5p-mixer", "mixer"), + ALIAS(CLK_VP, "s5p-mixer", "vp"), + ALIAS(CLK_HDMI, "s5p-hdmi", "hdmi"), + ALIAS(SCLK_HDMI, "s5p-hdmi", "hdmiphy"), + ALIAS(SCLK_DAC, NULL, "sclk_dac"), + ALIAS(CLK_USB_OTG, NULL, "usbotg"), + ALIAS(CLK_USB_OTG, NULL, "otg"), + ALIAS(CLK_USB_HOST, NULL, "usb-host"), + ALIAS(CLK_USB_HOST, NULL, "usbhost"), + ALIAS(CLK_FIMD, "s5pv210-fb", "lcd"), + ALIAS(CLK_CFCON, "s5pv210-pata.0", "cfcon"), + ALIAS(CLK_WDT, NULL, "watchdog"), + ALIAS(CLK_RTC, NULL, "rtc"), + ALIAS(CLK_I2C0, "s3c2440-i2c.0", "i2c"), + ALIAS(CLK_I2C1, "s3c2440-i2c.1", "i2c"), + ALIAS(CLK_I2C2, "s3c2440-i2c.2", "i2c"), + ALIAS(CLK_I2C_HDMI_PHY, "s3c2440-hdmiphy-i2c", "i2c"), + ALIAS(CLK_TSADC, NULL, "adc"), + ALIAS(CLK_KEYIF, "s5pv210-keypad", "keypad"), + ALIAS(CLK_I2S0, "samsung-i2s.0", "iis"), + ALIAS(CLK_I2S1, "samsung-i2s.1", "iis"), + ALIAS(CLK_I2S2, "samsung-i2s.2", "iis"), + ALIAS(CLK_SPDIF, NULL, "spdif"), + ALIAS(SCLK_AUDIO0, "soc-audio.0", "sclk_audio"), + ALIAS(SCLK_AUDIO1, "soc-audio.1", "sclk_audio"), + ALIAS(SCLK_AUDIO2, "soc-audio.2", "sclk_audio"), + ALIAS(CLK_MFC, "s5p-mfc", "sclk_mfc"), + ALIAS(SCLK_CAM0, "sclk_cam0", "sclk_cam0"), + ALIAS(SCLK_CAM1, "sclk_cam1", "sclk_cam1"), + ALIAS(CLK_G2D, "s5p-g2d", "fimg2d"), + ALIAS(DOUT_G2D, "s5p-g2d", "sclk_fimg2d"), + ALIAS(CLK_CSIS, "s5p-mipi-csis", "csis"), + ALIAS(SCLK_CSIS, "s5p-mipi-csis", "sclk_csis"), + ALIAS(SCLK_PWM, "samsung-pwm", "pwm-tclk0"), + ALIAS(SCLK_PWM, "samsung-pwm", "pwm-tclk1"), + ALIAS(SCLK_FIMD, NULL, "sclk_fimd"), + ALIAS(MOUT_CAM0, NULL, "mout_cam0"), + ALIAS(MOUT_CAM1, NULL, "mout_cam1"), + ALIAS(MOUT_CSIS, NULL, "mout_csis"), + ALIAS(MOUT_VPLL, NULL, "sclk_vpll"), + ALIAS(SCLK_MIXER, NULL, "sclk_mixer"), + ALIAS(SCLK_HDMI, NULL, "sclk_hdmi"), +}; + +static void __init s5pv210_clk_register_fixed_ext( + struct samsung_clk_provider *ctx, + unsigned long xxti_f, + unsigned long xusbxti_f) +{ + ext_clks[xxti].fixed_rate = xxti_f; + ext_clks[xusbxti].fixed_rate = xusbxti_f; + samsung_clk_register_fixed_rate(ctx, ext_clks, ARRAY_SIZE(ext_clks)); +} + +/* S5PV210-specific PLLs. */ +static struct samsung_pll_clock s5pv210_pll_clks[] __initdata = { + [apll] = PLL(pll_4508, FOUT_APLL, "fout_apll", "fin_pll", + APLL_LOCK, APLL_CON0, NULL), + [mpll] = PLL(pll_4502, FOUT_MPLL, "fout_mpll", "fin_pll", + MPLL_LOCK, MPLL_CON, NULL), + [epll] = PLL(pll_4600, FOUT_EPLL, "fout_epll", "fin_pll", + EPLL_LOCK, EPLL_CON0, NULL), + [vpll] = PLL(pll_4502, FOUT_VPLL, "fout_vpll", "mout_vpllsrc", + VPLL_LOCK, VPLL_CON, NULL), +}; + +/* S5P6442-specific PLLs. */ +static struct samsung_pll_clock s5p6442_pll_clks[] __initdata = { + [apll] = PLL(pll_4502, FOUT_APLL, "fout_apll", "fin_pll", + APLL_LOCK, APLL_CON0, NULL), + [mpll] = PLL(pll_4502, FOUT_MPLL, "fout_mpll", "fin_pll", + MPLL_LOCK, MPLL_CON, NULL), + [epll] = PLL(pll_4500, FOUT_EPLL, "fout_epll", "fin_pll", + EPLL_LOCK, EPLL_CON0, NULL), + [vpll] = PLL(pll_4500, FOUT_VPLL, "fout_vpll", "fin_pll", + VPLL_LOCK, VPLL_CON, NULL), +}; + +static void __init __s5pv210_clk_init(struct device_node *np, + unsigned long xxti_f, + unsigned long xusbxti_f, + bool is_s5p6442) +{ + struct samsung_clk_provider *ctx; + + ctx = samsung_clk_init(np, reg_base, NR_CLKS); + if (!ctx) + panic("%s: unable to allocate context.\n", __func__); + + /* Register external clocks (needed by board files). */ + if (!np) + s5pv210_clk_register_fixed_ext(ctx, xxti_f, xusbxti_f); + + samsung_clk_register_mux(ctx, early_mux_clks, + ARRAY_SIZE(early_mux_clks)); + + if (is_s5p6442) { + samsung_clk_register_fixed_rate(ctx, s5p6442_frate_clks, + ARRAY_SIZE(s5p6442_frate_clks)); + samsung_clk_register_pll(ctx, s5p6442_pll_clks, + ARRAY_SIZE(s5p6442_pll_clks), reg_base); + samsung_clk_register_mux(ctx, s5p6442_mux_clks, + ARRAY_SIZE(s5p6442_mux_clks)); + samsung_clk_register_div(ctx, s5p6442_div_clks, + ARRAY_SIZE(s5p6442_div_clks)); + samsung_clk_register_gate(ctx, s5p6442_gate_clks, + ARRAY_SIZE(s5p6442_gate_clks)); + } else { + samsung_clk_register_fixed_rate(ctx, s5pv210_frate_clks, + ARRAY_SIZE(s5pv210_frate_clks)); + samsung_clk_register_pll(ctx, s5pv210_pll_clks, + ARRAY_SIZE(s5pv210_pll_clks), reg_base); + samsung_clk_register_mux(ctx, s5pv210_mux_clks, + ARRAY_SIZE(s5pv210_mux_clks)); + samsung_clk_register_div(ctx, s5pv210_div_clks, + ARRAY_SIZE(s5pv210_div_clks)); + samsung_clk_register_gate(ctx, s5pv210_gate_clks, + ARRAY_SIZE(s5pv210_gate_clks)); + } + + samsung_clk_register_mux(ctx, mux_clks, ARRAY_SIZE(mux_clks)); + samsung_clk_register_div(ctx, div_clks, ARRAY_SIZE(div_clks)); + samsung_clk_register_gate(ctx, gate_clks, ARRAY_SIZE(gate_clks)); + + samsung_clk_register_fixed_factor(ctx, ffactor_clks, + ARRAY_SIZE(ffactor_clks)); + + samsung_clk_register_alias(ctx, s5pv210_aliases, + ARRAY_SIZE(s5pv210_aliases)); + + s5pv210_clk_sleep_init(); + + pr_info("%s clocks: mout_apll = %ld, mout_mpll = %ld\n" + "\tmout_epll = %ld, mout_vpll = %ld\n", + is_s5p6442 ? "S5P6442" : "S5PV210", + _get_rate("mout_apll"), _get_rate("mout_mpll"), + _get_rate("mout_epll"), _get_rate("mout_vpll")); +} + +/** + * s5pv210_clk_init + * @xxti_f: Rate of XXTI input clock. + * @xusbxti_f: Rate of XUSBXTI input clock. + * @base: + */ +void __init s5pv210_clk_init(unsigned long xxti_f, unsigned long xusbxti_f, + void __iomem *base) +{ + reg_base = base; + + __s5pv210_clk_init(NULL, xxti_f, xusbxti_f, false); +} + +static void __init s5pv210_clk_dt_init(struct device_node *np) +{ + reg_base = of_iomap(np, 0); + if (!reg_base) + panic("%s: failed to map registers\n", __func__); + + __s5pv210_clk_init(np, 0, 0, false); +} +CLK_OF_DECLARE(s5pv210_clk, "samsung,s5pv210-clock", s5pv210_clk_dt_init); + +static void __init s5p6442_clk_dt_init(struct device_node *np) +{ + reg_base = of_iomap(np, 0); + if (!reg_base) + panic("%s: failed to map registers\n", __func__); + + __s5pv210_clk_init(np, 0, 0, true); +} +CLK_OF_DECLARE(s5p6442_clk, "samsung,s5p6442-clock", s5p6442_clk_dt_init); diff --git a/include/dt-bindings/clock/s5pv210.h b/include/dt-bindings/clock/s5pv210.h new file mode 100644 index 000000000000..e88986b7c677 --- /dev/null +++ b/include/dt-bindings/clock/s5pv210.h @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * Author: Mateusz Krawczuk + * + * 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. + * + * Device Tree binding constants for Samsung S5PV210 clock controller. + */ + +#ifndef _DT_BINDINGS_CLOCK_S5PV210_H +#define _DT_BINDINGS_CLOCK_S5PV210_H + +/* Core clocks. */ +#define FIN_PLL 1 +#define FOUT_APLL 2 +#define FOUT_MPLL 3 +#define FOUT_EPLL 4 +#define FOUT_VPLL 5 + +/* Muxes. */ +#define MOUT_FLASH 6 +#define MOUT_PSYS 7 +#define MOUT_DSYS 8 +#define MOUT_MSYS 9 +#define MOUT_VPLL 10 +#define MOUT_EPLL 11 +#define MOUT_MPLL 12 +#define MOUT_APLL 13 +#define MOUT_VPLLSRC 14 +#define MOUT_CSIS 15 +#define MOUT_FIMD 16 +#define MOUT_CAM1 17 +#define MOUT_CAM0 18 +#define MOUT_DAC 19 +#define MOUT_MIXER 20 +#define MOUT_HDMI 21 +#define MOUT_G2D 22 +#define MOUT_MFC 23 +#define MOUT_G3D 24 +#define MOUT_FIMC2 25 +#define MOUT_FIMC1 26 +#define MOUT_FIMC0 27 +#define MOUT_UART3 28 +#define MOUT_UART2 29 +#define MOUT_UART1 30 +#define MOUT_UART0 31 +#define MOUT_MMC3 32 +#define MOUT_MMC2 33 +#define MOUT_MMC1 34 +#define MOUT_MMC0 35 +#define MOUT_PWM 36 +#define MOUT_SPI0 37 +#define MOUT_SPI1 38 +#define MOUT_DMC0 39 +#define MOUT_PWI 40 +#define MOUT_HPM 41 +#define MOUT_SPDIF 42 +#define MOUT_AUDIO2 43 +#define MOUT_AUDIO1 44 +#define MOUT_AUDIO0 45 + +/* Dividers. */ +#define DOUT_PCLKP 46 +#define DOUT_HCLKP 47 +#define DOUT_PCLKD 48 +#define DOUT_HCLKD 49 +#define DOUT_PCLKM 50 +#define DOUT_HCLKM 51 +#define DOUT_A2M 52 +#define DOUT_APLL 53 +#define DOUT_CSIS 54 +#define DOUT_FIMD 55 +#define DOUT_CAM1 56 +#define DOUT_CAM0 57 +#define DOUT_TBLK 58 +#define DOUT_G2D 59 +#define DOUT_MFC 60 +#define DOUT_G3D 61 +#define DOUT_FIMC2 62 +#define DOUT_FIMC1 63 +#define DOUT_FIMC0 64 +#define DOUT_UART3 65 +#define DOUT_UART2 66 +#define DOUT_UART1 67 +#define DOUT_UART0 68 +#define DOUT_MMC3 69 +#define DOUT_MMC2 70 +#define DOUT_MMC1 71 +#define DOUT_MMC0 72 +#define DOUT_PWM 73 +#define DOUT_SPI1 74 +#define DOUT_SPI0 75 +#define DOUT_DMC0 76 +#define DOUT_PWI 77 +#define DOUT_HPM 78 +#define DOUT_COPY 79 +#define DOUT_FLASH 80 +#define DOUT_AUDIO2 81 +#define DOUT_AUDIO1 82 +#define DOUT_AUDIO0 83 +#define DOUT_DPM 84 +#define DOUT_DVSEM 85 + +/* Gates */ +#define SCLK_FIMC 86 +#define CLK_CSIS 87 +#define CLK_ROTATOR 88 +#define CLK_FIMC2 89 +#define CLK_FIMC1 90 +#define CLK_FIMC0 91 +#define CLK_MFC 92 +#define CLK_G2D 93 +#define CLK_G3D 94 +#define CLK_IMEM 95 +#define CLK_PDMA1 96 +#define CLK_PDMA0 97 +#define CLK_MDMA 98 +#define CLK_DMC1 99 +#define CLK_DMC0 100 +#define CLK_NFCON 101 +#define CLK_SROMC 102 +#define CLK_CFCON 103 +#define CLK_NANDXL 104 +#define CLK_USB_HOST 105 +#define CLK_USB_OTG 106 +#define CLK_HDMI 107 +#define CLK_TVENC 108 +#define CLK_MIXER 109 +#define CLK_VP 110 +#define CLK_DSIM 111 +#define CLK_FIMD 112 +#define CLK_TZIC3 113 +#define CLK_TZIC2 114 +#define CLK_TZIC1 115 +#define CLK_TZIC0 116 +#define CLK_VIC3 117 +#define CLK_VIC2 118 +#define CLK_VIC1 119 +#define CLK_VIC0 120 +#define CLK_TSI 121 +#define CLK_HSMMC3 122 +#define CLK_HSMMC2 123 +#define CLK_HSMMC1 124 +#define CLK_HSMMC0 125 +#define CLK_JTAG 126 +#define CLK_MODEMIF 127 +#define CLK_CORESIGHT 128 +#define CLK_SDM 129 +#define CLK_SECSS 130 +#define CLK_PCM2 131 +#define CLK_PCM1 132 +#define CLK_PCM0 133 +#define CLK_SYSCON 134 +#define CLK_GPIO 135 +#define CLK_TSADC 136 +#define CLK_PWM 137 +#define CLK_WDT 138 +#define CLK_KEYIF 139 +#define CLK_UART3 140 +#define CLK_UART2 141 +#define CLK_UART1 142 +#define CLK_UART0 143 +#define CLK_SYSTIMER 144 +#define CLK_RTC 145 +#define CLK_SPI1 146 +#define CLK_SPI0 147 +#define CLK_I2C_HDMI_PHY 148 +#define CLK_I2C1 149 +#define CLK_I2C2 150 +#define CLK_I2C0 151 +#define CLK_I2S1 152 +#define CLK_I2S2 153 +#define CLK_I2S0 154 +#define CLK_AC97 155 +#define CLK_SPDIF 156 +#define CLK_TZPC3 157 +#define CLK_TZPC2 158 +#define CLK_TZPC1 159 +#define CLK_TZPC0 160 +#define CLK_SECKEY 161 +#define CLK_IEM_APC 162 +#define CLK_IEM_IEC 163 +#define CLK_CHIPID 164 +#define CLK_JPEG 163 + +/* Special clocks*/ +#define SCLK_PWI 164 +#define SCLK_SPDIF 165 +#define SCLK_AUDIO2 166 +#define SCLK_AUDIO1 167 +#define SCLK_AUDIO0 168 +#define SCLK_PWM 169 +#define SCLK_SPI1 170 +#define SCLK_SPI0 171 +#define SCLK_UART3 172 +#define SCLK_UART2 173 +#define SCLK_UART1 174 +#define SCLK_UART0 175 +#define SCLK_MMC3 176 +#define SCLK_MMC2 177 +#define SCLK_MMC1 178 +#define SCLK_MMC0 179 +#define SCLK_FINVPLL 180 +#define SCLK_CSIS 181 +#define SCLK_FIMD 182 +#define SCLK_CAM1 183 +#define SCLK_CAM0 184 +#define SCLK_DAC 185 +#define SCLK_MIXER 186 +#define SCLK_HDMI 187 +#define SCLK_FIMC2 188 +#define SCLK_FIMC1 189 +#define SCLK_FIMC0 190 +#define SCLK_HDMI27M 191 +#define SCLK_HDMIPHY 192 +#define SCLK_USBPHY0 193 +#define SCLK_USBPHY1 194 + +/* S5P6442-specific clocks */ +#define MOUT_D0SYNC 195 +#define MOUT_D1SYNC 196 +#define DOUT_MIXER 197 +#define CLK_ETB 198 +#define CLK_ETM 199 + +/* CLKOUT */ +#define FOUT_APLL_CLKOUT 200 +#define FOUT_MPLL_CLKOUT 201 +#define DOUT_APLL_CLKOUT 202 +#define MOUT_CLKSEL 203 +#define DOUT_CLKOUT 204 +#define MOUT_CLKOUT 205 + +/* Total number of clocks. */ +#define NR_CLKS 206 + +#endif /* _DT_BINDINGS_CLOCK_S5PV210_H */ -- cgit v1.2.3 From 9978f28f695adb63fa1726744a7f95e12920e8c9 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Wed, 2 Jul 2014 19:28:27 +0200 Subject: clk: samsung: Add S5PV210 Audio Subsystem clock driver This patch adds a driver for clock controller being a part of Audio Subsystem present on S5PV210 and compatible SoCs. It is used to provide clocks for other IP blocks of this subsystem. Signed-off-by: Tomasz Figa Signed-off-by: Kukjin Kim --- .../bindings/clock/clk-s5pv210-audss.txt | 53 +++++ drivers/clk/samsung/Makefile | 2 +- drivers/clk/samsung/clk-s5pv210-audss.c | 241 +++++++++++++++++++++ include/dt-bindings/clock/s5pv210-audss.h | 34 +++ 4 files changed, 329 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/clock/clk-s5pv210-audss.txt create mode 100644 drivers/clk/samsung/clk-s5pv210-audss.c create mode 100644 include/dt-bindings/clock/s5pv210-audss.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/clk-s5pv210-audss.txt b/Documentation/devicetree/bindings/clock/clk-s5pv210-audss.txt new file mode 100644 index 000000000000..4fc869b69d4a --- /dev/null +++ b/Documentation/devicetree/bindings/clock/clk-s5pv210-audss.txt @@ -0,0 +1,53 @@ +* Samsung Audio Subsystem Clock Controller + +The Samsung Audio Subsystem clock controller generates and supplies clocks +to Audio Subsystem block available in the S5PV210 and compatible SoCs. + +Required Properties: + +- compatible: should be "samsung,s5pv210-audss-clock". +- reg: physical base address and length of the controller's register set. + +- #clock-cells: should be 1. + +- clocks: + - hclk: AHB bus clock of the Audio Subsystem. + - xxti: Optional fixed rate PLL reference clock, parent of mout_audss. If + not specified (i.e. xusbxti is used for PLL reference), it is fixed to + a clock named "xxti". + - fout_epll: Input PLL to the AudioSS block, parent of mout_audss. + - iiscdclk0: Optional external i2s clock, parent of mout_i2s. If not + specified, it is fixed to a clock named "iiscdclk0". + - sclk_audio0: Audio bus clock, parent of mout_i2s. + +- clock-names: Aliases for the above clocks. They should be "hclk", + "xxti", "fout_epll", "iiscdclk0", and "sclk_audio0" respectively. + +All available clocks are defined as preprocessor macros in +dt-bindings/clock/s5pv210-audss-clk.h header and can be used in device +tree sources. + +Example: Clock controller node. + + clk_audss: clock-controller@c0900000 { + compatible = "samsung,s5pv210-audss-clock"; + reg = <0xc0900000 0x1000>; + #clock-cells = <1>; + clock-names = "hclk", "xxti", + "fout_epll", "sclk_audio0"; + clocks = <&clocks DOUT_HCLKP>, <&xxti>, + <&clocks FOUT_EPLL>, <&clocks SCLK_AUDIO0>; + }; + +Example: I2S controller node that consumes the clock generated by the clock + controller. Refer to the standard clock bindings for information + about 'clocks' and 'clock-names' property. + + i2s0: i2s@03830000 { + /* ... */ + clock-names = "iis", "i2s_opclk0", + "i2s_opclk1"; + clocks = <&clk_audss CLK_I2S>, <&clk_audss CLK_I2S>, + <&clk_audss CLK_DOUT_AUD_BUS>; + /* ... */ + }; diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index 49d6ce1ea107..9f256a4ba775 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile @@ -16,4 +16,4 @@ obj-$(CONFIG_S3C2410_COMMON_DCLK)+= clk-s3c2410-dclk.o obj-$(CONFIG_S3C2412_COMMON_CLK)+= clk-s3c2412.o obj-$(CONFIG_S3C2443_COMMON_CLK)+= clk-s3c2443.o obj-$(CONFIG_ARCH_S3C64XX) += clk-s3c64xx.o -obj-$(CONFIG_ARCH_S5PV210) += clk-s5pv210.o +obj-$(CONFIG_ARCH_S5PV210) += clk-s5pv210.o clk-s5pv210-audss.o diff --git a/drivers/clk/samsung/clk-s5pv210-audss.c b/drivers/clk/samsung/clk-s5pv210-audss.c new file mode 100644 index 000000000000..a8053b4aca56 --- /dev/null +++ b/drivers/clk/samsung/clk-s5pv210-audss.c @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2014 Tomasz Figa + * + * Based on Exynos Audio Subsystem Clock Controller driver: + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * Author: Padmavathi Venna + * + * 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. + * + * Driver for Audio Subsystem Clock Controller of S5PV210-compatible SoCs. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include + +static DEFINE_SPINLOCK(lock); +static struct clk **clk_table; +static void __iomem *reg_base; +static struct clk_onecell_data clk_data; + +#define ASS_CLK_SRC 0x0 +#define ASS_CLK_DIV 0x4 +#define ASS_CLK_GATE 0x8 + +#ifdef CONFIG_PM_SLEEP +static unsigned long reg_save[][2] = { + {ASS_CLK_SRC, 0}, + {ASS_CLK_DIV, 0}, + {ASS_CLK_GATE, 0}, +}; + +static int s5pv210_audss_clk_suspend(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(reg_save); i++) + reg_save[i][1] = readl(reg_base + reg_save[i][0]); + + return 0; +} + +static void s5pv210_audss_clk_resume(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(reg_save); i++) + writel(reg_save[i][1], reg_base + reg_save[i][0]); +} + +static struct syscore_ops s5pv210_audss_clk_syscore_ops = { + .suspend = s5pv210_audss_clk_suspend, + .resume = s5pv210_audss_clk_resume, +}; +#endif /* CONFIG_PM_SLEEP */ + +/* register s5pv210_audss clocks */ +static int s5pv210_audss_clk_probe(struct platform_device *pdev) +{ + int i, ret = 0; + struct resource *res; + const char *mout_audss_p[2]; + const char *mout_i2s_p[3]; + const char *hclk_p; + struct clk *hclk, *pll_ref, *pll_in, *cdclk, *sclk_audio; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(reg_base)) { + dev_err(&pdev->dev, "failed to map audss registers\n"); + return PTR_ERR(reg_base); + } + + clk_table = devm_kzalloc(&pdev->dev, + sizeof(struct clk *) * AUDSS_MAX_CLKS, + GFP_KERNEL); + if (!clk_table) + return -ENOMEM; + + clk_data.clks = clk_table; + clk_data.clk_num = AUDSS_MAX_CLKS; + + hclk = devm_clk_get(&pdev->dev, "hclk"); + if (IS_ERR(hclk)) { + dev_err(&pdev->dev, "failed to get hclk clock\n"); + return PTR_ERR(hclk); + } + + pll_in = devm_clk_get(&pdev->dev, "fout_epll"); + if (IS_ERR(pll_in)) { + dev_err(&pdev->dev, "failed to get fout_epll clock\n"); + return PTR_ERR(pll_in); + } + + sclk_audio = devm_clk_get(&pdev->dev, "sclk_audio0"); + if (IS_ERR(sclk_audio)) { + dev_err(&pdev->dev, "failed to get sclk_audio0 clock\n"); + return PTR_ERR(sclk_audio); + } + + /* iiscdclk0 is an optional external I2S codec clock */ + cdclk = devm_clk_get(&pdev->dev, "iiscdclk0"); + pll_ref = devm_clk_get(&pdev->dev, "xxti"); + + if (!IS_ERR(pll_ref)) + mout_audss_p[0] = __clk_get_name(pll_ref); + else + mout_audss_p[0] = "xxti"; + mout_audss_p[1] = __clk_get_name(pll_in); + clk_table[CLK_MOUT_AUDSS] = clk_register_mux(NULL, "mout_audss", + mout_audss_p, ARRAY_SIZE(mout_audss_p), + CLK_SET_RATE_NO_REPARENT, + reg_base + ASS_CLK_SRC, 0, 1, 0, &lock); + + mout_i2s_p[0] = "mout_audss"; + if (!IS_ERR(cdclk)) + mout_i2s_p[1] = __clk_get_name(cdclk); + else + mout_i2s_p[1] = "iiscdclk0"; + mout_i2s_p[2] = __clk_get_name(sclk_audio); + clk_table[CLK_MOUT_I2S_A] = clk_register_mux(NULL, "mout_i2s_audss", + mout_i2s_p, ARRAY_SIZE(mout_i2s_p), + CLK_SET_RATE_NO_REPARENT, + reg_base + ASS_CLK_SRC, 2, 2, 0, &lock); + + clk_table[CLK_DOUT_AUD_BUS] = clk_register_divider(NULL, + "dout_aud_bus", "mout_audss", 0, + reg_base + ASS_CLK_DIV, 0, 4, 0, &lock); + clk_table[CLK_DOUT_I2S_A] = clk_register_divider(NULL, "dout_i2s_audss", + "mout_i2s_audss", 0, reg_base + ASS_CLK_DIV, + 4, 4, 0, &lock); + + clk_table[CLK_I2S] = clk_register_gate(NULL, "i2s_audss", + "dout_i2s_audss", CLK_SET_RATE_PARENT, + reg_base + ASS_CLK_GATE, 6, 0, &lock); + + hclk_p = __clk_get_name(hclk); + + clk_table[CLK_HCLK_I2S] = clk_register_gate(NULL, "hclk_i2s_audss", + hclk_p, CLK_IGNORE_UNUSED, + reg_base + ASS_CLK_GATE, 5, 0, &lock); + clk_table[CLK_HCLK_UART] = clk_register_gate(NULL, "hclk_uart_audss", + hclk_p, CLK_IGNORE_UNUSED, + reg_base + ASS_CLK_GATE, 4, 0, &lock); + clk_table[CLK_HCLK_HWA] = clk_register_gate(NULL, "hclk_hwa_audss", + hclk_p, CLK_IGNORE_UNUSED, + reg_base + ASS_CLK_GATE, 3, 0, &lock); + clk_table[CLK_HCLK_DMA] = clk_register_gate(NULL, "hclk_dma_audss", + hclk_p, CLK_IGNORE_UNUSED, + reg_base + ASS_CLK_GATE, 2, 0, &lock); + clk_table[CLK_HCLK_BUF] = clk_register_gate(NULL, "hclk_buf_audss", + hclk_p, CLK_IGNORE_UNUSED, + reg_base + ASS_CLK_GATE, 1, 0, &lock); + clk_table[CLK_HCLK_RP] = clk_register_gate(NULL, "hclk_rp_audss", + hclk_p, CLK_IGNORE_UNUSED, + reg_base + ASS_CLK_GATE, 0, 0, &lock); + + for (i = 0; i < clk_data.clk_num; i++) { + if (IS_ERR(clk_table[i])) { + dev_err(&pdev->dev, "failed to register clock %d\n", i); + ret = PTR_ERR(clk_table[i]); + goto unregister; + } + } + + ret = of_clk_add_provider(pdev->dev.of_node, of_clk_src_onecell_get, + &clk_data); + if (ret) { + dev_err(&pdev->dev, "failed to add clock provider\n"); + goto unregister; + } + +#ifdef CONFIG_PM_SLEEP + register_syscore_ops(&s5pv210_audss_clk_syscore_ops); +#endif + + return 0; + +unregister: + for (i = 0; i < clk_data.clk_num; i++) { + if (!IS_ERR(clk_table[i])) + clk_unregister(clk_table[i]); + } + + return ret; +} + +static int s5pv210_audss_clk_remove(struct platform_device *pdev) +{ + int i; + + of_clk_del_provider(pdev->dev.of_node); + + for (i = 0; i < clk_data.clk_num; i++) { + if (!IS_ERR(clk_table[i])) + clk_unregister(clk_table[i]); + } + + return 0; +} + +static const struct of_device_id s5pv210_audss_clk_of_match[] = { + { .compatible = "samsung,s5pv210-audss-clock", }, + {}, +}; + +static struct platform_driver s5pv210_audss_clk_driver = { + .driver = { + .name = "s5pv210-audss-clk", + .owner = THIS_MODULE, + .of_match_table = s5pv210_audss_clk_of_match, + }, + .probe = s5pv210_audss_clk_probe, + .remove = s5pv210_audss_clk_remove, +}; + +static int __init s5pv210_audss_clk_init(void) +{ + return platform_driver_register(&s5pv210_audss_clk_driver); +} +core_initcall(s5pv210_audss_clk_init); + +static void __exit s5pv210_audss_clk_exit(void) +{ + platform_driver_unregister(&s5pv210_audss_clk_driver); +} +module_exit(s5pv210_audss_clk_exit); + +MODULE_AUTHOR("Tomasz Figa "); +MODULE_DESCRIPTION("S5PV210 Audio Subsystem Clock Controller"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:s5pv210-audss-clk"); diff --git a/include/dt-bindings/clock/s5pv210-audss.h b/include/dt-bindings/clock/s5pv210-audss.h new file mode 100644 index 000000000000..fe57406e24de --- /dev/null +++ b/include/dt-bindings/clock/s5pv210-audss.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2014 Tomasz Figa + * + * 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 header provides constants for Samsung audio subsystem + * clock controller. + * + * The constants defined in this header are being used in dts + * and s5pv210 audss driver. + */ + +#ifndef _DT_BINDINGS_CLOCK_S5PV210_AUDSS_H +#define _DT_BINDINGS_CLOCK_S5PV210_AUDSS_H + +#define CLK_MOUT_AUDSS 0 +#define CLK_MOUT_I2S_A 1 + +#define CLK_DOUT_AUD_BUS 2 +#define CLK_DOUT_I2S_A 3 + +#define CLK_I2S 4 +#define CLK_HCLK_I2S 5 +#define CLK_HCLK_UART 6 +#define CLK_HCLK_HWA 7 +#define CLK_HCLK_DMA 8 +#define CLK_HCLK_BUF 9 +#define CLK_HCLK_RP 10 + +#define AUDSS_MAX_CLKS 11 + +#endif -- cgit v1.2.3 From 8562c99cdd30217dea3609e268572f8764f401a5 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Fri, 13 Jun 2014 12:22:22 +0100 Subject: efi/reboot: Add generic wrapper around EfiResetSystem() Implement efi_reboot(), which is really just a wrapper around the EfiResetSystem() EFI runtime service, but it does at least allow us to funnel all callers through a single location. It also simplifies the callsites since users no longer need to check to see whether EFI_RUNTIME_SERVICES are enabled. Cc: Tony Luck Tested-by: Mark Salter Signed-off-by: Matt Fleming --- arch/ia64/kernel/process.c | 2 +- arch/x86/kernel/reboot.c | 6 +----- drivers/firmware/efi/Makefile | 2 +- drivers/firmware/efi/reboot.c | 26 ++++++++++++++++++++++++++ include/linux/efi.h | 4 ++++ 5 files changed, 33 insertions(+), 7 deletions(-) create mode 100644 drivers/firmware/efi/reboot.c (limited to 'include') diff --git a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c index 55d4ba47a907..deed6fa96bb0 100644 --- a/arch/ia64/kernel/process.c +++ b/arch/ia64/kernel/process.c @@ -662,7 +662,7 @@ void machine_restart (char *restart_cmd) { (void) notify_die(DIE_MACHINE_RESTART, restart_cmd, NULL, 0, 0, 0); - (*efi.reset_system)(EFI_RESET_WARM, 0, 0, NULL); + efi_reboot(REBOOT_WARM, NULL); } void diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index 52b1157c53eb..09e709fd1830 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -528,11 +528,7 @@ static void native_machine_emergency_restart(void) break; case BOOT_EFI: - if (efi_enabled(EFI_RUNTIME_SERVICES)) - efi.reset_system(reboot_mode == REBOOT_WARM ? - EFI_RESET_WARM : - EFI_RESET_COLD, - EFI_SUCCESS, 0, NULL); + efi_reboot(reboot_mode, NULL); reboot_type = BOOT_BIOS; break; diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index a204d1474cec..d8be608a9f3b 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -1,7 +1,7 @@ # # Makefile for linux kernel # -obj-$(CONFIG_EFI) += efi.o vars.o +obj-$(CONFIG_EFI) += efi.o vars.o reboot.o obj-$(CONFIG_EFI_VARS) += efivars.o obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o obj-$(CONFIG_UEFI_CPER) += cper.o diff --git a/drivers/firmware/efi/reboot.c b/drivers/firmware/efi/reboot.c new file mode 100644 index 000000000000..81bf925f70f5 --- /dev/null +++ b/drivers/firmware/efi/reboot.c @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2014 Intel Corporation; author Matt Fleming + * Copyright (c) 2014 Red Hat, Inc., Mark Salter + */ +#include +#include + +void efi_reboot(enum reboot_mode reboot_mode, const char *__unused) +{ + int efi_mode; + + if (!efi_enabled(EFI_RUNTIME_SERVICES)) + return; + + switch (reboot_mode) { + case REBOOT_WARM: + case REBOOT_SOFT: + efi_mode = EFI_RESET_WARM; + break; + default: + efi_mode = EFI_RESET_COLD; + break; + } + + efi.reset_system(efi_mode, EFI_SUCCESS, 0, NULL); +} diff --git a/include/linux/efi.h b/include/linux/efi.h index 3a64f2f85821..e6980ba528ec 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -928,11 +929,14 @@ static inline bool efi_enabled(int feature) { return test_bit(feature, &efi.flags) != 0; } +extern void efi_reboot(enum reboot_mode reboot_mode, const char *__unused); #else static inline bool efi_enabled(int feature) { return false; } +static inline void +efi_reboot(enum reboot_mode reboot_mode, const char *__unused) {} #endif /* -- cgit v1.2.3 From 0c5ed61adbdbf2ca5de934642d5be1e971c498c1 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Fri, 13 Jun 2014 12:35:21 +0100 Subject: efi/reboot: Allow powering off machines using EFI Not only can EfiResetSystem() be used to reboot, it can also be used to power down machines. By and large, this functionality doesn't work very well across the range of EFI machines in the wild, so it should definitely only be used as a last resort. In an ideal world, this wouldn't be needed at all. Unfortunately, we're starting to see machines where EFI is the *only* reliable way to power down, and nothing else, not PCI, not ACPI, works. efi_poweroff_required() should be implemented on a per-architecture basis, since exactly when we should be using EFI runtime services is a platform-specific decision. There's no analogue for reboot because each architecture handles reboot very differently - the x86 code in particular is pretty complex. Patches to enable this for specific classes of hardware will be submitted separately. Tested-by: Mark Salter Signed-off-by: Matt Fleming --- drivers/firmware/efi/reboot.c | 22 ++++++++++++++++++++++ include/linux/efi.h | 2 ++ 2 files changed, 24 insertions(+) (limited to 'include') diff --git a/drivers/firmware/efi/reboot.c b/drivers/firmware/efi/reboot.c index 81bf925f70f5..e9eeeb3c6345 100644 --- a/drivers/firmware/efi/reboot.c +++ b/drivers/firmware/efi/reboot.c @@ -24,3 +24,25 @@ void efi_reboot(enum reboot_mode reboot_mode, const char *__unused) efi.reset_system(efi_mode, EFI_SUCCESS, 0, NULL); } + +bool __weak efi_poweroff_required(void) +{ + return false; +} + +static void efi_power_off(void) +{ + efi.reset_system(EFI_RESET_SHUTDOWN, EFI_SUCCESS, 0, NULL); +} + +static int __init efi_shutdown_init(void) +{ + if (!efi_enabled(EFI_RUNTIME_SERVICES)) + return -ENODEV; + + if (efi_poweroff_required()) + pm_power_off = efi_power_off; + + return 0; +} +late_initcall(efi_shutdown_init); diff --git a/include/linux/efi.h b/include/linux/efi.h index e6980ba528ec..9917f58ee83e 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -878,6 +878,8 @@ extern void efi_reserve_boot_services(void); extern int efi_get_fdt_params(struct efi_fdt_params *params, int verbose); extern struct efi_memory_map memmap; +extern bool efi_poweroff_required(void); + /* Iterate through an efi_memory_map */ #define for_each_efi_memory_desc(m, md) \ for ((md) = (m)->map; \ -- cgit v1.2.3 From 44be28e9dd9880dca3e2cbf7a844f2114e67f2cb Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Fri, 13 Jun 2014 12:39:55 +0100 Subject: x86/reboot: Add EFI reboot quirk for ACPI Hardware Reduced flag It appears that the BayTrail-T class of hardware requires EFI in order to powerdown and reboot and no other reliable method exists. This quirk is generally applicable to all hardware that has the ACPI Hardware Reduced bit set, since usually ACPI would be the preferred method. Cc: Len Brown Cc: Mark Salter Cc: "Rafael J. Wysocki" Signed-off-by: Matt Fleming --- arch/x86/include/asm/efi.h | 6 ++++++ arch/x86/kernel/reboot.c | 18 ++++++++++++++++-- arch/x86/platform/efi/quirks.c | 23 +++++++++++++++++++++++ drivers/firmware/efi/reboot.c | 8 ++++++++ include/linux/efi.h | 1 + 5 files changed, 54 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index 9043f365ebf5..044a2fd3c5fe 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -183,6 +183,8 @@ extern struct efi_config *efi_early; #define efi_call_early(f, ...) \ efi_early->call(efi_early->f, __VA_ARGS__); +extern bool efi_reboot_required(void); + #else /* * IF EFI is not configured, have the EFI calls return -ENOSYS. @@ -195,6 +197,10 @@ extern struct efi_config *efi_early; #define efi_call5(_f, _a1, _a2, _a3, _a4, _a5) (-ENOSYS) #define efi_call6(_f, _a1, _a2, _a3, _a4, _a5, _a6) (-ENOSYS) static inline void parse_efi_setup(u64 phys_addr, u32 data_len) {} +static inline bool efi_reboot_required(void) +{ + return false; +} #endif /* CONFIG_EFI */ #endif /* _ASM_X86_EFI_H */ diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index 09e709fd1830..17962e667a91 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -28,6 +28,7 @@ #include #include #include +#include /* * Power off function, if any @@ -401,12 +402,25 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = { static int __init reboot_init(void) { + int rv; + /* * Only do the DMI check if reboot_type hasn't been overridden * on the command line */ - if (reboot_default) - dmi_check_system(reboot_dmi_table); + if (!reboot_default) + return 0; + + /* + * The DMI quirks table takes precedence. If no quirks entry + * matches and the ACPI Hardware Reduced bit is set, force EFI + * reboot. + */ + rv = dmi_check_system(reboot_dmi_table); + + if (!rv && efi_reboot_required()) + reboot_type = BOOT_EFI; + return 0; } core_initcall(reboot_init); diff --git a/arch/x86/platform/efi/quirks.c b/arch/x86/platform/efi/quirks.c index b4cb9182f155..1c7380da65ff 100644 --- a/arch/x86/platform/efi/quirks.c +++ b/arch/x86/platform/efi/quirks.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -265,3 +266,25 @@ void __init efi_apply_memmap_quirks(void) if (is_uv_system()) set_bit(EFI_OLD_MEMMAP, &efi.flags); } + +/* + * For most modern platforms the preferred method of powering off is via + * ACPI. However, there are some that are known to require the use of + * EFI runtime services and for which ACPI does not work at all. + * + * Using EFI is a last resort, to be used only if no other option + * exists. + */ +bool efi_reboot_required(void) +{ + if (!acpi_gbl_reduced_hardware) + return false; + + efi_reboot_quirk_mode = EFI_RESET_WARM; + return true; +} + +bool efi_poweroff_required(void) +{ + return !!acpi_gbl_reduced_hardware; +} diff --git a/drivers/firmware/efi/reboot.c b/drivers/firmware/efi/reboot.c index e9eeeb3c6345..9c59d1c795d1 100644 --- a/drivers/firmware/efi/reboot.c +++ b/drivers/firmware/efi/reboot.c @@ -5,6 +5,8 @@ #include #include +int efi_reboot_quirk_mode = -1; + void efi_reboot(enum reboot_mode reboot_mode, const char *__unused) { int efi_mode; @@ -22,6 +24,12 @@ void efi_reboot(enum reboot_mode reboot_mode, const char *__unused) break; } + /* + * If a quirk forced an EFI reset mode, always use that. + */ + if (efi_reboot_quirk_mode != -1) + efi_mode = efi_reboot_quirk_mode; + efi.reset_system(efi_mode, EFI_SUCCESS, 0, NULL); } diff --git a/include/linux/efi.h b/include/linux/efi.h index 9917f58ee83e..bac0f93dc473 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -878,6 +878,7 @@ extern void efi_reserve_boot_services(void); extern int efi_get_fdt_params(struct efi_fdt_params *params, int verbose); extern struct efi_memory_map memmap; +extern int efi_reboot_quirk_mode; extern bool efi_poweroff_required(void); /* Iterate through an efi_memory_map */ -- cgit v1.2.3 From 9f27bc543bdf92e179927037e2ab8ed0261579a9 Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Mon, 30 Jun 2014 19:52:58 +0200 Subject: efi: Introduce EFI_PARAVIRT flag Introduce EFI_PARAVIRT flag. If it is set then kernel runs on EFI platform but it has not direct control on EFI stuff like EFI runtime, tables, structures, etc. If not this means that Linux Kernel has direct access to EFI infrastructure and everything runs as usual. This functionality is used in Xen dom0 because hypervisor has full control on EFI stuff and all calls from dom0 to EFI must be requested via special hypercall which in turn executes relevant EFI code in behalf of dom0. Signed-off-by: Daniel Kiper Signed-off-by: Matt Fleming --- arch/x86/platform/efi/efi.c | 31 +++++++++++++++++++++++++------ drivers/firmware/efi/efi.c | 21 ++++++++++++--------- include/linux/efi.h | 3 ++- 3 files changed, 39 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 7d627a02ed82..d9026538cfdb 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -210,6 +210,9 @@ int __init efi_memblock_x86_reserve_range(void) struct efi_info *e = &boot_params.efi_info; unsigned long pmap; + if (efi_enabled(EFI_PARAVIRT)) + return 0; + #ifdef CONFIG_X86_32 /* Can't handle data above 4GB at this time */ if (e->efi_memmap_hi) { @@ -422,14 +425,24 @@ static int __init efi_runtime_init(void) * the runtime services table so that we can grab the physical * address of several of the EFI runtime functions, needed to * set the firmware into virtual mode. + * + * When EFI_PARAVIRT is in force then we could not map runtime + * service memory region because we do not have direct access to it. + * However, runtime services are available through proxy functions + * (e.g. in case of Xen dom0 EFI implementation they call special + * hypercall which executes relevant EFI functions) and that is why + * they are always enabled. */ - if (efi_enabled(EFI_64BIT)) - rv = efi_runtime_init64(); - else - rv = efi_runtime_init32(); - if (rv) - return rv; + if (!efi_enabled(EFI_PARAVIRT)) { + if (efi_enabled(EFI_64BIT)) + rv = efi_runtime_init64(); + else + rv = efi_runtime_init32(); + + if (rv) + return rv; + } set_bit(EFI_RUNTIME_SERVICES, &efi.flags); @@ -438,6 +451,9 @@ static int __init efi_runtime_init(void) static int __init efi_memmap_init(void) { + if (efi_enabled(EFI_PARAVIRT)) + return 0; + /* Map the EFI memory map */ memmap.map = early_memremap((unsigned long)memmap.phys_map, memmap.nr_map * memmap.desc_size); @@ -914,6 +930,9 @@ static void __init __efi_enter_virtual_mode(void) void __init efi_enter_virtual_mode(void) { + if (efi_enabled(EFI_PARAVIRT)) + return; + if (efi_setup) kexec_enter_virtual_mode(); else diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 023937a63a48..ac88ec05eb70 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -104,16 +104,19 @@ static struct attribute *efi_subsys_attrs[] = { static umode_t efi_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) { - umode_t mode = attr->mode; - - if (attr == &efi_attr_fw_vendor.attr) - return (efi.fw_vendor == EFI_INVALID_TABLE_ADDR) ? 0 : mode; - else if (attr == &efi_attr_runtime.attr) - return (efi.runtime == EFI_INVALID_TABLE_ADDR) ? 0 : mode; - else if (attr == &efi_attr_config_table.attr) - return (efi.config_table == EFI_INVALID_TABLE_ADDR) ? 0 : mode; + if (attr == &efi_attr_fw_vendor.attr) { + if (efi_enabled(EFI_PARAVIRT) || + efi.fw_vendor == EFI_INVALID_TABLE_ADDR) + return 0; + } else if (attr == &efi_attr_runtime.attr) { + if (efi.runtime == EFI_INVALID_TABLE_ADDR) + return 0; + } else if (attr == &efi_attr_config_table.attr) { + if (efi.config_table == EFI_INVALID_TABLE_ADDR) + return 0; + } - return mode; + return attr->mode; } static struct attribute_group efi_subsys_attr_group = { diff --git a/include/linux/efi.h b/include/linux/efi.h index bac0f93dc473..c7a29a26e900 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -922,7 +922,8 @@ extern int __init efi_setup_pcdp_console(char *); #define EFI_RUNTIME_SERVICES 3 /* Can we use runtime services? */ #define EFI_MEMMAP 4 /* Can we use EFI memory map? */ #define EFI_64BIT 5 /* Is the firmware 64-bit? */ -#define EFI_ARCH_1 6 /* First arch-specific bit */ +#define EFI_PARAVIRT 6 /* Access is via a paravirt interface */ +#define EFI_ARCH_1 7 /* First arch-specific bit */ #ifdef CONFIG_EFI /* -- cgit v1.2.3 From bf1e3ae468aca7ce34110a59ed5d1b877890014b Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Mon, 30 Jun 2014 19:53:01 +0200 Subject: xen: Define EFI related stuff Define constants and structures which are needed to properly execute EFI related hypercall in Xen dom0. This patch is based on Jan Beulich and Tang Liang work. Signed-off-by: Jan Beulich Signed-off-by: Tang Liang Signed-off-by: Daniel Kiper Reviewed-by: David Vrabel Acked-by: Stefano Stabellini Signed-off-by: Matt Fleming --- include/xen/interface/platform.h | 123 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) (limited to 'include') diff --git a/include/xen/interface/platform.h b/include/xen/interface/platform.h index f1331e3e7271..5cc49ea8d840 100644 --- a/include/xen/interface/platform.h +++ b/include/xen/interface/platform.h @@ -108,11 +108,113 @@ struct xenpf_platform_quirk { }; DEFINE_GUEST_HANDLE_STRUCT(xenpf_platform_quirk_t); +#define XENPF_efi_runtime_call 49 +#define XEN_EFI_get_time 1 +#define XEN_EFI_set_time 2 +#define XEN_EFI_get_wakeup_time 3 +#define XEN_EFI_set_wakeup_time 4 +#define XEN_EFI_get_next_high_monotonic_count 5 +#define XEN_EFI_get_variable 6 +#define XEN_EFI_set_variable 7 +#define XEN_EFI_get_next_variable_name 8 +#define XEN_EFI_query_variable_info 9 +#define XEN_EFI_query_capsule_capabilities 10 +#define XEN_EFI_update_capsule 11 + +struct xenpf_efi_runtime_call { + uint32_t function; + /* + * This field is generally used for per sub-function flags (defined + * below), except for the XEN_EFI_get_next_high_monotonic_count case, + * where it holds the single returned value. + */ + uint32_t misc; + xen_ulong_t status; + union { +#define XEN_EFI_GET_TIME_SET_CLEARS_NS 0x00000001 + struct { + struct xenpf_efi_time { + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t min; + uint8_t sec; + uint32_t ns; + int16_t tz; + uint8_t daylight; + } time; + uint32_t resolution; + uint32_t accuracy; + } get_time; + + struct xenpf_efi_time set_time; + +#define XEN_EFI_GET_WAKEUP_TIME_ENABLED 0x00000001 +#define XEN_EFI_GET_WAKEUP_TIME_PENDING 0x00000002 + struct xenpf_efi_time get_wakeup_time; + +#define XEN_EFI_SET_WAKEUP_TIME_ENABLE 0x00000001 +#define XEN_EFI_SET_WAKEUP_TIME_ENABLE_ONLY 0x00000002 + struct xenpf_efi_time set_wakeup_time; + +#define XEN_EFI_VARIABLE_NON_VOLATILE 0x00000001 +#define XEN_EFI_VARIABLE_BOOTSERVICE_ACCESS 0x00000002 +#define XEN_EFI_VARIABLE_RUNTIME_ACCESS 0x00000004 + struct { + GUEST_HANDLE(void) name; /* UCS-2/UTF-16 string */ + xen_ulong_t size; + GUEST_HANDLE(void) data; + struct xenpf_efi_guid { + uint32_t data1; + uint16_t data2; + uint16_t data3; + uint8_t data4[8]; + } vendor_guid; + } get_variable, set_variable; + + struct { + xen_ulong_t size; + GUEST_HANDLE(void) name; /* UCS-2/UTF-16 string */ + struct xenpf_efi_guid vendor_guid; + } get_next_variable_name; + + struct { + uint32_t attr; + uint64_t max_store_size; + uint64_t remain_store_size; + uint64_t max_size; + } query_variable_info; + + struct { + GUEST_HANDLE(void) capsule_header_array; + xen_ulong_t capsule_count; + uint64_t max_capsule_size; + uint32_t reset_type; + } query_capsule_capabilities; + + struct { + GUEST_HANDLE(void) capsule_header_array; + xen_ulong_t capsule_count; + uint64_t sg_list; /* machine address */ + } update_capsule; + } u; +}; +DEFINE_GUEST_HANDLE_STRUCT(xenpf_efi_runtime_call); + +#define XEN_FW_EFI_VERSION 0 +#define XEN_FW_EFI_CONFIG_TABLE 1 +#define XEN_FW_EFI_VENDOR 2 +#define XEN_FW_EFI_MEM_INFO 3 +#define XEN_FW_EFI_RT_VERSION 4 + #define XENPF_firmware_info 50 #define XEN_FW_DISK_INFO 1 /* from int 13 AH=08/41/48 */ #define XEN_FW_DISK_MBR_SIGNATURE 2 /* from MBR offset 0x1b8 */ #define XEN_FW_VBEDDC_INFO 3 /* from int 10 AX=4f15 */ +#define XEN_FW_EFI_INFO 4 /* from EFI */ #define XEN_FW_KBD_SHIFT_FLAGS 5 /* Int16, Fn02: Get keyboard shift flags. */ + struct xenpf_firmware_info { /* IN variables. */ uint32_t type; @@ -144,6 +246,26 @@ struct xenpf_firmware_info { GUEST_HANDLE(uchar) edid; } vbeddc_info; /* XEN_FW_VBEDDC_INFO */ + union xenpf_efi_info { + uint32_t version; + struct { + uint64_t addr; /* EFI_CONFIGURATION_TABLE */ + uint32_t nent; + } cfg; + struct { + uint32_t revision; + uint32_t bufsz; /* input, in bytes */ + GUEST_HANDLE(void) name; + /* UCS-2/UTF-16 string */ + } vendor; + struct { + uint64_t addr; + uint64_t size; + uint64_t attr; + uint32_t type; + } mem; + } efi_info; /* XEN_FW_EFI_INFO */ + uint8_t kbd_shift_flags; /* XEN_FW_KBD_SHIFT_FLAGS */ } u; }; @@ -362,6 +484,7 @@ struct xen_platform_op { struct xenpf_read_memtype read_memtype; struct xenpf_microcode_update microcode; struct xenpf_platform_quirk platform_quirk; + struct xenpf_efi_runtime_call efi_runtime_call; struct xenpf_firmware_info firmware_info; struct xenpf_enter_acpi_sleep enter_acpi_sleep; struct xenpf_change_freq change_freq; -- cgit v1.2.3 From be81c8a1da24288b0231be50130a64f5cdffdcd4 Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Mon, 30 Jun 2014 19:53:02 +0200 Subject: xen: Put EFI machinery in place This patch enables EFI usage under Xen dom0. Standard EFI Linux Kernel infrastructure cannot be used because it requires direct access to EFI data and code. However, in dom0 case it is not possible because above mentioned EFI stuff is fully owned and controlled by Xen hypervisor. In this case all calls from dom0 to EFI must be requested via special hypercall which in turn executes relevant EFI code in behalf of dom0. When dom0 kernel boots it checks for EFI availability on a machine. If it is detected then artificial EFI system table is filled. Native EFI callas are replaced by functions which mimics them by calling relevant hypercall. Later pointer to EFI system table is passed to standard EFI machinery and it continues EFI subsystem initialization taking into account that there is no direct access to EFI boot services, runtime, tables, structures, etc. After that system runs as usual. This patch is based on Jan Beulich and Tang Liang work. Signed-off-by: Jan Beulich Signed-off-by: Tang Liang Signed-off-by: Daniel Kiper Reviewed-by: David Vrabel Acked-by: Stefano Stabellini --- arch/x86/xen/enlighten.c | 15 ++ drivers/xen/Kconfig | 4 + drivers/xen/Makefile | 3 + drivers/xen/efi.c | 368 +++++++++++++++++++++++++++++++++++++++++++++++ include/xen/xen-ops.h | 11 ++ 5 files changed, 401 insertions(+) create mode 100644 drivers/xen/efi.c (limited to 'include') diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c index f17b29210ac4..bc89647f0325 100644 --- a/arch/x86/xen/enlighten.c +++ b/arch/x86/xen/enlighten.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -1520,6 +1521,7 @@ asmlinkage __visible void __init xen_start_kernel(void) { struct physdev_set_iopl set_iopl; int rc; + efi_system_table_t *efi_systab_xen; if (!xen_start_info) return; @@ -1715,6 +1717,19 @@ asmlinkage __visible void __init xen_start_kernel(void) xen_setup_runstate_info(0); + efi_systab_xen = xen_efi_probe(); + + if (efi_systab_xen) { + strncpy((char *)&boot_params.efi_info.efi_loader_signature, "Xen", + sizeof(boot_params.efi_info.efi_loader_signature)); + boot_params.efi_info.efi_systab = (__u32)__pa(efi_systab_xen); + boot_params.efi_info.efi_systab_hi = (__u32)(__pa(efi_systab_xen) >> 32); + + set_bit(EFI_BOOT, &efi.flags); + set_bit(EFI_PARAVIRT, &efi.flags); + set_bit(EFI_64BIT, &efi.flags); + } + /* Start the world */ #ifdef CONFIG_X86_32 i386_start_kernel(); diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig index 38fb36e1c592..8bc01838daf9 100644 --- a/drivers/xen/Kconfig +++ b/drivers/xen/Kconfig @@ -240,4 +240,8 @@ config XEN_MCE_LOG config XEN_HAVE_PVMMU bool +config XEN_EFI + def_bool y + depends on X86_64 && EFI + endmenu diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile index 45e00afa7f2d..84044b554e33 100644 --- a/drivers/xen/Makefile +++ b/drivers/xen/Makefile @@ -9,6 +9,8 @@ obj-y += xenbus/ nostackp := $(call cc-option, -fno-stack-protector) CFLAGS_features.o := $(nostackp) +CFLAGS_efi.o += -fshort-wchar + dom0-$(CONFIG_PCI) += pci.o dom0-$(CONFIG_USB_SUPPORT) += dbgp.o dom0-$(CONFIG_ACPI) += acpi.o $(xen-pad-y) @@ -33,6 +35,7 @@ obj-$(CONFIG_XEN_STUB) += xen-stub.o obj-$(CONFIG_XEN_ACPI_HOTPLUG_MEMORY) += xen-acpi-memhotplug.o obj-$(CONFIG_XEN_ACPI_HOTPLUG_CPU) += xen-acpi-cpuhotplug.o obj-$(CONFIG_XEN_ACPI_PROCESSOR) += xen-acpi-processor.o +obj-$(CONFIG_XEN_EFI) += efi.o xen-evtchn-y := evtchn.o xen-gntdev-y := gntdev.o xen-gntalloc-y := gntalloc.o diff --git a/drivers/xen/efi.c b/drivers/xen/efi.c new file mode 100644 index 000000000000..31f618a49661 --- /dev/null +++ b/drivers/xen/efi.c @@ -0,0 +1,368 @@ +/* + * EFI support for Xen. + * + * Copyright (C) 1999 VA Linux Systems + * Copyright (C) 1999 Walt Drummond + * Copyright (C) 1999-2002 Hewlett-Packard Co. + * David Mosberger-Tang + * Stephane Eranian + * Copyright (C) 2005-2008 Intel Co. + * Fenghua Yu + * Bibo Mao + * Chandramouli Narayanan + * Huang Ying + * Copyright (C) 2011 Novell Co. + * Jan Beulich + * Copyright (C) 2011-2012 Oracle Co. + * Liang Tang + * Copyright (c) 2014 Oracle Co., Daniel Kiper + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#define INIT_EFI_OP(name) \ + {.cmd = XENPF_efi_runtime_call, \ + .u.efi_runtime_call.function = XEN_EFI_##name, \ + .u.efi_runtime_call.misc = 0} + +#define efi_data(op) (op.u.efi_runtime_call) + +static efi_status_t xen_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) +{ + struct xen_platform_op op = INIT_EFI_OP(get_time); + + if (HYPERVISOR_dom0_op(&op) < 0) + return EFI_UNSUPPORTED; + + if (tm) { + BUILD_BUG_ON(sizeof(*tm) != sizeof(efi_data(op).u.get_time.time)); + memcpy(tm, &efi_data(op).u.get_time.time, sizeof(*tm)); + } + + if (tc) { + tc->resolution = efi_data(op).u.get_time.resolution; + tc->accuracy = efi_data(op).u.get_time.accuracy; + tc->sets_to_zero = !!(efi_data(op).misc & + XEN_EFI_GET_TIME_SET_CLEARS_NS); + } + + return efi_data(op).status; +} + +static efi_status_t xen_efi_set_time(efi_time_t *tm) +{ + struct xen_platform_op op = INIT_EFI_OP(set_time); + + BUILD_BUG_ON(sizeof(*tm) != sizeof(efi_data(op).u.set_time)); + memcpy(&efi_data(op).u.set_time, tm, sizeof(*tm)); + + if (HYPERVISOR_dom0_op(&op) < 0) + return EFI_UNSUPPORTED; + + return efi_data(op).status; +} + +static efi_status_t xen_efi_get_wakeup_time(efi_bool_t *enabled, + efi_bool_t *pending, + efi_time_t *tm) +{ + struct xen_platform_op op = INIT_EFI_OP(get_wakeup_time); + + if (HYPERVISOR_dom0_op(&op) < 0) + return EFI_UNSUPPORTED; + + if (tm) { + BUILD_BUG_ON(sizeof(*tm) != sizeof(efi_data(op).u.get_wakeup_time)); + memcpy(tm, &efi_data(op).u.get_wakeup_time, sizeof(*tm)); + } + + if (enabled) + *enabled = !!(efi_data(op).misc & XEN_EFI_GET_WAKEUP_TIME_ENABLED); + + if (pending) + *pending = !!(efi_data(op).misc & XEN_EFI_GET_WAKEUP_TIME_PENDING); + + return efi_data(op).status; +} + +static efi_status_t xen_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm) +{ + struct xen_platform_op op = INIT_EFI_OP(set_wakeup_time); + + BUILD_BUG_ON(sizeof(*tm) != sizeof(efi_data(op).u.set_wakeup_time)); + if (enabled) + efi_data(op).misc = XEN_EFI_SET_WAKEUP_TIME_ENABLE; + if (tm) + memcpy(&efi_data(op).u.set_wakeup_time, tm, sizeof(*tm)); + else + efi_data(op).misc |= XEN_EFI_SET_WAKEUP_TIME_ENABLE_ONLY; + + if (HYPERVISOR_dom0_op(&op) < 0) + return EFI_UNSUPPORTED; + + return efi_data(op).status; +} + +static efi_status_t xen_efi_get_variable(efi_char16_t *name, + efi_guid_t *vendor, + u32 *attr, + unsigned long *data_size, + void *data) +{ + struct xen_platform_op op = INIT_EFI_OP(get_variable); + + set_xen_guest_handle(efi_data(op).u.get_variable.name, name); + BUILD_BUG_ON(sizeof(*vendor) != + sizeof(efi_data(op).u.get_variable.vendor_guid)); + memcpy(&efi_data(op).u.get_variable.vendor_guid, vendor, sizeof(*vendor)); + efi_data(op).u.get_variable.size = *data_size; + set_xen_guest_handle(efi_data(op).u.get_variable.data, data); + + if (HYPERVISOR_dom0_op(&op) < 0) + return EFI_UNSUPPORTED; + + *data_size = efi_data(op).u.get_variable.size; + if (attr) + *attr = efi_data(op).misc; + + return efi_data(op).status; +} + +static efi_status_t xen_efi_get_next_variable(unsigned long *name_size, + efi_char16_t *name, + efi_guid_t *vendor) +{ + struct xen_platform_op op = INIT_EFI_OP(get_next_variable_name); + + efi_data(op).u.get_next_variable_name.size = *name_size; + set_xen_guest_handle(efi_data(op).u.get_next_variable_name.name, name); + BUILD_BUG_ON(sizeof(*vendor) != + sizeof(efi_data(op).u.get_next_variable_name.vendor_guid)); + memcpy(&efi_data(op).u.get_next_variable_name.vendor_guid, vendor, + sizeof(*vendor)); + + if (HYPERVISOR_dom0_op(&op) < 0) + return EFI_UNSUPPORTED; + + *name_size = efi_data(op).u.get_next_variable_name.size; + memcpy(vendor, &efi_data(op).u.get_next_variable_name.vendor_guid, + sizeof(*vendor)); + + return efi_data(op).status; +} + +static efi_status_t xen_efi_set_variable(efi_char16_t *name, + efi_guid_t *vendor, + u32 attr, + unsigned long data_size, + void *data) +{ + struct xen_platform_op op = INIT_EFI_OP(set_variable); + + set_xen_guest_handle(efi_data(op).u.set_variable.name, name); + efi_data(op).misc = attr; + BUILD_BUG_ON(sizeof(*vendor) != + sizeof(efi_data(op).u.set_variable.vendor_guid)); + memcpy(&efi_data(op).u.set_variable.vendor_guid, vendor, sizeof(*vendor)); + efi_data(op).u.set_variable.size = data_size; + set_xen_guest_handle(efi_data(op).u.set_variable.data, data); + + if (HYPERVISOR_dom0_op(&op) < 0) + return EFI_UNSUPPORTED; + + return efi_data(op).status; +} + +static efi_status_t xen_efi_query_variable_info(u32 attr, + u64 *storage_space, + u64 *remaining_space, + u64 *max_variable_size) +{ + struct xen_platform_op op = INIT_EFI_OP(query_variable_info); + + if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) + return EFI_UNSUPPORTED; + + efi_data(op).u.query_variable_info.attr = attr; + + if (HYPERVISOR_dom0_op(&op) < 0) + return EFI_UNSUPPORTED; + + *storage_space = efi_data(op).u.query_variable_info.max_store_size; + *remaining_space = efi_data(op).u.query_variable_info.remain_store_size; + *max_variable_size = efi_data(op).u.query_variable_info.max_size; + + return efi_data(op).status; +} + +static efi_status_t xen_efi_get_next_high_mono_count(u32 *count) +{ + struct xen_platform_op op = INIT_EFI_OP(get_next_high_monotonic_count); + + if (HYPERVISOR_dom0_op(&op) < 0) + return EFI_UNSUPPORTED; + + *count = efi_data(op).misc; + + return efi_data(op).status; +} + +static efi_status_t xen_efi_update_capsule(efi_capsule_header_t **capsules, + unsigned long count, + unsigned long sg_list) +{ + struct xen_platform_op op = INIT_EFI_OP(update_capsule); + + if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) + return EFI_UNSUPPORTED; + + set_xen_guest_handle(efi_data(op).u.update_capsule.capsule_header_array, + capsules); + efi_data(op).u.update_capsule.capsule_count = count; + efi_data(op).u.update_capsule.sg_list = sg_list; + + if (HYPERVISOR_dom0_op(&op) < 0) + return EFI_UNSUPPORTED; + + return efi_data(op).status; +} + +static efi_status_t xen_efi_query_capsule_caps(efi_capsule_header_t **capsules, + unsigned long count, + u64 *max_size, + int *reset_type) +{ + struct xen_platform_op op = INIT_EFI_OP(query_capsule_capabilities); + + if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) + return EFI_UNSUPPORTED; + + set_xen_guest_handle(efi_data(op).u.query_capsule_capabilities.capsule_header_array, + capsules); + efi_data(op).u.query_capsule_capabilities.capsule_count = count; + + if (HYPERVISOR_dom0_op(&op) < 0) + return EFI_UNSUPPORTED; + + *max_size = efi_data(op).u.query_capsule_capabilities.max_capsule_size; + *reset_type = efi_data(op).u.query_capsule_capabilities.reset_type; + + return efi_data(op).status; +} + +static efi_char16_t vendor[100] __initdata; + +static efi_system_table_t efi_systab_xen __initdata = { + .hdr = { + .signature = EFI_SYSTEM_TABLE_SIGNATURE, + .revision = 0, /* Initialized later. */ + .headersize = 0, /* Ignored by Linux Kernel. */ + .crc32 = 0, /* Ignored by Linux Kernel. */ + .reserved = 0 + }, + .fw_vendor = EFI_INVALID_TABLE_ADDR, /* Initialized later. */ + .fw_revision = 0, /* Initialized later. */ + .con_in_handle = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */ + .con_in = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */ + .con_out_handle = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */ + .con_out = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */ + .stderr_handle = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */ + .stderr = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */ + .runtime = (efi_runtime_services_t *)EFI_INVALID_TABLE_ADDR, + /* Not used under Xen. */ + .boottime = (efi_boot_services_t *)EFI_INVALID_TABLE_ADDR, + /* Not used under Xen. */ + .nr_tables = 0, /* Initialized later. */ + .tables = EFI_INVALID_TABLE_ADDR /* Initialized later. */ +}; + +static const struct efi efi_xen __initconst = { + .systab = NULL, /* Initialized later. */ + .runtime_version = 0, /* Initialized later. */ + .mps = EFI_INVALID_TABLE_ADDR, + .acpi = EFI_INVALID_TABLE_ADDR, + .acpi20 = EFI_INVALID_TABLE_ADDR, + .smbios = EFI_INVALID_TABLE_ADDR, + .sal_systab = EFI_INVALID_TABLE_ADDR, + .boot_info = EFI_INVALID_TABLE_ADDR, + .hcdp = EFI_INVALID_TABLE_ADDR, + .uga = EFI_INVALID_TABLE_ADDR, + .uv_systab = EFI_INVALID_TABLE_ADDR, + .fw_vendor = EFI_INVALID_TABLE_ADDR, + .runtime = EFI_INVALID_TABLE_ADDR, + .config_table = EFI_INVALID_TABLE_ADDR, + .get_time = xen_efi_get_time, + .set_time = xen_efi_set_time, + .get_wakeup_time = xen_efi_get_wakeup_time, + .set_wakeup_time = xen_efi_set_wakeup_time, + .get_variable = xen_efi_get_variable, + .get_next_variable = xen_efi_get_next_variable, + .set_variable = xen_efi_set_variable, + .query_variable_info = xen_efi_query_variable_info, + .update_capsule = xen_efi_update_capsule, + .query_capsule_caps = xen_efi_query_capsule_caps, + .get_next_high_mono_count = xen_efi_get_next_high_mono_count, + .reset_system = NULL, /* Functionality provided by Xen. */ + .set_virtual_address_map = NULL, /* Not used under Xen. */ + .memmap = NULL, /* Not used under Xen. */ + .flags = 0 /* Initialized later. */ +}; + +efi_system_table_t __init *xen_efi_probe(void) +{ + struct xen_platform_op op = { + .cmd = XENPF_firmware_info, + .u.firmware_info = { + .type = XEN_FW_EFI_INFO, + .index = XEN_FW_EFI_CONFIG_TABLE + } + }; + union xenpf_efi_info *info = &op.u.firmware_info.u.efi_info; + + if (!xen_initial_domain() || HYPERVISOR_dom0_op(&op) < 0) + return NULL; + + /* Here we know that Xen runs on EFI platform. */ + + efi = efi_xen; + + efi_systab_xen.tables = info->cfg.addr; + efi_systab_xen.nr_tables = info->cfg.nent; + + op.cmd = XENPF_firmware_info; + op.u.firmware_info.type = XEN_FW_EFI_INFO; + op.u.firmware_info.index = XEN_FW_EFI_VENDOR; + info->vendor.bufsz = sizeof(vendor); + set_xen_guest_handle(info->vendor.name, vendor); + + if (HYPERVISOR_dom0_op(&op) == 0) { + efi_systab_xen.fw_vendor = __pa_symbol(vendor); + efi_systab_xen.fw_revision = info->vendor.revision; + } else + efi_systab_xen.fw_vendor = __pa_symbol(L"UNKNOWN"); + + op.cmd = XENPF_firmware_info; + op.u.firmware_info.type = XEN_FW_EFI_INFO; + op.u.firmware_info.index = XEN_FW_EFI_VERSION; + + if (HYPERVISOR_dom0_op(&op) == 0) + efi_systab_xen.hdr.revision = info->version; + + op.cmd = XENPF_firmware_info; + op.u.firmware_info.type = XEN_FW_EFI_INFO; + op.u.firmware_info.index = XEN_FW_EFI_RT_VERSION; + + if (HYPERVISOR_dom0_op(&op) == 0) + efi.runtime_version = info->version; + + return &efi_systab_xen; +} diff --git a/include/xen/xen-ops.h b/include/xen/xen-ops.h index 0b3149ed7eaa..771bbba27ccb 100644 --- a/include/xen/xen-ops.h +++ b/include/xen/xen-ops.h @@ -3,6 +3,7 @@ #include #include +#include #include DECLARE_PER_CPU(struct vcpu_info *, xen_vcpu); @@ -35,4 +36,14 @@ int xen_unmap_domain_mfn_range(struct vm_area_struct *vma, int numpgs, struct page **pages); bool xen_running_on_version_or_later(unsigned int major, unsigned int minor); + +#ifdef CONFIG_XEN_EFI +extern efi_system_table_t *xen_efi_probe(void); +#else +static efi_system_table_t __init *xen_efi_probe(void) +{ + return NULL; +} +#endif + #endif /* INCLUDE_XEN_OPS_H */ -- cgit v1.2.3 From f383d00a0d1f94a7d60c753ec8e3e402889f9622 Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Mon, 30 Jun 2014 19:53:04 +0200 Subject: arch/x86: Remove efi_set_rtc_mmss() efi_set_rtc_mmss() is never used to set RTC due to bugs found on many EFI platforms. It is set directly by mach_set_rtc_mmss(). Hence, remove unused efi_set_rtc_mmss() function. Signed-off-by: Daniel Kiper Signed-off-by: Matt Fleming --- arch/x86/platform/efi/efi.c | 36 ------------------------------------ include/linux/efi.h | 1 - 2 files changed, 37 deletions(-) (limited to 'include') diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 85fb16cd6479..850da94fef30 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -104,42 +104,6 @@ static efi_status_t __init phys_efi_set_virtual_address_map( return status; } -int efi_set_rtc_mmss(const struct timespec *now) -{ - unsigned long nowtime = now->tv_sec; - efi_status_t status; - efi_time_t eft; - efi_time_cap_t cap; - struct rtc_time tm; - - status = efi.get_time(&eft, &cap); - if (status != EFI_SUCCESS) { - pr_err("Oops: efitime: can't read time!\n"); - return -1; - } - - rtc_time_to_tm(nowtime, &tm); - if (!rtc_valid_tm(&tm)) { - eft.year = tm.tm_year + 1900; - eft.month = tm.tm_mon + 1; - eft.day = tm.tm_mday; - eft.minute = tm.tm_min; - eft.second = tm.tm_sec; - eft.nanosecond = 0; - } else { - pr_err("%s: Invalid EFI RTC value: write of %lx to EFI RTC failed\n", - __func__, nowtime); - return -1; - } - - status = efi.set_time(&eft); - if (status != EFI_SUCCESS) { - pr_err("Oops: efitime: can't write time!\n"); - return -1; - } - return 0; -} - void efi_get_time(struct timespec *now) { efi_status_t status; diff --git a/include/linux/efi.h b/include/linux/efi.h index c7a29a26e900..59c8acfebca7 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -873,7 +873,6 @@ 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 void efi_get_time(struct timespec *now); -extern int efi_set_rtc_mmss(const struct timespec *now); extern void efi_reserve_boot_services(void); extern int efi_get_fdt_params(struct efi_fdt_params *params, int verbose); extern struct efi_memory_map memmap; -- cgit v1.2.3 From 82f990a82244f8dfeb7e776186dc4811eb651ed7 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Tue, 8 Jul 2014 12:28:33 +0100 Subject: efi: Update stale locking comment for struct efivars The comment describing how struct efivars->lock is used hasn't been updated in sync with the code. Fix it. Reported-by: Ard Biesheuvel Cc: Mike Waychison Signed-off-by: Matt Fleming --- include/linux/efi.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/efi.h b/include/linux/efi.h index 59c8acfebca7..efc681fd5895 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -1040,12 +1040,8 @@ struct efivar_operations { struct efivars { /* * ->lock protects two things: - * 1) ->list - adds, removals, reads, writes - * 2) ops.[gs]et_variable() calls. - * It must not be held when creating sysfs entries or calling kmalloc. - * ops.get_next_variable() is only called from register_efivars() - * or efivar_update_sysfs_entries(), - * which is protected by the BKL, so that path is safe. + * 1) efivarfs_list and efivars_sysfs_list + * 2) ->ops calls */ spinlock_t lock; struct kset *kset; -- cgit v1.2.3 From 57f7138d760700f56a8aa944376c28ff83c47c26 Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Sat, 12 Jul 2014 23:09:47 +0200 Subject: xen: Silence compiler warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add inline keyword to silence the following compiler warnings if xen_efi_probe() is not used: CC arch/x86/xen/setup.o In file included from arch/x86/xen/xen-ops.h:7:0, from arch/x86/xen/setup.c:31: include/xen/xen-ops.h:43:35: warning: ‘xen_efi_probe’ defined but not used [-Wunused-function] Signed-off-by: Daniel Kiper Reviewed-by: Konrad Rzeszutek Wilk Signed-off-by: Matt Fleming --- include/xen/xen-ops.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/xen/xen-ops.h b/include/xen/xen-ops.h index 771bbba27ccb..7491ee5d8164 100644 --- a/include/xen/xen-ops.h +++ b/include/xen/xen-ops.h @@ -40,7 +40,7 @@ bool xen_running_on_version_or_later(unsigned int major, unsigned int minor); #ifdef CONFIG_XEN_EFI extern efi_system_table_t *xen_efi_probe(void); #else -static efi_system_table_t __init *xen_efi_probe(void) +static inline efi_system_table_t __init *xen_efi_probe(void) { return NULL; } -- cgit v1.2.3 From 6bb1d272d7c9f5dcfbb790d6aef47d8f82dccbf5 Mon Sep 17 00:00:00 2001 From: Jenny TC Date: Tue, 8 Jul 2014 11:34:18 +0530 Subject: power_supply: Add inlmt,iterm, min/max temp props Add new power supply properties for input current, charge termination current, min and max temperature POWER_SUPPLY_PROP_TEMP_MIN - minimum operatable temperature POWER_SUPPLY_PROP_TEMP_MAX - maximum operatable temperature POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT - input current limit programmed by charger. Indicates the input current for a charging source. POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT - Charge termination current used to detect the end of charge condition Signed-off-by: Jenny TC Acked-by: Pavel Machek Signed-off-by: Sebastian Reichel --- Documentation/power/power_supply_class.txt | 6 ++++++ drivers/power/power_supply_sysfs.c | 4 ++++ include/linux/power_supply.h | 4 ++++ 3 files changed, 14 insertions(+) (limited to 'include') diff --git a/Documentation/power/power_supply_class.txt b/Documentation/power/power_supply_class.txt index 89a8816990ff..48cff881cb8a 100644 --- a/Documentation/power/power_supply_class.txt +++ b/Documentation/power/power_supply_class.txt @@ -118,6 +118,10 @@ relative, time-based measurements. CONSTANT_CHARGE_CURRENT - constant charge current programmed by charger. CONSTANT_CHARGE_CURRENT_MAX - maximum charge current supported by the power supply object. +INPUT_CURRENT_LIMIT - input current limit programmed by charger. Indicates +the current drawn from a charging source. +CHARGE_TERM_CURRENT - Charge termination current used to detect the end of charge +condition. CONSTANT_CHARGE_VOLTAGE - constant charge voltage programmed by charger. CONSTANT_CHARGE_VOLTAGE_MAX - maximum charge voltage supported by the @@ -140,6 +144,8 @@ TEMP_ALERT_MAX - maximum battery temperature alert. TEMP_AMBIENT - ambient temperature. TEMP_AMBIENT_ALERT_MIN - minimum ambient temperature alert. TEMP_AMBIENT_ALERT_MAX - maximum ambient temperature alert. +TEMP_MIN - minimum operatable temperature +TEMP_MAX - maximum operatable temperature TIME_TO_EMPTY - seconds left for battery to be considered empty (i.e. while battery powers a load) diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 44420d1e9094..750a20275664 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -167,6 +167,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(constant_charge_voltage_max), POWER_SUPPLY_ATTR(charge_control_limit), POWER_SUPPLY_ATTR(charge_control_limit_max), + POWER_SUPPLY_ATTR(input_current_limit), POWER_SUPPLY_ATTR(energy_full_design), POWER_SUPPLY_ATTR(energy_empty_design), POWER_SUPPLY_ATTR(energy_full), @@ -178,6 +179,8 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(capacity_alert_max), POWER_SUPPLY_ATTR(capacity_level), POWER_SUPPLY_ATTR(temp), + POWER_SUPPLY_ATTR(temp_max), + POWER_SUPPLY_ATTR(temp_min), POWER_SUPPLY_ATTR(temp_alert_min), POWER_SUPPLY_ATTR(temp_alert_max), POWER_SUPPLY_ATTR(temp_ambient), @@ -189,6 +192,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(time_to_full_avg), POWER_SUPPLY_ATTR(type), POWER_SUPPLY_ATTR(scope), + POWER_SUPPLY_ATTR(charge_term_current), /* Properties of type `const char *' */ POWER_SUPPLY_ATTR(model_name), POWER_SUPPLY_ATTR(manufacturer), diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index f2b76aeaf4e4..f3dea41dbcd2 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -120,6 +120,7 @@ enum power_supply_property { POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN, POWER_SUPPLY_PROP_ENERGY_FULL, @@ -131,6 +132,8 @@ enum power_supply_property { POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX, /* in percents! */ POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TEMP_MAX, + POWER_SUPPLY_PROP_TEMP_MIN, POWER_SUPPLY_PROP_TEMP_ALERT_MIN, POWER_SUPPLY_PROP_TEMP_ALERT_MAX, POWER_SUPPLY_PROP_TEMP_AMBIENT, @@ -142,6 +145,7 @@ enum power_supply_property { POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */ POWER_SUPPLY_PROP_SCOPE, + POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, /* Properties of type `const char *' */ POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_MANUFACTURER, -- cgit v1.2.3 From a8278efd84f7643d5e44e5e507b657c231b0743b Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 16 Jul 2014 16:57:52 +0800 Subject: ACPICA: Linux: Add stub support for Linux specific variables and functions. There are global variables and functions not upstreamed to the ACPICA code base. Such symbols still can be referenced by external users as they are listed in the acpixf.h. This patch uses ACPI_GLOBAL and ACPI_EXTERNAL_RETURN_STATUS mechanism to add stub support for such symbols. Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/bus.c | 3 --- include/acpi/acpixf.h | 28 +++++++++++++++++----------- 2 files changed, 17 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index c5bc8cfe09fa..8581f5b84f48 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -477,9 +477,6 @@ static int __init acpi_bus_init_irq(void) return 0; } -u8 acpi_gbl_permanent_mmap; - - void __init acpi_early_init(void) { acpi_status status; diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 35b525c19711..bdbb145b1381 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -53,8 +53,6 @@ #include #include -extern u8 acpi_gbl_permanent_mmap; - /***************************************************************************** * * Macros used for ACPICA globals and configuration @@ -865,17 +863,25 @@ ACPI_DBG_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(6) /* * Divergences */ -acpi_status acpi_get_id(acpi_handle object, acpi_owner_id * out_type); +ACPI_GLOBAL(u8, acpi_gbl_permanent_mmap); + +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_get_id(acpi_handle object, + acpi_owner_id * out_type)) -acpi_status acpi_unload_table_id(acpi_owner_id id); +ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_unload_table_id(acpi_owner_id id)) -acpi_status -acpi_get_table_with_size(acpi_string signature, - u32 instance, struct acpi_table_header **out_table, - acpi_size *tbl_size); +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_get_table_with_size(acpi_string signature, + u32 instance, + struct acpi_table_header + **out_table, + acpi_size *tbl_size)) -acpi_status -acpi_get_data_full(acpi_handle object, acpi_object_handler handler, void **data, - void (*callback)(void *)); +ACPI_EXTERNAL_RETURN_STATUS(acpi_status + acpi_get_data_full(acpi_handle object, + acpi_object_handler handler, + void **data, + void (*callback)(void *))) #endif /* __ACXFACE_H__ */ -- cgit v1.2.3 From 11b8ddab817eb8070a542d33caeb93cccfa4e383 Mon Sep 17 00:00:00 2001 From: Reyad Attiyat Date: Thu, 17 Jul 2014 19:18:00 +0100 Subject: iio: types: Added support for rotation from north usage attributes Added the rotation from north usage attributes to the iio modifier enum and to the iio modifier names array. Signed-off-by: Reyad Attiyat Acked-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 4 ++++ include/linux/iio/types.h | 4 ++++ 2 files changed, 8 insertions(+) (limited to 'include') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 4b1f375c5659..af3e76d652ba 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -87,6 +87,10 @@ static const char * const iio_modifier_names[] = { [IIO_MOD_QUATERNION] = "quaternion", [IIO_MOD_TEMP_AMBIENT] = "ambient", [IIO_MOD_TEMP_OBJECT] = "object", + [IIO_MOD_NORTH_MAGN] = "from_north_magnetic", + [IIO_MOD_NORTH_TRUE] = "from_north_true", + [IIO_MOD_NORTH_MAGN_TILT_COMP] = "from_north_magnetic_tilt_comp", + [IIO_MOD_NORTH_TRUE_TILT_COMP] = "from_north_true_tilt_comp", }; /* relies on pairs of these shared then separate */ diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index 4a848d6be3bf..4a2af8adf874 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -56,6 +56,10 @@ enum iio_modifier { IIO_MOD_QUATERNION, IIO_MOD_TEMP_AMBIENT, IIO_MOD_TEMP_OBJECT, + IIO_MOD_NORTH_MAGN, + IIO_MOD_NORTH_TRUE, + IIO_MOD_NORTH_MAGN_TILT_COMP, + IIO_MOD_NORTH_TRUE_TILT_COMP }; enum iio_event_type { -- cgit v1.2.3 From b4e05923f9c5bb65ac82988d7b53cfd7425e6f36 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 20 Jul 2014 13:35:44 -0700 Subject: Input: add support for Wacom protocol 4 serial tablets Recent version of xf86-input-wacom no longer support directly accessing serial tablets. Instead xf86-input-wacom now expects all wacom tablets to be driven by the kernel and to show up as evdev devices. This has caused old serial Wacom tablets to stop working for people who still have such tablets. Julian Squires has written a serio input driver to fix this: https://github.com/tokenrove/wacom-serial-iv This is a cleaned up version of this driver with improved Graphire support (I own an old Graphire myself). Signed-off-by: Julian Squires Signed-off-by: Hans de Goede Signed-off-by: Dmitry Torokhov --- MAINTAINERS | 7 + drivers/input/tablet/Kconfig | 10 + drivers/input/tablet/Makefile | 1 + drivers/input/tablet/wacom_serial4.c | 616 +++++++++++++++++++++++++++++++++++ include/uapi/linux/serio.h | 1 + 5 files changed, 635 insertions(+) create mode 100644 drivers/input/tablet/wacom_serial4.c (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index 51ebb779c5f3..c68bd8b6f8e9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9708,6 +9708,13 @@ M: Pierre Ossman S: Maintained F: drivers/mmc/host/wbsd.* +WACOM PROTOCOL 4 SERIAL TABLETS +M: Julian Squires +M: Hans de Goede +L: linux-input@vger.kernel.org +S: Maintained +F: drivers/input/tablet/wacom_serial4.c + WATCHDOG DEVICE DRIVERS M: Wim Van Sebroeck L: linux-watchdog@vger.kernel.org diff --git a/drivers/input/tablet/Kconfig b/drivers/input/tablet/Kconfig index bed7cbf84cfd..a5121b09c63f 100644 --- a/drivers/input/tablet/Kconfig +++ b/drivers/input/tablet/Kconfig @@ -89,4 +89,14 @@ config TABLET_USB_WACOM To compile this driver as a module, choose M here: the module will be called wacom. +config TABLET_SERIAL_WACOM4 + tristate "Wacom protocol 4 serial tablet support" + select SERIO + help + Say Y here if you want to use Wacom protocol 4 serial tablets. + E.g. serial versions of the Cintiq, Graphire or Penpartner. + + To compile this driver as a module, choose M here: the + module will be called wacom_serial4. + endif diff --git a/drivers/input/tablet/Makefile b/drivers/input/tablet/Makefile index 3f6c25220638..4d9339fb3b63 100644 --- a/drivers/input/tablet/Makefile +++ b/drivers/input/tablet/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_TABLET_USB_GTCO) += gtco.o obj-$(CONFIG_TABLET_USB_HANWANG) += hanwang.o obj-$(CONFIG_TABLET_USB_KBTAB) += kbtab.o obj-$(CONFIG_TABLET_USB_WACOM) += wacom.o +obj-$(CONFIG_TABLET_SERIAL_WACOM4) += wacom_serial4.o diff --git a/drivers/input/tablet/wacom_serial4.c b/drivers/input/tablet/wacom_serial4.c new file mode 100644 index 000000000000..d3d251e27307 --- /dev/null +++ b/drivers/input/tablet/wacom_serial4.c @@ -0,0 +1,616 @@ +/* + * Wacom protocol 4 serial tablet driver + * + * Copyright 2014 Hans de Goede + * Copyright 2011-2012 Julian Squires + * + * 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 of 2 of the License, or (at your + * option) any later version. See the file COPYING in the main directory of + * this archive for more details. + * + * Many thanks to Bill Seremetis, without whom PenPartner support + * would not have been possible. Thanks to Patrick Mahoney. + * + * This driver was developed with reference to much code written by others, + * particularly: + * - elo, gunze drivers by Vojtech Pavlik ; + * - wacom_w8001 driver by Jaya Kumar ; + * - the USB wacom input driver, credited to many people + * (see drivers/input/tablet/wacom.h); + * - new and old versions of linuxwacom / xf86-input-wacom credited to + * Frederic Lepied, France. and + * Ping Cheng, Wacom. ; + * - and xf86wacom.c (a presumably ancient version of the linuxwacom code), + * by Frederic Lepied and Raph Levien . + * + * To do: + * - support pad buttons; (requires access to a model with pad buttons) + * - support (protocol 4-style) tilt (requires access to a > 1.4 rom model) + */ + +/* + * Wacom serial protocol 4 documentation taken from linuxwacom-0.9.9 code, + * protocol 4 uses 7 or 9 byte of data in the following format: + * + * Byte 1 + * bit 7 Sync bit always 1 + * bit 6 Pointing device detected + * bit 5 Cursor = 0 / Stylus = 1 + * bit 4 Reserved + * bit 3 1 if a button on the pointing device has been pressed + * bit 2 P0 (optional) + * bit 1 X15 + * bit 0 X14 + * + * Byte 2 + * bit 7 Always 0 + * bits 6-0 = X13 - X7 + * + * Byte 3 + * bit 7 Always 0 + * bits 6-0 = X6 - X0 + * + * Byte 4 + * bit 7 Always 0 + * bit 6 B3 + * bit 5 B2 + * bit 4 B1 + * bit 3 B0 + * bit 2 P1 (optional) + * bit 1 Y15 + * bit 0 Y14 + * + * Byte 5 + * bit 7 Always 0 + * bits 6-0 = Y13 - Y7 + * + * Byte 6 + * bit 7 Always 0 + * bits 6-0 = Y6 - Y0 + * + * Byte 7 + * bit 7 Always 0 + * bit 6 Sign of pressure data; or wheel-rel for cursor tool + * bit 5 P7; or REL1 for cursor tool + * bit 4 P6; or REL0 for cursor tool + * bit 3 P5 + * bit 2 P4 + * bit 1 P3 + * bit 0 P2 + * + * byte 8 and 9 are optional and present only + * in tilt mode. + * + * Byte 8 + * bit 7 Always 0 + * bit 6 Sign of tilt X + * bit 5 Xt6 + * bit 4 Xt5 + * bit 3 Xt4 + * bit 2 Xt3 + * bit 1 Xt2 + * bit 0 Xt1 + * + * Byte 9 + * bit 7 Always 0 + * bit 6 Sign of tilt Y + * bit 5 Yt6 + * bit 4 Yt5 + * bit 3 Yt4 + * bit 2 Yt3 + * bit 1 Yt2 + * bit 0 Yt1 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wacom_wac.h" + +MODULE_AUTHOR("Julian Squires , Hans de Goede "); +MODULE_DESCRIPTION("Wacom protocol 4 serial tablet driver"); +MODULE_LICENSE("GPL"); + +#define REQUEST_MODEL_AND_ROM_VERSION "~#" +#define REQUEST_MAX_COORDINATES "~C\r" +#define REQUEST_CONFIGURATION_STRING "~R\r" +#define REQUEST_RESET_TO_PROTOCOL_IV "\r#" +/* + * Note: sending "\r$\r" causes at least the Digitizer II to send + * packets in ASCII instead of binary. "\r#" seems to undo that. + */ + +#define COMMAND_START_SENDING_PACKETS "ST\r" +#define COMMAND_STOP_SENDING_PACKETS "SP\r" +#define COMMAND_MULTI_MODE_INPUT "MU1\r" +#define COMMAND_ORIGIN_IN_UPPER_LEFT "OC1\r" +#define COMMAND_ENABLE_ALL_MACRO_BUTTONS "~M0\r" +#define COMMAND_DISABLE_GROUP_1_MACRO_BUTTONS "~M1\r" +#define COMMAND_TRANSMIT_AT_MAX_RATE "IT0\r" +#define COMMAND_DISABLE_INCREMENTAL_MODE "IN0\r" +#define COMMAND_ENABLE_CONTINUOUS_MODE "SR\r" +#define COMMAND_ENABLE_PRESSURE_MODE "PH1\r" +#define COMMAND_Z_FILTER "ZF1\r" + +/* Note that this is a protocol 4 packet without tilt information. */ +#define PACKET_LENGTH 7 +#define DATA_SIZE 32 + +/* flags */ +#define F_COVERS_SCREEN 0x01 +#define F_HAS_STYLUS2 0x02 +#define F_HAS_SCROLLWHEEL 0x04 + +enum { STYLUS = 1, ERASER, CURSOR }; + +static const struct { + int device_id; + int input_id; +} tools[] = { + { 0, 0 }, + { STYLUS_DEVICE_ID, BTN_TOOL_PEN }, + { ERASER_DEVICE_ID, BTN_TOOL_RUBBER }, + { CURSOR_DEVICE_ID, BTN_TOOL_MOUSE }, +}; + +struct wacom { + struct input_dev *dev; + struct completion cmd_done; + int result; + u8 expect; + u8 eraser_mask; + unsigned int extra_z_bits; + unsigned int flags; + unsigned int res_x, res_y; + unsigned int max_x, max_y; + unsigned int tool; + unsigned int idx; + u8 data[DATA_SIZE]; + char phys[32]; +}; + +enum { + MODEL_CINTIQ = 0x504C, /* PL */ + MODEL_CINTIQ2 = 0x4454, /* DT */ + MODEL_DIGITIZER_II = 0x5544, /* UD */ + MODEL_GRAPHIRE = 0x4554, /* ET */ + MODEL_PENPARTNER = 0x4354, /* CT */ +}; + +static void wacom_handle_model_response(struct wacom *wacom) +{ + int major_v, minor_v, r = 0; + char *p; + + p = strrchr(wacom->data, 'V'); + if (p) + r = sscanf(p + 1, "%u.%u", &major_v, &minor_v); + if (r != 2) + major_v = minor_v = 0; + + switch (wacom->data[2] << 8 | wacom->data[3]) { + case MODEL_CINTIQ: /* UNTESTED */ + case MODEL_CINTIQ2: + if ((wacom->data[2] << 8 | wacom->data[3]) == MODEL_CINTIQ) { + wacom->dev->name = "Wacom Cintiq"; + wacom->dev->id.version = MODEL_CINTIQ; + } else { + wacom->dev->name = "Wacom Cintiq II"; + wacom->dev->id.version = MODEL_CINTIQ2; + } + wacom->res_x = 508; + wacom->res_y = 508; + + switch (wacom->data[5] << 8 | wacom->data[6]) { + case 0x3731: /* PL-710 */ + wacom->res_x = 2540; + wacom->res_y = 2540; + /* fall through */ + case 0x3535: /* PL-550 */ + case 0x3830: /* PL-800 */ + wacom->extra_z_bits = 2; + } + + wacom->flags = F_COVERS_SCREEN; + break; + + case MODEL_PENPARTNER: + wacom->dev->name = "Wacom Penpartner"; + wacom->dev->id.version = MODEL_PENPARTNER; + wacom->res_x = 1000; + wacom->res_y = 1000; + break; + + case MODEL_GRAPHIRE: + wacom->dev->name = "Wacom Graphire"; + wacom->dev->id.version = MODEL_GRAPHIRE; + wacom->res_x = 1016; + wacom->res_y = 1016; + wacom->max_x = 5103; + wacom->max_y = 3711; + wacom->extra_z_bits = 2; + wacom->eraser_mask = 0x08; + wacom->flags = F_HAS_STYLUS2 | F_HAS_SCROLLWHEEL; + break; + + case MODEL_DIGITIZER_II: + wacom->dev->name = "Wacom Digitizer II"; + wacom->dev->id.version = MODEL_DIGITIZER_II; + if (major_v == 1 && minor_v <= 2) + wacom->extra_z_bits = 0; /* UNTESTED */ + break; + + default: + dev_err(&wacom->dev->dev, "Unsupported Wacom model %s\n", + wacom->data); + wacom->result = -ENODEV; + return; + } + + dev_info(&wacom->dev->dev, "%s tablet, version %u.%u\n", + wacom->dev->name, major_v, minor_v); +} + +static void wacom_handle_configuration_response(struct wacom *wacom) +{ + int r, skip; + + dev_dbg(&wacom->dev->dev, "Configuration string: %s\n", wacom->data); + r = sscanf(wacom->data, "~R%x,%u,%u,%u,%u", &skip, &skip, &skip, + &wacom->res_x, &wacom->res_y); + if (r != 5) + dev_warn(&wacom->dev->dev, "could not get resolution\n"); +} + +static void wacom_handle_coordinates_response(struct wacom *wacom) +{ + int r; + + dev_dbg(&wacom->dev->dev, "Coordinates string: %s\n", wacom->data); + r = sscanf(wacom->data, "~C%u,%u", &wacom->max_x, &wacom->max_y); + if (r != 2) + dev_warn(&wacom->dev->dev, "could not get max coordinates\n"); +} + +static void wacom_handle_response(struct wacom *wacom) +{ + if (wacom->data[0] != '~' || wacom->data[1] != wacom->expect) { + dev_err(&wacom->dev->dev, + "Wacom got an unexpected response: %s\n", wacom->data); + wacom->result = -EIO; + } else { + wacom->result = 0; + + switch (wacom->data[1]) { + case '#': + wacom_handle_model_response(wacom); + break; + case 'R': + wacom_handle_configuration_response(wacom); + break; + case 'C': + wacom_handle_coordinates_response(wacom); + break; + } + } + + complete(&wacom->cmd_done); +} + +static void wacom_handle_packet(struct wacom *wacom) +{ + u8 in_proximity_p, stylus_p, button; + unsigned int tool; + int x, y, z; + + in_proximity_p = wacom->data[0] & 0x40; + stylus_p = wacom->data[0] & 0x20; + button = (wacom->data[3] & 0x78) >> 3; + x = (wacom->data[0] & 3) << 14 | wacom->data[1]<<7 | wacom->data[2]; + y = (wacom->data[3] & 3) << 14 | wacom->data[4]<<7 | wacom->data[5]; + + if (in_proximity_p && stylus_p) { + z = wacom->data[6] & 0x7f; + if (wacom->extra_z_bits >= 1) + z = z << 1 | (wacom->data[3] & 0x4) >> 2; + if (wacom->extra_z_bits > 1) + z = z << 1 | (wacom->data[0] & 0x4) >> 2; + z = z ^ (0x40 << wacom->extra_z_bits); + } else { + z = -1; + } + + if (stylus_p) + tool = (button & wacom->eraser_mask) ? ERASER : STYLUS; + else + tool = CURSOR; + + if (tool != wacom->tool && wacom->tool != 0) { + input_report_key(wacom->dev, tools[wacom->tool].input_id, 0); + input_sync(wacom->dev); + } + wacom->tool = tool; + + input_report_key(wacom->dev, tools[tool].input_id, in_proximity_p); + input_report_abs(wacom->dev, ABS_MISC, + in_proximity_p ? tools[tool].device_id : 0); + input_report_abs(wacom->dev, ABS_X, x); + input_report_abs(wacom->dev, ABS_Y, y); + input_report_abs(wacom->dev, ABS_PRESSURE, z); + if (stylus_p) { + input_report_key(wacom->dev, BTN_TOUCH, button & 1); + input_report_key(wacom->dev, BTN_STYLUS, button & 2); + input_report_key(wacom->dev, BTN_STYLUS2, button & 4); + } else { + input_report_key(wacom->dev, BTN_LEFT, button & 1); + input_report_key(wacom->dev, BTN_RIGHT, button & 2); + input_report_key(wacom->dev, BTN_MIDDLE, button & 4); + /* handle relative wheel for non-stylus device */ + z = (wacom->data[6] & 0x30) >> 4; + if (wacom->data[6] & 0x40) + z = -z; + input_report_rel(wacom->dev, REL_WHEEL, z); + } + input_sync(wacom->dev); +} + +static void wacom_clear_data_buf(struct wacom *wacom) +{ + memset(wacom->data, 0, DATA_SIZE); + wacom->idx = 0; +} + +static irqreturn_t wacom_interrupt(struct serio *serio, unsigned char data, + unsigned int flags) +{ + struct wacom *wacom = serio_get_drvdata(serio); + + if (data & 0x80) + wacom->idx = 0; + + /* + * We're either expecting a carriage return-terminated ASCII + * response string, or a seven-byte packet with the MSB set on + * the first byte. + * + * Note however that some tablets (the PenPartner, for + * example) don't send a carriage return at the end of a + * command. We handle these by waiting for timeout. + */ + if (data == '\r' && !(wacom->data[0] & 0x80)) { + wacom_handle_response(wacom); + wacom_clear_data_buf(wacom); + return IRQ_HANDLED; + } + + /* Leave place for 0 termination */ + if (wacom->idx > (DATA_SIZE - 2)) { + dev_dbg(&wacom->dev->dev, + "throwing away %d bytes of garbage\n", wacom->idx); + wacom_clear_data_buf(wacom); + } + wacom->data[wacom->idx++] = data; + + if (wacom->idx == PACKET_LENGTH && (wacom->data[0] & 0x80)) { + wacom_handle_packet(wacom); + wacom_clear_data_buf(wacom); + } + + return IRQ_HANDLED; +} + +static void wacom_disconnect(struct serio *serio) +{ + struct wacom *wacom = serio_get_drvdata(serio); + + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_unregister_device(wacom->dev); + kfree(wacom); +} + +static int wacom_send(struct serio *serio, const u8 *command) +{ + int err = 0; + + for (; !err && *command; command++) + err = serio_write(serio, *command); + + return err; +} + +static int wacom_send_setup_string(struct wacom *wacom, struct serio *serio) +{ + const u8 *cmd; + + switch (wacom->dev->id.version) { + case MODEL_CINTIQ: /* UNTESTED */ + cmd = COMMAND_ORIGIN_IN_UPPER_LEFT + COMMAND_TRANSMIT_AT_MAX_RATE + COMMAND_ENABLE_CONTINUOUS_MODE + COMMAND_START_SENDING_PACKETS; + break; + + case MODEL_PENPARTNER: + cmd = COMMAND_ENABLE_PRESSURE_MODE + COMMAND_START_SENDING_PACKETS; + break; + + default: + cmd = COMMAND_MULTI_MODE_INPUT + COMMAND_ORIGIN_IN_UPPER_LEFT + COMMAND_ENABLE_ALL_MACRO_BUTTONS + COMMAND_DISABLE_GROUP_1_MACRO_BUTTONS + COMMAND_TRANSMIT_AT_MAX_RATE + COMMAND_DISABLE_INCREMENTAL_MODE + COMMAND_ENABLE_CONTINUOUS_MODE + COMMAND_Z_FILTER + COMMAND_START_SENDING_PACKETS; + break; + } + + return wacom_send(serio, cmd); +} + +static int wacom_send_and_wait(struct wacom *wacom, struct serio *serio, + const u8 *cmd, const char *desc) +{ + int err; + unsigned long u; + + wacom->expect = cmd[1]; + init_completion(&wacom->cmd_done); + + err = wacom_send(serio, cmd); + if (err) + return err; + + u = wait_for_completion_timeout(&wacom->cmd_done, HZ); + if (u == 0) { + /* Timeout, process what we've received. */ + wacom_handle_response(wacom); + } + + wacom->expect = 0; + return wacom->result; +} + +static int wacom_setup(struct wacom *wacom, struct serio *serio) +{ + int err; + + /* Note that setting the link speed is the job of inputattach. + * We assume that reset negotiation has already happened, + * here. */ + err = wacom_send_and_wait(wacom, serio, REQUEST_MODEL_AND_ROM_VERSION, + "model and version"); + if (err) + return err; + + if (!(wacom->res_x && wacom->res_y)) { + err = wacom_send_and_wait(wacom, serio, + REQUEST_CONFIGURATION_STRING, + "configuration string"); + if (err) + return err; + } + + if (!(wacom->max_x && wacom->max_y)) { + err = wacom_send_and_wait(wacom, serio, + REQUEST_MAX_COORDINATES, + "coordinates string"); + if (err) + return err; + } + + return wacom_send_setup_string(wacom, serio); +} + +static int wacom_connect(struct serio *serio, struct serio_driver *drv) +{ + struct wacom *wacom; + struct input_dev *input_dev; + int err = -ENOMEM; + + wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!wacom || !input_dev) + goto free_device; + + wacom->dev = input_dev; + wacom->extra_z_bits = 1; + wacom->eraser_mask = 0x04; + wacom->tool = wacom->idx = 0; + snprintf(wacom->phys, sizeof(wacom->phys), "%s/input0", serio->phys); + input_dev->phys = wacom->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_WACOM_IV; + input_dev->id.product = serio->id.extra; + input_dev->dev.parent = &serio->dev; + + input_dev->evbit[0] = + BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) | BIT_MASK(EV_REL); + set_bit(ABS_MISC, input_dev->absbit); + set_bit(BTN_TOOL_PEN, input_dev->keybit); + set_bit(BTN_TOOL_RUBBER, input_dev->keybit); + set_bit(BTN_TOOL_MOUSE, input_dev->keybit); + set_bit(BTN_TOUCH, input_dev->keybit); + set_bit(BTN_STYLUS, input_dev->keybit); + set_bit(BTN_LEFT, input_dev->keybit); + set_bit(BTN_RIGHT, input_dev->keybit); + set_bit(BTN_MIDDLE, input_dev->keybit); + + serio_set_drvdata(serio, wacom); + + err = serio_open(serio, drv); + if (err) + goto free_device; + + err = wacom_setup(wacom, serio); + if (err) + goto close_serio; + + set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + if (!(wacom->flags & F_COVERS_SCREEN)) + __set_bit(INPUT_PROP_POINTER, input_dev->propbit); + + if (wacom->flags & F_HAS_STYLUS2) + __set_bit(BTN_STYLUS2, input_dev->keybit); + + if (wacom->flags & F_HAS_SCROLLWHEEL) + __set_bit(REL_WHEEL, input_dev->relbit); + + input_abs_set_res(wacom->dev, ABS_X, wacom->res_x); + input_abs_set_res(wacom->dev, ABS_Y, wacom->res_y); + input_set_abs_params(wacom->dev, ABS_X, 0, wacom->max_x, 0, 0); + input_set_abs_params(wacom->dev, ABS_Y, 0, wacom->max_y, 0, 0); + input_set_abs_params(wacom->dev, ABS_PRESSURE, -1, + (1 << (7 + wacom->extra_z_bits)) - 1, 0, 0); + + err = input_register_device(wacom->dev); + if (err) + goto close_serio; + + return 0; + +close_serio: + serio_close(serio); +free_device: + serio_set_drvdata(serio, NULL); + input_free_device(input_dev); + kfree(wacom); + return err; +} + +static struct serio_device_id wacom_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_WACOM_IV, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, wacom_serio_ids); + +static struct serio_driver wacom_drv = { + .driver = { + .name = "wacom_serial4", + }, + .description = "Wacom protocol 4 serial tablet driver", + .id_table = wacom_serio_ids, + .interrupt = wacom_interrupt, + .connect = wacom_connect, + .disconnect = wacom_disconnect, +}; + +module_serio_driver(wacom_drv); diff --git a/include/uapi/linux/serio.h b/include/uapi/linux/serio.h index 9f53fa7fc132..becdd78295cc 100644 --- a/include/uapi/linux/serio.h +++ b/include/uapi/linux/serio.h @@ -76,5 +76,6 @@ #define SERIO_HAMPSHIRE 0x3b #define SERIO_PS2MULT 0x3c #define SERIO_TSC40 0x3d +#define SERIO_WACOM_IV 0x3e #endif /* _UAPI_SERIO_H */ -- cgit v1.2.3 From 55537c7e7d76417303c32f84a8dd1a12e02c4409 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Wed, 2 Jul 2014 10:16:15 -0700 Subject: NFC: digital: Add digital framing calls when in target mode Add new "NFC_DIGITAL_FRAMING_*" calls to the digital layer so the driver can make the necessary adjustments when performing anticollision while in target mode. The driver must ensure that the effect of these calls happens after the following response has been sent but before reception of the next request begins. Acked-by: Thierry Escande Signed-off-by: Mark A. Greer Signed-off-by: Samuel Ortiz --- include/net/nfc/digital.h | 2 ++ net/nfc/digital_technology.c | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+) (limited to 'include') diff --git a/include/net/nfc/digital.h b/include/net/nfc/digital.h index bdf55c3b7a19..2bc31d10f9eb 100644 --- a/include/net/nfc/digital.h +++ b/include/net/nfc/digital.h @@ -49,6 +49,7 @@ enum { NFC_DIGITAL_FRAMING_NFCA_SHORT = 0, NFC_DIGITAL_FRAMING_NFCA_STANDARD, NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A, + NFC_DIGITAL_FRAMING_NFCA_ANTICOL_COMPLETE, NFC_DIGITAL_FRAMING_NFCA_T1T, NFC_DIGITAL_FRAMING_NFCA_T2T, @@ -66,6 +67,7 @@ enum { NFC_DIGITAL_FRAMING_NFCB, NFC_DIGITAL_FRAMING_NFCB_T4T, + NFC_DIGITAL_FRAMING_LAST, }; diff --git a/net/nfc/digital_technology.c b/net/nfc/digital_technology.c index d1684cbfea68..d276518cc8bf 100644 --- a/net/nfc/digital_technology.c +++ b/net/nfc/digital_technology.c @@ -944,6 +944,13 @@ static int digital_tg_send_sel_res(struct nfc_digital_dev *ddev) if (!DIGITAL_DRV_CAPS_TG_CRC(ddev)) digital_skb_add_crc_a(skb); + rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, + NFC_DIGITAL_FRAMING_NFCA_ANTICOL_COMPLETE); + if (rc) { + kfree_skb(skb); + return rc; + } + rc = digital_tg_send_cmd(ddev, skb, 300, digital_tg_recv_atr_req, NULL); if (rc) @@ -1002,6 +1009,13 @@ static int digital_tg_send_sdd_res(struct nfc_digital_dev *ddev) for (i = 0; i < 4; i++) sdd_res->bcc ^= sdd_res->nfcid1[i]; + rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, + NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A); + if (rc) { + kfree_skb(skb); + return rc; + } + rc = digital_tg_send_cmd(ddev, skb, 300, digital_tg_recv_sel_req, NULL); if (rc) @@ -1054,6 +1068,13 @@ static int digital_tg_send_sens_res(struct nfc_digital_dev *ddev) sens_res[0] = (DIGITAL_SENS_RES_NFC_DEP >> 8) & 0xFF; sens_res[1] = DIGITAL_SENS_RES_NFC_DEP & 0xFF; + rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, + NFC_DIGITAL_FRAMING_NFCA_STANDARD); + if (rc) { + kfree_skb(skb); + return rc; + } + rc = digital_tg_send_cmd(ddev, skb, 300, digital_tg_recv_sdd_req, NULL); if (rc) -- cgit v1.2.3 From c6f854d57d704a97adbf952ef0948acc68f3312c Mon Sep 17 00:00:00 2001 From: Veaceslav Falico Date: Thu, 17 Jul 2014 19:46:09 +0200 Subject: net: use dev->name in netdev_pr* when it's available netdev_name() returns dev->name only when the net_device is in NETREG_REGISTERED state. However, dev->name is always populated on creation, so we can easily use it. There are two cases when there's no real name - when it's an empty string or when the name is in form of "eth%d", then netdev_name() returns "unnamed net_device". CC: "David S. Miller" CC: Tom Gundersen Signed-off-by: Veaceslav Falico Acked-by: Tom Gundersen Signed-off-by: David S. Miller --- include/linux/netdevice.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 15ed750458ad..70256aa2ae81 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3383,8 +3383,8 @@ extern struct pernet_operations __net_initdata loopback_net_ops; static inline const char *netdev_name(const struct net_device *dev) { - if (dev->reg_state != NETREG_REGISTERED) - return "(unregistered net_device)"; + if (!dev->name[0] || strchr(dev->name, '%')) + return "(unnamed net_device)"; return dev->name; } -- cgit v1.2.3 From ccc7f4968a18b980994e622006b84e0195754390 Mon Sep 17 00:00:00 2001 From: Veaceslav Falico Date: Thu, 17 Jul 2014 19:46:10 +0200 Subject: net: print net_device reg_state in netdev_* unless it's registered This way we'll always know in what status the device is, unless it's running normally (i.e. NETDEV_REGISTERED). Also, emit a warning once in case of a bad reg_state. CC: "David S. Miller" CC: Jason Baron CC: Eric Dumazet CC: Vlad Yasevich CC: stephen hemminger CC: Jerry Chu CC: Ben Hutchings CC: Joe Perches Signed-off-by: Veaceslav Falico Signed-off-by: David S. Miller --- include/linux/netdevice.h | 18 +++++++++++++++++- lib/dynamic_debug.c | 8 +++++--- net/core/dev.c | 8 +++++--- 3 files changed, 27 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 70256aa2ae81..8e8fb3ed574b 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3388,6 +3388,21 @@ static inline const char *netdev_name(const struct net_device *dev) return dev->name; } +static inline const char *netdev_reg_state(const struct net_device *dev) +{ + switch (dev->reg_state) { + case NETREG_UNINITIALIZED: return " (uninitialized)"; + case NETREG_REGISTERED: return ""; + case NETREG_UNREGISTERING: return " (unregistering)"; + case NETREG_UNREGISTERED: return " (unregistered)"; + case NETREG_RELEASED: return " (released)"; + case NETREG_DUMMY: return " (dummy)"; + } + + WARN_ONCE(1, "%s: unknown reg_state %d\n", dev->name, dev->reg_state); + return " (unknown)"; +} + __printf(3, 4) int netdev_printk(const char *level, const struct net_device *dev, const char *format, ...); @@ -3444,7 +3459,8 @@ do { \ * file/line information and a backtrace. */ #define netdev_WARN(dev, format, args...) \ - WARN(1, "netdevice: %s\n" format, netdev_name(dev), ##args) + WARN(1, "netdevice: %s%s\n" format, netdev_name(dev), \ + netdev_reg_state(dev), ##args) /* netif printk helpers, similar to netdev_printk */ diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 7288e38e1757..c9afbe2c445a 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -614,13 +614,15 @@ int __dynamic_netdev_dbg(struct _ddebug *descriptor, char buf[PREFIX_SIZE]; res = dev_printk_emit(7, dev->dev.parent, - "%s%s %s %s: %pV", + "%s%s %s %s%s: %pV", dynamic_emit_prefix(descriptor, buf), dev_driver_string(dev->dev.parent), dev_name(dev->dev.parent), - netdev_name(dev), &vaf); + netdev_name(dev), netdev_reg_state(dev), + &vaf); } else if (dev) { - res = printk(KERN_DEBUG "%s: %pV", netdev_name(dev), &vaf); + res = printk(KERN_DEBUG "%s%s: %pV", netdev_name(dev), + netdev_reg_state(dev), &vaf); } else { res = printk(KERN_DEBUG "(NULL net_device): %pV", &vaf); } diff --git a/net/core/dev.c b/net/core/dev.c index 239722af098d..81d61014fd9b 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -6950,12 +6950,14 @@ static int __netdev_printk(const char *level, const struct net_device *dev, if (dev && dev->dev.parent) { r = dev_printk_emit(level[1] - '0', dev->dev.parent, - "%s %s %s: %pV", + "%s %s %s%s: %pV", dev_driver_string(dev->dev.parent), dev_name(dev->dev.parent), - netdev_name(dev), vaf); + netdev_name(dev), netdev_reg_state(dev), + vaf); } else if (dev) { - r = printk("%s%s: %pV", level, netdev_name(dev), vaf); + r = printk("%s%s%s: %pV", level, netdev_name(dev), + netdev_reg_state(dev), vaf); } else { r = printk("%s(NULL net_device): %pV", level, vaf); } -- cgit v1.2.3 From 589008106b9b64153554c8f3f120ee512b134bb5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 16 Jul 2014 17:45:27 +0200 Subject: ALSA: pcm: Introduce protocol version field to sw_params For controlling the new fields more strictly, add sw_params.proto field indicating the protocol version of the user-space. User-space should fill the SNDRV_PCM_VERSION value it's built with, then kernel can know whether the new fields should be evaluated or not. And now tstamp_type field is evaluated only when the valid value is set there. This avoids the wrong override of tstamp_type to zero, which is SNDRV_PCM_TSTAMP_TYPE_GETTIMEOFDAY. Signed-off-by: Takashi Iwai --- include/uapi/sound/asound.h | 4 ++-- sound/core/pcm_native.c | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index a7e062f91f39..32168f7ffce3 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -391,8 +391,8 @@ struct snd_pcm_sw_params { snd_pcm_uframes_t silence_threshold; /* min distance from noise for silence filling */ snd_pcm_uframes_t silence_size; /* silence block size */ snd_pcm_uframes_t boundary; /* pointers wrap point */ - unsigned int tstamp_type; /* timestamp type */ - int pads; /* alignment, reserved */ + unsigned int proto; /* protocol version */ + unsigned int tstamp_type; /* timestamp type (req. proto >= 2.0.12) */ unsigned char reserved[56]; /* reserved for future */ }; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 81dedc381efd..8cd2f930ad0b 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -543,7 +543,8 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream, if (params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST) return -EINVAL; - if (params->tstamp_type > SNDRV_PCM_TSTAMP_TYPE_LAST) + if (params->proto >= SNDRV_PROTOCOL_VERSION(2, 0, 12) && + params->tstamp_type > SNDRV_PCM_TSTAMP_TYPE_LAST) return -EINVAL; if (params->avail_min == 0) return -EINVAL; @@ -559,7 +560,8 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream, err = 0; snd_pcm_stream_lock_irq(substream); runtime->tstamp_mode = params->tstamp_mode; - runtime->tstamp_type = params->tstamp_type; + if (params->proto >= SNDRV_PROTOCOL_VERSION(2, 0, 12)) + runtime->tstamp_type = params->tstamp_type; runtime->period_step = params->period_step; runtime->control->avail_min = params->avail_min; runtime->start_threshold = params->start_threshold; -- cgit v1.2.3 From 6f7eaa47e1de30159277f91f1145a6687f13ffd9 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 17 Jul 2014 17:14:24 +0300 Subject: mac80211: add TDLS QoS param IE on setup-confirm When TDLS QoS is supported by the the peer and the local card, add the WMM parameter IE to the setup-confirm frame. Take the QoS settings from the current AP, or if unsupported, use the default values from the specification. This behavior is mandated by IEEE802.11-2012 section 10.22.4. Signed-off-by: Arik Nemtsov Reviewed-by: Liad Kaufman Reviewed-by: Johannes Berg Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 20 ++++++++ net/mac80211/tdls.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+) (limited to 'include') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 75d17e15da33..63ab3873c5ed 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1001,6 +1001,26 @@ struct ieee80211_vendor_ie { u8 oui_type; } __packed; +struct ieee80211_wmm_ac_param { + u8 aci_aifsn; /* AIFSN, ACM, ACI */ + u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */ + __le16 txop_limit; +} __packed; + +struct ieee80211_wmm_param_ie { + u8 element_id; /* Element ID: 221 (0xdd); */ + u8 len; /* Length: 24 */ + /* required fields for WMM version 1 */ + u8 oui[3]; /* 00:50:f2 */ + u8 oui_type; /* 2 */ + u8 oui_subtype; /* 1 */ + u8 version; /* 1 for WMM version 1.0 */ + u8 qos_info; /* AP/STA specific QoS info */ + u8 reserved; /* 0 */ + /* AC_BE, AC_BK, AC_VI, AC_VO */ + struct ieee80211_wmm_ac_param ac[4]; +} __packed; + /* Control frames */ struct ieee80211_rts { __le16 frame_control; diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index bfd8fc4a6b2f..72eebea7e60a 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -8,6 +8,7 @@ */ #include +#include #include #include "ieee80211_i.h" #include "driver-ops.h" @@ -93,6 +94,74 @@ static void ieee80211_tdls_add_link_ie(struct ieee80211_sub_if_data *sdata, memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN); } +/* translate numbering in the WMM parameter IE to the mac80211 notation */ +static enum ieee80211_ac_numbers ieee80211_ac_from_wmm(int ac) +{ + switch (ac) { + default: + WARN_ON_ONCE(1); + case 0: + return IEEE80211_AC_BE; + case 1: + return IEEE80211_AC_BK; + case 2: + return IEEE80211_AC_VI; + case 3: + return IEEE80211_AC_VO; + } +} + +static u8 ieee80211_wmm_aci_aifsn(int aifsn, bool acm, int aci) +{ + u8 ret; + + ret = aifsn & 0x0f; + if (acm) + ret |= 0x10; + ret |= (aci << 5) & 0x60; + return ret; +} + +static u8 ieee80211_wmm_ecw(u16 cw_min, u16 cw_max) +{ + return ((ilog2(cw_min + 1) << 0x0) & 0x0f) | + ((ilog2(cw_max + 1) << 0x4) & 0xf0); +} + +static void ieee80211_tdls_add_wmm_param_ie(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) +{ + struct ieee80211_wmm_param_ie *wmm; + struct ieee80211_tx_queue_params *txq; + int i; + + wmm = (void *)skb_put(skb, sizeof(*wmm)); + memset(wmm, 0, sizeof(*wmm)); + + wmm->element_id = WLAN_EID_VENDOR_SPECIFIC; + wmm->len = sizeof(*wmm) - 2; + + wmm->oui[0] = 0x00; /* Microsoft OUI 00:50:F2 */ + wmm->oui[1] = 0x50; + wmm->oui[2] = 0xf2; + wmm->oui_type = 2; /* WME */ + wmm->oui_subtype = 1; /* WME param */ + wmm->version = 1; /* WME ver */ + wmm->qos_info = 0; /* U-APSD not in use */ + + /* + * Use the EDCA parameters defined for the BSS, or default if the AP + * doesn't support it, as mandated by 802.11-2012 section 10.22.4 + */ + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + txq = &sdata->tx_conf[ieee80211_ac_from_wmm(i)]; + wmm->ac[i].aci_aifsn = ieee80211_wmm_aci_aifsn(txq->aifs, + txq->acm, i); + wmm->ac[i].cw = ieee80211_wmm_ecw(txq->cw_min, txq->cw_max); + wmm->ac[i].txop_limit = cpu_to_le16(txq->txop); + } +} + static void ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, const u8 *peer, @@ -165,6 +234,56 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator); } +static void +ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, const u8 *peer, + bool initiator, const u8 *extra_ies, + size_t extra_ies_len) +{ + struct ieee80211_local *local = sdata->local; + size_t offset = 0, noffset; + struct sta_info *sta; + u8 *pos; + + rcu_read_lock(); + + sta = sta_info_get(sdata, peer); + if (WARN_ON_ONCE(!sta)) { + rcu_read_unlock(); + return; + } + + /* add any custom IEs that go before the QoS IE */ + if (extra_ies_len) { + static const u8 before_qos[] = { + WLAN_EID_RSN, + }; + noffset = ieee80211_ie_split(extra_ies, extra_ies_len, + before_qos, + ARRAY_SIZE(before_qos), + offset); + pos = skb_put(skb, noffset - offset); + memcpy(pos, extra_ies + offset, noffset - offset); + offset = noffset; + } + + /* add the QoS param IE if both the peer and we support it */ + if (local->hw.queues >= IEEE80211_NUM_ACS && + test_sta_flag(sta, WLAN_STA_WME)) + ieee80211_tdls_add_wmm_param_ie(sdata, skb); + + /* add any remaining IEs */ + if (extra_ies_len) { + noffset = extra_ies_len; + pos = skb_put(skb, noffset - offset); + memcpy(pos, extra_ies + offset, noffset - offset); + } + + ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator); + + rcu_read_unlock(); +} + static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, const u8 *peer, u8 action_code, u16 status_code, @@ -183,6 +302,11 @@ static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata, extra_ies_len); break; case WLAN_TDLS_SETUP_CONFIRM: + if (status_code == 0) + ieee80211_tdls_add_setup_cfm_ies(sdata, skb, peer, + initiator, extra_ies, + extra_ies_len); + break; case WLAN_TDLS_TEARDOWN: case WLAN_TDLS_DISCOVERY_REQUEST: if (extra_ies_len) -- cgit v1.2.3 From 5b0c0b16d48d20e26859907df4dd449e3b3c7f4c Mon Sep 17 00:00:00 2001 From: Stratos Karafotis Date: Mon, 30 Jun 2014 19:59:33 +0300 Subject: cpufreq: Introduce new relation for freq selection Introduce CPUFREQ_RELATION_C for frequency selection. It selects the frequency with the minimum euclidean distance to target. In case of equal distance between 2 frequencies, it will select the greater frequency. Signed-off-by: Stratos Karafotis Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/freq_table.c | 12 +++++++++++- include/linux/cpufreq.h | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c index 1632981c4b25..df14766a8e06 100644 --- a/drivers/cpufreq/freq_table.c +++ b/drivers/cpufreq/freq_table.c @@ -117,7 +117,7 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy, .frequency = 0, }; struct cpufreq_frequency_table *pos; - unsigned int freq, i = 0; + unsigned int freq, diff, i = 0; pr_debug("request for target %u kHz (relation: %u) for cpu %u\n", target_freq, relation, policy->cpu); @@ -127,6 +127,7 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy, suboptimal.frequency = ~0; break; case CPUFREQ_RELATION_L: + case CPUFREQ_RELATION_C: optimal.frequency = ~0; break; } @@ -168,6 +169,15 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy, } } break; + case CPUFREQ_RELATION_C: + diff = abs(freq - target_freq); + if (diff < optimal.frequency || + (diff == optimal.frequency && + freq > table[optimal.driver_data].frequency)) { + optimal.frequency = diff; + optimal.driver_data = i; + } + break; } } if (optimal.driver_data > i) { diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 8f8ae95c6e27..7d1955afa62c 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -176,6 +176,7 @@ static inline void disable_cpufreq(void) { } #define CPUFREQ_RELATION_L 0 /* lowest frequency at or above target */ #define CPUFREQ_RELATION_H 1 /* highest frequency below or at target */ +#define CPUFREQ_RELATION_C 2 /* closest frequency to target */ struct freq_attr { struct attribute attr; -- cgit v1.2.3 From 08cf42e843f9a7e253502011c81677f61f7e5c42 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 16 Jul 2014 12:12:15 +0200 Subject: mac80211: add support for Rx reordering offloading Some drivers may be performing most of Tx/Rx aggregation on their own (e.g. in firmware) including AddBa/DelBa negotiations but may otherwise require Rx reordering assistance. The patch exports 2 new functions for establishing Rx aggregation sessions in assumption device driver has taken care of the necessary negotiations. Signed-off-by: Michal Kazior [fix endian bug] Signed-off-by: Johannes Berg --- include/net/mac80211.h | 34 +++++++++++++++ net/mac80211/agg-rx.c | 101 ++++++++++++++++++++++++++++++++++----------- net/mac80211/ieee80211_i.h | 11 +++++ net/mac80211/iface.c | 29 +++++++++++++ 4 files changed, 152 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 9ce5cb17ed82..dae2e24616e1 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -4552,6 +4552,40 @@ void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap, */ void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn); +/** + * ieee80211_start_rx_ba_session_offl - start a Rx BA session + * + * Some device drivers may offload part of the Rx aggregation flow including + * AddBa/DelBa negotiation but may otherwise be incapable of full Rx + * reordering. + * + * Create structures responsible for reordering so device drivers may call here + * when they complete AddBa negotiation. + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback + * @addr: station mac address + * @tid: the rx tid + */ +void ieee80211_start_rx_ba_session_offl(struct ieee80211_vif *vif, + const u8 *addr, u16 tid); + +/** + * ieee80211_stop_rx_ba_session_offl - stop a Rx BA session + * + * Some device drivers may offload part of the Rx aggregation flow including + * AddBa/DelBa negotiation but may otherwise be incapable of full Rx + * reordering. + * + * Destroy structures responsible for reordering so device drivers may call here + * when they complete DelBa negotiation. + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback + * @addr: station mac address + * @tid: the rx tid + */ +void ieee80211_stop_rx_ba_session_offl(struct ieee80211_vif *vif, + const u8 *addr, u16 tid); + /* Rate control API */ /** diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index d38c49b644cd..f0e84bc48038 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -224,28 +224,15 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d ieee80211_tx_skb(sdata, skb); } -void ieee80211_process_addba_request(struct ieee80211_local *local, - struct sta_info *sta, - struct ieee80211_mgmt *mgmt, - size_t len) +void __ieee80211_start_rx_ba_session(struct sta_info *sta, + u8 dialog_token, u16 timeout, + u16 start_seq_num, u16 ba_policy, u16 tid, + u16 buf_size, bool tx) { + struct ieee80211_local *local = sta->sdata->local; struct tid_ampdu_rx *tid_agg_rx; - u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num, status; - u8 dialog_token; int i, ret = -EOPNOTSUPP; - - /* extract session parameters from addba request frame */ - dialog_token = mgmt->u.action.u.addba_req.dialog_token; - timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout); - start_seq_num = - le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4; - - capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab); - ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1; - tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2; - buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6; - - status = WLAN_STATUS_REQUEST_DECLINED; + u16 status = WLAN_STATUS_REQUEST_DECLINED; if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) { ht_dbg(sta->sdata, @@ -264,7 +251,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, status = WLAN_STATUS_INVALID_QOS_PARAM; 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); + sta->sta.addr, tid, ba_policy, buf_size); goto end_no_lock; } /* determine default buffer size */ @@ -281,7 +268,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, if (sta->ampdu_mlme.tid_rx[tid]) { ht_dbg_ratelimited(sta->sdata, "unexpected AddBA Req from %pM on tid %u\n", - mgmt->sa, tid); + sta->sta.addr, tid); /* delete existing Rx BA session on the same tid */ ___ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT, @@ -353,6 +340,74 @@ end: mutex_unlock(&sta->ampdu_mlme.mtx); end_no_lock: - ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid, - dialog_token, status, 1, buf_size, timeout); + if (tx) + ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid, + dialog_token, status, 1, buf_size, + timeout); +} + +void ieee80211_process_addba_request(struct ieee80211_local *local, + struct sta_info *sta, + struct ieee80211_mgmt *mgmt, + size_t len) +{ + u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num; + u8 dialog_token; + + /* extract session parameters from addba request frame */ + dialog_token = mgmt->u.action.u.addba_req.dialog_token; + timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout); + start_seq_num = + le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4; + + capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab); + ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1; + tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2; + buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6; + + __ieee80211_start_rx_ba_session(sta, dialog_token, timeout, + start_seq_num, ba_policy, tid, + buf_size, true); +} + +void ieee80211_start_rx_ba_session_offl(struct ieee80211_vif *vif, + const u8 *addr, u16 tid) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_local *local = sdata->local; + struct ieee80211_rx_agg *rx_agg; + struct sk_buff *skb = dev_alloc_skb(0); + + if (unlikely(!skb)) + return; + + rx_agg = (struct ieee80211_rx_agg *) &skb->cb; + memcpy(&rx_agg->addr, addr, ETH_ALEN); + rx_agg->tid = tid; + + skb->pkt_type = IEEE80211_SDATA_QUEUE_RX_AGG_START; + skb_queue_tail(&sdata->skb_queue, skb); + ieee80211_queue_work(&local->hw, &sdata->work); +} +EXPORT_SYMBOL(ieee80211_start_rx_ba_session_offl); + +void ieee80211_stop_rx_ba_session_offl(struct ieee80211_vif *vif, + const u8 *addr, u16 tid) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_local *local = sdata->local; + struct ieee80211_rx_agg *rx_agg; + struct sk_buff *skb = dev_alloc_skb(0); + + if (unlikely(!skb)) + return; + + rx_agg = (struct ieee80211_rx_agg *) &skb->cb; + memcpy(&rx_agg->addr, addr, ETH_ALEN); + rx_agg->tid = tid; + + skb->pkt_type = IEEE80211_SDATA_QUEUE_RX_AGG_STOP; + skb_queue_tail(&sdata->skb_queue, skb); + ieee80211_queue_work(&local->hw, &sdata->work); } +EXPORT_SYMBOL(ieee80211_stop_rx_ba_session_offl); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index c504e99a5404..ef7a089ac546 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -925,10 +925,17 @@ ieee80211_vif_get_shift(struct ieee80211_vif *vif) return shift; } +struct ieee80211_rx_agg { + u8 addr[ETH_ALEN]; + u16 tid; +}; + enum sdata_queue_type { IEEE80211_SDATA_QUEUE_TYPE_FRAME = 0, IEEE80211_SDATA_QUEUE_AGG_START = 1, IEEE80211_SDATA_QUEUE_AGG_STOP = 2, + IEEE80211_SDATA_QUEUE_RX_AGG_START = 3, + IEEE80211_SDATA_QUEUE_RX_AGG_STOP = 4, }; enum { @@ -1577,6 +1584,10 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, u16 initiator, u16 reason, bool stop); void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, u16 initiator, u16 reason, bool stop); +void __ieee80211_start_rx_ba_session(struct sta_info *sta, + u8 dialog_token, u16 timeout, + u16 start_seq_num, u16 ba_policy, u16 tid, + u16 buf_size, bool tx); void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, enum ieee80211_agg_stop_reason reason); void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 2a12b8aa6aad..29be8854a027 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1140,6 +1140,7 @@ static void ieee80211_iface_work(struct work_struct *work) struct sk_buff *skb; struct sta_info *sta; struct ieee80211_ra_tid *ra_tid; + struct ieee80211_rx_agg *rx_agg; if (!ieee80211_sdata_running(sdata)) return; @@ -1167,6 +1168,34 @@ static void ieee80211_iface_work(struct work_struct *work) ra_tid = (void *)&skb->cb; ieee80211_stop_tx_ba_cb(&sdata->vif, ra_tid->ra, ra_tid->tid); + } else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_RX_AGG_START) { + rx_agg = (void *)&skb->cb; + mutex_lock(&local->sta_mtx); + sta = sta_info_get_bss(sdata, rx_agg->addr); + if (sta) { + u16 last_seq; + + last_seq = le16_to_cpu( + sta->last_seq_ctrl[rx_agg->tid]); + + __ieee80211_start_rx_ba_session(sta, + 0, 0, + ieee80211_sn_inc(last_seq), + 1, rx_agg->tid, + IEEE80211_MAX_AMPDU_BUF, + false); + } + mutex_unlock(&local->sta_mtx); + } else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_RX_AGG_STOP) { + rx_agg = (void *)&skb->cb; + mutex_lock(&local->sta_mtx); + sta = sta_info_get_bss(sdata, rx_agg->addr); + if (sta) + __ieee80211_stop_rx_ba_session(sta, + rx_agg->tid, + WLAN_BACK_RECIPIENT, 0, + false); + mutex_unlock(&local->sta_mtx); } else if (ieee80211_is_action(mgmt->frame_control) && mgmt->u.action.category == WLAN_CATEGORY_BACK) { int len = skb->len; -- cgit v1.2.3 From 4362175dd65d1816a18ac3f14107d788d5fced27 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 14 Jul 2014 18:29:16 +0100 Subject: mfd: ab8500-debugfs: BIG clean-up When checkpatch is run on ab8500-debugfs.c it screamed blue murder! This patch fixes up all of the errors/warnings reported: WARNING: line over 80 characters + err = seq_printf(s, " [0x%02X/0x%02X]: 0x%02X\n", WARNING: Prefer [subsystem eg: netdev]_info([subsystem]dev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... + printk(KERN_INFO" [0x%02X/0x%02X]: 0x%02X\n", WARNING: Prefer seq_puts to seq_printf + seq_printf(s, AB8500_NAME_STRING " register values:\n"); WARNING: Prefer seq_puts to seq_printf + seq_printf(s, AB8500_NAME_STRING " register values:\n"); WARNING: Prefer [subsystem eg: netdev]_info([subsystem]dev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... + printk(KERN_INFO"ab8500 register values:\n"); WARNING: Prefer [subsystem eg: netdev]_info([subsystem]dev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... + printk(KERN_INFO" bank 0x%02X:\n", i); WARNING: externs should be avoided in .c files +extern int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size); WARNING: quoted string split across lines + pr_info("Saving all ABB registers at \"ab8500_complete_register_dump\" " + "for crash analyze.\n"); WARNING: Prefer [subsystem eg: netdev]_err([subsystem]dev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... + printk(KERN_ERR "abx500_set_reg failed %d, %d", err, __LINE__); WARNING: Prefer seq_puts to seq_printf + seq_printf(s, "name: number: number of: wake:\n"); WARNING: line over 80 characters + return single_open(file, ab8500_print_modem_registers, inode->i_private); WARNING: line over 80 characters + return single_open(file, ab8500_gpadc_btemp_ball_print, inode->i_private); WARNING: line over 80 characters + return single_open(file, ab8500_gpadc_main_bat_v_print, inode->i_private); WARNING: line over 80 characters + vbat_true_meas_convert = ab8500_gpadc_ad_to_voltage(gpadc, VBAT_TRUE_MEAS, WARNING: line over 80 characters +static int ab8540_gpadc_vbat_true_meas_and_ibat_print(struct seq_file *s, void *p) WARNING: line over 80 characters +static const struct file_operations ab8540_gpadc_vbat_true_meas_and_ibat_fops = { WARNING: line over 80 characters + vmain_l, vmain_h, btemp_l, btemp_h, vbat_l, vbat_h, ibat_l, ibat_h); WARNING: quoted string split across lines + dev_err(dev, "debugfs error input: " + "should be egal to 1, 4, 8 or 16\n"); WARNING: Missing a blank line after declarations + char *s = b; + if ((*s == '0') && ((*(s+1) == 'x') || (*(s+1) == 'X'))) { WARNING: simple_strtoul is obsolete, use kstrtoul instead + loc.mask = simple_strtoul(b, &b, 0); WARNING: simple_strtol is obsolete, use kstrtol instead + loc.shift = simple_strtol(b, &b, 0); WARNING: simple_strtoul is obsolete, use kstrtoul instead + loc.bank = simple_strtoul(b, &b, 0); WARNING: simple_strtoul is obsolete, use kstrtoul instead + loc.addr = simple_strtoul(b, &b, 0); WARNING: simple_strtoul is obsolete, use kstrtoul instead + val = simple_strtoul(b, &b, 0); WARNING: quoted string split across lines + pr_warn("HWREG request: %s, %s, addr=0x%08X, mask=0x%X, shift=%d" + "value=0x%X\n", (write) ? "write" : "read", WARNING: Prefer [subsystem eg: netdev]_err([subsystem]dev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... + printk(KERN_ERR "sysfs_create_file failed %d\n", err); WARNING: Prefer [subsystem eg: netdev]_err([subsystem]dev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... + printk(KERN_ERR "request_threaded_irq failed %d, %lu\n", ERROR: code indent should use tabs where possible + err, user_val);$ WARNING: please, no spaces at the start of a line + err, user_val);$ WARNING: Missing a blank line after declarations + struct resource *res; + debug_bank = AB8500_MISC; ERROR: space required after that ',' (ctx:VxV) + sizeof(*dev_attr)*num_irqs,GFP_KERNEL); ^ WARNING: return of an errno should typically be -ve (return -ENXIO) + return ENXIO; WARNING: line over 80 characters + file = debugfs_create_file("register-bank", (S_IRUGO | S_IWUSR | S_IWGRP), WARNING: line over 80 characters + file = debugfs_create_file("register-address", (S_IRUGO | S_IWUSR | S_IWGRP), WARNING: line over 80 characters + file = debugfs_create_file("register-value", (S_IRUGO | S_IWUSR | S_IWGRP), WARNING: line over 80 characters + file = debugfs_create_file("irq-subscribe", (S_IRUGO | S_IWUSR | S_IWGRP), WARNING: line over 80 characters + file = debugfs_create_file("irq-unsubscribe", (S_IRUGO | S_IWUSR | S_IWGRP), WARNING: line over 80 characters + file = debugfs_create_file("all-modem-registers", (S_IRUGO | S_IWUSR | S_IWGRP), WARNING: line over 80 characters + file = debugfs_create_file("main_charger_v", (S_IRUGO | S_IWUSR | S_IWGRP), WARNING: line over 80 characters + file = debugfs_create_file("main_charger_c", (S_IRUGO | S_IWUSR | S_IWGRP), WARNING: line over 80 characters + file = debugfs_create_file("usb_charger_c", (S_IRUGO | S_IWUSR | S_IWGRP), WARNING: line over 80 characters + file = debugfs_create_file("xtal_temp", (S_IRUGO | S_IWUSR | S_IWGRP), WARNING: line over 80 characters + ab8500_gpadc_dir, &plf->dev, &ab8540_gpadc_xtal_temp_fops); WARNING: line over 80 characters + file = debugfs_create_file("vbattruemeas", (S_IRUGO | S_IWUSR | S_IWGRP), WARNING: line over 80 characters + file = debugfs_create_file("otp_calib", (S_IRUGO | S_IWUSR | S_IWGRP), WARNING: line over 80 characters + ab8500_gpadc_dir, &plf->dev, &ab8540_gpadc_otp_calib_fops); total: 2 errors, 44 warnings, 3230 lines checked Signed-off-by: Lee Jones --- drivers/mfd/ab8500-debugfs.c | 288 +++++++++++++++++++++++--------------- include/linux/mfd/abx500/ab8500.h | 1 + 2 files changed, 173 insertions(+), 116 deletions(-) (limited to 'include') diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index f7f271c16f2c..b2c7e3b1edfa 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -135,10 +135,10 @@ struct ab8500_prcmu_ranges { /* hwreg- "mask" and "shift" entries ressources */ struct hwreg_cfg { u32 bank; /* target bank */ - u32 addr; /* target address */ + unsigned long addr; /* target address */ uint fmt; /* format */ - uint mask; /* read/write mask, applied before any bit shift */ - int shift; /* bit shift (read:right shift, write:left shift */ + unsigned long mask; /* read/write mask, applied before any bit shift */ + long shift; /* bit shift (read:right shift, write:left shift */ }; /* fmt bit #0: 0=hexa, 1=dec */ #define REG_FMT_DEC(c) ((c)->fmt & 0x1) @@ -1304,16 +1304,17 @@ static int ab8500_registers_print(struct device *dev, u32 bank, } if (s) { - err = seq_printf(s, " [0x%02X/0x%02X]: 0x%02X\n", - bank, reg, value); + err = seq_printf(s, + " [0x%02X/0x%02X]: 0x%02X\n", + bank, reg, value); if (err < 0) { /* Error is not returned here since * the output is wanted in any case */ return 0; } } else { - printk(KERN_INFO" [0x%02X/0x%02X]: 0x%02X\n", - bank, reg, value); + dev_info(dev, " [0x%02X/0x%02X]: 0x%02X\n", + bank, reg, value); } } } @@ -1325,7 +1326,7 @@ static int ab8500_print_bank_registers(struct seq_file *s, void *p) struct device *dev = s->private; u32 bank = debug_bank; - seq_printf(s, AB8500_NAME_STRING " register values:\n"); + seq_puts(s, AB8500_NAME_STRING " register values:\n"); seq_printf(s, " bank 0x%02X:\n", bank); @@ -1351,7 +1352,7 @@ static int ab8500_print_all_banks(struct seq_file *s, void *p) struct device *dev = s->private; unsigned int i; - seq_printf(s, AB8500_NAME_STRING " register values:\n"); + seq_puts(s, AB8500_NAME_STRING " register values:\n"); for (i = 0; i < AB8500_NUM_BANKS; i++) { seq_printf(s, " bank 0x%02X:\n", i); @@ -1366,10 +1367,10 @@ void ab8500_dump_all_banks(struct device *dev) { unsigned int i; - printk(KERN_INFO"ab8500 register values:\n"); + dev_info(dev, "ab8500 register values:\n"); for (i = 1; i < AB8500_NUM_BANKS; i++) { - printk(KERN_INFO" bank 0x%02X:\n", i); + dev_info(dev, " bank 0x%02X:\n", i); ab8500_registers_print(dev, i, NULL); } } @@ -1383,8 +1384,6 @@ static struct ab8500_register_dump u8 value; } ab8500_complete_register_dump[DUMP_MAX_REGS]; -extern int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size); - /* This shall only be called upon kernel panic! */ void ab8500_dump_all_banks_to_mem(void) { @@ -1392,8 +1391,7 @@ void ab8500_dump_all_banks_to_mem(void) u8 bank; int err = 0; - pr_info("Saving all ABB registers at \"ab8500_complete_register_dump\" " - "for crash analyze.\n"); + pr_info("Saving all ABB registers for crash analysis.\n"); for (bank = 0; bank < AB8500_NUM_BANKS; bank++) { for (i = 0; i < debug_ranges[bank].num_ranges; i++) { @@ -1563,7 +1561,7 @@ static ssize_t ab8500_val_write(struct file *file, err = abx500_set_register_interruptible(dev, (u8)debug_bank, debug_address, (u8)user_val); if (err < 0) { - printk(KERN_ERR "abx500_set_reg failed %d, %d", err, __LINE__); + pr_err("abx500_set_reg failed %d, %d", err, __LINE__); return -EINVAL; } @@ -1595,7 +1593,7 @@ static int ab8500_interrupts_print(struct seq_file *s, void *p) { int line; - seq_printf(s, "name: number: number of: wake:\n"); + seq_puts(s, "name: number: number of: wake:\n"); for (line = 0; line < num_interrupt_lines; line++) { struct irq_desc *desc = irq_to_desc(line + irq_first); @@ -1721,7 +1719,8 @@ static int ab8500_print_modem_registers(struct seq_file *s, void *p) static int ab8500_modem_open(struct inode *inode, struct file *file) { - return single_open(file, ab8500_print_modem_registers, inode->i_private); + return single_open(file, ab8500_print_modem_registers, + inode->i_private); } static const struct file_operations ab8500_modem_fops = { @@ -1750,7 +1749,8 @@ static int ab8500_gpadc_bat_ctrl_print(struct seq_file *s, void *p) static int ab8500_gpadc_bat_ctrl_open(struct inode *inode, struct file *file) { - return single_open(file, ab8500_gpadc_bat_ctrl_print, inode->i_private); + return single_open(file, ab8500_gpadc_bat_ctrl_print, + inode->i_private); } static const struct file_operations ab8500_gpadc_bat_ctrl_fops = { @@ -1780,7 +1780,8 @@ static int ab8500_gpadc_btemp_ball_print(struct seq_file *s, void *p) static int ab8500_gpadc_btemp_ball_open(struct inode *inode, struct file *file) { - return single_open(file, ab8500_gpadc_btemp_ball_print, inode->i_private); + return single_open(file, ab8500_gpadc_btemp_ball_print, + inode->i_private); } static const struct file_operations ab8500_gpadc_btemp_ball_fops = { @@ -1961,7 +1962,8 @@ static int ab8500_gpadc_main_bat_v_print(struct seq_file *s, void *p) static int ab8500_gpadc_main_bat_v_open(struct inode *inode, struct file *file) { - return single_open(file, ab8500_gpadc_main_bat_v_print, inode->i_private); + return single_open(file, ab8500_gpadc_main_bat_v_print, + inode->i_private); } static const struct file_operations ab8500_gpadc_main_bat_v_fops = { @@ -2081,7 +2083,8 @@ static int ab8500_gpadc_bk_bat_v_print(struct seq_file *s, void *p) static int ab8500_gpadc_bk_bat_v_open(struct inode *inode, struct file *file) { - return single_open(file, ab8500_gpadc_bk_bat_v_print, inode->i_private); + return single_open(file, ab8500_gpadc_bk_bat_v_print, + inode->i_private); } static const struct file_operations ab8500_gpadc_bk_bat_v_fops = { @@ -2110,7 +2113,8 @@ static int ab8500_gpadc_die_temp_print(struct seq_file *s, void *p) static int ab8500_gpadc_die_temp_open(struct inode *inode, struct file *file) { - return single_open(file, ab8500_gpadc_die_temp_print, inode->i_private); + return single_open(file, ab8500_gpadc_die_temp_print, + inode->i_private); } static const struct file_operations ab8500_gpadc_die_temp_fops = { @@ -2189,8 +2193,9 @@ static int ab8540_gpadc_vbat_true_meas_print(struct seq_file *s, void *p) gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); vbat_true_meas_raw = ab8500_gpadc_read_raw(gpadc, VBAT_TRUE_MEAS, avg_sample, trig_edge, trig_timer, conv_type); - vbat_true_meas_convert = ab8500_gpadc_ad_to_voltage(gpadc, VBAT_TRUE_MEAS, - vbat_true_meas_raw); + vbat_true_meas_convert = + ab8500_gpadc_ad_to_voltage(gpadc, VBAT_TRUE_MEAS, + vbat_true_meas_raw); return seq_printf(s, "%d,0x%X\n", vbat_true_meas_convert, vbat_true_meas_raw); @@ -2284,7 +2289,8 @@ static const struct file_operations ab8540_gpadc_vbat_meas_and_ibat_fops = { .owner = THIS_MODULE, }; -static int ab8540_gpadc_vbat_true_meas_and_ibat_print(struct seq_file *s, void *p) +static int ab8540_gpadc_vbat_true_meas_and_ibat_print(struct seq_file *s, + void *p) { int vbat_true_meas_raw; int vbat_true_meas_convert; @@ -2313,7 +2319,8 @@ static int ab8540_gpadc_vbat_true_meas_and_ibat_open(struct inode *inode, inode->i_private); } -static const struct file_operations ab8540_gpadc_vbat_true_meas_and_ibat_fops = { +static const struct file_operations +ab8540_gpadc_vbat_true_meas_and_ibat_fops = { .open = ab8540_gpadc_vbat_true_meas_and_ibat_open, .read = seq_read, .llseek = seq_lseek, @@ -2367,14 +2374,15 @@ static int ab8540_gpadc_otp_cal_print(struct seq_file *s, void *p) ab8540_gpadc_get_otp(gpadc, &vmain_l, &vmain_h, &btemp_l, &btemp_h, &vbat_l, &vbat_h, &ibat_l, &ibat_h); return seq_printf(s, "VMAIN_L:0x%X\n" - "VMAIN_H:0x%X\n" - "BTEMP_L:0x%X\n" - "BTEMP_H:0x%X\n" - "VBAT_L:0x%X\n" - "VBAT_H:0x%X\n" - "IBAT_L:0x%X\n" - "IBAT_H:0x%X\n", - vmain_l, vmain_h, btemp_l, btemp_h, vbat_l, vbat_h, ibat_l, ibat_h); + "VMAIN_H:0x%X\n" + "BTEMP_L:0x%X\n" + "BTEMP_H:0x%X\n" + "VBAT_L:0x%X\n" + "VBAT_H:0x%X\n" + "IBAT_L:0x%X\n" + "IBAT_H:0x%X\n", + vmain_l, vmain_h, btemp_l, btemp_h, + vbat_l, vbat_h, ibat_l, ibat_h); } static int ab8540_gpadc_otp_cal_open(struct inode *inode, struct file *file) @@ -2418,8 +2426,8 @@ static ssize_t ab8500_gpadc_avg_sample_write(struct file *file, || (user_avg_sample == SAMPLE_16)) { avg_sample = (u8) user_avg_sample; } else { - dev_err(dev, "debugfs error input: " - "should be egal to 1, 4, 8 or 16\n"); + dev_err(dev, + "debugfs err input: should be egal to 1, 4, 8 or 16\n"); return -EINVAL; } @@ -2578,6 +2586,7 @@ static const struct file_operations ab8500_gpadc_conv_type_fops = { static int strval_len(char *b) { char *s = b; + if ((*s == '0') && ((*(s+1) == 'x') || (*(s+1) == 'X'))) { s += 2; for (; *s && (*s != ' ') && (*s != '\n'); s++) { @@ -2642,13 +2651,17 @@ static ssize_t hwreg_common_write(char *b, struct hwreg_cfg *cfg, b += (*(b+2) == ' ') ? 3 : 6; if (strval_len(b) == 0) return -EINVAL; - loc.mask = simple_strtoul(b, &b, 0); + ret = kstrtoul(b, 0, &loc.mask); + if (ret) + return ret; } else if ((!strncmp(b, "-s ", 3)) || (!strncmp(b, "-shift ", 7))) { b += (*(b+2) == ' ') ? 3 : 7; if (strval_len(b) == 0) return -EINVAL; - loc.shift = simple_strtol(b, &b, 0); + ret = kstrtol(b, 0, &loc.shift); + if (ret) + return ret; } else { return -EINVAL; } @@ -2656,29 +2669,36 @@ static ssize_t hwreg_common_write(char *b, struct hwreg_cfg *cfg, /* get arg BANK and ADDRESS */ if (strval_len(b) == 0) return -EINVAL; - loc.bank = simple_strtoul(b, &b, 0); + ret = kstrtouint(b, 0, &loc.bank); + if (ret) + return ret; while (*b == ' ') b++; if (strval_len(b) == 0) return -EINVAL; - loc.addr = simple_strtoul(b, &b, 0); + ret = kstrtoul(b, 0, &loc.addr); + if (ret) + return ret; if (write) { while (*b == ' ') b++; if (strval_len(b) == 0) return -EINVAL; - val = simple_strtoul(b, &b, 0); + ret = kstrtouint(b, 0, &val); + if (ret) + return ret; } /* args are ok, update target cfg (mainly for read) */ *cfg = loc; #ifdef ABB_HWREG_DEBUG - pr_warn("HWREG request: %s, %s, addr=0x%08X, mask=0x%X, shift=%d" - "value=0x%X\n", (write) ? "write" : "read", - REG_FMT_DEC(cfg) ? "decimal" : "hexa", - cfg->addr, cfg->mask, cfg->shift, val); + pr_warn("HWREG request: %s, %s,\n" + " addr=0x%08X, mask=0x%X, shift=%d" "value=0x%X\n", + (write) ? "write" : "read", + REG_FMT_DEC(cfg) ? "decimal" : "hexa", + cfg->addr, cfg->mask, cfg->shift, val); #endif if (!write) @@ -2814,7 +2834,7 @@ static ssize_t ab8500_subscribe_write(struct file *file, dev_attr[irq_index]->attr.mode = S_IRUGO; err = sysfs_create_file(&dev->kobj, &dev_attr[irq_index]->attr); if (err < 0) { - printk(KERN_ERR "sysfs_create_file failed %d\n", err); + pr_info("sysfs_create_file failed %d\n", err); return err; } @@ -2822,8 +2842,8 @@ static ssize_t ab8500_subscribe_write(struct file *file, IRQF_SHARED | IRQF_NO_SUSPEND, "ab8500-debug", &dev->kobj); if (err < 0) { - printk(KERN_ERR "request_threaded_irq failed %d, %lu\n", - err, user_val); + pr_info("request_threaded_irq failed %d, %lu\n", + err, user_val); sysfs_remove_file(&dev->kobj, &dev_attr[irq_index]->attr); return err; } @@ -2945,6 +2965,7 @@ static int ab8500_debug_probe(struct platform_device *plf) struct dentry *file; struct ab8500 *ab8500; struct resource *res; + debug_bank = AB8500_MISC; debug_address = AB8500_REV_REG & 0x00FF; @@ -2957,7 +2978,7 @@ static int ab8500_debug_probe(struct platform_device *plf) return -ENOMEM; dev_attr = devm_kzalloc(&plf->dev, - sizeof(*dev_attr)*num_irqs,GFP_KERNEL); + sizeof(*dev_attr)*num_irqs, GFP_KERNEL); if (!dev_attr) return -ENOMEM; @@ -2968,23 +2989,20 @@ static int ab8500_debug_probe(struct platform_device *plf) res = platform_get_resource_byname(plf, 0, "IRQ_AB8500"); if (!res) { - dev_err(&plf->dev, "AB8500 irq not found, err %d\n", - irq_first); - return ENXIO; + dev_err(&plf->dev, "AB8500 irq not found, err %d\n", irq_first); + return -ENXIO; } irq_ab8500 = res->start; irq_first = platform_get_irq_byname(plf, "IRQ_FIRST"); if (irq_first < 0) { - dev_err(&plf->dev, "First irq not found, err %d\n", - irq_first); + dev_err(&plf->dev, "First irq not found, err %d\n", irq_first); return irq_first; } irq_last = platform_get_irq_byname(plf, "IRQ_LAST"); if (irq_last < 0) { - dev_err(&plf->dev, "Last irq not found, err %d\n", - irq_last); + dev_err(&plf->dev, "Last irq not found, err %d\n", irq_last); return irq_last; } @@ -2993,37 +3011,41 @@ static int ab8500_debug_probe(struct platform_device *plf) goto err; ab8500_gpadc_dir = debugfs_create_dir(AB8500_ADC_NAME_STRING, - ab8500_dir); + ab8500_dir); if (!ab8500_gpadc_dir) goto err; - file = debugfs_create_file("all-bank-registers", S_IRUGO, - ab8500_dir, &plf->dev, &ab8500_registers_fops); + file = debugfs_create_file("all-bank-registers", S_IRUGO, ab8500_dir, + &plf->dev, &ab8500_registers_fops); if (!file) goto err; - file = debugfs_create_file("all-banks", S_IRUGO, - ab8500_dir, &plf->dev, &ab8500_all_banks_fops); + file = debugfs_create_file("all-banks", S_IRUGO, ab8500_dir, + &plf->dev, &ab8500_all_banks_fops); if (!file) goto err; - file = debugfs_create_file("register-bank", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_dir, &plf->dev, &ab8500_bank_fops); + file = debugfs_create_file("register-bank", + (S_IRUGO | S_IWUSR | S_IWGRP), + ab8500_dir, &plf->dev, &ab8500_bank_fops); if (!file) goto err; - file = debugfs_create_file("register-address", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_dir, &plf->dev, &ab8500_address_fops); + file = debugfs_create_file("register-address", + (S_IRUGO | S_IWUSR | S_IWGRP), + ab8500_dir, &plf->dev, &ab8500_address_fops); if (!file) goto err; - file = debugfs_create_file("register-value", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_dir, &plf->dev, &ab8500_val_fops); + file = debugfs_create_file("register-value", + (S_IRUGO | S_IWUSR | S_IWGRP), + ab8500_dir, &plf->dev, &ab8500_val_fops); if (!file) goto err; - file = debugfs_create_file("irq-subscribe", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_dir, &plf->dev, &ab8500_subscribe_fops); + file = debugfs_create_file("irq-subscribe", + (S_IRUGO | S_IWUSR | S_IWGRP), ab8500_dir, + &plf->dev, &ab8500_subscribe_fops); if (!file) goto err; @@ -3041,150 +3063,184 @@ static int ab8500_debug_probe(struct platform_device *plf) num_interrupt_lines = AB8540_NR_IRQS; } - file = debugfs_create_file("interrupts", (S_IRUGO), - ab8500_dir, &plf->dev, &ab8500_interrupts_fops); + file = debugfs_create_file("interrupts", (S_IRUGO), ab8500_dir, + &plf->dev, &ab8500_interrupts_fops); if (!file) goto err; - file = debugfs_create_file("irq-unsubscribe", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_dir, &plf->dev, &ab8500_unsubscribe_fops); + file = debugfs_create_file("irq-unsubscribe", + (S_IRUGO | S_IWUSR | S_IWGRP), ab8500_dir, + &plf->dev, &ab8500_unsubscribe_fops); if (!file) goto err; file = debugfs_create_file("hwreg", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_dir, &plf->dev, &ab8500_hwreg_fops); + ab8500_dir, &plf->dev, &ab8500_hwreg_fops); if (!file) goto err; - file = debugfs_create_file("all-modem-registers", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_dir, &plf->dev, &ab8500_modem_fops); + file = debugfs_create_file("all-modem-registers", + (S_IRUGO | S_IWUSR | S_IWGRP), + ab8500_dir, &plf->dev, &ab8500_modem_fops); if (!file) goto err; file = debugfs_create_file("bat_ctrl", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_bat_ctrl_fops); + ab8500_gpadc_dir, &plf->dev, + &ab8500_gpadc_bat_ctrl_fops); if (!file) goto err; file = debugfs_create_file("btemp_ball", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_btemp_ball_fops); + ab8500_gpadc_dir, + &plf->dev, &ab8500_gpadc_btemp_ball_fops); if (!file) goto err; - file = debugfs_create_file("main_charger_v", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_main_charger_v_fops); + file = debugfs_create_file("main_charger_v", + (S_IRUGO | S_IWUSR | S_IWGRP), + ab8500_gpadc_dir, &plf->dev, + &ab8500_gpadc_main_charger_v_fops); if (!file) goto err; - file = debugfs_create_file("acc_detect1", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_acc_detect1_fops); + file = debugfs_create_file("acc_detect1", + (S_IRUGO | S_IWUSR | S_IWGRP), + ab8500_gpadc_dir, &plf->dev, + &ab8500_gpadc_acc_detect1_fops); if (!file) goto err; - file = debugfs_create_file("acc_detect2", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_acc_detect2_fops); + file = debugfs_create_file("acc_detect2", + (S_IRUGO | S_IWUSR | S_IWGRP), + ab8500_gpadc_dir, &plf->dev, + &ab8500_gpadc_acc_detect2_fops); if (!file) goto err; file = debugfs_create_file("adc_aux1", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_aux1_fops); + ab8500_gpadc_dir, &plf->dev, + &ab8500_gpadc_aux1_fops); if (!file) goto err; file = debugfs_create_file("adc_aux2", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_aux2_fops); + ab8500_gpadc_dir, &plf->dev, + &ab8500_gpadc_aux2_fops); if (!file) goto err; file = debugfs_create_file("main_bat_v", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_main_bat_v_fops); + ab8500_gpadc_dir, &plf->dev, + &ab8500_gpadc_main_bat_v_fops); if (!file) goto err; file = debugfs_create_file("vbus_v", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_vbus_v_fops); + ab8500_gpadc_dir, &plf->dev, + &ab8500_gpadc_vbus_v_fops); if (!file) goto err; - file = debugfs_create_file("main_charger_c", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_main_charger_c_fops); + file = debugfs_create_file("main_charger_c", + (S_IRUGO | S_IWUSR | S_IWGRP), + ab8500_gpadc_dir, &plf->dev, + &ab8500_gpadc_main_charger_c_fops); if (!file) goto err; - file = debugfs_create_file("usb_charger_c", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_usb_charger_c_fops); + file = debugfs_create_file("usb_charger_c", + (S_IRUGO | S_IWUSR | S_IWGRP), + ab8500_gpadc_dir, + &plf->dev, &ab8500_gpadc_usb_charger_c_fops); if (!file) goto err; file = debugfs_create_file("bk_bat_v", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_bk_bat_v_fops); + ab8500_gpadc_dir, &plf->dev, + &ab8500_gpadc_bk_bat_v_fops); if (!file) goto err; file = debugfs_create_file("die_temp", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_die_temp_fops); + ab8500_gpadc_dir, &plf->dev, + &ab8500_gpadc_die_temp_fops); if (!file) goto err; file = debugfs_create_file("usb_id", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_usb_id_fops); + ab8500_gpadc_dir, &plf->dev, + &ab8500_gpadc_usb_id_fops); if (!file) goto err; if (is_ab8540(ab8500)) { - file = debugfs_create_file("xtal_temp", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, &ab8540_gpadc_xtal_temp_fops); + file = debugfs_create_file("xtal_temp", + (S_IRUGO | S_IWUSR | S_IWGRP), + ab8500_gpadc_dir, &plf->dev, + &ab8540_gpadc_xtal_temp_fops); if (!file) goto err; - file = debugfs_create_file("vbattruemeas", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, - &ab8540_gpadc_vbat_true_meas_fops); + file = debugfs_create_file("vbattruemeas", + (S_IRUGO | S_IWUSR | S_IWGRP), + ab8500_gpadc_dir, &plf->dev, + &ab8540_gpadc_vbat_true_meas_fops); if (!file) goto err; file = debugfs_create_file("batctrl_and_ibat", - (S_IRUGO | S_IWUGO), ab8500_gpadc_dir, - &plf->dev, &ab8540_gpadc_bat_ctrl_and_ibat_fops); + (S_IRUGO | S_IWUGO), + ab8500_gpadc_dir, + &plf->dev, + &ab8540_gpadc_bat_ctrl_and_ibat_fops); if (!file) goto err; file = debugfs_create_file("vbatmeas_and_ibat", - (S_IRUGO | S_IWUGO), ab8500_gpadc_dir, - &plf->dev, - &ab8540_gpadc_vbat_meas_and_ibat_fops); + (S_IRUGO | S_IWUGO), + ab8500_gpadc_dir, &plf->dev, + &ab8540_gpadc_vbat_meas_and_ibat_fops); if (!file) goto err; file = debugfs_create_file("vbattruemeas_and_ibat", - (S_IRUGO | S_IWUGO), ab8500_gpadc_dir, - &plf->dev, - &ab8540_gpadc_vbat_true_meas_and_ibat_fops); + (S_IRUGO | S_IWUGO), + ab8500_gpadc_dir, + &plf->dev, + &ab8540_gpadc_vbat_true_meas_and_ibat_fops); if (!file) goto err; file = debugfs_create_file("battemp_and_ibat", - (S_IRUGO | S_IWUGO), ab8500_gpadc_dir, + (S_IRUGO | S_IWUGO), + ab8500_gpadc_dir, &plf->dev, &ab8540_gpadc_bat_temp_and_ibat_fops); if (!file) goto err; - file = debugfs_create_file("otp_calib", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, &ab8540_gpadc_otp_calib_fops); + file = debugfs_create_file("otp_calib", + (S_IRUGO | S_IWUSR | S_IWGRP), + ab8500_gpadc_dir, + &plf->dev, &ab8540_gpadc_otp_calib_fops); if (!file) goto err; } file = debugfs_create_file("avg_sample", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_avg_sample_fops); + ab8500_gpadc_dir, &plf->dev, + &ab8500_gpadc_avg_sample_fops); if (!file) goto err; file = debugfs_create_file("trig_edge", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_trig_edge_fops); + ab8500_gpadc_dir, &plf->dev, + &ab8500_gpadc_trig_edge_fops); if (!file) goto err; file = debugfs_create_file("trig_timer", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_trig_timer_fops); + ab8500_gpadc_dir, &plf->dev, + &ab8500_gpadc_trig_timer_fops); if (!file) goto err; file = debugfs_create_file("conv_type", (S_IRUGO | S_IWUSR | S_IWGRP), - ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_conv_type_fops); + ab8500_gpadc_dir, &plf->dev, + &ab8500_gpadc_conv_type_fops); if (!file) goto err; diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h index 4e7fe7417fc9..9475fee2bfc5 100644 --- a/include/linux/mfd/abx500/ab8500.h +++ b/include/linux/mfd/abx500/ab8500.h @@ -505,6 +505,7 @@ static inline int is_ab9540_2p0_or_earlier(struct ab8500 *ab) void ab8500_override_turn_on_stat(u8 mask, u8 set); #ifdef CONFIG_AB8500_DEBUG +extern int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size); void ab8500_dump_all_banks(struct device *dev); void ab8500_debug_register_interrupt(int line); #else -- cgit v1.2.3 From 6f1c1e71d933f58a6248f1681aededdd407f32a8 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Fri, 4 Jul 2014 22:24:04 +0200 Subject: mfd: max77686: Convert to use regmap_irq By using the generic IRQ support in the Register map API, it is possible to get rid max77686-irq.c and simplify the code. Suggested-by: Krzysztof Kozlowski Signed-off-by: Javier Martinez Canillas Reviewed-by: Doug Anderson Tested-by: Doug Anderson Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 1 + drivers/mfd/Makefile | 2 +- drivers/mfd/max77686-irq.c | 319 ----------------------------------- drivers/mfd/max77686.c | 97 ++++++++++- drivers/rtc/rtc-max77686.c | 27 +-- include/linux/mfd/max77686-private.h | 31 +++- include/linux/mfd/max77686.h | 2 - 7 files changed, 123 insertions(+), 356 deletions(-) delete mode 100644 drivers/mfd/max77686-irq.c (limited to 'include') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index b8d9ca0b68e2..30102042dcaf 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -384,6 +384,7 @@ config MFD_MAX77686 depends on I2C=y select MFD_CORE select REGMAP_I2C + select REGMAP_IRQ select IRQ_DOMAIN help Say yes here to add support for Maxim Semiconductor MAX77686. diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 4e2bc255b8b0..f00148782d9b 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -115,7 +115,7 @@ da9063-objs := da9063-core.o da9063-irq.o da9063-i2c.o obj-$(CONFIG_MFD_DA9063) += da9063.o obj-$(CONFIG_MFD_MAX14577) += max14577.o -obj-$(CONFIG_MFD_MAX77686) += max77686.o max77686-irq.o +obj-$(CONFIG_MFD_MAX77686) += max77686.o obj-$(CONFIG_MFD_MAX77693) += max77693.o obj-$(CONFIG_MFD_MAX8907) += max8907.o max8925-objs := max8925-core.o max8925-i2c.o diff --git a/drivers/mfd/max77686-irq.c b/drivers/mfd/max77686-irq.c deleted file mode 100644 index cdc3280e2ec7..000000000000 --- a/drivers/mfd/max77686-irq.c +++ /dev/null @@ -1,319 +0,0 @@ -/* - * 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->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_GROUP_NR; i++) - irq_reg[i] &= ~max77686->irq_masks_cur[i]; - - 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); - - if (max77686->irq_gpio && !max77686->irq) { - 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); - } - } - - if (!max77686->irq) { - dev_err(max77686->dev, "irq is not specified\n"); - return -ENODEV; - } - - /* 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 index ce869acf27ae..3cb41d02cd3d 100644 --- a/drivers/mfd/max77686.c +++ b/drivers/mfd/max77686.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include #include @@ -46,6 +48,54 @@ static struct regmap_config max77686_regmap_config = { .val_bits = 8, }; +static struct regmap_config max77686_rtc_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static const struct regmap_irq max77686_irqs[] = { + /* INT1 interrupts */ + { .reg_offset = 0, .mask = MAX77686_INT1_PWRONF_MSK, }, + { .reg_offset = 0, .mask = MAX77686_INT1_PWRONR_MSK, }, + { .reg_offset = 0, .mask = MAX77686_INT1_JIGONBF_MSK, }, + { .reg_offset = 0, .mask = MAX77686_INT1_JIGONBR_MSK, }, + { .reg_offset = 0, .mask = MAX77686_INT1_ACOKBF_MSK, }, + { .reg_offset = 0, .mask = MAX77686_INT1_ACOKBR_MSK, }, + { .reg_offset = 0, .mask = MAX77686_INT1_ONKEY1S_MSK, }, + { .reg_offset = 0, .mask = MAX77686_INT1_MRSTB_MSK, }, + /* INT2 interrupts */ + { .reg_offset = 1, .mask = MAX77686_INT2_140C_MSK, }, + { .reg_offset = 1, .mask = MAX77686_INT2_120C_MSK, }, +}; + +static const struct regmap_irq_chip max77686_irq_chip = { + .name = "max77686-pmic", + .status_base = MAX77686_REG_INT1, + .mask_base = MAX77686_REG_INT1MSK, + .num_regs = 2, + .irqs = max77686_irqs, + .num_irqs = ARRAY_SIZE(max77686_irqs), +}; + +static const struct regmap_irq max77686_rtc_irqs[] = { + /* RTC interrupts */ + { .reg_offset = 0, .mask = MAX77686_RTCINT_RTC60S_MSK, }, + { .reg_offset = 0, .mask = MAX77686_RTCINT_RTCA1_MSK, }, + { .reg_offset = 0, .mask = MAX77686_RTCINT_RTCA2_MSK, }, + { .reg_offset = 0, .mask = MAX77686_RTCINT_SMPL_MSK, }, + { .reg_offset = 0, .mask = MAX77686_RTCINT_RTC1S_MSK, }, + { .reg_offset = 0, .mask = MAX77686_RTCINT_WTSR_MSK, }, +}; + +static const struct regmap_irq_chip max77686_rtc_irq_chip = { + .name = "max77686-rtc", + .status_base = MAX77686_RTC_INT, + .mask_base = MAX77686_RTC_INTM, + .num_regs = 1, + .irqs = max77686_rtc_irqs, + .num_irqs = ARRAY_SIZE(max77686_rtc_irqs), +}; + #ifdef CONFIG_OF static const struct of_device_id max77686_pmic_dt_match[] = { {.compatible = "maxim,max77686", .data = NULL}, @@ -101,7 +151,6 @@ static int max77686_i2c_probe(struct i2c_client *i2c, max77686->type = id->driver_data; max77686->wakeup = pdata->wakeup; - max77686->irq_gpio = pdata->irq_gpio; max77686->irq = i2c->irq; max77686->regmap = devm_regmap_init_i2c(i2c, &max77686_regmap_config); @@ -117,8 +166,7 @@ static int max77686_i2c_probe(struct i2c_client *i2c, dev_err(max77686->dev, "device not found on this channel (this is not an error)\n"); return -ENODEV; - } else - dev_info(max77686->dev, "device found\n"); + } max77686->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC); if (!max77686->rtc) { @@ -127,15 +175,48 @@ static int max77686_i2c_probe(struct i2c_client *i2c, } i2c_set_clientdata(max77686->rtc, max77686); - max77686_irq_init(max77686); + max77686->rtc_regmap = devm_regmap_init_i2c(max77686->rtc, + &max77686_rtc_regmap_config); + if (IS_ERR(max77686->rtc_regmap)) { + ret = PTR_ERR(max77686->rtc_regmap); + dev_err(max77686->dev, "failed to allocate RTC regmap: %d\n", + ret); + goto err_unregister_i2c; + } + + ret = regmap_add_irq_chip(max77686->regmap, max77686->irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT | + IRQF_SHARED, 0, &max77686_irq_chip, + &max77686->irq_data); + if (ret != 0) { + dev_err(&i2c->dev, "failed to add PMIC irq chip: %d\n", ret); + goto err_unregister_i2c; + } + ret = regmap_add_irq_chip(max77686->rtc_regmap, max77686->irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT | + IRQF_SHARED, 0, &max77686_rtc_irq_chip, + &max77686->rtc_irq_data); + if (ret != 0) { + dev_err(&i2c->dev, "failed to add RTC irq chip: %d\n", ret); + goto err_del_irqc; + } ret = mfd_add_devices(max77686->dev, -1, max77686_devs, ARRAY_SIZE(max77686_devs), NULL, 0, NULL); if (ret < 0) { - mfd_remove_devices(max77686->dev); - i2c_unregister_device(max77686->rtc); + dev_err(&i2c->dev, "failed to add MFD devices: %d\n", ret); + goto err_del_rtc_irqc; } + return 0; + +err_del_rtc_irqc: + regmap_del_irq_chip(max77686->irq, max77686->rtc_irq_data); +err_del_irqc: + regmap_del_irq_chip(max77686->irq, max77686->irq_data); +err_unregister_i2c: + i2c_unregister_device(max77686->rtc); + return ret; } @@ -144,6 +225,10 @@ static int max77686_i2c_remove(struct i2c_client *i2c) struct max77686_dev *max77686 = i2c_get_clientdata(i2c); mfd_remove_devices(max77686->dev); + + regmap_del_irq_chip(max77686->irq, max77686->rtc_irq_data); + regmap_del_irq_chip(max77686->irq, max77686->irq_data); + i2c_unregister_device(max77686->rtc); return 0; diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c index 9efe118a28ba..d20a7f0786eb 100644 --- a/drivers/rtc/rtc-max77686.c +++ b/drivers/rtc/rtc-max77686.c @@ -492,16 +492,11 @@ static int max77686_rtc_init_reg(struct max77686_rtc_info *info) return ret; } -static struct regmap_config max77686_rtc_regmap_config = { - .reg_bits = 8, - .val_bits = 8, -}; - static int max77686_rtc_probe(struct platform_device *pdev) { struct max77686_dev *max77686 = dev_get_drvdata(pdev->dev.parent); struct max77686_rtc_info *info; - int ret, virq; + int ret; dev_info(&pdev->dev, "%s\n", __func__); @@ -514,14 +509,7 @@ static int max77686_rtc_probe(struct platform_device *pdev) info->dev = &pdev->dev; info->max77686 = max77686; info->rtc = max77686->rtc; - info->max77686->rtc_regmap = devm_regmap_init_i2c(info->max77686->rtc, - &max77686_rtc_regmap_config); - if (IS_ERR(info->max77686->rtc_regmap)) { - ret = PTR_ERR(info->max77686->rtc_regmap); - dev_err(info->max77686->dev, "Failed to allocate register map: %d\n", - ret); - return ret; - } + platform_set_drvdata(pdev, info); ret = max77686_rtc_init_reg(info); @@ -550,15 +538,16 @@ static int max77686_rtc_probe(struct platform_device *pdev) ret = -EINVAL; goto err_rtc; } - virq = irq_create_mapping(max77686->irq_domain, MAX77686_RTCIRQ_RTCA1); - if (!virq) { + + info->virq = regmap_irq_get_virq(max77686->rtc_irq_data, + MAX77686_RTCIRQ_RTCA1); + if (!info->virq) { ret = -ENXIO; goto err_rtc; } - info->virq = virq; - ret = devm_request_threaded_irq(&pdev->dev, virq, NULL, - max77686_rtc_alarm_irq, 0, "rtc-alarm0", info); + ret = devm_request_threaded_irq(&pdev->dev, info->virq, NULL, + max77686_rtc_alarm_irq, 0, "rtc-alarm1", info); if (ret < 0) dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", info->virq, ret); diff --git a/include/linux/mfd/max77686-private.h b/include/linux/mfd/max77686-private.h index 8c75a9c8dfab..8e177806cba1 100644 --- a/include/linux/mfd/max77686-private.h +++ b/include/linux/mfd/max77686-private.h @@ -181,9 +181,6 @@ enum max77686_rtc_reg { MAX77686_ALARM2_DATE = 0x1B, }; -#define MAX77686_IRQSRC_PMIC (0) -#define MAX77686_IRQSRC_RTC (1 << 0) - enum max77686_irq_source { PMIC_INT1 = 0, PMIC_INT2, @@ -205,16 +202,33 @@ enum max77686_irq { MAX77686_PMICIRQ_140C, MAX77686_PMICIRQ_120C, - MAX77686_RTCIRQ_RTC60S, + MAX77686_RTCIRQ_RTC60S = 0, MAX77686_RTCIRQ_RTCA1, MAX77686_RTCIRQ_RTCA2, MAX77686_RTCIRQ_SMPL, MAX77686_RTCIRQ_RTC1S, MAX77686_RTCIRQ_WTSR, - - MAX77686_IRQ_NR, }; +#define MAX77686_INT1_PWRONF_MSK BIT(0) +#define MAX77686_INT1_PWRONR_MSK BIT(1) +#define MAX77686_INT1_JIGONBF_MSK BIT(2) +#define MAX77686_INT1_JIGONBR_MSK BIT(3) +#define MAX77686_INT1_ACOKBF_MSK BIT(4) +#define MAX77686_INT1_ACOKBR_MSK BIT(5) +#define MAX77686_INT1_ONKEY1S_MSK BIT(6) +#define MAX77686_INT1_MRSTB_MSK BIT(7) + +#define MAX77686_INT2_140C_MSK BIT(0) +#define MAX77686_INT2_120C_MSK BIT(1) + +#define MAX77686_RTCINT_RTC60S_MSK BIT(0) +#define MAX77686_RTCINT_RTCA1_MSK BIT(1) +#define MAX77686_RTCINT_RTCA2_MSK BIT(2) +#define MAX77686_RTCINT_SMPL_MSK BIT(3) +#define MAX77686_RTCINT_RTC1S_MSK BIT(4) +#define MAX77686_RTCINT_WTSR_MSK BIT(5) + struct max77686_dev { struct device *dev; struct i2c_client *i2c; /* 0xcc / PMIC, Battery Control, and FLASH */ @@ -224,11 +238,10 @@ struct max77686_dev { struct regmap *regmap; /* regmap for mfd */ struct regmap *rtc_regmap; /* regmap for rtc */ - - struct irq_domain *irq_domain; + struct regmap_irq_chip_data *irq_data; + struct regmap_irq_chip_data *rtc_irq_data; int irq; - int irq_gpio; bool wakeup; struct mutex irqlock; int irq_masks_cur[MAX77686_IRQ_GROUP_NR]; diff --git a/include/linux/mfd/max77686.h b/include/linux/mfd/max77686.h index 46c0f320ed76..4cbcc13e8a2a 100644 --- a/include/linux/mfd/max77686.h +++ b/include/linux/mfd/max77686.h @@ -89,8 +89,6 @@ struct max77686_opmode_data { }; struct max77686_platform_data { - /* IRQ */ - int irq_gpio; int ono; int wakeup; -- cgit v1.2.3 From ba4e9a61ad54c438d4c7b655e94e31f23a6fe13f Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 20 Jul 2014 17:27:09 -0700 Subject: Input: uinput - add UI_GET_VERSION ioctl This ioctl is the counterpart to EVIOCGVERSION and returns the uinput-version the kernel was compiled with. Reviewed-by: Peter Hutterer Signed-off-by: David Herrmann Signed-off-by: Dmitry Torokhov --- drivers/input/misc/uinput.c | 6 ++++++ include/uapi/linux/uinput.h | 9 +++++++++ 2 files changed, 15 insertions(+) (limited to 'include') diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 883f045f37df..421e29e4cd81 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -723,6 +723,12 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd, } switch (cmd) { + case UI_GET_VERSION: + if (put_user(UINPUT_VERSION, + (unsigned int __user *)p)) + retval = -EFAULT; + goto out; + case UI_DEV_CREATE: retval = uinput_create_device(udev); goto out; diff --git a/include/uapi/linux/uinput.h b/include/uapi/linux/uinput.h index 0389b489bbba..baeab83deb64 100644 --- a/include/uapi/linux/uinput.h +++ b/include/uapi/linux/uinput.h @@ -84,6 +84,15 @@ struct uinput_ff_erase { */ #define UI_GET_SYSNAME(len) _IOC(_IOC_READ, UINPUT_IOCTL_BASE, 300, len) +/** + * UI_GET_VERSION - Return version of uinput protocol + * + * This writes uinput protocol version implemented by the kernel into + * the integer pointed to by the ioctl argument. The protocol version + * is hard-coded in the kernel and is independent of the uinput device. + */ +#define UI_GET_VERSION _IOR(UINPUT_IOCTL_BASE, 301, unsigned int) + /* * To write a force-feedback-capable driver, the upload_effect * and erase_effect callbacks in input_dev must be implemented. -- cgit v1.2.3 From 605b38403b710ca01a242448241bc45c722b341b Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 12 Jun 2014 13:09:39 -0300 Subject: [media] v4l: ctrls: Move control lock/unlock above the control access functions The v4l2_ctrl_{,un}lock will be needed elsewhere. Define them before the functions that perform operations on controls. Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-ctrls.h | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index c630345e9fd2..4546233a9a3d 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -367,6 +367,24 @@ int v4l2_ctrl_handler_init_class(struct v4l2_ctrl_handler *hdl, */ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl); +/** v4l2_ctrl_lock() - Helper function to lock the handler + * associated with the control. + * @ctrl: The control to lock. + */ +static inline void v4l2_ctrl_lock(struct v4l2_ctrl *ctrl) +{ + mutex_lock(ctrl->handler->lock); +} + +/** v4l2_ctrl_unlock() - Helper function to unlock the handler + * associated with the control. + * @ctrl: The control to unlock. + */ +static inline void v4l2_ctrl_unlock(struct v4l2_ctrl *ctrl) +{ + mutex_unlock(ctrl->handler->lock); +} + /** v4l2_ctrl_handler_setup() - Call the s_ctrl op for all controls belonging * to the handler to initialize the hardware to the current control values. * @hdl: The control handler. @@ -622,24 +640,6 @@ void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed); int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, s64 min, s64 max, u64 step, s64 def); -/** v4l2_ctrl_lock() - Helper function to lock the handler - * associated with the control. - * @ctrl: The control to lock. - */ -static inline void v4l2_ctrl_lock(struct v4l2_ctrl *ctrl) -{ - mutex_lock(ctrl->handler->lock); -} - -/** v4l2_ctrl_unlock() - Helper function to unlock the handler - * associated with the control. - * @ctrl: The control to unlock. - */ -static inline void v4l2_ctrl_unlock(struct v4l2_ctrl *ctrl) -{ - mutex_unlock(ctrl->handler->lock); -} - /** v4l2_ctrl_notify() - Function to set a notify callback for a control. * @ctrl: The control. * @notify: The callback function. -- cgit v1.2.3 From 5a573925159aeec1dd159627d849dc6c66000faf Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 12 Jun 2014 13:09:40 -0300 Subject: [media] v4l: ctrls: Provide an unlocked variant of v4l2_ctrl_modify_range() Drivers may use the v4l2_ctrl_modify_range() internally as part of other operations that need to be both serialised using a driver's lock which can also be used to serialise access to the control handler. Provide an unlocked version of the function, __v4l2_ctrl_modify_range() which then may be used by drivers for the purpose. Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 8 ++++---- include/media/v4l2-ctrls.h | 18 ++++++++++++++++-- 2 files changed, 20 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 8552c832074a..9cd978334b89 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -3213,12 +3213,14 @@ void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void } EXPORT_SYMBOL(v4l2_ctrl_notify); -int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, +int __v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, s64 min, s64 max, u64 step, s64 def) { int ret; struct v4l2_ext_control c; + lockdep_assert_held(ctrl->handler->lock); + switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER: case V4L2_CTRL_TYPE_INTEGER64: @@ -3237,7 +3239,6 @@ int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, default: return -EINVAL; } - v4l2_ctrl_lock(ctrl); ctrl->minimum = min; ctrl->maximum = max; ctrl->step = step; @@ -3249,10 +3250,9 @@ int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, ret = set_ctrl(NULL, ctrl, &c, V4L2_EVENT_CTRL_CH_RANGE); else send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_RANGE); - v4l2_ctrl_unlock(ctrl); return ret; } -EXPORT_SYMBOL(v4l2_ctrl_modify_range); +EXPORT_SYMBOL(__v4l2_ctrl_modify_range); static int v4l2_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems) { diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 4546233a9a3d..5d1a30ca29af 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -620,6 +620,11 @@ void v4l2_ctrl_activate(struct v4l2_ctrl *ctrl, bool active); */ void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed); + +/** __v4l2_ctrl_modify_range() - Unlocked variant of v4l2_ctrl_modify_range() */ +int __v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, + s64 min, s64 max, u64 step, s64 def); + /** v4l2_ctrl_modify_range() - Update the range of a control. * @ctrl: The control to update. * @min: The control's minimum value. @@ -637,8 +642,17 @@ void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed); * This function assumes that the control handler is not locked and will * take the lock itself. */ -int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, - s64 min, s64 max, u64 step, s64 def); +static inline int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, + s64 min, s64 max, u64 step, s64 def) +{ + int rval; + + v4l2_ctrl_lock(ctrl); + rval = __v4l2_ctrl_modify_range(ctrl, min, max, step, def); + v4l2_ctrl_unlock(ctrl); + + return rval; +} /** v4l2_ctrl_notify() - Function to set a notify callback for a control. * @ctrl: The control. -- cgit v1.2.3 From 0c4348ada001181637b8f73482242166ba2fb56e Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 12 Jun 2014 13:09:42 -0300 Subject: [media] v4l: ctrls: Unlocked variants of v4l2_ctrl_s_ctrl{,_int64}() Implement unlocked variants of v4l2_ctrl_s_ctrl() and v4l2_ctrl_s_ctrl_int64(). As drivers need to set controls as they access driver internal state elsewhere than in the control framework unlocked variants of these functions become handy. Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 26 ++++++++++++++++++++------ include/media/v4l2-ctrls.h | 27 +++++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 9cd978334b89..1acc7aa9d7e2 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -3175,27 +3175,41 @@ int v4l2_subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control) } EXPORT_SYMBOL(v4l2_subdev_s_ctrl); -int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val) +int __v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val) { struct v4l2_ext_control c; + int rval; + + lockdep_assert_held(ctrl->handler->lock); /* It's a driver bug if this happens. */ WARN_ON(!ctrl->is_int); c.value = val; - return set_ctrl_lock(NULL, ctrl, &c); + rval = set_ctrl(NULL, ctrl, &c, 0); + if (!rval) + cur_to_user(&c, ctrl); + + return rval; } -EXPORT_SYMBOL(v4l2_ctrl_s_ctrl); +EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl); -int v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val) +int __v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val) { struct v4l2_ext_control c; + int rval; + + lockdep_assert_held(ctrl->handler->lock); /* It's a driver bug if this happens. */ WARN_ON(ctrl->is_ptr || ctrl->type != V4L2_CTRL_TYPE_INTEGER64); c.value64 = val; - return set_ctrl_lock(NULL, ctrl, &c); + rval = set_ctrl(NULL, ctrl, &c, 0); + if (!rval) + cur_to_user(&c, ctrl); + + return rval; } -EXPORT_SYMBOL(v4l2_ctrl_s_ctrl_int64); +EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_int64); void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void *priv) { diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 5d1a30ca29af..8c4edd69fa4b 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -679,6 +679,8 @@ void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void */ s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl); +/** __v4l2_ctrl_s_ctrl() - Unlocked variant of v4l2_ctrl_s_ctrl(). */ +int __v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val); /** v4l2_ctrl_s_ctrl() - Helper function to set the control's value from within a driver. * @ctrl: The control. * @val: The new value. @@ -689,7 +691,16 @@ s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl); * * This function is for integer type controls only. */ -int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val); +static inline int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val) +{ + int rval; + + v4l2_ctrl_lock(ctrl); + rval = __v4l2_ctrl_s_ctrl(ctrl, val); + v4l2_ctrl_unlock(ctrl); + + return rval; +} /** v4l2_ctrl_g_ctrl_int64() - Helper function to get a 64-bit control's value from within a driver. * @ctrl: The control. @@ -702,6 +713,9 @@ int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val); */ s64 v4l2_ctrl_g_ctrl_int64(struct v4l2_ctrl *ctrl); +/** __v4l2_ctrl_s_ctrl_int64() - Unlocked variant of v4l2_ctrl_s_ctrl_int64(). */ +int __v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val); + /** v4l2_ctrl_s_ctrl_int64() - Helper function to set a 64-bit control's value from within a driver. * @ctrl: The control. * @val: The new value. @@ -712,7 +726,16 @@ s64 v4l2_ctrl_g_ctrl_int64(struct v4l2_ctrl *ctrl); * * This function is for 64-bit integer type controls only. */ -int v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val); +static inline int v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val) +{ + int rval; + + v4l2_ctrl_lock(ctrl); + rval = __v4l2_ctrl_s_ctrl_int64(ctrl, val); + v4l2_ctrl_unlock(ctrl); + + return rval; +} /* Internal helper functions that deal with control events. */ extern const struct v4l2_subscribed_event_ops v4l2_ctrl_sub_ev_ops; -- cgit v1.2.3 From 9e9e3927ea76c95873f153c19b7c0d7f319dabc4 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 27 Jun 2014 17:42:18 -0300 Subject: [media] v4l: uapi: add SDR format RU12LE V4L2_SDR_FMT_RU12LE - Real unsigned 12-bit little endian sample inside 16-bit (2 byte). V4L2 FourCC: RU12. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/videodev2.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 1f1a65c1fe8e..e8f01cd8a17d 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -453,6 +453,7 @@ struct v4l2_pix_format { /* SDR formats - used only for Software Defined Radio devices */ #define V4L2_SDR_FMT_CU8 v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */ #define V4L2_SDR_FMT_CU16LE v4l2_fourcc('C', 'U', '1', '6') /* IQ u16le */ +#define V4L2_SDR_FMT_RU12LE v4l2_fourcc('R', 'U', '1', '2') /* real u12le */ /* priv field value to indicates that subsequent fields are valid. */ #define V4L2_PIX_FMT_PRIV_MAGIC 0xfeedcafe -- cgit v1.2.3 From c0c03886763f4d5ccfc5d854fd8dfe77814753be Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 11 Jul 2014 13:25:14 -0300 Subject: [media] v4l: uapi: add SDR format CS8 V4L2_SDR_FMT_CS8 - Complex signed 8-bit IQ sample Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/videodev2.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index e8f01cd8a17d..e943e4cc8df6 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -453,6 +453,7 @@ struct v4l2_pix_format { /* SDR formats - used only for Software Defined Radio devices */ #define V4L2_SDR_FMT_CU8 v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */ #define V4L2_SDR_FMT_CU16LE v4l2_fourcc('C', 'U', '1', '6') /* IQ u16le */ +#define V4L2_SDR_FMT_CS8 v4l2_fourcc('C', 'S', '0', '8') /* complex s8 */ #define V4L2_SDR_FMT_RU12LE v4l2_fourcc('R', 'U', '1', '2') /* real u12le */ /* priv field value to indicates that subsequent fields are valid. */ -- cgit v1.2.3 From 5c5be570043f864b8373ed26fd68698fe352a0ef Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 11 Jul 2014 16:35:47 -0300 Subject: [media] v4l: uapi: add SDR format CS14 V4L2_SDR_FMT_CS14LE - Complex signed 14-bit IQ sample Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/videodev2.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index e943e4cc8df6..6da302d46145 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -454,6 +454,7 @@ struct v4l2_pix_format { #define V4L2_SDR_FMT_CU8 v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */ #define V4L2_SDR_FMT_CU16LE v4l2_fourcc('C', 'U', '1', '6') /* IQ u16le */ #define V4L2_SDR_FMT_CS8 v4l2_fourcc('C', 'S', '0', '8') /* complex s8 */ +#define V4L2_SDR_FMT_CS14LE v4l2_fourcc('C', 'S', '1', '4') /* complex s14le */ #define V4L2_SDR_FMT_RU12LE v4l2_fourcc('R', 'U', '1', '2') /* real u12le */ /* priv field value to indicates that subsequent fields are valid. */ -- cgit v1.2.3 From aaa968b636550042023bffa075adcdb01e6d257c Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 18 Jul 2014 15:28:24 -0300 Subject: [media] v4l: videodev2: add buffer size to SDR format Add buffer size field to struct v4l2_sdr_format. It is used for negotiate streaming buffer size between application and driver. Signed-off-by: Antti Palosaari Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/videodev2.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 6da302d46145..8fc4822c7883 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1783,10 +1783,12 @@ struct v4l2_pix_format_mplane { /** * struct v4l2_sdr_format - SDR format definition * @pixelformat: little endian four character code (fourcc) + * @buffersize: maximum size in bytes required for data */ struct v4l2_sdr_format { __u32 pixelformat; - __u8 reserved[28]; + __u32 buffersize; + __u8 reserved[24]; } __attribute__ ((packed)); /** -- cgit v1.2.3 From c708a98f01068fe07f77448031f9f5317423e777 Mon Sep 17 00:00:00 2001 From: Javi Merino Date: Wed, 25 Jun 2014 11:00:12 +0100 Subject: thermal: document struct thermal_zone_device and thermal_governor Document struct thermal_zone_device and struct thermal_governor fields and their use by the thermal framework code. Cc: Zhang Rui Cc: Eduardo Valentin Signed-off-by: Javi Merino Signed-off-by: Zhang Rui --- include/linux/thermal.h | 46 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/thermal.h b/include/linux/thermal.h index f7e11c7ea7d9..0305cde21a74 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -158,6 +158,42 @@ struct thermal_attr { char name[THERMAL_NAME_LENGTH]; }; +/** + * struct thermal_zone_device - structure for a thermal zone + * @id: unique id number for each thermal zone + * @type: the thermal zone device type + * @device: &struct device for this thermal zone + * @trip_temp_attrs: attributes for trip points for sysfs: trip temperature + * @trip_type_attrs: attributes for trip points for sysfs: trip type + * @trip_hyst_attrs: attributes for trip points for sysfs: trip hysteresis + * @devdata: private pointer for device private data + * @trips: number of trip points the thermal zone supports + * @passive_delay: number of milliseconds to wait between polls when + * performing passive cooling. Currenty only used by the + * step-wise governor + * @polling_delay: number of milliseconds to wait between polls when + * checking whether trip points have been crossed (0 for + * interrupt driven systems) + * @temperature: current temperature. This is only for core code, + * drivers should use thermal_zone_get_temp() to get the + * current temperature + * @last_temperature: previous temperature read + * @emul_temperature: emulated temperature when using CONFIG_THERMAL_EMULATION + * @passive: 1 if you've crossed a passive trip point, 0 otherwise. + * Currenty only used by the step-wise governor. + * @forced_passive: If > 0, temperature at which to switch on all ACPI + * processor cooling devices. Currently only used by the + * step-wise governor. + * @ops: operations this &thermal_zone_device supports + * @tzp: thermal zone parameters + * @governor: pointer to the governor for this thermal zone + * @thermal_instances: list of &struct thermal_instance of this thermal zone + * @idr: &struct idr to generate unique id for this zone's cooling + * devices + * @lock: lock to protect thermal_instances list + * @node: node in thermal_tz_list (in thermal_core.c) + * @poll_queue: delayed work for polling + */ struct thermal_zone_device { int id; char type[THERMAL_NAME_LENGTH]; @@ -179,12 +215,18 @@ struct thermal_zone_device { struct thermal_governor *governor; struct list_head thermal_instances; struct idr idr; - struct mutex lock; /* protect thermal_instances list */ + struct mutex lock; struct list_head node; struct delayed_work poll_queue; }; -/* Structure that holds thermal governor information */ +/** + * struct thermal_governor - structure that holds thermal governor information + * @name: name of the governor + * @throttle: callback called for every trip point even if temperature is + * below the trip point temperature + * @governor_list: node in thermal_governor_list (in thermal_core.c) + */ struct thermal_governor { char name[THERMAL_NAME_LENGTH]; int (*throttle)(struct thermal_zone_device *tz, int trip); -- cgit v1.2.3 From bb5fd0b6daaf0da0b1e78c699b8582984373d3f4 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Fri, 11 Jul 2014 09:49:41 +0200 Subject: mtd: nand: define struct nand_timings Define a struct containing the standard NAND timings as described in NAND datasheets. Signed-off-by: Boris BREZILLON Signed-off-by: Brian Norris --- include/linux/mtd/nand.h | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) (limited to 'include') diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 1cff329ae13d..cdda207c16e1 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -948,4 +948,53 @@ static inline int jedec_feature(struct nand_chip *chip) return chip->jedec_version ? le16_to_cpu(chip->jedec_params.features) : 0; } + +/** + * struct nand_sdr_timings - SDR NAND chip timings + * + * This struct defines the timing requirements of a SDR NAND chip. + * These informations can be found in every NAND datasheets and the timings + * meaning are described in the ONFI specifications: + * www.onfi.org/~/media/ONFI/specs/onfi_3_1_spec.pdf (chapter 4.15 Timing + * Parameters) + * + * All these timings are expressed in picoseconds. + */ + +struct nand_sdr_timings { + u32 tALH_min; + u32 tADL_min; + u32 tALS_min; + u32 tAR_min; + u32 tCEA_max; + u32 tCEH_min; + u32 tCH_min; + u32 tCHZ_max; + u32 tCLH_min; + u32 tCLR_min; + u32 tCLS_min; + u32 tCOH_min; + u32 tCS_min; + u32 tDH_min; + u32 tDS_min; + u32 tFEAT_max; + u32 tIR_min; + u32 tITC_max; + u32 tRC_min; + u32 tREA_max; + u32 tREH_min; + u32 tRHOH_min; + u32 tRHW_min; + u32 tRHZ_max; + u32 tRLOH_min; + u32 tRP_min; + u32 tRR_min; + u64 tRST_max; + u32 tWB_max; + u32 tWC_min; + u32 tWH_min; + u32 tWHR_min; + u32 tWP_min; + u32 tWW_min; +}; #endif /* __LINUX_MTD_NAND_H */ -- cgit v1.2.3 From 974647ea8a13021a91d558df61d598bcabf73439 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Fri, 11 Jul 2014 09:49:42 +0200 Subject: mtd: nand: add ONFI timing mode to nand_timings converter Add a converter to retrieve NAND timings from an ONFI NAND timing mode. At the moment, only SDR NAND timings are supported. Signed-off-by: Boris BREZILLON Signed-off-by: Brian Norris --- drivers/mtd/nand/Makefile | 2 +- drivers/mtd/nand/nand_timings.c | 253 ++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/nand.h | 3 + 3 files changed, 257 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/nand/nand_timings.c (limited to 'include') diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 542b5689eb63..a035e7cc6d46 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -50,4 +50,4 @@ obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/ -nand-objs := nand_base.o nand_bbt.o +nand-objs := nand_base.o nand_bbt.o nand_timings.o diff --git a/drivers/mtd/nand/nand_timings.c b/drivers/mtd/nand/nand_timings.c new file mode 100644 index 000000000000..8b36253420fa --- /dev/null +++ b/drivers/mtd/nand/nand_timings.c @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2014 Free Electrons + * + * Author: Boris BREZILLON + * + * 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 + +static const struct nand_sdr_timings onfi_sdr_timings[] = { + /* Mode 0 */ + { + .tADL_min = 200000, + .tALH_min = 20000, + .tALS_min = 50000, + .tAR_min = 25000, + .tCEA_max = 100000, + .tCEH_min = 20000, + .tCH_min = 20000, + .tCHZ_max = 100000, + .tCLH_min = 20000, + .tCLR_min = 20000, + .tCLS_min = 50000, + .tCOH_min = 0, + .tCS_min = 70000, + .tDH_min = 20000, + .tDS_min = 40000, + .tFEAT_max = 1000000, + .tIR_min = 10000, + .tITC_max = 1000000, + .tRC_min = 100000, + .tREA_max = 40000, + .tREH_min = 30000, + .tRHOH_min = 0, + .tRHW_min = 200000, + .tRHZ_max = 200000, + .tRLOH_min = 0, + .tRP_min = 50000, + .tRST_max = 250000000000, + .tWB_max = 200000, + .tRR_min = 40000, + .tWC_min = 100000, + .tWH_min = 30000, + .tWHR_min = 120000, + .tWP_min = 50000, + .tWW_min = 100000, + }, + /* Mode 1 */ + { + .tADL_min = 100000, + .tALH_min = 10000, + .tALS_min = 25000, + .tAR_min = 10000, + .tCEA_max = 45000, + .tCEH_min = 20000, + .tCH_min = 10000, + .tCHZ_max = 50000, + .tCLH_min = 10000, + .tCLR_min = 10000, + .tCLS_min = 25000, + .tCOH_min = 15000, + .tCS_min = 35000, + .tDH_min = 10000, + .tDS_min = 20000, + .tFEAT_max = 1000000, + .tIR_min = 0, + .tITC_max = 1000000, + .tRC_min = 50000, + .tREA_max = 30000, + .tREH_min = 15000, + .tRHOH_min = 15000, + .tRHW_min = 100000, + .tRHZ_max = 100000, + .tRLOH_min = 0, + .tRP_min = 25000, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tWC_min = 45000, + .tWH_min = 15000, + .tWHR_min = 80000, + .tWP_min = 25000, + .tWW_min = 100000, + }, + /* Mode 2 */ + { + .tADL_min = 100000, + .tALH_min = 10000, + .tALS_min = 15000, + .tAR_min = 10000, + .tCEA_max = 30000, + .tCEH_min = 20000, + .tCH_min = 10000, + .tCHZ_max = 50000, + .tCLH_min = 10000, + .tCLR_min = 10000, + .tCLS_min = 15000, + .tCOH_min = 15000, + .tCS_min = 25000, + .tDH_min = 5000, + .tDS_min = 15000, + .tFEAT_max = 1000000, + .tIR_min = 0, + .tITC_max = 1000000, + .tRC_min = 35000, + .tREA_max = 25000, + .tREH_min = 15000, + .tRHOH_min = 15000, + .tRHW_min = 100000, + .tRHZ_max = 100000, + .tRLOH_min = 0, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tRP_min = 17000, + .tWC_min = 35000, + .tWH_min = 15000, + .tWHR_min = 80000, + .tWP_min = 17000, + .tWW_min = 100000, + }, + /* Mode 3 */ + { + .tADL_min = 100000, + .tALH_min = 5000, + .tALS_min = 10000, + .tAR_min = 10000, + .tCEA_max = 25000, + .tCEH_min = 20000, + .tCH_min = 5000, + .tCHZ_max = 50000, + .tCLH_min = 5000, + .tCLR_min = 10000, + .tCLS_min = 10000, + .tCOH_min = 15000, + .tCS_min = 25000, + .tDH_min = 5000, + .tDS_min = 10000, + .tFEAT_max = 1000000, + .tIR_min = 0, + .tITC_max = 1000000, + .tRC_min = 30000, + .tREA_max = 20000, + .tREH_min = 10000, + .tRHOH_min = 15000, + .tRHW_min = 100000, + .tRHZ_max = 100000, + .tRLOH_min = 0, + .tRP_min = 15000, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tWC_min = 30000, + .tWH_min = 10000, + .tWHR_min = 80000, + .tWP_min = 15000, + .tWW_min = 100000, + }, + /* Mode 4 */ + { + .tADL_min = 70000, + .tALH_min = 5000, + .tALS_min = 10000, + .tAR_min = 10000, + .tCEA_max = 25000, + .tCEH_min = 20000, + .tCH_min = 5000, + .tCHZ_max = 30000, + .tCLH_min = 5000, + .tCLR_min = 10000, + .tCLS_min = 10000, + .tCOH_min = 15000, + .tCS_min = 20000, + .tDH_min = 5000, + .tDS_min = 10000, + .tFEAT_max = 1000000, + .tIR_min = 0, + .tITC_max = 1000000, + .tRC_min = 25000, + .tREA_max = 20000, + .tREH_min = 10000, + .tRHOH_min = 15000, + .tRHW_min = 100000, + .tRHZ_max = 100000, + .tRLOH_min = 5000, + .tRP_min = 12000, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tWC_min = 25000, + .tWH_min = 10000, + .tWHR_min = 80000, + .tWP_min = 12000, + .tWW_min = 100000, + }, + /* Mode 5 */ + { + .tADL_min = 70000, + .tALH_min = 5000, + .tALS_min = 10000, + .tAR_min = 10000, + .tCEA_max = 25000, + .tCEH_min = 20000, + .tCH_min = 5000, + .tCHZ_max = 30000, + .tCLH_min = 5000, + .tCLR_min = 10000, + .tCLS_min = 10000, + .tCOH_min = 15000, + .tCS_min = 15000, + .tDH_min = 5000, + .tDS_min = 7000, + .tFEAT_max = 1000000, + .tIR_min = 0, + .tITC_max = 1000000, + .tRC_min = 20000, + .tREA_max = 16000, + .tREH_min = 7000, + .tRHOH_min = 15000, + .tRHW_min = 100000, + .tRHZ_max = 100000, + .tRLOH_min = 5000, + .tRP_min = 10000, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tWC_min = 20000, + .tWH_min = 7000, + .tWHR_min = 80000, + .tWP_min = 10000, + .tWW_min = 100000, + }, +}; + +/** + * onfi_async_timing_mode_to_sdr_timings - [NAND Interface] Retrieve NAND + * timings according to the given ONFI timing mode + * @mode: ONFI timing mode + */ +const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode) +{ + if (mode < 0 || mode >= ARRAY_SIZE(onfi_sdr_timings)) + return ERR_PTR(-EINVAL); + + return &onfi_sdr_timings[mode]; +} +EXPORT_SYMBOL(onfi_async_timing_mode_to_sdr_timings); diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index cdda207c16e1..3083c53e0270 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -997,4 +997,7 @@ struct nand_sdr_timings { u32 tWP_min; u32 tWW_min; }; + +/* get timing characteristics from ONFI timing mode. */ +const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode); #endif /* __LINUX_MTD_NAND_H */ -- cgit v1.2.3 From 273886b4f71a1a960a3625833df2f037efffab45 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 18 Jul 2014 02:58:41 -0300 Subject: [media] videodev2.h: add defines for the VBI field start lines While working with raw and sliced VBI support in several applications I noticed that you really need to know the start linenumbers for each video field in order to correctly convert the start line numbers reported by v4l2_vbi_format to the line numbers used in v4l2_sliced_vbi_format. This patch adds four defines that specify the start lines for each field for both 525 and 625 line standards. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/videodev2.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 8fc4822c7883..5fd42027877a 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1642,6 +1642,12 @@ struct v4l2_vbi_format { #define V4L2_VBI_UNSYNC (1 << 0) #define V4L2_VBI_INTERLACED (1 << 1) +/* ITU-R start lines for each field */ +#define V4L2_VBI_ITU_525_F1_START (1) +#define V4L2_VBI_ITU_525_F2_START (264) +#define V4L2_VBI_ITU_625_F1_START (1) +#define V4L2_VBI_ITU_625_F2_START (314) + /* Sliced VBI * * This implements is a proposal V4L2 API to allow SLICED VBI -- cgit v1.2.3 From 76be4a54157ab0059fb29d8d516db46d239812e2 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 12 Jun 2014 17:15:22 +0530 Subject: ARM: OMAP2+: DMA: remove requirement of irq for platform-dma driver we have currently 2 DMA drivers that try to co-exist. drivers/dma/omap-dma.c which registers it's own IRQ and is device tree aware and uses arch/arm/plat-omap/dma.c instance created by arch/arm/mach-omap2/dma.c to maintain channel usage (omap_request_dma). Currently both try to register interrupts and mach-omap2/plat-omap dma.c attempts to use the IRQ number registered by hwmod to register it's own interrupt handler. Now, there is no reasonable way of static allocating DMA irq in GIC SPI when we use crossbar. However, since the dma_chan structure is freed as a result of IRQ not being present due to devm allocation, maintaining information of channel by platform code fails at a later point in time when that region of memory is reused. So, if hwmod does not indicate an IRQ number, then, assume that dma-engine will take care of the interrupt handling. Signed-off-by: Nishanth Menon Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/dma.c | 3 +++ arch/arm/plat-omap/dma.c | 5 +++-- include/linux/omap-dma.h | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-omap2/dma.c b/arch/arm/mach-omap2/dma.c index a6d2cf1f8d02..e1a56d87599e 100644 --- a/arch/arm/mach-omap2/dma.c +++ b/arch/arm/mach-omap2/dma.c @@ -259,6 +259,9 @@ static int __init omap2_system_dma_init_dev(struct omap_hwmod *oh, void *unused) if (cpu_is_omap34xx() && (omap_type() != OMAP2_DEVICE_TYPE_GP)) d->dev_caps |= HS_CHANNELS_RESERVED; + if (platform_get_irq_byname(pdev, "0") < 0) + d->dev_caps |= DMA_ENGINE_HANDLE_IRQ; + /* Check the capabilities register for descriptor loading feature */ if (dma_read(CAPS_0, 0) & DMA_HAS_DESCRIPTOR_CAPS) dma_common_ch_end = CCDN; diff --git a/arch/arm/plat-omap/dma.c b/arch/arm/plat-omap/dma.c index b5608b1f9fbd..7aae0e5b188c 100644 --- a/arch/arm/plat-omap/dma.c +++ b/arch/arm/plat-omap/dma.c @@ -2100,7 +2100,7 @@ static int omap_system_dma_probe(struct platform_device *pdev) omap_dma_set_global_params(DMA_DEFAULT_ARB_RATE, DMA_DEFAULT_FIFO_DEPTH, 0); - if (dma_omap2plus()) { + if (dma_omap2plus() && !(d->dev_caps & DMA_ENGINE_HANDLE_IRQ)) { strcpy(irq_name, "0"); dma_irq = platform_get_irq_byname(pdev, irq_name); if (dma_irq < 0) { @@ -2145,7 +2145,8 @@ static int omap_system_dma_remove(struct platform_device *pdev) char irq_name[4]; strcpy(irq_name, "0"); dma_irq = platform_get_irq_byname(pdev, irq_name); - remove_irq(dma_irq, &omap24xx_dma_irq); + if (dma_irq >= 0) + remove_irq(dma_irq, &omap24xx_dma_irq); } else { int irq_rel = 0; for ( ; irq_rel < dma_chan_count; irq_rel++) { diff --git a/include/linux/omap-dma.h b/include/linux/omap-dma.h index 88e6ea4a5d36..6f06f8bc612c 100644 --- a/include/linux/omap-dma.h +++ b/include/linux/omap-dma.h @@ -130,6 +130,7 @@ #define IS_WORD_16 BIT(0xd) #define ENABLE_16XX_MODE BIT(0xe) #define HS_CHANNELS_RESERVED BIT(0xf) +#define DMA_ENGINE_HANDLE_IRQ BIT(0x10) /* Defines for DMA Capabilities */ #define DMA_HAS_TRANSPARENT_CAPS (0x1 << 18) -- cgit v1.2.3 From d821bfa4ca7d2699ef82557bceb05192fffcc51a Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 9 Jul 2014 12:41:11 +0100 Subject: phy: miphy365x: Add MiPHY365x header file for DT x Driver defines This provides the shared header file which will be reference from both the MiPHY365x driver and its associated Device Tree node(s). Cc: Kishon Vijay Abraham I Acked-by: Mark Rutland Acked-by: Alexandre Torgue Signed-off-by: Lee Jones Signed-off-by: Kishon Vijay Abraham I --- include/dt-bindings/phy/phy-miphy365x.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 include/dt-bindings/phy/phy-miphy365x.h (limited to 'include') diff --git a/include/dt-bindings/phy/phy-miphy365x.h b/include/dt-bindings/phy/phy-miphy365x.h new file mode 100644 index 000000000000..8ef8aba6edd6 --- /dev/null +++ b/include/dt-bindings/phy/phy-miphy365x.h @@ -0,0 +1,14 @@ +/* + * This header provides constants for the phy framework + * based on the STMicroelectronics MiPHY365x. + * + * Author: Lee Jones + */ +#ifndef _DT_BINDINGS_PHY_MIPHY +#define _DT_BINDINGS_PHY_MIPHY + +#define MIPHY_TYPE_SATA 1 +#define MIPHY_TYPE_PCIE 2 +#define MIPHY_TYPE_USB 3 + +#endif /* _DT_BINDINGS_PHY_MIPHY */ -- cgit v1.2.3 From 371c359f83b4d19d2688e975a07dc44add307e24 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 21 Jul 2014 12:22:26 +0200 Subject: drm/dsi: Make mipi_dsi_dcs_write() return ssize_t This function returns the value of the struct mipi_dsi_host_ops' .transfer() so make sure the return types are consistent. Acked-by: Andrzej Hajda Signed-off-by: Thierry Reding --- drivers/gpu/drm/drm_mipi_dsi.c | 4 ++-- drivers/gpu/drm/panel/panel-s6e8aa0.c | 4 ++-- include/drm/drm_mipi_dsi.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c index e633df2f68d8..6d2fd2077dae 100644 --- a/drivers/gpu/drm/drm_mipi_dsi.c +++ b/drivers/gpu/drm/drm_mipi_dsi.c @@ -205,8 +205,8 @@ EXPORT_SYMBOL(mipi_dsi_detach); * @data: pointer to the command followed by parameters * @len: length of @data */ -int mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, unsigned int channel, - const void *data, size_t len) +ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, unsigned int channel, + const void *data, size_t len) { const struct mipi_dsi_host_ops *ops = dsi->host->ops; struct mipi_dsi_msg msg = { diff --git a/drivers/gpu/drm/panel/panel-s6e8aa0.c b/drivers/gpu/drm/panel/panel-s6e8aa0.c index 06e57a26db7a..beb43492b649 100644 --- a/drivers/gpu/drm/panel/panel-s6e8aa0.c +++ b/drivers/gpu/drm/panel/panel-s6e8aa0.c @@ -133,14 +133,14 @@ static int s6e8aa0_clear_error(struct s6e8aa0 *ctx) static void s6e8aa0_dcs_write(struct s6e8aa0 *ctx, const void *data, size_t len) { struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); - int ret; + ssize_t ret; if (ctx->error < 0) return; ret = mipi_dsi_dcs_write(dsi, dsi->channel, data, len); if (ret < 0) { - dev_err(ctx->dev, "error %d writing dcs seq: %*ph\n", ret, len, + dev_err(ctx->dev, "error %zd writing dcs seq: %*ph\n", ret, len, data); ctx->error = ret; } diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h index efa1b552adc5..4b0112781910 100644 --- a/include/drm/drm_mipi_dsi.h +++ b/include/drm/drm_mipi_dsi.h @@ -127,8 +127,8 @@ struct mipi_dsi_device { int mipi_dsi_attach(struct mipi_dsi_device *dsi); int mipi_dsi_detach(struct mipi_dsi_device *dsi); -int mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, unsigned int channel, - const void *data, size_t len); +ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, unsigned int channel, + const void *data, size_t len); ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, unsigned int channel, u8 cmd, void *data, size_t len); -- cgit v1.2.3 From 3c523d7d38a17b17371a9ea952c04a9b65c8a357 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 21 Jul 2014 12:28:25 +0200 Subject: drm/dsi: Use peripheral's channel for DCS commands When executing DCS commands, use the channel associated with the DSI peripheral rather than one explicitly specified in the function call. Devices shouldn't be able to step on each others' toes like this. Acked-by: Andrzej Hajda Signed-off-by: Thierry Reding --- drivers/gpu/drm/drm_mipi_dsi.c | 14 ++++++-------- drivers/gpu/drm/panel/panel-s6e8aa0.c | 4 ++-- include/drm/drm_mipi_dsi.h | 8 ++++---- 3 files changed, 12 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c index 6d2fd2077dae..6aa6a9e95570 100644 --- a/drivers/gpu/drm/drm_mipi_dsi.c +++ b/drivers/gpu/drm/drm_mipi_dsi.c @@ -201,16 +201,15 @@ EXPORT_SYMBOL(mipi_dsi_detach); /** * mipi_dsi_dcs_write - send DCS write command * @dsi: DSI device - * @channel: virtual channel * @data: pointer to the command followed by parameters * @len: length of @data */ -ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, unsigned int channel, - const void *data, size_t len) +ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, const void *data, + size_t len) { const struct mipi_dsi_host_ops *ops = dsi->host->ops; struct mipi_dsi_msg msg = { - .channel = channel, + .channel = dsi->channel, .tx_buf = data, .tx_len = len }; @@ -239,19 +238,18 @@ EXPORT_SYMBOL(mipi_dsi_dcs_write); /** * mipi_dsi_dcs_read - send DCS read request command * @dsi: DSI device - * @channel: virtual channel * @cmd: DCS read command * @data: pointer to read buffer * @len: length of @data * * Function returns number of read bytes or error code. */ -ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, unsigned int channel, - u8 cmd, void *data, size_t len) +ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data, + size_t len) { const struct mipi_dsi_host_ops *ops = dsi->host->ops; struct mipi_dsi_msg msg = { - .channel = channel, + .channel = dsi->channel, .type = MIPI_DSI_DCS_READ, .tx_buf = &cmd, .tx_len = 1, diff --git a/drivers/gpu/drm/panel/panel-s6e8aa0.c b/drivers/gpu/drm/panel/panel-s6e8aa0.c index beb43492b649..5502ef6bc074 100644 --- a/drivers/gpu/drm/panel/panel-s6e8aa0.c +++ b/drivers/gpu/drm/panel/panel-s6e8aa0.c @@ -138,7 +138,7 @@ static void s6e8aa0_dcs_write(struct s6e8aa0 *ctx, const void *data, size_t len) if (ctx->error < 0) return; - ret = mipi_dsi_dcs_write(dsi, dsi->channel, data, len); + ret = mipi_dsi_dcs_write(dsi, data, len); if (ret < 0) { dev_err(ctx->dev, "error %zd writing dcs seq: %*ph\n", ret, len, data); @@ -154,7 +154,7 @@ static int s6e8aa0_dcs_read(struct s6e8aa0 *ctx, u8 cmd, void *data, size_t len) if (ctx->error < 0) return ctx->error; - ret = mipi_dsi_dcs_read(dsi, dsi->channel, cmd, data, len); + ret = mipi_dsi_dcs_read(dsi, cmd, data, len); if (ret < 0) { dev_err(ctx->dev, "error %d reading dcs seq(%#x)\n", ret, cmd); ctx->error = ret; diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h index 4b0112781910..7b5e1a9244e1 100644 --- a/include/drm/drm_mipi_dsi.h +++ b/include/drm/drm_mipi_dsi.h @@ -127,10 +127,10 @@ struct mipi_dsi_device { int mipi_dsi_attach(struct mipi_dsi_device *dsi); int mipi_dsi_detach(struct mipi_dsi_device *dsi); -ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, unsigned int channel, - const void *data, size_t len); -ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, unsigned int channel, - u8 cmd, void *data, size_t len); +ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, const void *data, + size_t len); +ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data, + size_t len); /** * struct mipi_dsi_driver - DSI driver -- cgit v1.2.3 From f0e2cf7b912522c9c7146d9d6e99d1b0ea5c97c6 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Wed, 25 Jun 2014 23:22:57 +0530 Subject: phy: pipe3: insert delay to enumerate in GEN2 mode 8-bit delay value (0xF1) is required for GEN2 devices to be enumerated consistently. Added an API to be called from PHY drivers to set this delay value and called it from PIPE3 driver to set the delay value. Signed-off-by: Kishon Vijay Abraham I Reviewed-by: Roger Quadros --- Documentation/devicetree/bindings/phy/ti-phy.txt | 12 +++--- drivers/phy/phy-omap-control.c | 52 +++++++++++++++++++++++- drivers/phy/phy-ti-pipe3.c | 4 +- include/linux/phy/omap_control_phy.h | 10 +++++ 4 files changed, 71 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/phy/ti-phy.txt b/Documentation/devicetree/bindings/phy/ti-phy.txt index b50e1c10a05d..305e3df3d9b1 100644 --- a/Documentation/devicetree/bindings/phy/ti-phy.txt +++ b/Documentation/devicetree/bindings/phy/ti-phy.txt @@ -9,15 +9,17 @@ Required properties: e.g. USB2_PHY on OMAP5. "ti,control-phy-pipe3" - if it has DPLL and individual Rx & Tx power control e.g. USB3 PHY and SATA PHY on OMAP5. + "ti,control-phy-pcie" - for pcie to support external clock for pcie and to + set PCS delay value. + e.g. PCIE PHY in DRA7x "ti,control-phy-usb2-dra7" - if it has power down register like USB2 PHY on DRA7 platform. "ti,control-phy-usb2-am437" - if it has power down register like USB2 PHY on AM437 platform. - - reg : Address and length of the register set for the device. It contains - the address of "otghs_control" for control-phy-otghs or "power" register - for other types. - - reg-names: should be "otghs_control" control-phy-otghs and "power" for - other types. + - reg : register ranges as listed in the reg-names property + - reg-names: "otghs_control" for control-phy-otghs + "power", "pcie_pcs" and "control_sma" for control-phy-pcie + "power" for all other types omap_control_usb: omap-control-usb@4a002300 { compatible = "ti,control-phy-otghs"; diff --git a/drivers/phy/phy-omap-control.c b/drivers/phy/phy-omap-control.c index 311b4f9a5132..9487bf112267 100644 --- a/drivers/phy/phy-omap-control.c +++ b/drivers/phy/phy-omap-control.c @@ -26,6 +26,41 @@ #include #include +/** + * omap_control_pcie_pcs - set the PCS delay count + * @dev: the control module device + * @id: index of the pcie PHY (should be 1 or 2) + * @delay: 8 bit delay value + */ +void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay) +{ + u32 val; + struct omap_control_phy *control_phy; + + if (IS_ERR(dev) || !dev) { + pr_err("%s: invalid device\n", __func__); + return; + } + + control_phy = dev_get_drvdata(dev); + if (!control_phy) { + dev_err(dev, "%s: invalid control phy device\n", __func__); + return; + } + + if (control_phy->type != OMAP_CTRL_TYPE_PCIE) { + dev_err(dev, "%s: unsupported operation\n", __func__); + return; + } + + val = readl(control_phy->pcie_pcs); + val &= ~(OMAP_CTRL_PCIE_PCS_MASK << + (id * OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT)); + val |= delay << (id * OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT); + writel(val, control_phy->pcie_pcs); +} +EXPORT_SYMBOL_GPL(omap_control_pcie_pcs); + /** * omap_control_phy_power - power on/off the phy using control module reg * @dev: the control module device @@ -61,6 +96,7 @@ void omap_control_phy_power(struct device *dev, int on) val |= OMAP_CTRL_DEV_PHY_PD; break; + case OMAP_CTRL_TYPE_PCIE: case OMAP_CTRL_TYPE_PIPE3: rate = clk_get_rate(control_phy->sys_clk); rate = rate/1000000; @@ -211,6 +247,7 @@ EXPORT_SYMBOL_GPL(omap_control_usb_set_mode); static const enum omap_control_phy_type otghs_data = OMAP_CTRL_TYPE_OTGHS; static const enum omap_control_phy_type usb2_data = OMAP_CTRL_TYPE_USB2; static const enum omap_control_phy_type pipe3_data = OMAP_CTRL_TYPE_PIPE3; +static const enum omap_control_phy_type pcie_data = OMAP_CTRL_TYPE_PCIE; static const enum omap_control_phy_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2; static const enum omap_control_phy_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2; @@ -227,6 +264,10 @@ static const struct of_device_id omap_control_phy_id_table[] = { .compatible = "ti,control-phy-pipe3", .data = &pipe3_data, }, + { + .compatible = "ti,control-phy-pcie", + .data = &pcie_data, + }, { .compatible = "ti,control-phy-usb2-dra7", .data = &dra7usb2_data, @@ -279,7 +320,8 @@ static int omap_control_phy_probe(struct platform_device *pdev) } } - if (control_phy->type == OMAP_CTRL_TYPE_PIPE3) { + if (control_phy->type == OMAP_CTRL_TYPE_PIPE3 || + control_phy->type == OMAP_CTRL_TYPE_PCIE) { control_phy->sys_clk = devm_clk_get(control_phy->dev, "sys_clkin"); if (IS_ERR(control_phy->sys_clk)) { @@ -288,6 +330,14 @@ static int omap_control_phy_probe(struct platform_device *pdev) } } + if (control_phy->type == OMAP_CTRL_TYPE_PCIE) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "pcie_pcs"); + control_phy->pcie_pcs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(control_phy->pcie_pcs)) + return PTR_ERR(control_phy->pcie_pcs); + } + dev_set_drvdata(control_phy->dev, control_phy); return 0; diff --git a/drivers/phy/phy-ti-pipe3.c b/drivers/phy/phy-ti-pipe3.c index 6174f4b1a5de..93bcd67f1b22 100644 --- a/drivers/phy/phy-ti-pipe3.c +++ b/drivers/phy/phy-ti-pipe3.c @@ -217,8 +217,10 @@ static int ti_pipe3_init(struct phy *x) u32 val; int ret = 0; - if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) + if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) { + omap_control_pcie_pcs(phy->control_dev, phy->id, 0xF1); return 0; + } /* Bring it out of IDLE if it is IDLE */ val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); diff --git a/include/linux/phy/omap_control_phy.h b/include/linux/phy/omap_control_phy.h index 5450403c7546..e9e6cfbfbb58 100644 --- a/include/linux/phy/omap_control_phy.h +++ b/include/linux/phy/omap_control_phy.h @@ -23,6 +23,7 @@ enum omap_control_phy_type { OMAP_CTRL_TYPE_OTGHS = 1, /* Mailbox OTGHS_CONTROL */ OMAP_CTRL_TYPE_USB2, /* USB2_PHY, power down in CONTROL_DEV_CONF */ OMAP_CTRL_TYPE_PIPE3, /* PIPE3 PHY, DPLL & seperate Rx/Tx power */ + OMAP_CTRL_TYPE_PCIE, /* RX TX control of ACSPCIE */ OMAP_CTRL_TYPE_DRA7USB2, /* USB2 PHY, power and power_aux e.g. DRA7 */ OMAP_CTRL_TYPE_AM437USB2, /* USB2 PHY, power e.g. AM437x */ }; @@ -33,6 +34,7 @@ struct omap_control_phy { u32 __iomem *otghs_control; u32 __iomem *power; u32 __iomem *power_aux; + u32 __iomem *pcie_pcs; struct clk *sys_clk; @@ -63,6 +65,9 @@ enum omap_control_usb_mode { #define OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON 0x3 #define OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF 0x0 +#define OMAP_CTRL_PCIE_PCS_MASK 0xff +#define OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT 0x8 + #define OMAP_CTRL_USB2_PHY_PD BIT(28) #define AM437X_CTRL_USB2_PHY_PD BIT(0) @@ -74,6 +79,7 @@ enum omap_control_usb_mode { void omap_control_phy_power(struct device *dev, int on); void omap_control_usb_set_mode(struct device *dev, enum omap_control_usb_mode mode); +void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay); #else static inline void omap_control_phy_power(struct device *dev, int on) @@ -84,6 +90,10 @@ static inline void omap_control_usb_set_mode(struct device *dev, enum omap_control_usb_mode mode) { } + +static inline void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay) +{ +} #endif #endif /* __OMAP_CONTROL_PHY_H__ */ -- cgit v1.2.3 From 3be88125d85df587085b0be0a5c0e9953eb5ed6b Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 4 Jul 2014 12:55:45 +0300 Subject: phy: core: Support regulator supply for PHY power Some PHYs can be powered by an external power regulator. e.g. USB_HS PHY on DRA7 SoC. Make the PHY core support a power regulator. Signed-off-by: Roger Quadros Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/phy-core.c | 26 ++++++++++++++++++++++++++ include/linux/phy/phy.h | 2 ++ 2 files changed, 28 insertions(+) (limited to 'include') diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index 49c446530101..75c97396dbfa 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -21,6 +21,7 @@ #include #include #include +#include static struct class *phy_class; static DEFINE_MUTEX(phy_provider_mutex); @@ -226,6 +227,12 @@ int phy_power_on(struct phy *phy) if (!phy) return 0; + if (phy->pwr) { + ret = regulator_enable(phy->pwr); + if (ret) + return ret; + } + ret = phy_pm_runtime_get_sync(phy); if (ret < 0 && ret != -ENOTSUPP) return ret; @@ -247,6 +254,8 @@ int phy_power_on(struct phy *phy) out: mutex_unlock(&phy->mutex); phy_pm_runtime_put_sync(phy); + if (phy->pwr) + regulator_disable(phy->pwr); return ret; } @@ -272,6 +281,9 @@ int phy_power_off(struct phy *phy) mutex_unlock(&phy->mutex); phy_pm_runtime_put(phy); + if (phy->pwr) + regulator_disable(phy->pwr); + return 0; } EXPORT_SYMBOL_GPL(phy_power_off); @@ -588,6 +600,16 @@ struct phy *phy_create(struct device *dev, const struct phy_ops *ops, goto free_phy; } + /* phy-supply */ + phy->pwr = regulator_get_optional(dev, "phy"); + if (IS_ERR(phy->pwr)) { + if (PTR_ERR(phy->pwr) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto free_ida; + } + phy->pwr = NULL; + } + device_initialize(&phy->dev); mutex_init(&phy->mutex); @@ -617,6 +639,9 @@ put_dev: put_device(&phy->dev); /* calls phy_release() which frees resources */ return ERR_PTR(ret); +free_ida: + ida_simple_remove(&phy_ida, phy->id); + free_phy: kfree(phy); return ERR_PTR(ret); @@ -800,6 +825,7 @@ static void phy_release(struct device *dev) phy = to_phy(dev); dev_vdbg(dev, "releasing '%s'\n", dev_name(dev)); + regulator_put(phy->pwr); ida_simple_remove(&phy_ida, phy->id); kfree(phy); } diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index 2760744cb2a7..9a8694524742 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -18,6 +18,7 @@ #include #include #include +#include struct phy; @@ -65,6 +66,7 @@ struct phy { int init_count; int power_count; struct phy_attrs attrs; + struct regulator *pwr; }; /** -- cgit v1.2.3 From f0ed817638b59aa927f1f7e9564dd8796b18dc4f Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jul 2014 15:55:02 +0530 Subject: phy: core: Let node ptr of PHY point to PHY and not of PHY provider In case of multi-phy PHY providers, each PHY should be modeled as a sub node of the PHY provider. Then each PHY will have a different node pointer (node pointer of sub node) than that of PHY provider. Added this provision in the PHY core. Also fixed all drivers to use the updated API. Signed-off-by: Kishon Vijay Abraham I Acked-by: Lee Jones --- Documentation/phy.txt | 10 ++++++---- drivers/phy/phy-bcm-kona-usb2.c | 2 +- drivers/phy/phy-berlin-sata.c | 2 +- drivers/phy/phy-core.c | 25 ++++++++++++++++++------- drivers/phy/phy-exynos-dp-video.c | 2 +- drivers/phy/phy-exynos-mipi-video.c | 2 +- drivers/phy/phy-exynos5-usbdrd.c | 3 ++- drivers/phy/phy-exynos5250-sata.c | 2 +- drivers/phy/phy-hix5hd2-sata.c | 2 +- drivers/phy/phy-mvebu-sata.c | 2 +- drivers/phy/phy-omap-usb2.c | 2 +- drivers/phy/phy-qcom-apq8064-sata.c | 3 ++- drivers/phy/phy-samsung-usb2.c | 3 ++- drivers/phy/phy-sun4i-usb.c | 2 +- drivers/phy/phy-ti-pipe3.c | 2 +- drivers/phy/phy-twl4030-usb.c | 2 +- drivers/phy/phy-xgene.c | 2 +- include/linux/phy/phy.h | 15 ++++++++++----- 18 files changed, 52 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/Documentation/phy.txt b/Documentation/phy.txt index ebff6ee52441..c6594af94d25 100644 --- a/Documentation/phy.txt +++ b/Documentation/phy.txt @@ -53,10 +53,12 @@ unregister the PHY. The PHY driver should create the PHY in order for other peripheral controllers to make use of it. The PHY framework provides 2 APIs to create the PHY. -struct phy *phy_create(struct device *dev, const struct phy_ops *ops, - struct phy_init_data *init_data); -struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops, - struct phy_init_data *init_data); +struct phy *phy_create(struct device *dev, struct device_node *node, + const struct phy_ops *ops, + struct phy_init_data *init_data); +struct phy *devm_phy_create(struct device *dev, struct device_node *node, + const struct phy_ops *ops, + struct phy_init_data *init_data); The PHY drivers can use one of the above 2 APIs to create the PHY by passing the device pointer, phy ops and init_data. diff --git a/drivers/phy/phy-bcm-kona-usb2.c b/drivers/phy/phy-bcm-kona-usb2.c index e94f5a6a5645..894fe74c1e44 100644 --- a/drivers/phy/phy-bcm-kona-usb2.c +++ b/drivers/phy/phy-bcm-kona-usb2.c @@ -117,7 +117,7 @@ static int bcm_kona_usb2_probe(struct platform_device *pdev) platform_set_drvdata(pdev, phy); - gphy = devm_phy_create(dev, &ops, NULL); + gphy = devm_phy_create(dev, NULL, &ops, NULL); if (IS_ERR(gphy)) return PTR_ERR(gphy); diff --git a/drivers/phy/phy-berlin-sata.c b/drivers/phy/phy-berlin-sata.c index c5e688b0899f..5c3a0424aeb4 100644 --- a/drivers/phy/phy-berlin-sata.c +++ b/drivers/phy/phy-berlin-sata.c @@ -239,7 +239,7 @@ static int phy_berlin_sata_probe(struct platform_device *pdev) if (!phy_desc) return -ENOMEM; - phy = devm_phy_create(dev, &phy_berlin_sata_ops, NULL); + phy = devm_phy_create(dev, NULL, &phy_berlin_sata_ops, NULL); if (IS_ERR(phy)) { dev_err(dev, "failed to create PHY %d\n", phy_id); return PTR_ERR(phy); diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index 527e744a3809..ff5eec5af817 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -415,13 +415,20 @@ struct phy *of_phy_simple_xlate(struct device *dev, struct of_phandle_args struct phy *phy; struct class_dev_iter iter; struct device_node *node = dev->of_node; + struct device_node *child; class_dev_iter_init(&iter, phy_class, NULL, NULL); while ((dev = class_dev_iter_next(&iter))) { phy = to_phy(dev); - if (node != phy->dev.of_node) + if (node != phy->dev.of_node) { + for_each_child_of_node(node, child) { + if (child == phy->dev.of_node) + goto phy_found; + } continue; + } +phy_found: class_dev_iter_exit(&iter); return phy; } @@ -579,13 +586,15 @@ EXPORT_SYMBOL_GPL(devm_of_phy_get); /** * phy_create() - create a new phy * @dev: device that is creating the new phy + * @node: device node of the phy * @ops: function pointers for performing phy operations * @init_data: contains the list of PHY consumers or NULL * * Called to create a phy using phy framework. */ -struct phy *phy_create(struct device *dev, const struct phy_ops *ops, - struct phy_init_data *init_data) +struct phy *phy_create(struct device *dev, struct device_node *node, + const struct phy_ops *ops, + struct phy_init_data *init_data) { int ret; int id; @@ -620,7 +629,7 @@ struct phy *phy_create(struct device *dev, const struct phy_ops *ops, phy->dev.class = phy_class; phy->dev.parent = dev; - phy->dev.of_node = dev->of_node; + phy->dev.of_node = node ?: dev->of_node; phy->id = id; phy->ops = ops; phy->init_data = init_data; @@ -656,6 +665,7 @@ EXPORT_SYMBOL_GPL(phy_create); /** * devm_phy_create() - create a new phy * @dev: device that is creating the new phy + * @node: device node of the phy * @ops: function pointers for performing phy operations * @init_data: contains the list of PHY consumers or NULL * @@ -664,8 +674,9 @@ EXPORT_SYMBOL_GPL(phy_create); * On driver detach, release function is invoked on the devres data, * then, devres data is freed. */ -struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops, - struct phy_init_data *init_data) +struct phy *devm_phy_create(struct device *dev, struct device_node *node, + const struct phy_ops *ops, + struct phy_init_data *init_data) { struct phy **ptr, *phy; @@ -673,7 +684,7 @@ struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops, if (!ptr) return ERR_PTR(-ENOMEM); - phy = phy_create(dev, ops, init_data); + phy = phy_create(dev, node, ops, init_data); if (!IS_ERR(phy)) { *ptr = phy; devres_add(dev, ptr); diff --git a/drivers/phy/phy-exynos-dp-video.c b/drivers/phy/phy-exynos-dp-video.c index 098f822a2fa4..8b3026e2af7f 100644 --- a/drivers/phy/phy-exynos-dp-video.c +++ b/drivers/phy/phy-exynos-dp-video.c @@ -77,7 +77,7 @@ static int exynos_dp_video_phy_probe(struct platform_device *pdev) if (IS_ERR(state->regs)) return PTR_ERR(state->regs); - phy = devm_phy_create(dev, &exynos_dp_video_phy_ops, NULL); + phy = devm_phy_create(dev, NULL, &exynos_dp_video_phy_ops, NULL); if (IS_ERR(phy)) { dev_err(dev, "failed to create Display Port PHY\n"); return PTR_ERR(phy); diff --git a/drivers/phy/phy-exynos-mipi-video.c b/drivers/phy/phy-exynos-mipi-video.c index 6d6bcf52a10e..b55a92e12496 100644 --- a/drivers/phy/phy-exynos-mipi-video.c +++ b/drivers/phy/phy-exynos-mipi-video.c @@ -136,7 +136,7 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev) spin_lock_init(&state->slock); for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) { - struct phy *phy = devm_phy_create(dev, + struct phy *phy = devm_phy_create(dev, NULL, &exynos_mipi_video_phy_ops, NULL); if (IS_ERR(phy)) { dev_err(dev, "failed to create PHY %d\n", i); diff --git a/drivers/phy/phy-exynos5-usbdrd.c b/drivers/phy/phy-exynos5-usbdrd.c index 205159db37a3..b05302b09c9f 100644 --- a/drivers/phy/phy-exynos5-usbdrd.c +++ b/drivers/phy/phy-exynos5-usbdrd.c @@ -635,7 +635,8 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev) dev_vdbg(dev, "Creating usbdrd_phy phy\n"); for (i = 0; i < EXYNOS5_DRDPHYS_NUM; i++) { - struct phy *phy = devm_phy_create(dev, &exynos5_usbdrd_phy_ops, + struct phy *phy = devm_phy_create(dev, NULL, + &exynos5_usbdrd_phy_ops, NULL); if (IS_ERR(phy)) { dev_err(dev, "Failed to create usbdrd_phy phy\n"); diff --git a/drivers/phy/phy-exynos5250-sata.c b/drivers/phy/phy-exynos5250-sata.c index 05689450f93b..19a679aca4ac 100644 --- a/drivers/phy/phy-exynos5250-sata.c +++ b/drivers/phy/phy-exynos5250-sata.c @@ -210,7 +210,7 @@ static int exynos_sata_phy_probe(struct platform_device *pdev) return ret; } - sata_phy->phy = devm_phy_create(dev, &exynos_sata_phy_ops, NULL); + sata_phy->phy = devm_phy_create(dev, NULL, &exynos_sata_phy_ops, NULL); if (IS_ERR(sata_phy->phy)) { clk_disable_unprepare(sata_phy->phyclk); dev_err(dev, "failed to create PHY\n"); diff --git a/drivers/phy/phy-hix5hd2-sata.c b/drivers/phy/phy-hix5hd2-sata.c index d44283453d71..6a08fa5f81eb 100644 --- a/drivers/phy/phy-hix5hd2-sata.c +++ b/drivers/phy/phy-hix5hd2-sata.c @@ -156,7 +156,7 @@ static int hix5hd2_sata_phy_probe(struct platform_device *pdev) if (IS_ERR(priv->peri_ctrl)) priv->peri_ctrl = NULL; - phy = devm_phy_create(dev, &hix5hd2_sata_phy_ops, NULL); + phy = devm_phy_create(dev, NULL, &hix5hd2_sata_phy_ops, NULL); if (IS_ERR(phy)) { dev_err(dev, "failed to create PHY\n"); return PTR_ERR(phy); diff --git a/drivers/phy/phy-mvebu-sata.c b/drivers/phy/phy-mvebu-sata.c index d70ecd6a1b3f..cc3c0e166daf 100644 --- a/drivers/phy/phy-mvebu-sata.c +++ b/drivers/phy/phy-mvebu-sata.c @@ -99,7 +99,7 @@ static int phy_mvebu_sata_probe(struct platform_device *pdev) if (IS_ERR(priv->clk)) return PTR_ERR(priv->clk); - phy = devm_phy_create(&pdev->dev, &phy_mvebu_sata_ops, NULL); + phy = devm_phy_create(&pdev->dev, NULL, &phy_mvebu_sata_ops, NULL); if (IS_ERR(phy)) return PTR_ERR(phy); diff --git a/drivers/phy/phy-omap-usb2.c b/drivers/phy/phy-omap-usb2.c index 34b396146c8a..93d78359246c 100644 --- a/drivers/phy/phy-omap-usb2.c +++ b/drivers/phy/phy-omap-usb2.c @@ -263,7 +263,7 @@ static int omap_usb2_probe(struct platform_device *pdev) platform_set_drvdata(pdev, phy); - generic_phy = devm_phy_create(phy->dev, &ops, NULL); + generic_phy = devm_phy_create(phy->dev, NULL, &ops, NULL); if (IS_ERR(generic_phy)) return PTR_ERR(generic_phy); diff --git a/drivers/phy/phy-qcom-apq8064-sata.c b/drivers/phy/phy-qcom-apq8064-sata.c index c9b4dd6becf5..d7c01aa1f8d7 100644 --- a/drivers/phy/phy-qcom-apq8064-sata.c +++ b/drivers/phy/phy-qcom-apq8064-sata.c @@ -228,7 +228,8 @@ static int qcom_apq8064_sata_phy_probe(struct platform_device *pdev) if (IS_ERR(phy->mmio)) return PTR_ERR(phy->mmio); - generic_phy = devm_phy_create(dev, &qcom_apq8064_sata_phy_ops, NULL); + generic_phy = devm_phy_create(dev, NULL, &qcom_apq8064_sata_phy_ops, + NULL); if (IS_ERR(generic_phy)) { dev_err(dev, "%s: failed to create phy\n", __func__); return PTR_ERR(generic_phy); diff --git a/drivers/phy/phy-samsung-usb2.c b/drivers/phy/phy-samsung-usb2.c index 16aae7a285f0..ae30640a411d 100644 --- a/drivers/phy/phy-samsung-usb2.c +++ b/drivers/phy/phy-samsung-usb2.c @@ -196,7 +196,8 @@ static int samsung_usb2_phy_probe(struct platform_device *pdev) struct samsung_usb2_phy_instance *p = &drv->instances[i]; dev_dbg(dev, "Creating phy \"%s\"\n", label); - p->phy = devm_phy_create(dev, &samsung_usb2_phy_ops, NULL); + p->phy = devm_phy_create(dev, NULL, &samsung_usb2_phy_ops, + NULL); if (IS_ERR(p->phy)) { dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n", label); diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c index 7a4ea552f621..61ebea49709b 100644 --- a/drivers/phy/phy-sun4i-usb.c +++ b/drivers/phy/phy-sun4i-usb.c @@ -295,7 +295,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) return PTR_ERR(phy->pmu); } - phy->phy = devm_phy_create(dev, &sun4i_usb_phy_ops, NULL); + phy->phy = devm_phy_create(dev, NULL, &sun4i_usb_phy_ops, NULL); if (IS_ERR(phy->phy)) { dev_err(dev, "failed to create PHY %d\n", i); return PTR_ERR(phy->phy); diff --git a/drivers/phy/phy-ti-pipe3.c b/drivers/phy/phy-ti-pipe3.c index 93bcd67f1b22..b964aa967b46 100644 --- a/drivers/phy/phy-ti-pipe3.c +++ b/drivers/phy/phy-ti-pipe3.c @@ -400,7 +400,7 @@ static int ti_pipe3_probe(struct platform_device *pdev) platform_set_drvdata(pdev, phy); pm_runtime_enable(phy->dev); - generic_phy = devm_phy_create(phy->dev, &ops, NULL); + generic_phy = devm_phy_create(phy->dev, NULL, &ops, NULL); if (IS_ERR(generic_phy)) return PTR_ERR(generic_phy); diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index 2e0e9b3774c8..e1a6623d4696 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -695,7 +695,7 @@ static int twl4030_usb_probe(struct platform_device *pdev) otg->set_host = twl4030_set_host; otg->set_peripheral = twl4030_set_peripheral; - phy = devm_phy_create(twl->dev, &ops, init_data); + phy = devm_phy_create(twl->dev, NULL, &ops, init_data); if (IS_ERR(phy)) { dev_dbg(&pdev->dev, "Failed to create PHY\n"); return PTR_ERR(phy); diff --git a/drivers/phy/phy-xgene.c b/drivers/phy/phy-xgene.c index 4aa1ccd1511f..db809b97219e 100644 --- a/drivers/phy/phy-xgene.c +++ b/drivers/phy/phy-xgene.c @@ -1707,7 +1707,7 @@ static int xgene_phy_probe(struct platform_device *pdev) ctx->dev = &pdev->dev; platform_set_drvdata(pdev, ctx); - ctx->phy = devm_phy_create(ctx->dev, &xgene_phy_ops, NULL); + ctx->phy = devm_phy_create(ctx->dev, NULL, &xgene_phy_ops, NULL); if (IS_ERR(ctx->phy)) { dev_dbg(&pdev->dev, "Failed to create PHY\n"); rc = PTR_ERR(ctx->phy); diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index 9a8694524742..8cb6f815475b 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -158,9 +158,10 @@ void devm_phy_put(struct device *dev, struct phy *phy); struct phy *of_phy_get(struct device_node *np, const char *con_id); struct phy *of_phy_simple_xlate(struct device *dev, struct of_phandle_args *args); -struct phy *phy_create(struct device *dev, const struct phy_ops *ops, - struct phy_init_data *init_data); -struct phy *devm_phy_create(struct device *dev, +struct phy *phy_create(struct device *dev, struct device_node *node, + const struct phy_ops *ops, + struct phy_init_data *init_data); +struct phy *devm_phy_create(struct device *dev, struct device_node *node, const struct phy_ops *ops, struct phy_init_data *init_data); void phy_destroy(struct phy *phy); void devm_phy_destroy(struct device *dev, struct phy *phy); @@ -299,13 +300,17 @@ static inline struct phy *of_phy_simple_xlate(struct device *dev, } static inline struct phy *phy_create(struct device *dev, - const struct phy_ops *ops, struct phy_init_data *init_data) + struct device_node *node, + const struct phy_ops *ops, + struct phy_init_data *init_data) { return ERR_PTR(-ENOSYS); } static inline struct phy *devm_phy_create(struct device *dev, - const struct phy_ops *ops, struct phy_init_data *init_data) + struct device_node *node, + const struct phy_ops *ops, + struct phy_init_data *init_data) { return ERR_PTR(-ENOSYS); } -- cgit v1.2.3 From d7afaec0b564f0609e116f562983b8e72fc3e9c9 Mon Sep 17 00:00:00 2001 From: Andrew Gallagher Date: Tue, 22 Jul 2014 16:37:43 +0200 Subject: fuse: add FUSE_NO_OPEN_SUPPORT flag to INIT Here some additional changes to set a capability flag so that clients can detect when it's appropriate to return -ENOSYS from open. This amends the following commit introduced in 3.14: 7678ac50615d fuse: support clients that don't implement 'open' However we can only add the flag to 3.15 and later since there was no protocol version update in 3.14. Signed-off-by: Miklos Szeredi Cc: # v3.15+ --- fs/fuse/inode.c | 2 +- include/uapi/linux/fuse.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 5ca874f2415b..03246cd9d47a 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -935,7 +935,7 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req) FUSE_SPLICE_WRITE | FUSE_SPLICE_MOVE | FUSE_SPLICE_READ | FUSE_FLOCK_LOCKS | FUSE_IOCTL_DIR | FUSE_AUTO_INVAL_DATA | FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO | FUSE_ASYNC_DIO | - FUSE_WRITEBACK_CACHE; + FUSE_WRITEBACK_CACHE | FUSE_NO_OPEN_SUPPORT; req->in.h.opcode = FUSE_INIT; req->in.numargs = 1; req->in.args[0].size = sizeof(*arg); diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index 40b5ca8a1b1f..25084a052a1e 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -101,6 +101,7 @@ * - add FATTR_CTIME * - add ctime and ctimensec to fuse_setattr_in * - add FUSE_RENAME2 request + * - add FUSE_NO_OPEN_SUPPORT flag */ #ifndef _LINUX_FUSE_H @@ -229,6 +230,7 @@ struct fuse_file_lock { * FUSE_READDIRPLUS_AUTO: adaptive readdirplus * FUSE_ASYNC_DIO: asynchronous direct I/O submission * FUSE_WRITEBACK_CACHE: use writeback cache for buffered writes + * FUSE_NO_OPEN_SUPPORT: kernel supports zero-message opens */ #define FUSE_ASYNC_READ (1 << 0) #define FUSE_POSIX_LOCKS (1 << 1) @@ -247,6 +249,7 @@ struct fuse_file_lock { #define FUSE_READDIRPLUS_AUTO (1 << 14) #define FUSE_ASYNC_DIO (1 << 15) #define FUSE_WRITEBACK_CACHE (1 << 16) +#define FUSE_NO_OPEN_SUPPORT (1 << 17) /** * CUSE INIT request/reply flags -- cgit v1.2.3 From 1190a419e0f89254b831fb2928e1d5d6efe6abe4 Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Tue, 22 Jul 2014 09:36:04 -0300 Subject: [media] v4l2-mem2mem: export v4l2_m2m_try_schedule Some drivers might allow to decode remaining frames from an internal ringbuffer after a decoder stop command. Allow those to call v4l2_m2m_try_schedule directly. Signed-off-by: Michael Olbrich Signed-off-by: Philipp Zabel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 3 ++- include/media/v4l2-mem2mem.h | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 97defed7c1ac..80c588f4e429 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -208,7 +208,7 @@ static void v4l2_m2m_try_run(struct v4l2_m2m_dev *m2m_dev) * An example of the above could be an instance that requires more than one * src/dst buffer per transaction. */ -static void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) +void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) { struct v4l2_m2m_dev *m2m_dev; unsigned long flags_job, flags_out, flags_cap; @@ -274,6 +274,7 @@ static void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) v4l2_m2m_try_run(m2m_dev); } +EXPORT_SYMBOL_GPL(v4l2_m2m_try_schedule); /** * v4l2_m2m_cancel_job() - cancel pending jobs for the context diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h index 12ea5a6a4331..c5f3914bc4d8 100644 --- a/include/media/v4l2-mem2mem.h +++ b/include/media/v4l2-mem2mem.h @@ -95,6 +95,8 @@ void *v4l2_m2m_get_curr_priv(struct v4l2_m2m_dev *m2m_dev); struct vb2_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type); +void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx); + void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev, struct v4l2_m2m_ctx *m2m_ctx); -- cgit v1.2.3 From 526cbef778ad8e763b95608d7e5c988b96bf4af5 Mon Sep 17 00:00:00 2001 From: David Laight Date: Tue, 22 Jul 2014 08:59:14 +0000 Subject: net: sctp: Rename SCTP_XMIT_NAGLE_DELAY to SCTP_XMIT_DELAY MSG_MORE and 'corking' a socket would require that the transmit of a data chunk be delayed. Rename the return value to be less specific. Signed-off-by: David Laight Acked-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/net/sctp/constants.h | 2 +- net/sctp/output.c | 4 ++-- net/sctp/outqueue.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h index 307728f622ef..8c337cd0e1e4 100644 --- a/include/net/sctp/constants.h +++ b/include/net/sctp/constants.h @@ -311,7 +311,7 @@ typedef enum { SCTP_XMIT_OK, SCTP_XMIT_PMTU_FULL, SCTP_XMIT_RWND_FULL, - SCTP_XMIT_NAGLE_DELAY, + SCTP_XMIT_DELAY, } sctp_xmit_t; /* These are the commands for manipulating transports. */ diff --git a/net/sctp/output.c b/net/sctp/output.c index f3bf0ae244df..1eedba5195a3 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -178,7 +178,7 @@ sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *packet, case SCTP_XMIT_RWND_FULL: case SCTP_XMIT_OK: - case SCTP_XMIT_NAGLE_DELAY: + case SCTP_XMIT_DELAY: break; } @@ -712,7 +712,7 @@ static sctp_xmit_t sctp_packet_can_append_data(struct sctp_packet *packet, return SCTP_XMIT_OK; /* Defer until all data acked or packet full */ - return SCTP_XMIT_NAGLE_DELAY; + return SCTP_XMIT_DELAY; } /* This private function does management things when adding DATA chunk */ diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index d31435e559b2..7e8f0a117106 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -629,7 +629,7 @@ redo: done = 1; break; - case SCTP_XMIT_NAGLE_DELAY: + case SCTP_XMIT_DELAY: /* Send this packet. */ error = sctp_packet_transmit(pkt); @@ -1015,7 +1015,7 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) switch (status) { case SCTP_XMIT_PMTU_FULL: case SCTP_XMIT_RWND_FULL: - case SCTP_XMIT_NAGLE_DELAY: + case SCTP_XMIT_DELAY: /* We could not append this chunk, so put * the chunk back on the output queue. */ -- cgit v1.2.3 From 0bec8c88dc2b076a0a4a0437e1e878026cbaccb4 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Tue, 22 Jul 2014 12:06:23 +0200 Subject: net: skbuff: Use ALIGN macro instead of open coding it Use ALIGN from linux/kernel.h to define SKB_DATA_ALIGN instead of open coding it. Signed-off-by: Tobias Klauser Signed-off-by: David S. Miller --- include/linux/skbuff.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 369430340ed9..b613557132b9 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -112,8 +112,7 @@ #define CHECKSUM_COMPLETE 2 #define CHECKSUM_PARTIAL 3 -#define SKB_DATA_ALIGN(X) (((X) + (SMP_CACHE_BYTES - 1)) & \ - ~(SMP_CACHE_BYTES - 1)) +#define SKB_DATA_ALIGN(X) ALIGN(X, SMP_CACHE_BYTES) #define SKB_WITH_OVERHEAD(X) \ ((X) - SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) #define SKB_MAX_ORDER(X, ORDER) \ -- cgit v1.2.3 From fc7c70e0b6b637bbf6cf8b9cee547d5ae83899c9 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 18 Jul 2014 18:56:34 +0100 Subject: KEYS: struct key_preparsed_payload should have two payload pointers struct key_preparsed_payload should have two payload pointers to correspond with those in struct key. Signed-off-by: David Howells Acked-by: Steve Dickson Acked-by: Jeff Layton Reviewed-by: Sage Weil --- crypto/asymmetric_keys/asymmetric_type.c | 2 +- crypto/asymmetric_keys/x509_public_key.c | 2 +- include/linux/key-type.h | 2 +- security/keys/encrypted-keys/encrypted.c | 2 +- security/keys/key.c | 6 ++++-- 5 files changed, 8 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index c1fe0fcee8e3..21960a4e74e8 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -156,7 +156,7 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep) pr_devel("==>%s()\n", __func__); if (subtype) { - subtype->destroy(prep->payload); + subtype->destroy(prep->payload[0]); module_put(subtype->owner); } kfree(prep->type_data[1]); diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 382ef0d2ff2e..3fc8a0634ed7 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -177,7 +177,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) __module_get(public_key_subtype.owner); prep->type_data[0] = &public_key_subtype; prep->type_data[1] = cert->fingerprint; - prep->payload = cert->pub; + prep->payload[0] = cert->pub; prep->description = desc; prep->quotalen = 100; diff --git a/include/linux/key-type.h b/include/linux/key-type.h index 88503dca2a57..d2b4845d74bf 100644 --- a/include/linux/key-type.h +++ b/include/linux/key-type.h @@ -41,7 +41,7 @@ struct key_construction { struct key_preparsed_payload { char *description; /* Proposed key description (or NULL) */ void *type_data[2]; /* Private key-type data */ - void *payload; /* Proposed payload */ + void *payload[2]; /* Proposed payload */ const void *data; /* Raw data */ size_t datalen; /* Raw datalen */ size_t quotalen; /* Quota length for proposed payload */ diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 5fe443d120af..d252c5704f8a 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -811,7 +811,7 @@ static int encrypted_instantiate(struct key *key, goto out; } - rcu_assign_keypointer(key, epayload); + prep->payload[0] = epayload; out: kfree(datablob); return ret; diff --git a/security/keys/key.c b/security/keys/key.c index 7c9acbf106b6..03620a35a4dc 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -1043,10 +1043,12 @@ int generic_key_instantiate(struct key *key, struct key_preparsed_payload *prep) if (ret == 0) { key->type_data.p[0] = prep->type_data[0]; key->type_data.p[1] = prep->type_data[1]; - rcu_assign_keypointer(key, prep->payload); + rcu_assign_keypointer(key, prep->payload[0]); + key->payload.data2[1] = prep->payload[1]; prep->type_data[0] = NULL; prep->type_data[1] = NULL; - prep->payload = NULL; + prep->payload[0] = NULL; + prep->payload[1] = NULL; } pr_devel("<==%s() = %d\n", __func__, ret); return ret; -- cgit v1.2.3 From 7dfa0ca6a95de65b7a7760630cdbd7d30f204bfa Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 18 Jul 2014 18:56:34 +0100 Subject: KEYS: Allow expiry time to be set when preparsing a key Allow a key type's preparsing routine to set the expiry time for a key. Signed-off-by: David Howells Acked-by: Steve Dickson Acked-by: Jeff Layton Reviewed-by: Sage Weil --- Documentation/security/keys.txt | 10 +++++++--- include/linux/key-type.h | 1 + security/keys/key.c | 8 ++++++++ 3 files changed, 16 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index a4c33f1a7c6d..315cf96a41a2 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -1150,20 +1150,24 @@ The structure has a number of fields, some of which are mandatory: const void *data; size_t datalen; size_t quotalen; + time_t expiry; }; Before calling the method, the caller will fill in data and datalen with the payload blob parameters; quotalen will be filled in with the default - quota size from the key type and the rest will be cleared. + quota size from the key type; expiry will be set to TIME_T_MAX and the + rest will be cleared. If a description can be proposed from the payload contents, that should be attached as a string to the description field. This will be used for the key description if the caller of add_key() passes NULL or "". The method can attach anything it likes to type_data[] and payload. These - are merely passed along to the instantiate() or update() operations. + are merely passed along to the instantiate() or update() operations. If + set, the expiry time will be applied to the key if it is instantiated from + this data. - The method should return 0 if success ful or a negative error code + The method should return 0 if successful or a negative error code otherwise. diff --git a/include/linux/key-type.h b/include/linux/key-type.h index d2b4845d74bf..44792ee649de 100644 --- a/include/linux/key-type.h +++ b/include/linux/key-type.h @@ -45,6 +45,7 @@ struct key_preparsed_payload { const void *data; /* Raw data */ size_t datalen; /* Raw datalen */ size_t quotalen; /* Quota length for proposed payload */ + time_t expiry; /* Expiry time of key */ bool trusted; /* True if key is trusted */ }; diff --git a/security/keys/key.c b/security/keys/key.c index 03620a35a4dc..755fb02df5af 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -437,6 +437,11 @@ static int __key_instantiate_and_link(struct key *key, /* disable the authorisation key */ if (authkey) key_revoke(authkey); + + if (prep->expiry != TIME_T_MAX) { + key->expiry = prep->expiry; + key_schedule_gc(prep->expiry + key_gc_delay); + } } } @@ -479,6 +484,7 @@ int key_instantiate_and_link(struct key *key, prep.data = data; prep.datalen = datalen; prep.quotalen = key->type->def_datalen; + prep.expiry = TIME_T_MAX; if (key->type->preparse) { ret = key->type->preparse(&prep); if (ret < 0) @@ -811,6 +817,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, prep.datalen = plen; prep.quotalen = index_key.type->def_datalen; prep.trusted = flags & KEY_ALLOC_TRUSTED; + prep.expiry = TIME_T_MAX; if (index_key.type->preparse) { ret = index_key.type->preparse(&prep); if (ret < 0) { @@ -941,6 +948,7 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen) prep.data = payload; prep.datalen = plen; prep.quotalen = key->type->def_datalen; + prep.expiry = TIME_T_MAX; if (key->type->preparse) { ret = key->type->preparse(&prep); if (ret < 0) -- cgit v1.2.3 From f9167789df53f22af771fb6690a3d36aa21d74c5 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 18 Jul 2014 18:56:35 +0100 Subject: KEYS: user: Use key preparsing Make use of key preparsing in user-defined and logon keys so that quota size determination can take place prior to keyring locking when a key is being added. Also the idmapper key types need to change to match as they use the user-defined key type routines. Signed-off-by: David Howells Acked-by: Steve Dickson Acked-by: Jeff Layton --- fs/nfs/idmap.c | 8 ++++++-- include/keys/user-type.h | 3 ++- security/keys/user_defined.c | 41 ++++++++++++++++++++++------------------- 3 files changed, 30 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index 567983d2c0eb..59b217a3266d 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -174,7 +174,9 @@ static int nfs_map_numeric_to_string(__u32 id, char *buf, size_t buflen) static struct key_type key_type_id_resolver = { .name = "id_resolver", - .instantiate = user_instantiate, + .preparse = user_preparse, + .free_preparse = user_free_preparse, + .instantiate = generic_key_instantiate, .match = user_match, .revoke = user_revoke, .destroy = user_destroy, @@ -394,7 +396,9 @@ static const struct rpc_pipe_ops idmap_upcall_ops = { static struct key_type key_type_id_resolver_legacy = { .name = "id_legacy", - .instantiate = user_instantiate, + .preparse = user_preparse, + .free_preparse = user_free_preparse, + .instantiate = generic_key_instantiate, .match = user_match, .revoke = user_revoke, .destroy = user_destroy, diff --git a/include/keys/user-type.h b/include/keys/user-type.h index 5e452c84f1e6..3ab1873a4bfa 100644 --- a/include/keys/user-type.h +++ b/include/keys/user-type.h @@ -37,7 +37,8 @@ extern struct key_type key_type_logon; struct key_preparsed_payload; -extern int user_instantiate(struct key *key, struct key_preparsed_payload *prep); +extern int user_preparse(struct key_preparsed_payload *prep); +extern void user_free_preparse(struct key_preparsed_payload *prep); extern int user_update(struct key *key, struct key_preparsed_payload *prep); extern int user_match(const struct key *key, const void *criterion); extern void user_revoke(struct key *key); diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index faa2caeb593f..eee340011f2b 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -27,7 +27,9 @@ static int logon_vet_description(const char *desc); struct key_type key_type_user = { .name = "user", .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, - .instantiate = user_instantiate, + .preparse = user_preparse, + .free_preparse = user_free_preparse, + .instantiate = generic_key_instantiate, .update = user_update, .match = user_match, .revoke = user_revoke, @@ -47,7 +49,9 @@ EXPORT_SYMBOL_GPL(key_type_user); struct key_type key_type_logon = { .name = "logon", .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, - .instantiate = user_instantiate, + .preparse = user_preparse, + .free_preparse = user_free_preparse, + .instantiate = generic_key_instantiate, .update = user_update, .match = user_match, .revoke = user_revoke, @@ -58,38 +62,37 @@ struct key_type key_type_logon = { EXPORT_SYMBOL_GPL(key_type_logon); /* - * instantiate a user defined key + * Preparse a user defined key payload */ -int user_instantiate(struct key *key, struct key_preparsed_payload *prep) +int user_preparse(struct key_preparsed_payload *prep) { struct user_key_payload *upayload; size_t datalen = prep->datalen; - int ret; - ret = -EINVAL; if (datalen <= 0 || datalen > 32767 || !prep->data) - goto error; - - ret = key_payload_reserve(key, datalen); - if (ret < 0) - goto error; + return -EINVAL; - ret = -ENOMEM; upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL); if (!upayload) - goto error; + return -ENOMEM; /* attach the data */ + prep->quotalen = datalen; + prep->payload[0] = upayload; upayload->datalen = datalen; memcpy(upayload->data, prep->data, datalen); - rcu_assign_keypointer(key, upayload); - ret = 0; - -error: - return ret; + return 0; } +EXPORT_SYMBOL_GPL(user_preparse); -EXPORT_SYMBOL_GPL(user_instantiate); +/* + * Free a preparse of a user defined key payload + */ +void user_free_preparse(struct key_preparsed_payload *prep) +{ + kfree(prep->payload[0]); +} +EXPORT_SYMBOL_GPL(user_free_preparse); /* * update a user defined key -- cgit v1.2.3 From 002edaf76f09af658241029817f5ef66f6bef5e4 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 18 Jul 2014 18:56:36 +0100 Subject: KEYS: big_key: Use key preparsing Make use of key preparsing in the big key type so that quota size determination can take place prior to keyring locking when a key is being added. Signed-off-by: David Howells Acked-by: Steve Dickson --- include/keys/big_key-type.h | 3 ++- security/keys/big_key.c | 41 +++++++++++++++++++++++++---------------- 2 files changed, 27 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/keys/big_key-type.h b/include/keys/big_key-type.h index d69bc8af3292..e0970a578188 100644 --- a/include/keys/big_key-type.h +++ b/include/keys/big_key-type.h @@ -16,7 +16,8 @@ extern struct key_type key_type_big_key; -extern int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep); +extern int big_key_preparse(struct key_preparsed_payload *prep); +extern void big_key_free_preparse(struct key_preparsed_payload *prep); extern void big_key_revoke(struct key *key); extern void big_key_destroy(struct key *key); extern void big_key_describe(const struct key *big_key, struct seq_file *m); diff --git a/security/keys/big_key.c b/security/keys/big_key.c index 8137b27d641d..c2f91a0cf889 100644 --- a/security/keys/big_key.c +++ b/security/keys/big_key.c @@ -34,7 +34,9 @@ MODULE_LICENSE("GPL"); struct key_type key_type_big_key = { .name = "big_key", .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, - .instantiate = big_key_instantiate, + .preparse = big_key_preparse, + .free_preparse = big_key_free_preparse, + .instantiate = generic_key_instantiate, .match = user_match, .revoke = big_key_revoke, .destroy = big_key_destroy, @@ -43,11 +45,11 @@ struct key_type key_type_big_key = { }; /* - * Instantiate a big key + * Preparse a big key */ -int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep) +int big_key_preparse(struct key_preparsed_payload *prep) { - struct path *path = (struct path *)&key->payload.data2; + struct path *path = (struct path *)&prep->payload; struct file *file; ssize_t written; size_t datalen = prep->datalen; @@ -58,11 +60,9 @@ int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep) goto error; /* Set an arbitrary quota */ - ret = key_payload_reserve(key, 16); - if (ret < 0) - goto error; + prep->quotalen = 16; - key->type_data.x[1] = datalen; + prep->type_data[1] = (void *)(unsigned long)datalen; if (datalen > BIG_KEY_FILE_THRESHOLD) { /* Create a shmem file to store the data in. This will permit the data @@ -73,7 +73,7 @@ int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep) file = shmem_kernel_file_setup("", datalen, 0); if (IS_ERR(file)) { ret = PTR_ERR(file); - goto err_quota; + goto error; } written = kernel_write(file, prep->data, prep->datalen, 0); @@ -93,23 +93,32 @@ int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep) } else { /* Just store the data in a buffer */ void *data = kmalloc(datalen, GFP_KERNEL); - if (!data) { - ret = -ENOMEM; - goto err_quota; - } + if (!data) + return -ENOMEM; - key->payload.data = memcpy(data, prep->data, prep->datalen); + prep->payload[0] = memcpy(data, prep->data, prep->datalen); } return 0; err_fput: fput(file); -err_quota: - key_payload_reserve(key, 0); error: return ret; } +/* + * Clear preparsement. + */ +void big_key_free_preparse(struct key_preparsed_payload *prep) +{ + if (prep->datalen > BIG_KEY_FILE_THRESHOLD) { + struct path *path = (struct path *)&prep->payload; + path_put(path); + } else { + kfree(prep->payload[0]); + } +} + /* * dispose of the links from a revoked keyring * - called with the key sem write-locked -- cgit v1.2.3 From 9dae3d0d9e64c3cb8bb172f041d4e66d4b92088a Mon Sep 17 00:00:00 2001 From: Tomasz Nowicki Date: Tue, 22 Jul 2014 11:20:11 +0200 Subject: apei, mce: Factor out APEI architecture specific MCE calls. This commit abstracts MCE calls and provides weak corresponding default implementation for those architectures which do not need arch specific actions. Each platform willing to do additional architectural actions should provides desired function definition. It allows us to avoid wrap code into #ifdef in generic code and prevent new platform from introducing dummy stub function too. Initially, there are two APEI arch-specific calls: - arch_apei_enable_cmcff() - arch_apei_report_mem_error() Both interact with MCE driver for X86 architecture. Signed-off-by: Tomasz Nowicki Acked-by: Borislav Petkov Signed-off-by: Tony Luck --- arch/x86/kernel/acpi/Makefile | 1 + arch/x86/kernel/acpi/apei.c | 56 +++++++++++++++++++++++++++++++++++++++++++ drivers/acpi/apei/apei-base.c | 13 ++++++++++ drivers/acpi/apei/ghes.c | 6 ++--- drivers/acpi/apei/hest.c | 29 +--------------------- include/acpi/apei.h | 3 +++ 6 files changed, 76 insertions(+), 32 deletions(-) create mode 100644 arch/x86/kernel/acpi/apei.c (limited to 'include') diff --git a/arch/x86/kernel/acpi/Makefile b/arch/x86/kernel/acpi/Makefile index 163b22581472..3242e591fa82 100644 --- a/arch/x86/kernel/acpi/Makefile +++ b/arch/x86/kernel/acpi/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_ACPI) += boot.o obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup_$(BITS).o +obj-$(CONFIG_ACPI_APEI) += apei.o ifneq ($(CONFIG_ACPI_PROCESSOR),) obj-y += cstate.o diff --git a/arch/x86/kernel/acpi/apei.c b/arch/x86/kernel/acpi/apei.c new file mode 100644 index 000000000000..12b13de55255 --- /dev/null +++ b/arch/x86/kernel/acpi/apei.c @@ -0,0 +1,56 @@ +/* + * Arch-specific APEI-related functions. + * + * 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 + +int arch_apei_enable_cmcff(struct acpi_hest_header *hest_hdr, void *data) +{ +#ifdef CONFIG_X86_MCE + int i; + struct acpi_hest_ia_corrected *cmc; + struct acpi_hest_ia_error_bank *mc_bank; + + if (hest_hdr->type != ACPI_HEST_TYPE_IA32_CORRECTED_CHECK) + return 0; + + cmc = (struct acpi_hest_ia_corrected *)hest_hdr; + if (!cmc->enabled) + return 0; + + /* + * We expect HEST to provide a list of MC banks that report errors + * in firmware first mode. Otherwise, return non-zero value to + * indicate that we are done parsing HEST. + */ + if (!(cmc->flags & ACPI_HEST_FIRMWARE_FIRST) || + !cmc->num_hardware_banks) + return 1; + + pr_info("HEST: Enabling Firmware First mode for corrected errors.\n"); + + mc_bank = (struct acpi_hest_ia_error_bank *)(cmc + 1); + for (i = 0; i < cmc->num_hardware_banks; i++, mc_bank++) + mce_disable_bank(mc_bank->bank_number); +#endif + return 1; +} + +void arch_apei_report_mem_error(int sev, struct cper_sec_mem_err *mem_err) +{ +#ifdef CONFIG_X86_MCE + apei_mce_report_mem_error(sev, mem_err); +#endif +} diff --git a/drivers/acpi/apei/apei-base.c b/drivers/acpi/apei/apei-base.c index 8678dfe5366b..2cd7bdd6c8b3 100644 --- a/drivers/acpi/apei/apei-base.c +++ b/drivers/acpi/apei/apei-base.c @@ -745,6 +745,19 @@ struct dentry *apei_get_debugfs_dir(void) } EXPORT_SYMBOL_GPL(apei_get_debugfs_dir); +int __weak arch_apei_enable_cmcff(struct acpi_hest_header *hest_hdr, + void *data) +{ + return 1; +} +EXPORT_SYMBOL_GPL(arch_apei_enable_cmcff); + +void __weak arch_apei_report_mem_error(int sev, + struct cper_sec_mem_err *mem_err) +{ +} +EXPORT_SYMBOL_GPL(arch_apei_report_mem_error); + int apei_osc_setup(void) { static u8 whea_uuid_str[] = "ed855e0c-6c90-47bf-a62a-26de0fc5ad5c"; diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index dab7cb7349df..352170a74a2e 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -49,7 +49,7 @@ #include #include -#include +#include #include #include @@ -455,9 +455,7 @@ static void ghes_do_proc(struct ghes *ghes, mem_err = (struct cper_sec_mem_err *)(gdata+1); ghes_edac_report_mem_error(ghes, sev, mem_err); -#ifdef CONFIG_X86_MCE - apei_mce_report_mem_error(sev, mem_err); -#endif + arch_apei_report_mem_error(sev, mem_err); ghes_handle_memory_failure(gdata, sev); } #ifdef CONFIG_ACPI_APEI_PCIEAER diff --git a/drivers/acpi/apei/hest.c b/drivers/acpi/apei/hest.c index f5e37f32c71f..06e9b411a0a2 100644 --- a/drivers/acpi/apei/hest.c +++ b/drivers/acpi/apei/hest.c @@ -36,7 +36,6 @@ #include #include #include -#include #include "apei-internal.h" @@ -128,33 +127,7 @@ EXPORT_SYMBOL_GPL(apei_hest_parse); */ static int __init hest_parse_cmc(struct acpi_hest_header *hest_hdr, void *data) { -#ifdef CONFIG_X86_MCE - int i; - struct acpi_hest_ia_corrected *cmc; - struct acpi_hest_ia_error_bank *mc_bank; - - if (hest_hdr->type != ACPI_HEST_TYPE_IA32_CORRECTED_CHECK) - return 0; - - cmc = (struct acpi_hest_ia_corrected *)hest_hdr; - if (!cmc->enabled) - return 0; - - /* - * We expect HEST to provide a list of MC banks that report errors - * in firmware first mode. Otherwise, return non-zero value to - * indicate that we are done parsing HEST. - */ - if (!(cmc->flags & ACPI_HEST_FIRMWARE_FIRST) || !cmc->num_hardware_banks) - return 1; - - pr_info(HEST_PFX "Enabling Firmware First mode for corrected errors.\n"); - - mc_bank = (struct acpi_hest_ia_error_bank *)(cmc + 1); - for (i = 0; i < cmc->num_hardware_banks; i++, mc_bank++) - mce_disable_bank(mc_bank->bank_number); -#endif - return 1; + return arch_apei_enable_cmcff(hest_hdr, data); } struct ghes_arr { diff --git a/include/acpi/apei.h b/include/acpi/apei.h index 04f349d8da73..8a23c95109c6 100644 --- a/include/acpi/apei.h +++ b/include/acpi/apei.h @@ -42,5 +42,8 @@ ssize_t erst_read(u64 record_id, struct cper_record_header *record, size_t buflen); int erst_clear(u64 record_id); +int arch_apei_enable_cmcff(struct acpi_hest_header *hest_hdr, void *data); +void arch_apei_report_mem_error(int sev, struct cper_sec_mem_err *mem_err); + #endif #endif -- cgit v1.2.3 From 44a69f6195628f6f940566d133a72987559e102d Mon Sep 17 00:00:00 2001 From: Tomasz Nowicki Date: Tue, 22 Jul 2014 11:20:12 +0200 Subject: acpi, apei, ghes: Make NMI error notification to be GHES architecture extension. Currently APEI depends on x86 architecture. It is because of NMI hardware error notification of GHES which is currently supported by x86 only. However, many other APEI features can be still used perfectly by other architectures. This commit adds two symbols: 1. HAVE_ACPI_APEI for those archs which support APEI. 2. HAVE_ACPI_APEI_NMI which is used for NMI code isolation in ghes.c file. NMI related data and functions are grouped so they can be wrapped inside one #ifdef section. Appropriate function stubs are provided for !NMI case. Note there is no functional changes for x86 due to hard selected HAVE_ACPI_APEI and HAVE_ACPI_APEI_NMI symbols. Signed-off-by: Tomasz Nowicki Acked-by: Borislav Petkov Signed-off-by: Tony Luck --- arch/x86/Kconfig | 2 + drivers/acpi/apei/Kconfig | 8 ++- drivers/acpi/apei/ghes.c | 149 ++++++++++++++++++++++++++++++---------------- include/linux/nmi.h | 4 ++ 4 files changed, 110 insertions(+), 53 deletions(-) (limited to 'include') diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index d24887b645dc..43873442dee1 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -132,6 +132,8 @@ config X86 select GENERIC_CPU_AUTOPROBE select HAVE_ARCH_AUDITSYSCALL select ARCH_SUPPORTS_ATOMIC_RMW + select HAVE_ACPI_APEI if ACPI + select HAVE_ACPI_APEI_NMI if ACPI config INSTRUCTION_DECODER def_bool y diff --git a/drivers/acpi/apei/Kconfig b/drivers/acpi/apei/Kconfig index c4dac7150960..b0140c8fc733 100644 --- a/drivers/acpi/apei/Kconfig +++ b/drivers/acpi/apei/Kconfig @@ -1,9 +1,15 @@ +config HAVE_ACPI_APEI + bool + +config HAVE_ACPI_APEI_NMI + bool + config ACPI_APEI bool "ACPI Platform Error Interface (APEI)" select MISC_FILESYSTEMS select PSTORE select UEFI_CPER - depends on X86 + depends on HAVE_ACPI_APEI help APEI allows to report errors (for example from the chipset) to the operating system. This improves NMI handling diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 352170a74a2e..7fcf4d7b41f6 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -47,11 +47,11 @@ #include #include #include +#include #include #include #include -#include #include "apei-internal.h" @@ -86,8 +86,6 @@ bool ghes_disable; module_param_named(disable, ghes_disable, bool, 0); -static int ghes_panic_timeout __read_mostly = 30; - /* * All error sources notified with SCI shares one notifier function, * so they need to be linked and checked one by one. This is applied @@ -97,15 +95,8 @@ static int ghes_panic_timeout __read_mostly = 30; * list changing, not for traversing. */ static LIST_HEAD(ghes_sci); -static LIST_HEAD(ghes_nmi); static DEFINE_MUTEX(ghes_list_mutex); -/* - * NMI may be triggered on any CPU, so ghes_nmi_lock is used for - * mutual exclusion. - */ -static DEFINE_RAW_SPINLOCK(ghes_nmi_lock); - /* * Because the memory area used to transfer hardware error information * from BIOS to Linux can be determined only in NMI, IRQ or timer @@ -130,18 +121,8 @@ static struct vm_struct *ghes_ioremap_area; static DEFINE_RAW_SPINLOCK(ghes_ioremap_lock_nmi); static DEFINE_SPINLOCK(ghes_ioremap_lock_irq); -/* - * printk is not safe in NMI context. So in NMI handler, we allocate - * required memory from lock-less memory allocator - * (ghes_estatus_pool), save estatus into it, put them into lock-less - * list (ghes_estatus_llist), then delay printk into IRQ context via - * irq_work (ghes_proc_irq_work). ghes_estatus_size_request record - * required pool size by all NMI error source. - */ static struct gen_pool *ghes_estatus_pool; static unsigned long ghes_estatus_pool_size_request; -static struct llist_head ghes_estatus_llist; -static struct irq_work ghes_proc_irq_work; struct ghes_estatus_cache *ghes_estatus_caches[GHES_ESTATUS_CACHES_SIZE]; static atomic_t ghes_estatus_cache_alloced; @@ -249,11 +230,6 @@ static int ghes_estatus_pool_expand(unsigned long len) return 0; } -static void ghes_estatus_pool_shrink(unsigned long len) -{ - ghes_estatus_pool_size_request -= PAGE_ALIGN(len); -} - static struct ghes *ghes_new(struct acpi_hest_generic *generic) { struct ghes *ghes; @@ -732,6 +708,32 @@ static int ghes_notify_sci(struct notifier_block *this, return ret; } +static struct notifier_block ghes_notifier_sci = { + .notifier_call = ghes_notify_sci, +}; + +#ifdef CONFIG_HAVE_ACPI_APEI_NMI +/* + * printk is not safe in NMI context. So in NMI handler, we allocate + * required memory from lock-less memory allocator + * (ghes_estatus_pool), save estatus into it, put them into lock-less + * list (ghes_estatus_llist), then delay printk into IRQ context via + * irq_work (ghes_proc_irq_work). ghes_estatus_size_request record + * required pool size by all NMI error source. + */ +static struct llist_head ghes_estatus_llist; +static struct irq_work ghes_proc_irq_work; + +/* + * NMI may be triggered on any CPU, so ghes_nmi_lock is used for + * mutual exclusion. + */ +static DEFINE_RAW_SPINLOCK(ghes_nmi_lock); + +static LIST_HEAD(ghes_nmi); + +static int ghes_panic_timeout __read_mostly = 30; + static struct llist_node *llist_nodes_reverse(struct llist_node *llnode) { struct llist_node *next, *tail = NULL; @@ -875,10 +877,6 @@ out: return ret; } -static struct notifier_block ghes_notifier_sci = { - .notifier_call = ghes_notify_sci, -}; - static unsigned long ghes_esource_prealloc_size( const struct acpi_hest_generic *generic) { @@ -894,11 +892,71 @@ static unsigned long ghes_esource_prealloc_size( return prealloc_size; } +static void ghes_estatus_pool_shrink(unsigned long len) +{ + ghes_estatus_pool_size_request -= PAGE_ALIGN(len); +} + +static void ghes_nmi_add(struct ghes *ghes) +{ + unsigned long len; + + len = ghes_esource_prealloc_size(ghes->generic); + ghes_estatus_pool_expand(len); + mutex_lock(&ghes_list_mutex); + if (list_empty(&ghes_nmi)) + register_nmi_handler(NMI_LOCAL, ghes_notify_nmi, 0, "ghes"); + list_add_rcu(&ghes->list, &ghes_nmi); + mutex_unlock(&ghes_list_mutex); +} + +static void ghes_nmi_remove(struct ghes *ghes) +{ + unsigned long len; + + mutex_lock(&ghes_list_mutex); + list_del_rcu(&ghes->list); + if (list_empty(&ghes_nmi)) + unregister_nmi_handler(NMI_LOCAL, "ghes"); + mutex_unlock(&ghes_list_mutex); + /* + * To synchronize with NMI handler, ghes can only be + * freed after NMI handler finishes. + */ + synchronize_rcu(); + len = ghes_esource_prealloc_size(ghes->generic); + ghes_estatus_pool_shrink(len); +} + +static void ghes_nmi_init_cxt(void) +{ + init_irq_work(&ghes_proc_irq_work, ghes_proc_in_irq); +} +#else /* CONFIG_HAVE_ACPI_APEI_NMI */ +static inline void ghes_nmi_add(struct ghes *ghes) +{ + pr_err(GHES_PFX "ID: %d, trying to add NMI notification which is not supported!\n", + ghes->generic->header.source_id); + BUG(); +} + +static inline void ghes_nmi_remove(struct ghes *ghes) +{ + pr_err(GHES_PFX "ID: %d, trying to remove NMI notification which is not supported!\n", + ghes->generic->header.source_id); + BUG(); +} + +static inline void ghes_nmi_init_cxt(void) +{ +} +#endif /* CONFIG_HAVE_ACPI_APEI_NMI */ + static int ghes_probe(struct platform_device *ghes_dev) { struct acpi_hest_generic *generic; struct ghes *ghes = NULL; - unsigned long len; + int rc = -EINVAL; generic = *(struct acpi_hest_generic **)ghes_dev->dev.platform_data; @@ -909,7 +967,13 @@ static int ghes_probe(struct platform_device *ghes_dev) case ACPI_HEST_NOTIFY_POLLED: case ACPI_HEST_NOTIFY_EXTERNAL: case ACPI_HEST_NOTIFY_SCI: + break; case ACPI_HEST_NOTIFY_NMI: + if (!IS_ENABLED(CONFIG_HAVE_ACPI_APEI_NMI)) { + pr_warn(GHES_PFX "Generic hardware error source: %d notified via NMI interrupt is not supported!\n", + generic->header.source_id); + goto err; + } break; case ACPI_HEST_NOTIFY_LOCAL: pr_warning(GHES_PFX "Generic hardware error source: %d notified via local interrupt is not supported!\n", @@ -970,14 +1034,7 @@ static int ghes_probe(struct platform_device *ghes_dev) mutex_unlock(&ghes_list_mutex); break; case ACPI_HEST_NOTIFY_NMI: - len = ghes_esource_prealloc_size(generic); - ghes_estatus_pool_expand(len); - mutex_lock(&ghes_list_mutex); - if (list_empty(&ghes_nmi)) - register_nmi_handler(NMI_LOCAL, ghes_notify_nmi, 0, - "ghes"); - list_add_rcu(&ghes->list, &ghes_nmi); - mutex_unlock(&ghes_list_mutex); + ghes_nmi_add(ghes); break; default: BUG(); @@ -999,7 +1056,6 @@ static int ghes_remove(struct platform_device *ghes_dev) { struct ghes *ghes; struct acpi_hest_generic *generic; - unsigned long len; ghes = platform_get_drvdata(ghes_dev); generic = ghes->generic; @@ -1020,18 +1076,7 @@ static int ghes_remove(struct platform_device *ghes_dev) mutex_unlock(&ghes_list_mutex); break; case ACPI_HEST_NOTIFY_NMI: - mutex_lock(&ghes_list_mutex); - list_del_rcu(&ghes->list); - if (list_empty(&ghes_nmi)) - unregister_nmi_handler(NMI_LOCAL, "ghes"); - mutex_unlock(&ghes_list_mutex); - /* - * To synchronize with NMI handler, ghes can only be - * freed after NMI handler finishes. - */ - synchronize_rcu(); - len = ghes_esource_prealloc_size(generic); - ghes_estatus_pool_shrink(len); + ghes_nmi_remove(ghes); break; default: BUG(); @@ -1075,7 +1120,7 @@ static int __init ghes_init(void) return -EINVAL; } - init_irq_work(&ghes_proc_irq_work, ghes_proc_in_irq); + ghes_nmi_init_cxt(); rc = ghes_ioremap_init(); if (rc) diff --git a/include/linux/nmi.h b/include/linux/nmi.h index 447775ee2c4b..1d2a6ab6b8bb 100644 --- a/include/linux/nmi.h +++ b/include/linux/nmi.h @@ -63,4 +63,8 @@ extern int proc_dowatchdog(struct ctl_table *, int , void __user *, size_t *, loff_t *); #endif +#ifdef CONFIG_HAVE_ACPI_APEI_NMI +#include +#endif + #endif -- cgit v1.2.3 From 594c7255dce7a13cac50cf2470cc56e2c3b0494e Mon Sep 17 00:00:00 2001 From: Tomasz Nowicki Date: Tue, 22 Jul 2014 11:20:13 +0200 Subject: acpi, apei, ghes: Factor out ioremap virtual memory for IRQ and NMI context. GHES currently maps two pages with atomic_ioremap. From now on, NMI is architectural depended so there is no need to allocate an NMI page for platforms without NMI support. To make it possible to not use a second page, swap the existing page order so that the IRQ context page is first, and the optional NMI context page is second. Then, use HAVE_ACPI_APEI_NMI to decide how many pages are to be allocated. Signed-off-by: Tomasz Nowicki Acked-by: Borislav Petkov Signed-off-by: Tony Luck --- arch/x86/kernel/acpi/apei.c | 6 ++++++ drivers/acpi/apei/ghes.c | 18 +++++++++++------- include/acpi/apei.h | 1 + 3 files changed, 18 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/arch/x86/kernel/acpi/apei.c b/arch/x86/kernel/acpi/apei.c index 12b13de55255..c280df6b2aa2 100644 --- a/arch/x86/kernel/acpi/apei.c +++ b/arch/x86/kernel/acpi/apei.c @@ -15,6 +15,7 @@ #include #include +#include int arch_apei_enable_cmcff(struct acpi_hest_header *hest_hdr, void *data) { @@ -54,3 +55,8 @@ void arch_apei_report_mem_error(int sev, struct cper_sec_mem_err *mem_err) apei_mce_report_mem_error(sev, mem_err); #endif } + +void arch_apei_flush_tlb_one(unsigned long addr) +{ + __flush_tlb_one(addr); +} diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 7fcf4d7b41f6..e05d84e7b06d 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -105,12 +105,16 @@ static DEFINE_MUTEX(ghes_list_mutex); */ /* - * Two virtual pages are used, one for NMI context, the other for - * IRQ/PROCESS context + * Two virtual pages are used, one for IRQ/PROCESS context, the other for + * NMI context (optionally). */ -#define GHES_IOREMAP_PAGES 2 -#define GHES_IOREMAP_NMI_PAGE(base) (base) -#define GHES_IOREMAP_IRQ_PAGE(base) ((base) + PAGE_SIZE) +#ifdef CONFIG_HAVE_ACPI_APEI_NMI +#define GHES_IOREMAP_PAGES 2 +#else +#define GHES_IOREMAP_PAGES 1 +#endif +#define GHES_IOREMAP_IRQ_PAGE(base) (base) +#define GHES_IOREMAP_NMI_PAGE(base) ((base) + PAGE_SIZE) /* virtual memory area for atomic ioremap */ static struct vm_struct *ghes_ioremap_area; @@ -173,7 +177,7 @@ static void ghes_iounmap_nmi(void __iomem *vaddr_ptr) BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_NMI_PAGE(base)); unmap_kernel_range_noflush(vaddr, PAGE_SIZE); - __flush_tlb_one(vaddr); + arch_apei_flush_tlb_one(vaddr); } static void ghes_iounmap_irq(void __iomem *vaddr_ptr) @@ -183,7 +187,7 @@ static void ghes_iounmap_irq(void __iomem *vaddr_ptr) BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_IRQ_PAGE(base)); unmap_kernel_range_noflush(vaddr, PAGE_SIZE); - __flush_tlb_one(vaddr); + arch_apei_flush_tlb_one(vaddr); } static int ghes_estatus_pool_init(void) diff --git a/include/acpi/apei.h b/include/acpi/apei.h index 8a23c95109c6..76284bb560a6 100644 --- a/include/acpi/apei.h +++ b/include/acpi/apei.h @@ -44,6 +44,7 @@ int erst_clear(u64 record_id); int arch_apei_enable_cmcff(struct acpi_hest_header *hest_hdr, void *data); void arch_apei_report_mem_error(int sev, struct cper_sec_mem_err *mem_err); +void arch_apei_flush_tlb_one(unsigned long addr); #endif #endif -- cgit v1.2.3 From 9898e1ccf5af70ad2d03d5b77c591fb243c0f021 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 17 Jul 2014 22:01:05 +0200 Subject: ASoC: Remove per card platform list The platform_dev_list was added in commit f0fba2ad1b ("ASoC: multi-component - ASoC Multi-Component Support") and while platforms are added and remove from that list it is otherwise unused. This patch removes it again. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 3 --- sound/soc/soc-core.c | 2 -- 2 files changed, 5 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 98555f833ab4..1f5b4901415e 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -865,7 +865,6 @@ struct snd_soc_platform { struct snd_soc_card *card; struct list_head list; - struct list_head card_list; struct snd_soc_component component; @@ -1056,7 +1055,6 @@ struct snd_soc_card { /* lists of probed devices belonging to this card */ struct list_head codec_dev_list; - struct list_head platform_dev_list; struct list_head widgets; struct list_head paths; @@ -1299,7 +1297,6 @@ static inline void *snd_soc_pcm_get_drvdata(struct snd_soc_pcm_runtime *rtd) static inline void snd_soc_initialize_card_lists(struct snd_soc_card *card) { INIT_LIST_HEAD(&card->codec_dev_list); - INIT_LIST_HEAD(&card->platform_dev_list); INIT_LIST_HEAD(&card->widgets); INIT_LIST_HEAD(&card->paths); INIT_LIST_HEAD(&card->dapm_list); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 6e47610f73a0..a5edb31ddfff 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1027,7 +1027,6 @@ static int soc_remove_platform(struct snd_soc_platform *platform) soc_cleanup_platform_debugfs(platform); platform->probed = 0; - list_del(&platform->card_list); module_put(platform->dev->driver->owner); return 0; @@ -1297,7 +1296,6 @@ static int soc_probe_platform(struct snd_soc_card *card, /* mark platform as probed and add to card platform list */ platform->probed = 1; - list_add(&platform->card_list, &card->platform_dev_list); list_add(&platform->component.dapm.list, &card->dapm_list); return 0; -- cgit v1.2.3 From 00200107a296cad3a950049a5ad7134a0d962b8d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 17 Jul 2014 22:01:07 +0200 Subject: ASoC: Move card field form platform/codec to component Both the snd_soc_codec and snd_soc_platform struct do have a pointer to the parent card and both handle this pointer in mostly the same way. This patch moves the card field to the component level which will allow further code consolidation between platforms and CODECS. Since there are only a handful of users of the snd_soc_codec struct's card field (and none of the snd_soc_platform's) these are update in this patch as well, which allows it to be removed from the snd_soc_codec struct. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 3 +-- sound/soc/codecs/ac97.c | 4 ++-- sound/soc/codecs/cx20442.c | 6 +++--- sound/soc/codecs/uda134x.c | 2 +- sound/soc/codecs/wm8960.c | 2 +- sound/soc/codecs/wm_adsp.c | 4 ++-- sound/soc/omap/ams-delta.c | 2 +- sound/soc/soc-core.c | 19 ++++++++++--------- sound/soc/soc-dapm.c | 2 +- sound/soc/soc-jack.c | 4 ++-- 10 files changed, 24 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 1f5b4901415e..5ee6ddde4831 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -691,6 +691,7 @@ struct snd_soc_component { int id; const char *name_prefix; struct device *dev; + struct snd_soc_card *card; unsigned int active; @@ -725,7 +726,6 @@ struct snd_soc_codec { const struct snd_soc_codec_driver *driver; struct mutex mutex; - struct snd_soc_card *card; struct list_head list; struct list_head card_list; @@ -863,7 +863,6 @@ struct snd_soc_platform { unsigned int suspended:1; /* platform is suspended */ unsigned int probed:1; - struct snd_soc_card *card; struct list_head list; struct snd_soc_component component; diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c index 8d9ba4ba4bfe..e889e1b84192 100644 --- a/sound/soc/codecs/ac97.c +++ b/sound/soc/codecs/ac97.c @@ -89,8 +89,8 @@ static int ac97_soc_probe(struct snd_soc_codec *codec) int ret; /* add codec as bus device for standard ac97 */ - ret = snd_ac97_bus(codec->card->snd_card, 0, soc_ac97_ops, NULL, - &ac97_bus); + ret = snd_ac97_bus(codec->component.card->snd_card, 0, soc_ac97_ops, + NULL, &ac97_bus); if (ret < 0) return ret; diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c index d5fd00a64748..4ba60eb2cde1 100644 --- a/sound/soc/codecs/cx20442.c +++ b/sound/soc/codecs/cx20442.c @@ -253,7 +253,7 @@ static void v253_close(struct tty_struct *tty) /* Prevent the codec driver from further accessing the modem */ codec->hw_write = NULL; cx20442->control_data = NULL; - codec->card->pop_time = 0; + codec->component.card->pop_time = 0; } /* Line discipline .hangup() */ @@ -281,7 +281,7 @@ static void v253_receive(struct tty_struct *tty, /* Set up codec driver access to modem controls */ cx20442->control_data = tty; codec->hw_write = (hw_write_t)tty->ops->write; - codec->card->pop_time = 1; + codec->component.card->pop_time = 1; } } @@ -372,7 +372,7 @@ static int cx20442_codec_probe(struct snd_soc_codec *codec) snd_soc_codec_set_drvdata(codec, cx20442); codec->hw_write = NULL; - codec->card->pop_time = 0; + codec->component.card->pop_time = 0; return 0; } diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c index edf27acc1d77..12fc0aed7503 100644 --- a/sound/soc/codecs/uda134x.c +++ b/sound/soc/codecs/uda134x.c @@ -479,7 +479,7 @@ static struct snd_soc_dai_driver uda134x_dai = { static int uda134x_soc_probe(struct snd_soc_codec *codec) { struct uda134x_priv *uda134x; - struct uda134x_platform_data *pd = codec->card->dev->platform_data; + struct uda134x_platform_data *pd = codec->component.card->dev->platform_data; const struct snd_soc_dapm_widget *widgets; unsigned num_widgets; diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index a145d0431b63..e96349b04ba6 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -472,7 +472,7 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec) * list each time to find the desired power state do so now * and save the result. */ - list_for_each_entry(w, &codec->card->widgets, list) { + list_for_each_entry(w, &codec->component.card->widgets, list) { if (w->dapm != &codec->dapm) continue; if (strcmp(w->name, "LOUT1 PGA") == 0) diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 060027182dcb..fb86f072fa67 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -1382,7 +1382,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, int ret; int val; - dsp->card = codec->card; + dsp->card = codec->component.card; switch (event) { case SND_SOC_DAPM_POST_PMU: @@ -1617,7 +1617,7 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); struct wm_adsp *dsp = &dsps[w->shift]; - dsp->card = codec->card; + dsp->card = codec->component.card; switch (event) { case SND_SOC_DAPM_PRE_PMU: diff --git a/sound/soc/omap/ams-delta.c b/sound/soc/omap/ams-delta.c index 0cc41f94de4e..8c9cc64a9dfb 100644 --- a/sound/soc/omap/ams-delta.c +++ b/sound/soc/omap/ams-delta.c @@ -301,7 +301,7 @@ static int cx81801_open(struct tty_struct *tty) static void cx81801_close(struct tty_struct *tty) { struct snd_soc_codec *codec = tty->disc_data; - struct snd_soc_dapm_context *dapm = &codec->card->dapm; + struct snd_soc_dapm_context *dapm = &codec->component.card->dapm; del_timer_sync(&cx81801_timer); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index a5edb31ddfff..a0ae3d2263d1 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -292,7 +292,7 @@ static struct dentry *soc_debugfs_create_dir(struct dentry *parent, static void soc_init_codec_debugfs(struct snd_soc_codec *codec) { - struct dentry *debugfs_card_root = codec->card->debugfs_card_root; + struct dentry *debugfs_card_root = codec->component.card->debugfs_card_root; codec->debugfs_codec_root = soc_debugfs_create_dir(debugfs_card_root, "codec:%s", @@ -325,7 +325,7 @@ static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec) static void soc_init_platform_debugfs(struct snd_soc_platform *platform) { - struct dentry *debugfs_card_root = platform->card->debugfs_card_root; + struct dentry *debugfs_card_root = platform->component.card->debugfs_card_root; platform->debugfs_platform_root = soc_debugfs_create_dir(debugfs_card_root, "platform:%s", @@ -546,11 +546,12 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec) int err; codec->ac97->dev.bus = &ac97_bus_type; - codec->ac97->dev.parent = codec->card->dev; + codec->ac97->dev.parent = codec->component.card->dev; codec->ac97->dev.release = soc_ac97_device_release; dev_set_name(&codec->ac97->dev, "%d-%d:%s", - codec->card->snd_card->number, 0, codec->component.name); + codec->component.card->snd_card->number, 0, + codec->component.name); err = device_register(&codec->ac97->dev); if (err < 0) { dev_err(codec->dev, "ASoC: Can't register ac97 bus\n"); @@ -1179,7 +1180,7 @@ static int soc_probe_codec(struct snd_soc_card *card, const struct snd_soc_codec_driver *driver = codec->driver; struct snd_soc_dai *dai; - codec->card = card; + codec->component.card = card; codec->dapm.card = card; soc_set_name_prefix(card, &codec->component); @@ -1255,7 +1256,7 @@ static int soc_probe_platform(struct snd_soc_card *card, struct snd_soc_component *component; struct snd_soc_dai *dai; - platform->card = card; + platform->component.card = card; platform->component.dapm.card = card; if (!try_module_get(platform->dev->driver->owner)) @@ -2406,7 +2407,7 @@ EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol); int snd_soc_add_codec_controls(struct snd_soc_codec *codec, const struct snd_kcontrol_new *controls, int num_controls) { - struct snd_card *card = codec->card->snd_card; + struct snd_card *card = codec->component.card->snd_card; return snd_soc_add_controls(card, codec->dev, controls, num_controls, codec->component.name_prefix, &codec->component); @@ -2426,7 +2427,7 @@ EXPORT_SYMBOL_GPL(snd_soc_add_codec_controls); int snd_soc_add_platform_controls(struct snd_soc_platform *platform, const struct snd_kcontrol_new *controls, int num_controls) { - struct snd_card *card = platform->card->snd_card; + struct snd_card *card = platform->component.card->snd_card; return snd_soc_add_controls(card, platform->dev, controls, num_controls, NULL, &platform->component); @@ -3101,7 +3102,7 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range); int snd_soc_limit_volume(struct snd_soc_codec *codec, const char *name, int max) { - struct snd_card *card = codec->card->snd_card; + struct snd_card *card = codec->component.card->snd_card; struct snd_kcontrol *kctl; struct soc_mixer_control *mc; int found = 0; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 8cb68a38ad19..8348352dc2c6 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2090,7 +2090,7 @@ static ssize_t dapm_widget_show_codec(struct snd_soc_codec *codec, char *buf) int count = 0; char *state = "not set"; - list_for_each_entry(w, &codec->card->widgets, list) { + list_for_each_entry(w, &codec->component.card->widgets, list) { if (w->dapm != &codec->dapm) continue; diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index d0d98810af91..ab47fea997a3 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -43,7 +43,7 @@ int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type, INIT_LIST_HEAD(&jack->jack_zones); BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier); - return snd_jack_new(codec->card->snd_card, id, type, &jack->jack); + return snd_jack_new(codec->component.card->snd_card, id, type, &jack->jack); } EXPORT_SYMBOL_GPL(snd_soc_jack_new); @@ -260,7 +260,7 @@ static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio) static irqreturn_t gpio_handler(int irq, void *data) { struct snd_soc_jack_gpio *gpio = data; - struct device *dev = gpio->jack->codec->card->dev; + struct device *dev = gpio->jack->codec->component.card->dev; trace_snd_soc_jack_irq(gpio->name); -- cgit v1.2.3 From 0f2780ad4c2a398528c7bb1572158d6e894e5dd2 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 17 Jul 2014 22:01:08 +0200 Subject: ASoC: Add function to register component controls We have now everything in place to actual let a component register controls. Add a function which allows to do so. Also update snd_soc_add_codec_controls() and snd_soc_platform_controls() to use this new function internally. And while we are at it also change the num_controls parameter of those two functions from int to unsigned int. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 6 ++++-- sound/soc/soc-core.c | 35 +++++++++++++++++++++++++---------- 2 files changed, 29 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 5ee6ddde4831..50ff6f3459aa 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -507,10 +507,12 @@ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, const char *prefix); struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card, const char *name); +int snd_soc_add_component_controls(struct snd_soc_component *component, + const struct snd_kcontrol_new *controls, unsigned int num_controls); int snd_soc_add_codec_controls(struct snd_soc_codec *codec, - const struct snd_kcontrol_new *controls, int num_controls); + const struct snd_kcontrol_new *controls, unsigned int num_controls); int snd_soc_add_platform_controls(struct snd_soc_platform *platform, - const struct snd_kcontrol_new *controls, int num_controls); + const struct snd_kcontrol_new *controls, unsigned int num_controls); int snd_soc_add_card_controls(struct snd_soc_card *soc_card, const struct snd_kcontrol_new *controls, int num_controls); int snd_soc_add_dai_controls(struct snd_soc_dai *dai, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index a0ae3d2263d1..28caa63ae526 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2393,6 +2393,25 @@ struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card, } EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol); +/** + * snd_soc_add_component_controls - Add an array of controls to a component. + * + * @component: Component to add controls to + * @controls: Array of controls to add + * @num_controls: Number of elements in the array + * + * Return: 0 for success, else error. + */ +int snd_soc_add_component_controls(struct snd_soc_component *component, + const struct snd_kcontrol_new *controls, unsigned int num_controls) +{ + struct snd_card *card = component->card->snd_card; + + return snd_soc_add_controls(card, component->dev, controls, + num_controls, component->name_prefix, component); +} +EXPORT_SYMBOL_GPL(snd_soc_add_component_controls); + /** * snd_soc_add_codec_controls - add an array of controls to a codec. * Convenience function to add a list of controls. Many codecs were @@ -2405,12 +2424,10 @@ EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol); * Return 0 for success, else error. */ int snd_soc_add_codec_controls(struct snd_soc_codec *codec, - const struct snd_kcontrol_new *controls, int num_controls) + const struct snd_kcontrol_new *controls, unsigned int num_controls) { - struct snd_card *card = codec->component.card->snd_card; - - return snd_soc_add_controls(card, codec->dev, controls, num_controls, - codec->component.name_prefix, &codec->component); + return snd_soc_add_component_controls(&codec->component, controls, + num_controls); } EXPORT_SYMBOL_GPL(snd_soc_add_codec_controls); @@ -2425,12 +2442,10 @@ EXPORT_SYMBOL_GPL(snd_soc_add_codec_controls); * Return 0 for success, else error. */ int snd_soc_add_platform_controls(struct snd_soc_platform *platform, - const struct snd_kcontrol_new *controls, int num_controls) + const struct snd_kcontrol_new *controls, unsigned int num_controls) { - struct snd_card *card = platform->component.card->snd_card; - - return snd_soc_add_controls(card, platform->dev, controls, num_controls, - NULL, &platform->component); + return snd_soc_add_component_controls(&platform->component, controls, + num_controls); } EXPORT_SYMBOL_GPL(snd_soc_add_platform_controls); -- cgit v1.2.3 From 35630df68d6030daf12dde12ed07bbe26324e6ac Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sun, 25 May 2014 22:35:38 +0200 Subject: NFC: st21nfcb: Add driver for STMicroelectronics ST21NFCB NFC chip Add driver for STMicroelectronics ST21NFCB NFC controller. ST21NFCB is using NCI protocol and a proprietary low level transport protocol called NDLC used on top. NDLC: The protocol defines 2 types of frame: - One type carrying NCI data (referred as DATAFRAME frames). - One type carrying protocol information used for flow control and error control mechanisms (referred as SUPERVISOR frames). After each frame transmission to the NFC controller, the device host SHALL waitfor an ACK (SUPERVISOR frame) reception before sending a new frame. The NFC controller MAY send a frame at anytime to the device host. The NFC controller MAY send a specific WAIT supervisor frame to indicate to device host that a NCI data packet has been received but that it could take significant time before the NFC controller sends an ACK and thus allows next data reception. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/Kconfig | 2 +- drivers/nfc/Makefile | 3 +- drivers/nfc/st21nfcb/Kconfig | 22 ++ drivers/nfc/st21nfcb/Makefile | 8 + drivers/nfc/st21nfcb/i2c.c | 462 +++++++++++++++++++++++++++++++++ drivers/nfc/st21nfcb/ndlc.c | 298 +++++++++++++++++++++ drivers/nfc/st21nfcb/ndlc.h | 55 ++++ drivers/nfc/st21nfcb/st21nfcb.c | 129 +++++++++ drivers/nfc/st21nfcb/st21nfcb.h | 38 +++ include/linux/platform_data/st21nfcb.h | 32 +++ 10 files changed, 1047 insertions(+), 2 deletions(-) create mode 100644 drivers/nfc/st21nfcb/Kconfig create mode 100644 drivers/nfc/st21nfcb/Makefile create mode 100644 drivers/nfc/st21nfcb/i2c.c create mode 100644 drivers/nfc/st21nfcb/ndlc.c create mode 100644 drivers/nfc/st21nfcb/ndlc.h create mode 100644 drivers/nfc/st21nfcb/st21nfcb.c create mode 100644 drivers/nfc/st21nfcb/st21nfcb.h create mode 100644 include/linux/platform_data/st21nfcb.h (limited to 'include') diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig index 26c66a126551..7929fac13e1c 100644 --- a/drivers/nfc/Kconfig +++ b/drivers/nfc/Kconfig @@ -72,5 +72,5 @@ source "drivers/nfc/pn544/Kconfig" source "drivers/nfc/microread/Kconfig" source "drivers/nfc/nfcmrvl/Kconfig" source "drivers/nfc/st21nfca/Kconfig" - +source "drivers/nfc/st21nfcb/Kconfig" endmenu diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile index 23225b0287fd..6b23a2c6e34a 100644 --- a/drivers/nfc/Makefile +++ b/drivers/nfc/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_NFC_SIM) += nfcsim.o obj-$(CONFIG_NFC_PORT100) += port100.o obj-$(CONFIG_NFC_MRVL) += nfcmrvl/ obj-$(CONFIG_NFC_TRF7970A) += trf7970a.o -obj-$(CONFIG_NFC_ST21NFCA) += st21nfca/ +obj-$(CONFIG_NFC_ST21NFCA) += st21nfca/ +obj-$(CONFIG_NFC_ST21NFCB) += st21nfcb/ ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG diff --git a/drivers/nfc/st21nfcb/Kconfig b/drivers/nfc/st21nfcb/Kconfig new file mode 100644 index 000000000000..e0322dd03a70 --- /dev/null +++ b/drivers/nfc/st21nfcb/Kconfig @@ -0,0 +1,22 @@ +config NFC_ST21NFCB + tristate "STMicroelectronics ST21NFCB NFC driver" + depends on NFC_NCI + default n + ---help--- + STMicroelectronics ST21NFCB core driver. It implements the chipset + NCI logic and hooks into the NFC kernel APIs. Physical layers will + register against it. + + To compile this driver as a module, choose m here. The module will + be called st21nfcb. + Say N if unsure. + +config NFC_ST21NFCB_I2C + tristate "NFC ST21NFCB i2c support" + depends on NFC_ST21NFCB && I2C + ---help--- + This module adds support for the STMicroelectronics st21nfcb i2c interface. + Select this if your platform is using the i2c bus. + + If you choose to build a module, it'll be called st21nfcb_i2c. + Say N if unsure. diff --git a/drivers/nfc/st21nfcb/Makefile b/drivers/nfc/st21nfcb/Makefile new file mode 100644 index 000000000000..13d9f03b2fea --- /dev/null +++ b/drivers/nfc/st21nfcb/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for ST21NFCB NCI based NFC driver +# + +st21nfcb_i2c-objs = i2c.o + +obj-$(CONFIG_NFC_ST21NFCB) += st21nfcb.o ndlc.o +obj-$(CONFIG_NFC_ST21NFCB_I2C) += st21nfcb_i2c.o diff --git a/drivers/nfc/st21nfcb/i2c.c b/drivers/nfc/st21nfcb/i2c.c new file mode 100644 index 000000000000..0f690baaef7a --- /dev/null +++ b/drivers/nfc/st21nfcb/i2c.c @@ -0,0 +1,462 @@ +/* + * I2C Link Layer for ST21NFCB NCI based Driver + * Copyright (C) 2014 STMicroelectronics SAS. 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 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 . + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ndlc.h" + +#define DRIVER_DESC "NCI NFC driver for ST21NFCB" + +/* ndlc header */ +#define ST21NFCB_FRAME_HEADROOM 1 +#define ST21NFCB_FRAME_TAILROOM 0 + +#define ST21NFCB_NCI_I2C_MIN_SIZE 4 /* PCB(1) + NCI Packet header(3) */ +#define ST21NFCB_NCI_I2C_MAX_SIZE 250 /* req 4.2.1 */ + +#define ST21NFCB_NCI_I2C_DRIVER_NAME "st21nfcb_nci_i2c" + +static struct i2c_device_id st21nfcb_nci_i2c_id_table[] = { + {ST21NFCB_NCI_DRIVER_NAME, 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, st21nfcb_nci_i2c_id_table); + +struct st21nfcb_i2c_phy { + struct i2c_client *i2c_dev; + struct llt_ndlc *ndlc; + + unsigned int gpio_irq; + unsigned int gpio_reset; + unsigned int irq_polarity; + + int powered; + + /* + * < 0 if hardware error occured (e.g. i2c err) + * and prevents normal operation. + */ + int hard_fault; +}; + +#define I2C_DUMP_SKB(info, skb) \ +do { \ + pr_debug("%s:\n", info); \ + print_hex_dump(KERN_DEBUG, "i2c: ", DUMP_PREFIX_OFFSET, \ + 16, 1, (skb)->data, (skb)->len, 0); \ +} while (0) + +static int st21nfcb_nci_i2c_enable(void *phy_id) +{ + struct st21nfcb_i2c_phy *phy = phy_id; + + gpio_set_value(phy->gpio_reset, 0); + usleep_range(10000, 15000); + gpio_set_value(phy->gpio_reset, 1); + phy->powered = 1; + usleep_range(80000, 85000); + + return 0; +} + +static void st21nfcb_nci_i2c_disable(void *phy_id) +{ + struct st21nfcb_i2c_phy *phy = phy_id; + + pr_info("\n"); + + phy->powered = 0; + /* reset chip in order to flush clf */ + gpio_set_value(phy->gpio_reset, 0); + usleep_range(10000, 15000); + gpio_set_value(phy->gpio_reset, 1); +} + +static void st21nfcb_nci_remove_header(struct sk_buff *skb) +{ + skb_pull(skb, ST21NFCB_FRAME_HEADROOM); +} + +/* + * Writing a frame must not return the number of written bytes. + * It must return either zero for success, or <0 for error. + * In addition, it must not alter the skb + */ +static int st21nfcb_nci_i2c_write(void *phy_id, struct sk_buff *skb) +{ + int r = -1; + struct st21nfcb_i2c_phy *phy = phy_id; + struct i2c_client *client = phy->i2c_dev; + + I2C_DUMP_SKB("st21nfcb_nci_i2c_write", skb); + + if (phy->hard_fault != 0) + return phy->hard_fault; + + r = i2c_master_send(client, skb->data, skb->len); + if (r == -EREMOTEIO) { /* Retry, chip was in standby */ + usleep_range(1000, 4000); + r = i2c_master_send(client, skb->data, skb->len); + } + + if (r >= 0) { + if (r != skb->len) + r = -EREMOTEIO; + else + r = 0; + } + + st21nfcb_nci_remove_header(skb); + + return r; +} + +/* + * Reads an ndlc frame and returns it in a newly allocated sk_buff. + * returns: + * frame size : if received frame is complete (find ST21NFCB_SOF_EOF at + * end of read) + * -EAGAIN : if received frame is incomplete (not find ST21NFCB_SOF_EOF + * at end of read) + * -EREMOTEIO : i2c read error (fatal) + * -EBADMSG : frame was incorrect and discarded + * (value returned from st21nfcb_nci_i2c_repack) + * -EIO : if no ST21NFCB_SOF_EOF is found after reaching + * the read length end sequence + */ +static int st21nfcb_nci_i2c_read(struct st21nfcb_i2c_phy *phy, + struct sk_buff **skb) +{ + int r; + u8 len; + u8 buf[ST21NFCB_NCI_I2C_MAX_SIZE]; + struct i2c_client *client = phy->i2c_dev; + + r = i2c_master_recv(client, buf, 4); + if (r == -EREMOTEIO) { /* Retry, chip was in standby */ + usleep_range(1000, 4000); + r = i2c_master_recv(client, buf, 4); + } else if (r != 4) { + nfc_err(&client->dev, "cannot read ndlc & nci header\n"); + return -EREMOTEIO; + } + + len = be16_to_cpu(*(__be16 *) (buf + 2)); + if (len > ST21NFCB_NCI_I2C_MAX_SIZE) { + nfc_err(&client->dev, "invalid frame len\n"); + return -EBADMSG; + } + + *skb = alloc_skb(4 + len, GFP_KERNEL); + if (*skb == NULL) + return -ENOMEM; + + skb_reserve(*skb, 4); + skb_put(*skb, 4); + memcpy((*skb)->data, buf, 4); + + if (!len) + return 0; + + r = i2c_master_recv(client, buf, len); + if (r != len) { + kfree_skb(*skb); + return -EREMOTEIO; + } + + skb_put(*skb, len); + memcpy((*skb)->data + 4, buf, len); + + I2C_DUMP_SKB("i2c frame read", *skb); + + return 0; +} + +/* + * Reads an ndlc frame from the chip. + * + * On ST21NFCB, IRQ goes in idle state when read starts. + */ +static irqreturn_t st21nfcb_nci_irq_thread_fn(int irq, void *phy_id) +{ + struct st21nfcb_i2c_phy *phy = phy_id; + struct i2c_client *client; + struct sk_buff *skb = NULL; + int r; + + if (!phy || irq != phy->i2c_dev->irq) { + WARN_ON_ONCE(1); + return IRQ_NONE; + } + + client = phy->i2c_dev; + dev_dbg(&client->dev, "IRQ\n"); + + if (phy->hard_fault) + return IRQ_HANDLED; + + if (!phy->powered) { + st21nfcb_nci_i2c_disable(phy); + return IRQ_HANDLED; + } + + r = st21nfcb_nci_i2c_read(phy, &skb); + if (r == -EREMOTEIO) { + phy->hard_fault = r; + ndlc_recv(phy->ndlc, NULL); + return IRQ_HANDLED; + } else if (r == -ENOMEM || r == -EBADMSG) { + return IRQ_HANDLED; + } + + ndlc_recv(phy->ndlc, skb); + + return IRQ_HANDLED; +} + +static struct nfc_phy_ops i2c_phy_ops = { + .write = st21nfcb_nci_i2c_write, + .enable = st21nfcb_nci_i2c_enable, + .disable = st21nfcb_nci_i2c_disable, +}; + +#ifdef CONFIG_OF +static int st21nfcb_nci_i2c_of_request_resources(struct i2c_client *client) +{ + struct st21nfcb_i2c_phy *phy = i2c_get_clientdata(client); + struct device_node *pp; + int gpio; + int r; + + pp = client->dev.of_node; + if (!pp) + return -ENODEV; + + /* Get GPIO from device tree */ + gpio = of_get_named_gpio(pp, "reset-gpios", 0); + if (gpio < 0) { + nfc_err(&client->dev, + "Failed to retrieve reset-gpios from device tree\n"); + return gpio; + } + + /* GPIO request and configuration */ + r = devm_gpio_request(&client->dev, gpio, "clf_reset"); + if (r) { + nfc_err(&client->dev, "Failed to request reset pin\n"); + return -ENODEV; + } + + r = gpio_direction_output(gpio, 1); + if (r) { + nfc_err(&client->dev, + "Failed to set reset pin direction as output\n"); + return -ENODEV; + } + phy->gpio_reset = gpio; + + /* IRQ */ + r = irq_of_parse_and_map(pp, 0); + if (r < 0) { + nfc_err(&client->dev, + "Unable to get irq, error: %d\n", r); + return r; + } + + phy->irq_polarity = irq_get_trigger_type(r); + client->irq = r; + + return 0; +} +#else +static int st21nfcb_nci_i2c_of_request_resources(struct i2c_client *client) +{ + return -ENODEV; +} +#endif + +static int st21nfcb_nci_i2c_request_resources(struct i2c_client *client) +{ + struct st21nfcb_nfc_platform_data *pdata; + struct st21nfcb_i2c_phy *phy = i2c_get_clientdata(client); + int r; + int irq; + + pdata = client->dev.platform_data; + if (pdata == NULL) { + nfc_err(&client->dev, "No platform data\n"); + return -EINVAL; + } + + /* store for later use */ + phy->gpio_irq = pdata->gpio_irq; + phy->gpio_reset = pdata->gpio_reset; + phy->irq_polarity = pdata->irq_polarity; + + r = devm_gpio_request(&client->dev, phy->gpio_irq, "wake_up"); + if (r) { + pr_err("%s : gpio_request failed\n", __FILE__); + return -ENODEV; + } + + r = gpio_direction_input(phy->gpio_irq); + if (r) { + pr_err("%s : gpio_direction_input failed\n", __FILE__); + return -ENODEV; + } + + r = devm_gpio_request(&client->dev, + phy->gpio_reset, "clf_reset"); + if (r) { + pr_err("%s : reset gpio_request failed\n", __FILE__); + return -ENODEV; + } + + r = gpio_direction_output(phy->gpio_reset, 1); + if (r) { + pr_err("%s : reset gpio_direction_output failed\n", + __FILE__); + return -ENODEV; + } + + /* IRQ */ + irq = gpio_to_irq(phy->gpio_irq); + if (irq < 0) { + nfc_err(&client->dev, + "Unable to get irq number for GPIO %d error %d\n", + phy->gpio_irq, r); + return -ENODEV; + } + client->irq = irq; + + return 0; +} + +static int st21nfcb_nci_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct st21nfcb_i2c_phy *phy; + struct st21nfcb_nfc_platform_data *pdata; + int r; + + dev_dbg(&client->dev, "%s\n", __func__); + dev_dbg(&client->dev, "IRQ: %d\n", client->irq); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + nfc_err(&client->dev, "Need I2C_FUNC_I2C\n"); + return -ENODEV; + } + + phy = devm_kzalloc(&client->dev, sizeof(struct st21nfcb_i2c_phy), + GFP_KERNEL); + if (!phy) { + nfc_err(&client->dev, + "Cannot allocate memory for st21nfcb i2c phy.\n"); + return -ENOMEM; + } + + phy->i2c_dev = client; + + i2c_set_clientdata(client, phy); + + pdata = client->dev.platform_data; + if (!pdata && client->dev.of_node) { + r = st21nfcb_nci_i2c_of_request_resources(client); + if (r) { + nfc_err(&client->dev, "No platform data\n"); + return r; + } + } else if (pdata) { + r = st21nfcb_nci_i2c_request_resources(client); + if (r) { + nfc_err(&client->dev, + "Cannot get platform resources\n"); + return r; + } + } else { + nfc_err(&client->dev, + "st21nfcb platform resources not available\n"); + return -ENODEV; + } + + r = devm_request_threaded_irq(&client->dev, client->irq, NULL, + st21nfcb_nci_irq_thread_fn, + phy->irq_polarity | IRQF_ONESHOT, + ST21NFCB_NCI_DRIVER_NAME, phy); + if (r < 0) { + nfc_err(&client->dev, "Unable to register IRQ handler\n"); + return r; + } + + return ndlc_probe(phy, &i2c_phy_ops, &client->dev, + ST21NFCB_FRAME_HEADROOM, ST21NFCB_FRAME_TAILROOM, + &phy->ndlc); +} + +static int st21nfcb_nci_i2c_remove(struct i2c_client *client) +{ + struct st21nfcb_i2c_phy *phy = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "%s\n", __func__); + + ndlc_remove(phy->ndlc); + + if (phy->powered) + st21nfcb_nci_i2c_disable(phy); + + return 0; +} + +static const struct of_device_id of_st21nfcb_i2c_match[] = { + { .compatible = "st,st21nfcb_i2c", }, + {} +}; + +static struct i2c_driver st21nfcb_nci_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = ST21NFCB_NCI_I2C_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(of_st21nfcb_i2c_match), + }, + .probe = st21nfcb_nci_i2c_probe, + .id_table = st21nfcb_nci_i2c_id_table, + .remove = st21nfcb_nci_i2c_remove, +}; + +module_i2c_driver(st21nfcb_nci_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/nfc/st21nfcb/ndlc.c b/drivers/nfc/st21nfcb/ndlc.c new file mode 100644 index 000000000000..83c97c36112b --- /dev/null +++ b/drivers/nfc/st21nfcb/ndlc.c @@ -0,0 +1,298 @@ +/* + * Low Level Transport (NDLC) Driver for STMicroelectronics NFC Chip + * + * Copyright (C) 2014 STMicroelectronics SAS. 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 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 "ndlc.h" +#include "st21nfcb.h" + +#define NDLC_TIMER_T1 100 +#define NDLC_TIMER_T1_WAIT 400 +#define NDLC_TIMER_T2 1200 + +#define PCB_TYPE_DATAFRAME 0x80 +#define PCB_TYPE_SUPERVISOR 0xc0 +#define PCB_TYPE_MASK PCB_TYPE_SUPERVISOR + +#define PCB_SYNC_ACK 0x20 +#define PCB_SYNC_NACK 0x10 +#define PCB_SYNC_WAIT 0x30 +#define PCB_SYNC_NOINFO 0x00 +#define PCB_SYNC_MASK PCB_SYNC_WAIT + +#define PCB_DATAFRAME_RETRANSMIT_YES 0x00 +#define PCB_DATAFRAME_RETRANSMIT_NO 0x04 +#define PCB_DATAFRAME_RETRANSMIT_MASK PCB_DATAFRAME_RETRANSMIT_NO + +#define PCB_SUPERVISOR_RETRANSMIT_YES 0x00 +#define PCB_SUPERVISOR_RETRANSMIT_NO 0x02 +#define PCB_SUPERVISOR_RETRANSMIT_MASK PCB_SUPERVISOR_RETRANSMIT_NO + +#define PCB_FRAME_CRC_INFO_PRESENT 0x08 +#define PCB_FRAME_CRC_INFO_NOTPRESENT 0x00 +#define PCB_FRAME_CRC_INFO_MASK PCB_FRAME_CRC_INFO_PRESENT + +#define NDLC_DUMP_SKB(info, skb) \ +do { \ + pr_debug("%s:\n", info); \ + print_hex_dump(KERN_DEBUG, "ndlc: ", DUMP_PREFIX_OFFSET, \ + 16, 1, skb->data, skb->len, 0); \ +} while (0) + +int ndlc_open(struct llt_ndlc *ndlc) +{ + /* toggle reset pin */ + ndlc->ops->enable(ndlc->phy_id); + return 0; +} +EXPORT_SYMBOL(ndlc_open); + +void ndlc_close(struct llt_ndlc *ndlc) +{ + /* toggle reset pin */ + ndlc->ops->disable(ndlc->phy_id); +} +EXPORT_SYMBOL(ndlc_close); + +int ndlc_send(struct llt_ndlc *ndlc, struct sk_buff *skb) +{ + /* add ndlc header */ + u8 pcb = PCB_TYPE_DATAFRAME | PCB_DATAFRAME_RETRANSMIT_NO | + PCB_FRAME_CRC_INFO_NOTPRESENT; + + *skb_push(skb, 1) = pcb; + skb_queue_tail(&ndlc->send_q, skb); + + schedule_work(&ndlc->sm_work); + + return 0; +} +EXPORT_SYMBOL(ndlc_send); + +static void llt_ndlc_send_queue(struct llt_ndlc *ndlc) +{ + struct sk_buff *skb; + int r; + unsigned long time_sent; + + if (ndlc->send_q.qlen) + pr_debug("sendQlen=%d unackQlen=%d\n", + ndlc->send_q.qlen, ndlc->ack_pending_q.qlen); + + while (ndlc->send_q.qlen) { + skb = skb_dequeue(&ndlc->send_q); + NDLC_DUMP_SKB("ndlc frame written", skb); + r = ndlc->ops->write(ndlc->phy_id, skb); + if (r < 0) { + ndlc->hard_fault = r; + break; + } + time_sent = jiffies; + *(unsigned long *)skb->cb = time_sent; + + skb_queue_tail(&ndlc->ack_pending_q, skb); + + /* start timer t1 for ndlc aknowledge */ + ndlc->t1_active = true; + mod_timer(&ndlc->t1_timer, time_sent + + msecs_to_jiffies(NDLC_TIMER_T1)); + } +} + +static void llt_ndlc_requeue_data_pending(struct llt_ndlc *ndlc) +{ + struct sk_buff *skb; + u8 pcb; + + while ((skb = skb_dequeue_tail(&ndlc->ack_pending_q))) { + pcb = skb->data[0]; + switch (pcb & PCB_TYPE_MASK) { + case PCB_TYPE_SUPERVISOR: + skb->data[0] = (pcb & ~PCB_SUPERVISOR_RETRANSMIT_MASK) | + PCB_SUPERVISOR_RETRANSMIT_YES; + break; + case PCB_TYPE_DATAFRAME: + skb->data[0] = (pcb & ~PCB_DATAFRAME_RETRANSMIT_MASK) | + PCB_DATAFRAME_RETRANSMIT_YES; + break; + default: + pr_err("UNKNOWN Packet Control Byte=%d\n", pcb); + kfree_skb(skb); + break; + } + skb_queue_head(&ndlc->send_q, skb); + } +} + +static void llt_ndlc_rcv_queue(struct llt_ndlc *ndlc) +{ + struct sk_buff *skb; + u8 pcb; + unsigned long time_sent; + + if (ndlc->rcv_q.qlen) + pr_debug("rcvQlen=%d\n", ndlc->rcv_q.qlen); + + while ((skb = skb_dequeue(&ndlc->rcv_q)) != NULL) { + pcb = skb->data[0]; + skb_pull(skb, 1); + if ((pcb & PCB_TYPE_MASK) == PCB_TYPE_SUPERVISOR) { + switch (pcb & PCB_SYNC_MASK) { + case PCB_SYNC_ACK: + del_timer_sync(&ndlc->t1_timer); + del_timer_sync(&ndlc->t2_timer); + ndlc->t2_active = false; + ndlc->t1_active = false; + break; + case PCB_SYNC_NACK: + llt_ndlc_requeue_data_pending(ndlc); + llt_ndlc_send_queue(ndlc); + /* start timer t1 for ndlc aknowledge */ + time_sent = jiffies; + ndlc->t1_active = true; + mod_timer(&ndlc->t1_timer, time_sent + + msecs_to_jiffies(NDLC_TIMER_T1)); + break; + case PCB_SYNC_WAIT: + time_sent = jiffies; + ndlc->t1_active = true; + mod_timer(&ndlc->t1_timer, time_sent + + msecs_to_jiffies(NDLC_TIMER_T1_WAIT)); + break; + default: + pr_err("UNKNOWN Packet Control Byte=%d\n", pcb); + kfree_skb(skb); + break; + } + } else { + nci_recv_frame(ndlc->ndev, skb); + } + } +} + +static void llt_ndlc_sm_work(struct work_struct *work) +{ + struct llt_ndlc *ndlc = container_of(work, struct llt_ndlc, sm_work); + + llt_ndlc_send_queue(ndlc); + llt_ndlc_rcv_queue(ndlc); + + if (ndlc->t1_active && timer_pending(&ndlc->t1_timer) == 0) { + pr_debug + ("Handle T1(recv SUPERVISOR) elapsed (T1 now inactive)\n"); + ndlc->t1_active = false; + + llt_ndlc_requeue_data_pending(ndlc); + llt_ndlc_send_queue(ndlc); + } + + if (ndlc->t2_active && timer_pending(&ndlc->t2_timer) == 0) { + pr_debug("Handle T2(recv DATA) elapsed (T2 now inactive)\n"); + ndlc->t2_active = false; + ndlc->t1_active = false; + del_timer_sync(&ndlc->t1_timer); + + ndlc_close(ndlc); + ndlc->hard_fault = -EREMOTEIO; + } +} + +void ndlc_recv(struct llt_ndlc *ndlc, struct sk_buff *skb) +{ + if (skb == NULL) { + pr_err("NULL Frame -> link is dead\n"); + ndlc->hard_fault = -EREMOTEIO; + ndlc_close(ndlc); + } else { + NDLC_DUMP_SKB("incoming frame", skb); + skb_queue_tail(&ndlc->rcv_q, skb); + } + + schedule_work(&ndlc->sm_work); +} +EXPORT_SYMBOL(ndlc_recv); + +static void ndlc_t1_timeout(unsigned long data) +{ + struct llt_ndlc *ndlc = (struct llt_ndlc *)data; + + pr_debug("\n"); + + schedule_work(&ndlc->sm_work); +} + +static void ndlc_t2_timeout(unsigned long data) +{ + struct llt_ndlc *ndlc = (struct llt_ndlc *)data; + + pr_debug("\n"); + + schedule_work(&ndlc->sm_work); +} + +int ndlc_probe(void *phy_id, struct nfc_phy_ops *phy_ops, struct device *dev, + int phy_headroom, int phy_tailroom, struct llt_ndlc **ndlc_id) +{ + struct llt_ndlc *ndlc; + + ndlc = devm_kzalloc(dev, sizeof(struct llt_ndlc), GFP_KERNEL); + if (!ndlc) { + nfc_err(dev, "Cannot allocate memory for ndlc.\n"); + return -ENOMEM; + } + ndlc->ops = phy_ops; + ndlc->phy_id = phy_id; + ndlc->dev = dev; + + *ndlc_id = ndlc; + + /* start timers */ + init_timer(&ndlc->t1_timer); + ndlc->t1_timer.data = (unsigned long)ndlc; + ndlc->t1_timer.function = ndlc_t1_timeout; + + init_timer(&ndlc->t2_timer); + ndlc->t2_timer.data = (unsigned long)ndlc; + ndlc->t2_timer.function = ndlc_t2_timeout; + + skb_queue_head_init(&ndlc->rcv_q); + skb_queue_head_init(&ndlc->send_q); + skb_queue_head_init(&ndlc->ack_pending_q); + + INIT_WORK(&ndlc->sm_work, llt_ndlc_sm_work); + + return st21nfcb_nci_probe(ndlc, phy_headroom, phy_tailroom); +} +EXPORT_SYMBOL(ndlc_probe); + +void ndlc_remove(struct llt_ndlc *ndlc) +{ + /* cancel timers */ + del_timer_sync(&ndlc->t1_timer); + del_timer_sync(&ndlc->t2_timer); + ndlc->t2_active = false; + ndlc->t1_active = false; + + skb_queue_purge(&ndlc->rcv_q); + skb_queue_purge(&ndlc->send_q); + + st21nfcb_nci_remove(ndlc->ndev); + kfree(ndlc); +} +EXPORT_SYMBOL(ndlc_remove); diff --git a/drivers/nfc/st21nfcb/ndlc.h b/drivers/nfc/st21nfcb/ndlc.h new file mode 100644 index 000000000000..c30a2f0faa5f --- /dev/null +++ b/drivers/nfc/st21nfcb/ndlc.h @@ -0,0 +1,55 @@ +/* + * NCI based Driver for STMicroelectronics NFC Chip + * + * Copyright (C) 2014 STMicroelectronics SAS. 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 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 __LOCAL_NDLC_H_ +#define __LOCAL_NDLC_H_ + +#include +#include + +/* Low Level Transport description */ +struct llt_ndlc { + struct nci_dev *ndev; + struct nfc_phy_ops *ops; + void *phy_id; + + struct timer_list t1_timer; + bool t1_active; + + struct timer_list t2_timer; + bool t2_active; + + struct sk_buff_head rcv_q; + struct sk_buff_head send_q; + struct sk_buff_head ack_pending_q; + + struct work_struct sm_work; + + struct device *dev; + + int hard_fault; +}; + +int ndlc_open(struct llt_ndlc *ndlc); +void ndlc_close(struct llt_ndlc *ndlc); +int ndlc_send(struct llt_ndlc *ndlc, struct sk_buff *skb); +void ndlc_recv(struct llt_ndlc *ndlc, struct sk_buff *skb); +int ndlc_probe(void *phy_id, struct nfc_phy_ops *phy_ops, struct device *dev, + int phy_headroom, int phy_tailroom, struct llt_ndlc **ndlc_id); +void ndlc_remove(struct llt_ndlc *ndlc); +#endif /* __LOCAL_NDLC_H__ */ diff --git a/drivers/nfc/st21nfcb/st21nfcb.c b/drivers/nfc/st21nfcb/st21nfcb.c new file mode 100644 index 000000000000..4d95863e3063 --- /dev/null +++ b/drivers/nfc/st21nfcb/st21nfcb.c @@ -0,0 +1,129 @@ +/* + * NCI based Driver for STMicroelectronics NFC Chip + * + * Copyright (C) 2014 STMicroelectronics SAS. 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 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 "st21nfcb.h" +#include "ndlc.h" + +#define DRIVER_DESC "NCI NFC driver for ST21NFCB" + +static int st21nfcb_nci_open(struct nci_dev *ndev) +{ + struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + int r; + + if (test_and_set_bit(ST21NFCB_NCI_RUNNING, &info->flags)) + return 0; + + r = ndlc_open(info->ndlc); + if (r) + clear_bit(ST21NFCB_NCI_RUNNING, &info->flags); + + return r; +} + +static int st21nfcb_nci_close(struct nci_dev *ndev) +{ + struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + + if (!test_and_clear_bit(ST21NFCB_NCI_RUNNING, &info->flags)) + return 0; + + ndlc_close(info->ndlc); + + return 0; +} + +static int st21nfcb_nci_send(struct nci_dev *ndev, struct sk_buff *skb) +{ + struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + + skb->dev = (void *)ndev; + + if (!test_bit(ST21NFCB_NCI_RUNNING, &info->flags)) + return -EBUSY; + + return ndlc_send(info->ndlc, skb); +} + +static struct nci_ops st21nfcb_nci_ops = { + .open = st21nfcb_nci_open, + .close = st21nfcb_nci_close, + .send = st21nfcb_nci_send, +}; + +int st21nfcb_nci_probe(struct llt_ndlc *ndlc, int phy_headroom, + int phy_tailroom) +{ + struct st21nfcb_nci_info *info; + int r; + u32 protocols; + + info = devm_kzalloc(ndlc->dev, + sizeof(struct st21nfcb_nci_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + protocols = NFC_PROTO_JEWEL_MASK + | NFC_PROTO_MIFARE_MASK + | NFC_PROTO_FELICA_MASK + | NFC_PROTO_ISO14443_MASK + | NFC_PROTO_ISO14443_B_MASK + | NFC_PROTO_NFC_DEP_MASK; + + ndlc->ndev = nci_allocate_device(&st21nfcb_nci_ops, protocols, + phy_headroom, phy_tailroom); + if (!ndlc->ndev) { + pr_err("Cannot allocate nfc ndev\n"); + r = -ENOMEM; + goto err_alloc_ndev; + } + info->ndlc = ndlc; + + nci_set_drvdata(ndlc->ndev, info); + + r = nci_register_device(ndlc->ndev); + if (r) + goto err_regdev; + + return r; +err_regdev: + nci_free_device(ndlc->ndev); + +err_alloc_ndev: + kfree(info); + return r; +} +EXPORT_SYMBOL_GPL(st21nfcb_nci_probe); + +void st21nfcb_nci_remove(struct nci_dev *ndev) +{ + struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + + nci_unregister_device(ndev); + nci_free_device(ndev); + kfree(info); +} +EXPORT_SYMBOL_GPL(st21nfcb_nci_remove); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/nfc/st21nfcb/st21nfcb.h b/drivers/nfc/st21nfcb/st21nfcb.h new file mode 100644 index 000000000000..4bbbebb9f34d --- /dev/null +++ b/drivers/nfc/st21nfcb/st21nfcb.h @@ -0,0 +1,38 @@ +/* + * NCI based Driver for STMicroelectronics NFC Chip + * + * Copyright (C) 2014 STMicroelectronics SAS. 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 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 __LOCAL_ST21NFCB_H_ +#define __LOCAL_ST21NFCB_H_ + +#include + +#include "ndlc.h" + +/* Define private flags: */ +#define ST21NFCB_NCI_RUNNING 1 + +struct st21nfcb_nci_info { + struct llt_ndlc *ndlc; + unsigned long flags; +}; + +void st21nfcb_nci_remove(struct nci_dev *ndev); +int st21nfcb_nci_probe(struct llt_ndlc *ndlc, int phy_headroom, + int phy_tailroom); + +#endif /* __LOCAL_ST21NFCB_H_ */ diff --git a/include/linux/platform_data/st21nfcb.h b/include/linux/platform_data/st21nfcb.h new file mode 100644 index 000000000000..2a7b769c714d --- /dev/null +++ b/include/linux/platform_data/st21nfcb.h @@ -0,0 +1,32 @@ +/* + * Driver include for the ST21NFCB NFC chip. + * + * Copyright (C) 2014 STMicroelectronics SAS. 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 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 _ST21NFCA_HCI_H_ +#define _ST21NFCA_HCI_H_ + +#include + +#define ST21NFCB_NCI_DRIVER_NAME "st21nfcb_nci" + +struct st21nfcb_nfc_platform_data { + unsigned int gpio_irq; + unsigned int gpio_reset; + unsigned int irq_polarity; +}; + +#endif /* _ST21NFCA_HCI_H_ */ -- cgit v1.2.3 From fb92ff78f85b6c1a6f1277f7dd04a3762ba725ef Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Sun, 25 May 2014 22:46:58 +0200 Subject: NFC: st21nfcb: few code clean up Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st21nfcb/i2c.c | 16 ++++++++-------- include/linux/platform_data/st21nfcb.h | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/nfc/st21nfcb/i2c.c b/drivers/nfc/st21nfcb/i2c.c index 0f690baaef7a..8af880ead5db 100644 --- a/drivers/nfc/st21nfcb/i2c.c +++ b/drivers/nfc/st21nfcb/i2c.c @@ -164,11 +164,11 @@ static int st21nfcb_nci_i2c_read(struct st21nfcb_i2c_phy *phy, u8 buf[ST21NFCB_NCI_I2C_MAX_SIZE]; struct i2c_client *client = phy->i2c_dev; - r = i2c_master_recv(client, buf, 4); + r = i2c_master_recv(client, buf, ST21NFCB_NCI_I2C_MIN_SIZE); if (r == -EREMOTEIO) { /* Retry, chip was in standby */ usleep_range(1000, 4000); - r = i2c_master_recv(client, buf, 4); - } else if (r != 4) { + r = i2c_master_recv(client, buf, ST21NFCB_NCI_I2C_MIN_SIZE); + } else if (r != ST21NFCB_NCI_I2C_MIN_SIZE) { nfc_err(&client->dev, "cannot read ndlc & nci header\n"); return -EREMOTEIO; } @@ -179,13 +179,13 @@ static int st21nfcb_nci_i2c_read(struct st21nfcb_i2c_phy *phy, return -EBADMSG; } - *skb = alloc_skb(4 + len, GFP_KERNEL); + *skb = alloc_skb(ST21NFCB_NCI_I2C_MIN_SIZE + len, GFP_KERNEL); if (*skb == NULL) return -ENOMEM; - skb_reserve(*skb, 4); - skb_put(*skb, 4); - memcpy((*skb)->data, buf, 4); + skb_reserve(*skb, ST21NFCB_NCI_I2C_MIN_SIZE); + skb_put(*skb, ST21NFCB_NCI_I2C_MIN_SIZE); + memcpy((*skb)->data, buf, ST21NFCB_NCI_I2C_MIN_SIZE); if (!len) return 0; @@ -197,7 +197,7 @@ static int st21nfcb_nci_i2c_read(struct st21nfcb_i2c_phy *phy, } skb_put(*skb, len); - memcpy((*skb)->data + 4, buf, len); + memcpy((*skb)->data + ST21NFCB_NCI_I2C_MIN_SIZE, buf, len); I2C_DUMP_SKB("i2c frame read", *skb); diff --git a/include/linux/platform_data/st21nfcb.h b/include/linux/platform_data/st21nfcb.h index 2a7b769c714d..2d11f1f5efab 100644 --- a/include/linux/platform_data/st21nfcb.h +++ b/include/linux/platform_data/st21nfcb.h @@ -16,8 +16,8 @@ * along with this program; if not, see . */ -#ifndef _ST21NFCA_HCI_H_ -#define _ST21NFCA_HCI_H_ +#ifndef _ST21NFCB_NCI_H_ +#define _ST21NFCB_NCI_H_ #include -- cgit v1.2.3 From 28cb5ef16e578bbca0a562b09f12c8c98ca92720 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 23 Jul 2014 01:00:36 +0200 Subject: PM: Create PM workqueue if runtime PM is not configured too The PM workqueue is going to be used by ACPI PM notify handlers regardless of whether or not runtime PM is configured, so move it out of #ifdef CONFIG_PM_RUNTIME. Do that in three places in the ACPI device PM code. Signed-off-by: Rafael J. Wysocki --- include/linux/pm_runtime.h | 11 +++++++++-- kernel/power/main.c | 4 ---- 2 files changed, 9 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h index 43fd6716f662..367f49b9a1c9 100644 --- a/include/linux/pm_runtime.h +++ b/include/linux/pm_runtime.h @@ -24,11 +24,20 @@ #define RPM_AUTO 0x08 /* Use autosuspend_delay */ #ifdef CONFIG_PM +extern struct workqueue_struct *pm_wq; + +static inline bool queue_pm_work(struct work_struct *work) +{ + return queue_work(pm_wq, work); +} + extern int pm_generic_runtime_suspend(struct device *dev); extern int pm_generic_runtime_resume(struct device *dev); extern int pm_runtime_force_suspend(struct device *dev); extern int pm_runtime_force_resume(struct device *dev); #else +static inline bool queue_pm_work(struct work_struct *work) { return false; } + static inline int pm_generic_runtime_suspend(struct device *dev) { return 0; } static inline int pm_generic_runtime_resume(struct device *dev) { return 0; } static inline int pm_runtime_force_suspend(struct device *dev) { return 0; } @@ -37,8 +46,6 @@ static inline int pm_runtime_force_resume(struct device *dev) { return 0; } #ifdef CONFIG_PM_RUNTIME -extern struct workqueue_struct *pm_wq; - extern int __pm_runtime_idle(struct device *dev, int rpmflags); extern int __pm_runtime_suspend(struct device *dev, int rpmflags); extern int __pm_runtime_resume(struct device *dev, int rpmflags); diff --git a/kernel/power/main.c b/kernel/power/main.c index 8e90f330f139..a18efed75fa7 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -615,7 +615,6 @@ static struct attribute_group attr_group = { .attrs = g, }; -#ifdef CONFIG_PM_RUNTIME struct workqueue_struct *pm_wq; EXPORT_SYMBOL_GPL(pm_wq); @@ -625,9 +624,6 @@ static int __init pm_start_workqueue(void) return pm_wq ? 0 : -ENOMEM; } -#else -static inline int pm_start_workqueue(void) { return 0; } -#endif static int __init pm_init(void) { -- cgit v1.2.3 From c072530f391e33bd22ed0638c08f07528f154493 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 23 Jul 2014 01:00:45 +0200 Subject: ACPI / PM: Revork the handling of ACPI device wakeup notifications Since ACPI wakeup GPEs are going to be enabled during system suspend as well as for runtime wakeup by a subsequent patch and the same notify handlers will be used in both cases, rework the ACPI device wakeup notification framework so that the part specific to physical devices is always run asynchronously from the PM workqueue. This prevents runtime resume callbacks for those devices from being run during system suspend and resume which may not be appropriate, among other things. Also make ACPI device wakeup notification handling a bit more robust agaist subsequent removal of ACPI device objects, whould that ever happen, and create a wakeup source object for each ACPI device configured for wakeup so that wakeup notifications for those devices can wake up the system from the "freeze" sleep state. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/device_pm.c | 80 +++++++++++++++++++++++++++++++++++------------- drivers/acpi/pci_root.c | 2 +- drivers/pci/pci-acpi.c | 60 ++++++++++++------------------------ include/acpi/acpi_bus.h | 21 ++++++++----- include/linux/pci-acpi.h | 13 +++++--- 5 files changed, 101 insertions(+), 75 deletions(-) (limited to 'include') diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 49a51277f81d..366de0b0c39b 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -367,29 +367,61 @@ EXPORT_SYMBOL(acpi_bus_power_manageable); #ifdef CONFIG_PM static DEFINE_MUTEX(acpi_pm_notifier_lock); +static void acpi_pm_notify_handler(acpi_handle handle, u32 val, void *not_used) +{ + struct acpi_device *adev; + + if (val != ACPI_NOTIFY_DEVICE_WAKE) + return; + + adev = acpi_bus_get_acpi_device(handle); + if (!adev) + return; + + mutex_lock(&acpi_pm_notifier_lock); + + if (adev->wakeup.flags.notifier_present) { + __pm_wakeup_event(adev->wakeup.ws, 0); + if (adev->wakeup.context.work.func) + queue_pm_work(&adev->wakeup.context.work); + } + + mutex_unlock(&acpi_pm_notifier_lock); + + acpi_bus_put_acpi_device(adev); +} + /** - * acpi_add_pm_notifier - Register PM notifier for given ACPI device. - * @adev: ACPI device to add the notifier for. - * @context: Context information to pass to the notifier routine. + * acpi_add_pm_notifier - Register PM notify handler for given ACPI device. + * @adev: ACPI device to add the notify handler for. + * @dev: Device to generate a wakeup event for while handling the notification. + * @work_func: Work function to execute when handling the notification. * * NOTE: @adev need not be a run-wake or wakeup device to be a valid source of * PM wakeup events. For example, wakeup events may be generated for bridges * if one of the devices below the bridge is signaling wakeup, even if the * bridge itself doesn't have a wakeup GPE associated with it. */ -acpi_status acpi_add_pm_notifier(struct acpi_device *adev, - acpi_notify_handler handler, void *context) +acpi_status acpi_add_pm_notifier(struct acpi_device *adev, struct device *dev, + void (*work_func)(struct work_struct *work)) { acpi_status status = AE_ALREADY_EXISTS; + if (!dev && !work_func) + return AE_BAD_PARAMETER; + mutex_lock(&acpi_pm_notifier_lock); if (adev->wakeup.flags.notifier_present) goto out; - status = acpi_install_notify_handler(adev->handle, - ACPI_SYSTEM_NOTIFY, - handler, context); + adev->wakeup.ws = wakeup_source_register(dev_name(&adev->dev)); + adev->wakeup.context.dev = dev; + if (work_func) + INIT_WORK(&adev->wakeup.context.work, work_func); + + status = acpi_install_notify_handler(adev->handle, ACPI_SYSTEM_NOTIFY, + acpi_pm_notify_handler, NULL); if (ACPI_FAILURE(status)) goto out; @@ -404,8 +436,7 @@ acpi_status acpi_add_pm_notifier(struct acpi_device *adev, * acpi_remove_pm_notifier - Unregister PM notifier from given ACPI device. * @adev: ACPI device to remove the notifier from. */ -acpi_status acpi_remove_pm_notifier(struct acpi_device *adev, - acpi_notify_handler handler) +acpi_status acpi_remove_pm_notifier(struct acpi_device *adev) { acpi_status status = AE_BAD_PARAMETER; @@ -416,10 +447,17 @@ acpi_status acpi_remove_pm_notifier(struct acpi_device *adev, status = acpi_remove_notify_handler(adev->handle, ACPI_SYSTEM_NOTIFY, - handler); + acpi_pm_notify_handler); if (ACPI_FAILURE(status)) goto out; + if (adev->wakeup.context.work.func) { + cancel_work_sync(&adev->wakeup.context.work); + adev->wakeup.context.work.func = NULL; + } + adev->wakeup.context.dev = NULL; + wakeup_source_unregister(adev->wakeup.ws); + adev->wakeup.flags.notifier_present = false; out: @@ -602,16 +640,15 @@ EXPORT_SYMBOL(acpi_pm_device_sleep_state); #ifdef CONFIG_PM_RUNTIME /** - * acpi_wakeup_device - Wakeup notification handler for ACPI devices. - * @handle: ACPI handle of the device the notification is for. - * @event: Type of the signaled event. - * @context: Device corresponding to @handle. + * acpi_pm_notify_work_func - ACPI devices wakeup notification work function. + * @work: Work item to handle. */ -static void acpi_wakeup_device(acpi_handle handle, u32 event, void *context) +static void acpi_pm_notify_work_func(struct work_struct *work) { - struct device *dev = context; + struct device *dev; - if (event == ACPI_NOTIFY_DEVICE_WAKE && dev) { + dev = container_of(work, struct acpi_device_wakeup_context, work)->dev; + if (dev) { pm_wakeup_event(dev, 0); pm_runtime_resume(dev); } @@ -677,8 +714,7 @@ int acpi_pm_device_run_wake(struct device *phys_dev, bool enable) } EXPORT_SYMBOL(acpi_pm_device_run_wake); #else -static inline void acpi_wakeup_device(acpi_handle handle, u32 event, - void *context) {} +static inline void acpi_pm_notify_work_func(struct work_struct *work) {} #endif /* CONFIG_PM_RUNTIME */ #ifdef CONFIG_PM_SLEEP @@ -1048,7 +1084,7 @@ int acpi_dev_pm_attach(struct device *dev, bool power_on) if (dev->pm_domain) return -EEXIST; - acpi_add_pm_notifier(adev, acpi_wakeup_device, dev); + acpi_add_pm_notifier(adev, dev, acpi_pm_notify_work_func); dev->pm_domain = &acpi_general_pm_domain; if (power_on) { acpi_dev_pm_full_power(adev); @@ -1076,7 +1112,7 @@ void acpi_dev_pm_detach(struct device *dev, bool power_off) if (adev && dev->pm_domain == &acpi_general_pm_domain) { dev->pm_domain = NULL; - acpi_remove_pm_notifier(adev, acpi_wakeup_device); + acpi_remove_pm_notifier(adev); if (power_off) { /* * If the device's PM QoS resume latency limit or flags diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index d388f13d48b4..e6ae603ed1a1 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -593,7 +593,7 @@ static int acpi_pci_root_add(struct acpi_device *device, if (no_aspm) pcie_no_aspm(); - pci_acpi_add_bus_pm_notifier(device, root->bus); + pci_acpi_add_bus_pm_notifier(device); if (device->wakeup.flags.run_wake) device_set_run_wake(root->bus->bridge, true); diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index ca4927ba8433..7b8b2298840a 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -18,31 +18,31 @@ #include "pci.h" /** - * pci_acpi_wake_bus - Wake-up notification handler for root buses. - * @handle: ACPI handle of a device the notification is for. - * @event: Type of the signaled event. - * @context: PCI root bus to wake up devices on. + * pci_acpi_wake_bus - Root bus wakeup notification fork function. + * @work: Work item to handle. */ -static void pci_acpi_wake_bus(acpi_handle handle, u32 event, void *context) +static void pci_acpi_wake_bus(struct work_struct *work) { - struct pci_bus *pci_bus = context; + struct acpi_device *adev; + struct acpi_pci_root *root; - if (event == ACPI_NOTIFY_DEVICE_WAKE && pci_bus) - pci_pme_wakeup_bus(pci_bus); + adev = container_of(work, struct acpi_device, wakeup.context.work); + root = acpi_driver_data(adev); + pci_pme_wakeup_bus(root->bus); } /** - * pci_acpi_wake_dev - Wake-up notification handler for PCI devices. + * pci_acpi_wake_dev - PCI device wakeup notification work function. * @handle: ACPI handle of a device the notification is for. - * @event: Type of the signaled event. - * @context: PCI device object to wake up. + * @work: Work item to handle. */ -static void pci_acpi_wake_dev(acpi_handle handle, u32 event, void *context) +static void pci_acpi_wake_dev(struct work_struct *work) { - struct pci_dev *pci_dev = context; + struct acpi_device_wakeup_context *context; + struct pci_dev *pci_dev; - if (event != ACPI_NOTIFY_DEVICE_WAKE || !pci_dev) - return; + context = container_of(work, struct acpi_device_wakeup_context, work); + pci_dev = to_pci_dev(context->dev); if (pci_dev->pme_poll) pci_dev->pme_poll = false; @@ -65,23 +65,12 @@ static void pci_acpi_wake_dev(acpi_handle handle, u32 event, void *context) } /** - * pci_acpi_add_bus_pm_notifier - Register PM notifier for given PCI bus. - * @dev: ACPI device to add the notifier for. - * @pci_bus: PCI bus to walk checking for PME status if an event is signaled. + * pci_acpi_add_bus_pm_notifier - Register PM notifier for root PCI bus. + * @dev: PCI root bridge ACPI device. */ -acpi_status pci_acpi_add_bus_pm_notifier(struct acpi_device *dev, - struct pci_bus *pci_bus) +acpi_status pci_acpi_add_bus_pm_notifier(struct acpi_device *dev) { - return acpi_add_pm_notifier(dev, pci_acpi_wake_bus, pci_bus); -} - -/** - * pci_acpi_remove_bus_pm_notifier - Unregister PCI bus PM notifier. - * @dev: ACPI device to remove the notifier from. - */ -acpi_status pci_acpi_remove_bus_pm_notifier(struct acpi_device *dev) -{ - return acpi_remove_pm_notifier(dev, pci_acpi_wake_bus); + return acpi_add_pm_notifier(dev, NULL, pci_acpi_wake_bus); } /** @@ -92,16 +81,7 @@ acpi_status pci_acpi_remove_bus_pm_notifier(struct acpi_device *dev) acpi_status pci_acpi_add_pm_notifier(struct acpi_device *dev, struct pci_dev *pci_dev) { - return acpi_add_pm_notifier(dev, pci_acpi_wake_dev, pci_dev); -} - -/** - * pci_acpi_remove_pm_notifier - Unregister PCI device PM notifier. - * @dev: ACPI device to remove the notifier from. - */ -acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev) -{ - return acpi_remove_pm_notifier(dev, pci_acpi_wake_dev); + return acpi_add_pm_notifier(dev, &pci_dev->dev, pci_acpi_wake_dev); } phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle) diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index b5714580801a..99780d46abb6 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -315,12 +315,19 @@ struct acpi_device_wakeup_flags { u8 notifier_present:1; /* Wake-up notify handler has been installed */ }; +struct acpi_device_wakeup_context { + struct work_struct work; + struct device *dev; +}; + struct acpi_device_wakeup { acpi_handle gpe_device; u64 gpe_number; u64 sleep_state; struct list_head resources; struct acpi_device_wakeup_flags flags; + struct acpi_device_wakeup_context context; + struct wakeup_source *ws; int prepare_count; }; @@ -510,20 +517,18 @@ 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 -acpi_status acpi_add_pm_notifier(struct acpi_device *adev, - acpi_notify_handler handler, void *context); -acpi_status acpi_remove_pm_notifier(struct acpi_device *adev, - acpi_notify_handler handler); +acpi_status acpi_add_pm_notifier(struct acpi_device *adev, struct device *dev, + void (*work_func)(struct work_struct *work)); +acpi_status acpi_remove_pm_notifier(struct acpi_device *adev); int acpi_pm_device_sleep_state(struct device *, int *, int); #else static inline acpi_status acpi_add_pm_notifier(struct acpi_device *adev, - acpi_notify_handler handler, - void *context) + struct device *dev, + void (*work_func)(struct work_struct *work)) { return AE_SUPPORT; } -static inline acpi_status acpi_remove_pm_notifier(struct acpi_device *adev, - acpi_notify_handler handler) +static inline acpi_status acpi_remove_pm_notifier(struct acpi_device *adev) { return AE_SUPPORT; } diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index 637a608ded0b..64dacb7288a6 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -11,12 +11,17 @@ #include #ifdef CONFIG_ACPI -extern acpi_status pci_acpi_add_bus_pm_notifier(struct acpi_device *dev, - struct pci_bus *pci_bus); -extern acpi_status pci_acpi_remove_bus_pm_notifier(struct acpi_device *dev); +extern acpi_status pci_acpi_add_bus_pm_notifier(struct acpi_device *dev); +static inline acpi_status pci_acpi_remove_bus_pm_notifier(struct acpi_device *dev) +{ + return acpi_remove_pm_notifier(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); +static inline acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev) +{ + return acpi_remove_pm_notifier(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 f35cec255557d1037ff0d772edfd6e7b1e92cdc0 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 23 Jul 2014 01:00:53 +0200 Subject: ACPI / PM: Always enable wakeup GPEs when enabling device wakeup Wakeup GPEs are currently only enabled when setting up devices for remote wakeup at run time. During system-wide transitions they are enabled by ACPICA at the very last stage of suspend (before asking the BIOS to take over). Of course, that only works for system sleep states supported by ACPI, so in particular it doesn't work for the "freeze" sleep state. For this reason, modify the ACPI core device PM code to enable wakeup GPEs for devices when setting them up for wakeup regardless of whether that is remote wakeup at runtime or system wakeup. That allows the same device wakeup setup routine to be used for both runtime PM and system-wide PM and makes it possible to reduce code size quite a bit. This make ACPI-based PCI Wake-on-LAN work with the "freeze" sleep state on my venerable Toshiba Portege R500 and should help other systems too. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/device_pm.c | 49 +++++++++++++++++------------------------------- include/acpi/acpi_bus.h | 11 ----------- 2 files changed, 17 insertions(+), 43 deletions(-) (limited to 'include') diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 366de0b0c39b..ad2810900799 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -638,7 +638,6 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in) } EXPORT_SYMBOL(acpi_pm_device_sleep_state); -#ifdef CONFIG_PM_RUNTIME /** * acpi_pm_notify_work_func - ACPI devices wakeup notification work function. * @work: Work item to handle. @@ -655,8 +654,9 @@ static void acpi_pm_notify_work_func(struct work_struct *work) } /** - * __acpi_device_run_wake - Enable/disable runtime remote wakeup for device. - * @adev: ACPI device to enable/disable the remote wakeup for. + * acpi_device_wakeup - Enable/disable wakeup functionality for device. + * @adev: ACPI device to enable/disable wakeup functionality for. + * @target_state: State the system is transitioning into. * @enable: Whether to enable or disable the wakeup functionality. * * Enable/disable the GPE associated with @adev so that it can generate @@ -666,7 +666,8 @@ static void acpi_pm_notify_work_func(struct work_struct *work) * Callers must ensure that @adev is a valid ACPI device node before executing * this function. */ -int __acpi_device_run_wake(struct acpi_device *adev, bool enable) +static int acpi_device_wakeup(struct acpi_device *adev, u32 target_state, + bool enable) { struct acpi_device_wakeup *wakeup = &adev->wakeup; @@ -674,7 +675,7 @@ int __acpi_device_run_wake(struct acpi_device *adev, bool enable) acpi_status res; int error; - error = acpi_enable_wakeup_device_power(adev, ACPI_STATE_S0); + error = acpi_enable_wakeup_device_power(adev, target_state); if (error) return error; @@ -690,6 +691,7 @@ int __acpi_device_run_wake(struct acpi_device *adev, bool enable) return 0; } +#ifdef CONFIG_PM_RUNTIME /** * acpi_pm_device_run_wake - Enable/disable remote wakeup for given device. * @dev: Device to enable/disable the platform to wake up. @@ -710,28 +712,12 @@ int acpi_pm_device_run_wake(struct device *phys_dev, bool enable) return -ENODEV; } - return __acpi_device_run_wake(adev, enable); + return acpi_device_wakeup(adev, enable, ACPI_STATE_S0); } EXPORT_SYMBOL(acpi_pm_device_run_wake); -#else -static inline void acpi_pm_notify_work_func(struct work_struct *work) {} #endif /* CONFIG_PM_RUNTIME */ #ifdef CONFIG_PM_SLEEP -/** - * __acpi_device_sleep_wake - Enable or disable device to wake up the system. - * @dev: Device to enable/desible to wake up the system. - * @target_state: System state the device is supposed to wake up from. - * @enable: Whether to enable or disable @dev to wake up the system. - */ -int __acpi_device_sleep_wake(struct acpi_device *adev, u32 target_state, - bool enable) -{ - return enable ? - acpi_enable_wakeup_device_power(adev, target_state) : - acpi_disable_wakeup_device_power(adev); -} - /** * acpi_pm_device_sleep_wake - Enable or disable device to wake up the system. * @dev: Device to enable/desible to wake up the system from sleep states. @@ -752,8 +738,7 @@ int acpi_pm_device_sleep_wake(struct device *dev, bool enable) return -ENODEV; } - error = __acpi_device_sleep_wake(adev, acpi_target_system_state(), - enable); + error = acpi_device_wakeup(adev, acpi_target_system_state(), enable); if (!error) dev_info(dev, "System wakeup %s by ACPI\n", enable ? "enabled" : "disabled"); @@ -811,13 +796,13 @@ int acpi_dev_runtime_suspend(struct device *dev) remote_wakeup = dev_pm_qos_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP) > PM_QOS_FLAGS_NONE; - error = __acpi_device_run_wake(adev, remote_wakeup); + error = acpi_device_wakeup(adev, ACPI_STATE_S0, remote_wakeup); if (remote_wakeup && error) return -EAGAIN; error = acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0); if (error) - __acpi_device_run_wake(adev, false); + acpi_device_wakeup(adev, ACPI_STATE_S0, false); return error; } @@ -840,7 +825,7 @@ int acpi_dev_runtime_resume(struct device *dev) return 0; error = acpi_dev_pm_full_power(adev); - __acpi_device_run_wake(adev, false); + acpi_device_wakeup(adev, ACPI_STATE_S0, false); return error; } EXPORT_SYMBOL_GPL(acpi_dev_runtime_resume); @@ -896,13 +881,13 @@ int acpi_dev_suspend_late(struct device *dev) target_state = acpi_target_system_state(); wakeup = device_may_wakeup(dev); - error = __acpi_device_sleep_wake(adev, target_state, wakeup); + error = acpi_device_wakeup(adev, target_state, wakeup); if (wakeup && error) return error; error = acpi_dev_pm_low_power(dev, adev, target_state); if (error) - __acpi_device_sleep_wake(adev, ACPI_STATE_UNKNOWN, false); + acpi_device_wakeup(adev, ACPI_STATE_UNKNOWN, false); return error; } @@ -925,7 +910,7 @@ int acpi_dev_resume_early(struct device *dev) return 0; error = acpi_dev_pm_full_power(adev); - __acpi_device_sleep_wake(adev, ACPI_STATE_UNKNOWN, false); + acpi_device_wakeup(adev, ACPI_STATE_UNKNOWN, false); return error; } EXPORT_SYMBOL_GPL(acpi_dev_resume_early); @@ -1088,7 +1073,7 @@ int acpi_dev_pm_attach(struct device *dev, bool power_on) dev->pm_domain = &acpi_general_pm_domain; if (power_on) { acpi_dev_pm_full_power(adev); - __acpi_device_run_wake(adev, false); + acpi_device_wakeup(adev, ACPI_STATE_S0, false); } return 0; } @@ -1122,7 +1107,7 @@ void acpi_dev_pm_detach(struct device *dev, bool power_off) */ dev_pm_qos_hide_latency_limit(dev); dev_pm_qos_hide_flags(dev); - __acpi_device_run_wake(adev, false); + acpi_device_wakeup(adev, ACPI_STATE_S0, false); acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0); } } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 99780d46abb6..a0b08457452d 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -543,13 +543,8 @@ static inline int acpi_pm_device_sleep_state(struct device *d, int *p, int m) #endif #ifdef CONFIG_PM_RUNTIME -int __acpi_device_run_wake(struct acpi_device *, bool); int acpi_pm_device_run_wake(struct device *, bool); #else -static inline int __acpi_device_run_wake(struct acpi_device *adev, bool en) -{ - return -ENODEV; -} static inline int acpi_pm_device_run_wake(struct device *dev, bool enable) { return -ENODEV; @@ -557,14 +552,8 @@ static inline int acpi_pm_device_run_wake(struct device *dev, bool enable) #endif #ifdef CONFIG_PM_SLEEP -int __acpi_device_sleep_wake(struct acpi_device *, u32, bool); int acpi_pm_device_sleep_wake(struct device *, bool); #else -static inline int __acpi_device_sleep_wake(struct acpi_device *adev, - u32 target_state, bool enable) -{ - return -ENODEV; -} static inline int acpi_pm_device_sleep_wake(struct device *dev, bool enable) { return -ENODEV; -- cgit v1.2.3 From 95f7687b209954a8ec94b4974d14fdff005ebaac Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Tue, 20 May 2014 22:21:57 +0200 Subject: NFC: hci: Add stop_poll HCI operand. stop_poll allows to stop CLF reader polling. Some other operations might be necessary for some CLF to stop polling. For example in card mode. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- include/net/nfc/hci.h | 1 + net/nfc/hci/core.c | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index 61286db54388..7ee8f4cc610b 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -37,6 +37,7 @@ struct nfc_hci_ops { int (*xmit) (struct nfc_hci_dev *hdev, struct sk_buff *skb); int (*start_poll) (struct nfc_hci_dev *hdev, u32 im_protocols, u32 tm_protocols); + void (*stop_poll) (struct nfc_hci_dev *hdev); int (*dep_link_up)(struct nfc_hci_dev *hdev, struct nfc_target *target, u8 comm_mode, u8 *gb, size_t gb_len); int (*dep_link_down)(struct nfc_hci_dev *hdev); diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index 47403705197e..117708263ced 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -553,8 +553,11 @@ static void hci_stop_poll(struct nfc_dev *nfc_dev) { struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev); - nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, - NFC_HCI_EVT_END_OPERATION, NULL, 0); + if (hdev->ops->stop_poll) + hdev->ops->stop_poll(hdev); + else + nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, + NFC_HCI_EVT_END_OPERATION, NULL, 0); } static int hci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, -- cgit v1.2.3 From daba25d6e09e923ca4458211ca086eeb8bef8b5a Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 16 Jul 2014 16:58:00 +0800 Subject: ACPICA: Linux: Add stub implementation of ACPICA 64-bit mathematics. This patch adds default 64-bit mathematics in aclinux.h using do_div(). As do_div() can be used for all Linux architectures, this can also be used as stub macros for ACPICA 64-bit mathematics. These macros are required by drivers/acpi/utmath.c when ACPI_USE_NATIVE_DIVIDE is not defined. It is used by ACPICA, so currently this is only meaningful to CONFIG_ACPI builds. So the kernel will not use these macros unless CONFIG_ACPI is defined and ACPI_USE_DIVIDE is not defined. For 64-bit kernels: In include/acpi/actypes.h, for ACPI_MACHINE_WIDTH=64, ACPI_USE_NATIVE_DIVIDE will be defined, thus these macros are not used. In include/acpi/platform/aclinux.h, for __KERNEL__ surrounded code, ACPI_MACHINE_WIDTH is defined to be BITS_PER_LONG. So all 64-bit kernels do not use these macros. For 32-bit kernels: As mentioned above, these macros will be used when BITS_PER_LONG is 32. Thus currently the i328 kernels are the only users for these macros. But they won't use this default implementation provided by this patch, because in arch/x86/include/asm/acenv.h, there are already overrides implemented. So these default macros are not used by 32-bit x86 (i386) kernels. These macros will only be used by future non x86 32-bit architectures that try to support ACPI in Linux kernel. During the period they do not have arch specific implementations of such macros, we can avoid build errors for them. And since they can see ACPICA functioning without implementing any arch specific environment tunings, we can also avoid function errors for them. As this implementation is not performance friendly, those architectures still need to implement real support in the end. Signed-off-by: Lv Zheng [rjw: Changelog] Signed-off-by: Rafael J. Wysocki --- include/acpi/platform/aclinuxex.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'include') diff --git a/include/acpi/platform/aclinuxex.h b/include/acpi/platform/aclinuxex.h index 191e741cfa0e..568d4b886712 100644 --- a/include/acpi/platform/aclinuxex.h +++ b/include/acpi/platform/aclinuxex.h @@ -46,6 +46,28 @@ #ifdef __KERNEL__ +#ifndef ACPI_USE_NATIVE_DIVIDE + +#ifndef ACPI_DIV_64_BY_32 +#define ACPI_DIV_64_BY_32(n_hi, n_lo, d32, q32, r32) \ + do { \ + u64 (__n) = ((u64) n_hi) << 32 | (n_lo); \ + (r32) = do_div ((__n), (d32)); \ + (q32) = (u32) (__n); \ + } while (0) +#endif + +#ifndef ACPI_SHIFT_RIGHT_64 +#define ACPI_SHIFT_RIGHT_64(n_hi, n_lo) \ + do { \ + (n_lo) >>= 1; \ + (n_lo) |= (((n_hi) & 1) << 31); \ + (n_hi) >>= 1; \ + } while (0) +#endif + +#endif + /* * Overrides for in-kernel ACPICA */ -- cgit v1.2.3 From d334c823b27401721591e0f1220050a41af08165 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 16 Jul 2014 16:58:08 +0800 Subject: ACPICA: Linux: Add support to exclude inclusion. The forthcoming patch will make to be visible to all kernel source code. Thus for the architectures that do not support ACPI and haven't implemented , we need to make it excluded. Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- arch/ia64/include/asm/acenv.h | 4 ---- arch/x86/include/asm/acenv.h | 4 ---- include/acpi/platform/aclinux.h | 2 ++ 3 files changed, 2 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/arch/ia64/include/asm/acenv.h b/arch/ia64/include/asm/acenv.h index 3f9eaeec9873..35ff13afbf34 100644 --- a/arch/ia64/include/asm/acenv.h +++ b/arch/ia64/include/asm/acenv.h @@ -19,8 +19,6 @@ /* Asm macros */ -#ifdef CONFIG_ACPI - static inline int ia64_acpi_acquire_global_lock(unsigned int *lock) { @@ -51,6 +49,4 @@ ia64_acpi_release_global_lock(unsigned int *lock) #define ACPI_RELEASE_GLOBAL_LOCK(facs, Acq) \ ((Acq) = ia64_acpi_release_global_lock(&facs->global_lock)) -#endif - #endif /* _ASM_IA64_ACENV_H */ diff --git a/arch/x86/include/asm/acenv.h b/arch/x86/include/asm/acenv.h index 66873297e9f5..1b010a859b8b 100644 --- a/arch/x86/include/asm/acenv.h +++ b/arch/x86/include/asm/acenv.h @@ -18,8 +18,6 @@ #define ACPI_FLUSH_CPU_CACHE() wbinvd() -#ifdef CONFIG_ACPI - int __acpi_acquire_global_lock(unsigned int *lock); int __acpi_release_global_lock(unsigned int *lock); @@ -44,6 +42,4 @@ int __acpi_release_global_lock(unsigned int *lock); : "=r"(n_hi), "=r"(n_lo) \ : "0"(n_hi), "1"(n_lo)) -#endif - #endif /* _ASM_X86_ACENV_H */ diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h index cd1f052d55bb..dfba35476b8a 100644 --- a/include/acpi/platform/aclinux.h +++ b/include/acpi/platform/aclinux.h @@ -70,7 +70,9 @@ #ifdef EXPORT_ACPI_INTERFACES #include #endif +#ifdef CONFIG_ACPI #include +#endif #ifndef CONFIG_ACPI -- cgit v1.2.3 From 633adaba49d46dcaa4289de5b25c562b54ff575b Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 16 Jul 2014 16:58:30 +0800 Subject: ACPICA: Linux: Allow ACPICA inclusion for CONFIG_ACPI=n builds. This patch moves out of CONFIG_ACPI condition so that all ACPICA prototypes can be seen by the CONFIG_ACPI=n Linux kernel builds. Note that we can do this because ACPICA has implemented stubs for all ACPICA prototypes that are currently referenced by the Linux kernel. Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- include/linux/acpi.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 358c01b971db..5320153c311b 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -29,17 +29,17 @@ #include /* for struct resource */ #include -#ifdef CONFIG_ACPI - #ifndef _LINUX #define _LINUX #endif +#include + +#ifdef CONFIG_ACPI #include #include #include -#include #include #include #include -- cgit v1.2.3 From f997ea54479e85076873a70fe53e66c9153e6f00 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 16 Jul 2014 16:58:40 +0800 Subject: ACPI / SFI: Fix wrong inclusion in SFI/ACPI wrapper - table definitions. This patch removes inclusions from as has already included it for CONFIG_ACPI=n builds. Cc: Len Brown Cc: sfi-devel@simplefirmware.org Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- include/linux/sfi_acpi.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include') diff --git a/include/linux/sfi_acpi.h b/include/linux/sfi_acpi.h index 4723bbfa1c26..a6e555cbe05c 100644 --- a/include/linux/sfi_acpi.h +++ b/include/linux/sfi_acpi.h @@ -63,8 +63,6 @@ #include #ifdef CONFIG_SFI -#include /* FIXME: inclusion should be removed */ - extern int sfi_acpi_table_parse(char *signature, char *oem_id, char *oem_table_id, int (*handler)(struct acpi_table_header *)); @@ -78,7 +76,6 @@ static inline int __init acpi_sfi_table_parse(char *signature, return sfi_acpi_table_parse(signature, NULL, NULL, handler); } #else /* !CONFIG_SFI */ - static inline int sfi_acpi_table_parse(char *signature, char *oem_id, char *oem_table_id, int (*handler)(struct acpi_table_header *)) -- cgit v1.2.3 From 417b4a73b62760db67512892c32f8acc008ab54e Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 16 Jul 2014 16:58:53 +0800 Subject: ACPI: Add support to force header inclusion rules for . As there is only CONFIG_ACPI=n processing in the , it is not safe to include directly for source out of Linux ACPI subsystems. This patch adds error messaging to warn developers of such wrong inclusions. In order not to be bisected and reverted as a wrong commit, warning messages are carefully split into a seperate patch other than the wrong inclusion cleanups. Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/Makefile | 2 +- include/acpi/platform/aclinux.h | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile index 8bb43f06e11f..87a24ac21363 100644 --- a/drivers/acpi/acpica/Makefile +++ b/drivers/acpi/acpica/Makefile @@ -2,7 +2,7 @@ # Makefile for ACPICA Core interpreter # -ccflags-y := -Os +ccflags-y := -Os -DBUILDING_ACPICA ccflags-$(CONFIG_ACPI_DEBUG) += -DACPI_DEBUG_OUTPUT # use acpi.o to put all files here into acpi.o modparam namespace diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h index dfba35476b8a..1ba7c190c2cc 100644 --- a/include/acpi/platform/aclinux.h +++ b/include/acpi/platform/aclinux.h @@ -44,6 +44,16 @@ #ifndef __ACLINUX_H__ #define __ACLINUX_H__ +#ifdef __KERNEL__ + +/* ACPICA external files should not include ACPICA headers directly. */ + +#if !defined(BUILDING_ACPICA) && !defined(_LINUX_ACPI_H) +#error "Please don't include directly, include instead." +#endif + +#endif + /* Common (in-kernel/user-space) ACPICA configuration */ #define ACPI_USE_SYSTEM_CLIBRARY -- cgit v1.2.3 From f63bac94bfe2b7f98d28e5c7d3432a5060841f51 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Mon, 21 Jul 2014 21:22:29 -0700 Subject: NFC: digital: Remove extra blank line Remove extra blank line that was inadvertently added by a recent commit. CC: Thierry Escande Signed-off-by: Mark A. Greer Signed-off-by: Samuel Ortiz --- include/net/nfc/digital.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/net/nfc/digital.h b/include/net/nfc/digital.h index 2bc31d10f9eb..575d668b7852 100644 --- a/include/net/nfc/digital.h +++ b/include/net/nfc/digital.h @@ -67,7 +67,6 @@ enum { NFC_DIGITAL_FRAMING_NFCB, NFC_DIGITAL_FRAMING_NFCB_T4T, - NFC_DIGITAL_FRAMING_LAST, }; -- cgit v1.2.3 From bf30a67c947ed57c1cf7c68a47dc24331458037e Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Mon, 21 Jul 2014 21:24:39 -0700 Subject: NFC: digital: Add 'tg_listen_md' and 'tg_get_rf_tech' driver hooks The digital layer of the NFC subsystem currently supports a 'tg_listen_mdaa' driver hook that supports devices that can do mode detection and automatic anticollision. However, there are some devices that can do mode detection but not automatic anitcollision so add the 'tg_listen_md' hook to support those devices. In order for the digital layer to get the RF technology detected by the device from the driver, add the 'tg_get_rf_tech' hook. It is only valid to call this hook immediately after a successful call to 'tg_listen_md'. CC: Thierry Escande Signed-off-by: Mark A. Greer Signed-off-by: Samuel Ortiz --- include/net/nfc/digital.h | 12 ++++++++ net/nfc/digital.h | 3 ++ net/nfc/digital_core.c | 16 +++++++++- net/nfc/digital_technology.c | 71 +++++++++++++++++++++++++++++++++++++++----- 4 files changed, 93 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/nfc/digital.h b/include/net/nfc/digital.h index 575d668b7852..d9a5cf7ac1c4 100644 --- a/include/net/nfc/digital.h +++ b/include/net/nfc/digital.h @@ -127,6 +127,15 @@ typedef void (*nfc_digital_cmd_complete_t)(struct nfc_digital_dev *ddev, * the NFC-DEP ATR_REQ command through cb. The digital stack deducts the RF * tech by analyzing the SoD of the frame containing the ATR_REQ command. * This is an asynchronous function. + * @tg_listen_md: If supported, put the device in automatic listen mode with + * mode detection but without automatic anti-collision. In this mode, the + * device automatically detects the RF technology. What the actual + * RF technology is can be retrieved by calling @tg_get_rf_tech. + * The digital stack will then perform the appropriate anti-collision + * sequence. This is an asynchronous function. + * @tg_get_rf_tech: Required when @tg_listen_md is supported, unused otherwise. + * Return the RF Technology that was detected by the @tg_listen_md call. + * This is a synchronous function. * * @switch_rf: Turns device radio on or off. The stack does not call explicitly * switch_rf to turn the radio on. A call to in|tg_configure_hw must turn @@ -161,6 +170,9 @@ struct nfc_digital_ops { struct digital_tg_mdaa_params *mdaa_params, u16 timeout, nfc_digital_cmd_complete_t cb, void *arg); + int (*tg_listen_md)(struct nfc_digital_dev *ddev, u16 timeout, + nfc_digital_cmd_complete_t cb, void *arg); + int (*tg_get_rf_tech)(struct nfc_digital_dev *ddev, u8 *rf_tech); int (*switch_rf)(struct nfc_digital_dev *ddev, bool on); void (*abort_cmd)(struct nfc_digital_dev *ddev); diff --git a/net/nfc/digital.h b/net/nfc/digital.h index 71ad7eefddd4..3c39c72eb038 100644 --- a/net/nfc/digital.h +++ b/net/nfc/digital.h @@ -29,6 +29,7 @@ #define DIGITAL_CMD_TG_SEND 1 #define DIGITAL_CMD_TG_LISTEN 2 #define DIGITAL_CMD_TG_LISTEN_MDAA 3 +#define DIGITAL_CMD_TG_LISTEN_MD 4 #define DIGITAL_MAX_HEADER_LEN 7 #define DIGITAL_CRC_LEN 2 @@ -121,6 +122,8 @@ int digital_tg_send_dep_res(struct nfc_digital_dev *ddev, struct sk_buff *skb); int digital_tg_listen_nfca(struct nfc_digital_dev *ddev, u8 rf_tech); int digital_tg_listen_nfcf(struct nfc_digital_dev *ddev, u8 rf_tech); +void digital_tg_recv_md_req(struct nfc_digital_dev *ddev, void *arg, + struct sk_buff *resp); typedef u16 (*crc_func_t)(u16, const u8 *, size_t); diff --git a/net/nfc/digital_core.c b/net/nfc/digital_core.c index 361bc37d2db1..009bcf317101 100644 --- a/net/nfc/digital_core.c +++ b/net/nfc/digital_core.c @@ -201,6 +201,11 @@ static void digital_wq_cmd(struct work_struct *work) digital_send_cmd_complete, cmd); break; + case DIGITAL_CMD_TG_LISTEN_MD: + rc = ddev->ops->tg_listen_md(ddev, cmd->timeout, + digital_send_cmd_complete, cmd); + break; + default: pr_err("Unknown cmd type %d\n", cmd->type); return; @@ -293,6 +298,12 @@ static int digital_tg_listen_mdaa(struct nfc_digital_dev *ddev, u8 rf_tech) 500, digital_tg_recv_atr_req, NULL); } +static int digital_tg_listen_md(struct nfc_digital_dev *ddev, u8 rf_tech) +{ + return digital_send_cmd(ddev, DIGITAL_CMD_TG_LISTEN_MD, NULL, NULL, 500, + digital_tg_recv_md_req, NULL); +} + int digital_target_found(struct nfc_digital_dev *ddev, struct nfc_target *target, u8 protocol) { @@ -510,6 +521,9 @@ static int digital_start_poll(struct nfc_dev *nfc_dev, __u32 im_protocols, if (ddev->ops->tg_listen_mdaa) { digital_add_poll_tech(ddev, 0, digital_tg_listen_mdaa); + } else if (ddev->ops->tg_listen_md) { + digital_add_poll_tech(ddev, 0, + digital_tg_listen_md); } else { digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_106A, digital_tg_listen_nfca); @@ -737,7 +751,7 @@ struct nfc_digital_dev *nfc_digital_allocate_device(struct nfc_digital_ops *ops, if (!ops->in_configure_hw || !ops->in_send_cmd || !ops->tg_listen || !ops->tg_configure_hw || !ops->tg_send_cmd || !ops->abort_cmd || - !ops->switch_rf) + !ops->switch_rf || (ops->tg_listen_md && !ops->tg_get_rf_tech)) return NULL; ddev = kzalloc(sizeof(struct nfc_digital_dev), GFP_KERNEL); diff --git a/net/nfc/digital_technology.c b/net/nfc/digital_technology.c index d276518cc8bf..fb58ed2dd41d 100644 --- a/net/nfc/digital_technology.c +++ b/net/nfc/digital_technology.c @@ -1218,33 +1218,48 @@ exit: dev_kfree_skb(resp); } -int digital_tg_listen_nfca(struct nfc_digital_dev *ddev, u8 rf_tech) +static int digital_tg_config_nfca(struct nfc_digital_dev *ddev) { int rc; - rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, rf_tech); + rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, + NFC_DIGITAL_RF_TECH_106A); if (rc) return rc; - rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, - NFC_DIGITAL_FRAMING_NFCA_NFC_DEP); + return digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, + NFC_DIGITAL_FRAMING_NFCA_NFC_DEP); +} + +int digital_tg_listen_nfca(struct nfc_digital_dev *ddev, u8 rf_tech) +{ + int rc; + + rc = digital_tg_config_nfca(ddev); if (rc) return rc; return digital_tg_listen(ddev, 300, digital_tg_recv_sens_req, NULL); } -int digital_tg_listen_nfcf(struct nfc_digital_dev *ddev, u8 rf_tech) +static int digital_tg_config_nfcf(struct nfc_digital_dev *ddev, u8 rf_tech) { int rc; - u8 *nfcid2; rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, rf_tech); if (rc) return rc; - rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, - NFC_DIGITAL_FRAMING_NFCF_NFC_DEP); + return digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, + NFC_DIGITAL_FRAMING_NFCF_NFC_DEP); +} + +int digital_tg_listen_nfcf(struct nfc_digital_dev *ddev, u8 rf_tech) +{ + int rc; + u8 *nfcid2; + + rc = digital_tg_config_nfcf(ddev, rf_tech); if (rc) return rc; @@ -1258,3 +1273,43 @@ int digital_tg_listen_nfcf(struct nfc_digital_dev *ddev, u8 rf_tech) return digital_tg_listen(ddev, 300, digital_tg_recv_sensf_req, nfcid2); } + +void digital_tg_recv_md_req(struct nfc_digital_dev *ddev, void *arg, + struct sk_buff *resp) +{ + u8 rf_tech; + int rc; + + if (IS_ERR(resp)) { + resp = NULL; + goto exit_free_skb; + } + + rc = ddev->ops->tg_get_rf_tech(ddev, &rf_tech); + if (rc) + goto exit_free_skb; + + switch (rf_tech) { + case NFC_DIGITAL_RF_TECH_106A: + rc = digital_tg_config_nfca(ddev); + if (rc) + goto exit_free_skb; + digital_tg_recv_sens_req(ddev, arg, resp); + break; + case NFC_DIGITAL_RF_TECH_212F: + case NFC_DIGITAL_RF_TECH_424F: + rc = digital_tg_config_nfcf(ddev, rf_tech); + if (rc) + goto exit_free_skb; + digital_tg_recv_sensf_req(ddev, arg, resp); + break; + default: + goto exit_free_skb; + } + + return; + +exit_free_skb: + digital_poll_next_tech(ddev); + dev_kfree_skb(resp); +} -- cgit v1.2.3 From 914b881f9452fd615cc597b434fd8c0e12a7dae2 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Thu, 22 May 2014 14:06:33 +0900 Subject: extcon: sm5502: Add support new SM5502 extcon device driver This patch add new SM5502 MUIC(Micro-USB Interface Controller) device by using EXTCON subsystem. The extcon-sm5502 driver is capable of identifying the type of the external power source and attached accessory. An external power sources, such as Deticated Charger or a standard USB port, are able to charge the battery in the smart phone via the connector. Signed-off-by: Chanwoo Choi --- drivers/extcon/Kconfig | 10 + drivers/extcon/Makefile | 1 + drivers/extcon/extcon-sm5502.c | 620 +++++++++++++++++++++++++++++++++++++++++ include/linux/extcon/sm5502.h | 264 ++++++++++++++++++ 4 files changed, 895 insertions(+) create mode 100644 drivers/extcon/extcon-sm5502.c create mode 100644 include/linux/extcon/sm5502.h (limited to 'include') diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index 9125eba6b3d6..6f2f4727de2c 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -70,4 +70,14 @@ config EXTCON_PALMAS Say Y here to enable support for USB peripheral and USB host detection by palmas usb. +config EXTCON_SM5502 + tristate "SM5502 EXTCON support" + select IRQ_DOMAIN + select REGMAP_I2C + select REGMAP_IRQ + help + If you say yes here you get support for the MUIC device of + Silicon Mitus SM5502. The SM5502 is a USB port accessory + detector and switch. + endif # MULTISTATE_SWITCH diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index e48abc6d230f..b38546eb522a 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_EXTCON_MAX14577) += extcon-max14577.o obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o obj-$(CONFIG_EXTCON_PALMAS) += extcon-palmas.o +obj-$(CONFIG_EXTCON_SM5502) += extcon-sm5502.o diff --git a/drivers/extcon/extcon-sm5502.c b/drivers/extcon/extcon-sm5502.c new file mode 100644 index 000000000000..9f318c222b89 --- /dev/null +++ b/drivers/extcon/extcon-sm5502.c @@ -0,0 +1,620 @@ +/* + * extcon-sm5502.c - Silicon Mitus SM5502 extcon drvier to support USB switches + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd + * Author: Chanwoo Choi + * + * 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 +#include +#include +#include +#include + +struct muic_irq { + unsigned int irq; + const char *name; + unsigned int virq; +}; + +struct reg_data { + u8 reg; + unsigned int val; + bool invert; +}; + +struct sm5502_muic_info { + struct device *dev; + struct extcon_dev *edev; + + struct i2c_client *i2c; + struct regmap *regmap; + + struct regmap_irq_chip_data *irq_data; + struct muic_irq *muic_irqs; + unsigned int num_muic_irqs; + int irq; + bool irq_attach; + bool irq_detach; + struct work_struct irq_work; + + struct reg_data *reg_data; + unsigned int num_reg_data; + + struct mutex mutex; +}; + +/* Default value of SM5502 register to bring up MUIC device. */ +static struct reg_data sm5502_reg_data[] = { + { + .reg = SM5502_REG_CONTROL, + .val = SM5502_REG_CONTROL_MASK_INT_MASK, + .invert = false, + }, { + .reg = SM5502_REG_INTMASK1, + .val = SM5502_REG_INTM1_KP_MASK + | SM5502_REG_INTM1_LKP_MASK + | SM5502_REG_INTM1_LKR_MASK, + .invert = true, + }, { + .reg = SM5502_REG_INTMASK2, + .val = SM5502_REG_INTM2_VBUS_DET_MASK + | SM5502_REG_INTM2_REV_ACCE_MASK + | SM5502_REG_INTM2_ADC_CHG_MASK + | SM5502_REG_INTM2_STUCK_KEY_MASK + | SM5502_REG_INTM2_STUCK_KEY_RCV_MASK + | SM5502_REG_INTM2_MHL_MASK, + .invert = true, + }, + { } +}; + +/* List of detectable cables */ +enum { + EXTCON_CABLE_USB = 0, + EXTCON_CABLE_USB_HOST, + EXTCON_CABLE_TA, + + EXTCON_CABLE_END, +}; + +static const char *sm5502_extcon_cable[] = { + [EXTCON_CABLE_USB] = "USB", + [EXTCON_CABLE_USB_HOST] = "USB-Host", + [EXTCON_CABLE_TA] = "TA", + NULL, +}; + +/* Define supported accessory type */ +enum sm5502_muic_acc_type { + SM5502_MUIC_ADC_GROUND = 0x0, + SM5502_MUIC_ADC_SEND_END_BUTTON, + SM5502_MUIC_ADC_REMOTE_S1_BUTTON, + SM5502_MUIC_ADC_REMOTE_S2_BUTTON, + SM5502_MUIC_ADC_REMOTE_S3_BUTTON, + SM5502_MUIC_ADC_REMOTE_S4_BUTTON, + SM5502_MUIC_ADC_REMOTE_S5_BUTTON, + SM5502_MUIC_ADC_REMOTE_S6_BUTTON, + SM5502_MUIC_ADC_REMOTE_S7_BUTTON, + SM5502_MUIC_ADC_REMOTE_S8_BUTTON, + SM5502_MUIC_ADC_REMOTE_S9_BUTTON, + SM5502_MUIC_ADC_REMOTE_S10_BUTTON, + SM5502_MUIC_ADC_REMOTE_S11_BUTTON, + SM5502_MUIC_ADC_REMOTE_S12_BUTTON, + SM5502_MUIC_ADC_RESERVED_ACC_1, + SM5502_MUIC_ADC_RESERVED_ACC_2, + SM5502_MUIC_ADC_RESERVED_ACC_3, + SM5502_MUIC_ADC_RESERVED_ACC_4, + SM5502_MUIC_ADC_RESERVED_ACC_5, + SM5502_MUIC_ADC_AUDIO_TYPE2, + SM5502_MUIC_ADC_PHONE_POWERED_DEV, + SM5502_MUIC_ADC_TTY_CONVERTER, + SM5502_MUIC_ADC_UART_CABLE, + SM5502_MUIC_ADC_TYPE1_CHARGER, + SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_USB, + SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_USB, + SM5502_MUIC_ADC_AUDIO_VIDEO_CABLE, + SM5502_MUIC_ADC_TYPE2_CHARGER, + SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_UART, + SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_UART, + SM5502_MUIC_ADC_AUDIO_TYPE1, + SM5502_MUIC_ADC_OPEN = 0x1f, + + /* The below accessories have same ADC value (0x1f or 0x1e). + So, Device type1 is used to separate specific accessory. */ + /* |---------|--ADC| */ + /* | [7:5]|[4:0]| */ + SM5502_MUIC_ADC_AUDIO_TYPE1_FULL_REMOTE = 0x3e, /* | 001|11110| */ + SM5502_MUIC_ADC_AUDIO_TYPE1_SEND_END = 0x5e, /* | 010|11110| */ + /* |Dev Type1|--ADC| */ + SM5502_MUIC_ADC_OPEN_USB = 0x5f, /* | 010|11111| */ + SM5502_MUIC_ADC_OPEN_TA = 0xdf, /* | 110|11111| */ + SM5502_MUIC_ADC_OPEN_USB_OTG = 0xff, /* | 111|11111| */ +}; + +/* List of supported interrupt for SM5502 */ +static struct muic_irq sm5502_muic_irqs[] = { + { SM5502_IRQ_INT1_ATTACH, "muic-attach" }, + { SM5502_IRQ_INT1_DETACH, "muic-detach" }, + { SM5502_IRQ_INT1_KP, "muic-kp" }, + { SM5502_IRQ_INT1_LKP, "muic-lkp" }, + { SM5502_IRQ_INT1_LKR, "muic-lkr" }, + { SM5502_IRQ_INT1_OVP_EVENT, "muic-ovp-event" }, + { SM5502_IRQ_INT1_OCP_EVENT, "muic-ocp-event" }, + { SM5502_IRQ_INT1_OVP_OCP_DIS, "muic-ovp-ocp-dis" }, + { SM5502_IRQ_INT2_VBUS_DET, "muic-vbus-det" }, + { SM5502_IRQ_INT2_REV_ACCE, "muic-rev-acce" }, + { SM5502_IRQ_INT2_ADC_CHG, "muic-adc-chg" }, + { SM5502_IRQ_INT2_STUCK_KEY, "muic-stuck-key" }, + { SM5502_IRQ_INT2_STUCK_KEY_RCV, "muic-stuck-key-rcv" }, + { SM5502_IRQ_INT2_MHL, "muic-mhl" }, +}; + +/* Define interrupt list of SM5502 to register regmap_irq */ +static const struct regmap_irq sm5502_irqs[] = { + /* INT1 interrupts */ + { .reg_offset = 0, .mask = SM5502_IRQ_INT1_ATTACH_MASK, }, + { .reg_offset = 0, .mask = SM5502_IRQ_INT1_DETACH_MASK, }, + { .reg_offset = 0, .mask = SM5502_IRQ_INT1_KP_MASK, }, + { .reg_offset = 0, .mask = SM5502_IRQ_INT1_LKP_MASK, }, + { .reg_offset = 0, .mask = SM5502_IRQ_INT1_LKR_MASK, }, + { .reg_offset = 0, .mask = SM5502_IRQ_INT1_OVP_EVENT_MASK, }, + { .reg_offset = 0, .mask = SM5502_IRQ_INT1_OCP_EVENT_MASK, }, + { .reg_offset = 0, .mask = SM5502_IRQ_INT1_OVP_OCP_DIS_MASK, }, + + /* INT2 interrupts */ + { .reg_offset = 1, .mask = SM5502_IRQ_INT2_VBUS_DET_MASK,}, + { .reg_offset = 1, .mask = SM5502_IRQ_INT2_REV_ACCE_MASK, }, + { .reg_offset = 1, .mask = SM5502_IRQ_INT2_ADC_CHG_MASK, }, + { .reg_offset = 1, .mask = SM5502_IRQ_INT2_STUCK_KEY_MASK, }, + { .reg_offset = 1, .mask = SM5502_IRQ_INT2_STUCK_KEY_RCV_MASK, }, + { .reg_offset = 1, .mask = SM5502_IRQ_INT2_MHL_MASK, }, +}; + +static const struct regmap_irq_chip sm5502_muic_irq_chip = { + .name = "sm5502", + .status_base = SM5502_REG_INT1, + .mask_base = SM5502_REG_INTMASK1, + .mask_invert = false, + .num_regs = 2, + .irqs = sm5502_irqs, + .num_irqs = ARRAY_SIZE(sm5502_irqs), +}; + +/* Define regmap configuration of SM5502 for I2C communication */ +static bool sm5502_muic_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SM5502_REG_INTMASK1: + case SM5502_REG_INTMASK2: + return true; + default: + break; + } + return false; +} + +static const struct regmap_config sm5502_muic_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .volatile_reg = sm5502_muic_volatile_reg, + .max_register = SM5502_REG_END, +}; + +/* Return cable type of attached or detached accessories */ +static unsigned int sm5502_muic_get_cable_type(struct sm5502_muic_info *info) +{ + unsigned int cable_type = -1, adc, dev_type1; + int ret; + + /* Read ADC value according to external cable or button */ + ret = regmap_read(info->regmap, SM5502_REG_ADC, &adc); + if (ret) { + dev_err(info->dev, "failed to read ADC register\n"); + return ret; + } + + /* + * If ADC is SM5502_MUIC_ADC_GROUND(0x0), external cable hasn't + * connected with to MUIC device. + */ + cable_type &= SM5502_REG_ADC_MASK; + if (cable_type == SM5502_MUIC_ADC_GROUND) + return SM5502_MUIC_ADC_GROUND; + + switch (cable_type) { + case SM5502_MUIC_ADC_GROUND: + case SM5502_MUIC_ADC_SEND_END_BUTTON: + case SM5502_MUIC_ADC_REMOTE_S1_BUTTON: + case SM5502_MUIC_ADC_REMOTE_S2_BUTTON: + case SM5502_MUIC_ADC_REMOTE_S3_BUTTON: + case SM5502_MUIC_ADC_REMOTE_S4_BUTTON: + case SM5502_MUIC_ADC_REMOTE_S5_BUTTON: + case SM5502_MUIC_ADC_REMOTE_S6_BUTTON: + case SM5502_MUIC_ADC_REMOTE_S7_BUTTON: + case SM5502_MUIC_ADC_REMOTE_S8_BUTTON: + case SM5502_MUIC_ADC_REMOTE_S9_BUTTON: + case SM5502_MUIC_ADC_REMOTE_S10_BUTTON: + case SM5502_MUIC_ADC_REMOTE_S11_BUTTON: + case SM5502_MUIC_ADC_REMOTE_S12_BUTTON: + case SM5502_MUIC_ADC_RESERVED_ACC_1: + case SM5502_MUIC_ADC_RESERVED_ACC_2: + case SM5502_MUIC_ADC_RESERVED_ACC_3: + case SM5502_MUIC_ADC_RESERVED_ACC_4: + case SM5502_MUIC_ADC_RESERVED_ACC_5: + case SM5502_MUIC_ADC_AUDIO_TYPE2: + case SM5502_MUIC_ADC_PHONE_POWERED_DEV: + case SM5502_MUIC_ADC_TTY_CONVERTER: + case SM5502_MUIC_ADC_UART_CABLE: + case SM5502_MUIC_ADC_TYPE1_CHARGER: + case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_USB: + case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_USB: + case SM5502_MUIC_ADC_AUDIO_VIDEO_CABLE: + case SM5502_MUIC_ADC_TYPE2_CHARGER: + case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_UART: + case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_UART: + break; + case SM5502_MUIC_ADC_AUDIO_TYPE1: + /* + * Check whether cable type is + * SM5502_MUIC_ADC_AUDIO_TYPE1_FULL_REMOTE + * or SM5502_MUIC_ADC_AUDIO_TYPE1_SEND_END + * by using Button event. + */ + break; + case SM5502_MUIC_ADC_OPEN: + ret = regmap_read(info->regmap, SM5502_REG_DEV_TYPE1, + &dev_type1); + if (ret) { + dev_err(info->dev, "failed to read DEV_TYPE1 reg\n"); + return ret; + } + + switch (dev_type1) { + case SM5502_REG_DEV_TYPE1_USB_SDP_MASK: + cable_type = SM5502_MUIC_ADC_OPEN_USB; + break; + case SM5502_REG_DEV_TYPE1_DEDICATED_CHG_MASK: + cable_type = SM5502_MUIC_ADC_OPEN_TA; + break; + case SM5502_REG_DEV_TYPE1_USB_OTG_MASK: + cable_type = SM5502_MUIC_ADC_OPEN_USB_OTG; + break; + default: + dev_dbg(info->dev, + "cannot identify the cable type: adc(0x%x) " + "dev_type1(0x%x)\n", adc, dev_type1); + return -EINVAL; + }; + break; + default: + dev_err(info->dev, + "failed to identify the cable type: adc(0x%x)\n", adc); + return -EINVAL; + }; + + return cable_type; +} + +static int sm5502_muic_cable_handler(struct sm5502_muic_info *info, + bool attached) +{ + static unsigned int prev_cable_type = SM5502_MUIC_ADC_GROUND; + const char **cable_names = info->edev->supported_cable; + unsigned int cable_type = SM5502_MUIC_ADC_GROUND; + unsigned int idx = 0; + + if (!cable_names) + return 0; + + /* Get the type of attached or detached cable */ + if (attached) + cable_type = sm5502_muic_get_cable_type(info); + else if (!attached) + cable_type = prev_cable_type; + prev_cable_type = cable_type; + + switch (cable_type) { + case SM5502_MUIC_ADC_OPEN_USB: + idx = EXTCON_CABLE_USB; + break; + case SM5502_MUIC_ADC_OPEN_TA: + idx = EXTCON_CABLE_TA; + break; + case SM5502_MUIC_ADC_OPEN_USB_OTG: + idx = EXTCON_CABLE_USB_HOST; + break; + default: + dev_dbg(info->dev, + "cannot handle this cable_type (0x%x)\n", cable_type); + return 0; + }; + + extcon_set_cable_state(info->edev, cable_names[idx], attached); + + return 0; +} + +static void sm5502_muic_irq_work(struct work_struct *work) +{ + struct sm5502_muic_info *info = container_of(work, + struct sm5502_muic_info, irq_work); + int ret = 0; + + if (!info->edev) + return; + + mutex_lock(&info->mutex); + + /* Detect attached or detached cables */ + if (info->irq_attach) { + ret = sm5502_muic_cable_handler(info, true); + info->irq_attach = false; + } + if (info->irq_detach) { + ret = sm5502_muic_cable_handler(info, false); + info->irq_detach = false; + } + + if (ret < 0) + dev_err(info->dev, "failed to handle MUIC interrupt\n"); + + mutex_unlock(&info->mutex); + + return; +} + +/* + * Sets irq_attach or irq_detach in sm5502_muic_info and returns 0. + * Returns -ESRCH if irq_type does not match registered IRQ for this dev type. + */ +static int sm5502_parse_irq(struct sm5502_muic_info *info, int irq_type) +{ + switch (irq_type) { + case SM5502_IRQ_INT1_ATTACH: + info->irq_attach = true; + break; + case SM5502_IRQ_INT1_DETACH: + info->irq_detach = true; + break; + case SM5502_IRQ_INT1_KP: + case SM5502_IRQ_INT1_LKP: + case SM5502_IRQ_INT1_LKR: + case SM5502_IRQ_INT1_OVP_EVENT: + case SM5502_IRQ_INT1_OCP_EVENT: + case SM5502_IRQ_INT1_OVP_OCP_DIS: + case SM5502_IRQ_INT2_VBUS_DET: + case SM5502_IRQ_INT2_REV_ACCE: + case SM5502_IRQ_INT2_ADC_CHG: + case SM5502_IRQ_INT2_STUCK_KEY: + case SM5502_IRQ_INT2_STUCK_KEY_RCV: + case SM5502_IRQ_INT2_MHL: + default: + break; + } + + return 0; +} + +static irqreturn_t sm5502_muic_irq_handler(int irq, void *data) +{ + struct sm5502_muic_info *info = data; + int i, irq_type = -1, ret; + + for (i = 0; i < info->num_muic_irqs; i++) + if (irq == info->muic_irqs[i].virq) + irq_type = info->muic_irqs[i].irq; + + ret = sm5502_parse_irq(info, irq_type); + if (ret < 0) { + dev_warn(info->dev, "cannot handle is interrupt:%d\n", + irq_type); + return IRQ_HANDLED; + } + schedule_work(&info->irq_work); + + return IRQ_HANDLED; +} + +static void sm5502_init_dev_type(struct sm5502_muic_info *info) +{ + unsigned int reg_data, vendor_id, version_id; + int i, ret; + + /* To test I2C, Print version_id and vendor_id of SM5502 */ + ret = regmap_read(info->regmap, SM5502_REG_DEVICE_ID, ®_data); + if (ret) { + dev_err(info->dev, + "failed to read DEVICE_ID register: %d\n", ret); + return; + } + + vendor_id = ((reg_data & SM5502_REG_DEVICE_ID_VENDOR_MASK) >> + SM5502_REG_DEVICE_ID_VENDOR_SHIFT); + version_id = ((reg_data & SM5502_REG_DEVICE_ID_VERSION_MASK) >> + SM5502_REG_DEVICE_ID_VERSION_SHIFT); + + dev_info(info->dev, "Device type: version: 0x%x, vendor: 0x%x\n", + version_id, vendor_id); + + /* Initiazle the register of SM5502 device to bring-up */ + for (i = 0; i < info->num_reg_data; i++) { + unsigned int val = 0; + + if (!info->reg_data[i].invert) + val |= ~info->reg_data[i].val; + else + val = info->reg_data[i].val; + regmap_write(info->regmap, info->reg_data[i].reg, val); + } +} + +static int sm5022_muic_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct device_node *np = i2c->dev.of_node; + struct sm5502_muic_info *info; + int i, ret, irq_flags; + + if (!np) + return -EINVAL; + + info = devm_kzalloc(&i2c->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + i2c_set_clientdata(i2c, info); + + info->dev = &i2c->dev; + info->i2c = i2c; + info->irq = i2c->irq; + info->muic_irqs = sm5502_muic_irqs; + info->num_muic_irqs = ARRAY_SIZE(sm5502_muic_irqs); + info->reg_data = sm5502_reg_data; + info->num_reg_data = ARRAY_SIZE(sm5502_reg_data); + + mutex_init(&info->mutex); + + INIT_WORK(&info->irq_work, sm5502_muic_irq_work); + + info->regmap = devm_regmap_init_i2c(i2c, &sm5502_muic_regmap_config); + if (IS_ERR(info->regmap)) { + ret = PTR_ERR(info->regmap); + dev_err(info->dev, "failed to allocate register map: %d\n", + ret); + return ret; + } + + /* Support irq domain for SM5502 MUIC device */ + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED; + ret = regmap_add_irq_chip(info->regmap, info->irq, irq_flags, 0, + &sm5502_muic_irq_chip, &info->irq_data); + if (ret != 0) { + dev_err(info->dev, "failed to request IRQ %d: %d\n", + info->irq, ret); + return ret; + } + + for (i = 0; i < info->num_muic_irqs; i++) { + struct muic_irq *muic_irq = &info->muic_irqs[i]; + unsigned int virq = 0; + + virq = regmap_irq_get_virq(info->irq_data, muic_irq->irq); + if (virq <= 0) + return -EINVAL; + muic_irq->virq = virq; + + ret = devm_request_threaded_irq(info->dev, virq, NULL, + sm5502_muic_irq_handler, + IRQF_NO_SUSPEND, + muic_irq->name, info); + if (ret) { + dev_err(info->dev, "failed: irq request (IRQ: %d," + " error :%d)\n", muic_irq->irq, ret); + return ret; + } + } + + /* Allocate extcon device */ + info->edev = devm_extcon_dev_allocate(info->dev, sm5502_extcon_cable); + if (IS_ERR(info->edev)) { + dev_err(info->dev, "failed to allocate memory for extcon\n"); + return -ENOMEM; + } + info->edev->name = np->name; + + /* Register extcon device */ + ret = devm_extcon_dev_register(info->dev, info->edev); + if (ret) { + dev_err(info->dev, "failed to register extcon device\n"); + return ret; + } + + /* Initialize SM5502 device and print vendor id and version id */ + sm5502_init_dev_type(info); + + return 0; +} + +static int sm5502_muic_i2c_remove(struct i2c_client *i2c) +{ + struct sm5502_muic_info *info = i2c_get_clientdata(i2c); + + regmap_del_irq_chip(info->irq, info->irq_data); + + return 0; +} + +static struct of_device_id sm5502_dt_match[] = { + { .compatible = "siliconmitus,sm5502-muic" }, + { }, +}; + +#ifdef CONFIG_PM_SLEEP +static int sm5502_muic_suspend(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct sm5502_muic_info *info = i2c_get_clientdata(i2c); + + enable_irq_wake(info->irq); + + return 0; +} + +static int sm5502_muic_resume(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct sm5502_muic_info *info = i2c_get_clientdata(i2c); + + disable_irq_wake(info->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(sm5502_muic_pm_ops, + sm5502_muic_suspend, sm5502_muic_resume); + +static const struct i2c_device_id sm5502_i2c_id[] = { + { "sm5502", TYPE_SM5502 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sm5502_i2c_id); + +static struct i2c_driver sm5502_muic_i2c_driver = { + .driver = { + .name = "sm5502", + .owner = THIS_MODULE, + .pm = &sm5502_muic_pm_ops, + .of_match_table = sm5502_dt_match, + }, + .probe = sm5022_muic_i2c_probe, + .remove = sm5502_muic_i2c_remove, + .id_table = sm5502_i2c_id, +}; + +static int __init sm5502_muic_i2c_init(void) +{ + return i2c_add_driver(&sm5502_muic_i2c_driver); +} +subsys_initcall(sm5502_muic_i2c_init); + +MODULE_DESCRIPTION("Silicon Mitus SM5502 Extcon driver"); +MODULE_AUTHOR("Chanwoo Choi "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/extcon/sm5502.h b/include/linux/extcon/sm5502.h new file mode 100644 index 000000000000..17bd6550c485 --- /dev/null +++ b/include/linux/extcon/sm5502.h @@ -0,0 +1,264 @@ +/* + * sm5502.h + * + * Copyright (c) 2014 Samsung Electronics Co., 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. + * + * 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 __LINUX_EXTCON_SM5502_H +#define __LINUX_EXTCON_SM5502_H + +enum sm5502_types { + TYPE_SM5502, +}; + +/* SM5502 registers */ +enum sm5502_reg { + SM5502_REG_DEVICE_ID = 0x01, + SM5502_REG_CONTROL, + SM5502_REG_INT1, + SM5502_REG_INT2, + SM5502_REG_INTMASK1, + SM5502_REG_INTMASK2, + SM5502_REG_ADC, + SM5502_REG_TIMING_SET1, + SM5502_REG_TIMING_SET2, + SM5502_REG_DEV_TYPE1, + SM5502_REG_DEV_TYPE2, + SM5502_REG_BUTTON1, + SM5502_REG_BUTTON2, + SM5502_REG_CAR_KIT_STATUS, + SM5502_REG_RSVD1, + SM5502_REG_RSVD2, + SM5502_REG_RSVD3, + SM5502_REG_RSVD4, + SM5502_REG_MANUAL_SW1, + SM5502_REG_MANUAL_SW2, + SM5502_REG_DEV_TYPE3, + SM5502_REG_RSVD5, + SM5502_REG_RSVD6, + SM5502_REG_RSVD7, + SM5502_REG_RSVD8, + SM5502_REG_RSVD9, + SM5502_REG_RESET, + SM5502_REG_RSVD10, + SM5502_REG_RESERVED_ID1, + SM5502_REG_RSVD11, + SM5502_REG_RSVD12, + SM5502_REG_RESERVED_ID2, + SM5502_REG_RSVD13, + SM5502_REG_OCP, + SM5502_REG_RSVD14, + SM5502_REG_RSVD15, + SM5502_REG_RSVD16, + SM5502_REG_RSVD17, + SM5502_REG_RSVD18, + SM5502_REG_RSVD19, + SM5502_REG_RSVD20, + SM5502_REG_RSVD21, + SM5502_REG_RSVD22, + SM5502_REG_RSVD23, + SM5502_REG_RSVD24, + SM5502_REG_RSVD25, + SM5502_REG_RSVD26, + SM5502_REG_RSVD27, + SM5502_REG_RSVD28, + SM5502_REG_RSVD29, + SM5502_REG_RSVD30, + SM5502_REG_RSVD31, + SM5502_REG_RSVD32, + SM5502_REG_RSVD33, + SM5502_REG_RSVD34, + SM5502_REG_RSVD35, + SM5502_REG_RSVD36, + SM5502_REG_RESERVED_ID3, + + SM5502_REG_END, +}; + +/* Define SM5502 MASK/SHIFT constant */ +#define SM5502_REG_DEVICE_ID_VENDOR_SHIFT 0 +#define SM5502_REG_DEVICE_ID_VERSION_SHIFT 3 +#define SM5502_REG_DEVICE_ID_VENDOR_MASK (0x3 << SM5502_REG_DEVICE_ID_VENDOR_SHIFT) +#define SM5502_REG_DEVICE_ID_VERSION_MASK (0x1f << SM5502_REG_DEVICE_ID_VERSION_SHIFT) + +#define SM5502_REG_CONTROL_MASK_INT_SHIFT 0 +#define SM5502_REG_CONTROL_WAIT_SHIFT 1 +#define SM5502_REG_CONTROL_MANUAL_SW_SHIFT 2 +#define SM5502_REG_CONTROL_RAW_DATA_SHIFT 3 +#define SM5502_REG_CONTROL_SW_OPEN_SHIFT 4 +#define SM5502_REG_CONTROL_MASK_INT_MASK (0x1 << SM5502_REG_CONTROL_MASK_INT_SHIFT) +#define SM5502_REG_CONTROL_WAIT_MASK (0x1 << SM5502_REG_CONTROL_WAIT_SHIFT) +#define SM5502_REG_CONTROL_MANUAL_SW_MASK (0x1 << SM5502_REG_CONTROL_MANUAL_SW_SHIFT) +#define SM5502_REG_CONTROL_RAW_DATA_MASK (0x1 << SM5502_REG_CONTROL_RAW_DATA_SHIFT) +#define SM5502_REG_CONTROL_SW_OPEN_MASK (0x1 << SM5502_REG_CONTROL_SW_OPEN_SHIFT) + +#define SM5502_REG_INTM1_ATTACH_SHIFT 0 +#define SM5502_REG_INTM1_DETACH_SHIFT 1 +#define SM5502_REG_INTM1_KP_SHIFT 2 +#define SM5502_REG_INTM1_LKP_SHIFT 3 +#define SM5502_REG_INTM1_LKR_SHIFT 4 +#define SM5502_REG_INTM1_OVP_EVENT_SHIFT 5 +#define SM5502_REG_INTM1_OCP_EVENT_SHIFT 6 +#define SM5502_REG_INTM1_OVP_OCP_DIS_SHIFT 7 +#define SM5502_REG_INTM1_ATTACH_MASK (0x1 << SM5502_REG_INTM1_ATTACH_SHIFT) +#define SM5502_REG_INTM1_DETACH_MASK (0x1 << SM5502_REG_INTM1_DETACH_SHIFT) +#define SM5502_REG_INTM1_KP_MASK (0x1 << SM5502_REG_INTM1_KP_SHIFT) +#define SM5502_REG_INTM1_LKP_MASK (0x1 << SM5502_REG_INTM1_LKP_SHIFT) +#define SM5502_REG_INTM1_LKR_MASK (0x1 << SM5502_REG_INTM1_LKR_SHIFT) +#define SM5502_REG_INTM1_OVP_EVENT_MASK (0x1 << SM5502_REG_INTM1_OVP_EVENT_SHIFT) +#define SM5502_REG_INTM1_OCP_EVENT_MASK (0x1 << SM5502_REG_INTM1_OCP_EVENT_SHIFT) +#define SM5502_REG_INTM1_OVP_OCP_DIS_MASK (0x1 << SM5502_REG_INTM1_OVP_OCP_DIS_SHIFT) + +#define SM5502_REG_INTM2_VBUS_DET_SHIFT 0 +#define SM5502_REG_INTM2_REV_ACCE_SHIFT 1 +#define SM5502_REG_INTM2_ADC_CHG_SHIFT 2 +#define SM5502_REG_INTM2_STUCK_KEY_SHIFT 3 +#define SM5502_REG_INTM2_STUCK_KEY_RCV_SHIFT 4 +#define SM5502_REG_INTM2_MHL_SHIFT 5 +#define SM5502_REG_INTM2_VBUS_DET_MASK (0x1 << SM5502_REG_INTM2_VBUS_DET_SHIFT) +#define SM5502_REG_INTM2_REV_ACCE_MASK (0x1 << SM5502_REG_INTM2_REV_ACCE_SHIFT) +#define SM5502_REG_INTM2_ADC_CHG_MASK (0x1 << SM5502_REG_INTM2_ADC_CHG_SHIFT) +#define SM5502_REG_INTM2_STUCK_KEY_MASK (0x1 << SM5502_REG_INTM2_STUCK_KEY_SHIFT) +#define SM5502_REG_INTM2_STUCK_KEY_RCV_MASK (0x1 << SM5502_REG_INTM2_STUCK_KEY_RCV_SHIFT) +#define SM5502_REG_INTM2_MHL_MASK (0x1 << SM5502_REG_INTM2_MHL_SHIFT) + +#define SM5502_REG_ADC_SHIFT 0 +#define SM5502_REG_ADC_MASK (0x1f << SM5502_REG_ADC_SHIFT) + +#define SM5502_REG_TIMING_SET1_KEY_PRESS_SHIFT 4 +#define SM5502_REG_TIMING_SET1_KEY_PRESS_MASK (0xf << SM5502_REG_TIMING_SET1_KEY_PRESS_SHIFT) +#define TIMING_KEY_PRESS_100MS 0x0 +#define TIMING_KEY_PRESS_200MS 0x1 +#define TIMING_KEY_PRESS_300MS 0x2 +#define TIMING_KEY_PRESS_400MS 0x3 +#define TIMING_KEY_PRESS_500MS 0x4 +#define TIMING_KEY_PRESS_600MS 0x5 +#define TIMING_KEY_PRESS_700MS 0x6 +#define TIMING_KEY_PRESS_800MS 0x7 +#define TIMING_KEY_PRESS_900MS 0x8 +#define TIMING_KEY_PRESS_1000MS 0x9 +#define SM5502_REG_TIMING_SET1_ADC_DET_SHIFT 0 +#define SM5502_REG_TIMING_SET1_ADC_DET_MASK (0xf << SM5502_REG_TIMING_SET1_ADC_DET_SHIFT) +#define TIMING_ADC_DET_50MS 0x0 +#define TIMING_ADC_DET_100MS 0x1 +#define TIMING_ADC_DET_150MS 0x2 +#define TIMING_ADC_DET_200MS 0x3 +#define TIMING_ADC_DET_300MS 0x4 +#define TIMING_ADC_DET_400MS 0x5 +#define TIMING_ADC_DET_500MS 0x6 +#define TIMING_ADC_DET_600MS 0x7 +#define TIMING_ADC_DET_700MS 0x8 +#define TIMING_ADC_DET_800MS 0x9 +#define TIMING_ADC_DET_900MS 0xA +#define TIMING_ADC_DET_1000MS 0xB + +#define SM5502_REG_TIMING_SET2_SW_WAIT_SHIFT 4 +#define SM5502_REG_TIMING_SET2_SW_WAIT_MASK (0xf << SM5502_REG_TIMING_SET2_SW_WAIT_SHIFT) +#define TIMING_SW_WAIT_10MS 0x0 +#define TIMING_SW_WAIT_30MS 0x1 +#define TIMING_SW_WAIT_50MS 0x2 +#define TIMING_SW_WAIT_70MS 0x3 +#define TIMING_SW_WAIT_90MS 0x4 +#define TIMING_SW_WAIT_110MS 0x5 +#define TIMING_SW_WAIT_130MS 0x6 +#define TIMING_SW_WAIT_150MS 0x7 +#define TIMING_SW_WAIT_170MS 0x8 +#define TIMING_SW_WAIT_190MS 0x9 +#define TIMING_SW_WAIT_210MS 0xA +#define SM5502_REG_TIMING_SET2_LONG_KEY_SHIFT 0 +#define SM5502_REG_TIMING_SET2_LONG_KEY_MASK (0xf << SM5502_REG_TIMING_SET2_LONG_KEY_SHIFT) +#define TIMING_LONG_KEY_300MS 0x0 +#define TIMING_LONG_KEY_400MS 0x1 +#define TIMING_LONG_KEY_500MS 0x2 +#define TIMING_LONG_KEY_600MS 0x3 +#define TIMING_LONG_KEY_700MS 0x4 +#define TIMING_LONG_KEY_800MS 0x5 +#define TIMING_LONG_KEY_900MS 0x6 +#define TIMING_LONG_KEY_1000MS 0x7 +#define TIMING_LONG_KEY_1100MS 0x8 +#define TIMING_LONG_KEY_1200MS 0x9 +#define TIMING_LONG_KEY_1300MS 0xA +#define TIMING_LONG_KEY_1400MS 0xB +#define TIMING_LONG_KEY_1500MS 0xC + +#define SM5502_REG_DEV_TYPE1_AUDIO_TYPE1_SHIFT 0 +#define SM5502_REG_DEV_TYPE1_AUDIO_TYPE2_SHIFT 1 +#define SM5502_REG_DEV_TYPE1_USB_SDP_SHIFT 2 +#define SM5502_REG_DEV_TYPE1_UART_SHIFT 3 +#define SM5502_REG_DEV_TYPE1_CAR_KIT_CHARGER_SHIFT 4 +#define SM5502_REG_DEV_TYPE1_USB_CHG_SHIFT 5 +#define SM5502_REG_DEV_TYPE1_DEDICATED_CHG_SHIFT 6 +#define SM5502_REG_DEV_TYPE1_USB_OTG_SHIFT 7 +#define SM5502_REG_DEV_TYPE1_AUDIO_TYPE1_MASK (0x1 << SM5502_REG_DEV_TYPE1_AUDIO_TYPE1_SHIFT) +#define SM5502_REG_DEV_TYPE1_AUDIO_TYPE1__MASK (0x1 << SM5502_REG_DEV_TYPE1_AUDIO_TYPE2_SHIFT) +#define SM5502_REG_DEV_TYPE1_USB_SDP_MASK (0x1 << SM5502_REG_DEV_TYPE1_USB_SDP_SHIFT) +#define SM5502_REG_DEV_TYPE1_UART_MASK (0x1 << SM5502_REG_DEV_TYPE1_UART_SHIFT) +#define SM5502_REG_DEV_TYPE1_CAR_KIT_CHARGER_MASK (0x1 << SM5502_REG_DEV_TYPE1_CAR_KIT_CHARGER_SHIFT) +#define SM5502_REG_DEV_TYPE1_USB_CHG_MASK (0x1 << SM5502_REG_DEV_TYPE1_USB_CHG_SHIFT) +#define SM5502_REG_DEV_TYPE1_DEDICATED_CHG_MASK (0x1 << SM5502_REG_DEV_TYPE1_DEDICATED_CHG_SHIFT) +#define SM5502_REG_DEV_TYPE1_USB_OTG_MASK (0x1 << SM5502_REG_DEV_TYPE1_USB_OTG_SHIFT) + +#define SM5502_REG_DEV_TYPE2_JIG_USB_ON_SHIFT 0 +#define SM5502_REG_DEV_TYPE2_JIG_USB_OFF_SHIFT 1 +#define SM5502_REG_DEV_TYPE2_JIG_UART_ON_SHIFT 2 +#define SM5502_REG_DEV_TYPE2_JIG_UART_OFF_SHIFT 3 +#define SM5502_REG_DEV_TYPE2_PPD_SHIFT 4 +#define SM5502_REG_DEV_TYPE2_TTY_SHIFT 5 +#define SM5502_REG_DEV_TYPE2_AV_CABLE_SHIFT 6 +#define SM5502_REG_DEV_TYPE2_JIG_USB_ON_MASK (0x1 << SM5502_REG_DEV_TYPE2_JIG_USB_ON_SHIFT) +#define SM5502_REG_DEV_TYPE2_JIG_USB_OFF_MASK (0x1 << SM5502_REG_DEV_TYPE2_JIG_USB_OFF_SHIFT) +#define SM5502_REG_DEV_TYPE2_JIG_UART_ON_MASK (0x1 << SM5502_REG_DEV_TYPE2_JIG_UART_ON_SHIFT) +#define SM5502_REG_DEV_TYPE2_JIG_UART_OFF_MASK (0x1 << SM5502_REG_DEV_TYPE2_JIG_UART_OFF_SHIFT) +#define SM5502_REG_DEV_TYPE2_PPD_MASK (0x1 << SM5502_REG_DEV_TYPE2_PPD_SHIFT) +#define SM5502_REG_DEV_TYPE2_TTY_MASK (0x1 << SM5502_REG_DEV_TYPE2_TTY_SHIFT) +#define SM5502_REG_DEV_TYPE2_AV_CABLE_MASK (0x1 << SM5502_REG_DEV_TYPE2_AV_CABLE_SHIFT) + +/* SM5502 Interrupts */ +enum sm5502_irq { + /* INT1 */ + SM5502_IRQ_INT1_ATTACH, + SM5502_IRQ_INT1_DETACH, + SM5502_IRQ_INT1_KP, + SM5502_IRQ_INT1_LKP, + SM5502_IRQ_INT1_LKR, + SM5502_IRQ_INT1_OVP_EVENT, + SM5502_IRQ_INT1_OCP_EVENT, + SM5502_IRQ_INT1_OVP_OCP_DIS, + + /* INT2 */ + SM5502_IRQ_INT2_VBUS_DET, + SM5502_IRQ_INT2_REV_ACCE, + SM5502_IRQ_INT2_ADC_CHG, + SM5502_IRQ_INT2_STUCK_KEY, + SM5502_IRQ_INT2_STUCK_KEY_RCV, + SM5502_IRQ_INT2_MHL, + + SM5502_IRQ_NUM, +}; + +#define SM5502_IRQ_INT1_ATTACH_MASK BIT(0) +#define SM5502_IRQ_INT1_DETACH_MASK BIT(1) +#define SM5502_IRQ_INT1_KP_MASK BIT(2) +#define SM5502_IRQ_INT1_LKP_MASK BIT(3) +#define SM5502_IRQ_INT1_LKR_MASK BIT(4) +#define SM5502_IRQ_INT1_OVP_EVENT_MASK BIT(5) +#define SM5502_IRQ_INT1_OCP_EVENT_MASK BIT(6) +#define SM5502_IRQ_INT1_OVP_OCP_DIS_MASK BIT(7) +#define SM5502_IRQ_INT2_VBUS_DET_MASK BIT(0) +#define SM5502_IRQ_INT2_REV_ACCE_MASK BIT(1) +#define SM5502_IRQ_INT2_ADC_CHG_MASK BIT(2) +#define SM5502_IRQ_INT2_STUCK_KEY_MASK BIT(3) +#define SM5502_IRQ_INT2_STUCK_KEY_RCV_MASK BIT(4) +#define SM5502_IRQ_INT2_MHL_MASK BIT(5) + +#endif /* __LINUX_EXTCON_SM5502_H */ -- cgit v1.2.3 From a75fed2ee6c187ab32b1cb01882c1032c4c9e4a8 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Wed, 28 May 2014 15:35:29 +0900 Subject: extcon: sm5502: Change internal hardware switch according to cable type This patch changes internal hardware DP_CON/DM_CON switch according to cable type. The SM5502 MUIC device can set hardware switch as following: - OPEN (not connected state) / USB / UART / AUDIO Also, this patch set VBUSIN switch according to cable type. Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-sm5502.c | 78 +++++++++++++++++++++++++++++++++++++++--- include/linux/extcon/sm5502.h | 23 +++++++++++++ 2 files changed, 96 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/extcon/extcon-sm5502.c b/drivers/extcon/extcon-sm5502.c index a32d40f97ff0..560d7dccec7b 100644 --- a/drivers/extcon/extcon-sm5502.c +++ b/drivers/extcon/extcon-sm5502.c @@ -228,6 +228,61 @@ static const struct regmap_config sm5502_muic_regmap_config = { .max_register = SM5502_REG_END, }; +/* Change DM_CON/DP_CON/VBUSIN switch according to cable type */ +static int sm5502_muic_set_path(struct sm5502_muic_info *info, + unsigned int con_sw, unsigned int vbus_sw, + bool attached) +{ + int ret; + + if (!attached) { + con_sw = DM_DP_SWITCH_OPEN; + vbus_sw = VBUSIN_SWITCH_OPEN; + } + + switch (con_sw) { + case DM_DP_SWITCH_OPEN: + case DM_DP_SWITCH_USB: + case DM_DP_SWITCH_AUDIO: + case DM_DP_SWITCH_UART: + ret = regmap_update_bits(info->regmap, SM5502_REG_MANUAL_SW1, + SM5502_REG_MANUAL_SW1_DP_MASK | + SM5502_REG_MANUAL_SW1_DM_MASK, + con_sw); + if (ret < 0) { + dev_err(info->dev, + "cannot update DM_CON/DP_CON switch\n"); + return ret; + } + break; + default: + dev_err(info->dev, "Unknown DM_CON/DP_CON switch type (%d)\n", + con_sw); + return -EINVAL; + }; + + switch (vbus_sw) { + case VBUSIN_SWITCH_OPEN: + case VBUSIN_SWITCH_VBUSOUT: + case VBUSIN_SWITCH_MIC: + case VBUSIN_SWITCH_VBUSOUT_WITH_USB: + ret = regmap_update_bits(info->regmap, SM5502_REG_MANUAL_SW1, + SM5502_REG_MANUAL_SW1_VBUSIN_MASK, + vbus_sw); + if (ret < 0) { + dev_err(info->dev, + "cannot update VBUSIN switch\n"); + return ret; + } + break; + default: + dev_err(info->dev, "Unknown VBUS switch type (%d)\n", vbus_sw); + return -EINVAL; + }; + + return 0; +} + /* Return cable type of attached or detached accessories */ static unsigned int sm5502_muic_get_cable_type(struct sm5502_muic_info *info) { @@ -329,7 +384,10 @@ static int sm5502_muic_cable_handler(struct sm5502_muic_info *info, static unsigned int prev_cable_type = SM5502_MUIC_ADC_GROUND; const char **cable_names = info->edev->supported_cable; unsigned int cable_type = SM5502_MUIC_ADC_GROUND; + unsigned int con_sw = DM_DP_SWITCH_OPEN; + unsigned int vbus_sw = VBUSIN_SWITCH_OPEN; unsigned int idx = 0; + int ret; if (!cable_names) return 0; @@ -343,15 +401,19 @@ static int sm5502_muic_cable_handler(struct sm5502_muic_info *info, switch (cable_type) { case SM5502_MUIC_ADC_OPEN_USB: - idx = EXTCON_CABLE_USB; + idx = EXTCON_CABLE_USB; + con_sw = DM_DP_SWITCH_USB; + vbus_sw = VBUSIN_SWITCH_VBUSOUT_WITH_USB; break; case SM5502_MUIC_ADC_OPEN_TA: - idx = EXTCON_CABLE_TA; + idx = EXTCON_CABLE_TA; + con_sw = DM_DP_SWITCH_OPEN; + vbus_sw = VBUSIN_SWITCH_VBUSOUT; break; case SM5502_MUIC_ADC_OPEN_USB_OTG: - idx = EXTCON_CABLE_USB_HOST; - break; - case SM5502_MUIC_ADC_GROUND: + idx = EXTCON_CABLE_USB_HOST; + con_sw = DM_DP_SWITCH_USB; + vbus_sw = VBUSIN_SWITCH_OPEN; break; default: dev_dbg(info->dev, @@ -359,6 +421,12 @@ static int sm5502_muic_cable_handler(struct sm5502_muic_info *info, return 0; }; + /* Change internal hardware path(DM_CON/DP_CON, VBUSIN) */ + ret = sm5502_muic_set_path(info, con_sw, vbus_sw, attached); + if (ret < 0) + return ret; + + /* Change the state of external accessory */ extcon_set_cable_state(info->edev, cable_names[idx], attached); return 0; diff --git a/include/linux/extcon/sm5502.h b/include/linux/extcon/sm5502.h index 17bd6550c485..030526bf8d79 100644 --- a/include/linux/extcon/sm5502.h +++ b/include/linux/extcon/sm5502.h @@ -223,6 +223,29 @@ enum sm5502_reg { #define SM5502_REG_DEV_TYPE2_TTY_MASK (0x1 << SM5502_REG_DEV_TYPE2_TTY_SHIFT) #define SM5502_REG_DEV_TYPE2_AV_CABLE_MASK (0x1 << SM5502_REG_DEV_TYPE2_AV_CABLE_SHIFT) +#define SM5502_REG_MANUAL_SW1_VBUSIN_SHIFT 0 +#define SM5502_REG_MANUAL_SW1_DP_SHIFT 2 +#define SM5502_REG_MANUAL_SW1_DM_SHIFT 5 +#define SM5502_REG_MANUAL_SW1_VBUSIN_MASK (0x3 << SM5502_REG_MANUAL_SW1_VBUSIN_SHIFT) +#define SM5502_REG_MANUAL_SW1_DP_MASK (0x7 << SM5502_REG_MANUAL_SW1_DP_SHIFT) +#define SM5502_REG_MANUAL_SW1_DM_MASK (0x7 << SM5502_REG_MANUAL_SW1_DM_SHIFT) +#define VBUSIN_SWITCH_OPEN 0x0 +#define VBUSIN_SWITCH_VBUSOUT 0x1 +#define VBUSIN_SWITCH_MIC 0x2 +#define VBUSIN_SWITCH_VBUSOUT_WITH_USB 0x3 +#define DM_DP_CON_SWITCH_OPEN 0x0 +#define DM_DP_CON_SWITCH_USB 0x1 +#define DM_DP_CON_SWITCH_AUDIO 0x2 +#define DM_DP_CON_SWITCH_UART 0x3 +#define DM_DP_SWITCH_OPEN ((DM_DP_CON_SWITCH_OPEN < Date: Tue, 22 Jul 2014 15:44:11 +0300 Subject: net/mlx4_core: Use low memory profile on kdump kernel When running in kdump kernel, reduce number of resources allocated for the hardware. This will enable the NIC to operate in this low memory environment at the expense of performance and some features not related to the basic NIC functionality. Signed-off-by: Amir Vadai Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/main.c | 28 +++++++++++++++++++++++++--- include/linux/mlx4/device.h | 7 +++++++ 2 files changed, 32 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 82ab427290c3..80b8c5f30e4e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -120,6 +120,16 @@ static struct mlx4_profile default_profile = { .num_mtt = 1 << 20, /* It is really num mtt segements */ }; +static struct mlx4_profile low_mem_profile = { + .num_qp = 1 << 17, + .num_srq = 1 << 6, + .rdmarc_per_qp = 1 << 4, + .num_cq = 1 << 8, + .num_mcg = 1 << 8, + .num_mpt = 1 << 9, + .num_mtt = 1 << 7, +}; + static int log_num_mac = 7; module_param_named(log_num_mac, log_num_mac, int, 0444); MODULE_PARM_DESC(log_num_mac, "Log2 max number of MACs per ETH port (1-7)"); @@ -129,6 +139,8 @@ module_param_named(log_num_vlan, log_num_vlan, int, 0444); MODULE_PARM_DESC(log_num_vlan, "Log2 max number of VLANs per ETH port (0-7)"); /* Log2 max number of VLANs per ETH port (0-7) */ #define MLX4_LOG_NUM_VLANS 7 +#define MLX4_MIN_LOG_NUM_VLANS 0 +#define MLX4_MIN_LOG_NUM_MAC 1 static bool use_prio; module_param_named(use_prio, use_prio, bool, 0444); @@ -287,8 +299,13 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) if (mlx4_is_mfunc(dev)) dev->caps.flags &= ~MLX4_DEV_CAP_FLAG_SENSE_SUPPORT; - dev->caps.log_num_macs = log_num_mac; - dev->caps.log_num_vlans = MLX4_LOG_NUM_VLANS; + if (mlx4_low_memory_profile()) { + dev->caps.log_num_macs = MLX4_MIN_LOG_NUM_MAC; + dev->caps.log_num_vlans = MLX4_MIN_LOG_NUM_VLANS; + } else { + dev->caps.log_num_macs = log_num_mac; + dev->caps.log_num_vlans = MLX4_LOG_NUM_VLANS; + } for (i = 1; i <= dev->caps.num_ports; ++i) { dev->caps.port_type[i] = MLX4_PORT_TYPE_NONE; @@ -1587,7 +1604,12 @@ static int mlx4_init_hca(struct mlx4_dev *dev) if (mlx4_is_master(dev)) mlx4_parav_master_pf_caps(dev); - profile = default_profile; + if (mlx4_low_memory_profile()) { + mlx4_info(dev, "Running from within kdump kernel. Using low memory profile\n"); + profile = low_mem_profile; + } else { + profile = default_profile; + } if (dev->caps.steering_mode == MLX4_STEERING_MODE_DEVICE_MANAGED) profile.num_mcg = MLX4_FS_NUM_MCG; diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index fa660aedb822..e15b1544ea83 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -1254,4 +1254,11 @@ int mlx4_vf_smi_enabled(struct mlx4_dev *dev, int slave, int port); int mlx4_vf_get_enable_smi_admin(struct mlx4_dev *dev, int slave, int port); int mlx4_vf_set_enable_smi_admin(struct mlx4_dev *dev, int slave, int port, int enable); + +/* Returns true if running in low memory profile (kdump kernel) */ +static inline bool mlx4_low_memory_profile(void) +{ + return reset_devices; +} + #endif /* MLX4_DEVICE_H */ -- cgit v1.2.3 From ff587e45a1a1690f5cd713a2782672c579460365 Mon Sep 17 00:00:00 2001 From: Vandana Kannan Date: Wed, 11 Jun 2014 10:46:48 +0530 Subject: drm/crtc: Add property for aspect ratio Added a property to enable user space to set aspect ratio. This patch contains declaration of the property and code to create the property. v2: Thierry's review comments. - Made aspect ratio enum generic instead of HDMI/CEA specfic - Removed usage of temporary aspect_ratio variable v3: Thierry's review comments. - Fixed indentation v4: Thierry's review comments. - Return ENOMEM when property creation fails Signed-off-by: Vandana Kannan Cc: Thierry Reding Reviewed-by: Thierry Reding Acked-by: Dave Airlie Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 33 +++++++++++++++++++++++++++++++++ include/drm/drm_crtc.h | 2 ++ include/uapi/drm/drm_mode.h | 5 +++++ 3 files changed, 40 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 89bab66558ef..f0a777747907 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -182,6 +182,12 @@ static const struct drm_prop_enum_list drm_scaling_mode_enum_list[] = { DRM_MODE_SCALE_ASPECT, "Full aspect" }, }; +static const struct drm_prop_enum_list drm_aspect_ratio_enum_list[] = { + { DRM_MODE_PICTURE_ASPECT_NONE, "Automatic" }, + { DRM_MODE_PICTURE_ASPECT_4_3, "4:3" }, + { DRM_MODE_PICTURE_ASPECT_16_9, "16:9" }, +}; + /* * Non-global properties, but "required" for certain connectors. */ @@ -1390,6 +1396,33 @@ int drm_mode_create_scaling_mode_property(struct drm_device *dev) } EXPORT_SYMBOL(drm_mode_create_scaling_mode_property); +/** + * drm_mode_create_aspect_ratio_property - create aspect ratio property + * @dev: DRM device + * + * Called by a driver the first time it's needed, must be attached to desired + * connectors. + * + * Returns: + * Zero on success, errno on failure. + */ +int drm_mode_create_aspect_ratio_property(struct drm_device *dev) +{ + if (dev->mode_config.aspect_ratio_property) + return 0; + + dev->mode_config.aspect_ratio_property = + drm_property_create_enum(dev, 0, "aspect ratio", + drm_aspect_ratio_enum_list, + ARRAY_SIZE(drm_aspect_ratio_enum_list)); + + if (dev->mode_config.aspect_ratio_property == NULL) + return -ENOMEM; + + return 0; +} +EXPORT_SYMBOL(drm_mode_create_aspect_ratio_property); + /** * drm_mode_create_dirty_property - create dirty property * @dev: DRM device diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 08ed55e02762..be7114e76d1b 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -835,6 +835,7 @@ struct drm_mode_config { /* Optional properties */ struct drm_property *scaling_mode_property; + struct drm_property *aspect_ratio_property; struct drm_property *dirty_info_property; /* dumb ioctl parameters */ @@ -1023,6 +1024,7 @@ extern int drm_mode_create_dvi_i_properties(struct drm_device *dev); extern int drm_mode_create_tv_properties(struct drm_device *dev, int num_formats, char *formats[]); extern int drm_mode_create_scaling_mode_property(struct drm_device *dev); +extern int drm_mode_create_aspect_ratio_property(struct drm_device *dev); extern int drm_mode_create_dirty_info_property(struct drm_device *dev); extern int drm_mode_connector_attach_encoder(struct drm_connector *connector, diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index def54f9e07ca..a0db2d4aa5f0 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -88,6 +88,11 @@ #define DRM_MODE_SCALE_CENTER 2 /* Centered, no scaling */ #define DRM_MODE_SCALE_ASPECT 3 /* Full screen, preserve aspect */ +/* Picture aspect ratio options */ +#define DRM_MODE_PICTURE_ASPECT_NONE 0 +#define DRM_MODE_PICTURE_ASPECT_4_3 1 +#define DRM_MODE_PICTURE_ASPECT_16_9 2 + /* Dithering mode options */ #define DRM_MODE_DITHERING_OFF 0 #define DRM_MODE_DITHERING_ON 1 -- cgit v1.2.3 From 23d9cec07c589276561c13b180577c0b87930140 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Tue, 22 Jul 2014 10:39:54 -0500 Subject: pinctrl: dra: dt-bindings: Fix pull enable/disable The DRA74/72 control module pins have a weak pull up and pull down. This is configured by bit offset 17. if BIT(17) is 1, a pull up is selected, else a pull down is selected. However, this pull resisstor is applied based on BIT(16) - PULLUDENABLE - if BIT(18) is *0*, then pull as defined in BIT(17) is applied, else no weak pulls are applied. We defined this in reverse. Reference: Table 18-5 (Description of the pad configuration register bits) in Technical Reference Manual Revision (DRA74x revision Q: SPRUHI2Q Revised June 2014 and DRA72x revision F: SPRUHP2F - Revised June 2014) Fixes: 6e58b8f1daaf1a ("ARM: dts: DRA7: Add the dts files for dra7 SoC and dra7-evm board") Signed-off-by: Nishanth Menon Tested-by: Felipe Balbi Acked-by: Felipe Balbi Signed-off-by: Tony Lindgren --- include/dt-bindings/pinctrl/dra.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/dt-bindings/pinctrl/dra.h b/include/dt-bindings/pinctrl/dra.h index 002a2855c046..3d33794e4f3e 100644 --- a/include/dt-bindings/pinctrl/dra.h +++ b/include/dt-bindings/pinctrl/dra.h @@ -30,7 +30,8 @@ #define MUX_MODE14 0xe #define MUX_MODE15 0xf -#define PULL_ENA (1 << 16) +#define PULL_ENA (0 << 16) +#define PULL_DIS (1 << 16) #define PULL_UP (1 << 17) #define INPUT_EN (1 << 18) #define SLEWCONTROL (1 << 19) @@ -38,10 +39,10 @@ #define WAKEUP_EVENT (1 << 25) /* Active pin states */ -#define PIN_OUTPUT 0 +#define PIN_OUTPUT (0 | PULL_DIS) #define PIN_OUTPUT_PULLUP (PIN_OUTPUT | PULL_ENA | PULL_UP) #define PIN_OUTPUT_PULLDOWN (PIN_OUTPUT | PULL_ENA) -#define PIN_INPUT INPUT_EN +#define PIN_INPUT (INPUT_EN | PULL_DIS) #define PIN_INPUT_SLEW (INPUT_EN | SLEWCONTROL) #define PIN_INPUT_PULLUP (PULL_ENA | INPUT_EN | PULL_UP) #define PIN_INPUT_PULLDOWN (PULL_ENA | INPUT_EN) -- cgit v1.2.3 From 14c8a620ba436511b1347c592633befa49535176 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 23 Jul 2014 10:47:49 +0200 Subject: gpio: drop retval check enforcing from gpiochip_remove() As we start to decomission the return value from gpiochip_remove() the compilers emit warnings due to the function being tagged __must_check. So drop this until we remove the return value altogether. Cc: Abdoulaye Berthe Suggested-by: Stephen Rothwell Signed-off-by: Linus Walleij --- include/linux/gpio/driver.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 573e4f3243d0..ca3024554a2d 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -141,7 +141,7 @@ extern const char *gpiochip_is_requested(struct gpio_chip *chip, /* add/remove chips */ extern int gpiochip_add(struct gpio_chip *chip); -extern int __must_check gpiochip_remove(struct gpio_chip *chip); +extern int gpiochip_remove(struct gpio_chip *chip); extern struct gpio_chip *gpiochip_find(void *data, int (*match)(struct gpio_chip *chip, void *data)); -- cgit v1.2.3 From efd342fb0031a17758571dce42e3f373d94e2fec Mon Sep 17 00:00:00 2001 From: Matthias Brugger Date: Fri, 18 Jul 2014 11:36:39 +0200 Subject: of: Provide a function to request and map memory A call to of_iomap does not request the memory region. This patch adds the function of_io_request_and_map which requests the memory region before mapping it. Signed-off-by: Matthias Brugger Suggested-by: Thomas Gleixner Suggested-by: Rob Herring Acked-by: Rob Herring Signed-off-by: Daniel Lezcano --- drivers/of/address.c | 36 ++++++++++++++++++++++++++++++++++++ include/linux/io.h | 2 ++ include/linux/of_address.h | 11 +++++++++++ lib/devres.c | 2 -- 4 files changed, 49 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/of/address.c b/drivers/of/address.c index 5edfcb0da37d..e3718250d66e 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -702,6 +702,42 @@ void __iomem *of_iomap(struct device_node *np, int index) } EXPORT_SYMBOL(of_iomap); +/* + * of_io_request_and_map - Requests a resource and maps the memory mapped IO + * for a given device_node + * @device: the device whose io range will be mapped + * @index: index of the io range + * @name: name of the resource + * + * Returns a pointer to the requested and mapped memory or an ERR_PTR() encoded + * error code on failure. Usage example: + * + * base = of_io_request_and_map(node, 0, "foo"); + * if (IS_ERR(base)) + * return PTR_ERR(base); + */ +void __iomem *of_io_request_and_map(struct device_node *np, int index, + char *name) +{ + struct resource res; + void __iomem *mem; + + if (of_address_to_resource(np, index, &res)) + return IOMEM_ERR_PTR(-EINVAL); + + if (!request_mem_region(res.start, resource_size(&res), name)) + return IOMEM_ERR_PTR(-EBUSY); + + mem = ioremap(res.start, resource_size(&res)); + if (!mem) { + release_mem_region(res.start, resource_size(&res)); + return IOMEM_ERR_PTR(-ENOMEM); + } + + return mem; +} +EXPORT_SYMBOL(of_io_request_and_map); + /** * of_dma_get_range - Get DMA range info * @np: device node to get DMA range info diff --git a/include/linux/io.h b/include/linux/io.h index b76e6e545806..d5fc9b8d8b03 100644 --- a/include/linux/io.h +++ b/include/linux/io.h @@ -58,6 +58,8 @@ static inline void devm_ioport_unmap(struct device *dev, void __iomem *addr) } #endif +#define IOMEM_ERR_PTR(err) (__force void __iomem *)ERR_PTR(err) + void __iomem *devm_ioremap(struct device *dev, resource_size_t offset, unsigned long size); void __iomem *devm_ioremap_nocache(struct device *dev, resource_size_t offset, diff --git a/include/linux/of_address.h b/include/linux/of_address.h index c13b8782a4eb..fb7b7221e063 100644 --- a/include/linux/of_address.h +++ b/include/linux/of_address.h @@ -109,7 +109,12 @@ static inline bool of_dma_is_coherent(struct device_node *np) extern int of_address_to_resource(struct device_node *dev, int index, struct resource *r); void __iomem *of_iomap(struct device_node *node, int index); +void __iomem *of_io_request_and_map(struct device_node *device, + int index, char *name); #else + +#include + static inline int of_address_to_resource(struct device_node *dev, int index, struct resource *r) { @@ -120,6 +125,12 @@ static inline void __iomem *of_iomap(struct device_node *device, int index) { return NULL; } + +static inline void __iomem *of_io_request_and_map(struct device_node *device, + int index, char *name) +{ + return IOMEM_ERR_PTR(-EINVAL); +} #endif #if defined(CONFIG_OF_ADDRESS) && defined(CONFIG_PCI) diff --git a/lib/devres.c b/lib/devres.c index f562bf6ff71d..bb632484a860 100644 --- a/lib/devres.c +++ b/lib/devres.c @@ -86,8 +86,6 @@ void devm_iounmap(struct device *dev, void __iomem *addr) } EXPORT_SYMBOL(devm_iounmap); -#define IOMEM_ERR_PTR(err) (__force void __iomem *)ERR_PTR(err) - /** * devm_ioremap_resource() - check, request region, and ioremap resource * @dev: generic device to handle the resource for -- cgit v1.2.3 From a38b1f60b5245a3f610baac2019c0ecd8abd8752 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Mon, 14 Jul 2014 18:52:04 +0200 Subject: ARM: pxa: Add non device-tree timer link to clocksource As clocksource pxa_timer was moved to clocksource framework, the pxa_timer initialization needs to be a bit amended, to pass the necessary informations to clocksource, ie : - the timer interrupt (mach specific) - the timer registers base (ditto) - the timer clockrate Signed-off-by: Robert Jarzmik Signed-off-by: Daniel Lezcano --- arch/arm/mach-pxa/generic.c | 11 +++++++++++ include/clocksource/pxa.h | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 include/clocksource/pxa.h (limited to 'include') diff --git a/arch/arm/mach-pxa/generic.c b/arch/arm/mach-pxa/generic.c index 42254175fcf4..6f38e1af45af 100644 --- a/arch/arm/mach-pxa/generic.c +++ b/arch/arm/mach-pxa/generic.c @@ -25,11 +25,13 @@ #include #include +#include #include #include #include #include "generic.h" +#include void clear_reset_status(unsigned int mask) { @@ -56,6 +58,15 @@ unsigned long get_clock_tick_rate(void) } EXPORT_SYMBOL(get_clock_tick_rate); +/* + * For non device-tree builds, keep legacy timer init + */ +void pxa_timer_init(void) +{ + pxa_timer_nodt_init(IRQ_OST0, io_p2v(0x40a00000), + get_clock_tick_rate()); +} + /* * Get the clock frequency as reflected by CCCR and the turbo flag. * We assume these values have been applied via a fcs. diff --git a/include/clocksource/pxa.h b/include/clocksource/pxa.h new file mode 100644 index 000000000000..1efbe5a66958 --- /dev/null +++ b/include/clocksource/pxa.h @@ -0,0 +1,18 @@ +/* + * PXA clocksource, clockevents, and OST interrupt handlers. + * + * Copyright (C) 2014 Robert Jarzmik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + */ + +#ifndef _CLOCKSOURCE_PXA_H +#define _CLOCKSOURCE_PXA_H + +extern void pxa_timer_nodt_init(int irq, void __iomem *base, + unsigned long clock_tick_rate); + +#endif -- cgit v1.2.3 From 8594d83261d14754288ef00993f0971131ff08eb Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 11 Jul 2014 14:19:32 +0800 Subject: iommu/vt-d: Simplify include/linux/dmar.h Simplify include/linux/dmar.h a bit based on the fact that both CONFIG_INTEL_IOMMU and CONFIG_IRQ_REMAP select CONFIG_DMAR_TABLE. Signed-off-by: Jiang Liu Signed-off-by: Joerg Roedel --- include/linux/dmar.h | 50 ++++++++++++++++++-------------------------------- 1 file changed, 18 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/include/linux/dmar.h b/include/linux/dmar.h index 23c8db129560..1deece46a0ca 100644 --- a/include/linux/dmar.h +++ b/include/linux/dmar.h @@ -114,22 +114,30 @@ extern int dmar_remove_dev_scope(struct dmar_pci_notify_info *info, /* Intel IOMMU detection */ extern int detect_intel_iommu(void); extern int enable_drhd_fault_handling(void); -#else -struct dmar_pci_notify_info; -static inline int detect_intel_iommu(void) + +#ifdef CONFIG_INTEL_IOMMU +extern int iommu_detected, no_iommu; +extern int intel_iommu_init(void); +extern int dmar_parse_one_rmrr(struct acpi_dmar_header *header); +extern int dmar_parse_one_atsr(struct acpi_dmar_header *header); +extern int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info); +#else /* !CONFIG_INTEL_IOMMU: */ +static inline int intel_iommu_init(void) { return -ENODEV; } +static inline int dmar_parse_one_rmrr(struct acpi_dmar_header *header) { - return -ENODEV; + return 0; } - -static inline int dmar_table_init(void) +static inline int dmar_parse_one_atsr(struct acpi_dmar_header *header) { - return -ENODEV; + return 0; } -static inline int enable_drhd_fault_handling(void) +static inline int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info) { - return -1; + return 0; } -#endif /* !CONFIG_DMAR_TABLE */ +#endif /* CONFIG_INTEL_IOMMU */ + +#endif /* CONFIG_DMAR_TABLE */ struct irte { union { @@ -177,26 +185,4 @@ extern int dmar_set_interrupt(struct intel_iommu *iommu); extern irqreturn_t dmar_fault(int irq, void *dev_id); extern int arch_setup_dmar_msi(unsigned int irq); -#ifdef CONFIG_INTEL_IOMMU -extern int iommu_detected, no_iommu; -extern int dmar_parse_one_rmrr(struct acpi_dmar_header *header); -extern int dmar_parse_one_atsr(struct acpi_dmar_header *header); -extern int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info); -extern int intel_iommu_init(void); -#else /* !CONFIG_INTEL_IOMMU: */ -static inline int intel_iommu_init(void) { return -ENODEV; } -static inline int dmar_parse_one_rmrr(struct acpi_dmar_header *header) -{ - return 0; -} -static inline int dmar_parse_one_atsr(struct acpi_dmar_header *header) -{ - return 0; -} -static inline int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info) -{ - return 0; -} -#endif /* CONFIG_INTEL_IOMMU */ - #endif /* __DMAR_H__ */ -- cgit v1.2.3 From a156ef99e874f3701367cc192aa604bcf8c0a236 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 11 Jul 2014 14:19:36 +0800 Subject: iommu/vt-d: Introduce helper function iova_size() to improve code readability Signed-off-by: Jiang Liu Signed-off-by: Joerg Roedel --- drivers/iommu/intel-iommu.c | 7 +++---- include/linux/iova.h | 5 +++++ 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index c9e65edaa2ad..cd1ba24c766a 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -3101,10 +3101,10 @@ static void flush_unmaps(void) /* On real hardware multiple invalidations are expensive */ if (cap_caching_mode(iommu->cap)) iommu_flush_iotlb_psi(iommu, domain->id, - iova->pfn_lo, iova->pfn_hi - iova->pfn_lo + 1, + iova->pfn_lo, iova_size(iova), !deferred_flush[i].freelist[j], 0); else { - mask = ilog2(mm_to_dma_pfn(iova->pfn_hi - iova->pfn_lo + 1)); + mask = ilog2(mm_to_dma_pfn(iova_size(iova))); iommu_flush_dev_iotlb(deferred_flush[i].domain[j], (uint64_t)iova->pfn_lo << PAGE_SHIFT, mask); } @@ -3905,8 +3905,7 @@ static int intel_iommu_memory_notifier(struct notifier_block *nb, rcu_read_lock(); for_each_active_iommu(iommu, drhd) iommu_flush_iotlb_psi(iommu, si_domain->id, - iova->pfn_lo, - iova->pfn_hi - iova->pfn_lo + 1, + iova->pfn_lo, iova_size(iova), !freelist, 0); rcu_read_unlock(); dma_free_pagelist(freelist); diff --git a/include/linux/iova.h b/include/linux/iova.h index 3277f4711349..19e81d5ccb6d 100644 --- a/include/linux/iova.h +++ b/include/linux/iova.h @@ -34,6 +34,11 @@ struct iova_domain { unsigned long dma_32bit_pfn; }; +static inline unsigned long iova_size(struct iova *iova) +{ + return iova->pfn_hi - iova->pfn_lo + 1; +} + struct iova *alloc_iova_mem(void); void free_iova_mem(struct iova *iova); void free_iova(struct iova_domain *iovad, unsigned long pfn); -- cgit v1.2.3 From 1a112d10f03e83fb3a2fdc4c9165865dec8a3ca6 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 23 Jul 2014 09:05:27 -0400 Subject: libata: introduce ata_host->n_tags to avoid oops on SAS controllers 1871ee134b73 ("libata: support the ata host which implements a queue depth less than 32") directly used ata_port->scsi_host->can_queue from ata_qc_new() to determine the number of tags supported by the host; unfortunately, SAS controllers doing SATA don't initialize ->scsi_host leading to the following oops. BUG: unable to handle kernel NULL pointer dereference at 0000000000000058 IP: [] ata_qc_new_init+0x188/0x1b0 PGD 0 Oops: 0002 [#1] SMP Modules linked in: isci libsas scsi_transport_sas mgag200 drm_kms_helper ttm CPU: 1 PID: 518 Comm: udevd Not tainted 3.16.0-rc6+ #62 Hardware name: Intel Corporation S2600CO/S2600CO, BIOS SE5C600.86B.02.02.0002.122320131210 12/23/2013 task: ffff880c1a00b280 ti: ffff88061a000000 task.ti: ffff88061a000000 RIP: 0010:[] [] ata_qc_new_init+0x188/0x1b0 RSP: 0018:ffff88061a003ae8 EFLAGS: 00010012 RAX: 0000000000000001 RBX: ffff88000241ca80 RCX: 00000000000000fa RDX: 0000000000000020 RSI: 0000000000000020 RDI: ffff8806194aa298 RBP: ffff88061a003ae8 R08: ffff8806194a8000 R09: 0000000000000000 R10: 0000000000000000 R11: ffff88000241ca80 R12: ffff88061ad58200 R13: ffff8806194aa298 R14: ffffffff814e67a0 R15: ffff8806194a8000 FS: 00007f3ad7fe3840(0000) GS:ffff880627620000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000058 CR3: 000000061a118000 CR4: 00000000001407e0 Stack: ffff88061a003b20 ffffffff814e96e1 ffff88000241ca80 ffff88061ad58200 ffff8800b6bf6000 ffff880c1c988000 ffff880619903850 ffff88061a003b68 ffffffffa0056ce1 ffff88061a003b48 0000000013d6e6f8 ffff88000241ca80 Call Trace: [] ata_sas_queuecmd+0xa1/0x430 [] sas_queuecommand+0x191/0x220 [libsas] [] scsi_dispatch_cmd+0x10e/0x300 [] scsi_request_fn+0x2f5/0x550 [] __blk_run_queue+0x33/0x40 [] queue_unplugged+0x2a/0x90 [] blk_flush_plug_list+0x1b4/0x210 [] blk_finish_plug+0x14/0x50 [] __do_page_cache_readahead+0x198/0x1f0 [] force_page_cache_readahead+0x31/0x50 [] page_cache_sync_readahead+0x3e/0x50 [] generic_file_read_iter+0x496/0x5a0 [] blkdev_read_iter+0x37/0x40 [] new_sync_read+0x7e/0xb0 [] vfs_read+0x94/0x170 [] SyS_read+0x46/0xb0 [] ? SyS_lseek+0x91/0xb0 [] system_call_fastpath+0x16/0x1b Code: 00 00 00 88 50 29 83 7f 08 01 19 d2 83 e2 f0 83 ea 50 88 50 34 c6 81 1d 02 00 00 40 c6 81 17 02 00 00 00 5d c3 66 0f 1f 44 00 00 <89> 14 25 58 00 00 00 Fix it by introducing ata_host->n_tags which is initialized to ATA_MAX_QUEUE - 1 in ata_host_init() for SAS controllers and set to scsi_host_template->can_queue in ata_host_register() for !SAS ones. As SAS hosts are never registered, this will give them the same ATA_MAX_QUEUE - 1 as before. Note that we can't use scsi_host->can_queue directly for SAS hosts anyway as they can go higher than the libata maximum. Signed-off-by: Tejun Heo Reported-by: Mike Qiu Reported-by: Jesse Brandeburg Reported-by: Peter Hurley Reported-by: Peter Zijlstra Tested-by: Alexey Kardashevskiy Fixes: 1871ee134b73 ("libata: support the ata host which implements a queue depth less than 32") Cc: Kevin Hao Cc: Dan Williams Cc: stable@vger.kernel.org --- drivers/ata/libata-core.c | 16 ++++------------ include/linux/libata.h | 1 + 2 files changed, 5 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index d19c37a7abc9..677c0c1b03bd 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4798,9 +4798,8 @@ void swap_buf_le16(u16 *buf, unsigned int buf_words) static struct ata_queued_cmd *ata_qc_new(struct ata_port *ap) { struct ata_queued_cmd *qc = NULL; - unsigned int i, tag, max_queue; - - max_queue = ap->scsi_host->can_queue; + unsigned int max_queue = ap->host->n_tags; + unsigned int i, tag; /* no command while frozen */ if (unlikely(ap->pflags & ATA_PFLAG_FROZEN)) @@ -6094,6 +6093,7 @@ void ata_host_init(struct ata_host *host, struct device *dev, { spin_lock_init(&host->lock); mutex_init(&host->eh_mutex); + host->n_tags = ATA_MAX_QUEUE - 1; host->dev = dev; host->ops = ops; } @@ -6175,15 +6175,7 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) { int i, rc; - /* - * The max queue supported by hardware must not be greater than - * ATA_MAX_QUEUE. - */ - if (sht->can_queue > ATA_MAX_QUEUE) { - dev_err(host->dev, "BUG: the hardware max queue is too large\n"); - WARN_ON(1); - return -EINVAL; - } + host->n_tags = clamp(sht->can_queue, 1, ATA_MAX_QUEUE - 1); /* host must have been started */ if (!(host->flags & ATA_HOST_STARTED)) { diff --git a/include/linux/libata.h b/include/linux/libata.h index 5ab4e3a76721..92abb497ab14 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -593,6 +593,7 @@ struct ata_host { struct device *dev; void __iomem * const *iomap; unsigned int n_ports; + unsigned int n_tags; /* nr of NCQ tags */ void *private_data; struct ata_port_operations *ops; unsigned long flags; -- cgit v1.2.3 From ec4c4d877becf1c224f45347f4fc0016765e00d0 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 15 Jul 2014 09:58:06 +0200 Subject: video: fix up versatile CLCD helper move commit 11c32d7b6274cb0f ("video: move Versatile CLCD helpers") moved files out of the plat-versatile directory but in the process got a few of the dependencies wrong: - If CONFIG_FB is not set, the file no longer gets built, resulting in a link error - If CONFIG_FB or CONFIG_FB_ARMCLCD are disabled, we also get a Kconfig warning for incorrect dependencies due to the symbol being 'select'ed from the platform Kconfig. - When the file is not built, we also get a link error for missing symbols. This patch should fix all three, by removing the 'select' statements, changing the Kconfig description of the symbol to be enabled in exactly the right configurations, and adding inline stub functions for the case when the framebuffer driver is disabled. Signed-off-by: Arnd Bergmann Reviewed-by: Linus Walleij --- arch/arm/Kconfig | 2 -- arch/arm/mach-vexpress/Kconfig | 1 - drivers/video/fbdev/Kconfig | 6 ++---- include/linux/platform_data/video-clcd-versatile.h | 18 ++++++++++++++++++ 4 files changed, 20 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index c32064de77d8..11f18a04c066 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -332,7 +332,6 @@ config ARCH_REALVIEW select ICST select NEED_MACH_MEMORY_H select PLAT_VERSATILE - select PLAT_VERSATILE_CLCD help This enables support for ARM Ltd RealView boards. @@ -347,7 +346,6 @@ config ARCH_VERSATILE select HAVE_MACH_CLKDEV select ICST select PLAT_VERSATILE - select PLAT_VERSATILE_CLCD select PLAT_VERSATILE_CLOCK select VERSATILE_FPGA_IRQ help diff --git a/arch/arm/mach-vexpress/Kconfig b/arch/arm/mach-vexpress/Kconfig index d8b9330f896a..e9166dfc4756 100644 --- a/arch/arm/mach-vexpress/Kconfig +++ b/arch/arm/mach-vexpress/Kconfig @@ -13,7 +13,6 @@ menuconfig ARCH_VEXPRESS select ICST select NO_IOPORT_MAP select PLAT_VERSATILE - select PLAT_VERSATILE_CLCD select POWER_RESET select POWER_RESET_VEXPRESS select POWER_SUPPLY diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index 92026d31bb48..bdf463072247 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -292,10 +292,8 @@ config FB_ARMCLCD # Helper logic selected only by the ARM Versatile platform family. config PLAT_VERSATILE_CLCD - depends on FB_ARMCLCD - depends on (PLAT_VERSATILE || ARCH_INTEGRATOR) - default y - bool + def_bool ARCH_VERSATILE || ARCH_REALVIEW || ARCH_VEXPRESS + depends on FB_ARMCLCD && FB=y config FB_ACORN bool "Acorn VIDC support" diff --git a/include/linux/platform_data/video-clcd-versatile.h b/include/linux/platform_data/video-clcd-versatile.h index 6bb6a1d2019b..09ccf182af4d 100644 --- a/include/linux/platform_data/video-clcd-versatile.h +++ b/include/linux/platform_data/video-clcd-versatile.h @@ -1,9 +1,27 @@ #ifndef PLAT_CLCD_H #define PLAT_CLCD_H +#ifdef CONFIG_PLAT_VERSATILE_CLCD struct clcd_panel *versatile_clcd_get_panel(const char *); int versatile_clcd_setup_dma(struct clcd_fb *, unsigned long); int versatile_clcd_mmap_dma(struct clcd_fb *, struct vm_area_struct *); void versatile_clcd_remove_dma(struct clcd_fb *); +#else +static inline struct clcd_panel *versatile_clcd_get_panel(const char *s) +{ + return NULL; +} +static inline int versatile_clcd_setup_dma(struct clcd_fb *fb, unsigned long framesize) +{ + return -ENODEV; +} +static inline int versatile_clcd_mmap_dma(struct clcd_fb *fb, struct vm_area_struct *vm) +{ + return -ENODEV; +} +static inline void versatile_clcd_remove_dma(struct clcd_fb *fb) +{ +} +#endif #endif -- cgit v1.2.3 From 1bd6b601fe196b6fbce2c93536ce0f3f53577cec Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Tue, 22 Jul 2014 16:17:41 +0900 Subject: gpio: make gpiochip_get_desc() gpiolib-private As GPIO descriptors are not going to remain unique anymore, having this function public is not safe. Restrain its use to gpiolib since we have no user outside of it. Signed-off-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-of.c | 2 +- drivers/gpio/gpiolib.c | 1 - drivers/gpio/gpiolib.h | 2 ++ include/linux/gpio/driver.h | 3 --- 4 files changed, 3 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 3e2fae205bee..7cfdc2278905 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -23,7 +23,7 @@ #include #include -struct gpio_desc; +#include "gpiolib.h" /* Private data structure for of_gpiochip_find_and_xlate */ struct gg_data { diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index c5509359ba88..38d176e31379 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -82,7 +82,6 @@ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, return &chip->desc[hwnum]; } -EXPORT_SYMBOL_GPL(gpiochip_get_desc); /** * Convert a GPIO descriptor to the integer namespace. diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 98020c393eb3..acbb9335f08c 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -51,6 +51,8 @@ void gpiochip_free_own_desc(struct gpio_desc *desc); struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np, const char *list_name, int index, enum of_gpio_flags *flags); +struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum); + extern struct spinlock gpio_lock; extern struct list_head gpio_chips; diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index ca3024554a2d..88f92dfae545 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -151,9 +151,6 @@ void gpiod_unlock_as_irq(struct gpio_desc *desc); struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc); -struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, - u16 hwnum); - enum gpio_lookup_flags { GPIO_ACTIVE_HIGH = (0 << 0), GPIO_ACTIVE_LOW = (1 << 0), -- cgit v1.2.3 From d74be6dfea1b96cfb4bd79d9254fa9d21ed5f131 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Tue, 22 Jul 2014 16:17:42 +0900 Subject: gpio: remove gpiod_lock/unlock_as_irq() gpio_lock/unlock_as_irq() are working with (chip, offset) arguments and are thus not using the old integer namespace. Therefore, there is no reason to have gpiod variants of these functions working with descriptors, especially since the (chip, offset) tuple is more suitable to the users of these functions (GPIO drivers, whereas GPIO descriptors are targeted at GPIO consumers). Signed-off-by: Alexandre Courbot Signed-off-by: Linus Walleij --- Documentation/gpio/driver.txt | 4 ++-- drivers/gpio/gpiolib-acpi.c | 6 +++--- drivers/gpio/gpiolib-legacy.c | 12 ------------ drivers/gpio/gpiolib-sysfs.c | 4 ++-- drivers/gpio/gpiolib.c | 30 ++++++++++++++++-------------- include/asm-generic/gpio.h | 3 --- include/linux/gpio/driver.h | 4 ++-- 7 files changed, 25 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/Documentation/gpio/driver.txt b/Documentation/gpio/driver.txt index fa9a0a8b3734..224dbbcd1804 100644 --- a/Documentation/gpio/driver.txt +++ b/Documentation/gpio/driver.txt @@ -157,12 +157,12 @@ Locking IRQ usage Input GPIOs can be used as IRQ signals. When this happens, a driver is requested to mark the GPIO as being used as an IRQ: - int gpiod_lock_as_irq(struct gpio_desc *desc) + int gpio_lock_as_irq(struct gpio_chip *chip, unsigned int offset) This will prevent the use of non-irq related GPIO APIs until the GPIO IRQ lock is released: - void gpiod_unlock_as_irq(struct gpio_desc *desc) + void gpio_unlock_as_irq(struct gpio_chip *chip, unsigned int offset) When implementing an irqchip inside a GPIO driver, these two functions should typically be called in the .startup() and .shutdown() callbacks from the diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 4a987917c186..d2e8600df02c 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -157,7 +157,7 @@ static acpi_status acpi_gpiochip_request_interrupt(struct acpi_resource *ares, gpiod_direction_input(desc); - ret = gpiod_lock_as_irq(desc); + ret = gpio_lock_as_irq(chip, pin); if (ret) { dev_err(chip->dev, "Failed to lock GPIO as interrupt\n"); goto fail_free_desc; @@ -212,7 +212,7 @@ static acpi_status acpi_gpiochip_request_interrupt(struct acpi_resource *ares, fail_free_event: kfree(event); fail_unlock_irq: - gpiod_unlock_as_irq(desc); + gpio_unlock_as_irq(chip, pin); fail_free_desc: gpiochip_free_own_desc(desc); @@ -263,7 +263,7 @@ static void acpi_gpiochip_free_interrupts(struct acpi_gpio_chip *acpi_gpio) desc = gpiochip_get_desc(chip, event->pin); if (WARN_ON(IS_ERR(desc))) continue; - gpiod_unlock_as_irq(desc); + gpio_unlock_as_irq(chip, event->pin); gpiochip_free_own_desc(desc); list_del(&event->node); kfree(event); diff --git a/drivers/gpio/gpiolib-legacy.c b/drivers/gpio/gpiolib-legacy.c index c684d94cdbb4..078ae6c2df79 100644 --- a/drivers/gpio/gpiolib-legacy.c +++ b/drivers/gpio/gpiolib-legacy.c @@ -100,15 +100,3 @@ void gpio_free_array(const struct gpio *array, size_t num) gpio_free((array++)->gpio); } EXPORT_SYMBOL_GPL(gpio_free_array); - -int gpio_lock_as_irq(struct gpio_chip *chip, unsigned int offset) -{ - return gpiod_lock_as_irq(gpiochip_get_desc(chip, offset)); -} -EXPORT_SYMBOL_GPL(gpio_lock_as_irq); - -void gpio_unlock_as_irq(struct gpio_chip *chip, unsigned int offset) -{ - return gpiod_unlock_as_irq(gpiochip_get_desc(chip, offset)); -} -EXPORT_SYMBOL_GPL(gpio_unlock_as_irq); diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index f150aa288fa1..be45a9283c28 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -161,7 +161,7 @@ static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev, desc->flags &= ~GPIO_TRIGGER_MASK; if (!gpio_flags) { - gpiod_unlock_as_irq(desc); + gpio_unlock_as_irq(desc->chip, gpio_chip_hwgpio(desc)); ret = 0; goto free_id; } @@ -200,7 +200,7 @@ static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev, if (ret < 0) goto free_id; - ret = gpiod_lock_as_irq(desc); + ret = gpio_lock_as_irq(desc->chip, gpio_chip_hwgpio(desc)); if (ret < 0) { gpiod_warn(desc, "failed to flag the GPIO for IRQ\n"); goto free_id; diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 38d176e31379..7582207c92e7 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1428,44 +1428,46 @@ int gpiod_to_irq(const struct gpio_desc *desc) EXPORT_SYMBOL_GPL(gpiod_to_irq); /** - * gpiod_lock_as_irq() - lock a GPIO to be used as IRQ - * @gpio: the GPIO line to lock as used for IRQ + * gpio_lock_as_irq() - lock a GPIO to be used as IRQ + * @chip: the chip the GPIO to lock belongs to + * @offset: the offset of the GPIO to lock as IRQ * * This is used directly by GPIO drivers that want to lock down * a certain GPIO line to be used for IRQs. */ -int gpiod_lock_as_irq(struct gpio_desc *desc) +int gpio_lock_as_irq(struct gpio_chip *chip, unsigned int offset) { - if (!desc) + if (offset >= chip->ngpio) return -EINVAL; - if (test_bit(FLAG_IS_OUT, &desc->flags)) { - gpiod_err(desc, + if (test_bit(FLAG_IS_OUT, &chip->desc[offset].flags)) { + chip_err(chip, "%s: tried to flag a GPIO set as output for IRQ\n", __func__); return -EIO; } - set_bit(FLAG_USED_AS_IRQ, &desc->flags); + set_bit(FLAG_USED_AS_IRQ, &chip->desc[offset].flags); return 0; } -EXPORT_SYMBOL_GPL(gpiod_lock_as_irq); +EXPORT_SYMBOL_GPL(gpio_lock_as_irq); /** - * gpiod_unlock_as_irq() - unlock a GPIO used as IRQ - * @gpio: the GPIO line to unlock from IRQ usage + * gpio_unlock_as_irq() - unlock a GPIO used as IRQ + * @chip: the chip the GPIO to lock belongs to + * @offset: the offset of the GPIO to lock as IRQ * * This is used directly by GPIO drivers that want to indicate * that a certain GPIO is no longer used exclusively for IRQ. */ -void gpiod_unlock_as_irq(struct gpio_desc *desc) +void gpio_unlock_as_irq(struct gpio_chip *chip, unsigned int offset) { - if (!desc) + if (offset >= chip->ngpio) return; - clear_bit(FLAG_USED_AS_IRQ, &desc->flags); + clear_bit(FLAG_USED_AS_IRQ, &chip->desc[offset].flags); } -EXPORT_SYMBOL_GPL(gpiod_unlock_as_irq); +EXPORT_SYMBOL_GPL(gpio_unlock_as_irq); /** * gpiod_get_raw_value_cansleep() - return a gpio's raw value diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 23e364538ab5..c1d4105e1c1d 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -110,9 +110,6 @@ static inline int __gpio_to_irq(unsigned gpio) return gpiod_to_irq(gpio_to_desc(gpio)); } -extern int gpio_lock_as_irq(struct gpio_chip *chip, unsigned int offset); -extern void gpio_unlock_as_irq(struct gpio_chip *chip, unsigned int offset); - extern int gpio_request_one(unsigned gpio, unsigned long flags, const char *label); extern int gpio_request_array(const struct gpio *array, size_t num); extern void gpio_free_array(const struct gpio *array, size_t num); diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 88f92dfae545..c66c91682d9e 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -146,8 +146,8 @@ extern struct gpio_chip *gpiochip_find(void *data, int (*match)(struct gpio_chip *chip, void *data)); /* lock/unlock as IRQ */ -int gpiod_lock_as_irq(struct gpio_desc *desc); -void gpiod_unlock_as_irq(struct gpio_desc *desc); +int gpio_lock_as_irq(struct gpio_chip *chip, unsigned int offset); +void gpio_unlock_as_irq(struct gpio_chip *chip, unsigned int offset); struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc); -- cgit v1.2.3 From d82da79722400c63cc70f4c9c2493e31561ea607 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Tue, 22 Jul 2014 16:17:43 +0900 Subject: gpio: move gpio_ensure_requested() into legacy C file gpio_ensure_requested() only makes sense when using the integer-based GPIO API, so make sure it is called from there instead of the gpiod API which we know cannot be called with a non-requested GPIO anyway. The uses of gpio_ensure_requested() in the gpiod API were kind of out-of-place anyway, so putting them in gpio-legacy.c helps clearing the code. Actually, considering the time this ensure_requested mechanism has been around, maybe we should just turn this patch into "remove gpio_ensure_requested()" if we know for sure that no user depend on it anymore? Signed-off-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-legacy.c | 106 ++++++++++++++++++++++++++++++++++ drivers/gpio/gpiolib.c | 129 ++---------------------------------------- include/asm-generic/gpio.h | 15 +---- 3 files changed, 113 insertions(+), 137 deletions(-) (limited to 'include') diff --git a/drivers/gpio/gpiolib-legacy.c b/drivers/gpio/gpiolib-legacy.c index 078ae6c2df79..0f9429b2522a 100644 --- a/drivers/gpio/gpiolib-legacy.c +++ b/drivers/gpio/gpiolib-legacy.c @@ -5,6 +5,64 @@ #include "gpiolib.h" +/* Warn when drivers omit gpio_request() calls -- legal but ill-advised + * when setting direction, and otherwise illegal. Until board setup code + * and drivers use explicit requests everywhere (which won't happen when + * those calls have no teeth) we can't avoid autorequesting. This nag + * message should motivate switching to explicit requests... so should + * the weaker cleanup after faults, compared to gpio_request(). + * + * NOTE: the autorequest mechanism is going away; at this point it's + * only "legal" in the sense that (old) code using it won't break yet, + * but instead only triggers a WARN() stack dump. + */ +static int gpio_ensure_requested(struct gpio_desc *desc) +{ + struct gpio_chip *chip = desc->chip; + unsigned long flags; + bool request = false; + int err = 0; + + spin_lock_irqsave(&gpio_lock, flags); + + if (WARN(test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0, + "autorequest GPIO-%d\n", desc_to_gpio(desc))) { + if (!try_module_get(chip->owner)) { + gpiod_err(desc, "%s: module can't be gotten\n", + __func__); + clear_bit(FLAG_REQUESTED, &desc->flags); + /* lose */ + err = -EIO; + goto end; + } + desc->label = "[auto]"; + /* caller must chip->request() w/o spinlock */ + if (chip->request) + request = true; + } + +end: + spin_unlock_irqrestore(&gpio_lock, flags); + + if (request) { + might_sleep_if(chip->can_sleep); + err = chip->request(chip, gpio_chip_hwgpio(desc)); + + if (err < 0) { + gpiod_dbg(desc, "%s: chip request fail, %d\n", + __func__, err); + spin_lock_irqsave(&gpio_lock, flags); + + desc->label = NULL; + clear_bit(FLAG_REQUESTED, &desc->flags); + + spin_unlock_irqrestore(&gpio_lock, flags); + } + } + + return err; +} + void gpio_free(unsigned gpio) { gpiod_free(gpio_to_desc(gpio)); @@ -100,3 +158,51 @@ void gpio_free_array(const struct gpio *array, size_t num) gpio_free((array++)->gpio); } EXPORT_SYMBOL_GPL(gpio_free_array); + +int gpio_direction_input(unsigned gpio) +{ + struct gpio_desc *desc = gpio_to_desc(gpio); + int err; + + if (!desc) + return -EINVAL; + + err = gpio_ensure_requested(desc); + if (err < 0) + return err; + + return gpiod_direction_input(desc); +} +EXPORT_SYMBOL_GPL(gpio_direction_input); + +int gpio_direction_output(unsigned gpio, int value) +{ + struct gpio_desc *desc = gpio_to_desc(gpio); + int err; + + if (!desc) + return -EINVAL; + + err = gpio_ensure_requested(desc); + if (err < 0) + return err; + + return gpiod_direction_output_raw(desc, value); +} +EXPORT_SYMBOL_GPL(gpio_direction_output); + +int gpio_set_debounce(unsigned gpio, unsigned debounce) +{ + struct gpio_desc *desc = gpio_to_desc(gpio); + int err; + + if (!desc) + return -EINVAL; + + err = gpio_ensure_requested(desc); + if (err < 0) + return err; + + return gpiod_set_debounce(desc, debounce); +} +EXPORT_SYMBOL_GPL(gpio_set_debounce); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 7582207c92e7..412d64e93cfb 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -95,39 +95,6 @@ int desc_to_gpio(const struct gpio_desc *desc) EXPORT_SYMBOL_GPL(desc_to_gpio); -/* Warn when drivers omit gpio_request() calls -- legal but ill-advised - * when setting direction, and otherwise illegal. Until board setup code - * and drivers use explicit requests everywhere (which won't happen when - * those calls have no teeth) we can't avoid autorequesting. This nag - * message should motivate switching to explicit requests... so should - * the weaker cleanup after faults, compared to gpio_request(). - * - * NOTE: the autorequest mechanism is going away; at this point it's - * only "legal" in the sense that (old) code using it won't break yet, - * but instead only triggers a WARN() stack dump. - */ -static int gpio_ensure_requested(struct gpio_desc *desc) -{ - const struct gpio_chip *chip = desc->chip; - const int gpio = desc_to_gpio(desc); - - if (WARN(test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0, - "autorequest GPIO-%d\n", gpio)) { - if (!try_module_get(chip->owner)) { - gpiod_err(desc, "%s: module can't be gotten\n", - __func__); - clear_bit(FLAG_REQUESTED, &desc->flags); - /* lose */ - return -EIO; - } - desc_set_label(desc, "[auto]"); - /* caller must chip->request() w/o spinlock */ - if (chip->request) - return 1; - } - return 0; -} - /** * gpiod_to_chip - Return the GPIO chip to which a GPIO descriptor belongs * @desc: descriptor to return the chip of @@ -964,10 +931,8 @@ void gpiochip_free_own_desc(struct gpio_desc *desc) */ int gpiod_direction_input(struct gpio_desc *desc) { - unsigned long flags; struct gpio_chip *chip; int status = -EINVAL; - int offset; if (!desc || !desc->chip) { pr_warn("%s: invalid GPIO\n", __func__); @@ -982,52 +947,20 @@ int gpiod_direction_input(struct gpio_desc *desc) return -EIO; } - spin_lock_irqsave(&gpio_lock, flags); - - status = gpio_ensure_requested(desc); - if (status < 0) - goto fail; - - /* now we know the gpio is valid and chip won't vanish */ - - spin_unlock_irqrestore(&gpio_lock, flags); - - might_sleep_if(chip->can_sleep); - - offset = gpio_chip_hwgpio(desc); - if (status) { - status = chip->request(chip, offset); - if (status < 0) { - gpiod_dbg(desc, "%s: chip request fail, %d\n", - __func__, status); - /* and it's not available to anyone else ... - * gpio_request() is the fully clean solution. - */ - goto lose; - } - } - - status = chip->direction_input(chip, offset); + status = chip->direction_input(chip, gpio_chip_hwgpio(desc)); if (status == 0) clear_bit(FLAG_IS_OUT, &desc->flags); trace_gpio_direction(desc_to_gpio(desc), 1, status); -lose: - return status; -fail: - spin_unlock_irqrestore(&gpio_lock, flags); - if (status) - gpiod_dbg(desc, "%s: status %d\n", __func__, status); + return status; } EXPORT_SYMBOL_GPL(gpiod_direction_input); static int _gpiod_direction_output_raw(struct gpio_desc *desc, int value) { - unsigned long flags; struct gpio_chip *chip; int status = -EINVAL; - int offset; /* GPIOs used for IRQs shall not be set as output */ if (test_bit(FLAG_USED_AS_IRQ, &desc->flags)) { @@ -1053,42 +986,11 @@ static int _gpiod_direction_output_raw(struct gpio_desc *desc, int value) return -EIO; } - spin_lock_irqsave(&gpio_lock, flags); - - status = gpio_ensure_requested(desc); - if (status < 0) - goto fail; - - /* now we know the gpio is valid and chip won't vanish */ - - spin_unlock_irqrestore(&gpio_lock, flags); - - might_sleep_if(chip->can_sleep); - - offset = gpio_chip_hwgpio(desc); - if (status) { - status = chip->request(chip, offset); - if (status < 0) { - gpiod_dbg(desc, "%s: chip request fail, %d\n", - __func__, status); - /* and it's not available to anyone else ... - * gpio_request() is the fully clean solution. - */ - goto lose; - } - } - - status = chip->direction_output(chip, offset, value); + status = chip->direction_output(chip, gpio_chip_hwgpio(desc), value); if (status == 0) set_bit(FLAG_IS_OUT, &desc->flags); trace_gpio_value(desc_to_gpio(desc), 0, value); trace_gpio_direction(desc_to_gpio(desc), 0, status); -lose: - return status; -fail: - spin_unlock_irqrestore(&gpio_lock, flags); - if (status) - gpiod_dbg(desc, "%s: gpio status %d\n", __func__, status); return status; } @@ -1147,10 +1049,7 @@ EXPORT_SYMBOL_GPL(gpiod_direction_output); */ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) { - unsigned long flags; struct gpio_chip *chip; - int status = -EINVAL; - int offset; if (!desc || !desc->chip) { pr_warn("%s: invalid GPIO\n", __func__); @@ -1165,27 +1064,7 @@ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) return -ENOTSUPP; } - spin_lock_irqsave(&gpio_lock, flags); - - status = gpio_ensure_requested(desc); - if (status < 0) - goto fail; - - /* now we know the gpio is valid and chip won't vanish */ - - spin_unlock_irqrestore(&gpio_lock, flags); - - might_sleep_if(chip->can_sleep); - - offset = gpio_chip_hwgpio(desc); - return chip->set_debounce(chip, offset, debounce); - -fail: - spin_unlock_irqrestore(&gpio_lock, flags); - if (status) - gpiod_dbg(desc, "%s: status %d\n", __func__, status); - - return status; + return chip->set_debounce(chip, gpio_chip_hwgpio(desc), debounce); } EXPORT_SYMBOL_GPL(gpiod_set_debounce); diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index c1d4105e1c1d..39a1d06950d9 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -63,19 +63,10 @@ static inline struct gpio_chip *gpio_to_chip(unsigned gpio) extern int gpio_request(unsigned gpio, const char *label); extern void gpio_free(unsigned gpio); -static inline int gpio_direction_input(unsigned gpio) -{ - return gpiod_direction_input(gpio_to_desc(gpio)); -} -static inline int gpio_direction_output(unsigned gpio, int value) -{ - return gpiod_direction_output_raw(gpio_to_desc(gpio), value); -} +extern int gpio_direction_input(unsigned gpio); +extern int gpio_direction_output(unsigned gpio, int value); -static inline int gpio_set_debounce(unsigned gpio, unsigned debounce) -{ - return gpiod_set_debounce(gpio_to_desc(gpio), debounce); -} +extern int gpio_set_debounce(unsigned gpio, unsigned debounce); static inline int gpio_get_value_cansleep(unsigned gpio) { -- cgit v1.2.3 From 76f4108892d9a9e3408bba839914f97a54086a6f Mon Sep 17 00:00:00 2001 From: John Stultz Date: Wed, 16 Jul 2014 21:03:52 +0000 Subject: hrtimer: Cleanup hrtimer accessors to the timekepeing state Rather then having two similar but totally different implementations that provide timekeeping state to the hrtimer code, try to unify the two implementations to be more simliar. Thus this clarifies ktime_get_update_offsets to ktime_get_update_offsets_now and changes get_xtime... to ktime_get_update_offsets_tick. Signed-off-by: John Stultz Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/hrtimer.h | 9 ++++++--- include/linux/time.h | 2 -- kernel/time/hrtimer.c | 19 ++++++++----------- kernel/time/timekeeping.c | 36 +++++++++++++++++++++++------------- 4 files changed, 37 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index bb4ffff31c69..e84eb4f228cd 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -331,9 +331,12 @@ extern ktime_t ktime_get_real(void); extern ktime_t ktime_get_boottime(void); extern ktime_t ktime_get_monotonic_offset(void); extern ktime_t ktime_get_clocktai(void); -extern ktime_t ktime_get_update_offsets(ktime_t *offs_real, ktime_t *offs_boot, - ktime_t *offs_tai); - +extern ktime_t ktime_get_update_offsets_tick(ktime_t *offs_real, + ktime_t *offs_boot, + ktime_t *offs_tai); +extern ktime_t ktime_get_update_offsets_now(ktime_t *offs_real, + ktime_t *offs_boot, + ktime_t *offs_tai); DECLARE_PER_CPU(struct tick_device, tick_cpu_device); diff --git a/include/linux/time.h b/include/linux/time.h index d5d229b2e5af..f6d990d1c79a 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -133,8 +133,6 @@ unsigned long get_seconds(void); struct timespec current_kernel_time(void); struct timespec __current_kernel_time(void); /* does not take xtime_lock */ struct timespec get_monotonic_coarse(void); -void get_xtime_and_monotonic_and_sleep_offset(struct timespec *xtim, - struct timespec *wtom, struct timespec *sleep); void timekeeping_inject_sleeptime(struct timespec *delta); #define CURRENT_TIME (current_kernel_time()) diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c index 66a6dc1075ad..2f4ef8a1e5ff 100644 --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c @@ -114,21 +114,18 @@ static inline int hrtimer_clockid_to_base(clockid_t clock_id) */ static void hrtimer_get_softirq_time(struct hrtimer_cpu_base *base) { - ktime_t xtim, mono, boot; - struct timespec xts, tom, slp; - s32 tai_offset; + ktime_t xtim, mono, boot, tai; + ktime_t off_real, off_boot, off_tai; - get_xtime_and_monotonic_and_sleep_offset(&xts, &tom, &slp); - tai_offset = timekeeping_get_tai_offset(); + mono = ktime_get_update_offsets_tick(&off_real, &off_boot, &off_tai); + boot = ktime_add(mono, off_boot); + xtim = ktime_add(mono, off_real); + tai = ktime_add(xtim, off_tai); - xtim = timespec_to_ktime(xts); - mono = ktime_add(xtim, timespec_to_ktime(tom)); - boot = ktime_add(mono, timespec_to_ktime(slp)); base->clock_base[HRTIMER_BASE_REALTIME].softirq_time = xtim; base->clock_base[HRTIMER_BASE_MONOTONIC].softirq_time = mono; base->clock_base[HRTIMER_BASE_BOOTTIME].softirq_time = boot; - base->clock_base[HRTIMER_BASE_TAI].softirq_time = - ktime_add(xtim, ktime_set(tai_offset, 0)); + base->clock_base[HRTIMER_BASE_TAI].softirq_time = tai; } /* @@ -673,7 +670,7 @@ static inline ktime_t hrtimer_update_base(struct hrtimer_cpu_base *base) ktime_t *offs_boot = &base->clock_base[HRTIMER_BASE_BOOTTIME].offset; ktime_t *offs_tai = &base->clock_base[HRTIMER_BASE_TAI].offset; - return ktime_get_update_offsets(offs_real, offs_boot, offs_tai); + return ktime_get_update_offsets_now(offs_real, offs_boot, offs_tai); } /* diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 908861c58e62..b94fa3652aaa 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -1581,29 +1581,39 @@ void do_timer(unsigned long ticks) } /** - * get_xtime_and_monotonic_and_sleep_offset() - get xtime, wall_to_monotonic, - * and sleep offsets. - * @xtim: pointer to timespec to be set with xtime - * @wtom: pointer to timespec to be set with wall_to_monotonic - * @sleep: pointer to timespec to be set with time in suspend + * ktime_get_update_offsets_tick - hrtimer helper + * @offs_real: pointer to storage for monotonic -> realtime offset + * @offs_boot: pointer to storage for monotonic -> boottime offset + * @offs_tai: pointer to storage for monotonic -> clock tai offset + * + * Returns monotonic time at last tick and various offsets */ -void get_xtime_and_monotonic_and_sleep_offset(struct timespec *xtim, - struct timespec *wtom, struct timespec *sleep) +ktime_t ktime_get_update_offsets_tick(ktime_t *offs_real, ktime_t *offs_boot, + ktime_t *offs_tai) { struct timekeeper *tk = &timekeeper; - unsigned long seq; + struct timespec ts; + ktime_t now; + unsigned int seq; do { seq = read_seqcount_begin(&timekeeper_seq); - *xtim = tk_xtime(tk); - *wtom = tk->wall_to_monotonic; - *sleep = tk->total_sleep_time; + + ts = tk_xtime(tk); + + *offs_real = tk->offs_real; + *offs_boot = tk->offs_boot; + *offs_tai = tk->offs_tai; } while (read_seqcount_retry(&timekeeper_seq, seq)); + + now = ktime_set(ts.tv_sec, ts.tv_nsec); + now = ktime_sub(now, *offs_real); + return now; } #ifdef CONFIG_HIGH_RES_TIMERS /** - * ktime_get_update_offsets - hrtimer helper + * ktime_get_update_offsets_now - hrtimer helper * @offs_real: pointer to storage for monotonic -> realtime offset * @offs_boot: pointer to storage for monotonic -> boottime offset * @offs_tai: pointer to storage for monotonic -> clock tai offset @@ -1611,7 +1621,7 @@ void get_xtime_and_monotonic_and_sleep_offset(struct timespec *xtim, * Returns current monotonic time and updates the offsets * Called from hrtimer_interrupt() or retrigger_next_event() */ -ktime_t ktime_get_update_offsets(ktime_t *offs_real, ktime_t *offs_boot, +ktime_t ktime_get_update_offsets_now(ktime_t *offs_real, ktime_t *offs_boot, ktime_t *offs_tai) { struct timekeeper *tk = &timekeeper; -- cgit v1.2.3 From 24e4a8c3e8868874835b0f1ad6dd417341e99822 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Wed, 16 Jul 2014 21:03:53 +0000 Subject: ktime: Kill non-scalar ktime_t implementation for 2038 The non-scalar ktime_t implementation is basically a timespec which has to be changed to support dates past 2038 on 32bit systems. This patch removes the non-scalar ktime_t implementation, forcing the scalar s64 nanosecond version on all architectures. This may have additional performance overhead on some 32bit systems when converting between ktime_t and timespec structures, however the majority of 32bit systems (arm and i386) were already using scalar ktime_t, so no performance regressions will be seen on those platforms. On affected platforms, I'm open to finding optimizations, including avoiding converting to timespecs where possible. [ tglx: We can now cleanup the ktime_t.tv64 mess, but thats a different issue and we can throw a coccinelle script at it ] Signed-off-by: John Stultz Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- arch/arm/Kconfig | 1 - arch/hexagon/Kconfig | 1 - arch/s390/Kconfig | 1 - arch/x86/Kconfig | 1 - include/linux/ktime.h | 173 +--------------------------------------------- include/linux/time.h | 11 ++- kernel/time/Kconfig | 4 -- kernel/time/hrtimer.c | 54 --------------- kernel/time/timekeeping.c | 7 +- 9 files changed, 7 insertions(+), 246 deletions(-) (limited to 'include') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 05a71511ab3c..b9f6728331c8 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -64,7 +64,6 @@ config ARM select HAVE_UID16 select HAVE_VIRT_CPU_ACCOUNTING_GEN select IRQ_FORCED_THREADING - select KTIME_SCALAR select MODULES_USE_ELF_REL select NO_BOOTMEM select OLD_SIGACTION diff --git a/arch/hexagon/Kconfig b/arch/hexagon/Kconfig index 0fd6138f6203..4dc89d1f9c48 100644 --- a/arch/hexagon/Kconfig +++ b/arch/hexagon/Kconfig @@ -23,7 +23,6 @@ config HEXAGON select GENERIC_IOMAP select GENERIC_SMP_IDLE_THREAD select STACKTRACE_SUPPORT - select KTIME_SCALAR select GENERIC_CLOCKEVENTS select GENERIC_CLOCKEVENTS_BROADCAST select MODULES_USE_ELF_RELA diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index bb63499fc5d3..1afc7a686702 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -137,7 +137,6 @@ config S390 select HAVE_SYSCALL_TRACEPOINTS select HAVE_UID16 if 32BIT select HAVE_VIRT_CPU_ACCOUNTING - select KTIME_SCALAR if 32BIT select MODULES_USE_ELF_RELA select NO_BOOTMEM select OLD_SIGACTION diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index a8f749ef0fdc..7fa17b5ce668 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -111,7 +111,6 @@ config X86 select ARCH_CLOCKSOURCE_DATA select GENERIC_CLOCKEVENTS_BROADCAST if X86_64 || (X86_32 && X86_LOCAL_APIC) select GENERIC_TIME_VSYSCALL - select KTIME_SCALAR if X86_32 select GENERIC_STRNCPY_FROM_USER select GENERIC_STRNLEN_USER select HAVE_CONTEXT_TRACKING if X86_64 diff --git a/include/linux/ktime.h b/include/linux/ktime.h index de9e46e6bcc9..fbc64f8481b7 100644 --- a/include/linux/ktime.h +++ b/include/linux/ktime.h @@ -27,43 +27,19 @@ /* * ktime_t: * - * On 64-bit CPUs a single 64-bit variable is used to store the hrtimers + * A single 64-bit variable is used to store the hrtimers * internal representation of time values in scalar nanoseconds. The * design plays out best on 64-bit CPUs, where most conversions are * NOPs and most arithmetic ktime_t operations are plain arithmetic * operations. * - * On 32-bit CPUs an optimized representation of the timespec structure - * is used to avoid expensive conversions from and to timespecs. The - * endian-aware order of the tv struct members is chosen to allow - * mathematical operations on the tv64 member of the union too, which - * for certain operations produces better code. - * - * For architectures with efficient support for 64/32-bit conversions the - * plain scalar nanosecond based representation can be selected by the - * config switch CONFIG_KTIME_SCALAR. */ union ktime { s64 tv64; -#if BITS_PER_LONG != 64 && !defined(CONFIG_KTIME_SCALAR) - struct { -# ifdef __BIG_ENDIAN - s32 sec, nsec; -# else - s32 nsec, sec; -# endif - } tv; -#endif }; typedef union ktime ktime_t; /* Kill this */ -/* - * ktime_t definitions when using the 64-bit scalar representation: - */ - -#if (BITS_PER_LONG == 64) || defined(CONFIG_KTIME_SCALAR) - /** * ktime_set - Set a ktime_t variable from a seconds/nanoseconds value * @secs: seconds to set @@ -123,153 +99,6 @@ static inline ktime_t timeval_to_ktime(struct timeval tv) /* Convert ktime_t to nanoseconds - NOP in the scalar storage format: */ #define ktime_to_ns(kt) ((kt).tv64) -#else /* !((BITS_PER_LONG == 64) || defined(CONFIG_KTIME_SCALAR)) */ - -/* - * Helper macros/inlines to get the ktime_t math right in the timespec - * representation. The macros are sometimes ugly - their actual use is - * pretty okay-ish, given the circumstances. We do all this for - * performance reasons. The pure scalar nsec_t based code was nice and - * simple, but created too many 64-bit / 32-bit conversions and divisions. - * - * Be especially aware that negative values are represented in a way - * that the tv.sec field is negative and the tv.nsec field is greater - * or equal to zero but less than nanoseconds per second. This is the - * same representation which is used by timespecs. - * - * tv.sec < 0 and 0 >= tv.nsec < NSEC_PER_SEC - */ - -/* Set a ktime_t variable to a value in sec/nsec representation: */ -static inline ktime_t ktime_set(const long secs, const unsigned long nsecs) -{ - return (ktime_t) { .tv = { .sec = secs, .nsec = nsecs } }; -} - -/** - * ktime_sub - subtract two ktime_t variables - * @lhs: minuend - * @rhs: subtrahend - * - * Return: The remainder of the subtraction. - */ -static inline ktime_t ktime_sub(const ktime_t lhs, const ktime_t rhs) -{ - ktime_t res; - - res.tv64 = lhs.tv64 - rhs.tv64; - if (res.tv.nsec < 0) - res.tv.nsec += NSEC_PER_SEC; - - return res; -} - -/** - * ktime_add - add two ktime_t variables - * @add1: addend1 - * @add2: addend2 - * - * Return: The sum of @add1 and @add2. - */ -static inline ktime_t ktime_add(const ktime_t add1, const ktime_t add2) -{ - ktime_t res; - - res.tv64 = add1.tv64 + add2.tv64; - /* - * performance trick: the (u32) -NSEC gives 0x00000000Fxxxxxxx - * so we subtract NSEC_PER_SEC and add 1 to the upper 32 bit. - * - * it's equivalent to: - * tv.nsec -= NSEC_PER_SEC - * tv.sec ++; - */ - if (res.tv.nsec >= NSEC_PER_SEC) - res.tv64 += (u32)-NSEC_PER_SEC; - - return res; -} - -/** - * ktime_add_ns - Add a scalar nanoseconds value to a ktime_t variable - * @kt: addend - * @nsec: the scalar nsec value to add - * - * Return: The sum of @kt and @nsec in ktime_t format. - */ -extern ktime_t ktime_add_ns(const ktime_t kt, u64 nsec); - -/** - * ktime_sub_ns - Subtract a scalar nanoseconds value from a ktime_t variable - * @kt: minuend - * @nsec: the scalar nsec value to subtract - * - * Return: The subtraction of @nsec from @kt in ktime_t format. - */ -extern ktime_t ktime_sub_ns(const ktime_t kt, u64 nsec); - -/** - * timespec_to_ktime - convert a timespec to ktime_t format - * @ts: the timespec variable to convert - * - * Return: A ktime_t variable with the converted timespec value. - */ -static inline ktime_t timespec_to_ktime(const struct timespec ts) -{ - return (ktime_t) { .tv = { .sec = (s32)ts.tv_sec, - .nsec = (s32)ts.tv_nsec } }; -} - -/** - * timeval_to_ktime - convert a timeval to ktime_t format - * @tv: the timeval variable to convert - * - * Return: A ktime_t variable with the converted timeval value. - */ -static inline ktime_t timeval_to_ktime(const struct timeval tv) -{ - return (ktime_t) { .tv = { .sec = (s32)tv.tv_sec, - .nsec = (s32)(tv.tv_usec * - NSEC_PER_USEC) } }; -} - -/** - * ktime_to_timespec - convert a ktime_t variable to timespec format - * @kt: the ktime_t variable to convert - * - * Return: The timespec representation of the ktime value. - */ -static inline struct timespec ktime_to_timespec(const ktime_t kt) -{ - return (struct timespec) { .tv_sec = (time_t) kt.tv.sec, - .tv_nsec = (long) kt.tv.nsec }; -} - -/** - * ktime_to_timeval - convert a ktime_t variable to timeval format - * @kt: the ktime_t variable to convert - * - * Return: The timeval representation of the ktime value. - */ -static inline struct timeval ktime_to_timeval(const ktime_t kt) -{ - return (struct timeval) { - .tv_sec = (time_t) kt.tv.sec, - .tv_usec = (suseconds_t) (kt.tv.nsec / NSEC_PER_USEC) }; -} - -/** - * ktime_to_ns - convert a ktime_t variable to scalar nanoseconds - * @kt: the ktime_t variable to convert - * - * Return: The scalar nanoseconds representation of @kt. - */ -static inline s64 ktime_to_ns(const ktime_t kt) -{ - return (s64) kt.tv.sec * NSEC_PER_SEC + kt.tv.nsec; -} - -#endif /* !((BITS_PER_LONG == 64) || defined(CONFIG_KTIME_SCALAR)) */ /** * ktime_equal - Compares two ktime_t variables to see if they are equal diff --git a/include/linux/time.h b/include/linux/time.h index f6d990d1c79a..129f0bd36a8d 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -19,6 +19,10 @@ extern struct timezone sys_tz; #define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1) +/* Located here for timespec_valid_strict */ +#define KTIME_MAX ((s64)~((u64)1 << 63)) +#define KTIME_SEC_MAX (KTIME_MAX / NSEC_PER_SEC) + static inline int timespec_equal(const struct timespec *a, const struct timespec *b) { @@ -84,13 +88,6 @@ 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: */ diff --git a/kernel/time/Kconfig b/kernel/time/Kconfig index f448513a45ed..feccfd888732 100644 --- a/kernel/time/Kconfig +++ b/kernel/time/Kconfig @@ -20,10 +20,6 @@ config GENERIC_TIME_VSYSCALL config GENERIC_TIME_VSYSCALL_OLD bool -# ktime_t scalar 64bit nsec representation -config KTIME_SCALAR - bool - # Old style timekeeping config ARCH_USES_GETTIMEOFFSET bool diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c index 2f4ef8a1e5ff..19f211051c35 100644 --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c @@ -261,60 +261,6 @@ lock_hrtimer_base(const struct hrtimer *timer, unsigned long *flags) * too large for inlining: */ #if BITS_PER_LONG < 64 -# ifndef CONFIG_KTIME_SCALAR -/** - * ktime_add_ns - Add a scalar nanoseconds value to a ktime_t variable - * @kt: addend - * @nsec: the scalar nsec value to add - * - * Returns the sum of kt and nsec in ktime_t format - */ -ktime_t ktime_add_ns(const ktime_t kt, u64 nsec) -{ - ktime_t tmp; - - if (likely(nsec < NSEC_PER_SEC)) { - tmp.tv64 = nsec; - } else { - unsigned long rem = do_div(nsec, NSEC_PER_SEC); - - /* Make sure nsec fits into long */ - if (unlikely(nsec > KTIME_SEC_MAX)) - return (ktime_t){ .tv64 = KTIME_MAX }; - - tmp = ktime_set((long)nsec, rem); - } - - return ktime_add(kt, tmp); -} - -EXPORT_SYMBOL_GPL(ktime_add_ns); - -/** - * ktime_sub_ns - Subtract a scalar nanoseconds value from a ktime_t variable - * @kt: minuend - * @nsec: the scalar nsec value to subtract - * - * Returns the subtraction of @nsec from @kt in ktime_t format - */ -ktime_t ktime_sub_ns(const ktime_t kt, u64 nsec) -{ - ktime_t tmp; - - if (likely(nsec < NSEC_PER_SEC)) { - tmp.tv64 = nsec; - } else { - unsigned long rem = do_div(nsec, NSEC_PER_SEC); - - tmp = ktime_set((long)nsec, rem); - } - - return ktime_sub(kt, tmp); -} - -EXPORT_SYMBOL_GPL(ktime_sub_ns); -# endif /* !CONFIG_KTIME_SCALAR */ - /* * Divide a ktime value by a nanosecond value */ diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index b94fa3652aaa..cafef242d8f9 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -344,11 +344,8 @@ ktime_t ktime_get(void) nsecs = timekeeping_get_ns(tk) + tk->wall_to_monotonic.tv_nsec; } while (read_seqcount_retry(&timekeeper_seq, seq)); - /* - * Use ktime_set/ktime_add_ns to create a proper ktime on - * 32-bit architectures without CONFIG_KTIME_SCALAR. - */ - return ktime_add_ns(ktime_set(secs, 0), nsecs); + + return ktime_set(secs, nsecs); } EXPORT_SYMBOL_GPL(ktime_get); -- cgit v1.2.3 From 166afb64511eef08e13331b970c44fe91cea45ef Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jul 2014 21:03:55 +0000 Subject: ktime: Sanitize ktime_to_us/ms conversion With the plain nanoseconds based ktime_t we can simply use ktime_divns() instead of going through loops and hoops of timespec/timeval conversion. Reported-by: John Stultz Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/hrtimer.h | 6 ------ include/linux/ktime.h | 12 ++++++++---- kernel/time/hrtimer.c | 1 + 3 files changed, 9 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index e84eb4f228cd..adf5056bd7b3 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -457,12 +457,6 @@ extern void hrtimer_run_pending(void); /* Bootup initialization: */ extern void __init hrtimers_init(void); -#if BITS_PER_LONG < 64 -extern u64 ktime_divns(const ktime_t kt, s64 div); -#else /* BITS_PER_LONG < 64 */ -# define ktime_divns(kt, div) (u64)((kt).tv64 / (div)) -#endif - /* Show pending timers: */ extern void sysrq_timer_list_show(void); diff --git a/include/linux/ktime.h b/include/linux/ktime.h index fbc64f8481b7..74eaba9b3569 100644 --- a/include/linux/ktime.h +++ b/include/linux/ktime.h @@ -157,16 +157,20 @@ static inline bool ktime_before(const ktime_t cmp1, const ktime_t cmp2) return ktime_compare(cmp1, cmp2) < 0; } +#if BITS_PER_LONG < 64 +extern u64 ktime_divns(const ktime_t kt, s64 div); +#else /* BITS_PER_LONG < 64 */ +# define ktime_divns(kt, div) (u64)((kt).tv64 / (div)) +#endif + static inline s64 ktime_to_us(const ktime_t kt) { - struct timeval tv = ktime_to_timeval(kt); - return (s64) tv.tv_sec * USEC_PER_SEC + tv.tv_usec; + return ktime_divns(kt, NSEC_PER_USEC); } static inline s64 ktime_to_ms(const ktime_t kt) { - struct timeval tv = ktime_to_timeval(kt); - return (s64) tv.tv_sec * MSEC_PER_SEC + tv.tv_usec / USEC_PER_MSEC; + return ktime_divns(kt, NSEC_PER_MSEC); } static inline s64 ktime_us_delta(const ktime_t later, const ktime_t earlier) diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c index 19f211051c35..64843a836637 100644 --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c @@ -280,6 +280,7 @@ u64 ktime_divns(const ktime_t kt, s64 div) return dclc; } +EXPORT_SYMBOL_GPL(ktime_divns); #endif /* BITS_PER_LONG >= 64 */ /* -- cgit v1.2.3 From b17b20d70dcbe48dd1aa6aba073a60ddfce5d7db Mon Sep 17 00:00:00 2001 From: John Stultz Date: Wed, 16 Jul 2014 21:03:56 +0000 Subject: ktime: Change ktime_set() to take 64bit seconds value In order to support dates past 2038 on 32bit systems, ktime_set() needs to handle 64bit second values. [ tglx: Removed the BITS_PER_LONG check ] Signed-off-by: John Stultz Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/ktime.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/ktime.h b/include/linux/ktime.h index 74eaba9b3569..538c283714e1 100644 --- a/include/linux/ktime.h +++ b/include/linux/ktime.h @@ -47,13 +47,12 @@ typedef union ktime ktime_t; /* Kill this */ * * Return: The ktime_t representation of the value. */ -static inline ktime_t ktime_set(const long secs, const unsigned long nsecs) +static inline ktime_t ktime_set(const s64 secs, const unsigned long nsecs) { -#if (BITS_PER_LONG == 64) if (unlikely(secs >= KTIME_SEC_MAX)) return (ktime_t){ .tv64 = KTIME_MAX }; -#endif - return (ktime_t) { .tv64 = (s64)secs * NSEC_PER_SEC + (s64)nsecs }; + + return (ktime_t) { .tv64 = secs * NSEC_PER_SEC + (s64)nsecs }; } /* Subtract two ktime_t variables. rem = lhs -rhs: */ -- cgit v1.2.3 From 361a3bf00582469877f8d18ff20f1efa6b781274 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Wed, 16 Jul 2014 21:03:58 +0000 Subject: time64: Add time64.h header and define struct timespec64 Define the timespec64 structure and standard helper functions. [ tglx: Make it 32bit only. 64bit really can map timespec to timespec64 ] Signed-off-by: John Stultz Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/time.h | 15 +---- include/linux/time64.h | 162 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+), 14 deletions(-) create mode 100644 include/linux/time64.h (limited to 'include') diff --git a/include/linux/time.h b/include/linux/time.h index 129f0bd36a8d..234feac7f1c3 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -4,25 +4,12 @@ # include # include # include -#include +# include extern struct timezone sys_tz; -/* Parameters used to convert the timespec values: */ -#define MSEC_PER_SEC 1000L -#define USEC_PER_MSEC 1000L -#define NSEC_PER_USEC 1000L -#define NSEC_PER_MSEC 1000000L -#define USEC_PER_SEC 1000000L -#define NSEC_PER_SEC 1000000000L -#define FSEC_PER_SEC 1000000000000000LL - #define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1) -/* Located here for timespec_valid_strict */ -#define KTIME_MAX ((s64)~((u64)1 << 63)) -#define KTIME_SEC_MAX (KTIME_MAX / NSEC_PER_SEC) - static inline int timespec_equal(const struct timespec *a, const struct timespec *b) { diff --git a/include/linux/time64.h b/include/linux/time64.h new file mode 100644 index 000000000000..e7b499e1cd79 --- /dev/null +++ b/include/linux/time64.h @@ -0,0 +1,162 @@ +#ifndef _LINUX_TIME64_H +#define _LINUX_TIME64_H + +#include + +typedef __s64 time64_t; + +/* + * This wants to go into uapi/linux/time.h once we agreed about the + * userspace interfaces. + */ +#if __BITS_PER_LONG == 64 +# define timespec64 timespec +#else +struct timespec64 { + time64_t tv_sec; /* seconds */ + long tv_nsec; /* nanoseconds */ +}; +#endif + +/* Parameters used to convert the timespec values: */ +#define MSEC_PER_SEC 1000L +#define USEC_PER_MSEC 1000L +#define NSEC_PER_USEC 1000L +#define NSEC_PER_MSEC 1000000L +#define USEC_PER_SEC 1000000L +#define NSEC_PER_SEC 1000000000L +#define FSEC_PER_SEC 1000000000000000LL + +/* Located here for timespec[64]_valid_strict */ +#define KTIME_MAX ((s64)~((u64)1 << 63)) +#define KTIME_SEC_MAX (KTIME_MAX / NSEC_PER_SEC) + +#if __BITS_PER_LONG == 64 + +# define timespec64_equal timespec_equal +# define timespec64_compare timespec_compare +# define set_normalized_timespec64 set_normalized_timespec +# define timespec64_add_safe timespec_add_safe +# define timespec64_add timespec_add +# define timespec64_sub timespec_sub +# define timespec64_valid timespec_valid +# define timespec64_valid_strict timespec_valid_strict +# define timespec64_to_ns timespec_to_ns +# define ns_to_timespec64 ns_to_timespec +# define timespec64_add_ns timespec_add_ns + +#else + +static inline int timespec64_equal(const struct timespec64 *a, + const struct timespec64 *b) +{ + return (a->tv_sec == b->tv_sec) && (a->tv_nsec == b->tv_nsec); +} + +/* + * lhs < rhs: return <0 + * lhs == rhs: return 0 + * lhs > rhs: return >0 + */ +static inline int timespec64_compare(const struct timespec64 *lhs, const struct timespec64 *rhs) +{ + if (lhs->tv_sec < rhs->tv_sec) + return -1; + if (lhs->tv_sec > rhs->tv_sec) + return 1; + return lhs->tv_nsec - rhs->tv_nsec; +} + +extern void set_normalized_timespec64(struct timespec64 *ts, time64_t sec, s64 nsec); + +/* + * timespec64_add_safe assumes both values are positive and checks for + * overflow. It will return TIME_T_MAX if the returned value would be + * smaller then either of the arguments. + */ +extern struct timespec64 timespec64_add_safe(const struct timespec64 lhs, + const struct timespec64 rhs); + + +static inline struct timespec64 timespec64_add(struct timespec64 lhs, + struct timespec64 rhs) +{ + struct timespec64 ts_delta; + set_normalized_timespec64(&ts_delta, lhs.tv_sec + rhs.tv_sec, + lhs.tv_nsec + rhs.tv_nsec); + return ts_delta; +} + +/* + * sub = lhs - rhs, in normalized form + */ +static inline struct timespec64 timespec64_sub(struct timespec64 lhs, + struct timespec64 rhs) +{ + struct timespec64 ts_delta; + set_normalized_timespec64(&ts_delta, lhs.tv_sec - rhs.tv_sec, + lhs.tv_nsec - rhs.tv_nsec); + return ts_delta; +} + +/* + * Returns true if the timespec64 is norm, false if denorm: + */ +static inline bool timespec64_valid(const struct timespec64 *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; + return true; +} + +static inline bool timespec64_valid_strict(const struct timespec64 *ts) +{ + if (!timespec64_valid(ts)) + return false; + /* Disallow values that could overflow ktime_t */ + if ((unsigned long long)ts->tv_sec >= KTIME_SEC_MAX) + return false; + return true; +} + +/** + * timespec64_to_ns - Convert timespec64 to nanoseconds + * @ts: pointer to the timespec64 variable to be converted + * + * Returns the scalar nanosecond representation of the timespec64 + * parameter. + */ +static inline s64 timespec64_to_ns(const struct timespec64 *ts) +{ + return ((s64) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec; +} + +/** + * ns_to_timespec64 - Convert nanoseconds to timespec64 + * @nsec: the nanoseconds value to be converted + * + * Returns the timespec64 representation of the nsec parameter. + */ +extern struct timespec64 ns_to_timespec64(const s64 nsec); + +/** + * timespec64_add_ns - Adds nanoseconds to a timespec64 + * @a: pointer to timespec64 to be incremented + * @ns: unsigned nanoseconds value to be added + * + * This must always be inlined because its used from the x86-64 vdso, + * which cannot call other kernel functions. + */ +static __always_inline void timespec64_add_ns(struct timespec64 *a, u64 ns) +{ + a->tv_sec += __iter_div_u64_rem(a->tv_nsec + ns, NSEC_PER_SEC, &ns); + a->tv_nsec = ns; +} + +#endif + +#endif /* _LINUX_TIME64_H */ -- cgit v1.2.3 From 49cd6f869984692547c57621bf42697aaa7f5622 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Wed, 16 Jul 2014 21:03:59 +0000 Subject: time: More core infrastructure for timespec64 Helper and conversion functions for timespec64. Signed-off-by: John Stultz Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/ktime.h | 28 +++++++++++++++++++++++ include/linux/time64.h | 28 +++++++++++++++++++++++ kernel/time/time.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+) (limited to 'include') diff --git a/include/linux/ktime.h b/include/linux/ktime.h index 538c283714e1..da6b680c252b 100644 --- a/include/linux/ktime.h +++ b/include/linux/ktime.h @@ -83,6 +83,12 @@ static inline ktime_t timespec_to_ktime(struct timespec ts) return ktime_set(ts.tv_sec, ts.tv_nsec); } +/* convert a timespec64 to ktime_t format: */ +static inline ktime_t timespec64_to_ktime(struct timespec64 ts) +{ + return ktime_set(ts.tv_sec, ts.tv_nsec); +} + /* convert a timeval to ktime_t format: */ static inline ktime_t timeval_to_ktime(struct timeval tv) { @@ -92,6 +98,9 @@ static inline ktime_t timeval_to_ktime(struct timeval tv) /* Map the ktime_t to timespec conversion to ns_to_timespec function */ #define ktime_to_timespec(kt) ns_to_timespec((kt).tv64) +/* Map the ktime_t to timespec conversion to ns_to_timespec function */ +#define ktime_to_timespec64(kt) ns_to_timespec64((kt).tv64) + /* Map the ktime_t to timeval conversion to ns_to_timeval function */ #define ktime_to_timeval(kt) ns_to_timeval((kt).tv64) @@ -213,6 +222,25 @@ static inline __must_check bool ktime_to_timespec_cond(const ktime_t kt, } } +/** + * ktime_to_timespec64_cond - convert a ktime_t variable to timespec64 + * format only if the variable contains data + * @kt: the ktime_t variable to convert + * @ts: the timespec variable to store the result in + * + * Return: %true if there was a successful conversion, %false if kt was 0. + */ +static inline __must_check bool ktime_to_timespec64_cond(const ktime_t kt, + struct timespec64 *ts) +{ + if (kt.tv64) { + *ts = ktime_to_timespec64(kt); + return true; + } else { + return false; + } +} + /* * The resolution of the clocks. The resolution value is returned in * the clock_getres() system call to give application programmers an diff --git a/include/linux/time64.h b/include/linux/time64.h index e7b499e1cd79..a3831478d9cf 100644 --- a/include/linux/time64.h +++ b/include/linux/time64.h @@ -33,6 +33,16 @@ struct timespec64 { #if __BITS_PER_LONG == 64 +static inline struct timespec timespec64_to_timespec(const struct timespec64 ts64) +{ + return ts64; +} + +static inline struct timespec64 timespec_to_timespec64(const struct timespec ts) +{ + return ts; +} + # define timespec64_equal timespec_equal # define timespec64_compare timespec_compare # define set_normalized_timespec64 set_normalized_timespec @@ -47,6 +57,24 @@ struct timespec64 { #else +static inline struct timespec timespec64_to_timespec(const struct timespec64 ts64) +{ + struct timespec ret; + + ret.tv_sec = (time_t)ts64.tv_sec; + ret.tv_nsec = ts64.tv_nsec; + return ret; +} + +static inline struct timespec64 timespec_to_timespec64(const struct timespec ts) +{ + struct timespec64 ret; + + ret.tv_sec = ts.tv_sec; + ret.tv_nsec = ts.tv_nsec; + return ret; +} + static inline int timespec64_equal(const struct timespec64 *a, const struct timespec64 *b) { diff --git a/kernel/time/time.c b/kernel/time/time.c index 7c7964c33ae7..e8121a67fd74 100644 --- a/kernel/time/time.c +++ b/kernel/time/time.c @@ -420,6 +420,68 @@ struct timeval ns_to_timeval(const s64 nsec) } EXPORT_SYMBOL(ns_to_timeval); +#if BITS_PER_LONG == 32 +/** + * set_normalized_timespec - set timespec sec and nsec parts and normalize + * + * @ts: pointer to timespec variable to be set + * @sec: seconds to set + * @nsec: nanoseconds to set + * + * Set seconds and nanoseconds field of a timespec variable and + * normalize to the timespec storage format + * + * Note: The tv_nsec part is always in the range of + * 0 <= tv_nsec < NSEC_PER_SEC + * For negative values only the tv_sec field is negative ! + */ +void set_normalized_timespec64(struct timespec64 *ts, time64_t sec, s64 nsec) +{ + while (nsec >= NSEC_PER_SEC) { + /* + * The following asm() prevents the compiler from + * optimising this loop into a modulo operation. See + * also __iter_div_u64_rem() in include/linux/time.h + */ + asm("" : "+rm"(nsec)); + nsec -= NSEC_PER_SEC; + ++sec; + } + while (nsec < 0) { + asm("" : "+rm"(nsec)); + nsec += NSEC_PER_SEC; + --sec; + } + ts->tv_sec = sec; + ts->tv_nsec = nsec; +} +EXPORT_SYMBOL(set_normalized_timespec64); + +/** + * ns_to_timespec64 - Convert nanoseconds to timespec64 + * @nsec: the nanoseconds value to be converted + * + * Returns the timespec64 representation of the nsec parameter. + */ +struct timespec64 ns_to_timespec64(const s64 nsec) +{ + struct timespec64 ts; + s32 rem; + + if (!nsec) + return (struct timespec64) {0, 0}; + + ts.tv_sec = div_s64_rem(nsec, NSEC_PER_SEC, &rem); + if (unlikely(rem < 0)) { + ts.tv_sec--; + rem += NSEC_PER_SEC; + } + ts.tv_nsec = rem; + + return ts; +} +EXPORT_SYMBOL(ns_to_timespec64); +#endif /* * When we convert to jiffies then we interpret incoming values * the following way: -- cgit v1.2.3 From 7d489d15ce4be5310ca60e5896df833f9b3b4088 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Wed, 16 Jul 2014 21:04:01 +0000 Subject: timekeeping: Convert timekeeping core to use timespec64s Convert the core timekeeping logic to use timespec64s. This moves the 2038 issues out of the core logic and into all of the accessor functions. Future changes will need to push the timespec64s out to all timekeeping users, but that can be done interface by interface. Signed-off-by: John Stultz Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/timekeeper_internal.h | 10 +-- kernel/time/ntp.c | 8 +- kernel/time/ntp_internal.h | 2 +- kernel/time/timekeeping.c | 172 ++++++++++++++++++++---------------- kernel/time/timekeeping_debug.c | 2 +- kernel/time/timekeeping_internal.h | 2 +- 6 files changed, 109 insertions(+), 87 deletions(-) (limited to 'include') diff --git a/include/linux/timekeeper_internal.h b/include/linux/timekeeper_internal.h index c1825eb436ed..1b05491e10f9 100644 --- a/include/linux/timekeeper_internal.h +++ b/include/linux/timekeeper_internal.h @@ -55,15 +55,15 @@ struct timekeeper { * - wall_to_monotonic is no longer the boot time, getboottime must be * used instead. */ - struct timespec wall_to_monotonic; + struct timespec64 wall_to_monotonic; /* Offset clock monotonic -> clock realtime */ ktime_t offs_real; /* time spent in suspend */ - struct timespec total_sleep_time; + struct timespec64 total_sleep_time; /* Offset clock monotonic -> clock boottime */ ktime_t offs_boot; /* The raw monotonic time for the CLOCK_MONOTONIC_RAW posix clock. */ - struct timespec raw_time; + struct timespec64 raw_time; /* The current UTC to TAI offset in seconds */ s32 tai_offset; /* Offset clock monotonic -> clock tai */ @@ -71,9 +71,9 @@ struct timekeeper { }; -static inline struct timespec tk_xtime(struct timekeeper *tk) +static inline struct timespec64 tk_xtime(struct timekeeper *tk) { - struct timespec ts; + struct timespec64 ts; ts.tv_sec = tk->xtime_sec; ts.tv_nsec = (long)(tk->xtime_nsec >> tk->shift); diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c index 33db43a39515..6e87df94122f 100644 --- a/kernel/time/ntp.c +++ b/kernel/time/ntp.c @@ -531,7 +531,7 @@ void ntp_notify_cmos_timer(void) { } /* * Propagate a new txc->status value into the NTP state: */ -static inline void process_adj_status(struct timex *txc, struct timespec *ts) +static inline void process_adj_status(struct timex *txc, struct timespec64 *ts) { if ((time_status & STA_PLL) && !(txc->status & STA_PLL)) { time_state = TIME_OK; @@ -554,7 +554,7 @@ static inline void process_adj_status(struct timex *txc, struct timespec *ts) static inline void process_adjtimex_modes(struct timex *txc, - struct timespec *ts, + struct timespec64 *ts, s32 *time_tai) { if (txc->modes & ADJ_STATUS) @@ -640,7 +640,7 @@ int ntp_validate_timex(struct timex *txc) * adjtimex mainly allows reading (and writing, if superuser) of * kernel time-keeping variables. used by xntpd. */ -int __do_adjtimex(struct timex *txc, struct timespec *ts, s32 *time_tai) +int __do_adjtimex(struct timex *txc, struct timespec64 *ts, s32 *time_tai) { int result; @@ -684,7 +684,7 @@ int __do_adjtimex(struct timex *txc, struct timespec *ts, s32 *time_tai) /* fill PPS status fields */ pps_fill_timex(txc); - txc->time.tv_sec = ts->tv_sec; + txc->time.tv_sec = (time_t)ts->tv_sec; txc->time.tv_usec = ts->tv_nsec; if (!(time_status & STA_NANO)) txc->time.tv_usec /= NSEC_PER_USEC; diff --git a/kernel/time/ntp_internal.h b/kernel/time/ntp_internal.h index 1950cb4ca2a4..bbd102ad9df7 100644 --- a/kernel/time/ntp_internal.h +++ b/kernel/time/ntp_internal.h @@ -7,6 +7,6 @@ extern void ntp_clear(void); extern u64 ntp_tick_length(void); extern int second_overflow(unsigned long secs); extern int ntp_validate_timex(struct timex *); -extern int __do_adjtimex(struct timex *, struct timespec *, s32 *); +extern int __do_adjtimex(struct timex *, struct timespec64 *, s32 *); extern void __hardpps(const struct timespec *, const struct timespec *); #endif /* _LINUX_NTP_INTERNAL_H */ diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index cafef242d8f9..84a2075c3eb4 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -51,43 +51,43 @@ static inline void tk_normalize_xtime(struct timekeeper *tk) } } -static void tk_set_xtime(struct timekeeper *tk, const struct timespec *ts) +static void tk_set_xtime(struct timekeeper *tk, const struct timespec64 *ts) { tk->xtime_sec = ts->tv_sec; tk->xtime_nsec = (u64)ts->tv_nsec << tk->shift; } -static void tk_xtime_add(struct timekeeper *tk, const struct timespec *ts) +static void tk_xtime_add(struct timekeeper *tk, const struct timespec64 *ts) { tk->xtime_sec += ts->tv_sec; tk->xtime_nsec += (u64)ts->tv_nsec << tk->shift; tk_normalize_xtime(tk); } -static void tk_set_wall_to_mono(struct timekeeper *tk, struct timespec wtm) +static void tk_set_wall_to_mono(struct timekeeper *tk, struct timespec64 wtm) { - struct timespec tmp; + struct timespec64 tmp; /* * Verify consistency of: offset_real = -wall_to_monotonic * before modifying anything */ - set_normalized_timespec(&tmp, -tk->wall_to_monotonic.tv_sec, + set_normalized_timespec64(&tmp, -tk->wall_to_monotonic.tv_sec, -tk->wall_to_monotonic.tv_nsec); - WARN_ON_ONCE(tk->offs_real.tv64 != timespec_to_ktime(tmp).tv64); + WARN_ON_ONCE(tk->offs_real.tv64 != timespec64_to_ktime(tmp).tv64); tk->wall_to_monotonic = wtm; - set_normalized_timespec(&tmp, -wtm.tv_sec, -wtm.tv_nsec); - tk->offs_real = timespec_to_ktime(tmp); + set_normalized_timespec64(&tmp, -wtm.tv_sec, -wtm.tv_nsec); + tk->offs_real = timespec64_to_ktime(tmp); tk->offs_tai = ktime_add(tk->offs_real, ktime_set(tk->tai_offset, 0)); } -static void tk_set_sleep_time(struct timekeeper *tk, struct timespec t) +static void tk_set_sleep_time(struct timekeeper *tk, struct timespec64 t) { /* Verify consistency before modifying */ - WARN_ON_ONCE(tk->offs_boot.tv64 != timespec_to_ktime(tk->total_sleep_time).tv64); + WARN_ON_ONCE(tk->offs_boot.tv64 != timespec64_to_ktime(tk->total_sleep_time).tv64); tk->total_sleep_time = t; - tk->offs_boot = timespec_to_ktime(t); + tk->offs_boot = timespec64_to_ktime(t); } /** @@ -281,7 +281,7 @@ static void timekeeping_forward_now(struct timekeeper *tk) tk_normalize_xtime(tk); nsec = clocksource_cyc2ns(cycle_delta, clock->mult, clock->shift); - timespec_add_ns(&tk->raw_time, nsec); + timespec64_add_ns(&tk->raw_time, nsec); } /** @@ -360,7 +360,7 @@ EXPORT_SYMBOL_GPL(ktime_get); void ktime_get_ts(struct timespec *ts) { struct timekeeper *tk = &timekeeper; - struct timespec tomono; + struct timespec64 ts64, tomono; s64 nsec; unsigned int seq; @@ -368,15 +368,16 @@ void ktime_get_ts(struct timespec *ts) do { seq = read_seqcount_begin(&timekeeper_seq); - ts->tv_sec = tk->xtime_sec; + ts64.tv_sec = tk->xtime_sec; nsec = timekeeping_get_ns(tk); tomono = tk->wall_to_monotonic; } while (read_seqcount_retry(&timekeeper_seq, seq)); - ts->tv_sec += tomono.tv_sec; - ts->tv_nsec = 0; - timespec_add_ns(ts, nsec + tomono.tv_nsec); + ts64.tv_sec += tomono.tv_sec; + ts64.tv_nsec = 0; + timespec64_add_ns(&ts64, nsec + tomono.tv_nsec); + *ts = timespec64_to_timespec(ts64); } EXPORT_SYMBOL_GPL(ktime_get_ts); @@ -390,6 +391,7 @@ EXPORT_SYMBOL_GPL(ktime_get_ts); void timekeeping_clocktai(struct timespec *ts) { struct timekeeper *tk = &timekeeper; + struct timespec64 ts64; unsigned long seq; u64 nsecs; @@ -398,13 +400,14 @@ void timekeeping_clocktai(struct timespec *ts) do { seq = read_seqcount_begin(&timekeeper_seq); - ts->tv_sec = tk->xtime_sec + tk->tai_offset; + ts64.tv_sec = tk->xtime_sec + tk->tai_offset; nsecs = timekeeping_get_ns(tk); } while (read_seqcount_retry(&timekeeper_seq, seq)); - ts->tv_nsec = 0; - timespec_add_ns(ts, nsecs); + ts64.tv_nsec = 0; + timespec64_add_ns(&ts64, nsecs); + *ts = timespec64_to_timespec(ts64); } EXPORT_SYMBOL(timekeeping_clocktai); @@ -446,7 +449,7 @@ void getnstime_raw_and_real(struct timespec *ts_raw, struct timespec *ts_real) do { seq = read_seqcount_begin(&timekeeper_seq); - *ts_raw = tk->raw_time; + *ts_raw = timespec64_to_timespec(tk->raw_time); ts_real->tv_sec = tk->xtime_sec; ts_real->tv_nsec = 0; @@ -487,7 +490,7 @@ EXPORT_SYMBOL(do_gettimeofday); int do_settimeofday(const struct timespec *tv) { struct timekeeper *tk = &timekeeper; - struct timespec ts_delta, xt; + struct timespec64 ts_delta, xt, tmp; unsigned long flags; if (!timespec_valid_strict(tv)) @@ -502,9 +505,10 @@ int do_settimeofday(const struct timespec *tv) ts_delta.tv_sec = tv->tv_sec - xt.tv_sec; ts_delta.tv_nsec = tv->tv_nsec - xt.tv_nsec; - tk_set_wall_to_mono(tk, timespec_sub(tk->wall_to_monotonic, ts_delta)); + tk_set_wall_to_mono(tk, timespec64_sub(tk->wall_to_monotonic, ts_delta)); - tk_set_xtime(tk, tv); + tmp = timespec_to_timespec64(*tv); + tk_set_xtime(tk, &tmp); timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR | TK_CLOCK_WAS_SET); @@ -528,26 +532,28 @@ int timekeeping_inject_offset(struct timespec *ts) { struct timekeeper *tk = &timekeeper; unsigned long flags; - struct timespec tmp; + struct timespec64 ts64, tmp; int ret = 0; if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC) return -EINVAL; + ts64 = timespec_to_timespec64(*ts); + raw_spin_lock_irqsave(&timekeeper_lock, flags); write_seqcount_begin(&timekeeper_seq); timekeeping_forward_now(tk); /* Make sure the proposed value is valid */ - tmp = timespec_add(tk_xtime(tk), *ts); - if (!timespec_valid_strict(&tmp)) { + tmp = timespec64_add(tk_xtime(tk), ts64); + if (!timespec64_valid_strict(&tmp)) { ret = -EINVAL; goto error; } - tk_xtime_add(tk, ts); - tk_set_wall_to_mono(tk, timespec_sub(tk->wall_to_monotonic, *ts)); + tk_xtime_add(tk, &ts64); + tk_set_wall_to_mono(tk, timespec64_sub(tk->wall_to_monotonic, ts64)); error: /* even if we error out, we forwarded the time, so call update */ timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR | TK_CLOCK_WAS_SET); @@ -691,17 +697,19 @@ EXPORT_SYMBOL_GPL(ktime_get_real); void getrawmonotonic(struct timespec *ts) { struct timekeeper *tk = &timekeeper; + struct timespec64 ts64; unsigned long seq; s64 nsecs; do { seq = read_seqcount_begin(&timekeeper_seq); nsecs = timekeeping_get_ns_raw(tk); - *ts = tk->raw_time; + ts64 = tk->raw_time; } while (read_seqcount_retry(&timekeeper_seq, seq)); - timespec_add_ns(ts, nsecs); + timespec64_add_ns(&ts64, nsecs); + *ts = timespec64_to_timespec(ts64); } EXPORT_SYMBOL(getrawmonotonic); @@ -781,11 +789,12 @@ void __init timekeeping_init(void) struct timekeeper *tk = &timekeeper; struct clocksource *clock; unsigned long flags; - struct timespec now, boot, tmp; - - read_persistent_clock(&now); + struct timespec64 now, boot, tmp; + struct timespec ts; - if (!timespec_valid_strict(&now)) { + read_persistent_clock(&ts); + now = timespec_to_timespec64(ts); + if (!timespec64_valid_strict(&now)) { pr_warn("WARNING: Persistent clock returned invalid value!\n" " Check your CMOS/BIOS settings.\n"); now.tv_sec = 0; @@ -793,8 +802,9 @@ void __init timekeeping_init(void) } else if (now.tv_sec || now.tv_nsec) persistent_clock_exist = true; - read_boot_clock(&boot); - if (!timespec_valid_strict(&boot)) { + read_boot_clock(&ts); + boot = timespec_to_timespec64(ts); + if (!timespec64_valid_strict(&boot)) { pr_warn("WARNING: Boot clock returned invalid value!\n" " Check your CMOS/BIOS settings.\n"); boot.tv_sec = 0; @@ -816,7 +826,7 @@ void __init timekeeping_init(void) if (boot.tv_sec == 0 && boot.tv_nsec == 0) boot = tk_xtime(tk); - set_normalized_timespec(&tmp, -boot.tv_sec, -boot.tv_nsec); + set_normalized_timespec64(&tmp, -boot.tv_sec, -boot.tv_nsec); tk_set_wall_to_mono(tk, tmp); tmp.tv_sec = 0; @@ -830,7 +840,7 @@ void __init timekeeping_init(void) } /* time in seconds when suspend began */ -static struct timespec timekeeping_suspend_time; +static struct timespec64 timekeeping_suspend_time; /** * __timekeeping_inject_sleeptime - Internal function to add sleep interval @@ -840,17 +850,17 @@ static struct timespec timekeeping_suspend_time; * adds the sleep offset to the timekeeping variables. */ static void __timekeeping_inject_sleeptime(struct timekeeper *tk, - struct timespec *delta) + struct timespec64 *delta) { - if (!timespec_valid_strict(delta)) { + if (!timespec64_valid_strict(delta)) { printk_deferred(KERN_WARNING "__timekeeping_inject_sleeptime: Invalid " "sleep delta value!\n"); return; } tk_xtime_add(tk, delta); - tk_set_wall_to_mono(tk, timespec_sub(tk->wall_to_monotonic, *delta)); - tk_set_sleep_time(tk, timespec_add(tk->total_sleep_time, *delta)); + tk_set_wall_to_mono(tk, timespec64_sub(tk->wall_to_monotonic, *delta)); + tk_set_sleep_time(tk, timespec64_add(tk->total_sleep_time, *delta)); tk_debug_account_sleep_time(delta); } @@ -867,6 +877,7 @@ static void __timekeeping_inject_sleeptime(struct timekeeper *tk, void timekeeping_inject_sleeptime(struct timespec *delta) { struct timekeeper *tk = &timekeeper; + struct timespec64 tmp; unsigned long flags; /* @@ -881,7 +892,8 @@ void timekeeping_inject_sleeptime(struct timespec *delta) timekeeping_forward_now(tk); - __timekeeping_inject_sleeptime(tk, delta); + tmp = timespec_to_timespec64(*delta); + __timekeeping_inject_sleeptime(tk, &tmp); timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR | TK_CLOCK_WAS_SET); @@ -904,11 +916,13 @@ static void timekeeping_resume(void) struct timekeeper *tk = &timekeeper; struct clocksource *clock = tk->clock; unsigned long flags; - struct timespec ts_new, ts_delta; + struct timespec64 ts_new, ts_delta; + struct timespec tmp; cycle_t cycle_now, cycle_delta; bool suspendtime_found = false; - read_persistent_clock(&ts_new); + read_persistent_clock(&tmp); + ts_new = timespec_to_timespec64(tmp); clockevents_resume(); clocksource_resume(); @@ -951,10 +965,10 @@ static void timekeeping_resume(void) } nsec += ((u64) cycle_delta * mult) >> shift; - ts_delta = ns_to_timespec(nsec); + ts_delta = ns_to_timespec64(nsec); suspendtime_found = true; - } else if (timespec_compare(&ts_new, &timekeeping_suspend_time) > 0) { - ts_delta = timespec_sub(ts_new, timekeeping_suspend_time); + } else if (timespec64_compare(&ts_new, &timekeeping_suspend_time) > 0) { + ts_delta = timespec64_sub(ts_new, timekeeping_suspend_time); suspendtime_found = true; } @@ -981,10 +995,12 @@ static int timekeeping_suspend(void) { struct timekeeper *tk = &timekeeper; unsigned long flags; - struct timespec delta, delta_delta; - static struct timespec old_delta; + struct timespec64 delta, delta_delta; + static struct timespec64 old_delta; + struct timespec tmp; - read_persistent_clock(&timekeeping_suspend_time); + read_persistent_clock(&tmp); + timekeeping_suspend_time = timespec_to_timespec64(tmp); /* * On some systems the persistent_clock can not be detected at @@ -1005,8 +1021,8 @@ static int timekeeping_suspend(void) * try to compensate so the difference in system time * and persistent_clock time stays close to constant. */ - delta = timespec_sub(tk_xtime(tk), timekeeping_suspend_time); - delta_delta = timespec_sub(delta, old_delta); + delta = timespec64_sub(tk_xtime(tk), timekeeping_suspend_time); + delta_delta = timespec64_sub(delta, old_delta); if (abs(delta_delta.tv_sec) >= 2) { /* * if delta_delta is too large, assume time correction @@ -1016,7 +1032,7 @@ static int timekeeping_suspend(void) } else { /* Otherwise try to adjust old_system to compensate */ timekeeping_suspend_time = - timespec_add(timekeeping_suspend_time, delta_delta); + timespec64_add(timekeeping_suspend_time, delta_delta); } timekeeping_update(tk, TK_MIRROR); @@ -1253,14 +1269,14 @@ static inline unsigned int accumulate_nsecs_to_secs(struct timekeeper *tk) /* Figure out if its a leap sec and apply if needed */ leap = second_overflow(tk->xtime_sec); if (unlikely(leap)) { - struct timespec ts; + struct timespec64 ts; tk->xtime_sec += leap; ts.tv_sec = leap; ts.tv_nsec = 0; tk_set_wall_to_mono(tk, - timespec_sub(tk->wall_to_monotonic, ts)); + timespec64_sub(tk->wall_to_monotonic, ts)); __timekeeping_set_tai_offset(tk, tk->tai_offset - leap); @@ -1469,7 +1485,7 @@ EXPORT_SYMBOL_GPL(getboottime); void get_monotonic_boottime(struct timespec *ts) { struct timekeeper *tk = &timekeeper; - struct timespec tomono, sleep; + struct timespec64 tomono, sleep, ret; s64 nsec; unsigned int seq; @@ -1477,16 +1493,17 @@ void get_monotonic_boottime(struct timespec *ts) do { seq = read_seqcount_begin(&timekeeper_seq); - ts->tv_sec = tk->xtime_sec; + ret.tv_sec = tk->xtime_sec; nsec = timekeeping_get_ns(tk); tomono = tk->wall_to_monotonic; sleep = tk->total_sleep_time; } while (read_seqcount_retry(&timekeeper_seq, seq)); - ts->tv_sec += tomono.tv_sec + sleep.tv_sec; - ts->tv_nsec = 0; - timespec_add_ns(ts, nsec + tomono.tv_nsec + sleep.tv_nsec); + ret.tv_sec += tomono.tv_sec + sleep.tv_sec; + ret.tv_nsec = 0; + timespec64_add_ns(&ret, nsec + tomono.tv_nsec + sleep.tv_nsec); + *ts = timespec64_to_timespec(ret); } EXPORT_SYMBOL_GPL(get_monotonic_boottime); @@ -1514,8 +1531,11 @@ EXPORT_SYMBOL_GPL(ktime_get_boottime); void monotonic_to_bootbased(struct timespec *ts) { struct timekeeper *tk = &timekeeper; + struct timespec64 ts64; - *ts = timespec_add(*ts, tk->total_sleep_time); + ts64 = timespec_to_timespec64(*ts); + ts64 = timespec64_add(ts64, tk->total_sleep_time); + *ts = timespec64_to_timespec(ts64); } EXPORT_SYMBOL_GPL(monotonic_to_bootbased); @@ -1531,13 +1551,13 @@ struct timespec __current_kernel_time(void) { struct timekeeper *tk = &timekeeper; - return tk_xtime(tk); + return timespec64_to_timespec(tk_xtime(tk)); } struct timespec current_kernel_time(void) { struct timekeeper *tk = &timekeeper; - struct timespec now; + struct timespec64 now; unsigned long seq; do { @@ -1546,14 +1566,14 @@ struct timespec current_kernel_time(void) now = tk_xtime(tk); } while (read_seqcount_retry(&timekeeper_seq, seq)); - return now; + return timespec64_to_timespec(now); } EXPORT_SYMBOL(current_kernel_time); struct timespec get_monotonic_coarse(void) { struct timekeeper *tk = &timekeeper; - struct timespec now, mono; + struct timespec64 now, mono; unsigned long seq; do { @@ -1563,9 +1583,10 @@ struct timespec get_monotonic_coarse(void) mono = tk->wall_to_monotonic; } while (read_seqcount_retry(&timekeeper_seq, seq)); - set_normalized_timespec(&now, now.tv_sec + mono.tv_sec, + set_normalized_timespec64(&now, now.tv_sec + mono.tv_sec, now.tv_nsec + mono.tv_nsec); - return now; + + return timespec64_to_timespec(now); } /* @@ -1589,7 +1610,7 @@ ktime_t ktime_get_update_offsets_tick(ktime_t *offs_real, ktime_t *offs_boot, ktime_t *offs_tai) { struct timekeeper *tk = &timekeeper; - struct timespec ts; + struct timespec64 ts; ktime_t now; unsigned int seq; @@ -1597,7 +1618,6 @@ ktime_t ktime_get_update_offsets_tick(ktime_t *offs_real, ktime_t *offs_boot, seq = read_seqcount_begin(&timekeeper_seq); ts = tk_xtime(tk); - *offs_real = tk->offs_real; *offs_boot = tk->offs_boot; *offs_tai = tk->offs_tai; @@ -1650,14 +1670,14 @@ ktime_t ktime_get_monotonic_offset(void) { struct timekeeper *tk = &timekeeper; unsigned long seq; - struct timespec wtom; + struct timespec64 wtom; do { seq = read_seqcount_begin(&timekeeper_seq); wtom = tk->wall_to_monotonic; } while (read_seqcount_retry(&timekeeper_seq, seq)); - return timespec_to_ktime(wtom); + return timespec64_to_ktime(wtom); } EXPORT_SYMBOL_GPL(ktime_get_monotonic_offset); @@ -1668,7 +1688,8 @@ int do_adjtimex(struct timex *txc) { struct timekeeper *tk = &timekeeper; unsigned long flags; - struct timespec ts; + struct timespec64 ts; + struct timespec tmp; s32 orig_tai, tai; int ret; @@ -1688,7 +1709,8 @@ int do_adjtimex(struct timex *txc) return ret; } - getnstimeofday(&ts); + getnstimeofday(&tmp); + ts = timespec_to_timespec64(tmp); raw_spin_lock_irqsave(&timekeeper_lock, flags); write_seqcount_begin(&timekeeper_seq); diff --git a/kernel/time/timekeeping_debug.c b/kernel/time/timekeeping_debug.c index 4d54f97558df..f6bd65236712 100644 --- a/kernel/time/timekeeping_debug.c +++ b/kernel/time/timekeeping_debug.c @@ -67,7 +67,7 @@ static int __init tk_debug_sleep_time_init(void) } late_initcall(tk_debug_sleep_time_init); -void tk_debug_account_sleep_time(struct timespec *t) +void tk_debug_account_sleep_time(struct timespec64 *t) { sleep_time_bin[fls(t->tv_sec)]++; } diff --git a/kernel/time/timekeeping_internal.h b/kernel/time/timekeeping_internal.h index 13323ea08ffa..e3d28ad236f9 100644 --- a/kernel/time/timekeeping_internal.h +++ b/kernel/time/timekeeping_internal.h @@ -6,7 +6,7 @@ #include #ifdef CONFIG_DEBUG_FS -extern void tk_debug_account_sleep_time(struct timespec *t); +extern void tk_debug_account_sleep_time(struct timespec64 *t); #else #define tk_debug_account_sleep_time(x) #endif -- cgit v1.2.3 From 8b094cd03b4a3793220d8d8d86a173bfea8c285b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jul 2014 21:04:02 +0000 Subject: time: Consolidate the time accessor prototypes Right now we have time related prototypes in 3 different header files. Move it to a single timekeeping header file and move the core internal stuff into a core private header. Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/hrtimer.h | 11 ------- include/linux/ktime.h | 8 ++--- include/linux/time.h | 45 +++----------------------- include/linux/timekeeping.h | 78 +++++++++++++++++++++++++++++++++++++++++++++ kernel/time/hrtimer.c | 2 ++ kernel/time/posix-timers.c | 2 ++ kernel/time/tick-internal.h | 2 ++ kernel/time/time.c | 1 + kernel/time/timekeeping.h | 20 ++++++++++++ 9 files changed, 111 insertions(+), 58 deletions(-) create mode 100644 include/linux/timekeeping.h create mode 100644 kernel/time/timekeeping.h (limited to 'include') diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index adf5056bd7b3..a036d058a249 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -326,17 +326,6 @@ static inline void timerfd_clock_was_set(void) { } #endif extern void hrtimers_resume(void); -extern ktime_t ktime_get(void); -extern ktime_t ktime_get_real(void); -extern ktime_t ktime_get_boottime(void); -extern ktime_t ktime_get_monotonic_offset(void); -extern ktime_t ktime_get_clocktai(void); -extern ktime_t ktime_get_update_offsets_tick(ktime_t *offs_real, - ktime_t *offs_boot, - ktime_t *offs_tai); -extern ktime_t ktime_get_update_offsets_now(ktime_t *offs_real, - ktime_t *offs_boot, - ktime_t *offs_tai); DECLARE_PER_CPU(struct tick_device, tick_cpu_device); diff --git a/include/linux/ktime.h b/include/linux/ktime.h index da6b680c252b..c9d645ad98ff 100644 --- a/include/linux/ktime.h +++ b/include/linux/ktime.h @@ -250,12 +250,6 @@ static inline __must_check bool ktime_to_timespec64_cond(const ktime_t kt, #define LOW_RES_NSEC TICK_NSEC #define KTIME_LOW_RES (ktime_t){ .tv64 = LOW_RES_NSEC } -/* Get the monotonic time in timespec format: */ -extern void ktime_get_ts(struct timespec *ts); - -/* Get the real (wall-) time in timespec format: */ -#define ktime_get_real_ts(ts) getnstimeofday(ts) - static inline ktime_t ns_to_ktime(u64 ns) { static const ktime_t ktime_zero = { .tv64 = 0 }; @@ -270,4 +264,6 @@ static inline ktime_t ms_to_ktime(u64 ms) return ktime_add_ms(ktime_zero, ms); } +# include + #endif diff --git a/include/linux/time.h b/include/linux/time.h index 234feac7f1c3..8c42cf8d2444 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -99,25 +99,7 @@ static inline bool timespec_valid_strict(const struct timespec *ts) return true; } -extern bool persistent_clock_exist; - -static inline bool has_persistent_clock(void) -{ - return persistent_clock_exist; -} - -extern void read_persistent_clock(struct timespec *ts); -extern void read_boot_clock(struct timespec *ts); -extern int persistent_clock_is_local; -extern int update_persistent_clock(struct timespec now); -void timekeeping_init(void); -extern int timekeeping_suspended; - -unsigned long get_seconds(void); -struct timespec current_kernel_time(void); -struct timespec __current_kernel_time(void); /* does not take xtime_lock */ -struct timespec get_monotonic_coarse(void); -void timekeeping_inject_sleeptime(struct timespec *delta); +extern struct timespec timespec_trunc(struct timespec t, unsigned gran); #define CURRENT_TIME (current_kernel_time()) #define CURRENT_TIME_SEC ((struct timespec) { get_seconds(), 0 }) @@ -135,33 +117,14 @@ void timekeeping_inject_sleeptime(struct timespec *delta); extern u32 (*arch_gettimeoffset)(void); #endif -extern void do_gettimeofday(struct timeval *tv); -extern int do_settimeofday(const struct timespec *tv); -extern int do_sys_settimeofday(const struct timespec *tv, - const struct timezone *tz); -#define do_posix_clock_monotonic_gettime(ts) ktime_get_ts(ts) -extern long do_utimes(int dfd, const char __user *filename, struct timespec *times, int flags); struct itimerval; extern int do_setitimer(int which, struct itimerval *value, struct itimerval *ovalue); -extern unsigned int alarm_setitimer(unsigned int seconds); extern int do_getitimer(int which, struct itimerval *value); -extern int __getnstimeofday(struct timespec *tv); -extern void getnstimeofday(struct timespec *tv); -extern void getrawmonotonic(struct timespec *ts); -extern void getnstime_raw_and_real(struct timespec *ts_raw, - struct timespec *ts_real); -extern void getboottime(struct timespec *ts); -extern void monotonic_to_bootbased(struct timespec *ts); -extern void get_monotonic_boottime(struct timespec *ts); -extern struct timespec timespec_trunc(struct timespec t, unsigned gran); -extern int timekeeping_valid_for_hres(void); -extern u64 timekeeping_max_deferment(void); -extern int timekeeping_inject_offset(struct timespec *ts); -extern s32 timekeeping_get_tai_offset(void); -extern void timekeeping_set_tai_offset(s32 tai_offset); -extern void timekeeping_clocktai(struct timespec *ts); +extern unsigned int alarm_setitimer(unsigned int seconds); + +extern long do_utimes(int dfd, const char __user *filename, struct timespec *times, int flags); struct tms; extern void do_sys_times(struct tms *); diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h new file mode 100644 index 000000000000..e81c35b71da7 --- /dev/null +++ b/include/linux/timekeeping.h @@ -0,0 +1,78 @@ +#ifndef _LINUX_TIMEKEEPING_H +#define _LINUX_TIMEKEEPING_H + +/* Included from linux/ktime.h */ + +void timekeeping_init(void); +extern int timekeeping_suspended; + +/* + * Get and set timeofday + */ +extern void do_gettimeofday(struct timeval *tv); +extern int do_settimeofday(const struct timespec *tv); +extern int do_sys_settimeofday(const struct timespec *tv, + const struct timezone *tz); + +/* + * Kernel time accessors + */ +unsigned long get_seconds(void); +struct timespec current_kernel_time(void); +/* does not take xtime_lock */ +struct timespec __current_kernel_time(void); + +/* + * timespec based interfaces + */ +struct timespec get_monotonic_coarse(void); +extern void getrawmonotonic(struct timespec *ts); +extern void monotonic_to_bootbased(struct timespec *ts); +extern void get_monotonic_boottime(struct timespec *ts); +extern void ktime_get_ts(struct timespec *ts); + +extern int __getnstimeofday(struct timespec *tv); +extern void getnstimeofday(struct timespec *tv); +extern void getboottime(struct timespec *ts); + +#define do_posix_clock_monotonic_gettime(ts) ktime_get_ts(ts) +#define ktime_get_real_ts(ts) getnstimeofday(ts) + + +/* + * ktime_t based interfaces + */ +extern ktime_t ktime_get(void); +extern ktime_t ktime_get_real(void); +extern ktime_t ktime_get_boottime(void); +extern ktime_t ktime_get_monotonic_offset(void); +extern ktime_t ktime_get_clocktai(void); + +/* + * RTC specific + */ +extern void timekeeping_inject_sleeptime(struct timespec *delta); + +/* + * PPS accessor + */ +extern void getnstime_raw_and_real(struct timespec *ts_raw, + struct timespec *ts_real); + +/* + * Persistent clock related interfaces + */ +extern bool persistent_clock_exist; +extern int persistent_clock_is_local; + +static inline bool has_persistent_clock(void) +{ + return persistent_clock_exist; +} + +extern void read_persistent_clock(struct timespec *ts); +extern void read_boot_clock(struct timespec *ts); +extern int update_persistent_clock(struct timespec now); + + +#endif diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c index 64843a836637..1c2fe7de2842 100644 --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c @@ -54,6 +54,8 @@ #include +#include "timekeeping.h" + /* * The timer bases: * diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c index 424c2d4265c9..42b463ad90f2 100644 --- a/kernel/time/posix-timers.c +++ b/kernel/time/posix-timers.c @@ -49,6 +49,8 @@ #include #include +#include "timekeeping.h" + /* * Management arrays for POSIX timers. Timers are now kept in static hash table * with 512 entries. diff --git a/kernel/time/tick-internal.h b/kernel/time/tick-internal.h index 7ab92b19965a..c19c1d84b6f3 100644 --- a/kernel/time/tick-internal.h +++ b/kernel/time/tick-internal.h @@ -4,6 +4,8 @@ #include #include +#include "timekeeping.h" + extern seqlock_t jiffies_lock; #define CS_NAME_LEN 32 diff --git a/kernel/time/time.c b/kernel/time/time.c index e8121a67fd74..278c63cc8054 100644 --- a/kernel/time/time.c +++ b/kernel/time/time.c @@ -42,6 +42,7 @@ #include #include "timeconst.h" +#include "timekeeping.h" /* * The timezone where the local system is located. Used as a default by some diff --git a/kernel/time/timekeeping.h b/kernel/time/timekeeping.h new file mode 100644 index 000000000000..adc1fc98bde3 --- /dev/null +++ b/kernel/time/timekeeping.h @@ -0,0 +1,20 @@ +#ifndef _KERNEL_TIME_TIMEKEEPING_H +#define _KERNEL_TIME_TIMEKEEPING_H +/* + * Internal interfaces for kernel/time/ + */ +extern ktime_t ktime_get_update_offsets_tick(ktime_t *offs_real, + ktime_t *offs_boot, + ktime_t *offs_tai); +extern ktime_t ktime_get_update_offsets_now(ktime_t *offs_real, + ktime_t *offs_boot, + ktime_t *offs_tai); + +extern int timekeeping_valid_for_hres(void); +extern u64 timekeeping_max_deferment(void); +extern int timekeeping_inject_offset(struct timespec *ts); +extern s32 timekeeping_get_tai_offset(void); +extern void timekeeping_set_tai_offset(s32 tai_offset); +extern void timekeeping_clocktai(struct timespec *ts); + +#endif -- cgit v1.2.3 From d6d29896c665dfd50e6e0be7a9039901640433a3 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jul 2014 21:04:04 +0000 Subject: timekeeping: Provide timespec64 based interfaces To convert callers of the core code to timespec64 we need to provide the proper interfaces. Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/timekeeping.h | 66 +++++++++++++++++++++++++++++++++++++++++---- kernel/time/ntp.c | 7 ++--- kernel/time/timekeeping.c | 47 +++++++++++++++----------------- 3 files changed, 87 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h index e81c35b71da7..3eb19e34cc20 100644 --- a/include/linux/timekeeping.h +++ b/include/linux/timekeeping.h @@ -29,15 +29,71 @@ struct timespec get_monotonic_coarse(void); extern void getrawmonotonic(struct timespec *ts); extern void monotonic_to_bootbased(struct timespec *ts); extern void get_monotonic_boottime(struct timespec *ts); -extern void ktime_get_ts(struct timespec *ts); +extern void ktime_get_ts64(struct timespec64 *ts); + +extern int __getnstimeofday64(struct timespec64 *tv); +extern void getnstimeofday64(struct timespec64 *tv); + +#if BITS_PER_LONG == 64 +static inline int __getnstimeofday(struct timespec *ts) +{ + return __getnstimeofday64(ts); +} + +static inline void getnstimeofday(struct timespec *ts) +{ + getnstimeofday64(ts); +} + +static inline void ktime_get_ts(struct timespec *ts) +{ + ktime_get_ts64(ts); +} + +static inline void ktime_get_real_ts(struct timespec *ts) +{ + getnstimeofday64(ts); +} + +#else +static inline int __getnstimeofday(struct timespec *ts) +{ + struct timespec64 ts64; + int ret = __getnstimeofday64(&ts64); + + *ts = timespec64_to_timespec(ts64); + return ret; +} + +static inline void getnstimeofday(struct timespec *ts) +{ + struct timespec64 ts64; + + getnstimeofday64(&ts64); + *ts = timespec64_to_timespec(ts64); +} + +static inline void ktime_get_ts(struct timespec *ts) +{ + struct timespec64 ts64; + + ktime_get_ts64(&ts64); + *ts = timespec64_to_timespec(ts64); +} + +static inline void ktime_get_real_ts(struct timespec *ts) +{ + struct timespec64 ts64; + + getnstimeofday64(&ts64); + *ts = timespec64_to_timespec(ts64); +} +#endif -extern int __getnstimeofday(struct timespec *tv); -extern void getnstimeofday(struct timespec *tv); extern void getboottime(struct timespec *ts); #define do_posix_clock_monotonic_gettime(ts) ktime_get_ts(ts) -#define ktime_get_real_ts(ts) getnstimeofday(ts) - +#define ktime_get_real_ts64(ts) getnstimeofday64(ts) /* * ktime_t based interfaces diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c index 6e87df94122f..87a346fd6d61 100644 --- a/kernel/time/ntp.c +++ b/kernel/time/ntp.c @@ -466,7 +466,8 @@ static DECLARE_DELAYED_WORK(sync_cmos_work, sync_cmos_clock); static void sync_cmos_clock(struct work_struct *work) { - struct timespec now, next; + struct timespec64 now; + struct timespec next; int fail = 1; /* @@ -485,9 +486,9 @@ static void sync_cmos_clock(struct work_struct *work) return; } - getnstimeofday(&now); + getnstimeofday64(&now); if (abs(now.tv_nsec - (NSEC_PER_SEC / 2)) <= tick_nsec * 5) { - struct timespec adjust = now; + struct timespec adjust = timespec64_to_timespec(now); fail = -ENODEV; if (persistent_clock_is_local) diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 84a2075c3eb4..3210c9e690c5 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -285,13 +285,13 @@ static void timekeeping_forward_now(struct timekeeper *tk) } /** - * __getnstimeofday - Returns the time of day in a timespec. + * __getnstimeofday64 - Returns the time of day in a timespec64. * @ts: pointer to the timespec to be set * * Updates the time of day in the timespec. * Returns 0 on success, or -ve when suspended (timespec will be undefined). */ -int __getnstimeofday(struct timespec *ts) +int __getnstimeofday64(struct timespec64 *ts) { struct timekeeper *tk = &timekeeper; unsigned long seq; @@ -306,7 +306,7 @@ int __getnstimeofday(struct timespec *ts) } while (read_seqcount_retry(&timekeeper_seq, seq)); ts->tv_nsec = 0; - timespec_add_ns(ts, nsecs); + timespec64_add_ns(ts, nsecs); /* * Do not bail out early, in case there were callers still using @@ -316,19 +316,19 @@ int __getnstimeofday(struct timespec *ts) return -EAGAIN; return 0; } -EXPORT_SYMBOL(__getnstimeofday); +EXPORT_SYMBOL(__getnstimeofday64); /** - * getnstimeofday - Returns the time of day in a timespec. + * getnstimeofday64 - Returns the time of day in a timespec64. * @ts: pointer to the timespec to be set * * Returns the time of day in a timespec (WARN if suspended). */ -void getnstimeofday(struct timespec *ts) +void getnstimeofday64(struct timespec64 *ts) { - WARN_ON(__getnstimeofday(ts)); + WARN_ON(__getnstimeofday64(ts)); } -EXPORT_SYMBOL(getnstimeofday); +EXPORT_SYMBOL(getnstimeofday64); ktime_t ktime_get(void) { @@ -350,17 +350,17 @@ ktime_t ktime_get(void) EXPORT_SYMBOL_GPL(ktime_get); /** - * ktime_get_ts - get the monotonic clock in timespec format + * ktime_get_ts64 - get the monotonic clock in timespec64 format * @ts: pointer to timespec variable * * The function calculates the monotonic clock from the realtime * clock and the wall_to_monotonic offset and stores the result * in normalized timespec format in the variable pointed to by @ts. */ -void ktime_get_ts(struct timespec *ts) +void ktime_get_ts64(struct timespec64 *ts) { struct timekeeper *tk = &timekeeper; - struct timespec64 ts64, tomono; + struct timespec64 tomono; s64 nsec; unsigned int seq; @@ -368,18 +368,17 @@ void ktime_get_ts(struct timespec *ts) do { seq = read_seqcount_begin(&timekeeper_seq); - ts64.tv_sec = tk->xtime_sec; + ts->tv_sec = tk->xtime_sec; nsec = timekeeping_get_ns(tk); tomono = tk->wall_to_monotonic; } while (read_seqcount_retry(&timekeeper_seq, seq)); - ts64.tv_sec += tomono.tv_sec; - ts64.tv_nsec = 0; - timespec64_add_ns(&ts64, nsec + tomono.tv_nsec); - *ts = timespec64_to_timespec(ts64); + ts->tv_sec += tomono.tv_sec; + ts->tv_nsec = 0; + timespec64_add_ns(ts, nsec + tomono.tv_nsec); } -EXPORT_SYMBOL_GPL(ktime_get_ts); +EXPORT_SYMBOL_GPL(ktime_get_ts64); /** @@ -473,9 +472,9 @@ EXPORT_SYMBOL(getnstime_raw_and_real); */ void do_gettimeofday(struct timeval *tv) { - struct timespec now; + struct timespec64 now; - getnstimeofday(&now); + getnstimeofday64(&now); tv->tv_sec = now.tv_sec; tv->tv_usec = now.tv_nsec/1000; } @@ -680,11 +679,11 @@ int timekeeping_notify(struct clocksource *clock) */ ktime_t ktime_get_real(void) { - struct timespec now; + struct timespec64 now; - getnstimeofday(&now); + getnstimeofday64(&now); - return timespec_to_ktime(now); + return timespec64_to_ktime(now); } EXPORT_SYMBOL_GPL(ktime_get_real); @@ -1689,7 +1688,6 @@ int do_adjtimex(struct timex *txc) struct timekeeper *tk = &timekeeper; unsigned long flags; struct timespec64 ts; - struct timespec tmp; s32 orig_tai, tai; int ret; @@ -1709,8 +1707,7 @@ int do_adjtimex(struct timex *txc) return ret; } - getnstimeofday(&tmp); - ts = timespec_to_timespec64(tmp); + getnstimeofday64(&ts); raw_spin_lock_irqsave(&timekeeper_lock, flags); write_seqcount_begin(&timekeeper_seq); -- cgit v1.2.3 From c905fae43f61c2b4508fc01722e8db61b6b8ac0b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jul 2014 21:04:05 +0000 Subject: timekeeper: Move tk_xtime to core code No users outside of the core. Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/timekeeper_internal.h | 18 ---------- kernel/time/timekeeping.c | 70 +++++++++++++++++++++++-------------- 2 files changed, 43 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/include/linux/timekeeper_internal.h b/include/linux/timekeeper_internal.h index 1b05491e10f9..16de6d7c240a 100644 --- a/include/linux/timekeeper_internal.h +++ b/include/linux/timekeeper_internal.h @@ -71,16 +71,6 @@ struct timekeeper { }; -static inline struct timespec64 tk_xtime(struct timekeeper *tk) -{ - struct timespec64 ts; - - ts.tv_sec = tk->xtime_sec; - ts.tv_nsec = (long)(tk->xtime_nsec >> tk->shift); - return ts; -} - - #ifdef CONFIG_GENERIC_TIME_VSYSCALL extern void update_vsyscall(struct timekeeper *tk); @@ -92,14 +82,6 @@ extern void update_vsyscall_old(struct timespec *ts, struct timespec *wtm, struct clocksource *c, u32 mult); extern void update_vsyscall_tz(void); -static inline void update_vsyscall(struct timekeeper *tk) -{ - struct timespec xt; - - xt = tk_xtime(tk); - update_vsyscall_old(&xt, &tk->wall_to_monotonic, tk->clock, tk->mult); -} - #else static inline void update_vsyscall(struct timekeeper *tk) diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 3210c9e690c5..983d67b388d7 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -51,6 +51,15 @@ static inline void tk_normalize_xtime(struct timekeeper *tk) } } +static inline struct timespec64 tk_xtime(struct timekeeper *tk) +{ + struct timespec64 ts; + + ts.tv_sec = tk->xtime_sec; + ts.tv_nsec = (long)(tk->xtime_nsec >> tk->shift); + return ts; +} + static void tk_set_xtime(struct timekeeper *tk, const struct timespec64 *ts) { tk->xtime_sec = ts->tv_sec; @@ -199,6 +208,40 @@ static inline s64 timekeeping_get_ns_raw(struct timekeeper *tk) return nsec + arch_gettimeoffset(); } +#ifdef CONFIG_GENERIC_TIME_VSYSCALL_OLD + +static inline void update_vsyscall(struct timekeeper *tk) +{ + struct timespec xt; + + xt = tk_xtime(tk); + update_vsyscall_old(&xt, &tk->wall_to_monotonic, tk->clock, tk->mult); +} + +static inline void old_vsyscall_fixup(struct timekeeper *tk) +{ + s64 remainder; + + /* + * Store only full nanoseconds into xtime_nsec after rounding + * it up and add the remainder to the error difference. + * XXX - This is necessary to avoid small 1ns inconsistnecies caused + * by truncating the remainder in vsyscalls. However, it causes + * additional work to be done in timekeeping_adjust(). Once + * the vsyscall implementations are converted to use xtime_nsec + * (shifted nanoseconds), and CONFIG_GENERIC_TIME_VSYSCALL_OLD + * users are removed, this can be killed. + */ + remainder = tk->xtime_nsec & ((1ULL << tk->shift) - 1); + tk->xtime_nsec -= remainder; + tk->xtime_nsec += 1ULL << tk->shift; + tk->ntp_error += remainder << tk->ntp_error_shift; + tk->ntp_error -= (1ULL << tk->shift) << tk->ntp_error_shift; +} +#else +#define old_vsyscall_fixup(tk) +#endif + static RAW_NOTIFIER_HEAD(pvclock_gtod_chain); static void update_pvclock_gtod(struct timekeeper *tk, bool was_set) @@ -1330,33 +1373,6 @@ static cycle_t logarithmic_accumulation(struct timekeeper *tk, cycle_t offset, return offset; } -#ifdef CONFIG_GENERIC_TIME_VSYSCALL_OLD -static inline void old_vsyscall_fixup(struct timekeeper *tk) -{ - s64 remainder; - - /* - * Store only full nanoseconds into xtime_nsec after rounding - * it up and add the remainder to the error difference. - * XXX - This is necessary to avoid small 1ns inconsistnecies caused - * by truncating the remainder in vsyscalls. However, it causes - * additional work to be done in timekeeping_adjust(). Once - * the vsyscall implementations are converted to use xtime_nsec - * (shifted nanoseconds), and CONFIG_GENERIC_TIME_VSYSCALL_OLD - * users are removed, this can be killed. - */ - remainder = tk->xtime_nsec & ((1ULL << tk->shift) - 1); - tk->xtime_nsec -= remainder; - tk->xtime_nsec += 1ULL << tk->shift; - tk->ntp_error += remainder << tk->ntp_error_shift; - tk->ntp_error -= (1ULL << tk->shift) << tk->ntp_error_shift; -} -#else -#define old_vsyscall_fixup(tk) -#endif - - - /** * update_wall_time - Uses the current clocksource to increment the wall time * -- cgit v1.2.3 From 3fdb14fd1df70325e1e91e1203a699a4803ed741 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jul 2014 21:04:07 +0000 Subject: timekeeping: Cache optimize struct timekeeper struct timekeeper is quite badly sorted for the hot readout path. Most time access functions need to load two cache lines. Rearrange it so ktime_get() and getnstimeofday() are happy with a single cache line. Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/timekeeper_internal.h | 84 ++++++++-------- kernel/time/timekeeping.c | 185 +++++++++++++++++++----------------- 2 files changed, 143 insertions(+), 126 deletions(-) (limited to 'include') diff --git a/include/linux/timekeeper_internal.h b/include/linux/timekeeper_internal.h index 16de6d7c240a..2cb96235c249 100644 --- a/include/linux/timekeeper_internal.h +++ b/include/linux/timekeeper_internal.h @@ -10,7 +10,22 @@ #include #include -/* Structure holding internal timekeeping values. */ +/* + * Structure holding internal timekeeping values. + * + * Note: wall_to_monotonic is what we need to add to xtime (or xtime + * corrected for sub jiffie times) to get to monotonic time. + * Monotonic is pegged at zero at system boot time, so + * wall_to_monotonic will be negative, however, we will ALWAYS keep + * the tv_nsec part positive so we can use the usual normalization. + * + * wall_to_monotonic is moved after resume from suspend for the + * monotonic time not to jump. We need to add total_sleep_time to + * wall_to_monotonic to get the real boot based time offset. + * + * - wall_to_monotonic is no longer the boot time, getboottime must be + * used instead. + */ struct timekeeper { /* Current clocksource used for timekeeping. */ struct clocksource *clock; @@ -18,6 +33,29 @@ struct timekeeper { u32 mult; /* The shift value of the current clocksource. */ u32 shift; + /* Clock shifted nano seconds */ + u64 xtime_nsec; + + /* Current CLOCK_REALTIME time in seconds */ + u64 xtime_sec; + /* CLOCK_REALTIME to CLOCK_MONOTONIC offset */ + struct timespec64 wall_to_monotonic; + + /* Offset clock monotonic -> clock realtime */ + ktime_t offs_real; + /* Offset clock monotonic -> clock boottime */ + ktime_t offs_boot; + /* Offset clock monotonic -> clock tai */ + ktime_t offs_tai; + + /* time spent in suspend */ + struct timespec64 total_sleep_time; + /* The current UTC to TAI offset in seconds */ + s32 tai_offset; + + /* The raw monotonic time for the CLOCK_MONOTONIC_RAW posix clock. */ + struct timespec64 raw_time; + /* Number of clock cycles in one NTP interval. */ cycle_t cycle_interval; /* Last cycle value (also stored in clock->cycle_last) */ @@ -29,46 +67,16 @@ struct timekeeper { /* Raw nano seconds accumulated per NTP interval. */ u32 raw_interval; - /* Current CLOCK_REALTIME time in seconds */ - u64 xtime_sec; - /* Clock shifted nano seconds */ - u64 xtime_nsec; - - /* Difference between accumulated time and NTP time in ntp - * shifted nano seconds. */ + /* + * Difference between accumulated time and NTP time in ntp + * shifted nano seconds. + */ s64 ntp_error; - /* Shift conversion between clock shifted nano seconds and - * ntp shifted nano seconds. */ - u32 ntp_error_shift; - /* - * wall_to_monotonic is what we need to add to xtime (or xtime corrected - * for sub jiffie times) to get to monotonic time. Monotonic is pegged - * at zero at system boot time, so wall_to_monotonic will be negative, - * however, we will ALWAYS keep the tv_nsec part positive so we can use - * the usual normalization. - * - * wall_to_monotonic is moved after resume from suspend for the - * monotonic time not to jump. We need to add total_sleep_time to - * wall_to_monotonic to get the real boot based time offset. - * - * - wall_to_monotonic is no longer the boot time, getboottime must be - * used instead. + * Shift conversion between clock shifted nano seconds and + * ntp shifted nano seconds. */ - struct timespec64 wall_to_monotonic; - /* Offset clock monotonic -> clock realtime */ - ktime_t offs_real; - /* time spent in suspend */ - struct timespec64 total_sleep_time; - /* Offset clock monotonic -> clock boottime */ - ktime_t offs_boot; - /* The raw monotonic time for the CLOCK_MONOTONIC_RAW posix clock. */ - struct timespec64 raw_time; - /* The current UTC to TAI offset in seconds */ - s32 tai_offset; - /* Offset clock monotonic -> clock tai */ - ktime_t offs_tai; - + u32 ntp_error_shift; }; #ifdef CONFIG_GENERIC_TIME_VSYSCALL diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 983d67b388d7..7ca150ad387d 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -32,9 +32,16 @@ #define TK_MIRROR (1 << 1) #define TK_CLOCK_WAS_SET (1 << 2) -static struct timekeeper timekeeper; +/* + * The most important data for readout fits into a single 64 byte + * cache line. + */ +static struct { + seqcount_t seq; + struct timekeeper timekeeper; +} tk_core ____cacheline_aligned; + static DEFINE_RAW_SPINLOCK(timekeeper_lock); -static seqcount_t timekeeper_seq; static struct timekeeper shadow_timekeeper; /* flag for if timekeeping is suspended */ @@ -254,7 +261,7 @@ static void update_pvclock_gtod(struct timekeeper *tk, bool was_set) */ int pvclock_gtod_register_notifier(struct notifier_block *nb) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; unsigned long flags; int ret; @@ -295,7 +302,8 @@ static void timekeeping_update(struct timekeeper *tk, unsigned int action) update_pvclock_gtod(tk, action & TK_CLOCK_WAS_SET); if (action & TK_MIRROR) - memcpy(&shadow_timekeeper, &timekeeper, sizeof(timekeeper)); + memcpy(&shadow_timekeeper, &tk_core.timekeeper, + sizeof(tk_core.timekeeper)); } /** @@ -336,17 +344,17 @@ static void timekeeping_forward_now(struct timekeeper *tk) */ int __getnstimeofday64(struct timespec64 *ts) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; unsigned long seq; s64 nsecs = 0; do { - seq = read_seqcount_begin(&timekeeper_seq); + seq = read_seqcount_begin(&tk_core.seq); ts->tv_sec = tk->xtime_sec; nsecs = timekeeping_get_ns(tk); - } while (read_seqcount_retry(&timekeeper_seq, seq)); + } while (read_seqcount_retry(&tk_core.seq, seq)); ts->tv_nsec = 0; timespec64_add_ns(ts, nsecs); @@ -375,18 +383,18 @@ EXPORT_SYMBOL(getnstimeofday64); ktime_t ktime_get(void) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; unsigned int seq; s64 secs, nsecs; WARN_ON(timekeeping_suspended); do { - seq = read_seqcount_begin(&timekeeper_seq); + seq = read_seqcount_begin(&tk_core.seq); secs = tk->xtime_sec + tk->wall_to_monotonic.tv_sec; nsecs = timekeeping_get_ns(tk) + tk->wall_to_monotonic.tv_nsec; - } while (read_seqcount_retry(&timekeeper_seq, seq)); + } while (read_seqcount_retry(&tk_core.seq, seq)); return ktime_set(secs, nsecs); } @@ -402,7 +410,7 @@ EXPORT_SYMBOL_GPL(ktime_get); */ void ktime_get_ts64(struct timespec64 *ts) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; struct timespec64 tomono; s64 nsec; unsigned int seq; @@ -410,12 +418,12 @@ void ktime_get_ts64(struct timespec64 *ts) WARN_ON(timekeeping_suspended); do { - seq = read_seqcount_begin(&timekeeper_seq); + seq = read_seqcount_begin(&tk_core.seq); ts->tv_sec = tk->xtime_sec; nsec = timekeeping_get_ns(tk); tomono = tk->wall_to_monotonic; - } while (read_seqcount_retry(&timekeeper_seq, seq)); + } while (read_seqcount_retry(&tk_core.seq, seq)); ts->tv_sec += tomono.tv_sec; ts->tv_nsec = 0; @@ -432,7 +440,7 @@ EXPORT_SYMBOL_GPL(ktime_get_ts64); */ void timekeeping_clocktai(struct timespec *ts) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; struct timespec64 ts64; unsigned long seq; u64 nsecs; @@ -440,12 +448,12 @@ void timekeeping_clocktai(struct timespec *ts) WARN_ON(timekeeping_suspended); do { - seq = read_seqcount_begin(&timekeeper_seq); + seq = read_seqcount_begin(&tk_core.seq); ts64.tv_sec = tk->xtime_sec + tk->tai_offset; nsecs = timekeeping_get_ns(tk); - } while (read_seqcount_retry(&timekeeper_seq, seq)); + } while (read_seqcount_retry(&tk_core.seq, seq)); ts64.tv_nsec = 0; timespec64_add_ns(&ts64, nsecs); @@ -482,14 +490,14 @@ EXPORT_SYMBOL(ktime_get_clocktai); */ void getnstime_raw_and_real(struct timespec *ts_raw, struct timespec *ts_real) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; unsigned long seq; s64 nsecs_raw, nsecs_real; WARN_ON_ONCE(timekeeping_suspended); do { - seq = read_seqcount_begin(&timekeeper_seq); + seq = read_seqcount_begin(&tk_core.seq); *ts_raw = timespec64_to_timespec(tk->raw_time); ts_real->tv_sec = tk->xtime_sec; @@ -498,7 +506,7 @@ void getnstime_raw_and_real(struct timespec *ts_raw, struct timespec *ts_real) nsecs_raw = timekeeping_get_ns_raw(tk); nsecs_real = timekeeping_get_ns(tk); - } while (read_seqcount_retry(&timekeeper_seq, seq)); + } while (read_seqcount_retry(&tk_core.seq, seq)); timespec_add_ns(ts_raw, nsecs_raw); timespec_add_ns(ts_real, nsecs_real); @@ -531,7 +539,7 @@ EXPORT_SYMBOL(do_gettimeofday); */ int do_settimeofday(const struct timespec *tv) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; struct timespec64 ts_delta, xt, tmp; unsigned long flags; @@ -539,7 +547,7 @@ int do_settimeofday(const struct timespec *tv) return -EINVAL; raw_spin_lock_irqsave(&timekeeper_lock, flags); - write_seqcount_begin(&timekeeper_seq); + write_seqcount_begin(&tk_core.seq); timekeeping_forward_now(tk); @@ -554,7 +562,7 @@ int do_settimeofday(const struct timespec *tv) timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR | TK_CLOCK_WAS_SET); - write_seqcount_end(&timekeeper_seq); + write_seqcount_end(&tk_core.seq); raw_spin_unlock_irqrestore(&timekeeper_lock, flags); /* signal hrtimers about time change */ @@ -572,7 +580,7 @@ EXPORT_SYMBOL(do_settimeofday); */ int timekeeping_inject_offset(struct timespec *ts) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; unsigned long flags; struct timespec64 ts64, tmp; int ret = 0; @@ -583,7 +591,7 @@ int timekeeping_inject_offset(struct timespec *ts) ts64 = timespec_to_timespec64(*ts); raw_spin_lock_irqsave(&timekeeper_lock, flags); - write_seqcount_begin(&timekeeper_seq); + write_seqcount_begin(&tk_core.seq); timekeeping_forward_now(tk); @@ -600,7 +608,7 @@ int timekeeping_inject_offset(struct timespec *ts) error: /* even if we error out, we forwarded the time, so call update */ timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR | TK_CLOCK_WAS_SET); - write_seqcount_end(&timekeeper_seq); + write_seqcount_end(&tk_core.seq); raw_spin_unlock_irqrestore(&timekeeper_lock, flags); /* signal hrtimers about time change */ @@ -617,14 +625,14 @@ EXPORT_SYMBOL(timekeeping_inject_offset); */ s32 timekeeping_get_tai_offset(void) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; unsigned int seq; s32 ret; do { - seq = read_seqcount_begin(&timekeeper_seq); + seq = read_seqcount_begin(&tk_core.seq); ret = tk->tai_offset; - } while (read_seqcount_retry(&timekeeper_seq, seq)); + } while (read_seqcount_retry(&tk_core.seq, seq)); return ret; } @@ -645,14 +653,14 @@ static void __timekeeping_set_tai_offset(struct timekeeper *tk, s32 tai_offset) */ void timekeeping_set_tai_offset(s32 tai_offset) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; unsigned long flags; raw_spin_lock_irqsave(&timekeeper_lock, flags); - write_seqcount_begin(&timekeeper_seq); + write_seqcount_begin(&tk_core.seq); __timekeeping_set_tai_offset(tk, tai_offset); timekeeping_update(tk, TK_MIRROR | TK_CLOCK_WAS_SET); - write_seqcount_end(&timekeeper_seq); + write_seqcount_end(&tk_core.seq); raw_spin_unlock_irqrestore(&timekeeper_lock, flags); clock_was_set(); } @@ -664,14 +672,14 @@ void timekeeping_set_tai_offset(s32 tai_offset) */ static int change_clocksource(void *data) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; struct clocksource *new, *old; unsigned long flags; new = (struct clocksource *) data; raw_spin_lock_irqsave(&timekeeper_lock, flags); - write_seqcount_begin(&timekeeper_seq); + write_seqcount_begin(&tk_core.seq); timekeeping_forward_now(tk); /* @@ -691,7 +699,7 @@ static int change_clocksource(void *data) } timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR | TK_CLOCK_WAS_SET); - write_seqcount_end(&timekeeper_seq); + write_seqcount_end(&tk_core.seq); raw_spin_unlock_irqrestore(&timekeeper_lock, flags); return 0; @@ -706,7 +714,7 @@ static int change_clocksource(void *data) */ int timekeeping_notify(struct clocksource *clock) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; if (tk->clock == clock) return 0; @@ -738,17 +746,17 @@ EXPORT_SYMBOL_GPL(ktime_get_real); */ void getrawmonotonic(struct timespec *ts) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; struct timespec64 ts64; unsigned long seq; s64 nsecs; do { - seq = read_seqcount_begin(&timekeeper_seq); + seq = read_seqcount_begin(&tk_core.seq); nsecs = timekeeping_get_ns_raw(tk); ts64 = tk->raw_time; - } while (read_seqcount_retry(&timekeeper_seq, seq)); + } while (read_seqcount_retry(&tk_core.seq, seq)); timespec64_add_ns(&ts64, nsecs); *ts = timespec64_to_timespec(ts64); @@ -760,16 +768,16 @@ EXPORT_SYMBOL(getrawmonotonic); */ int timekeeping_valid_for_hres(void) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; unsigned long seq; int ret; do { - seq = read_seqcount_begin(&timekeeper_seq); + seq = read_seqcount_begin(&tk_core.seq); ret = tk->clock->flags & CLOCK_SOURCE_VALID_FOR_HRES; - } while (read_seqcount_retry(&timekeeper_seq, seq)); + } while (read_seqcount_retry(&tk_core.seq, seq)); return ret; } @@ -779,16 +787,16 @@ int timekeeping_valid_for_hres(void) */ u64 timekeeping_max_deferment(void) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; unsigned long seq; u64 ret; do { - seq = read_seqcount_begin(&timekeeper_seq); + seq = read_seqcount_begin(&tk_core.seq); ret = tk->clock->max_idle_ns; - } while (read_seqcount_retry(&timekeeper_seq, seq)); + } while (read_seqcount_retry(&tk_core.seq, seq)); return ret; } @@ -828,7 +836,7 @@ void __weak read_boot_clock(struct timespec *ts) */ void __init timekeeping_init(void) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; struct clocksource *clock; unsigned long flags; struct timespec64 now, boot, tmp; @@ -854,7 +862,7 @@ void __init timekeeping_init(void) } raw_spin_lock_irqsave(&timekeeper_lock, flags); - write_seqcount_begin(&timekeeper_seq); + write_seqcount_begin(&tk_core.seq); ntp_init(); clock = clocksource_default_clock(); @@ -875,9 +883,10 @@ void __init timekeeping_init(void) tmp.tv_nsec = 0; tk_set_sleep_time(tk, tmp); - memcpy(&shadow_timekeeper, &timekeeper, sizeof(timekeeper)); + memcpy(&shadow_timekeeper, &tk_core.timekeeper, + sizeof(tk_core.timekeeper)); - write_seqcount_end(&timekeeper_seq); + write_seqcount_end(&tk_core.seq); raw_spin_unlock_irqrestore(&timekeeper_lock, flags); } @@ -918,7 +927,7 @@ static void __timekeeping_inject_sleeptime(struct timekeeper *tk, */ void timekeeping_inject_sleeptime(struct timespec *delta) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; struct timespec64 tmp; unsigned long flags; @@ -930,7 +939,7 @@ void timekeeping_inject_sleeptime(struct timespec *delta) return; raw_spin_lock_irqsave(&timekeeper_lock, flags); - write_seqcount_begin(&timekeeper_seq); + write_seqcount_begin(&tk_core.seq); timekeeping_forward_now(tk); @@ -939,7 +948,7 @@ void timekeeping_inject_sleeptime(struct timespec *delta) timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR | TK_CLOCK_WAS_SET); - write_seqcount_end(&timekeeper_seq); + write_seqcount_end(&tk_core.seq); raw_spin_unlock_irqrestore(&timekeeper_lock, flags); /* signal hrtimers about time change */ @@ -955,7 +964,7 @@ void timekeeping_inject_sleeptime(struct timespec *delta) */ static void timekeeping_resume(void) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; struct clocksource *clock = tk->clock; unsigned long flags; struct timespec64 ts_new, ts_delta; @@ -970,7 +979,7 @@ static void timekeeping_resume(void) clocksource_resume(); raw_spin_lock_irqsave(&timekeeper_lock, flags); - write_seqcount_begin(&timekeeper_seq); + write_seqcount_begin(&tk_core.seq); /* * After system resumes, we need to calculate the suspended time and @@ -1022,7 +1031,7 @@ static void timekeeping_resume(void) tk->ntp_error = 0; timekeeping_suspended = 0; timekeeping_update(tk, TK_MIRROR | TK_CLOCK_WAS_SET); - write_seqcount_end(&timekeeper_seq); + write_seqcount_end(&tk_core.seq); raw_spin_unlock_irqrestore(&timekeeper_lock, flags); touch_softlockup_watchdog(); @@ -1035,7 +1044,7 @@ static void timekeeping_resume(void) static int timekeeping_suspend(void) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; unsigned long flags; struct timespec64 delta, delta_delta; static struct timespec64 old_delta; @@ -1053,7 +1062,7 @@ static int timekeeping_suspend(void) persistent_clock_exist = true; raw_spin_lock_irqsave(&timekeeper_lock, flags); - write_seqcount_begin(&timekeeper_seq); + write_seqcount_begin(&tk_core.seq); timekeeping_forward_now(tk); timekeeping_suspended = 1; @@ -1078,7 +1087,7 @@ static int timekeeping_suspend(void) } timekeeping_update(tk, TK_MIRROR); - write_seqcount_end(&timekeeper_seq); + write_seqcount_end(&tk_core.seq); raw_spin_unlock_irqrestore(&timekeeper_lock, flags); clockevents_notify(CLOCK_EVT_NOTIFY_SUSPEND, NULL); @@ -1380,7 +1389,7 @@ static cycle_t logarithmic_accumulation(struct timekeeper *tk, cycle_t offset, void update_wall_time(void) { struct clocksource *clock; - struct timekeeper *real_tk = &timekeeper; + struct timekeeper *real_tk = &tk_core.timekeeper; struct timekeeper *tk = &shadow_timekeeper; cycle_t offset; int shift = 0, maxshift; @@ -1440,7 +1449,7 @@ void update_wall_time(void) */ clock_set |= accumulate_nsecs_to_secs(tk); - write_seqcount_begin(&timekeeper_seq); + write_seqcount_begin(&tk_core.seq); /* Update clock->cycle_last with the new value */ clock->cycle_last = tk->cycle_last; /* @@ -1450,12 +1459,12 @@ void update_wall_time(void) * requires changes to all other timekeeper usage sites as * well, i.e. move the timekeeper pointer getter into the * spinlocked/seqcount protected sections. And we trade this - * memcpy under the timekeeper_seq against one before we start + * memcpy under the tk_core.seq against one before we start * updating. */ memcpy(real_tk, tk, sizeof(*tk)); timekeeping_update(real_tk, clock_set); - write_seqcount_end(&timekeeper_seq); + write_seqcount_end(&tk_core.seq); out: raw_spin_unlock_irqrestore(&timekeeper_lock, flags); if (clock_set) @@ -1476,7 +1485,7 @@ out: */ void getboottime(struct timespec *ts) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; struct timespec boottime = { .tv_sec = tk->wall_to_monotonic.tv_sec + tk->total_sleep_time.tv_sec, @@ -1499,7 +1508,7 @@ EXPORT_SYMBOL_GPL(getboottime); */ void get_monotonic_boottime(struct timespec *ts) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; struct timespec64 tomono, sleep, ret; s64 nsec; unsigned int seq; @@ -1507,13 +1516,13 @@ void get_monotonic_boottime(struct timespec *ts) WARN_ON(timekeeping_suspended); do { - seq = read_seqcount_begin(&timekeeper_seq); + seq = read_seqcount_begin(&tk_core.seq); ret.tv_sec = tk->xtime_sec; nsec = timekeeping_get_ns(tk); tomono = tk->wall_to_monotonic; sleep = tk->total_sleep_time; - } while (read_seqcount_retry(&timekeeper_seq, seq)); + } while (read_seqcount_retry(&tk_core.seq, seq)); ret.tv_sec += tomono.tv_sec + sleep.tv_sec; ret.tv_nsec = 0; @@ -1545,7 +1554,7 @@ EXPORT_SYMBOL_GPL(ktime_get_boottime); */ void monotonic_to_bootbased(struct timespec *ts) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; struct timespec64 ts64; ts64 = timespec_to_timespec64(*ts); @@ -1556,7 +1565,7 @@ EXPORT_SYMBOL_GPL(monotonic_to_bootbased); unsigned long get_seconds(void) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; return tk->xtime_sec; } @@ -1564,22 +1573,22 @@ EXPORT_SYMBOL(get_seconds); struct timespec __current_kernel_time(void) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; return timespec64_to_timespec(tk_xtime(tk)); } struct timespec current_kernel_time(void) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; struct timespec64 now; unsigned long seq; do { - seq = read_seqcount_begin(&timekeeper_seq); + seq = read_seqcount_begin(&tk_core.seq); now = tk_xtime(tk); - } while (read_seqcount_retry(&timekeeper_seq, seq)); + } while (read_seqcount_retry(&tk_core.seq, seq)); return timespec64_to_timespec(now); } @@ -1587,16 +1596,16 @@ EXPORT_SYMBOL(current_kernel_time); struct timespec get_monotonic_coarse(void) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; struct timespec64 now, mono; unsigned long seq; do { - seq = read_seqcount_begin(&timekeeper_seq); + seq = read_seqcount_begin(&tk_core.seq); now = tk_xtime(tk); mono = tk->wall_to_monotonic; - } while (read_seqcount_retry(&timekeeper_seq, seq)); + } while (read_seqcount_retry(&tk_core.seq, seq)); set_normalized_timespec64(&now, now.tv_sec + mono.tv_sec, now.tv_nsec + mono.tv_nsec); @@ -1624,19 +1633,19 @@ void do_timer(unsigned long ticks) ktime_t ktime_get_update_offsets_tick(ktime_t *offs_real, ktime_t *offs_boot, ktime_t *offs_tai) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; struct timespec64 ts; ktime_t now; unsigned int seq; do { - seq = read_seqcount_begin(&timekeeper_seq); + seq = read_seqcount_begin(&tk_core.seq); ts = tk_xtime(tk); *offs_real = tk->offs_real; *offs_boot = tk->offs_boot; *offs_tai = tk->offs_tai; - } while (read_seqcount_retry(&timekeeper_seq, seq)); + } while (read_seqcount_retry(&tk_core.seq, seq)); now = ktime_set(ts.tv_sec, ts.tv_nsec); now = ktime_sub(now, *offs_real); @@ -1656,13 +1665,13 @@ ktime_t ktime_get_update_offsets_tick(ktime_t *offs_real, ktime_t *offs_boot, ktime_t ktime_get_update_offsets_now(ktime_t *offs_real, ktime_t *offs_boot, ktime_t *offs_tai) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; ktime_t now; unsigned int seq; u64 secs, nsecs; do { - seq = read_seqcount_begin(&timekeeper_seq); + seq = read_seqcount_begin(&tk_core.seq); secs = tk->xtime_sec; nsecs = timekeeping_get_ns(tk); @@ -1670,7 +1679,7 @@ ktime_t ktime_get_update_offsets_now(ktime_t *offs_real, ktime_t *offs_boot, *offs_real = tk->offs_real; *offs_boot = tk->offs_boot; *offs_tai = tk->offs_tai; - } while (read_seqcount_retry(&timekeeper_seq, seq)); + } while (read_seqcount_retry(&tk_core.seq, seq)); now = ktime_add_ns(ktime_set(secs, 0), nsecs); now = ktime_sub(now, *offs_real); @@ -1683,14 +1692,14 @@ ktime_t ktime_get_update_offsets_now(ktime_t *offs_real, ktime_t *offs_boot, */ ktime_t ktime_get_monotonic_offset(void) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; unsigned long seq; struct timespec64 wtom; do { - seq = read_seqcount_begin(&timekeeper_seq); + seq = read_seqcount_begin(&tk_core.seq); wtom = tk->wall_to_monotonic; - } while (read_seqcount_retry(&timekeeper_seq, seq)); + } while (read_seqcount_retry(&tk_core.seq, seq)); return timespec64_to_ktime(wtom); } @@ -1701,7 +1710,7 @@ EXPORT_SYMBOL_GPL(ktime_get_monotonic_offset); */ int do_adjtimex(struct timex *txc) { - struct timekeeper *tk = &timekeeper; + struct timekeeper *tk = &tk_core.timekeeper; unsigned long flags; struct timespec64 ts; s32 orig_tai, tai; @@ -1726,7 +1735,7 @@ int do_adjtimex(struct timex *txc) getnstimeofday64(&ts); raw_spin_lock_irqsave(&timekeeper_lock, flags); - write_seqcount_begin(&timekeeper_seq); + write_seqcount_begin(&tk_core.seq); orig_tai = tai = tk->tai_offset; ret = __do_adjtimex(txc, &ts, &tai); @@ -1735,7 +1744,7 @@ int do_adjtimex(struct timex *txc) __timekeeping_set_tai_offset(tk, tai); timekeeping_update(tk, TK_MIRROR | TK_CLOCK_WAS_SET); } - write_seqcount_end(&timekeeper_seq); + write_seqcount_end(&tk_core.seq); raw_spin_unlock_irqrestore(&timekeeper_lock, flags); if (tai != orig_tai) @@ -1755,11 +1764,11 @@ void hardpps(const struct timespec *phase_ts, const struct timespec *raw_ts) unsigned long flags; raw_spin_lock_irqsave(&timekeeper_lock, flags); - write_seqcount_begin(&timekeeper_seq); + write_seqcount_begin(&tk_core.seq); __hardpps(phase_ts, raw_ts); - write_seqcount_end(&timekeeper_seq); + write_seqcount_end(&tk_core.seq); raw_spin_unlock_irqrestore(&timekeeper_lock, flags); } EXPORT_SYMBOL(hardpps); -- cgit v1.2.3 From 7c032df5570388044b4efda3d9f4d2ffb96a3116 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jul 2014 21:04:10 +0000 Subject: timekeeping: Provide internal ktime_t based data The ktime_t based interfaces are used a lot in performance critical code pathes. Add ktime_t based data so the interfaces don't have to convert from the xtime/timespec based data. Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/timekeeper_internal.h | 3 +++ kernel/time/timekeeping.c | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+) (limited to 'include') diff --git a/include/linux/timekeeper_internal.h b/include/linux/timekeeper_internal.h index 2cb96235c249..87e0992564f2 100644 --- a/include/linux/timekeeper_internal.h +++ b/include/linux/timekeeper_internal.h @@ -36,6 +36,9 @@ struct timekeeper { /* Clock shifted nano seconds */ u64 xtime_nsec; + /* Monotonic base time */ + ktime_t base_mono; + /* Current CLOCK_REALTIME time in seconds */ u64 xtime_sec; /* CLOCK_REALTIME to CLOCK_MONOTONIC offset */ diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index bfe3ea09afc9..86a92476c027 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -291,6 +291,26 @@ int pvclock_gtod_unregister_notifier(struct notifier_block *nb) } EXPORT_SYMBOL_GPL(pvclock_gtod_unregister_notifier); +/* + * Update the ktime_t based scalar nsec members of the timekeeper + */ +static inline void tk_update_ktime_data(struct timekeeper *tk) +{ + s64 nsec; + + /* + * The xtime based monotonic readout is: + * nsec = (xtime_sec + wtm_sec) * 1e9 + wtm_nsec + now(); + * The ktime based monotonic readout is: + * nsec = base_mono + now(); + * ==> base_mono = (xtime_sec + wtm_sec) * 1e9 + wtm_nsec + */ + nsec = (s64)(tk->xtime_sec + tk->wall_to_monotonic.tv_sec); + nsec *= NSEC_PER_SEC; + nsec += tk->wall_to_monotonic.tv_nsec; + tk->base_mono = ns_to_ktime(nsec); +} + /* must hold timekeeper_lock */ static void timekeeping_update(struct timekeeper *tk, unsigned int action) { @@ -301,6 +321,8 @@ static void timekeeping_update(struct timekeeper *tk, unsigned int action) update_vsyscall(tk); update_pvclock_gtod(tk, action & TK_CLOCK_WAS_SET); + tk_update_ktime_data(tk); + if (action & TK_MIRROR) memcpy(&shadow_timekeeper, &tk_core.timekeeper, sizeof(tk_core.timekeeper)); -- cgit v1.2.3 From 0077dc60f274b9a7e9aa705a34784fefb87e0eee Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jul 2014 21:04:13 +0000 Subject: timekeeping: Provide ktime_get_with_offset() Provide a helper function which lets us implement ktime_t based interfaces for real, boot and tai clocks. Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/timekeeping.h | 9 +++++++++ kernel/time/timekeeping.c | 27 +++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) (limited to 'include') diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h index 3eb19e34cc20..a58e4b1879db 100644 --- a/include/linux/timekeeping.h +++ b/include/linux/timekeeping.h @@ -98,7 +98,16 @@ extern void getboottime(struct timespec *ts); /* * ktime_t based interfaces */ + +enum tk_offsets { + TK_OFFS_REAL, + TK_OFFS_BOOT, + TK_OFFS_TAI, + TK_OFFS_MAX, +}; + extern ktime_t ktime_get(void); +extern ktime_t ktime_get_with_offset(enum tk_offsets offs); extern ktime_t ktime_get_real(void); extern ktime_t ktime_get_boottime(void); extern ktime_t ktime_get_monotonic_offset(void); diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index d5be1425cc03..7c5f5e4a006c 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -423,6 +423,33 @@ ktime_t ktime_get(void) } EXPORT_SYMBOL_GPL(ktime_get); +static ktime_t *offsets[TK_OFFS_MAX] = { + [TK_OFFS_REAL] = &tk_core.timekeeper.offs_real, + [TK_OFFS_BOOT] = &tk_core.timekeeper.offs_boot, + [TK_OFFS_TAI] = &tk_core.timekeeper.offs_tai, +}; + +ktime_t ktime_get_with_offset(enum tk_offsets offs) +{ + struct timekeeper *tk = &tk_core.timekeeper; + unsigned int seq; + ktime_t base, *offset = offsets[offs]; + s64 nsecs; + + WARN_ON(timekeeping_suspended); + + do { + seq = read_seqcount_begin(&tk_core.seq); + base = ktime_add(tk->base_mono, *offset); + nsecs = timekeeping_get_ns(tk); + + } while (read_seqcount_retry(&tk_core.seq, seq)); + + return ktime_add_ns(base, nsecs); + +} +EXPORT_SYMBOL_GPL(ktime_get_with_offset); + /** * ktime_get_ts64 - get the monotonic clock in timespec64 format * @ts: pointer to timespec variable -- cgit v1.2.3 From f5264d5d5a0729306cc792d84432b97785d2662a Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jul 2014 21:04:14 +0000 Subject: timekeeping: Use ktime_t based data for ktime_get_real() Speed up the readout. Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/timekeeping.h | 9 ++++++++- kernel/time/timekeeping.c | 15 --------------- 2 files changed, 8 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h index a58e4b1879db..68e6678a743b 100644 --- a/include/linux/timekeeping.h +++ b/include/linux/timekeeping.h @@ -108,11 +108,18 @@ enum tk_offsets { extern ktime_t ktime_get(void); extern ktime_t ktime_get_with_offset(enum tk_offsets offs); -extern ktime_t ktime_get_real(void); extern ktime_t ktime_get_boottime(void); extern ktime_t ktime_get_monotonic_offset(void); extern ktime_t ktime_get_clocktai(void); +/** + * ktime_get_real - get the real (wall-) time in ktime_t format + */ +static inline ktime_t ktime_get_real(void) +{ + return ktime_get_with_offset(TK_OFFS_REAL); +} + /* * RTC specific */ diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 7c5f5e4a006c..56db2e16970a 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -773,21 +773,6 @@ int timekeeping_notify(struct clocksource *clock) return tk->clock == clock ? 0 : -1; } -/** - * ktime_get_real - get the real (wall-) time in ktime_t format - * - * returns the time in ktime_t format - */ -ktime_t ktime_get_real(void) -{ - struct timespec64 now; - - getnstimeofday64(&now); - - return timespec64_to_ktime(now); -} -EXPORT_SYMBOL_GPL(ktime_get_real); - /** * getrawmonotonic - Returns the raw monotonic time in a timespec * @ts: pointer to the timespec to be set -- cgit v1.2.3 From b82c817e2d16e818c472eb71019de521816000a3 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jul 2014 21:04:16 +0000 Subject: timekeeping; Use ktime_t based data for ktime_get_boottime() Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/timekeeping.h | 12 +++++++++++- kernel/time/timekeeping.c | 17 ----------------- 2 files changed, 11 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h index 68e6678a743b..2fc606203c8c 100644 --- a/include/linux/timekeeping.h +++ b/include/linux/timekeeping.h @@ -108,7 +108,6 @@ enum tk_offsets { extern ktime_t ktime_get(void); extern ktime_t ktime_get_with_offset(enum tk_offsets offs); -extern ktime_t ktime_get_boottime(void); extern ktime_t ktime_get_monotonic_offset(void); extern ktime_t ktime_get_clocktai(void); @@ -120,6 +119,17 @@ static inline ktime_t ktime_get_real(void) return ktime_get_with_offset(TK_OFFS_REAL); } +/** + * ktime_get_boottime - Returns monotonic time since boot in ktime_t format + * + * This is similar to CLOCK_MONTONIC/ktime_get, but also includes the + * time spent in suspend. + */ +static inline ktime_t ktime_get_boottime(void) +{ + return ktime_get_with_offset(TK_OFFS_BOOT); +} + /* * RTC specific */ diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 56db2e16970a..5e60aa09af79 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -1565,23 +1565,6 @@ void get_monotonic_boottime(struct timespec *ts) } EXPORT_SYMBOL_GPL(get_monotonic_boottime); -/** - * ktime_get_boottime - Returns monotonic time since boot in a ktime - * - * Returns the monotonic time since boot in a ktime - * - * This is similar to CLOCK_MONTONIC/ktime_get, but also - * includes the time spent in suspend. - */ -ktime_t ktime_get_boottime(void) -{ - struct timespec ts; - - get_monotonic_boottime(&ts); - return timespec_to_ktime(ts); -} -EXPORT_SYMBOL_GPL(ktime_get_boottime); - /** * monotonic_to_bootbased - Convert the monotonic time to boot based. * @ts: pointer to the timespec to be converted -- cgit v1.2.3 From afab07c0e91ecf098abf34573ccfcd86d6be26f9 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jul 2014 21:04:17 +0000 Subject: timekeeping: Use ktime_t based data for ktime_get_clocktai() Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/timekeeping.h | 9 ++++++++- kernel/time/timekeeping.c | 15 --------------- 2 files changed, 8 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h index 2fc606203c8c..3050a7d0a5a9 100644 --- a/include/linux/timekeeping.h +++ b/include/linux/timekeeping.h @@ -109,7 +109,6 @@ enum tk_offsets { extern ktime_t ktime_get(void); extern ktime_t ktime_get_with_offset(enum tk_offsets offs); extern ktime_t ktime_get_monotonic_offset(void); -extern ktime_t ktime_get_clocktai(void); /** * ktime_get_real - get the real (wall-) time in ktime_t format @@ -130,6 +129,14 @@ static inline ktime_t ktime_get_boottime(void) return ktime_get_with_offset(TK_OFFS_BOOT); } +/** + * ktime_get_clocktai - Returns the TAI time of day in ktime_t format + */ +static inline ktime_t ktime_get_clocktai(void) +{ + return ktime_get_with_offset(TK_OFFS_TAI); +} + /* * RTC specific */ diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 5e60aa09af79..c083ae2c34b5 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -512,21 +512,6 @@ void timekeeping_clocktai(struct timespec *ts) } EXPORT_SYMBOL(timekeeping_clocktai); - -/** - * ktime_get_clocktai - Returns the TAI time of day in a ktime - * - * Returns the time of day in a ktime. - */ -ktime_t ktime_get_clocktai(void) -{ - struct timespec ts; - - timekeeping_clocktai(&ts); - return timespec_to_ktime(ts); -} -EXPORT_SYMBOL(ktime_get_clocktai); - #ifdef CONFIG_NTP_PPS /** -- cgit v1.2.3 From 9a6b51976ea3a326b6de534beec3fd87275f4ef6 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jul 2014 21:04:22 +0000 Subject: timekeeping: Provide ktime_mono_to_any() ktime based conversion function to map a monotonic time stamp to a different CLOCK. Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/timekeeping.h | 9 +++++++++ kernel/time/timekeeping.c | 20 ++++++++++++++++++++ 2 files changed, 29 insertions(+) (limited to 'include') diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h index 3050a7d0a5a9..910a98ef2154 100644 --- a/include/linux/timekeeping.h +++ b/include/linux/timekeeping.h @@ -109,6 +109,7 @@ enum tk_offsets { extern ktime_t ktime_get(void); extern ktime_t ktime_get_with_offset(enum tk_offsets offs); extern ktime_t ktime_get_monotonic_offset(void); +extern ktime_t ktime_mono_to_any(ktime_t tmono, enum tk_offsets offs); /** * ktime_get_real - get the real (wall-) time in ktime_t format @@ -137,6 +138,14 @@ static inline ktime_t ktime_get_clocktai(void) return ktime_get_with_offset(TK_OFFS_TAI); } +/** + * ktime_mono_to_real - Convert monotonic time to clock realtime + */ +static inline ktime_t ktime_mono_to_real(ktime_t mono) +{ + return ktime_mono_to_any(mono, TK_OFFS_REAL); +} + /* * RTC specific */ diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index e99350319eec..032e77a54a79 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -450,6 +450,26 @@ ktime_t ktime_get_with_offset(enum tk_offsets offs) } EXPORT_SYMBOL_GPL(ktime_get_with_offset); +/** + * ktime_mono_to_any() - convert mononotic time to any other time + * @tmono: time to convert. + * @offs: which offset to use + */ +ktime_t ktime_mono_to_any(ktime_t tmono, enum tk_offsets offs) +{ + ktime_t *offset = offsets[offs]; + unsigned long seq; + ktime_t tconv; + + do { + seq = read_seqcount_begin(&tk_core.seq); + tconv = ktime_add(tmono, *offset); + } while (read_seqcount_retry(&tk_core.seq, seq)); + + return tconv; +} +EXPORT_SYMBOL_GPL(ktime_mono_to_any); + /** * ktime_get_ts64 - get the monotonic clock in timespec64 format * @ts: pointer to timespec variable -- cgit v1.2.3 From dcaab54e348c5b66cca4802815ceebd37059e70c Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jul 2014 21:04:28 +0000 Subject: timekeeping: Remove ktime_get_monotonic_offset() No more users. Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/timekeeping.h | 1 - kernel/time/timekeeping.c | 18 ------------------ 2 files changed, 19 deletions(-) (limited to 'include') diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h index 910a98ef2154..64c81f367866 100644 --- a/include/linux/timekeeping.h +++ b/include/linux/timekeeping.h @@ -108,7 +108,6 @@ enum tk_offsets { extern ktime_t ktime_get(void); extern ktime_t ktime_get_with_offset(enum tk_offsets offs); -extern ktime_t ktime_get_monotonic_offset(void); extern ktime_t ktime_mono_to_any(ktime_t tmono, enum tk_offsets offs); /** diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 032e77a54a79..f7378eaebe67 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -1707,24 +1707,6 @@ ktime_t ktime_get_update_offsets_now(ktime_t *offs_real, ktime_t *offs_boot, } #endif -/** - * ktime_get_monotonic_offset() - get wall_to_monotonic in ktime_t format - */ -ktime_t ktime_get_monotonic_offset(void) -{ - struct timekeeper *tk = &tk_core.timekeeper; - unsigned long seq; - struct timespec64 wtom; - - do { - seq = read_seqcount_begin(&tk_core.seq); - wtom = tk->wall_to_monotonic; - } while (read_seqcount_retry(&tk_core.seq, seq)); - - return timespec64_to_ktime(wtom); -} -EXPORT_SYMBOL_GPL(ktime_get_monotonic_offset); - /** * do_adjtimex() - Accessor function to NTP __do_adjtimex function */ -- cgit v1.2.3 From 897994e32b2b0a41ce4222c3b38a05bd2d1ee9fa Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jul 2014 21:04:29 +0000 Subject: timekeeping: Provide ktime_get[*]_ns() helpers A lot of code converts either timespecs or ktime_t to nanoseconds. Provide helper functions. Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/timekeeping.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'include') diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h index 64c81f367866..903ecc10fcff 100644 --- a/include/linux/timekeeping.h +++ b/include/linux/timekeeping.h @@ -145,6 +145,21 @@ static inline ktime_t ktime_mono_to_real(ktime_t mono) return ktime_mono_to_any(mono, TK_OFFS_REAL); } +static inline u64 ktime_get_ns(void) +{ + return ktime_to_ns(ktime_get()); +} + +static inline u64 ktime_get_real_ns(void) +{ + return ktime_to_ns(ktime_get_real()); +} + +static inline u64 ktime_get_boot_ns(void) +{ + return ktime_to_ns(ktime_get_boottime()); +} + /* * RTC specific */ -- cgit v1.2.3 From 57e0be041d9e21a7397eed3b67a7936ac4ac83c0 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jul 2014 21:04:32 +0000 Subject: sched: Make task->real_start_time nanoseconds based Simplify the only user of this data by removing the timespec conversion. Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- fs/proc/array.c | 7 +------ include/linux/sched.h | 2 +- kernel/fork.c | 3 +-- 3 files changed, 3 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/fs/proc/array.c b/fs/proc/array.c index 64db2bceac59..d7f9199217bb 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -473,13 +473,8 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, priority = task_prio(task); nice = task_nice(task); - /* Temporary variable needed for gcc-2.96 */ - /* convert timespec -> nsec*/ - start_time = - (unsigned long long)task->real_start_time.tv_sec * NSEC_PER_SEC - + task->real_start_time.tv_nsec; /* convert nsec -> ticks */ - start_time = nsec_to_clock_t(start_time); + start_time = nsec_to_clock_t(task->real_start_time); seq_printf(m, "%d (%s) %c", pid_nr_ns(pid, ns), tcomm, state); seq_put_decimal_ll(m, ' ', ppid); diff --git a/include/linux/sched.h b/include/linux/sched.h index 306f4f0c987a..67678fa76f99 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1368,7 +1368,7 @@ struct task_struct { #endif unsigned long nvcsw, nivcsw; /* context switch counts */ struct timespec start_time; /* monotonic time */ - struct timespec real_start_time; /* boot based time */ + u64 real_start_time; /* boot based time in nsec */ /* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */ unsigned long min_flt, maj_flt; diff --git a/kernel/fork.c b/kernel/fork.c index 8f541930ce26..a7ab82db2f60 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1263,8 +1263,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, posix_cpu_timers_init(p); ktime_get_ts(&p->start_time); - p->real_start_time = p->start_time; - monotonic_to_bootbased(&p->real_start_time); + p->real_start_time = ktime_get_boot_ns(); p->io_context = NULL; p->audit_context = NULL; if (clone_flags & CLONE_THREAD) -- cgit v1.2.3 From ccbf62d8a284cf181ac28c8e8407dd077d90dd4b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jul 2014 21:04:34 +0000 Subject: sched: Make task->start_time nanoseconds based Simplify the timespec to nsec/usec conversions. Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/sched.h | 2 +- kernel/acct.c | 10 +++------- kernel/fork.c | 2 +- kernel/tsacct.c | 19 +++++++++---------- 4 files changed, 14 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 67678fa76f99..10c6e829927f 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1367,7 +1367,7 @@ struct task_struct { } vtime_snap_whence; #endif unsigned long nvcsw, nivcsw; /* context switch counts */ - struct timespec start_time; /* monotonic time */ + u64 start_time; /* monotonic time in nsec */ u64 real_start_time; /* boot based time in nsec */ /* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */ unsigned long min_flt, maj_flt; diff --git a/kernel/acct.c b/kernel/acct.c index 1be013c6053e..a1844f14c6d6 100644 --- a/kernel/acct.c +++ b/kernel/acct.c @@ -458,9 +458,7 @@ static void do_acct_process(struct bsd_acct_struct *acct, acct_t ac; mm_segment_t fs; unsigned long flim; - u64 elapsed; - u64 run_time; - struct timespec uptime; + u64 elapsed, run_time; struct tty_struct *tty; const struct cred *orig_cred; @@ -484,10 +482,8 @@ static void do_acct_process(struct bsd_acct_struct *acct, strlcpy(ac.ac_comm, current->comm, sizeof(ac.ac_comm)); /* calculate run_time in nsec*/ - ktime_get_ts(&uptime); - run_time = (u64)uptime.tv_sec*NSEC_PER_SEC + uptime.tv_nsec; - run_time -= (u64)current->group_leader->start_time.tv_sec * NSEC_PER_SEC - + current->group_leader->start_time.tv_nsec; + run_time = ktime_get_ns(); + run_time -= current->group_leader->start_time; /* convert nsec -> AHZ */ elapsed = nsec_to_AHZ(run_time); #if ACCT_VERSION==3 diff --git a/kernel/fork.c b/kernel/fork.c index a7ab82db2f60..627b7f80afb0 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1262,7 +1262,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, posix_cpu_timers_init(p); - ktime_get_ts(&p->start_time); + p->start_time = ktime_get_ns(); p->real_start_time = ktime_get_boot_ns(); p->io_context = NULL; p->audit_context = NULL; diff --git a/kernel/tsacct.c b/kernel/tsacct.c index ea6d170452c4..975cb49e32bf 100644 --- a/kernel/tsacct.c +++ b/kernel/tsacct.c @@ -31,20 +31,19 @@ void bacct_add_tsk(struct user_namespace *user_ns, struct taskstats *stats, struct task_struct *tsk) { const struct cred *tcred; - struct timespec uptime, ts; cputime_t utime, stime, utimescaled, stimescaled; - u64 ac_etime; + u64 delta; BUILD_BUG_ON(TS_COMM_LEN < TASK_COMM_LEN); - /* calculate task elapsed time in timespec */ - ktime_get_ts(&uptime); - ts = timespec_sub(uptime, tsk->start_time); - /* rebase elapsed time to usec (should never be negative) */ - ac_etime = timespec_to_ns(&ts); - do_div(ac_etime, NSEC_PER_USEC); - stats->ac_etime = ac_etime; - stats->ac_btime = get_seconds() - ts.tv_sec; + /* calculate task elapsed time in nsec */ + delta = ktime_get_ns() - tsk->start_time; + /* Convert to micro seconds */ + do_div(delta, NSEC_PER_USEC); + stats->ac_etime = delta; + /* Convert to seconds for btime */ + do_div(delta, USEC_PER_SEC); + stats->ac_btime = get_seconds() - delta; if (thread_group_leader(tsk)) { stats->ac_exitcode = tsk->exit_code; if (tsk->flags & PF_FORKNOEXEC) -- cgit v1.2.3 From 9667a23db0dc0bd4892f0ada7e4e71528eaeed62 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jul 2014 21:04:35 +0000 Subject: delayacct: Make accounting nanosecond based Kill the timespec juggling and calculate with plain nanoseconds. Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/sched.h | 4 ++-- kernel/delayacct.c | 34 ++++++++++++---------------------- 2 files changed, 14 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 10c6e829927f..653744ae8d27 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -813,7 +813,7 @@ struct task_delay_info { * associated with the operation is added to XXX_delay. * XXX_delay contains the accumulated delay time in nanoseconds. */ - struct timespec blkio_start, blkio_end; /* Shared by blkio, swapin */ + u64 blkio_start; /* Shared by blkio, swapin */ u64 blkio_delay; /* wait for sync block io completion */ u64 swapin_delay; /* wait for swapin block io completion */ u32 blkio_count; /* total count of the number of sync block */ @@ -821,7 +821,7 @@ struct task_delay_info { u32 swapin_count; /* total count of the number of swapin block */ /* io operations performed */ - struct timespec freepages_start, freepages_end; + u64 freepages_start; u64 freepages_delay; /* wait for memory reclaim */ u32 freepages_count; /* total count of memory reclaim */ }; diff --git a/kernel/delayacct.c b/kernel/delayacct.c index de699f42f9bc..cf2e65dddb19 100644 --- a/kernel/delayacct.c +++ b/kernel/delayacct.c @@ -46,32 +46,25 @@ void __delayacct_tsk_init(struct task_struct *tsk) } /* - * Finish delay accounting for a statistic using - * its timestamps (@start, @end), accumalator (@total) and @count + * Finish delay accounting for a statistic using its timestamps (@start), + * accumalator (@total) and @count */ - -static void delayacct_end(struct timespec *start, struct timespec *end, - u64 *total, u32 *count) +static void delayacct_end(u64 *start, u64 *total, u32 *count) { - struct timespec ts; - s64 ns; + s64 ns = ktime_get_ns() - *start; unsigned long flags; - ktime_get_ts(end); - ts = timespec_sub(*end, *start); - ns = timespec_to_ns(&ts); - if (ns < 0) - return; - - spin_lock_irqsave(¤t->delays->lock, flags); - *total += ns; - (*count)++; - spin_unlock_irqrestore(¤t->delays->lock, flags); + if (ns > 0) { + spin_lock_irqsave(¤t->delays->lock, flags); + *total += ns; + (*count)++; + spin_unlock_irqrestore(¤t->delays->lock, flags); + } } void __delayacct_blkio_start(void) { - ktime_get_ts(¤t->delays->blkio_start); + current->delays->blkio_start = ktime_get_ns(); } void __delayacct_blkio_end(void) @@ -79,12 +72,10 @@ void __delayacct_blkio_end(void) if (current->delays->flags & DELAYACCT_PF_SWAPIN) /* Swapin block I/O */ delayacct_end(¤t->delays->blkio_start, - ¤t->delays->blkio_end, ¤t->delays->swapin_delay, ¤t->delays->swapin_count); else /* Other block I/O */ delayacct_end(¤t->delays->blkio_start, - ¤t->delays->blkio_end, ¤t->delays->blkio_delay, ¤t->delays->blkio_count); } @@ -159,13 +150,12 @@ __u64 __delayacct_blkio_ticks(struct task_struct *tsk) void __delayacct_freepages_start(void) { - ktime_get_ts(¤t->delays->freepages_start); + current->delays->freepages_start = ktime_get_ns(); } void __delayacct_freepages_end(void) { delayacct_end(¤t->delays->freepages_start, - ¤t->delays->freepages_end, ¤t->delays->freepages_delay, ¤t->delays->freepages_count); } -- cgit v1.2.3 From 109e3191935a77d123375f045e719b164fa471aa Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 23 Jul 2014 19:24:56 +0200 Subject: Bluetooth: Read list of local codecs supported by the controller If the Bluetooth controller supports Read Local Supported Codecs command, then issue it during initialization so that the list of codecs is known. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 2 ++ net/bluetooth/hci_core.c | 4 ++++ 2 files changed, 6 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index f0a3d8890760..5d30919eaba9 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1074,6 +1074,8 @@ struct hci_rp_read_data_block_size { __le16 num_blocks; } __packed; +#define HCI_OP_READ_LOCAL_CODECS 0x100b + #define HCI_OP_READ_PAGE_SCAN_ACTIVITY 0x0c1b struct hci_rp_read_page_scan_activity { __u8 status; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index cfcb6055ced8..f3e14103b76b 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1686,6 +1686,10 @@ static void hci_init4_req(struct hci_request *req, unsigned long opt) if (hdev->commands[22] & 0x04) hci_set_event_mask_page_2(req); + /* Read local codec list if the HCI command is supported */ + if (hdev->commands[29] & 0x20) + hci_req_add(req, HCI_OP_READ_LOCAL_CODECS, 0, NULL); + /* Check for Synchronization Train support */ if (lmp_sync_train_capable(hdev)) hci_req_add(req, HCI_OP_READ_SYNC_TRAIN_PARAMS, 0, NULL); -- cgit v1.2.3 From f4fe73ed564b1c0c375481cb7a773b03767b0216 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 23 Jul 2014 19:24:57 +0200 Subject: Bluetooth: Get MWS transport configuration of the controller If the Bluetooth controller supports Get MWS Transport Layer Configuration command, then issue it during initialization. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 2 ++ net/bluetooth/hci_core.c | 4 ++++ 2 files changed, 6 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 5d30919eaba9..e3fd926df13f 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1172,6 +1172,8 @@ struct hci_rp_write_remote_amp_assoc { __u8 phy_handle; } __packed; +#define HCI_OP_GET_MWS_TRANSPORT_CONFIG 0x140c + #define HCI_OP_ENABLE_DUT_MODE 0x1803 #define HCI_OP_WRITE_SSP_DEBUG_MODE 0x1804 diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index f3e14103b76b..078f1ecbc058 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1690,6 +1690,10 @@ static void hci_init4_req(struct hci_request *req, unsigned long opt) if (hdev->commands[29] & 0x20) hci_req_add(req, HCI_OP_READ_LOCAL_CODECS, 0, NULL); + /* Get MWS transport configuration if the HCI command is supported */ + if (hdev->commands[30] & 0x08) + hci_req_add(req, HCI_OP_GET_MWS_TRANSPORT_CONFIG, 0, NULL); + /* Check for Synchronization Train support */ if (lmp_sync_train_capable(hdev)) hci_req_add(req, HCI_OP_READ_SYNC_TRAIN_PARAMS, 0, NULL); -- cgit v1.2.3 From d9bb5a43277d2dcc514fa693f741bbc38e2e2271 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 22 Jul 2014 17:48:04 -0400 Subject: svcrdma: Double the default credit limit The RDMA credit limit controls how many concurrent RPCs are allowed per connection. An NFS/RDMA client and server exchange their credit limits in the RPC/RDMA headers. The Linux client and the Solaris client and server allow 32 credits. The Linux server allows only 16, which limits its performance. Set the server's default credit limit to 32, like the other well- known implementations, so the out-of-the-shrinkwrap performance of the Linux server is better. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/svc_rdma.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index 5cf99a016368..975da754c778 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -174,8 +174,7 @@ struct svcxprt_rdma { * page size of 4k, or 32k * 2 ops / 4k = 16 outstanding RDMA_READ. */ #define RPCRDMA_ORD (64/4) #define RPCRDMA_SQ_DEPTH_MULT 8 -#define RPCRDMA_MAX_THREADS 16 -#define RPCRDMA_MAX_REQUESTS 16 +#define RPCRDMA_MAX_REQUESTS 32 #define RPCRDMA_MAX_REQ_SIZE 4096 /* svc_rdma_marshal.c */ -- cgit v1.2.3 From 0162d621ddf3bd02bf7de324dcf002d9c84c5059 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Wed, 23 Jul 2014 15:03:00 -0400 Subject: ftrace: Rename ftrace_ops field from trampolines to nr_trampolines Having two fields within the same struct that is off by one character can be confusing and error prone. Rename the counter "trampolines" to "nr_trampolines" to explicitly show it is a counter and not to be confused by the "trampoline" field. Suggested-by: Oleg Nesterov Signed-off-by: Steven Rostedt --- include/linux/ftrace.h | 2 +- kernel/trace/ftrace.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 7a5b7b97e539..6bb5e3f2a3b4 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -120,7 +120,7 @@ struct ftrace_ops { void *private; int __percpu *disabled; #ifdef CONFIG_DYNAMIC_FTRACE - int trampolines; + int nr_trampolines; struct ftrace_hash *notrace_hash; struct ftrace_hash *filter_hash; struct ftrace_hash *tramp_hash; diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 762806026561..eda69c9f78d0 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -1513,7 +1513,7 @@ static void ftrace_remove_tramp(struct ftrace_ops *ops, * The tramp_hash entry will be removed at time * of update. */ - ops->trampolines--; + ops->nr_trampolines--; rec->flags &= ~FTRACE_FL_TRAMP; } @@ -1522,7 +1522,7 @@ static void ftrace_clear_tramps(struct dyn_ftrace *rec) struct ftrace_ops *op; do_for_each_ftrace_op(op, ftrace_ops_list) { - if (op->trampolines) + if (op->nr_trampolines) ftrace_remove_tramp(op, rec); } while_for_each_ftrace_op(op); } @@ -1617,7 +1617,7 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops, */ if (ftrace_rec_count(rec) == 1 && ops->trampoline) { rec->flags |= FTRACE_FL_TRAMP; - ops->trampolines++; + ops->nr_trampolines++; } else { /* * If we are adding another function callback @@ -2185,7 +2185,7 @@ static int ftrace_save_ops_tramp_hash(struct ftrace_ops *ops) int size, bits; int ret; - size = ops->trampolines; + size = ops->nr_trampolines; bits = 0; /* * Make the hash size about 1/2 the # found @@ -2239,7 +2239,7 @@ static int ftrace_save_tramp_hashes(void) free_ftrace_hash(op->tramp_hash); op->tramp_hash = NULL; - if (op->trampolines) { + if (op->nr_trampolines) { ret = ftrace_save_ops_tramp_hash(op); if (ret) return ret; -- cgit v1.2.3 From 274f482d33a309c87096f2983601ceda2761094e Mon Sep 17 00:00:00 2001 From: Sorin Dumitru Date: Tue, 22 Jul 2014 21:16:51 +0300 Subject: sock: remove skb argument from sk_rcvqueues_full It hasn't been used since commit 0fd7bac(net: relax rcvbuf limits). Signed-off-by: Sorin Dumitru Signed-off-by: David S. Miller --- include/net/sock.h | 5 ++--- net/core/sock.c | 2 +- net/ipv4/udp.c | 2 +- net/ipv6/udp.c | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index 28f734601b50..720773304a85 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -810,8 +810,7 @@ static inline void __sk_add_backlog(struct sock *sk, struct sk_buff *skb) * Do not take into account this skb truesize, * to allow even a single big packet to come. */ -static inline bool sk_rcvqueues_full(const struct sock *sk, const struct sk_buff *skb, - unsigned int limit) +static inline bool sk_rcvqueues_full(const struct sock *sk, unsigned int limit) { unsigned int qsize = sk->sk_backlog.len + atomic_read(&sk->sk_rmem_alloc); @@ -822,7 +821,7 @@ static inline bool sk_rcvqueues_full(const struct sock *sk, const struct sk_buff static inline __must_check int sk_add_backlog(struct sock *sk, struct sk_buff *skb, unsigned int limit) { - if (sk_rcvqueues_full(sk, skb, limit)) + if (sk_rcvqueues_full(sk, limit)) return -ENOBUFS; __sk_add_backlog(sk, skb); diff --git a/net/core/sock.c b/net/core/sock.c index 026e01f70274..ca9b65199d28 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -491,7 +491,7 @@ int sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested) skb->dev = NULL; - if (sk_rcvqueues_full(sk, skb, sk->sk_rcvbuf)) { + if (sk_rcvqueues_full(sk, sk->sk_rcvbuf)) { atomic_inc(&sk->sk_drops); goto discard_and_relse; } diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index f31053b90ee0..f57c0e4c2326 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1567,7 +1567,7 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) goto csum_error; - if (sk_rcvqueues_full(sk, skb, sk->sk_rcvbuf)) { + if (sk_rcvqueues_full(sk, sk->sk_rcvbuf)) { UDP_INC_STATS_BH(sock_net(sk), UDP_MIB_RCVBUFERRORS, is_udplite); goto drop; diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index f9d8800bb72f..5b6091de5868 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -673,7 +673,7 @@ int udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) goto csum_error; } - if (sk_rcvqueues_full(sk, skb, sk->sk_rcvbuf)) { + if (sk_rcvqueues_full(sk, sk->sk_rcvbuf)) { UDP6_INC_STATS_BH(sock_net(sk), UDP_MIB_RCVBUFERRORS, is_udplite); goto drop; -- cgit v1.2.3 From c12f07d17c12193256a99e20c9a0f130fb8f7be8 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 23 Jul 2014 13:12:16 +0800 Subject: ACPICA: Add new GPE public interface - acpi_mark_gpe_for_wake. ACPICA commit c49dbfed2bc069d0038ea7e1294409bfde7c2c8c Some potential callers of acpi_setup_gpe_for_wake may know in advance that there won't be any notify handlers installed for device wake notifications from the given GPE (one example is a button GPE in Linux). For these cases, acpi_mark_gpe_for_wake should be used instead of acpi_setup_gpe_for_wake. This will set the ACPI_GPE_CAN_WAKE flag for the GPE without trying to setup implicit wake notification for it (since there's no handler method). Rafael Wysocki. Signed-off-by: Rafael J. Wysocki Signed-off-by: Bob Moore Signed-off-by: Lv Zheng --- drivers/acpi/acpica/evxfgpe.c | 47 +++++++++++++++++++++++++++++++++++++++++++ include/acpi/acpixf.h | 4 ++++ 2 files changed, 51 insertions(+) (limited to 'include') diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c index 82e5c146bdd4..0cf159cc6e6d 100644 --- a/drivers/acpi/acpica/evxfgpe.c +++ b/drivers/acpi/acpica/evxfgpe.c @@ -185,6 +185,53 @@ acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number) ACPI_EXPORT_SYMBOL(acpi_disable_gpe) +/******************************************************************************* + * + * FUNCTION: acpi_mark_gpe_for_wake + * + * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 + * gpe_number - GPE level within the GPE block + * + * RETURN: Status + * + * DESCRIPTION: Mark a GPE as having the ability to wake the system. Simply + * sets the ACPI_GPE_CAN_WAKE flag. + * + * Some potential callers of acpi_setup_gpe_for_wake may know in advance that + * there won't be any notify handlers installed for device wake notifications + * from the given GPE (one example is a button GPE in Linux). For these cases, + * acpi_mark_gpe_for_wake should be used instead of acpi_setup_gpe_for_wake. + * This will set the ACPI_GPE_CAN_WAKE flag for the GPE without trying to + * setup implicit wake notification for it (since there's no handler method). + * + ******************************************************************************/ +acpi_status acpi_mark_gpe_for_wake(acpi_handle gpe_device, u32 gpe_number) +{ + struct acpi_gpe_event_info *gpe_event_info; + acpi_status status = AE_BAD_PARAMETER; + acpi_cpu_flags flags; + + ACPI_FUNCTION_TRACE(acpi_mark_gpe_for_wake); + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + /* Ensure that we have a valid GPE number */ + + gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); + if (gpe_event_info) { + + /* Mark the GPE as a possible wake event */ + + gpe_event_info->flags |= ACPI_GPE_CAN_WAKE; + status = AE_OK; + } + + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_mark_gpe_for_wake) + /******************************************************************************* * * FUNCTION: acpi_setup_gpe_for_wake diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 3f6e14ff24b1..508e5649dda9 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -674,6 +674,10 @@ ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_finish_gpe(acpi_handle gpe_device, u32 gpe_number)) +ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status + acpi_mark_gpe_for_wake(acpi_handle gpe_device, + u32 gpe_number)) + ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_setup_gpe_for_wake(acpi_handle parent_device, -- cgit v1.2.3 From 4b9e7e7516135b1d5f047ad59188b5355bacc106 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 23 Jul 2014 21:55:23 +0200 Subject: Bluetooth: Fix issue with ADV_IND reports and auto-connection handling When adding remote devices to the kernel using the Add Device management command, these devices are explicitly allowed to connect. This kind of incoming connections are possible even when the controller itself is not connectable. For BR/EDR this distinction is pretty simple since there is only one type of incoming connections. With LE this is not that simple anymore since there are ADV_IND and ADV_DIRECT_IND advertising events. The ADV_DIRECT_IND advertising events are send for incoming (slave initiated) connections only. And this is the only thing the kernel should allow when adding devices using action 0x01. This meaning of incoming connections is coming from BR/EDR and needs to be mapped to LE the same way. Supporting the auto-connection of devices using ADV_IND advertising events is an important feature as well. However it does not map to incoming connections. So introduce a new action 0x02 that allows the kernel to connect to devices using ADV_DIRECT_IND and in addition ADV_IND advertising reports. This difference is represented by the new HCI_AUTO_CONN_DIRECT value for only connecting to ADV_DIRECT_IND. For connection to ADV_IND and ADV_DIRECT_IND the old value HCI_AUTO_CONN_ALWAYS is used. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 1 + net/bluetooth/hci_event.c | 27 ++++++++++++++++++++++++++- net/bluetooth/mgmt.c | 9 ++++++--- 4 files changed, 34 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 996ed065b6c2..747a0c3d9947 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -458,6 +458,7 @@ struct hci_conn_params { enum { HCI_AUTO_CONN_DISABLED, HCI_AUTO_CONN_REPORT, + HCI_AUTO_CONN_DIRECT, HCI_AUTO_CONN_ALWAYS, HCI_AUTO_CONN_LINK_LOSS, } auto_connect; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 078f1ecbc058..d8f91d5b0e56 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3647,6 +3647,7 @@ int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, list_add(¶ms->action, &hdev->pend_le_reports); hci_update_background_scan(hdev); break; + case HCI_AUTO_CONN_DIRECT: case HCI_AUTO_CONN_ALWAYS: if (!is_connected(hdev, addr, addr_type)) { list_add(¶ms->action, &hdev->pend_le_conns); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 293dd98ae98f..ba26fbfe481a 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2259,6 +2259,7 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) break; /* Fall through */ + case HCI_AUTO_CONN_DIRECT: case HCI_AUTO_CONN_ALWAYS: list_del_init(¶ms->action); list_add(¶ms->action, &hdev->pend_le_conns); @@ -4251,6 +4252,7 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, u8 adv_type) { struct hci_conn *conn; + struct hci_conn_params *params; /* If the event is not connectable don't proceed further */ if (adv_type != LE_ADV_IND && adv_type != LE_ADV_DIRECT_IND) @@ -4269,8 +4271,31 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, /* If we're not connectable only connect devices that we have in * our pend_le_conns list. */ - if (!hci_pend_le_action_lookup(&hdev->pend_le_conns, addr, addr_type)) + params = hci_pend_le_action_lookup(&hdev->pend_le_conns, + addr, addr_type); + if (!params) + return; + + switch (params->auto_connect) { + case HCI_AUTO_CONN_DIRECT: + /* Only devices advertising with ADV_DIRECT_IND are + * triggering a connection attempt. This is allowing + * incoming connections from slave devices. + */ + if (adv_type != LE_ADV_DIRECT_IND) + return; + break; + case HCI_AUTO_CONN_ALWAYS: + /* Devices advertising with ADV_IND or ADV_DIRECT_IND + * are triggering a connection attempt. This means + * that incoming connectioms from slave device are + * accepted and also outgoing connections to slave + * devices are established when found. + */ + break; + default: return; + } conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW, HCI_LE_AUTOCONN_TIMEOUT, HCI_ROLE_MASTER); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 190668367e42..ccc4653ce658 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5271,7 +5271,7 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, MGMT_STATUS_INVALID_PARAMS, &cp->addr, sizeof(cp->addr)); - if (cp->action != 0x00 && cp->action != 0x01) + if (cp->action != 0x00 && cp->action != 0x01 && cp->action != 0x02) return cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, MGMT_STATUS_INVALID_PARAMS, &cp->addr, sizeof(cp->addr)); @@ -5281,7 +5281,7 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, if (cp->addr.type == BDADDR_BREDR) { bool update_scan; - /* Only "connect" action supported for now */ + /* Only incoming connections action is supported for now */ if (cp->action != 0x01) { err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, MGMT_STATUS_INVALID_PARAMS, @@ -5307,8 +5307,10 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, else addr_type = ADDR_LE_DEV_RANDOM; - if (cp->action) + if (cp->action == 0x02) auto_conn = HCI_AUTO_CONN_ALWAYS; + else if (cp->action == 0x01) + auto_conn = HCI_AUTO_CONN_DIRECT; else auto_conn = HCI_AUTO_CONN_REPORT; @@ -5870,6 +5872,7 @@ static void restart_le_actions(struct hci_dev *hdev) list_del_init(&p->action); switch (p->auto_connect) { + case HCI_AUTO_CONN_DIRECT: case HCI_AUTO_CONN_ALWAYS: list_add(&p->action, &hdev->pend_le_conns); break; -- cgit v1.2.3 From 50a77c658b80e7e3303e3bcec195b30e2b62d513 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 23 Jul 2014 12:38:48 -0700 Subject: Input: atmel_mxt_ts - download device config using firmware loader The existing implementation which encodes the configuration as a binary blob in platform data is unsatisfactory since it requires a kernel recompile for the configuration to be changed, and it doesn't deal well with firmware changes that move values around on the chip. Atmel define an ASCII format for the configuration which can be exported from their tools. This patch implements a parser for that format which loads the configuration via the firmware loader and sends it to the MXT chip. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 278 ++++++++++++++++++++++-------- drivers/platform/chrome/chromeos_laptop.c | 4 - include/linux/i2c/atmel_mxt_ts.h | 3 - 3 files changed, 204 insertions(+), 81 deletions(-) (limited to 'include') diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 4317273c2138..c6b5e7dbd942 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2,6 +2,7 @@ * Atmel maXTouch Touchscreen driver * * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Copyright (C) 2011-2014 Atmel Corporation * Copyright (C) 2012 Google, Inc. * * Author: Joonyoung Shim @@ -30,8 +31,10 @@ #define MXT_VER_21 21 #define MXT_VER_22 22 -/* Firmware */ +/* Firmware files */ #define MXT_FW_NAME "maxtouch.fw" +#define MXT_CFG_NAME "maxtouch.cfg" +#define MXT_CFG_MAGIC "OBP_RAW V1" /* Registers */ #define MXT_INFO 0x00 @@ -298,37 +301,6 @@ static bool mxt_object_readable(unsigned int type) } } -static bool mxt_object_writable(unsigned int type) -{ - switch (type) { - case MXT_GEN_COMMAND_T6: - case MXT_GEN_POWER_T7: - case MXT_GEN_ACQUIRE_T8: - case MXT_TOUCH_MULTI_T9: - case MXT_TOUCH_KEYARRAY_T15: - case MXT_TOUCH_PROXIMITY_T23: - case MXT_TOUCH_PROXKEY_T52: - case MXT_PROCI_GRIPFACE_T20: - case MXT_PROCG_NOISE_T22: - case MXT_PROCI_ONETOUCH_T24: - case MXT_PROCI_TWOTOUCH_T27: - case MXT_PROCI_GRIP_T40: - case MXT_PROCI_PALM_T41: - case MXT_PROCI_TOUCHSUPPRESSION_T42: - case MXT_PROCI_STYLUS_T47: - case MXT_PROCG_NOISESUPPRESSION_T48: - case MXT_SPT_COMMSCONFIG_T18: - case MXT_SPT_GPIOPWM_T19: - case MXT_SPT_SELFTEST_T25: - case MXT_SPT_CTECONFIG_T28: - case MXT_SPT_DIGITIZER_T43: - case MXT_SPT_CTECONFIG_T46: - return true; - default: - return false; - } -} - static void mxt_dump_message(struct device *dev, struct mxt_message *message) { @@ -606,7 +578,7 @@ mxt_get_object(struct mxt_data *data, u8 type) return object; } - dev_err(&data->client->dev, "Invalid object type T%u\n", type); + dev_warn(&data->client->dev, "Invalid object type T%u\n", type); return NULL; } @@ -877,58 +849,197 @@ static void mxt_update_crc(struct mxt_data *data, u8 cmd, u8 value) mxt_wait_for_completion(data, &data->crc_completion, MXT_CRC_TIMEOUT); } -static int mxt_check_reg_init(struct mxt_data *data) +/* + * mxt_update_cfg - download configuration to chip + * + * Atmel Raw Config File Format + * + * The first four lines of the raw config file contain: + * 1) Version + * 2) Chip ID Information (first 7 bytes of device memory) + * 3) Chip Information Block 24-bit CRC Checksum + * 4) Chip Configuration 24-bit CRC Checksum + * + * The rest of the file consists of one line per object instance: + * + * + * - 2-byte object type as hex + * - 2-byte object instance number as hex + * - 2-byte object size as hex + * - array of 1-byte hex values + */ +static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg) { - const struct mxt_platform_data *pdata = data->pdata; - struct mxt_object *object; struct device *dev = &data->client->dev; - int index = 0; - int i, size; + struct mxt_info cfg_info; + struct mxt_object *object; int ret; + int offset; + int pos; + int i; + u32 info_crc, config_crc; + unsigned int type, instance, size; + u8 val; + u16 reg; - if (!pdata->config) { - dev_dbg(dev, "No cfg data defined, skipping reg init\n"); - return 0; + mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1); + + if (strncmp(cfg->data, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) { + dev_err(dev, "Unrecognised config file\n"); + ret = -EINVAL; + goto release; } - mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1); + pos = strlen(MXT_CFG_MAGIC); + + /* Load information block and check */ + for (i = 0; i < sizeof(struct mxt_info); i++) { + ret = sscanf(cfg->data + pos, "%hhx%n", + (unsigned char *)&cfg_info + i, + &offset); + if (ret != 1) { + dev_err(dev, "Bad format\n"); + ret = -EINVAL; + goto release; + } - if (data->config_crc == pdata->config_crc) { - dev_info(dev, "Config CRC 0x%06X: OK\n", data->config_crc); - return 0; + pos += offset; + } + + if (cfg_info.family_id != data->info.family_id) { + dev_err(dev, "Family ID mismatch!\n"); + ret = -EINVAL; + goto release; } - dev_info(dev, "Config CRC 0x%06X: does not match 0x%06X\n", - data->config_crc, pdata->config_crc); + if (cfg_info.variant_id != data->info.variant_id) { + dev_err(dev, "Variant ID mismatch!\n"); + ret = -EINVAL; + goto release; + } - for (i = 0; i < data->info.object_num; i++) { - object = data->object_table + i; + if (cfg_info.version != data->info.version) + dev_err(dev, "Warning: version mismatch!\n"); - if (!mxt_object_writable(object->type)) + if (cfg_info.build != data->info.build) + dev_err(dev, "Warning: build num mismatch!\n"); + + ret = sscanf(cfg->data + pos, "%x%n", &info_crc, &offset); + if (ret != 1) { + dev_err(dev, "Bad format: failed to parse Info CRC\n"); + ret = -EINVAL; + goto release; + } + pos += offset; + + /* Check config CRC */ + ret = sscanf(cfg->data + pos, "%x%n", &config_crc, &offset); + if (ret != 1) { + dev_err(dev, "Bad format: failed to parse Config CRC\n"); + ret = -EINVAL; + goto release; + } + pos += offset; + + if (data->config_crc == config_crc) { + dev_dbg(dev, "Config CRC 0x%06X: OK\n", config_crc); + ret = 0; + goto release; + } + + dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n", + data->config_crc, config_crc); + + while (pos < cfg->size) { + /* Read type, instance, length */ + ret = sscanf(cfg->data + pos, "%x %x %x%n", + &type, &instance, &size, &offset); + if (ret == 0) { + /* EOF */ + ret = 1; + goto release; + } else if (ret != 3) { + dev_err(dev, "Bad format: failed to parse object\n"); + ret = -EINVAL; + goto release; + } + pos += offset; + + object = mxt_get_object(data, type); + if (!object) { + /* Skip object */ + for (i = 0; i < size; i++) { + ret = sscanf(cfg->data + pos, "%hhx%n", + &val, + &offset); + pos += offset; + } continue; + } - size = mxt_obj_size(object) * mxt_obj_instances(object); - if (index + size > pdata->config_length) { - dev_err(dev, "Not enough config data!\n"); - return -EINVAL; + if (size > mxt_obj_size(object)) { + dev_err(dev, "Discarding %zu byte(s) in T%u\n", + size - mxt_obj_size(object), type); } - ret = __mxt_write_reg(data->client, object->start_address, - size, &pdata->config[index]); - if (ret) - return ret; - index += size; + if (instance >= mxt_obj_instances(object)) { + dev_err(dev, "Object instances exceeded!\n"); + ret = -EINVAL; + goto release; + } + + reg = object->start_address + mxt_obj_size(object) * instance; + + for (i = 0; i < size; i++) { + ret = sscanf(cfg->data + pos, "%hhx%n", + &val, + &offset); + if (ret != 1) { + dev_err(dev, "Bad format in T%d\n", type); + ret = -EINVAL; + goto release; + } + pos += offset; + + if (i > mxt_obj_size(object)) + continue; + + ret = mxt_write_reg(data->client, reg + i, val); + if (ret) + goto release; + + } + + /* + * If firmware is upgraded, new bytes may be added to end of + * objects. It is generally forward compatible to zero these + * bytes - previous behaviour will be retained. However + * this does invalidate the CRC and will force a config + * download every time until the configuration is updated. + */ + if (size < mxt_obj_size(object)) { + dev_info(dev, "Zeroing %zu byte(s) in T%d\n", + mxt_obj_size(object) - size, type); + + for (i = size + 1; i < mxt_obj_size(object); i++) { + ret = mxt_write_reg(data->client, reg + i, 0); + if (ret) + goto release; + } + } } mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE); ret = mxt_soft_reset(data); if (ret) - return ret; + goto release; dev_info(dev, "Config successfully updated\n"); - return 0; +release: + release_firmware(cfg); + return ret; } static int mxt_make_highchg(struct mxt_data *data) @@ -1204,10 +1315,17 @@ err_free_mem: return error; } +static int mxt_configure_objects(struct mxt_data *data, + const struct firmware *cfg); + +static void mxt_config_cb(const struct firmware *cfg, void *ctx) +{ + mxt_configure_objects(ctx, cfg); +} + static int mxt_initialize(struct mxt_data *data) { struct i2c_client *client = data->client; - struct mxt_info *info = &data->info; int error; error = mxt_get_info(data); @@ -1225,28 +1343,40 @@ static int mxt_initialize(struct mxt_data *data) if (error) goto err_free_object_table; - /* Check register init values */ - error = mxt_check_reg_init(data); - if (error) { - dev_err(&client->dev, "Error %d initializing configuration\n", - error); - goto err_free_object_table; + request_firmware_nowait(THIS_MODULE, true, MXT_CFG_NAME, + &data->client->dev, GFP_KERNEL, data, + mxt_config_cb); + + return 0; + +err_free_object_table: + mxt_free_object_table(data); + return error; +} + +static int mxt_configure_objects(struct mxt_data *data, + const struct firmware *cfg) +{ + struct device *dev = &data->client->dev; + struct mxt_info *info = &data->info; + int error; + + if (cfg) { + error = mxt_update_cfg(data, cfg); + if (error) + dev_warn(dev, "Error %d updating config\n", error); } error = mxt_initialize_t9_input_device(data); if (error) - goto err_free_object_table; + return error; - dev_info(&client->dev, + dev_info(dev, "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n", info->family_id, info->variant_id, info->version >> 4, info->version & 0xf, info->build, info->object_num); return 0; - -err_free_object_table: - mxt_free_object_table(data); - return error; } /* Firmware Version is returned as Major.Minor.Build */ diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c index 7f1a2e2711bd..67b316b2a2bd 100644 --- a/drivers/platform/chrome/chromeos_laptop.c +++ b/drivers/platform/chrome/chromeos_laptop.c @@ -97,8 +97,6 @@ static struct mxt_platform_data atmel_224s_tp_platform_data = { .irqflags = IRQF_TRIGGER_FALLING, .t19_num_keys = ARRAY_SIZE(mxt_t19_keys), .t19_keymap = mxt_t19_keys, - .config = NULL, - .config_length = 0, }; static struct i2c_board_info atmel_224s_tp_device = { @@ -109,8 +107,6 @@ static struct i2c_board_info atmel_224s_tp_device = { static struct mxt_platform_data atmel_1664s_platform_data = { .irqflags = IRQF_TRIGGER_FALLING, - .config = NULL, - .config_length = 0, }; static struct i2c_board_info atmel_1664s_device = { diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h index 3891dc1de21c..02bf6ea31701 100644 --- a/include/linux/i2c/atmel_mxt_ts.h +++ b/include/linux/i2c/atmel_mxt_ts.h @@ -17,9 +17,6 @@ /* The platform data for the Atmel maXTouch touchscreen driver */ struct mxt_platform_data { - const u8 *config; - size_t config_length; - u32 config_crc; unsigned long irqflags; u8 t19_num_keys; const unsigned int *t19_keymap; -- cgit v1.2.3 From 14a7004671246d1b799f545335995a9897de1268 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jul 2014 21:04:44 +0000 Subject: net: mlx5: Use ktime_get_ns() This code is beyond silly: struct timespec ts = ktime_get_ts(); ktime_t ktime = timespec_to_ktime(ts); Further down the code builds the delta of two ktime_t values and converts the result to nanoseconds. Use ktime_get_ns() and replace all the nonsense. Signed-off-by: Thomas Gleixner Cc: Eli Cohen Signed-off-by: John Stultz --- drivers/net/ethernet/mellanox/mlx5/core/cmd.c | 16 ++++------------ include/linux/mlx5/driver.h | 4 ++-- 2 files changed, 6 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index 87d1b018a9c3..67f8f5a1dc86 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -548,7 +548,7 @@ static void cmd_work_handler(struct work_struct *work) lay->status_own = CMD_OWNER_HW; set_signature(ent, !cmd->checksum_disabled); dump_command(dev, ent, 1); - ktime_get_ts(&ent->ts1); + ent->ts1 = ktime_get_ns(); /* ring doorbell after the descriptor is valid */ wmb(); @@ -637,7 +637,6 @@ static int mlx5_cmd_invoke(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *in, { struct mlx5_cmd *cmd = &dev->cmd; struct mlx5_cmd_work_ent *ent; - ktime_t t1, t2, delta; struct mlx5_cmd_stats *stats; int err = 0; s64 ds; @@ -668,10 +667,7 @@ static int mlx5_cmd_invoke(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *in, if (err == -ETIMEDOUT) goto out; - t1 = timespec_to_ktime(ent->ts1); - t2 = timespec_to_ktime(ent->ts2); - delta = ktime_sub(t2, t1); - ds = ktime_to_ns(delta); + ds = ent->ts2 - ent->ts1; op = be16_to_cpu(((struct mlx5_inbox_hdr *)in->first.data)->opcode); if (op < ARRAY_SIZE(cmd->stats)) { stats = &cmd->stats[op]; @@ -1135,7 +1131,6 @@ void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, unsigned long vector) void *context; int err; int i; - ktime_t t1, t2, delta; s64 ds; struct mlx5_cmd_stats *stats; unsigned long flags; @@ -1149,7 +1144,7 @@ void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, unsigned long vector) sem = &cmd->pages_sem; else sem = &cmd->sem; - ktime_get_ts(&ent->ts2); + ent->ts2 = ktime_get_ns(); memcpy(ent->out->first.data, ent->lay->out, sizeof(ent->lay->out)); dump_command(dev, ent, 0); if (!ent->ret) { @@ -1163,10 +1158,7 @@ void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, unsigned long vector) } free_ent(cmd, ent->idx); if (ent->callback) { - t1 = timespec_to_ktime(ent->ts1); - t2 = timespec_to_ktime(ent->ts2); - delta = ktime_sub(t2, t1); - ds = ktime_to_ns(delta); + ds = ent->ts2 - ent->ts1; if (ent->op < ARRAY_SIZE(cmd->stats)) { stats = &cmd->stats[ent->op]; spin_lock_irqsave(&stats->lock, flags); diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index 2bce4aad2570..52d631ca32cf 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -604,8 +604,8 @@ struct mlx5_cmd_work_ent { int page_queue; u8 status; u8 token; - struct timespec ts1; - struct timespec ts2; + u64 ts1; + u64 ts2; u16 op; }; -- cgit v1.2.3 From fb31cc153dec0d4bdd9a5d7ce60d61acd04b4304 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jul 2014 21:04:49 +0000 Subject: iio: Use ktime_get_real_ns() No idea why iio needs wall clock based time stamps, but we can avoid the timespec conversion dance by using the new interfaces. Signed-off-by: Thomas Gleixner Acked-by: Jonathan Cameron Signed-off-by: John Stultz --- include/linux/iio/iio.h | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index ccde91725f98..15dc6bc2bdd2 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -277,14 +277,7 @@ static inline bool iio_channel_has_info(const struct iio_chan_spec *chan, **/ static inline s64 iio_get_time_ns(void) { - struct timespec ts; - /* - * calls getnstimeofday. - * If hrtimers then up to ns accurate, if not microsecond. - */ - ktime_get_real_ts(&ts); - - return timespec_to_ns(&ts); + return ktime_get_real_ns(); } /* Device operating modes */ -- cgit v1.2.3 From 250fade8af2ac5dda8d5106ea06738b6f9e768a7 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jul 2014 21:04:55 +0000 Subject: timekeeping: Remove monotonic_to_bootbased No more users. Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/timekeeping.h | 1 - kernel/time/timekeeping.c | 15 --------------- 2 files changed, 16 deletions(-) (limited to 'include') diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h index 903ecc10fcff..8ea3ca1b0ee5 100644 --- a/include/linux/timekeeping.h +++ b/include/linux/timekeeping.h @@ -27,7 +27,6 @@ struct timespec __current_kernel_time(void); */ struct timespec get_monotonic_coarse(void); extern void getrawmonotonic(struct timespec *ts); -extern void monotonic_to_bootbased(struct timespec *ts); extern void get_monotonic_boottime(struct timespec *ts); extern void ktime_get_ts64(struct timespec64 *ts); diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index f7378eaebe67..b35613508725 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -1570,21 +1570,6 @@ void get_monotonic_boottime(struct timespec *ts) } EXPORT_SYMBOL_GPL(get_monotonic_boottime); -/** - * monotonic_to_bootbased - Convert the monotonic time to boot based. - * @ts: pointer to the timespec to be converted - */ -void monotonic_to_bootbased(struct timespec *ts) -{ - struct timekeeper *tk = &tk_core.timekeeper; - struct timespec64 ts64; - - ts64 = timespec_to_timespec64(*ts); - ts64 = timespec64_add(ts64, tk->total_sleep_time); - *ts = timespec64_to_timespec(ts64); -} -EXPORT_SYMBOL_GPL(monotonic_to_bootbased); - unsigned long get_seconds(void) { struct timekeeper *tk = &tk_core.timekeeper; -- cgit v1.2.3 From 48f18fd6addc199f330d838d54fe7b0a0892adaa Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jul 2014 21:04:57 +0000 Subject: timekeeping: Use ktime_get_boottime() for get_monotonic_boottime() get_monotonic_boottime() is not used in fast pathes, so the extra timespec conversion is not problematic. Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/timekeeping.h | 9 ++++++++- kernel/time/timekeeping.c | 34 ---------------------------------- 2 files changed, 8 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h index 8ea3ca1b0ee5..7b8f20007871 100644 --- a/include/linux/timekeeping.h +++ b/include/linux/timekeeping.h @@ -27,7 +27,6 @@ struct timespec __current_kernel_time(void); */ struct timespec get_monotonic_coarse(void); extern void getrawmonotonic(struct timespec *ts); -extern void get_monotonic_boottime(struct timespec *ts); extern void ktime_get_ts64(struct timespec64 *ts); extern int __getnstimeofday64(struct timespec64 *tv); @@ -159,6 +158,14 @@ static inline u64 ktime_get_boot_ns(void) return ktime_to_ns(ktime_get_boottime()); } +/* + * Timespec interfaces utilizing the ktime based ones + */ +static inline void get_monotonic_boottime(struct timespec *ts) +{ + *ts = ktime_to_timespec(ktime_get_boottime()); +} + /* * RTC specific */ diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index b35613508725..f63476fb0daf 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -1536,40 +1536,6 @@ void getboottime(struct timespec *ts) } EXPORT_SYMBOL_GPL(getboottime); -/** - * get_monotonic_boottime - Returns monotonic time since boot - * @ts: pointer to the timespec to be set - * - * Returns the monotonic time since boot in a timespec. - * - * This is similar to CLOCK_MONTONIC/ktime_get_ts, but also - * includes the time spent in suspend. - */ -void get_monotonic_boottime(struct timespec *ts) -{ - struct timekeeper *tk = &tk_core.timekeeper; - struct timespec64 tomono, sleep, ret; - s64 nsec; - unsigned int seq; - - WARN_ON(timekeeping_suspended); - - do { - seq = read_seqcount_begin(&tk_core.seq); - ret.tv_sec = tk->xtime_sec; - nsec = timekeeping_get_ns(tk); - tomono = tk->wall_to_monotonic; - sleep = tk->total_sleep_time; - - } while (read_seqcount_retry(&tk_core.seq, seq)); - - ret.tv_sec += tomono.tv_sec + sleep.tv_sec; - ret.tv_nsec = 0; - timespec64_add_ns(&ret, nsec + tomono.tv_nsec + sleep.tv_nsec); - *ts = timespec64_to_timespec(ret); -} -EXPORT_SYMBOL_GPL(get_monotonic_boottime); - unsigned long get_seconds(void) { struct timekeeper *tk = &tk_core.timekeeper; -- cgit v1.2.3 From 47da70d32535000ec29cc206cfc1d318fbd8761f Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jul 2014 21:05:00 +0000 Subject: timekeeping: Remove timekeeper.total_sleep_time No more users. Remove it Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/timekeeper_internal.h | 6 ++---- kernel/time/timekeeping.c | 14 +++----------- 2 files changed, 5 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/linux/timekeeper_internal.h b/include/linux/timekeeper_internal.h index 87e0992564f2..8e5d77a01787 100644 --- a/include/linux/timekeeper_internal.h +++ b/include/linux/timekeeper_internal.h @@ -20,8 +20,8 @@ * the tv_nsec part positive so we can use the usual normalization. * * wall_to_monotonic is moved after resume from suspend for the - * monotonic time not to jump. We need to add total_sleep_time to - * wall_to_monotonic to get the real boot based time offset. + * monotonic time not to jump. To calculate the real boot time offset + * we need to do offs_real - offs_boot. * * - wall_to_monotonic is no longer the boot time, getboottime must be * used instead. @@ -51,8 +51,6 @@ struct timekeeper { /* Offset clock monotonic -> clock tai */ ktime_t offs_tai; - /* time spent in suspend */ - struct timespec64 total_sleep_time; /* The current UTC to TAI offset in seconds */ s32 tai_offset; diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 3edc0c1d6fe8..50d5de05b837 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -97,13 +97,9 @@ static void tk_set_wall_to_mono(struct timekeeper *tk, struct timespec64 wtm) tk->offs_tai = ktime_add(tk->offs_real, ktime_set(tk->tai_offset, 0)); } -static void tk_set_sleep_time(struct timekeeper *tk, struct timespec64 t) +static inline void tk_update_sleep_time(struct timekeeper *tk, ktime_t delta) { - /* Verify consistency before modifying */ - WARN_ON_ONCE(tk->offs_boot.tv64 != timespec64_to_ktime(tk->total_sleep_time).tv64); - - tk->total_sleep_time = t; - tk->offs_boot = timespec64_to_ktime(t); + tk->offs_boot = ktime_add(tk->offs_boot, delta); } /** @@ -919,10 +915,6 @@ void __init timekeeping_init(void) set_normalized_timespec64(&tmp, -boot.tv_sec, -boot.tv_nsec); tk_set_wall_to_mono(tk, tmp); - tmp.tv_sec = 0; - tmp.tv_nsec = 0; - tk_set_sleep_time(tk, tmp); - timekeeping_update(tk, TK_MIRROR); write_seqcount_end(&tk_core.seq); @@ -950,7 +942,7 @@ static void __timekeeping_inject_sleeptime(struct timekeeper *tk, } tk_xtime_add(tk, delta); tk_set_wall_to_mono(tk, timespec64_sub(tk->wall_to_monotonic, *delta)); - tk_set_sleep_time(tk, timespec64_add(tk->total_sleep_time, *delta)); + tk_update_sleep_time(tk, timespec64_to_ktime(*delta)); tk_debug_account_sleep_time(delta); } -- cgit v1.2.3 From 61edec81d260bc96a73c878bbdb4c614460346da Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jul 2014 21:05:01 +0000 Subject: timekeeping: Simplify timekeeping_clocktai() timekeeping_clocktai() is not used in fast pathes, so the extra timespec conversion is not problematic. Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/timekeeping.h | 5 +++++ kernel/time/timekeeping.c | 31 ------------------------------- 2 files changed, 5 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h index 7b8f20007871..f0f12a84a31b 100644 --- a/include/linux/timekeeping.h +++ b/include/linux/timekeeping.h @@ -166,6 +166,11 @@ static inline void get_monotonic_boottime(struct timespec *ts) *ts = ktime_to_timespec(ktime_get_boottime()); } +static inline void timekeeping_clocktai(struct timespec *ts) +{ + *ts = ktime_to_timespec(ktime_get_clocktai()); +} + /* * RTC specific */ diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 50d5de05b837..118e91e3071c 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -497,37 +497,6 @@ void ktime_get_ts64(struct timespec64 *ts) } EXPORT_SYMBOL_GPL(ktime_get_ts64); - -/** - * timekeeping_clocktai - Returns the TAI time of day in a timespec - * @ts: pointer to the timespec to be set - * - * Returns the time of day in a timespec. - */ -void timekeeping_clocktai(struct timespec *ts) -{ - struct timekeeper *tk = &tk_core.timekeeper; - struct timespec64 ts64; - unsigned long seq; - u64 nsecs; - - WARN_ON(timekeeping_suspended); - - do { - seq = read_seqcount_begin(&tk_core.seq); - - ts64.tv_sec = tk->xtime_sec + tk->tai_offset; - nsecs = timekeeping_get_ns(tk); - - } while (read_seqcount_retry(&tk_core.seq, seq)); - - ts64.tv_nsec = 0; - timespec64_add_ns(&ts64, nsecs); - *ts = timespec64_to_timespec(ts64); - -} -EXPORT_SYMBOL(timekeeping_clocktai); - #ifdef CONFIG_NTP_PPS /** -- cgit v1.2.3 From f519b1a2e08c913375324a927992bb328387f169 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jul 2014 21:05:04 +0000 Subject: timekeeping: Provide ktime_get_raw() Provide a ktime_t based interface for raw monotonic time. Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/timekeeper_internal.h | 3 +++ include/linux/timekeeping.h | 6 ++++++ kernel/time/timekeeping.c | 25 +++++++++++++++++++++++++ 3 files changed, 34 insertions(+) (limited to 'include') diff --git a/include/linux/timekeeper_internal.h b/include/linux/timekeeper_internal.h index 8e5d77a01787..2e20275a7083 100644 --- a/include/linux/timekeeper_internal.h +++ b/include/linux/timekeeper_internal.h @@ -54,6 +54,9 @@ struct timekeeper { /* The current UTC to TAI offset in seconds */ s32 tai_offset; + /* Monotonic raw base time */ + ktime_t base_raw; + /* The raw monotonic time for the CLOCK_MONOTONIC_RAW posix clock. */ struct timespec64 raw_time; diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h index f0f12a84a31b..58ad7eff83ff 100644 --- a/include/linux/timekeeping.h +++ b/include/linux/timekeeping.h @@ -107,6 +107,7 @@ enum tk_offsets { extern ktime_t ktime_get(void); extern ktime_t ktime_get_with_offset(enum tk_offsets offs); extern ktime_t ktime_mono_to_any(ktime_t tmono, enum tk_offsets offs); +extern ktime_t ktime_get_raw(void); /** * ktime_get_real - get the real (wall-) time in ktime_t format @@ -158,6 +159,11 @@ static inline u64 ktime_get_boot_ns(void) return ktime_to_ns(ktime_get_boottime()); } +static inline u64 ktime_get_raw_ns(void) +{ + return ktime_to_ns(ktime_get_raw()); +} + /* * Timespec interfaces utilizing the ktime based ones */ diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 118e91e3071c..af8051f4420d 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -305,6 +305,9 @@ static inline void tk_update_ktime_data(struct timekeeper *tk) nsec *= NSEC_PER_SEC; nsec += tk->wall_to_monotonic.tv_nsec; tk->base_mono = ns_to_ktime(nsec); + + /* Update the monotonic raw base */ + tk->base_raw = timespec64_to_ktime(tk->raw_time); } /* must hold timekeeper_lock */ @@ -466,6 +469,27 @@ ktime_t ktime_mono_to_any(ktime_t tmono, enum tk_offsets offs) } EXPORT_SYMBOL_GPL(ktime_mono_to_any); +/** + * ktime_get_raw - Returns the raw monotonic time in ktime_t format + */ +ktime_t ktime_get_raw(void) +{ + struct timekeeper *tk = &tk_core.timekeeper; + unsigned int seq; + ktime_t base; + s64 nsecs; + + do { + seq = read_seqcount_begin(&tk_core.seq); + base = tk->base_raw; + nsecs = timekeeping_get_ns_raw(tk); + + } while (read_seqcount_retry(&tk_core.seq, seq)); + + return ktime_add_ns(base, nsecs); +} +EXPORT_SYMBOL_GPL(ktime_get_raw); + /** * ktime_get_ts64 - get the monotonic clock in timespec64 format * @ts: pointer to timespec variable @@ -878,6 +902,7 @@ void __init timekeeping_init(void) tk_set_xtime(tk, &now); tk->raw_time.tv_sec = 0; tk->raw_time.tv_nsec = 0; + tk->base_raw.tv64 = 0; if (boot.tv_sec == 0 && boot.tv_nsec == 0) boot = tk_xtime(tk); -- cgit v1.2.3 From 4a0e637738f06673725792d74eed67f8779b62c7 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jul 2014 21:05:13 +0000 Subject: clocksource: Get rid of cycle_last cycle_last was added to the clocksource to support the TSC validation. We moved that to the core code, so we can get rid of the extra copy. Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- arch/arm64/kernel/vdso.c | 2 +- arch/ia64/kernel/time.c | 4 ++-- arch/powerpc/kernel/time.c | 4 ++-- arch/s390/kernel/time.c | 2 +- arch/tile/kernel/time.c | 2 +- arch/x86/kernel/vsyscall_gtod.c | 2 +- arch/x86/kvm/x86.c | 2 +- include/linux/clocksource.h | 2 -- include/linux/timekeeper_internal.h | 7 ++++--- kernel/time/timekeeping.c | 23 +++++++++++------------ 10 files changed, 24 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/arch/arm64/kernel/vdso.c b/arch/arm64/kernel/vdso.c index 50384fec56c4..574672f001f7 100644 --- a/arch/arm64/kernel/vdso.c +++ b/arch/arm64/kernel/vdso.c @@ -224,7 +224,7 @@ void update_vsyscall(struct timekeeper *tk) vdso_data->wtm_clock_nsec = tk->wall_to_monotonic.tv_nsec; if (!use_syscall) { - vdso_data->cs_cycle_last = tk->clock->cycle_last; + vdso_data->cs_cycle_last = tk->cycle_last; vdso_data->xtime_clock_sec = tk->xtime_sec; vdso_data->xtime_clock_nsec = tk->xtime_nsec; vdso_data->cs_mult = tk->mult; diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c index 71c52bc7c28d..11dc42da7daf 100644 --- a/arch/ia64/kernel/time.c +++ b/arch/ia64/kernel/time.c @@ -441,7 +441,7 @@ void update_vsyscall_tz(void) } void update_vsyscall_old(struct timespec *wall, struct timespec *wtm, - struct clocksource *c, u32 mult) + struct clocksource *c, u32 mult, cycles_t cycle_last) { write_seqcount_begin(&fsyscall_gtod_data.seq); @@ -450,7 +450,7 @@ void update_vsyscall_old(struct timespec *wall, struct timespec *wtm, fsyscall_gtod_data.clk_mult = mult; fsyscall_gtod_data.clk_shift = c->shift; fsyscall_gtod_data.clk_fsys_mmio = c->archdata.fsys_mmio; - fsyscall_gtod_data.clk_cycle_last = c->cycle_last; + fsyscall_gtod_data.clk_cycle_last = cycle_last; /* copy kernel time structures */ fsyscall_gtod_data.wall_time.tv_sec = wall->tv_sec; diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index 9fff9cdcc519..368ab374d33c 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -741,7 +741,7 @@ static cycle_t timebase_read(struct clocksource *cs) } void update_vsyscall_old(struct timespec *wall_time, struct timespec *wtm, - struct clocksource *clock, u32 mult) + struct clocksource *clock, u32 mult, cycle_t cycle_last) { u64 new_tb_to_xs, new_stamp_xsec; u32 frac_sec; @@ -774,7 +774,7 @@ void update_vsyscall_old(struct timespec *wall_time, struct timespec *wtm, * We expect the caller to have done the first increment of * vdso_data->tb_update_count already. */ - vdso_data->tb_orig_stamp = clock->cycle_last; + vdso_data->tb_orig_stamp = cycle_last; vdso_data->stamp_xsec = new_stamp_xsec; vdso_data->tb_to_xs = new_tb_to_xs; vdso_data->wtom_clock_sec = wtm->tv_sec; diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index 0931b110c826..97950f392613 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -220,7 +220,7 @@ void update_vsyscall(struct timekeeper *tk) /* Make userspace gettimeofday spin until we're done. */ ++vdso_data->tb_update_count; smp_wmb(); - vdso_data->xtime_tod_stamp = tk->clock->cycle_last; + vdso_data->xtime_tod_stamp = tk->cycle_last; vdso_data->xtime_clock_sec = tk->xtime_sec; vdso_data->xtime_clock_nsec = tk->xtime_nsec; vdso_data->wtom_clock_sec = diff --git a/arch/tile/kernel/time.c b/arch/tile/kernel/time.c index ae70155c2f16..d22d5bfc1e4e 100644 --- a/arch/tile/kernel/time.c +++ b/arch/tile/kernel/time.c @@ -269,7 +269,7 @@ void update_vsyscall(struct timekeeper *tk) /* Userspace gettimeofday will spin while this value is odd. */ ++vdso_data->tb_update_count; smp_wmb(); - vdso_data->xtime_tod_stamp = clock->cycle_last; + vdso_data->xtime_tod_stamp = tk->cycle_last; vdso_data->xtime_clock_sec = tk->xtime_sec; vdso_data->xtime_clock_nsec = tk->xtime_nsec; vdso_data->wtom_clock_sec = wtm->tv_sec; diff --git a/arch/x86/kernel/vsyscall_gtod.c b/arch/x86/kernel/vsyscall_gtod.c index 9531fbb123ba..c3cb3c144591 100644 --- a/arch/x86/kernel/vsyscall_gtod.c +++ b/arch/x86/kernel/vsyscall_gtod.c @@ -32,7 +32,7 @@ void update_vsyscall(struct timekeeper *tk) /* copy vsyscall data */ vdata->vclock_mode = tk->clock->archdata.vclock_mode; - vdata->cycle_last = tk->clock->cycle_last; + vdata->cycle_last = tk->cycle_last; vdata->mask = tk->clock->mask; vdata->mult = tk->mult; vdata->shift = tk->shift; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 63832f5110b6..7b25125f3f42 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -1001,7 +1001,7 @@ static void update_pvclock_gtod(struct timekeeper *tk) /* copy pvclock gtod data */ vdata->clock.vclock_mode = tk->clock->archdata.vclock_mode; - vdata->clock.cycle_last = tk->clock->cycle_last; + vdata->clock.cycle_last = tk->cycle_last; vdata->clock.mask = tk->clock->mask; vdata->clock.mult = tk->mult; vdata->clock.shift = tk->shift; diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index a16b497d5159..653f0e2b6ca9 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -162,7 +162,6 @@ extern u64 timecounter_cyc2time(struct timecounter *tc, * @archdata: arch-specific data * @suspend: suspend function for the clocksource, if necessary * @resume: resume function for the clocksource, if necessary - * @cycle_last: most recent cycle counter value seen by ::read() * @owner: module reference, must be set by clocksource in modules */ struct clocksource { @@ -171,7 +170,6 @@ struct clocksource { * clocksource itself is cacheline aligned. */ cycle_t (*read)(struct clocksource *cs); - cycle_t cycle_last; cycle_t mask; u32 mult; u32 shift; diff --git a/include/linux/timekeeper_internal.h b/include/linux/timekeeper_internal.h index 2e20275a7083..cb88096222c0 100644 --- a/include/linux/timekeeper_internal.h +++ b/include/linux/timekeeper_internal.h @@ -29,6 +29,8 @@ struct timekeeper { /* Current clocksource used for timekeeping. */ struct clocksource *clock; + /* Last cycle value */ + cycle_t cycle_last; /* NTP adjusted clock multiplier */ u32 mult; /* The shift value of the current clocksource. */ @@ -62,8 +64,6 @@ struct timekeeper { /* Number of clock cycles in one NTP interval. */ cycle_t cycle_interval; - /* Last cycle value (also stored in clock->cycle_last) */ - cycle_t cycle_last; /* Number of clock shifted nano seconds in one NTP interval. */ u64 xtime_interval; /* shifted nano seconds left over when rounding cycle_interval */ @@ -91,7 +91,8 @@ extern void update_vsyscall_tz(void); #elif defined(CONFIG_GENERIC_TIME_VSYSCALL_OLD) extern void update_vsyscall_old(struct timespec *ts, struct timespec *wtm, - struct clocksource *c, u32 mult); + struct clocksource *c, u32 mult, + cycles_t cycle_last); extern void update_vsyscall_tz(void); #else diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 531805013786..4e748c404749 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -121,7 +121,7 @@ static void tk_setup_internals(struct timekeeper *tk, struct clocksource *clock) old_clock = tk->clock; tk->clock = clock; - tk->cycle_last = clock->cycle_last = clock->read(clock); + tk->cycle_last = clock->read(clock); /* Do the ns -> cycle conversion first, using original mult */ tmp = NTP_INTERVAL_LENGTH; @@ -182,7 +182,7 @@ static inline s64 timekeeping_get_ns(struct timekeeper *tk) cycle_now = clock->read(clock); /* calculate the delta since the last update_wall_time: */ - delta = clocksource_delta(cycle_now, clock->cycle_last, clock->mask); + delta = clocksource_delta(cycle_now, tk->cycle_last, clock->mask); nsec = delta * tk->mult + tk->xtime_nsec; nsec >>= tk->shift; @@ -202,7 +202,7 @@ static inline s64 timekeeping_get_ns_raw(struct timekeeper *tk) cycle_now = clock->read(clock); /* calculate the delta since the last update_wall_time: */ - delta = clocksource_delta(cycle_now, clock->cycle_last, clock->mask); + delta = clocksource_delta(cycle_now, tk->cycle_last, clock->mask); /* convert delta to nanoseconds. */ nsec = clocksource_cyc2ns(delta, clock->mult, clock->shift); @@ -218,7 +218,8 @@ static inline void update_vsyscall(struct timekeeper *tk) struct timespec xt; xt = tk_xtime(tk); - update_vsyscall_old(&xt, &tk->wall_to_monotonic, tk->clock, tk->mult); + update_vsyscall_old(&xt, &tk->wall_to_monotonic, tk->clock, tk->mult, + tk->cycle_last); } static inline void old_vsyscall_fixup(struct timekeeper *tk) @@ -342,8 +343,8 @@ static void timekeeping_forward_now(struct timekeeper *tk) clock = tk->clock; cycle_now = clock->read(clock); - delta = clocksource_delta(cycle_now, clock->cycle_last, clock->mask); - tk->cycle_last = clock->cycle_last = cycle_now; + delta = clocksource_delta(cycle_now, tk->cycle_last, clock->mask); + tk->cycle_last = cycle_now; tk->xtime_nsec += delta * tk->mult; @@ -1020,13 +1021,13 @@ static void timekeeping_resume(void) */ cycle_now = clock->read(clock); if ((clock->flags & CLOCK_SOURCE_SUSPEND_NONSTOP) && - cycle_now > clock->cycle_last) { + cycle_now > tk->cycle_last) { u64 num, max = ULLONG_MAX; u32 mult = clock->mult; u32 shift = clock->shift; s64 nsec = 0; - cycle_delta = clocksource_delta(cycle_now, clock->cycle_last, + cycle_delta = clocksource_delta(cycle_now, tk->cycle_last, clock->mask); /* @@ -1053,7 +1054,7 @@ static void timekeeping_resume(void) __timekeeping_inject_sleeptime(tk, &ts_delta); /* Re-base the last cycle value */ - tk->cycle_last = clock->cycle_last = cycle_now; + tk->cycle_last = cycle_now; tk->ntp_error = 0; timekeeping_suspended = 0; timekeeping_update(tk, TK_MIRROR | TK_CLOCK_WAS_SET); @@ -1433,7 +1434,7 @@ void update_wall_time(void) #ifdef CONFIG_ARCH_USES_GETTIMEOFFSET offset = real_tk->cycle_interval; #else - offset = clocksource_delta(clock->read(clock), clock->cycle_last, + offset = clocksource_delta(clock->read(clock), tk->cycle_last, clock->mask); #endif @@ -1477,8 +1478,6 @@ void update_wall_time(void) clock_set |= accumulate_nsecs_to_secs(tk); write_seqcount_begin(&tk_core.seq); - /* Update clock->cycle_last with the new value */ - clock->cycle_last = tk->cycle_last; /* * Update the real timekeeper. * -- cgit v1.2.3 From 6d3aadf3e180e09dbefab16478c6876b584ce16e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jul 2014 21:05:15 +0000 Subject: timekeeping: Restructure the timekeeper some more Access to time requires to touch two cachelines at minimum 1) The timekeeper data structure 2) The clocksource data structure The access to the clocksource data structure can be avoided as almost all clocksource implementations ignore the argument to the read callback, which is a pointer to the clocksource. But the core needs to touch it to access the members @read and @mask. So we are better off by copying the @read function pointer and the @mask from the clocksource to the core data structure itself. For the most used ktime_get() access all required data including the @read and @mask copies fits together with the sequence counter into a single 64 byte cacheline. For the other time access functions we touch in the current code three cache lines in the worst case. But with the clocksource data copies we can reduce that to two adjacent cachelines, which is more efficient than disjunct cache lines. Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/timekeeper_internal.h | 4 ++++ kernel/time/timekeeping.c | 35 +++++++++++++++-------------------- 2 files changed, 19 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/linux/timekeeper_internal.h b/include/linux/timekeeper_internal.h index cb88096222c0..75bb8add78f5 100644 --- a/include/linux/timekeeper_internal.h +++ b/include/linux/timekeeper_internal.h @@ -29,6 +29,10 @@ struct timekeeper { /* Current clocksource used for timekeeping. */ struct clocksource *clock; + /* Read function of @clock */ + cycle_t (*read)(struct clocksource *cs); + /* Bitmask for two's complement subtraction of non 64bit counters */ + cycle_t mask; /* Last cycle value */ cycle_t cycle_last; /* NTP adjusted clock multiplier */ diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 4e748c404749..14b7367e6b94 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -121,7 +121,9 @@ static void tk_setup_internals(struct timekeeper *tk, struct clocksource *clock) old_clock = tk->clock; tk->clock = clock; - tk->cycle_last = clock->read(clock); + tk->read = clock->read; + tk->mask = clock->mask; + tk->cycle_last = tk->read(clock); /* Do the ns -> cycle conversion first, using original mult */ tmp = NTP_INTERVAL_LENGTH; @@ -174,15 +176,13 @@ static inline u32 arch_gettimeoffset(void) { return 0; } static inline s64 timekeeping_get_ns(struct timekeeper *tk) { cycle_t cycle_now, delta; - struct clocksource *clock; s64 nsec; /* read clocksource: */ - clock = tk->clock; - cycle_now = clock->read(clock); + cycle_now = tk->read(tk->clock); /* calculate the delta since the last update_wall_time: */ - delta = clocksource_delta(cycle_now, tk->cycle_last, clock->mask); + delta = clocksource_delta(cycle_now, tk->cycle_last, tk->mask); nsec = delta * tk->mult + tk->xtime_nsec; nsec >>= tk->shift; @@ -193,16 +193,15 @@ static inline s64 timekeeping_get_ns(struct timekeeper *tk) static inline s64 timekeeping_get_ns_raw(struct timekeeper *tk) { + struct clocksource *clock = tk->clock; cycle_t cycle_now, delta; - struct clocksource *clock; s64 nsec; /* read clocksource: */ - clock = tk->clock; - cycle_now = clock->read(clock); + cycle_now = tk->read(clock); /* calculate the delta since the last update_wall_time: */ - delta = clocksource_delta(cycle_now, tk->cycle_last, clock->mask); + delta = clocksource_delta(cycle_now, tk->cycle_last, tk->mask); /* convert delta to nanoseconds. */ nsec = clocksource_cyc2ns(delta, clock->mult, clock->shift); @@ -337,13 +336,12 @@ static void timekeeping_update(struct timekeeper *tk, unsigned int action) */ static void timekeeping_forward_now(struct timekeeper *tk) { + struct clocksource *clock = tk->clock; cycle_t cycle_now, delta; - struct clocksource *clock; s64 nsec; - clock = tk->clock; - cycle_now = clock->read(clock); - delta = clocksource_delta(cycle_now, tk->cycle_last, clock->mask); + cycle_now = tk->read(clock); + delta = clocksource_delta(cycle_now, tk->cycle_last, tk->mask); tk->cycle_last = cycle_now; tk->xtime_nsec += delta * tk->mult; @@ -1019,7 +1017,7 @@ static void timekeeping_resume(void) * The less preferred source will only be tried if there is no better * usable source. The rtc part is handled separately in rtc core code. */ - cycle_now = clock->read(clock); + cycle_now = tk->read(clock); if ((clock->flags & CLOCK_SOURCE_SUSPEND_NONSTOP) && cycle_now > tk->cycle_last) { u64 num, max = ULLONG_MAX; @@ -1028,7 +1026,7 @@ static void timekeeping_resume(void) s64 nsec = 0; cycle_delta = clocksource_delta(cycle_now, tk->cycle_last, - clock->mask); + tk->mask); /* * "cycle_delta * mutl" may cause 64 bits overflow, if the @@ -1415,7 +1413,6 @@ static cycle_t logarithmic_accumulation(struct timekeeper *tk, cycle_t offset, */ void update_wall_time(void) { - struct clocksource *clock; struct timekeeper *real_tk = &tk_core.timekeeper; struct timekeeper *tk = &shadow_timekeeper; cycle_t offset; @@ -1429,13 +1426,11 @@ void update_wall_time(void) if (unlikely(timekeeping_suspended)) goto out; - clock = real_tk->clock; - #ifdef CONFIG_ARCH_USES_GETTIMEOFFSET offset = real_tk->cycle_interval; #else - offset = clocksource_delta(clock->read(clock), tk->cycle_last, - clock->mask); + offset = clocksource_delta(tk->read(tk->clock), tk->cycle_last, + tk->mask); #endif /* Check if there's really nothing to do */ -- cgit v1.2.3 From d28ede83791defee9a81e558540699dc46dbbe13 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jul 2014 21:05:16 +0000 Subject: timekeeping: Create struct tk_read_base and use it in struct timekeeper The members of the new struct are the required ones for the new NMI safe accessor to clcok monotonic. In order to reuse the existing timekeeping code and to make the update of the fast NMI safe timekeepers a simple memcpy use the struct for the timekeeper as well and convert all users. Signed-off-by: Thomas Gleixner Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Mathieu Desnoyers Signed-off-by: John Stultz --- arch/arm64/kernel/vdso.c | 10 +-- arch/s390/kernel/time.c | 16 ++--- arch/tile/kernel/time.c | 10 +-- arch/x86/kernel/vsyscall_gtod.c | 23 ++++--- arch/x86/kvm/x86.c | 14 ++-- include/linux/timekeeper_internal.h | 103 +++++++++++++++------------- kernel/time/timekeeping.c | 132 ++++++++++++++++++------------------ 7 files changed, 158 insertions(+), 150 deletions(-) (limited to 'include') diff --git a/arch/arm64/kernel/vdso.c b/arch/arm64/kernel/vdso.c index 574672f001f7..8296f7f5f0ba 100644 --- a/arch/arm64/kernel/vdso.c +++ b/arch/arm64/kernel/vdso.c @@ -211,7 +211,7 @@ struct vm_area_struct *get_gate_vma(struct mm_struct *mm) void update_vsyscall(struct timekeeper *tk) { struct timespec xtime_coarse; - u32 use_syscall = strcmp(tk->clock->name, "arch_sys_counter"); + u32 use_syscall = strcmp(tk->tkr.clock->name, "arch_sys_counter"); ++vdso_data->tb_seq_count; smp_wmb(); @@ -224,11 +224,11 @@ void update_vsyscall(struct timekeeper *tk) vdso_data->wtm_clock_nsec = tk->wall_to_monotonic.tv_nsec; if (!use_syscall) { - vdso_data->cs_cycle_last = tk->cycle_last; + vdso_data->cs_cycle_last = tk->tkr.cycle_last; vdso_data->xtime_clock_sec = tk->xtime_sec; - vdso_data->xtime_clock_nsec = tk->xtime_nsec; - vdso_data->cs_mult = tk->mult; - vdso_data->cs_shift = tk->shift; + vdso_data->xtime_clock_nsec = tk->tkr.xtime_nsec; + vdso_data->cs_mult = tk->tkr.mult; + vdso_data->cs_shift = tk->tkr.shift; } smp_wmb(); diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index 97950f392613..4cef607f3711 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -214,26 +214,26 @@ void update_vsyscall(struct timekeeper *tk) { u64 nsecps; - if (tk->clock != &clocksource_tod) + if (tk->tkr.clock != &clocksource_tod) return; /* Make userspace gettimeofday spin until we're done. */ ++vdso_data->tb_update_count; smp_wmb(); - vdso_data->xtime_tod_stamp = tk->cycle_last; + vdso_data->xtime_tod_stamp = tk->tkr.cycle_last; vdso_data->xtime_clock_sec = tk->xtime_sec; - vdso_data->xtime_clock_nsec = tk->xtime_nsec; + vdso_data->xtime_clock_nsec = tk->tkr.xtime_nsec; vdso_data->wtom_clock_sec = tk->xtime_sec + tk->wall_to_monotonic.tv_sec; - vdso_data->wtom_clock_nsec = tk->xtime_nsec + - + ((u64) tk->wall_to_monotonic.tv_nsec << tk->shift); - nsecps = (u64) NSEC_PER_SEC << tk->shift; + vdso_data->wtom_clock_nsec = tk->tkr.xtime_nsec + + + ((u64) tk->wall_to_monotonic.tv_nsec << tk->tkr.shift); + nsecps = (u64) NSEC_PER_SEC << tk->tkr.shift; while (vdso_data->wtom_clock_nsec >= nsecps) { vdso_data->wtom_clock_nsec -= nsecps; vdso_data->wtom_clock_sec++; } - vdso_data->tk_mult = tk->mult; - vdso_data->tk_shift = tk->shift; + vdso_data->tk_mult = tk->tkr.mult; + vdso_data->tk_shift = tk->tkr.shift; smp_wmb(); ++vdso_data->tb_update_count; } diff --git a/arch/tile/kernel/time.c b/arch/tile/kernel/time.c index d22d5bfc1e4e..d8fbc289e680 100644 --- a/arch/tile/kernel/time.c +++ b/arch/tile/kernel/time.c @@ -261,7 +261,7 @@ void update_vsyscall_tz(void) void update_vsyscall(struct timekeeper *tk) { struct timespec *wtm = &tk->wall_to_monotonic; - struct clocksource *clock = tk->clock; + struct clocksource *clock = tk->tkr.clock; if (clock != &cycle_counter_cs) return; @@ -269,13 +269,13 @@ void update_vsyscall(struct timekeeper *tk) /* Userspace gettimeofday will spin while this value is odd. */ ++vdso_data->tb_update_count; smp_wmb(); - vdso_data->xtime_tod_stamp = tk->cycle_last; + vdso_data->xtime_tod_stamp = tk->tkr.cycle_last; vdso_data->xtime_clock_sec = tk->xtime_sec; - vdso_data->xtime_clock_nsec = tk->xtime_nsec; + vdso_data->xtime_clock_nsec = tk->tkr.xtime_nsec; vdso_data->wtom_clock_sec = wtm->tv_sec; vdso_data->wtom_clock_nsec = wtm->tv_nsec; - vdso_data->mult = tk->mult; - vdso_data->shift = tk->shift; + vdso_data->mult = tk->tkr.mult; + vdso_data->shift = tk->tkr.shift; smp_wmb(); ++vdso_data->tb_update_count; } diff --git a/arch/x86/kernel/vsyscall_gtod.c b/arch/x86/kernel/vsyscall_gtod.c index c3cb3c144591..c7d791f32b98 100644 --- a/arch/x86/kernel/vsyscall_gtod.c +++ b/arch/x86/kernel/vsyscall_gtod.c @@ -31,29 +31,30 @@ void update_vsyscall(struct timekeeper *tk) gtod_write_begin(vdata); /* copy vsyscall data */ - vdata->vclock_mode = tk->clock->archdata.vclock_mode; - vdata->cycle_last = tk->cycle_last; - vdata->mask = tk->clock->mask; - vdata->mult = tk->mult; - vdata->shift = tk->shift; + vdata->vclock_mode = tk->tkr.clock->archdata.vclock_mode; + vdata->cycle_last = tk->tkr.cycle_last; + vdata->mask = tk->tkr.mask; + vdata->mult = tk->tkr.mult; + vdata->shift = tk->tkr.shift; vdata->wall_time_sec = tk->xtime_sec; - vdata->wall_time_snsec = tk->xtime_nsec; + vdata->wall_time_snsec = tk->tkr.xtime_nsec; vdata->monotonic_time_sec = tk->xtime_sec + tk->wall_to_monotonic.tv_sec; - vdata->monotonic_time_snsec = tk->xtime_nsec + vdata->monotonic_time_snsec = tk->tkr.xtime_nsec + ((u64)tk->wall_to_monotonic.tv_nsec - << tk->shift); + << tk->tkr.shift); while (vdata->monotonic_time_snsec >= - (((u64)NSEC_PER_SEC) << tk->shift)) { + (((u64)NSEC_PER_SEC) << tk->tkr.shift)) { vdata->monotonic_time_snsec -= - ((u64)NSEC_PER_SEC) << tk->shift; + ((u64)NSEC_PER_SEC) << tk->tkr.shift; vdata->monotonic_time_sec++; } vdata->wall_time_coarse_sec = tk->xtime_sec; - vdata->wall_time_coarse_nsec = (long)(tk->xtime_nsec >> tk->shift); + vdata->wall_time_coarse_nsec = (long)(tk->tkr.xtime_nsec >> + tk->tkr.shift); vdata->monotonic_time_coarse_sec = vdata->wall_time_coarse_sec + tk->wall_to_monotonic.tv_sec; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 7b25125f3f42..b7e57946d1c1 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -995,19 +995,19 @@ static void update_pvclock_gtod(struct timekeeper *tk) struct pvclock_gtod_data *vdata = &pvclock_gtod_data; u64 boot_ns; - boot_ns = ktime_to_ns(ktime_add(tk->base_mono, tk->offs_boot)); + boot_ns = ktime_to_ns(ktime_add(tk->tkr.base_mono, tk->offs_boot)); write_seqcount_begin(&vdata->seq); /* copy pvclock gtod data */ - vdata->clock.vclock_mode = tk->clock->archdata.vclock_mode; - vdata->clock.cycle_last = tk->cycle_last; - vdata->clock.mask = tk->clock->mask; - vdata->clock.mult = tk->mult; - vdata->clock.shift = tk->shift; + vdata->clock.vclock_mode = tk->tkr.clock->archdata.vclock_mode; + vdata->clock.cycle_last = tk->tkr.cycle_last; + vdata->clock.mask = tk->tkr.mask; + vdata->clock.mult = tk->tkr.mult; + vdata->clock.shift = tk->tkr.shift; vdata->boot_ns = boot_ns; - vdata->nsec_base = tk->xtime_nsec; + vdata->nsec_base = tk->tkr.xtime_nsec; write_seqcount_end(&vdata->seq); } diff --git a/include/linux/timekeeper_internal.h b/include/linux/timekeeper_internal.h index 75bb8add78f5..97381997625b 100644 --- a/include/linux/timekeeper_internal.h +++ b/include/linux/timekeeper_internal.h @@ -10,80 +10,87 @@ #include #include -/* - * Structure holding internal timekeeping values. - * - * Note: wall_to_monotonic is what we need to add to xtime (or xtime - * corrected for sub jiffie times) to get to monotonic time. - * Monotonic is pegged at zero at system boot time, so - * wall_to_monotonic will be negative, however, we will ALWAYS keep - * the tv_nsec part positive so we can use the usual normalization. +/** + * struct tk_read_base - base structure for timekeeping readout + * @clock: Current clocksource used for timekeeping. + * @read: Read function of @clock + * @mask: Bitmask for two's complement subtraction of non 64bit clocks + * @cycle_last: @clock cycle value at last update + * @mult: NTP adjusted multiplier for scaled math conversion + * @shift: Shift value for scaled math conversion + * @xtime_nsec: Shifted (fractional) nano seconds offset for readout + * @base_mono: ktime_t (nanoseconds) base time for readout * - * wall_to_monotonic is moved after resume from suspend for the - * monotonic time not to jump. To calculate the real boot time offset - * we need to do offs_real - offs_boot. + * This struct has size 56 byte on 64 bit. Together with a seqcount it + * occupies a single 64byte cache line. * - * - wall_to_monotonic is no longer the boot time, getboottime must be - * used instead. + * The struct is separate from struct timekeeper as it is also used + * for a fast NMI safe accessor to clock monotonic. */ -struct timekeeper { - /* Current clocksource used for timekeeping. */ +struct tk_read_base { struct clocksource *clock; - /* Read function of @clock */ cycle_t (*read)(struct clocksource *cs); - /* Bitmask for two's complement subtraction of non 64bit counters */ cycle_t mask; - /* Last cycle value */ cycle_t cycle_last; - /* NTP adjusted clock multiplier */ u32 mult; - /* The shift value of the current clocksource. */ u32 shift; - /* Clock shifted nano seconds */ u64 xtime_nsec; - - /* Monotonic base time */ ktime_t base_mono; +}; - /* Current CLOCK_REALTIME time in seconds */ +/** + * struct timekeeper - Structure holding internal timekeeping values. + * @tkr: The readout base structure + * @xtime_sec: Current CLOCK_REALTIME time in seconds + * @wall_to_monotonic: CLOCK_REALTIME to CLOCK_MONOTONIC offset + * @offs_real: Offset clock monotonic -> clock realtime + * @offs_boot: Offset clock monotonic -> clock boottime + * @offs_tai: Offset clock monotonic -> clock tai + * @tai_offset: The current UTC to TAI offset in seconds + * @base_raw: Monotonic raw base time in ktime_t format + * @raw_time: Monotonic raw base time in timespec64 format + * @cycle_interval: Number of clock cycles in one NTP interval + * @xtime_interval: Number of clock shifted nano seconds in one NTP + * interval. + * @xtime_remainder: Shifted nano seconds left over when rounding + * @cycle_interval + * @raw_interval: Raw nano seconds accumulated per NTP interval. + * @ntp_error: Difference between accumulated time and NTP time in ntp + * shifted nano seconds. + * @ntp_error_shift: Shift conversion between clock shifted nano seconds and + * ntp shifted nano seconds. + * + * Note: For timespec(64) based interfaces wall_to_monotonic is what + * we need to add to xtime (or xtime corrected for sub jiffie times) + * to get to monotonic time. Monotonic is pegged at zero at system + * boot time, so wall_to_monotonic will be negative, however, we will + * ALWAYS keep the tv_nsec part positive so we can use the usual + * normalization. + * + * wall_to_monotonic is moved after resume from suspend for the + * monotonic time not to jump. We need to add total_sleep_time to + * wall_to_monotonic to get the real boot based time offset. + * + * wall_to_monotonic is no longer the boot time, getboottime must be + * used instead. + */ +struct timekeeper { + struct tk_read_base tkr; u64 xtime_sec; - /* CLOCK_REALTIME to CLOCK_MONOTONIC offset */ struct timespec64 wall_to_monotonic; - - /* Offset clock monotonic -> clock realtime */ ktime_t offs_real; - /* Offset clock monotonic -> clock boottime */ ktime_t offs_boot; - /* Offset clock monotonic -> clock tai */ ktime_t offs_tai; - - /* The current UTC to TAI offset in seconds */ s32 tai_offset; - - /* Monotonic raw base time */ ktime_t base_raw; - - /* The raw monotonic time for the CLOCK_MONOTONIC_RAW posix clock. */ struct timespec64 raw_time; - /* Number of clock cycles in one NTP interval. */ + /* The following members are for timekeeping internal use */ cycle_t cycle_interval; - /* Number of clock shifted nano seconds in one NTP interval. */ u64 xtime_interval; - /* shifted nano seconds left over when rounding cycle_interval */ s64 xtime_remainder; - /* Raw nano seconds accumulated per NTP interval. */ u32 raw_interval; - - /* - * Difference between accumulated time and NTP time in ntp - * shifted nano seconds. - */ s64 ntp_error; - /* - * Shift conversion between clock shifted nano seconds and - * ntp shifted nano seconds. - */ u32 ntp_error_shift; }; diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 14b7367e6b94..ccb69980ef7e 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -52,8 +52,8 @@ bool __read_mostly persistent_clock_exist = false; static inline void tk_normalize_xtime(struct timekeeper *tk) { - while (tk->xtime_nsec >= ((u64)NSEC_PER_SEC << tk->shift)) { - tk->xtime_nsec -= (u64)NSEC_PER_SEC << tk->shift; + while (tk->tkr.xtime_nsec >= ((u64)NSEC_PER_SEC << tk->tkr.shift)) { + tk->tkr.xtime_nsec -= (u64)NSEC_PER_SEC << tk->tkr.shift; tk->xtime_sec++; } } @@ -63,20 +63,20 @@ static inline struct timespec64 tk_xtime(struct timekeeper *tk) struct timespec64 ts; ts.tv_sec = tk->xtime_sec; - ts.tv_nsec = (long)(tk->xtime_nsec >> tk->shift); + ts.tv_nsec = (long)(tk->tkr.xtime_nsec >> tk->tkr.shift); return ts; } static void tk_set_xtime(struct timekeeper *tk, const struct timespec64 *ts) { tk->xtime_sec = ts->tv_sec; - tk->xtime_nsec = (u64)ts->tv_nsec << tk->shift; + tk->tkr.xtime_nsec = (u64)ts->tv_nsec << tk->tkr.shift; } static void tk_xtime_add(struct timekeeper *tk, const struct timespec64 *ts) { tk->xtime_sec += ts->tv_sec; - tk->xtime_nsec += (u64)ts->tv_nsec << tk->shift; + tk->tkr.xtime_nsec += (u64)ts->tv_nsec << tk->tkr.shift; tk_normalize_xtime(tk); } @@ -119,11 +119,11 @@ static void tk_setup_internals(struct timekeeper *tk, struct clocksource *clock) u64 tmp, ntpinterval; struct clocksource *old_clock; - old_clock = tk->clock; - tk->clock = clock; - tk->read = clock->read; - tk->mask = clock->mask; - tk->cycle_last = tk->read(clock); + old_clock = tk->tkr.clock; + tk->tkr.clock = clock; + tk->tkr.read = clock->read; + tk->tkr.mask = clock->mask; + tk->tkr.cycle_last = tk->tkr.read(clock); /* Do the ns -> cycle conversion first, using original mult */ tmp = NTP_INTERVAL_LENGTH; @@ -147,11 +147,11 @@ static void tk_setup_internals(struct timekeeper *tk, struct clocksource *clock) if (old_clock) { int shift_change = clock->shift - old_clock->shift; if (shift_change < 0) - tk->xtime_nsec >>= -shift_change; + tk->tkr.xtime_nsec >>= -shift_change; else - tk->xtime_nsec <<= shift_change; + tk->tkr.xtime_nsec <<= shift_change; } - tk->shift = clock->shift; + tk->tkr.shift = clock->shift; tk->ntp_error = 0; tk->ntp_error_shift = NTP_SCALE_SHIFT - clock->shift; @@ -161,7 +161,7 @@ static void tk_setup_internals(struct timekeeper *tk, struct clocksource *clock) * active clocksource. These value will be adjusted via NTP * to counteract clock drifting. */ - tk->mult = clock->mult; + tk->tkr.mult = clock->mult; } /* Timekeeper helper functions. */ @@ -179,13 +179,13 @@ static inline s64 timekeeping_get_ns(struct timekeeper *tk) s64 nsec; /* read clocksource: */ - cycle_now = tk->read(tk->clock); + cycle_now = tk->tkr.read(tk->tkr.clock); /* calculate the delta since the last update_wall_time: */ - delta = clocksource_delta(cycle_now, tk->cycle_last, tk->mask); + delta = clocksource_delta(cycle_now, tk->tkr.cycle_last, tk->tkr.mask); - nsec = delta * tk->mult + tk->xtime_nsec; - nsec >>= tk->shift; + nsec = delta * tk->tkr.mult + tk->tkr.xtime_nsec; + nsec >>= tk->tkr.shift; /* If arch requires, add in get_arch_timeoffset() */ return nsec + arch_gettimeoffset(); @@ -193,15 +193,15 @@ static inline s64 timekeeping_get_ns(struct timekeeper *tk) static inline s64 timekeeping_get_ns_raw(struct timekeeper *tk) { - struct clocksource *clock = tk->clock; + struct clocksource *clock = tk->tkr.clock; cycle_t cycle_now, delta; s64 nsec; /* read clocksource: */ - cycle_now = tk->read(clock); + cycle_now = tk->tkr.read(clock); /* calculate the delta since the last update_wall_time: */ - delta = clocksource_delta(cycle_now, tk->cycle_last, tk->mask); + delta = clocksource_delta(cycle_now, tk->tkr.cycle_last, tk->tkr.mask); /* convert delta to nanoseconds. */ nsec = clocksource_cyc2ns(delta, clock->mult, clock->shift); @@ -217,8 +217,8 @@ static inline void update_vsyscall(struct timekeeper *tk) struct timespec xt; xt = tk_xtime(tk); - update_vsyscall_old(&xt, &tk->wall_to_monotonic, tk->clock, tk->mult, - tk->cycle_last); + update_vsyscall_old(&xt, &tk->wall_to_monotonic, tk->tkr.clock, tk->tkr.mult, + tk->tkr.cycle_last); } static inline void old_vsyscall_fixup(struct timekeeper *tk) @@ -235,11 +235,11 @@ static inline void old_vsyscall_fixup(struct timekeeper *tk) * (shifted nanoseconds), and CONFIG_GENERIC_TIME_VSYSCALL_OLD * users are removed, this can be killed. */ - remainder = tk->xtime_nsec & ((1ULL << tk->shift) - 1); - tk->xtime_nsec -= remainder; - tk->xtime_nsec += 1ULL << tk->shift; + remainder = tk->tkr.xtime_nsec & ((1ULL << tk->tkr.shift) - 1); + tk->tkr.xtime_nsec -= remainder; + tk->tkr.xtime_nsec += 1ULL << tk->tkr.shift; tk->ntp_error += remainder << tk->ntp_error_shift; - tk->ntp_error -= (1ULL << tk->shift) << tk->ntp_error_shift; + tk->ntp_error -= (1ULL << tk->tkr.shift) << tk->ntp_error_shift; } #else #define old_vsyscall_fixup(tk) @@ -304,7 +304,7 @@ static inline void tk_update_ktime_data(struct timekeeper *tk) nsec = (s64)(tk->xtime_sec + tk->wall_to_monotonic.tv_sec); nsec *= NSEC_PER_SEC; nsec += tk->wall_to_monotonic.tv_nsec; - tk->base_mono = ns_to_ktime(nsec); + tk->tkr.base_mono = ns_to_ktime(nsec); /* Update the monotonic raw base */ tk->base_raw = timespec64_to_ktime(tk->raw_time); @@ -336,18 +336,18 @@ static void timekeeping_update(struct timekeeper *tk, unsigned int action) */ static void timekeeping_forward_now(struct timekeeper *tk) { - struct clocksource *clock = tk->clock; + struct clocksource *clock = tk->tkr.clock; cycle_t cycle_now, delta; s64 nsec; - cycle_now = tk->read(clock); - delta = clocksource_delta(cycle_now, tk->cycle_last, tk->mask); - tk->cycle_last = cycle_now; + cycle_now = tk->tkr.read(clock); + delta = clocksource_delta(cycle_now, tk->tkr.cycle_last, tk->tkr.mask); + tk->tkr.cycle_last = cycle_now; - tk->xtime_nsec += delta * tk->mult; + tk->tkr.xtime_nsec += delta * tk->tkr.mult; /* If arch requires, add in get_arch_timeoffset() */ - tk->xtime_nsec += (u64)arch_gettimeoffset() << tk->shift; + tk->tkr.xtime_nsec += (u64)arch_gettimeoffset() << tk->tkr.shift; tk_normalize_xtime(tk); @@ -412,7 +412,7 @@ ktime_t ktime_get(void) do { seq = read_seqcount_begin(&tk_core.seq); - base = tk->base_mono; + base = tk->tkr.base_mono; nsecs = timekeeping_get_ns(tk); } while (read_seqcount_retry(&tk_core.seq, seq)); @@ -438,7 +438,7 @@ ktime_t ktime_get_with_offset(enum tk_offsets offs) do { seq = read_seqcount_begin(&tk_core.seq); - base = ktime_add(tk->base_mono, *offset); + base = ktime_add(tk->tkr.base_mono, *offset); nsecs = timekeeping_get_ns(tk); } while (read_seqcount_retry(&tk_core.seq, seq)); @@ -731,7 +731,7 @@ static int change_clocksource(void *data) */ if (try_module_get(new->owner)) { if (!new->enable || new->enable(new) == 0) { - old = tk->clock; + old = tk->tkr.clock; tk_setup_internals(tk, new); if (old->disable) old->disable(old); @@ -759,11 +759,11 @@ int timekeeping_notify(struct clocksource *clock) { struct timekeeper *tk = &tk_core.timekeeper; - if (tk->clock == clock) + if (tk->tkr.clock == clock) return 0; stop_machine(change_clocksource, clock, NULL); tick_clock_notify(); - return tk->clock == clock ? 0 : -1; + return tk->tkr.clock == clock ? 0 : -1; } /** @@ -803,7 +803,7 @@ int timekeeping_valid_for_hres(void) do { seq = read_seqcount_begin(&tk_core.seq); - ret = tk->clock->flags & CLOCK_SOURCE_VALID_FOR_HRES; + ret = tk->tkr.clock->flags & CLOCK_SOURCE_VALID_FOR_HRES; } while (read_seqcount_retry(&tk_core.seq, seq)); @@ -822,7 +822,7 @@ u64 timekeeping_max_deferment(void) do { seq = read_seqcount_begin(&tk_core.seq); - ret = tk->clock->max_idle_ns; + ret = tk->tkr.clock->max_idle_ns; } while (read_seqcount_retry(&tk_core.seq, seq)); @@ -989,7 +989,7 @@ void timekeeping_inject_sleeptime(struct timespec *delta) static void timekeeping_resume(void) { struct timekeeper *tk = &tk_core.timekeeper; - struct clocksource *clock = tk->clock; + struct clocksource *clock = tk->tkr.clock; unsigned long flags; struct timespec64 ts_new, ts_delta; struct timespec tmp; @@ -1017,16 +1017,16 @@ static void timekeeping_resume(void) * The less preferred source will only be tried if there is no better * usable source. The rtc part is handled separately in rtc core code. */ - cycle_now = tk->read(clock); + cycle_now = tk->tkr.read(clock); if ((clock->flags & CLOCK_SOURCE_SUSPEND_NONSTOP) && - cycle_now > tk->cycle_last) { + cycle_now > tk->tkr.cycle_last) { u64 num, max = ULLONG_MAX; u32 mult = clock->mult; u32 shift = clock->shift; s64 nsec = 0; - cycle_delta = clocksource_delta(cycle_now, tk->cycle_last, - tk->mask); + cycle_delta = clocksource_delta(cycle_now, tk->tkr.cycle_last, + tk->tkr.mask); /* * "cycle_delta * mutl" may cause 64 bits overflow, if the @@ -1052,7 +1052,7 @@ static void timekeeping_resume(void) __timekeeping_inject_sleeptime(tk, &ts_delta); /* Re-base the last cycle value */ - tk->cycle_last = cycle_now; + tk->tkr.cycle_last = cycle_now; tk->ntp_error = 0; timekeeping_suspended = 0; timekeeping_update(tk, TK_MIRROR | TK_CLOCK_WAS_SET); @@ -1239,12 +1239,12 @@ static void timekeeping_adjust(struct timekeeper *tk, s64 offset) } } - if (unlikely(tk->clock->maxadj && - (tk->mult + adj > tk->clock->mult + tk->clock->maxadj))) { + if (unlikely(tk->tkr.clock->maxadj && + (tk->tkr.mult + adj > tk->tkr.clock->mult + tk->tkr.clock->maxadj))) { printk_deferred_once(KERN_WARNING "Adjusting %s more than 11%% (%ld vs %ld)\n", - tk->clock->name, (long)tk->mult + adj, - (long)tk->clock->mult + tk->clock->maxadj); + tk->tkr.clock->name, (long)tk->tkr.mult + adj, + (long)tk->tkr.clock->mult + tk->tkr.clock->maxadj); } /* * So the following can be confusing. @@ -1295,9 +1295,9 @@ static void timekeeping_adjust(struct timekeeper *tk, s64 offset) * * XXX - TODO: Doc ntp_error calculation. */ - tk->mult += adj; + tk->tkr.mult += adj; tk->xtime_interval += interval; - tk->xtime_nsec -= offset; + tk->tkr.xtime_nsec -= offset; tk->ntp_error -= (interval - offset) << tk->ntp_error_shift; out_adjust: @@ -1315,9 +1315,9 @@ out_adjust: * We'll correct this error next time through this function, when * xtime_nsec is not as small. */ - if (unlikely((s64)tk->xtime_nsec < 0)) { - s64 neg = -(s64)tk->xtime_nsec; - tk->xtime_nsec = 0; + if (unlikely((s64)tk->tkr.xtime_nsec < 0)) { + s64 neg = -(s64)tk->tkr.xtime_nsec; + tk->tkr.xtime_nsec = 0; tk->ntp_error += neg << tk->ntp_error_shift; } @@ -1333,13 +1333,13 @@ out_adjust: */ static inline unsigned int accumulate_nsecs_to_secs(struct timekeeper *tk) { - u64 nsecps = (u64)NSEC_PER_SEC << tk->shift; + u64 nsecps = (u64)NSEC_PER_SEC << tk->tkr.shift; unsigned int clock_set = 0; - while (tk->xtime_nsec >= nsecps) { + while (tk->tkr.xtime_nsec >= nsecps) { int leap; - tk->xtime_nsec -= nsecps; + tk->tkr.xtime_nsec -= nsecps; tk->xtime_sec++; /* Figure out if its a leap sec and apply if needed */ @@ -1384,9 +1384,9 @@ static cycle_t logarithmic_accumulation(struct timekeeper *tk, cycle_t offset, /* Accumulate one shifted interval */ offset -= interval; - tk->cycle_last += interval; + tk->tkr.cycle_last += interval; - tk->xtime_nsec += tk->xtime_interval << shift; + tk->tkr.xtime_nsec += tk->xtime_interval << shift; *clock_set |= accumulate_nsecs_to_secs(tk); /* Accumulate raw time */ @@ -1429,8 +1429,8 @@ void update_wall_time(void) #ifdef CONFIG_ARCH_USES_GETTIMEOFFSET offset = real_tk->cycle_interval; #else - offset = clocksource_delta(tk->read(tk->clock), tk->cycle_last, - tk->mask); + offset = clocksource_delta(tk->tkr.read(tk->tkr.clock), + tk->tkr.cycle_last, tk->tkr.mask); #endif /* Check if there's really nothing to do */ @@ -1591,8 +1591,8 @@ ktime_t ktime_get_update_offsets_tick(ktime_t *offs_real, ktime_t *offs_boot, do { seq = read_seqcount_begin(&tk_core.seq); - base = tk->base_mono; - nsecs = tk->xtime_nsec >> tk->shift; + base = tk->tkr.base_mono; + nsecs = tk->tkr.xtime_nsec >> tk->tkr.shift; *offs_real = tk->offs_real; *offs_boot = tk->offs_boot; @@ -1623,7 +1623,7 @@ ktime_t ktime_get_update_offsets_now(ktime_t *offs_real, ktime_t *offs_boot, do { seq = read_seqcount_begin(&tk_core.seq); - base = tk->base_mono; + base = tk->tkr.base_mono; nsecs = timekeeping_get_ns(tk); *offs_real = tk->offs_real; -- cgit v1.2.3 From 0ea5a520f73ca31abc4c10b6d5bc14a884a0641b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jul 2014 21:05:20 +0000 Subject: seqcount: Provide raw_read_seqcount() raw_read_seqcount opens a read critical section of the given seqcount without any lockdep checking and without checking or masking the LSB. Calling code is responsible for handling that. Preparatory patch to provide a NMI safe clock monotonic accessor function. Signed-off-by: Thomas Gleixner Cc: John Stultz Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Mathieu Desnoyers Signed-off-by: John Stultz --- include/linux/seqlock.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'include') diff --git a/include/linux/seqlock.h b/include/linux/seqlock.h index 535f158977b9..dcc64b9bfc41 100644 --- a/include/linux/seqlock.h +++ b/include/linux/seqlock.h @@ -116,6 +116,22 @@ repeat: return ret; } +/** + * raw_read_seqcount - Read the raw seqcount + * @s: pointer to seqcount_t + * Returns: count to be passed to read_seqcount_retry + * + * raw_read_seqcount opens a read critical section of the given + * seqcount without any lockdep checking and without checking or + * masking the LSB. Calling code is responsible for handling that. + */ +static inline unsigned raw_read_seqcount(const seqcount_t *s) +{ + unsigned ret = ACCESS_ONCE(s->sequence); + smp_rmb(); + return ret; +} + /** * raw_read_seqcount_begin - start seq-read critical section w/o lockdep * @s: pointer to seqcount_t -- cgit v1.2.3 From 9b0fd802e8c0545148324916055e7b40d97963fa Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Wed, 16 Jul 2014 21:05:21 +0000 Subject: seqcount: Add raw_write_seqcount_latch() For NMI safe access to clock monotonic we use the seqcount LSB as index of a timekeeper array. The update sequence looks like this: smp_wmb(); <- prior stores to a[1] seq++; smp_wmb(); <- seq increment before update of a[0] update(a[0]); smp_wmb(); <- update of a[0] seq++; smp_wmb(); <- seq increment before update of a[1] update(a[1]); To avoid open coded barriers, provide a helper function. [ tglx: Split out of a combo patch against the first implementation of the NMI safe accessor ] Signed-off-by: Mathieu Desnoyers Cc: John Stultz Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Steven Rostedt Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- include/linux/seqlock.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'include') diff --git a/include/linux/seqlock.h b/include/linux/seqlock.h index dcc64b9bfc41..cce6e7453592 100644 --- a/include/linux/seqlock.h +++ b/include/linux/seqlock.h @@ -235,6 +235,17 @@ static inline void raw_write_seqcount_end(seqcount_t *s) s->sequence++; } +/* + * raw_write_seqcount_latch - redirect readers to even/odd copy + * @s: pointer to seqcount_t + */ +static inline void raw_write_seqcount_latch(seqcount_t *s) +{ + smp_wmb(); /* prior stores before incrementing "sequence" */ + s->sequence++; + smp_wmb(); /* increment "sequence" before following stores */ +} + /* * Sequence counter only version assumes that callers are using their * own mutexing. -- cgit v1.2.3 From 4396e058c52e167729729cf64ea3dfa229637086 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 16 Jul 2014 21:05:23 +0000 Subject: timekeeping: Provide fast and NMI safe access to CLOCK_MONOTONIC Tracers want a correlated time between the kernel instrumentation and user space. We really do not want to export sched_clock() to user space, so we need to provide something sensible for this. Using separate data structures with an non blocking sequence count based update mechanism allows us to do that. The data structure required for the readout has a sequence counter and two copies of the timekeeping data. On the update side: smp_wmb(); tkf->seq++; smp_wmb(); update(tkf->base[0], tk); smp_wmb(); tkf->seq++; smp_wmb(); update(tkf->base[1], tk); On the reader side: do { seq = tkf->seq; smp_rmb(); idx = seq & 0x01; now = now(tkf->base[idx]); smp_rmb(); } while (seq != tkf->seq) So if a NMI hits the update of base[0] it will use base[1] which is still consistent, but this timestamp is not guaranteed to be monotonic across an update. The timestamp is calculated by: now = base_mono + clock_delta * slope So if the update lowers the slope, readers who are forced to the not yet updated second array are still using the old steeper slope. tmono ^ | o n | o n | u | o |o |12345678---> reader order o = old slope u = update n = new slope So reader 6 will observe time going backwards versus reader 5. While other CPUs are likely to be able observe that, the only way for a CPU local observation is when an NMI hits in the middle of the update. Timestamps taken from that NMI context might be ahead of the following timestamps. Callers need to be aware of that and deal with it. V2: Got rid of clock monotonic raw and reorganized the data structures. Folded in the barrier fix from Mathieu. Signed-off-by: Thomas Gleixner Cc: Peter Zijlstra Cc: Steven Rostedt Cc: Mathieu Desnoyers Signed-off-by: John Stultz --- include/linux/timekeeping.h | 2 + kernel/time/timekeeping.c | 124 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) (limited to 'include') diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h index 58ad7eff83ff..1caa6b04fdc5 100644 --- a/include/linux/timekeeping.h +++ b/include/linux/timekeeping.h @@ -164,6 +164,8 @@ static inline u64 ktime_get_raw_ns(void) return ktime_to_ns(ktime_get_raw()); } +extern u64 ktime_get_mono_fast_ns(void); + /* * Timespec interfaces utilizing the ktime based ones */ diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index dee23c9d6c21..8980fb722fc5 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -44,6 +44,22 @@ static struct { static DEFINE_RAW_SPINLOCK(timekeeper_lock); static struct timekeeper shadow_timekeeper; +/** + * struct tk_fast - NMI safe timekeeper + * @seq: Sequence counter for protecting updates. The lowest bit + * is the index for the tk_read_base array + * @base: tk_read_base array. Access is indexed by the lowest bit of + * @seq. + * + * See @update_fast_timekeeper() below. + */ +struct tk_fast { + seqcount_t seq; + struct tk_read_base base[2]; +}; + +static struct tk_fast tk_fast_mono ____cacheline_aligned; + /* flag for if timekeeping is suspended */ int __read_mostly timekeeping_suspended; @@ -210,6 +226,112 @@ static inline s64 timekeeping_get_ns_raw(struct timekeeper *tk) return nsec + arch_gettimeoffset(); } +/** + * update_fast_timekeeper - Update the fast and NMI safe monotonic timekeeper. + * @tk: The timekeeper from which we take the update + * @tkf: The fast timekeeper to update + * @tbase: The time base for the fast timekeeper (mono/raw) + * + * We want to use this from any context including NMI and tracing / + * instrumenting the timekeeping code itself. + * + * So we handle this differently than the other timekeeping accessor + * functions which retry when the sequence count has changed. The + * update side does: + * + * smp_wmb(); <- Ensure that the last base[1] update is visible + * tkf->seq++; + * smp_wmb(); <- Ensure that the seqcount update is visible + * update(tkf->base[0], tk); + * smp_wmb(); <- Ensure that the base[0] update is visible + * tkf->seq++; + * smp_wmb(); <- Ensure that the seqcount update is visible + * update(tkf->base[1], tk); + * + * The reader side does: + * + * do { + * seq = tkf->seq; + * smp_rmb(); + * idx = seq & 0x01; + * now = now(tkf->base[idx]); + * smp_rmb(); + * } while (seq != tkf->seq) + * + * As long as we update base[0] readers are forced off to + * base[1]. Once base[0] is updated readers are redirected to base[0] + * and the base[1] update takes place. + * + * So if a NMI hits the update of base[0] then it will use base[1] + * which is still consistent. In the worst case this can result is a + * slightly wrong timestamp (a few nanoseconds). See + * @ktime_get_mono_fast_ns. + */ +static void update_fast_timekeeper(struct timekeeper *tk) +{ + struct tk_read_base *base = tk_fast_mono.base; + + /* Force readers off to base[1] */ + raw_write_seqcount_latch(&tk_fast_mono.seq); + + /* Update base[0] */ + memcpy(base, &tk->tkr, sizeof(*base)); + + /* Force readers back to base[0] */ + raw_write_seqcount_latch(&tk_fast_mono.seq); + + /* Update base[1] */ + memcpy(base + 1, base, sizeof(*base)); +} + +/** + * ktime_get_mono_fast_ns - Fast NMI safe access to clock monotonic + * + * This timestamp is not guaranteed to be monotonic across an update. + * The timestamp is calculated by: + * + * now = base_mono + clock_delta * slope + * + * So if the update lowers the slope, readers who are forced to the + * not yet updated second array are still using the old steeper slope. + * + * tmono + * ^ + * | o n + * | o n + * | u + * | o + * |o + * |12345678---> reader order + * + * o = old slope + * u = update + * n = new slope + * + * So reader 6 will observe time going backwards versus reader 5. + * + * While other CPUs are likely to be able observe that, the only way + * for a CPU local observation is when an NMI hits in the middle of + * the update. Timestamps taken from that NMI context might be ahead + * of the following timestamps. Callers need to be aware of that and + * deal with it. + */ +u64 notrace ktime_get_mono_fast_ns(void) +{ + struct tk_read_base *tkr; + unsigned int seq; + u64 now; + + do { + seq = raw_read_seqcount(&tk_fast_mono.seq); + tkr = tk_fast_mono.base + (seq & 0x01); + now = ktime_to_ns(tkr->base_mono) + timekeeping_get_ns(tkr); + + } while (read_seqcount_retry(&tk_fast_mono.seq, seq)); + return now; +} +EXPORT_SYMBOL_GPL(ktime_get_mono_fast_ns); + #ifdef CONFIG_GENERIC_TIME_VSYSCALL_OLD static inline void update_vsyscall(struct timekeeper *tk) @@ -325,6 +447,8 @@ static void timekeeping_update(struct timekeeper *tk, unsigned int action) if (action & TK_MIRROR) memcpy(&shadow_timekeeper, &tk_core.timekeeper, sizeof(tk_core.timekeeper)); + + update_fast_timekeeper(tk); } /** -- cgit v1.2.3 From dc491596f6394382fbc74ad331156207d619fa0a Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 6 Dec 2013 17:25:21 -0800 Subject: timekeeping: Rework frequency adjustments to work better w/ nohz The existing timekeeping_adjust logic has always been complicated to understand. Further, since it was developed prior to NOHZ becoming common, its not surprising it performs poorly when NOHZ is enabled. Since Miroslav pointed out the problematic nature of the existing code in the NOHZ case, I've tried to refactor the code to perform better. The problem with the previous approach was that it tried to adjust for the total cumulative error using a scaled dampening factor. This resulted in large errors to be corrected slowly, while small errors were corrected quickly. With NOHZ the timekeeping code doesn't know how far out the next tick will be, so this results in bad over-correction to small errors, and insufficient correction to large errors. Inspired by Miroslav's patch, I've refactored the code to try to address the correction in two steps. 1) Check the future freq error for the next tick, and if the frequency error is large, try to make sure we correct it so it doesn't cause much accumulated error. 2) Then make a small single unit adjustment to correct any cumulative error that has collected over time. This method performs fairly well in the simulator Miroslav created. Major credit to Miroslav for pointing out the issue, providing the original patch to resolve this, a simulator for testing, as well as helping debug and resolve issues in my implementation so that it performed closer to his original implementation. Cc: Miroslav Lichvar Cc: Richard Cochran Cc: Prarit Bhargava Reported-by: Miroslav Lichvar Signed-off-by: John Stultz --- include/linux/timekeeper_internal.h | 1 + kernel/time/timekeeping.c | 193 ++++++++++++++++-------------------- 2 files changed, 84 insertions(+), 110 deletions(-) (limited to 'include') diff --git a/include/linux/timekeeper_internal.h b/include/linux/timekeeper_internal.h index 97381997625b..f7ac48d2edf5 100644 --- a/include/linux/timekeeper_internal.h +++ b/include/linux/timekeeper_internal.h @@ -92,6 +92,7 @@ struct timekeeper { u32 raw_interval; s64 ntp_error; u32 ntp_error_shift; + u32 ntp_err_mult; }; #ifdef CONFIG_GENERIC_TIME_VSYSCALL diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 2b56b959615b..43c706a7a728 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -178,6 +178,7 @@ static void tk_setup_internals(struct timekeeper *tk, struct clocksource *clock) * to counteract clock drifting. */ tk->tkr.mult = clock->mult; + tk->ntp_err_mult = 0; } /* Timekeeper helper functions. */ @@ -1257,125 +1258,34 @@ static int __init timekeeping_init_ops(void) register_syscore_ops(&timekeeping_syscore_ops); return 0; } - device_initcall(timekeeping_init_ops); /* - * If the error is already larger, we look ahead even further - * to compensate for late or lost adjustments. - */ -static __always_inline int timekeeping_bigadjust(struct timekeeper *tk, - s64 error, s64 *interval, - s64 *offset) -{ - s64 tick_error, i; - u32 look_ahead, adj; - s32 error2, mult; - - /* - * Use the current error value to determine how much to look ahead. - * The larger the error the slower we adjust for it to avoid problems - * with losing too many ticks, otherwise we would overadjust and - * produce an even larger error. The smaller the adjustment the - * faster we try to adjust for it, as lost ticks can do less harm - * here. This is tuned so that an error of about 1 msec is adjusted - * within about 1 sec (or 2^20 nsec in 2^SHIFT_HZ ticks). - */ - error2 = tk->ntp_error >> (NTP_SCALE_SHIFT + 22 - 2 * SHIFT_HZ); - error2 = abs(error2); - for (look_ahead = 0; error2 > 0; look_ahead++) - error2 >>= 2; - - /* - * Now calculate the error in (1 << look_ahead) ticks, but first - * remove the single look ahead already included in the error. - */ - tick_error = ntp_tick_length() >> (tk->ntp_error_shift + 1); - tick_error -= tk->xtime_interval >> 1; - error = ((error - tick_error) >> look_ahead) + tick_error; - - /* Finally calculate the adjustment shift value. */ - i = *interval; - mult = 1; - if (error < 0) { - error = -error; - *interval = -*interval; - *offset = -*offset; - mult = -1; - } - for (adj = 0; error > i; adj++) - error >>= 1; - - *interval <<= adj; - *offset <<= adj; - return mult << adj; -} - -/* - * Adjust the multiplier to reduce the error value, - * this is optimized for the most common adjustments of -1,0,1, - * for other values we can do a bit more work. + * Apply a multiplier adjustment to the timekeeper */ -static void timekeeping_adjust(struct timekeeper *tk, s64 offset) +static __always_inline void timekeeping_apply_adjustment(struct timekeeper *tk, + s64 offset, + bool negative, + int adj_scale) { - s64 error, interval = tk->cycle_interval; - int adj; + s64 interval = tk->cycle_interval; + s32 mult_adj = 1; - /* - * The point of this is to check if the error is greater than half - * an interval. - * - * First we shift it down from NTP_SHIFT to clocksource->shifted nsecs. - * - * Note we subtract one in the shift, so that error is really error*2. - * This "saves" dividing(shifting) interval twice, but keeps the - * (error > interval) comparison as still measuring if error is - * larger than half an interval. - * - * Note: It does not "save" on aggravation when reading the code. - */ - error = tk->ntp_error >> (tk->ntp_error_shift - 1); - if (error > interval) { - /* - * We now divide error by 4(via shift), which checks if - * the error is greater than twice the interval. - * If it is greater, we need a bigadjust, if its smaller, - * we can adjust by 1. - */ - error >>= 2; - if (likely(error <= interval)) - adj = 1; - else - adj = timekeeping_bigadjust(tk, error, &interval, &offset); - } else { - if (error < -interval) { - /* See comment above, this is just switched for the negative */ - error >>= 2; - if (likely(error >= -interval)) { - adj = -1; - interval = -interval; - offset = -offset; - } else { - adj = timekeeping_bigadjust(tk, error, &interval, &offset); - } - } else { - goto out_adjust; - } + if (negative) { + mult_adj = -mult_adj; + interval = -interval; + offset = -offset; } + mult_adj <<= adj_scale; + interval <<= adj_scale; + offset <<= adj_scale; - if (unlikely(tk->tkr.clock->maxadj && - (tk->tkr.mult + adj > tk->tkr.clock->mult + tk->tkr.clock->maxadj))) { - printk_deferred_once(KERN_WARNING - "Adjusting %s more than 11%% (%ld vs %ld)\n", - tk->tkr.clock->name, (long)tk->tkr.mult + adj, - (long)tk->tkr.clock->mult + tk->tkr.clock->maxadj); - } /* * So the following can be confusing. * - * To keep things simple, lets assume adj == 1 for now. + * To keep things simple, lets assume mult_adj == 1 for now. * - * When adj != 1, remember that the interval and offset values + * When mult_adj != 1, remember that the interval and offset values * have been appropriately scaled so the math is the same. * * The basic idea here is that we're increasing the multiplier @@ -1419,12 +1329,76 @@ static void timekeeping_adjust(struct timekeeper *tk, s64 offset) * * XXX - TODO: Doc ntp_error calculation. */ - tk->tkr.mult += adj; + tk->tkr.mult += mult_adj; tk->xtime_interval += interval; tk->tkr.xtime_nsec -= offset; tk->ntp_error -= (interval - offset) << tk->ntp_error_shift; +} + +/* + * Calculate the multiplier adjustment needed to match the frequency + * specified by NTP + */ +static __always_inline void timekeeping_freqadjust(struct timekeeper *tk, + s64 offset) +{ + s64 interval = tk->cycle_interval; + s64 xinterval = tk->xtime_interval; + s64 tick_error; + bool negative; + u32 adj; + + /* Remove any current error adj from freq calculation */ + if (tk->ntp_err_mult) + xinterval -= tk->cycle_interval; + + /* Calculate current error per tick */ + tick_error = ntp_tick_length() >> tk->ntp_error_shift; + tick_error -= (xinterval + tk->xtime_remainder); + + /* Don't worry about correcting it if its small */ + if (likely((tick_error >= 0) && (tick_error <= interval))) + return; + + /* preserve the direction of correction */ + negative = (tick_error < 0); + + /* Sort out the magnitude of the correction */ + tick_error = abs(tick_error); + for (adj = 0; tick_error > interval; adj++) + tick_error >>= 1; + + /* scale the corrections */ + timekeeping_apply_adjustment(tk, offset, negative, adj); +} + +/* + * Adjust the timekeeper's multiplier to the correct frequency + * and also to reduce the accumulated error value. + */ +static void timekeeping_adjust(struct timekeeper *tk, s64 offset) +{ + /* Correct for the current frequency error */ + timekeeping_freqadjust(tk, offset); + + /* Next make a small adjustment to fix any cumulative error */ + if (!tk->ntp_err_mult && (tk->ntp_error > 0)) { + tk->ntp_err_mult = 1; + timekeeping_apply_adjustment(tk, offset, 0, 0); + } else if (tk->ntp_err_mult && (tk->ntp_error <= 0)) { + /* Undo any existing error adjustment */ + timekeeping_apply_adjustment(tk, offset, 1, 0); + tk->ntp_err_mult = 0; + } + + if (unlikely(tk->tkr.clock->maxadj && + (tk->tkr.mult > tk->tkr.clock->mult + tk->tkr.clock->maxadj))) { + printk_once(KERN_WARNING + "Adjusting %s more than 11%% (%ld vs %ld)\n", + tk->tkr.clock->name, (long)tk->tkr.mult, + (long)tk->tkr.clock->mult + tk->tkr.clock->maxadj); + } -out_adjust: /* * It may be possible that when we entered this function, xtime_nsec * was very small. Further, if we're slightly speeding the clocksource @@ -1444,7 +1418,6 @@ out_adjust: tk->tkr.xtime_nsec = 0; tk->ntp_error += neg << tk->ntp_error_shift; } - } /** -- cgit v1.2.3 From 375f45b5b53a91dfa8f0c11328e0e044f82acbed Mon Sep 17 00:00:00 2001 From: John Stultz Date: Wed, 23 Apr 2014 20:53:29 -0700 Subject: timekeeping: Use cached ntp_tick_length when accumulating error By caching the ntp_tick_length() when we correct the frequency error, and then using that cached value to accumulate error, we avoid large initial errors when the tick length is changed. This makes convergence happen much faster in the simulator, since the initial error doesn't have to be slowly whittled away. This initially seems like an accounting error, but Miroslav pointed out that ntp_tick_length() can change mid-tick, so when we apply it in the error accumulation, we are applying any recent change to the entire tick. This approach chooses to apply changes in the ntp_tick_length() only to the next tick, which allows us to calculate the freq correction before using the new tick length, which avoids accummulating error. Credit to Miroslav for pointing this out and providing the original patch this functionality has been pulled out from, along with the rational. Cc: Miroslav Lichvar Cc: Richard Cochran Cc: Prarit Bhargava Reported-by: Miroslav Lichvar Signed-off-by: John Stultz --- include/linux/timekeeper_internal.h | 9 +++++++++ kernel/time/timekeeping.c | 5 ++++- 2 files changed, 13 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/timekeeper_internal.h b/include/linux/timekeeper_internal.h index f7ac48d2edf5..e9660e52dc09 100644 --- a/include/linux/timekeeper_internal.h +++ b/include/linux/timekeeper_internal.h @@ -90,6 +90,15 @@ struct timekeeper { u64 xtime_interval; s64 xtime_remainder; u32 raw_interval; + /* The ntp_tick_length() value currently being used. + * This cached copy ensures we consistently apply the tick + * length for an entire tick, as ntp_tick_length may change + * mid-tick, and we don't want to apply that new value to + * the tick in progress. + */ + u64 ntp_tick; + /* Difference between accumulated time and NTP time in ntp + * shifted nano seconds. */ s64 ntp_error; u32 ntp_error_shift; u32 ntp_err_mult; diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 43c706a7a728..f36b02838a47 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -171,6 +171,7 @@ static void tk_setup_internals(struct timekeeper *tk, struct clocksource *clock) tk->ntp_error = 0; tk->ntp_error_shift = NTP_SCALE_SHIFT - clock->shift; + tk->ntp_tick = ntpinterval << tk->ntp_error_shift; /* * The timekeeper keeps its own mult values for the currently @@ -1352,6 +1353,8 @@ static __always_inline void timekeeping_freqadjust(struct timekeeper *tk, if (tk->ntp_err_mult) xinterval -= tk->cycle_interval; + tk->ntp_tick = ntp_tick_length(); + /* Calculate current error per tick */ tick_error = ntp_tick_length() >> tk->ntp_error_shift; tick_error -= (xinterval + tk->xtime_remainder); @@ -1497,7 +1500,7 @@ static cycle_t logarithmic_accumulation(struct timekeeper *tk, cycle_t offset, tk->raw_time.tv_nsec = raw_nsecs; /* Accumulate error between NTP and clock interval */ - tk->ntp_error += ntp_tick_length() << shift; + tk->ntp_error += tk->ntp_tick << shift; tk->ntp_error -= (tk->xtime_interval + tk->xtime_remainder) << (tk->ntp_error_shift + shift); -- cgit v1.2.3 From a0f7a756c2f7543585657cdeeefdfcc11b567293 Mon Sep 17 00:00:00 2001 From: Naoya Horiguchi Date: Wed, 23 Jul 2014 14:00:01 -0700 Subject: mm/rmap.c: fix pgoff calculation to handle hugepage correctly I triggered VM_BUG_ON() in vma_address() when I tried to migrate an anonymous hugepage with mbind() in the kernel v3.16-rc3. This is because pgoff's calculation in rmap_walk_anon() fails to consider compound_order() only to have an incorrect value. This patch introduces page_to_pgoff(), which gets the page's offset in PAGE_CACHE_SIZE. Kirill pointed out that page cache tree should natively handle hugepages, and in order to make hugetlbfs fit it, page->index of hugetlbfs page should be in PAGE_CACHE_SIZE. This is beyond this patch, but page_to_pgoff() contains the point to be fixed in a single function. Signed-off-by: Naoya Horiguchi Acked-by: Kirill A. Shutemov Cc: Joonsoo Kim Cc: Hugh Dickins Cc: Rik van Riel Cc: Hillf Danton Cc: Naoya Horiguchi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/pagemap.h | 12 ++++++++++++ mm/memory-failure.c | 4 ++-- mm/rmap.c | 10 +++------- 3 files changed, 17 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index 0a97b583ee8d..e1474ae18c88 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -398,6 +398,18 @@ static inline struct page *read_mapping_page(struct address_space *mapping, return read_cache_page(mapping, index, filler, data); } +/* + * Get the offset in PAGE_SIZE. + * (TODO: hugepage should have ->index in PAGE_SIZE) + */ +static inline pgoff_t page_to_pgoff(struct page *page) +{ + if (unlikely(PageHeadHuge(page))) + return page->index << compound_order(page); + else + return page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT); +} + /* * Return byte-offset into filesystem object for page. */ diff --git a/mm/memory-failure.c b/mm/memory-failure.c index c6399e328931..7211a73ba14d 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -435,7 +435,7 @@ static void collect_procs_anon(struct page *page, struct list_head *to_kill, if (av == NULL) /* Not actually mapped anymore */ return; - pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT); + pgoff = page_to_pgoff(page); read_lock(&tasklist_lock); for_each_process (tsk) { struct anon_vma_chain *vmac; @@ -469,7 +469,7 @@ static void collect_procs_file(struct page *page, struct list_head *to_kill, mutex_lock(&mapping->i_mmap_mutex); read_lock(&tasklist_lock); for_each_process(tsk) { - pgoff_t pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT); + pgoff_t pgoff = page_to_pgoff(page); struct task_struct *t = task_early_kill(tsk, force_early); if (!t) diff --git a/mm/rmap.c b/mm/rmap.c index b7e94ebbd09e..22a4a7699cdb 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -517,11 +517,7 @@ void page_unlock_anon_vma_read(struct anon_vma *anon_vma) static inline unsigned long __vma_address(struct page *page, struct vm_area_struct *vma) { - pgoff_t pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT); - - if (unlikely(is_vm_hugetlb_page(vma))) - pgoff = page->index << huge_page_order(page_hstate(page)); - + pgoff_t pgoff = page_to_pgoff(page); return vma->vm_start + ((pgoff - vma->vm_pgoff) << PAGE_SHIFT); } @@ -1639,7 +1635,7 @@ static struct anon_vma *rmap_walk_anon_lock(struct page *page, static int rmap_walk_anon(struct page *page, struct rmap_walk_control *rwc) { struct anon_vma *anon_vma; - pgoff_t pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT); + pgoff_t pgoff = page_to_pgoff(page); struct anon_vma_chain *avc; int ret = SWAP_AGAIN; @@ -1680,7 +1676,7 @@ static int rmap_walk_anon(struct page *page, struct rmap_walk_control *rwc) static int rmap_walk_file(struct page *page, struct rmap_walk_control *rwc) { struct address_space *mapping = page->mapping; - pgoff_t pgoff = page->index << compound_order(page); + pgoff_t pgoff = page_to_pgoff(page); struct vm_area_struct *vma; int ret = SWAP_AGAIN; -- cgit v1.2.3 From 8a2b22a2595bf89d4396530edf8388159fad9d83 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Wed, 23 Jul 2014 17:05:06 -0600 Subject: of: Make devicetree sysfs update functions consistent. All of the DT modification functions are split into two parts, the first part manipulates the DT data structure, and the second part updates sysfs, but the code isn't very consistent about how the second half is called. They don't all enforce the same rules about when it is valid to update sysfs, and there isn't any clarity on locking. The transactional DT modification feature that is coming also needs access to these functions so that it can perform all the structure changes together, and then all the sysfs updates as a second stage instead of doing each one at a time. Fix up the second have by creating a separate __of_*_sysfs() function for each of the helpers. The new functions have consistent naming (ie. of_node_add() becomes __of_attach_node_sysfs()) and all of them now defer if of_init hasn't been called yet. Callers of the new functions must hold the of_mutex to ensure there are no race conditions with of_init(). The mutex ensures that there will only ever be one writer to the tree at any given time. There can still be any number of readers and the raw_spin_lock is still used to make sure access to the data structure is still consistent. Finally, put the function prototypes into of_private.h so they are accessible to the transaction code. Signed-off-by: Pantelis Antoniou [grant.likely: Changed suffix from _post to _sysfs to match existing code] [grant.likely: Reorganized to eliminate trivial wrappers] Signed-off-by: Grant Likely --- drivers/of/base.c | 96 +++++++++++++++++++++++++------------------------ drivers/of/dynamic.c | 12 +++++-- drivers/of/of_private.h | 10 ++++++ include/linux/of.h | 2 -- 4 files changed, 69 insertions(+), 51 deletions(-) (limited to 'include') diff --git a/drivers/of/base.c b/drivers/of/base.c index b403f9d98461..ad4929cbd876 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -37,10 +37,13 @@ struct device_node *of_chosen; struct device_node *of_aliases; static struct device_node *of_stdout; -static struct kset *of_kset; +struct kset *of_kset; /* - * Used to protect the of_aliases, to hold off addition of nodes to sysfs + * Used to protect the of_aliases, to hold off addition of nodes to sysfs. + * This mutex must be held whenever modifications are being made to the + * device tree. The of_{attach,detach}_node() and + * of_{add,remove,update}_property() helpers make sure this happens. */ DEFINE_MUTEX(of_mutex); @@ -127,13 +130,16 @@ static const char *safe_name(struct kobject *kobj, const char *orig_name) return name; } -static int __of_add_property_sysfs(struct device_node *np, struct property *pp) +int __of_add_property_sysfs(struct device_node *np, struct property *pp) { int rc; /* Important: Don't leak passwords */ bool secure = strncmp(pp->name, "security-", 9) == 0; + if (!of_kset || !of_node_is_attached(np)) + return 0; + sysfs_bin_attr_init(&pp->attr); pp->attr.attr.name = safe_name(&np->kobj, pp->name); pp->attr.attr.mode = secure ? S_IRUSR : S_IRUGO; @@ -145,12 +151,15 @@ static int __of_add_property_sysfs(struct device_node *np, struct property *pp) return rc; } -static int __of_node_add(struct device_node *np) +int __of_attach_node_sysfs(struct device_node *np) { const char *name; struct property *pp; int rc; + if (!of_kset) + return 0; + np->kobj.kset = of_kset; if (!np->parent) { /* Nodes without parents are new top level trees */ @@ -172,26 +181,6 @@ static int __of_node_add(struct device_node *np) return 0; } -int of_node_add(struct device_node *np) -{ - int rc = 0; - - BUG_ON(!of_node_is_initialized(np)); - - /* - * Grab the mutex here so that in a race condition between of_init() and - * of_node_add(), node addition will still be consistent. - */ - mutex_lock(&of_mutex); - if (of_kset) - rc = __of_node_add(np); - else - /* This scenario may be perfectly valid, but report it anyway */ - pr_info("of_node_add(%s) before of_init()\n", np->full_name); - mutex_unlock(&of_mutex); - return rc; -} - static int __init of_init(void) { struct device_node *np; @@ -204,7 +193,7 @@ static int __init of_init(void) return -ENOMEM; } for_each_of_allnodes(np) - __of_node_add(np); + __of_attach_node_sysfs(np); mutex_unlock(&of_mutex); /* Symlink in /proc as required by userspace ABI */ @@ -1689,15 +1678,17 @@ int of_add_property(struct device_node *np, struct property *prop) if (rc) return rc; + mutex_lock(&of_mutex); + raw_spin_lock_irqsave(&devtree_lock, flags); rc = __of_add_property(np, prop); raw_spin_unlock_irqrestore(&devtree_lock, flags); - if (rc) - return rc; - if (of_node_is_attached(np)) + if (!rc) __of_add_property_sysfs(np, prop); + mutex_unlock(&of_mutex); + return rc; } @@ -1720,6 +1711,13 @@ int __of_remove_property(struct device_node *np, struct property *prop) return 0; } +void __of_remove_property_sysfs(struct device_node *np, struct property *prop) +{ + /* at early boot, bail here and defer setup to of_init() */ + if (of_kset && of_node_is_attached(np)) + sysfs_remove_bin_file(&np->kobj, &prop->attr); +} + /** * of_remove_property - Remove a property from a node. * @@ -1737,20 +1735,18 @@ int of_remove_property(struct device_node *np, struct property *prop) if (rc) return rc; + mutex_lock(&of_mutex); + raw_spin_lock_irqsave(&devtree_lock, flags); rc = __of_remove_property(np, prop); raw_spin_unlock_irqrestore(&devtree_lock, flags); - if (rc) - return rc; - - /* at early boot, bail hear and defer setup to of_init() */ - if (!of_kset) - return 0; + if (!rc) + __of_remove_property_sysfs(np, prop); - sysfs_remove_bin_file(&np->kobj, &prop->attr); + mutex_unlock(&of_mutex); - return 0; + return rc; } int __of_update_property(struct device_node *np, struct property *newprop, @@ -1779,6 +1775,18 @@ int __of_update_property(struct device_node *np, struct property *newprop, return 0; } +void __of_update_property_sysfs(struct device_node *np, struct property *newprop, + struct property *oldprop) +{ + /* At early boot, bail out and defer setup to of_init() */ + if (!of_kset) + return; + + if (oldprop) + sysfs_remove_bin_file(&np->kobj, &oldprop->attr); + __of_add_property_sysfs(np, newprop); +} + /* * of_update_property - Update a property in a node, if the property does * not exist, add it. @@ -1801,22 +1809,18 @@ int of_update_property(struct device_node *np, struct property *newprop) if (rc) return rc; + mutex_lock(&of_mutex); + raw_spin_lock_irqsave(&devtree_lock, flags); rc = __of_update_property(np, newprop, &oldprop); raw_spin_unlock_irqrestore(&devtree_lock, flags); - if (rc) - return rc; - /* At early boot, bail out and defer setup to of_init() */ - if (!of_kset) - return 0; + if (!rc) + __of_update_property_sysfs(np, newprop, oldprop); - /* Update the sysfs attribute */ - if (oldprop) - sysfs_remove_bin_file(&np->kobj, &oldprop->attr); - __of_add_property_sysfs(np, newprop); + mutex_unlock(&of_mutex); - return 0; + return rc; } static void of_alias_add(struct alias_prop *ap, struct device_node *np, diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index 75fcc66fcefd..c875787fa394 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -41,11 +41,13 @@ void of_node_put(struct device_node *node) } EXPORT_SYMBOL(of_node_put); -static void of_node_remove(struct device_node *np) +void __of_detach_node_sysfs(struct device_node *np) { struct property *pp; BUG_ON(!of_node_is_initialized(np)); + if (!of_kset) + return; /* only remove properties if on sysfs */ if (of_node_is_attached(np)) { @@ -115,11 +117,13 @@ int of_attach_node(struct device_node *np) if (rc) return rc; + mutex_lock(&of_mutex); raw_spin_lock_irqsave(&devtree_lock, flags); __of_attach_node(np); raw_spin_unlock_irqrestore(&devtree_lock, flags); - of_node_add(np); + __of_attach_node_sysfs(np); + mutex_unlock(&of_mutex); return 0; } @@ -174,11 +178,13 @@ int of_detach_node(struct device_node *np) if (rc) return rc; + mutex_lock(&of_mutex); raw_spin_lock_irqsave(&devtree_lock, flags); __of_detach_node(np); raw_spin_unlock_irqrestore(&devtree_lock, flags); - of_node_remove(np); + __of_detach_node_sysfs(np); + mutex_unlock(&of_mutex); return rc; } diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index 0f6089722af9..0d99ba8caeed 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -33,6 +33,8 @@ struct alias_prop { extern struct mutex of_mutex; extern struct list_head aliases_lookup; +extern struct kset *of_kset; + static inline struct device_node *kobj_to_device_node(struct kobject *kobj) { @@ -62,11 +64,19 @@ struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags); struct device_node *__of_node_alloc(const char *full_name, gfp_t allocflags); extern int __of_add_property(struct device_node *np, struct property *prop); +extern int __of_add_property_sysfs(struct device_node *np, + struct property *prop); extern int __of_remove_property(struct device_node *np, struct property *prop); +extern void __of_remove_property_sysfs(struct device_node *np, + struct property *prop); extern int __of_update_property(struct device_node *np, struct property *newprop, struct property **oldprop); +extern void __of_update_property_sysfs(struct device_node *np, + struct property *newprop, struct property *oldprop); extern void __of_attach_node(struct device_node *np); +extern int __of_attach_node_sysfs(struct device_node *np); extern void __of_detach_node(struct device_node *np); +extern void __of_detach_node_sysfs(struct device_node *np); #endif /* _LINUX_OF_PRIVATE_H */ diff --git a/include/linux/of.h b/include/linux/of.h index abf829a1f150..705fa12fca7f 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -74,8 +74,6 @@ struct of_phandle_args { uint32_t args[MAX_PHANDLE_ARGS]; }; -extern int of_node_add(struct device_node *node); - /* initialize a node */ extern struct kobj_type of_node_ktype; static inline void of_node_init(struct device_node *node) -- cgit v1.2.3 From 4dd9bb91bb5dc44e3f8c23c60a0ba432e50d7488 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Thu, 3 Apr 2014 20:31:25 -0300 Subject: [media] rc-core: improve ir-kbd-i2c get_key functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The arguments used for ir-kbd-i2c's get_key() functions are not really suited for rc-core and the ir_raw/ir_key distinction is just confusing. Convert all of them to return a protocol/scancode/toggle triple instead. Signed-off-by: David Härdeman Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ir-kbd-i2c.c | 90 +++++++++++++++---------------- drivers/media/pci/bt8xx/bttv-input.c | 8 +-- drivers/media/pci/cx88/cx88-input.c | 8 +-- drivers/media/pci/ivtv/ivtv-i2c.c | 9 ++-- drivers/media/pci/saa7134/saa7134-input.c | 76 +++++++++++++++----------- drivers/media/usb/cx231xx/cx231xx-input.c | 20 +++---- include/media/ir-kbd-i2c.h | 6 ++- include/media/rc-map.h | 10 ++++ 8 files changed, 127 insertions(+), 100 deletions(-) (limited to 'include') diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c index c8fe1358ec9e..143cb2b26c46 100644 --- a/drivers/media/i2c/ir-kbd-i2c.c +++ b/drivers/media/i2c/ir-kbd-i2c.c @@ -62,8 +62,8 @@ module_param(debug, int, 0644); /* debug level (0,1,2) */ /* ----------------------------------------------------------------------- */ -static int get_key_haup_common(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, - int size, int offset) +static int get_key_haup_common(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *ptoggle, int size, int offset) { unsigned char buf[6]; int start, range, toggle, dev, code, ircode; @@ -86,19 +86,10 @@ static int get_key_haup_common(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, if (!start) /* no key pressed */ return 0; - /* - * Hauppauge remotes (black/silver) always use - * specific device ids. If we do not filter the - * device ids then messages destined for devices - * such as TVs (id=0) will get through causing - * mis-fired events. - * - * We also filter out invalid key presses which - * produce annoying debug log entries. - */ - ircode= (start << 12) | (toggle << 11) | (dev << 6) | code; - if ((ircode & 0x1fff)==0x1fff) - /* invalid key press */ + + /* filter out invalid key presses */ + ircode = (start << 12) | (toggle << 11) | (dev << 6) | code; + if ((ircode & 0x1fff) == 0x1fff) return 0; if (!range) @@ -107,18 +98,20 @@ static int get_key_haup_common(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, dprintk(1,"ir hauppauge (rc5): s%d r%d t%d dev=%d code=%d\n", start, range, toggle, dev, code); - /* return key */ - *ir_key = (dev << 8) | code; - *ir_raw = ircode; + *protocol = RC_TYPE_RC5; + *scancode = RC_SCANCODE_RC5(dev, code); + *ptoggle = toggle; return 1; } -static int get_key_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_haup(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { - return get_key_haup_common (ir, ir_key, ir_raw, 3, 0); + return get_key_haup_common (ir, protocol, scancode, toggle, 3, 0); } -static int get_key_haup_xvr(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_haup_xvr(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { int ret; unsigned char buf[1] = { 0 }; @@ -133,10 +126,11 @@ static int get_key_haup_xvr(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) if (ret != 1) return (ret < 0) ? ret : -EINVAL; - return get_key_haup_common (ir, ir_key, ir_raw, 6, 3); + return get_key_haup_common(ir, protocol, scancode, toggle, 6, 3); } -static int get_key_pixelview(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_pixelview(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { unsigned char b; @@ -145,12 +139,15 @@ static int get_key_pixelview(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) dprintk(1,"read error\n"); return -EIO; } - *ir_key = b; - *ir_raw = b; + + *protocol = RC_TYPE_OTHER; + *scancode = b; + *toggle = 0; return 1; } -static int get_key_fusionhdtv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_fusionhdtv(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { unsigned char buf[4]; @@ -168,13 +165,14 @@ static int get_key_fusionhdtv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) if(buf[0] != 0x1 || buf[1] != 0xfe) return 0; - *ir_key = buf[2]; - *ir_raw = (buf[2] << 8) | buf[3]; - + *protocol = RC_TYPE_UNKNOWN; + *scancode = buf[2]; + *toggle = 0; return 1; } -static int get_key_knc1(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_knc1(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { unsigned char b; @@ -197,13 +195,14 @@ static int get_key_knc1(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) /* keep old data */ return 1; - *ir_key = b; - *ir_raw = b; + *protocol = RC_TYPE_UNKNOWN; + *scancode = b; + *toggle = 0; return 1; } -static int get_key_avermedia_cardbus(struct IR_i2c *ir, - u32 *ir_key, u32 *ir_raw) +static int get_key_avermedia_cardbus(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { unsigned char subaddr, key, keygroup; struct i2c_msg msg[] = { { .addr = ir->c->addr, .flags = 0, @@ -237,12 +236,11 @@ static int get_key_avermedia_cardbus(struct IR_i2c *ir, } key |= (keygroup & 1) << 6; - *ir_key = key; - *ir_raw = key; - if (!strcmp(ir->ir_codes, RC_MAP_AVERMEDIA_M733A_RM_K6)) { - *ir_key |= keygroup << 8; - *ir_raw |= keygroup << 8; - } + *protocol = RC_TYPE_UNKNOWN; + *scancode = key; + if (ir->c->addr == 0x41) /* AVerMedia EM78P153 */ + *scancode |= keygroup << 8; + *toggle = 0; return 1; } @@ -250,19 +248,21 @@ static int get_key_avermedia_cardbus(struct IR_i2c *ir, static int ir_key_poll(struct IR_i2c *ir) { - static u32 ir_key, ir_raw; + enum rc_type protocol; + u32 scancode; + u8 toggle; int rc; dprintk(3, "%s\n", __func__); - rc = ir->get_key(ir, &ir_key, &ir_raw); + rc = ir->get_key(ir, &protocol, &scancode, &toggle); if (rc < 0) { dprintk(2,"error\n"); return rc; } if (rc) { - dprintk(1, "%s: keycode = 0x%04x\n", __func__, ir_key); - rc_keydown(ir->rc, ir_key, 0); + dprintk(1, "%s: scancode = 0x%08x\n", __func__, scancode); + rc_keydown(ir->rc, scancode, toggle); } return 0; } @@ -327,7 +327,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) case 0x6b: name = "FusionHDTV"; ir->get_key = get_key_fusionhdtv; - rc_type = RC_BIT_RC5; + rc_type = RC_BIT_UNKNOWN; ir_codes = RC_MAP_FUSIONHDTV_MCE; break; case 0x40: diff --git a/drivers/media/pci/bt8xx/bttv-input.c b/drivers/media/pci/bt8xx/bttv-input.c index ffc0ee1d848b..e745f5a04a40 100644 --- a/drivers/media/pci/bt8xx/bttv-input.c +++ b/drivers/media/pci/bt8xx/bttv-input.c @@ -336,7 +336,8 @@ static void bttv_ir_stop(struct bttv *btv) * Get_key functions used by I2C remotes */ -static int get_key_pv951(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_pv951(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { unsigned char b; @@ -363,8 +364,9 @@ static int get_key_pv951(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) * the device is bound to the vendor-provided RC. */ - *ir_key = b; - *ir_raw = b; + *protocol = RC_TYPE_UNKNOWN; + *scancode = b; + *toggle = 0; return 1; } diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c index f991696a6c59..779fc630327a 100644 --- a/drivers/media/pci/cx88/cx88-input.c +++ b/drivers/media/pci/cx88/cx88-input.c @@ -539,7 +539,8 @@ void cx88_ir_irq(struct cx88_core *core) ir_raw_event_handle(ir->dev); } -static int get_key_pvr2000(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_pvr2000(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { int flags, code; @@ -563,8 +564,9 @@ static int get_key_pvr2000(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) dprintk("IR Key/Flags: (0x%02x/0x%02x)\n", code & 0xff, flags & 0xff); - *ir_key = code & 0xff; - *ir_raw = code; + *protocol = RC_TYPE_UNKNOWN; + *scancode = code & 0xff; + *toggle = 0; return 1; } diff --git a/drivers/media/pci/ivtv/ivtv-i2c.c b/drivers/media/pci/ivtv/ivtv-i2c.c index ceed2d87abfd..1a41ba5c7d30 100644 --- a/drivers/media/pci/ivtv/ivtv-i2c.c +++ b/drivers/media/pci/ivtv/ivtv-i2c.c @@ -148,7 +148,8 @@ static const char * const hw_devicenames[] = { "ir_video", /* IVTV_HW_I2C_IR_RX_ADAPTEC */ }; -static int get_key_adaptec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_adaptec(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { unsigned char keybuf[4]; @@ -167,9 +168,9 @@ static int get_key_adaptec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) keybuf[2] &= 0x7f; keybuf[3] |= 0x80; - *ir_key = keybuf[3] | keybuf[2] << 8 | keybuf[1] << 16 |keybuf[0] << 24; - *ir_raw = *ir_key; - + *protocol = RC_TYPE_UNKNOWN; + *scancode = keybuf[3] | keybuf[2] << 8 | keybuf[1] << 16 |keybuf[0] << 24; + *toggle = 0; return 1; } diff --git a/drivers/media/pci/saa7134/saa7134-input.c b/drivers/media/pci/saa7134/saa7134-input.c index 6f4312663bdf..73670ed7156b 100644 --- a/drivers/media/pci/saa7134/saa7134-input.c +++ b/drivers/media/pci/saa7134/saa7134-input.c @@ -108,7 +108,8 @@ static int build_key(struct saa7134_dev *dev) /* --------------------- Chip specific I2C key builders ----------------- */ -static int get_key_flydvb_trio(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_flydvb_trio(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { int gpio; int attempt = 0; @@ -158,13 +159,14 @@ static int get_key_flydvb_trio(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) return -EIO; } - *ir_key = b; - *ir_raw = b; + *protocol = RC_TYPE_UNKNOWN; + *scancode = b; + *toggle = 0; return 1; } -static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir, u32 *ir_key, - u32 *ir_raw) +static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { unsigned char b; int gpio; @@ -205,14 +207,15 @@ static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir, u32 *ir_key, /* Button pressed */ dprintk("get_key_msi_tvanywhere_plus: Key = 0x%02X\n", b); - *ir_key = b; - *ir_raw = b; + *protocol = RC_TYPE_UNKNOWN; + *scancode = b; + *toggle = 0; return 1; } /* copied and modified from get_key_msi_tvanywhere_plus() */ -static int get_key_kworld_pc150u(struct IR_i2c *ir, u32 *ir_key, - u32 *ir_raw) +static int get_key_kworld_pc150u(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { unsigned char b; unsigned int gpio; @@ -253,12 +256,14 @@ static int get_key_kworld_pc150u(struct IR_i2c *ir, u32 *ir_key, /* Button pressed */ dprintk("get_key_kworld_pc150u: Key = 0x%02X\n", b); - *ir_key = b; - *ir_raw = b; + *protocol = RC_TYPE_UNKNOWN; + *scancode = b; + *toggle = 0; return 1; } -static int get_key_purpletv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_purpletv(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { unsigned char b; @@ -276,12 +281,14 @@ static int get_key_purpletv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) if (b & 0x80) return 1; - *ir_key = b; - *ir_raw = b; + *protocol = RC_TYPE_UNKNOWN; + *scancode = b; + *toggle = 0; return 1; } -static int get_key_hvr1110(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_hvr1110(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { unsigned char buf[5]; @@ -299,14 +306,20 @@ static int get_key_hvr1110(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) * by preserving it into two separate readings * buf[4] bits 0 and 1, and buf[1] and buf[2] are always * zero. + * + * Note that the keymap which the hvr1110 uses is RC5. + * + * FIXME: start bits could maybe be used...? */ - *ir_key = 0x1fff & ((buf[3] << 8) | (buf[4] >> 2)); - *ir_raw = *ir_key; + *protocol = RC_TYPE_RC5; + *scancode = RC_SCANCODE_RC5(buf[3] & 0x1f, buf[4] >> 2); + *toggle = !!(buf[3] & 0x40); return 1; } -static int get_key_beholdm6xx(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_beholdm6xx(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { unsigned char data[12]; u32 gpio; @@ -332,17 +345,18 @@ static int get_key_beholdm6xx(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) if (data[9] != (unsigned char)(~data[8])) return 0; - *ir_raw = ((data[10] << 16) | (data[11] << 8) | (data[9] << 0)); - *ir_key = *ir_raw; - + *protocol = RC_TYPE_NEC; + *scancode = RC_SCANCODE_NECX(((data[10] << 8) | data[11]), data[9]); + *toggle = 0; return 1; } /* Common (grey or coloured) pinnacle PCTV remote handling * */ -static int get_key_pinnacle(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, - int parity_offset, int marker, int code_modulo) +static int get_key_pinnacle(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle, int parity_offset, + int marker, int code_modulo) { unsigned char b[4]; unsigned int start = 0,parity = 0,code = 0; @@ -377,11 +391,11 @@ static int get_key_pinnacle(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, code %= code_modulo; - *ir_raw = code; - *ir_key = code; + *protocol = RC_TYPE_UNKNOWN; + *scancode = code; + *toggle = 0; i2cdprintk("Pinnacle PCTV key %02x\n", code); - return 1; } @@ -394,10 +408,11 @@ static int get_key_pinnacle(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, * * Sylvain Pasche */ -static int get_key_pinnacle_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_pinnacle_grey(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { - return get_key_pinnacle(ir, ir_key, ir_raw, 1, 0xfe, 0xff); + return get_key_pinnacle(ir, protocol, scancode, toggle, 1, 0xfe, 0xff); } @@ -405,7 +420,8 @@ static int get_key_pinnacle_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) * * Ricardo Cerqueira */ -static int get_key_pinnacle_color(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_pinnacle_color(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle) { /* code_modulo parameter (0x88) is used to reduce code value to fit inside IR_KEYTAB_SIZE * @@ -413,7 +429,7 @@ static int get_key_pinnacle_color(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) * codes < 128 */ - return get_key_pinnacle(ir, ir_key, ir_raw, 2, 0x80, 0x88); + return get_key_pinnacle(ir, protocol, scancode, toggle, 2, 0x80, 0x88); } void saa7134_input_irq(struct saa7134_dev *dev) diff --git a/drivers/media/usb/cx231xx/cx231xx-input.c b/drivers/media/usb/cx231xx/cx231xx-input.c index 46d52fac8680..adcdd92179b2 100644 --- a/drivers/media/usb/cx231xx/cx231xx-input.c +++ b/drivers/media/usb/cx231xx/cx231xx-input.c @@ -21,11 +21,12 @@ #include "cx231xx.h" #include #include +#include #define MODULE_NAME "cx231xx-input" -static int get_key_isdbt(struct IR_i2c *ir, u32 *ir_key, - u32 *ir_raw) +static int get_key_isdbt(struct IR_i2c *ir, enum rc_type *protocol, + u32 *pscancode, u8 *toggle) { int rc; u8 cmd, scancode; @@ -46,21 +47,14 @@ static int get_key_isdbt(struct IR_i2c *ir, u32 *ir_key, if (cmd == 0xff) return 0; - scancode = - ((cmd & 0x01) ? 0x80 : 0) | - ((cmd & 0x02) ? 0x40 : 0) | - ((cmd & 0x04) ? 0x20 : 0) | - ((cmd & 0x08) ? 0x10 : 0) | - ((cmd & 0x10) ? 0x08 : 0) | - ((cmd & 0x20) ? 0x04 : 0) | - ((cmd & 0x40) ? 0x02 : 0) | - ((cmd & 0x80) ? 0x01 : 0); + scancode = bitrev8(cmd); dev_dbg(&ir->rc->input_dev->dev, "cmd %02x, scan = %02x\n", cmd, scancode); - *ir_key = scancode; - *ir_raw = scancode; + *protocol = RC_TYPE_OTHER; + *pscancode = scancode; + *toggle = 0; return 1; } diff --git a/include/media/ir-kbd-i2c.h b/include/media/ir-kbd-i2c.h index e221bc74020b..d8564354debb 100644 --- a/include/media/ir-kbd-i2c.h +++ b/include/media/ir-kbd-i2c.h @@ -20,7 +20,8 @@ struct IR_i2c { struct delayed_work work; char name[32]; char phys[32]; - int (*get_key)(struct IR_i2c*, u32*, u32*); + int (*get_key)(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle); }; enum ir_kbd_get_key_fn { @@ -44,7 +45,8 @@ struct IR_i2c_init_data { * Specify either a function pointer or a value indicating one of * ir_kbd_i2c's internal get_key functions */ - int (*get_key)(struct IR_i2c*, u32*, u32*); + int (*get_key)(struct IR_i2c *ir, enum rc_type *protocol, + u32 *scancode, u8 *toggle); enum ir_kbd_get_key_fn internal_get_key_func; struct rc_dev *rc_dev; diff --git a/include/media/rc-map.h b/include/media/rc-map.h index e5aa2409c0ea..894c7e4c409b 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -62,6 +62,16 @@ enum rc_type { RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 | \ RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE | RC_BIT_SHARP) +#define RC_SCANCODE_UNKNOWN(x) (x) +#define RC_SCANCODE_OTHER(x) (x) +#define RC_SCANCODE_NEC(addr, cmd) (((addr) << 8) | (cmd)) +#define RC_SCANCODE_NECX(addr, cmd) (((addr) << 8) | (cmd)) +#define RC_SCANCODE_NEC32(data) ((data) & 0xffffffff) +#define RC_SCANCODE_RC5(sys, cmd) (((sys) << 8) | (cmd)) +#define RC_SCANCODE_RC5_SZ(sys, cmd) (((sys) << 8) | (cmd)) +#define RC_SCANCODE_RC6_0(sys, cmd) (((sys) << 8) | (cmd)) +#define RC_SCANCODE_RC6_6A(vendor, sys, cmd) (((vendor) << 16) | ((sys) << 8) | (cmd)) + struct rc_map_table { u32 scancode; u32 keycode; -- cgit v1.2.3 From 259092a35c7e11f1d4616b0f5b3ba7b851fe4fa6 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Wed, 16 Jul 2014 12:48:23 -0600 Subject: of: Reorder device tree changes and notifiers Currently, devicetree reconfig notifiers get emitted before the change is applied to the tree, but that behaviour is problematic if the receiver wants the determine the new state of the tree. The current users don't care, but the changeset code to follow will be making multiple changes at once. Reorder notifiers to get emitted after the change has been applied to the tree so that callbacks see the new tree state. At the same time, fixup the existing callbacks to expect the new order. There are a few callbacks that compare the old and new values of a changed property. Put both property pointers into the of_prop_reconfig structure. The current notifiers also allow the notifier callback to fail and cancel the change to the tree, but that feature isn't actually used. It really isn't valid to ignore a tree modification provided by firmware anyway, so remove the ability to cancel a change to the tree. Signed-off-by: Grant Likely Cc: Nathan Fontenot --- arch/powerpc/platforms/pseries/hotplug-memory.c | 2 +- drivers/crypto/nx/nx-842.c | 30 +++++++------------------ drivers/of/base.c | 21 ++++++++--------- drivers/of/dynamic.c | 18 +++++++-------- drivers/of/of_private.h | 4 ++-- include/linux/of.h | 1 + 6 files changed, 29 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/arch/powerpc/platforms/pseries/hotplug-memory.c b/arch/powerpc/platforms/pseries/hotplug-memory.c index 7995135170a3..ac01e188faef 100644 --- a/arch/powerpc/platforms/pseries/hotplug-memory.c +++ b/arch/powerpc/platforms/pseries/hotplug-memory.c @@ -194,7 +194,7 @@ static int pseries_update_drconf_memory(struct of_prop_reconfig *pr) if (!memblock_size) return -EINVAL; - p = (u32 *)of_get_property(pr->dn, "ibm,dynamic-memory", NULL); + p = (u32 *) pr->old_prop->value; if (!p) return -EINVAL; diff --git a/drivers/crypto/nx/nx-842.c b/drivers/crypto/nx/nx-842.c index 502edf0a2933..c897c3a5ee17 100644 --- a/drivers/crypto/nx/nx-842.c +++ b/drivers/crypto/nx/nx-842.c @@ -936,28 +936,14 @@ static int nx842_OF_upd(struct property *new_prop) goto error_out; } - /* Set ptr to new property if provided */ - if (new_prop) { - /* Single property */ - if (!strncmp(new_prop->name, "status", new_prop->length)) { - status = new_prop; - - } else if (!strncmp(new_prop->name, "ibm,max-sg-len", - new_prop->length)) { - maxsglen = new_prop; - - } else if (!strncmp(new_prop->name, "ibm,max-sync-cop", - new_prop->length)) { - maxsyncop = new_prop; - - } else { - /* - * Skip the update, the property being updated - * has no impact. - */ - goto out; - } - } + /* + * If this is a property update, there are only certain properties that + * we care about. Bail if it isn't in the below list + */ + if (new_prop && (strncmp(new_prop->name, "status", new_prop->length) || + strncmp(new_prop->name, "ibm,max-sg-len", new_prop->length) || + strncmp(new_prop->name, "ibm,max-sync-cop", new_prop->length))) + goto out; /* Perform property updates */ ret = nx842_OF_upd_status(new_devdata, status); diff --git a/drivers/of/base.c b/drivers/of/base.c index ededf8e33145..a7ad1013edfa 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1674,10 +1674,6 @@ int of_add_property(struct device_node *np, struct property *prop) unsigned long flags; int rc; - rc = of_property_notify(OF_RECONFIG_ADD_PROPERTY, np, prop); - if (rc) - return rc; - mutex_lock(&of_mutex); raw_spin_lock_irqsave(&devtree_lock, flags); @@ -1689,6 +1685,9 @@ int of_add_property(struct device_node *np, struct property *prop) mutex_unlock(&of_mutex); + if (!rc) + of_property_notify(OF_RECONFIG_ADD_PROPERTY, np, prop, NULL); + return rc; } @@ -1731,10 +1730,6 @@ int of_remove_property(struct device_node *np, struct property *prop) unsigned long flags; int rc; - rc = of_property_notify(OF_RECONFIG_REMOVE_PROPERTY, np, prop); - if (rc) - return rc; - mutex_lock(&of_mutex); raw_spin_lock_irqsave(&devtree_lock, flags); @@ -1746,6 +1741,9 @@ int of_remove_property(struct device_node *np, struct property *prop) mutex_unlock(&of_mutex); + if (!rc) + of_property_notify(OF_RECONFIG_REMOVE_PROPERTY, np, prop, NULL); + return rc; } @@ -1805,10 +1803,6 @@ int of_update_property(struct device_node *np, struct property *newprop) if (!newprop->name) return -EINVAL; - rc = of_property_notify(OF_RECONFIG_UPDATE_PROPERTY, np, newprop); - if (rc) - return rc; - mutex_lock(&of_mutex); raw_spin_lock_irqsave(&devtree_lock, flags); @@ -1820,6 +1814,9 @@ int of_update_property(struct device_node *np, struct property *newprop) mutex_unlock(&of_mutex); + if (!rc) + of_property_notify(OF_RECONFIG_UPDATE_PROPERTY, np, newprop, oldprop); + return rc; } diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index 7c020b9a3317..7bd5501736a6 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -83,7 +83,7 @@ int of_reconfig_notify(unsigned long action, void *p) } int of_property_notify(int action, struct device_node *np, - struct property *prop) + struct property *prop, struct property *oldprop) { struct of_prop_reconfig pr; @@ -93,6 +93,7 @@ int of_property_notify(int action, struct device_node *np, pr.dn = np; pr.prop = prop; + pr.old_prop = oldprop; return of_reconfig_notify(action, &pr); } @@ -125,11 +126,6 @@ void __of_attach_node(struct device_node *np) int of_attach_node(struct device_node *np) { unsigned long flags; - int rc; - - rc = of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, np); - if (rc) - return rc; mutex_lock(&of_mutex); raw_spin_lock_irqsave(&devtree_lock, flags); @@ -138,6 +134,9 @@ int of_attach_node(struct device_node *np) __of_attach_node_sysfs(np); mutex_unlock(&of_mutex); + + of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, np); + return 0; } @@ -188,10 +187,6 @@ int of_detach_node(struct device_node *np) unsigned long flags; int rc = 0; - rc = of_reconfig_notify(OF_RECONFIG_DETACH_NODE, np); - if (rc) - return rc; - mutex_lock(&of_mutex); raw_spin_lock_irqsave(&devtree_lock, flags); __of_detach_node(np); @@ -199,6 +194,9 @@ int of_detach_node(struct device_node *np) __of_detach_node_sysfs(np); mutex_unlock(&of_mutex); + + of_reconfig_notify(OF_RECONFIG_DETACH_NODE, np); + return rc; } diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index 8129c0e58d70..f69ccb1fa308 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -43,11 +43,11 @@ static inline struct device_node *kobj_to_device_node(struct kobject *kobj) #if defined(CONFIG_OF_DYNAMIC) extern int of_property_notify(int action, struct device_node *np, - struct property *prop); + struct property *prop, struct property *old_prop); extern void of_node_release(struct kobject *kobj); #else /* CONFIG_OF_DYNAMIC */ static inline int of_property_notify(int action, struct device_node *np, - struct property *prop) + struct property *prop, struct property *old_prop) { return 0; } diff --git a/include/linux/of.h b/include/linux/of.h index 705fa12fca7f..400f18cb4fff 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -321,6 +321,7 @@ extern int of_update_property(struct device_node *np, struct property *newprop); struct of_prop_reconfig { struct device_node *dn; struct property *prop; + struct property *old_prop; }; extern int of_reconfig_notifier_register(struct notifier_block *); -- cgit v1.2.3 From 201c910bd6898d81d4ac6685d0f421b7e10f3c5d Mon Sep 17 00:00:00 2001 From: Pantelis Antoniou Date: Fri, 4 Jul 2014 19:58:49 +0300 Subject: of: Transactional DT support. Introducing DT transactional support. A DT transaction is a method which allows one to apply changes in the live tree, in such a way that either the full set of changes take effect, or the state of the tree can be rolled-back to the state it was before it was attempted. An applied transaction can be rolled-back at any time. Documentation is in Documentation/devicetree/changesets.txt Signed-off-by: Pantelis Antoniou [glikely: Removed device notifiers and reworked to be more consistent] Signed-off-by: Grant Likely --- Documentation/devicetree/changesets.txt | 40 ++++ drivers/of/dynamic.c | 344 ++++++++++++++++++++++++++++++++ drivers/of/of_private.h | 9 + drivers/of/selftest.c | 51 +++++ drivers/of/testcase-data/testcases.dtsi | 10 + include/linux/of.h | 76 +++++++ 6 files changed, 530 insertions(+) create mode 100644 Documentation/devicetree/changesets.txt (limited to 'include') diff --git a/Documentation/devicetree/changesets.txt b/Documentation/devicetree/changesets.txt new file mode 100644 index 000000000000..935ba5acc34e --- /dev/null +++ b/Documentation/devicetree/changesets.txt @@ -0,0 +1,40 @@ +A DT changeset is a method which allows one to apply changes +in the live tree in such a way that either the full set of changes +will be applied, or none of them will be. If an error occurs partway +through applying the changeset, then the tree will be rolled back to the +previous state. A changeset can also be removed after it has been +applied. + +When a changeset is applied, all of the changes get applied to the tree +at once before emitting OF_RECONFIG notifiers. This is so that the +receiver sees a complete and consistent state of the tree when it +receives the notifier. + +The sequence of a changeset is as follows. + +1. of_changeset_init() - initializes a changeset + +2. A number of DT tree change calls, of_changeset_attach_node(), +of_changeset_detach_node(), of_changeset_add_property(), +of_changeset_remove_property, of_changeset_update_property() to prepare +a set of changes. No changes to the active tree are made at this point. +All the change operations are recorded in the of_changeset 'entries' +list. + +3. mutex_lock(of_mutex) - starts a changeset; The global of_mutex +ensures there can only be one editor at a time. + +4. of_changeset_apply() - Apply the changes to the tree. Either the +entire changeset will get applied, or if there is an error the tree will +be restored to the previous state + +5. mutex_unlock(of_mutex) - All operations complete, release the mutex + +If a successfully applied changeset needs to be removed, it can be done +with the following sequence. + +1. mutex_lock(of_mutex) + +2. of_changeset_revert() + +3. mutex_unlock(of_mutex) diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index 7bd5501736a6..c1002b7be786 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -314,3 +314,347 @@ struct device_node *__of_node_alloc(const char *full_name, gfp_t allocflags) kfree(node); return NULL; } + +static void __of_changeset_entry_destroy(struct of_changeset_entry *ce) +{ + of_node_put(ce->np); + list_del(&ce->node); + kfree(ce); +} + +#ifdef DEBUG +static void __of_changeset_entry_dump(struct of_changeset_entry *ce) +{ + switch (ce->action) { + case OF_RECONFIG_ADD_PROPERTY: + pr_debug("%p: %s %s/%s\n", + ce, "ADD_PROPERTY ", ce->np->full_name, + ce->prop->name); + break; + case OF_RECONFIG_REMOVE_PROPERTY: + pr_debug("%p: %s %s/%s\n", + ce, "REMOVE_PROPERTY", ce->np->full_name, + ce->prop->name); + break; + case OF_RECONFIG_UPDATE_PROPERTY: + pr_debug("%p: %s %s/%s\n", + ce, "UPDATE_PROPERTY", ce->np->full_name, + ce->prop->name); + break; + case OF_RECONFIG_ATTACH_NODE: + pr_debug("%p: %s %s\n", + ce, "ATTACH_NODE ", ce->np->full_name); + break; + case OF_RECONFIG_DETACH_NODE: + pr_debug("%p: %s %s\n", + ce, "DETACH_NODE ", ce->np->full_name); + break; + } +} +#else +static inline void __of_changeset_entry_dump(struct of_changeset_entry *ce) +{ + /* empty */ +} +#endif + +static void __of_changeset_entry_invert(struct of_changeset_entry *ce, + struct of_changeset_entry *rce) +{ + memcpy(rce, ce, sizeof(*rce)); + + switch (ce->action) { + case OF_RECONFIG_ATTACH_NODE: + rce->action = OF_RECONFIG_DETACH_NODE; + break; + case OF_RECONFIG_DETACH_NODE: + rce->action = OF_RECONFIG_ATTACH_NODE; + break; + case OF_RECONFIG_ADD_PROPERTY: + rce->action = OF_RECONFIG_REMOVE_PROPERTY; + break; + case OF_RECONFIG_REMOVE_PROPERTY: + rce->action = OF_RECONFIG_ADD_PROPERTY; + break; + case OF_RECONFIG_UPDATE_PROPERTY: + rce->old_prop = ce->prop; + rce->prop = ce->old_prop; + break; + } +} + +static void __of_changeset_entry_notify(struct of_changeset_entry *ce, bool revert) +{ + struct of_changeset_entry ce_inverted; + int ret; + + if (revert) { + __of_changeset_entry_invert(ce, &ce_inverted); + ce = &ce_inverted; + } + + switch (ce->action) { + case OF_RECONFIG_ATTACH_NODE: + case OF_RECONFIG_DETACH_NODE: + ret = of_reconfig_notify(ce->action, ce->np); + break; + case OF_RECONFIG_ADD_PROPERTY: + case OF_RECONFIG_REMOVE_PROPERTY: + case OF_RECONFIG_UPDATE_PROPERTY: + ret = of_property_notify(ce->action, ce->np, ce->prop, ce->old_prop); + break; + default: + pr_err("%s: invalid devicetree changeset action: %i\n", __func__, + (int)ce->action); + return; + } + + if (ret) + pr_err("%s: notifier error @%s\n", __func__, ce->np->full_name); +} + +static int __of_changeset_entry_apply(struct of_changeset_entry *ce) +{ + struct property *old_prop, **propp; + unsigned long flags; + int ret = 0; + + __of_changeset_entry_dump(ce); + + raw_spin_lock_irqsave(&devtree_lock, flags); + switch (ce->action) { + case OF_RECONFIG_ATTACH_NODE: + __of_attach_node(ce->np); + break; + case OF_RECONFIG_DETACH_NODE: + __of_detach_node(ce->np); + break; + case OF_RECONFIG_ADD_PROPERTY: + /* If the property is in deadprops then it must be removed */ + for (propp = &ce->np->deadprops; *propp; propp = &(*propp)->next) { + if (*propp == ce->prop) { + *propp = ce->prop->next; + ce->prop->next = NULL; + break; + } + } + + ret = __of_add_property(ce->np, ce->prop); + if (ret) { + pr_err("%s: add_property failed @%s/%s\n", + __func__, ce->np->full_name, + ce->prop->name); + break; + } + break; + case OF_RECONFIG_REMOVE_PROPERTY: + ret = __of_remove_property(ce->np, ce->prop); + if (ret) { + pr_err("%s: remove_property failed @%s/%s\n", + __func__, ce->np->full_name, + ce->prop->name); + break; + } + break; + + case OF_RECONFIG_UPDATE_PROPERTY: + /* If the property is in deadprops then it must be removed */ + for (propp = &ce->np->deadprops; *propp; propp = &(*propp)->next) { + if (*propp == ce->prop) { + *propp = ce->prop->next; + ce->prop->next = NULL; + break; + } + } + + ret = __of_update_property(ce->np, ce->prop, &old_prop); + if (ret) { + pr_err("%s: update_property failed @%s/%s\n", + __func__, ce->np->full_name, + ce->prop->name); + break; + } + break; + default: + ret = -EINVAL; + } + raw_spin_unlock_irqrestore(&devtree_lock, flags); + + if (ret) + return ret; + + switch (ce->action) { + case OF_RECONFIG_ATTACH_NODE: + __of_attach_node_sysfs(ce->np); + break; + case OF_RECONFIG_DETACH_NODE: + __of_detach_node_sysfs(ce->np); + break; + case OF_RECONFIG_ADD_PROPERTY: + /* ignore duplicate names */ + __of_add_property_sysfs(ce->np, ce->prop); + break; + case OF_RECONFIG_REMOVE_PROPERTY: + __of_remove_property_sysfs(ce->np, ce->prop); + break; + case OF_RECONFIG_UPDATE_PROPERTY: + __of_update_property_sysfs(ce->np, ce->prop, ce->old_prop); + break; + } + + return 0; +} + +static inline int __of_changeset_entry_revert(struct of_changeset_entry *ce) +{ + struct of_changeset_entry ce_inverted; + + __of_changeset_entry_invert(ce, &ce_inverted); + return __of_changeset_entry_apply(&ce_inverted); +} + +/** + * of_changeset_init - Initialize a changeset for use + * + * @ocs: changeset pointer + * + * Initialize a changeset structure + */ +void of_changeset_init(struct of_changeset *ocs) +{ + memset(ocs, 0, sizeof(*ocs)); + INIT_LIST_HEAD(&ocs->entries); +} + +/** + * of_changeset_destroy - Destroy a changeset + * + * @ocs: changeset pointer + * + * Destroys a changeset. Note that if a changeset is applied, + * its changes to the tree cannot be reverted. + */ +void of_changeset_destroy(struct of_changeset *ocs) +{ + struct of_changeset_entry *ce, *cen; + + list_for_each_entry_safe_reverse(ce, cen, &ocs->entries, node) + __of_changeset_entry_destroy(ce); +} + +/** + * of_changeset_apply - Applies a changeset + * + * @ocs: changeset pointer + * + * Applies a changeset to the live tree. + * Any side-effects of live tree state changes are applied here on + * sucess, like creation/destruction of devices and side-effects + * like creation of sysfs properties and directories. + * Returns 0 on success, a negative error value in case of an error. + * On error the partially applied effects are reverted. + */ +int of_changeset_apply(struct of_changeset *ocs) +{ + struct of_changeset_entry *ce; + int ret; + + /* perform the rest of the work */ + pr_debug("of_changeset: applying...\n"); + list_for_each_entry(ce, &ocs->entries, node) { + ret = __of_changeset_entry_apply(ce); + if (ret) { + pr_err("%s: Error applying changeset (%d)\n", __func__, ret); + list_for_each_entry_continue_reverse(ce, &ocs->entries, node) + __of_changeset_entry_revert(ce); + return ret; + } + } + pr_debug("of_changeset: applied, emitting notifiers.\n"); + + /* drop the global lock while emitting notifiers */ + mutex_unlock(&of_mutex); + list_for_each_entry(ce, &ocs->entries, node) + __of_changeset_entry_notify(ce, 0); + mutex_lock(&of_mutex); + pr_debug("of_changeset: notifiers sent.\n"); + + return 0; +} + +/** + * of_changeset_revert - Reverts an applied changeset + * + * @ocs: changeset pointer + * + * Reverts a changeset returning the state of the tree to what it + * was before the application. + * Any side-effects like creation/destruction of devices and + * removal of sysfs properties and directories are applied. + * Returns 0 on success, a negative error value in case of an error. + */ +int of_changeset_revert(struct of_changeset *ocs) +{ + struct of_changeset_entry *ce; + int ret; + + pr_debug("of_changeset: reverting...\n"); + list_for_each_entry_reverse(ce, &ocs->entries, node) { + ret = __of_changeset_entry_revert(ce); + if (ret) { + pr_err("%s: Error reverting changeset (%d)\n", __func__, ret); + list_for_each_entry_continue(ce, &ocs->entries, node) + __of_changeset_entry_apply(ce); + return ret; + } + } + pr_debug("of_changeset: reverted, emitting notifiers.\n"); + + /* drop the global lock while emitting notifiers */ + mutex_unlock(&of_mutex); + list_for_each_entry_reverse(ce, &ocs->entries, node) + __of_changeset_entry_notify(ce, 1); + mutex_lock(&of_mutex); + pr_debug("of_changeset: notifiers sent.\n"); + + return 0; +} + +/** + * of_changeset_action - Perform a changeset action + * + * @ocs: changeset pointer + * @action: action to perform + * @np: Pointer to device node + * @prop: Pointer to property + * + * On action being one of: + * + OF_RECONFIG_ATTACH_NODE + * + OF_RECONFIG_DETACH_NODE, + * + OF_RECONFIG_ADD_PROPERTY + * + OF_RECONFIG_REMOVE_PROPERTY, + * + OF_RECONFIG_UPDATE_PROPERTY + * Returns 0 on success, a negative error value in case of an error. + */ +int of_changeset_action(struct of_changeset *ocs, unsigned long action, + struct device_node *np, struct property *prop) +{ + struct of_changeset_entry *ce; + + ce = kzalloc(sizeof(*ce), GFP_KERNEL); + if (!ce) { + pr_err("%s: Failed to allocate\n", __func__); + return -ENOMEM; + } + /* get a reference to the node */ + ce->action = action; + ce->np = of_node_get(np); + ce->prop = prop; + + if (action == OF_RECONFIG_UPDATE_PROPERTY && prop) + ce->old_prop = of_find_property(np, prop->name, NULL); + + /* add it to the list */ + list_add_tail(&ce->node, &ocs->entries); + return 0; +} diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index f69ccb1fa308..858e0a5d9a11 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -81,4 +81,13 @@ extern int __of_attach_node_sysfs(struct device_node *np); extern void __of_detach_node(struct device_node *np); extern void __of_detach_node_sysfs(struct device_node *np); +/* iterators for transactions, used for overlays */ +/* forward iterator */ +#define for_each_transaction_entry(_oft, _te) \ + list_for_each_entry(_te, &(_oft)->te_list, node) + +/* reverse iterator */ +#define for_each_transaction_entry_reverse(_oft, _te) \ + list_for_each_entry_reverse(_te, &(_oft)->te_list, node) + #endif /* _LINUX_OF_PRIVATE_H */ diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c index ee2166f0f36a..04e39a183e53 100644 --- a/drivers/of/selftest.c +++ b/drivers/of/selftest.c @@ -293,6 +293,56 @@ static void __init of_selftest_property_copy(void) #endif } +static void __init of_selftest_changeset(void) +{ +#ifdef CONFIG_OF_DYNAMIC + struct property *ppadd, padd = { .name = "prop-add", .length = 0, .value = "" }; + struct property *ppupdate, pupdate = { .name = "prop-update", .length = 5, .value = "abcd" }; + struct property *ppremove; + struct device_node *n1, *n2, *n21, *nremove, *parent; + struct of_changeset chgset; + + of_changeset_init(&chgset); + n1 = __of_node_alloc("/testcase-data/changeset/n1", GFP_KERNEL); + selftest(n1, "testcase setup failure\n"); + n2 = __of_node_alloc("/testcase-data/changeset/n2", GFP_KERNEL); + selftest(n2, "testcase setup failure\n"); + n21 = __of_node_alloc("/testcase-data/changeset/n2/n21", GFP_KERNEL); + selftest(n21, "testcase setup failure %p\n", n21); + nremove = of_find_node_by_path("/testcase-data/changeset/node-remove"); + selftest(nremove, "testcase setup failure\n"); + ppadd = __of_prop_dup(&padd, GFP_KERNEL); + selftest(ppadd, "testcase setup failure\n"); + ppupdate = __of_prop_dup(&pupdate, GFP_KERNEL); + selftest(ppupdate, "testcase setup failure\n"); + parent = nremove->parent; + n1->parent = parent; + n2->parent = parent; + n21->parent = n2; + n2->child = n21; + ppremove = of_find_property(parent, "prop-remove", NULL); + selftest(ppremove, "failed to find removal prop"); + + of_changeset_init(&chgset); + selftest(!of_changeset_attach_node(&chgset, n1), "fail attach n1\n"); + selftest(!of_changeset_attach_node(&chgset, n2), "fail attach n2\n"); + selftest(!of_changeset_detach_node(&chgset, nremove), "fail remove node\n"); + selftest(!of_changeset_attach_node(&chgset, n21), "fail attach n21\n"); + selftest(!of_changeset_add_property(&chgset, parent, ppadd), "fail add prop\n"); + selftest(!of_changeset_update_property(&chgset, parent, ppupdate), "fail update prop\n"); + selftest(!of_changeset_remove_property(&chgset, parent, ppremove), "fail remove prop\n"); + mutex_lock(&of_mutex); + selftest(!of_changeset_apply(&chgset), "apply failed\n"); + mutex_unlock(&of_mutex); + + mutex_lock(&of_mutex); + selftest(!of_changeset_revert(&chgset), "revert failed\n"); + mutex_unlock(&of_mutex); + + of_changeset_destroy(&chgset); +#endif +} + static void __init of_selftest_parse_interrupts(void) { struct device_node *np; @@ -561,6 +611,7 @@ static int __init of_selftest(void) of_selftest_parse_phandle_with_args(); of_selftest_property_match_string(); of_selftest_property_copy(); + of_selftest_changeset(); of_selftest_parse_interrupts(); of_selftest_parse_interrupts_extended(); of_selftest_match_node(); diff --git a/drivers/of/testcase-data/testcases.dtsi b/drivers/of/testcase-data/testcases.dtsi index 6d8d980ac858..669bb07df142 100644 --- a/drivers/of/testcase-data/testcases.dtsi +++ b/drivers/of/testcase-data/testcases.dtsi @@ -1,3 +1,13 @@ +/ { + testcase-data { + changeset { + prop-update = "hello"; + prop-remove = "world"; + node-remove { + }; + }; + }; +}; #include "tests-phandle.dtsi" #include "tests-interrupts.dtsi" #include "tests-match.dtsi" diff --git a/include/linux/of.h b/include/linux/of.h index 400f18cb4fff..bc91fbb13ce8 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -786,4 +786,80 @@ typedef void (*of_init_fn_1)(struct device_node *); #define OF_DECLARE_2(table, name, compat, fn) \ _OF_DECLARE(table, name, compat, fn, of_init_fn_2) +/** + * struct of_changeset_entry - Holds a changeset entry + * + * @node: list_head for the log list + * @action: notifier action + * @np: pointer to the device node affected + * @prop: pointer to the property affected + * @old_prop: hold a pointer to the original property + * + * Every modification of the device tree during a changeset + * is held in a list of of_changeset_entry structures. + * That way we can recover from a partial application, or we can + * revert the changeset + */ +struct of_changeset_entry { + struct list_head node; + unsigned long action; + struct device_node *np; + struct property *prop; + struct property *old_prop; +}; + +/** + * struct of_changeset - changeset tracker structure + * + * @entries: list_head for the changeset entries + * + * changesets are a convenient way to apply bulk changes to the + * live tree. In case of an error, changes are rolled-back. + * changesets live on after initial application, and if not + * destroyed after use, they can be reverted in one single call. + */ +struct of_changeset { + struct list_head entries; +}; + +#ifdef CONFIG_OF_DYNAMIC +extern void of_changeset_init(struct of_changeset *ocs); +extern void of_changeset_destroy(struct of_changeset *ocs); +extern int of_changeset_apply(struct of_changeset *ocs); +extern int of_changeset_revert(struct of_changeset *ocs); +extern int of_changeset_action(struct of_changeset *ocs, + unsigned long action, struct device_node *np, + struct property *prop); + +static inline int of_changeset_attach_node(struct of_changeset *ocs, + struct device_node *np) +{ + return of_changeset_action(ocs, OF_RECONFIG_ATTACH_NODE, np, NULL); +} + +static inline int of_changeset_detach_node(struct of_changeset *ocs, + struct device_node *np) +{ + return of_changeset_action(ocs, OF_RECONFIG_DETACH_NODE, np, NULL); +} + +static inline int of_changeset_add_property(struct of_changeset *ocs, + struct device_node *np, struct property *prop) +{ + return of_changeset_action(ocs, OF_RECONFIG_ADD_PROPERTY, np, prop); +} + +static inline int of_changeset_remove_property(struct of_changeset *ocs, + struct device_node *np, struct property *prop) +{ + return of_changeset_action(ocs, OF_RECONFIG_REMOVE_PROPERTY, np, prop); +} + +static inline int of_changeset_update_property(struct of_changeset *ocs, + struct device_node *np, struct property *prop) +{ + return of_changeset_action(ocs, OF_RECONFIG_UPDATE_PROPERTY, np, prop); +} +#endif + #endif /* _LINUX_OF_H */ -- cgit v1.2.3 From 120703f9eb32033f0e39bdc552c0273c8ab45f33 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Thu, 3 Apr 2014 20:31:30 -0300 Subject: [media] rc-core: document the protocol type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Right now the protocol information is not preserved, rc-core gets handed a scancode but has no idea which protocol it corresponds to. This patch (which required reading through the source/keymap for all drivers, not fun) makes the protocol information explicit which is important documentation and makes it easier to e.g. support multiple protocols with one decoder (think rc5 and rc-streamzap). The information isn't used yet so there should be no functional changes. [m.chehab@samsung.com: rebased, added cxusb and removed bad whitespacing] Signed-off-by: David Härdeman Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ir-kbd-i2c.c | 5 +- drivers/media/pci/bt8xx/bttv-input.c | 12 ++-- drivers/media/pci/cx88/cx88-input.c | 26 ++++++-- drivers/media/pci/dm1105/dm1105.c | 3 +- drivers/media/pci/saa7134/saa7134-input.c | 6 +- drivers/media/pci/ttpci/budget-ci.c | 8 +-- drivers/media/rc/ati_remote.c | 4 +- drivers/media/rc/img-ir/img-ir-hw.c | 10 +-- drivers/media/rc/img-ir/img-ir-hw.h | 3 +- drivers/media/rc/img-ir/img-ir-jvc.c | 4 +- drivers/media/rc/img-ir/img-ir-nec.c | 4 +- drivers/media/rc/img-ir/img-ir-sanyo.c | 4 +- drivers/media/rc/img-ir/img-ir-sharp.c | 4 +- drivers/media/rc/img-ir/img-ir-sony.c | 12 ++-- drivers/media/rc/imon.c | 5 +- drivers/media/rc/ir-jvc-decoder.c | 2 +- drivers/media/rc/ir-nec-decoder.c | 2 +- drivers/media/rc/ir-rc5-decoder.c | 5 +- drivers/media/rc/ir-rc5-sz-decoder.c | 2 +- drivers/media/rc/ir-rc6-decoder.c | 37 ++++++++--- drivers/media/rc/ir-sanyo-decoder.c | 2 +- drivers/media/rc/ir-sharp-decoder.c | 2 +- drivers/media/rc/ir-sony-decoder.c | 6 +- drivers/media/rc/rc-main.c | 30 +++++---- drivers/media/usb/dvb-usb-v2/af9015.c | 18 +++--- drivers/media/usb/dvb-usb-v2/af9035.c | 9 +-- drivers/media/usb/dvb-usb-v2/anysee.c | 3 +- drivers/media/usb/dvb-usb-v2/az6007.c | 25 ++++---- drivers/media/usb/dvb-usb-v2/lmedm04.c | 9 ++- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 12 ++-- drivers/media/usb/dvb-usb/cxusb.c | 2 +- drivers/media/usb/dvb-usb/dib0700_core.c | 9 ++- drivers/media/usb/dvb-usb/dib0700_devices.c | 24 +++---- drivers/media/usb/dvb-usb/dw2102.c | 7 ++- drivers/media/usb/dvb-usb/m920x.c | 2 +- drivers/media/usb/dvb-usb/pctv452e.c | 8 +-- drivers/media/usb/dvb-usb/ttusb2.c | 6 +- drivers/media/usb/em28xx/em28xx-input.c | 98 +++++++++++++++++------------ drivers/media/usb/tm6000/tm6000-input.c | 51 ++++++++++----- include/media/rc-core.h | 8 ++- 40 files changed, 305 insertions(+), 184 deletions(-) (limited to 'include') diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c index 143cb2b26c46..f9c4233ba96e 100644 --- a/drivers/media/i2c/ir-kbd-i2c.c +++ b/drivers/media/i2c/ir-kbd-i2c.c @@ -261,8 +261,9 @@ static int ir_key_poll(struct IR_i2c *ir) } if (rc) { - dprintk(1, "%s: scancode = 0x%08x\n", __func__, scancode); - rc_keydown(ir->rc, scancode, toggle); + dprintk(1, "%s: proto = 0x%04x, scancode = 0x%08x\n", + __func__, protocol, scancode); + rc_keydown(ir->rc, protocol, scancode, toggle); } return 0; } diff --git a/drivers/media/pci/bt8xx/bttv-input.c b/drivers/media/pci/bt8xx/bttv-input.c index e745f5a04a40..67c8d6b2c335 100644 --- a/drivers/media/pci/bt8xx/bttv-input.c +++ b/drivers/media/pci/bt8xx/bttv-input.c @@ -73,12 +73,12 @@ static void ir_handle_key(struct bttv *btv) if ((ir->mask_keydown && (gpio & ir->mask_keydown)) || (ir->mask_keyup && !(gpio & ir->mask_keyup))) { - rc_keydown_notimeout(ir->dev, data, 0); + rc_keydown_notimeout(ir->dev, RC_TYPE_UNKNOWN, data, 0); } else { /* HACK: Probably, ir->mask_keydown is missing for this board */ if (btv->c.type == BTTV_BOARD_WINFAST2000) - rc_keydown_notimeout(ir->dev, data, 0); + rc_keydown_notimeout(ir->dev, RC_TYPE_UNKNOWN, data, 0); rc_keyup(ir->dev); } @@ -103,7 +103,7 @@ static void ir_enltv_handle_key(struct bttv *btv) gpio, data, (gpio & ir->mask_keyup) ? " up" : "up/down"); - rc_keydown_notimeout(ir->dev, data, 0); + rc_keydown_notimeout(ir->dev, RC_TYPE_UNKNOWN, data, 0); if (keyup) rc_keyup(ir->dev); } else { @@ -117,7 +117,7 @@ static void ir_enltv_handle_key(struct bttv *btv) if (keyup) rc_keyup(ir->dev); else - rc_keydown_notimeout(ir->dev, data, 0); + rc_keydown_notimeout(ir->dev, RC_TYPE_UNKNOWN, data, 0); } ir->last_gpio = data | keyup; @@ -241,8 +241,8 @@ static void bttv_rc5_timer_end(unsigned long data) return; } - scancode = system << 8 | command; - rc_keydown(ir->dev, scancode, toggle); + scancode = RC_SCANCODE_RC5(system, command); + rc_keydown(ir->dev, RC_TYPE_RC5, scancode, toggle); dprintk("scancode %x, toggle %x\n", scancode, toggle); } diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c index 779fc630327a..9bf48ca619c7 100644 --- a/drivers/media/pci/cx88/cx88-input.c +++ b/drivers/media/pci/cx88/cx88-input.c @@ -130,25 +130,41 @@ static void cx88_ir_handle_key(struct cx88_IR *ir) data = (data << 4) | ((gpio_key & 0xf0) >> 4); - rc_keydown(ir->dev, data, 0); + rc_keydown(ir->dev, RC_TYPE_UNKNOWN, data, 0); + + } else if (ir->core->boardnr == CX88_BOARD_PROLINK_PLAYTVPVR || + ir->core->boardnr == CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO) { + /* bit cleared on keydown, NEC scancode, 0xAAAACC, A = 0x866b */ + u16 addr; + u8 cmd; + u32 scancode; + + addr = (data >> 8) & 0xffff; + cmd = (data >> 0) & 0x00ff; + scancode = RC_SCANCODE_NECX(addr, cmd); + + if (0 == (gpio & ir->mask_keyup)) + rc_keydown_notimeout(ir->dev, RC_TYPE_NEC, scancode, 0); + else + rc_keyup(ir->dev); } else if (ir->mask_keydown) { /* bit set on keydown */ if (gpio & ir->mask_keydown) - rc_keydown_notimeout(ir->dev, data, 0); + rc_keydown_notimeout(ir->dev, RC_TYPE_UNKNOWN, data, 0); else rc_keyup(ir->dev); } else if (ir->mask_keyup) { /* bit cleared on keydown */ if (0 == (gpio & ir->mask_keyup)) - rc_keydown_notimeout(ir->dev, data, 0); + rc_keydown_notimeout(ir->dev, RC_TYPE_UNKNOWN, data, 0); else rc_keyup(ir->dev); } else { /* can't distinguish keydown/up :-/ */ - rc_keydown_notimeout(ir->dev, data, 0); + rc_keydown_notimeout(ir->dev, RC_TYPE_UNKNOWN, data, 0); rc_keyup(ir->dev); } } @@ -329,6 +345,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) * 002-T mini RC, provided with newer PV hardware */ ir_codes = RC_MAP_PIXELVIEW_MK12; + rc_type = RC_BIT_NEC; ir->gpio_addr = MO_GP1_IO; ir->mask_keyup = 0x80; ir->polling = 10; /* ms */ @@ -416,7 +433,6 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) break; case CX88_BOARD_TWINHAN_VP1027_DVBS: ir_codes = RC_MAP_TWINHAN_VP1027_DVBS; - rc_type = RC_BIT_NEC; ir->sampling = 0xff00; /* address */ break; } diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c index e60ac35fc10c..e8826c535ccd 100644 --- a/drivers/media/pci/dm1105/dm1105.c +++ b/drivers/media/pci/dm1105/dm1105.c @@ -678,7 +678,8 @@ static void dm1105_emit_key(struct work_struct *work) data = (ircom >> 8) & 0x7f; - rc_keydown(ir->dev, data, 0); + /* FIXME: UNKNOWN because we don't generate a full NEC scancode (yet?) */ + rc_keydown(ir->dev, RC_TYPE_UNKNOWN, data, 0); } /* work handler */ diff --git a/drivers/media/pci/saa7134/saa7134-input.c b/drivers/media/pci/saa7134/saa7134-input.c index 73670ed7156b..43dd8bda02e1 100644 --- a/drivers/media/pci/saa7134/saa7134-input.c +++ b/drivers/media/pci/saa7134/saa7134-input.c @@ -83,14 +83,14 @@ static int build_key(struct saa7134_dev *dev) if (data == ir->mask_keycode) rc_keyup(ir->dev); else - rc_keydown_notimeout(ir->dev, data, 0); + rc_keydown_notimeout(ir->dev, RC_TYPE_UNKNOWN, data, 0); return 0; } if (ir->polling) { if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) || (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) { - rc_keydown_notimeout(ir->dev, data, 0); + rc_keydown_notimeout(ir->dev, RC_TYPE_UNKNOWN, data, 0); } else { rc_keyup(ir->dev); } @@ -98,7 +98,7 @@ static int build_key(struct saa7134_dev *dev) else { /* IRQ driven mode - handle key press and release in one go */ if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) || (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) { - rc_keydown_notimeout(ir->dev, data, 0); + rc_keydown_notimeout(ir->dev, RC_TYPE_UNKNOWN, data, 0); rc_keyup(ir->dev); } } diff --git a/drivers/media/pci/ttpci/budget-ci.c b/drivers/media/pci/ttpci/budget-ci.c index 0acf9202103d..41ce7dec60e6 100644 --- a/drivers/media/pci/ttpci/budget-ci.c +++ b/drivers/media/pci/ttpci/budget-ci.c @@ -161,14 +161,14 @@ static void msp430_ir_interrupt(unsigned long data) return; if (budget_ci->ir.full_rc5) { - rc_keydown(dev, - budget_ci->ir.rc5_device <<8 | budget_ci->ir.ir_key, - (command & 0x20) ? 1 : 0); + rc_keydown(dev, RC_TYPE_RC5, + RC_SCANCODE_RC5(budget_ci->ir.rc5_device, budget_ci->ir.ir_key), + !!(command & 0x20)); return; } /* FIXME: We should generate complete scancodes for all devices */ - rc_keydown(dev, budget_ci->ir.ir_key, (command & 0x20) ? 1 : 0); + rc_keydown(dev, RC_TYPE_UNKNOWN, budget_ci->ir.ir_key, !!(command & 0x20)); } static int msp430_ir_init(struct budget_ci *budget_ci) diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index 2df7c5516013..8730b32a067c 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -622,8 +622,8 @@ static void ati_remote_input_report(struct urb *urb) * it would cause ghost repeats which would be a * regression for this driver. */ - rc_keydown_notimeout(ati_remote->rdev, scancode, - data[2]); + rc_keydown_notimeout(ati_remote->rdev, RC_TYPE_OTHER, + scancode, data[2]); rc_keyup(ati_remote->rdev); } return; diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c index 153c0840bf4c..b0ec55f52228 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.c +++ b/drivers/media/rc/img-ir/img-ir-hw.c @@ -512,7 +512,7 @@ unlock: static int img_ir_set_normal_filter(struct rc_dev *dev, struct rc_scancode_filter *sc_filter) { - return img_ir_set_filter(dev, RC_FILTER_NORMAL, sc_filter); + return img_ir_set_filter(dev, RC_FILTER_NORMAL, sc_filter); } static int img_ir_set_wakeup_filter(struct rc_dev *dev, @@ -795,9 +795,11 @@ static void img_ir_handle_data(struct img_ir_priv *priv, u32 len, u64 raw) struct img_ir_priv_hw *hw = &priv->hw; const struct img_ir_decoder *dec = hw->decoder; int ret = IMG_IR_SCANCODE; - int scancode; + u32 scancode; + enum rc_type protocol = RC_TYPE_UNKNOWN; + if (dec->scancode) - ret = dec->scancode(len, raw, &scancode, hw->enabled_protocols); + ret = dec->scancode(len, raw, &protocol, &scancode, hw->enabled_protocols); else if (len >= 32) scancode = (u32)raw; else if (len < 32) @@ -806,7 +808,7 @@ static void img_ir_handle_data(struct img_ir_priv *priv, u32 len, u64 raw) len, (unsigned long long)raw); if (ret == IMG_IR_SCANCODE) { dev_dbg(priv->dev, "decoded scan code %#x\n", scancode); - rc_keydown(hw->rdev, scancode, 0); + rc_keydown(hw->rdev, protocol, scancode, 0); img_ir_end_repeat(priv); } else if (ret == IMG_IR_REPEATCODE) { if (hw->mode == IMG_IR_M_REPEATING) { diff --git a/drivers/media/rc/img-ir/img-ir-hw.h b/drivers/media/rc/img-ir/img-ir-hw.h index 450f17dfb66a..3e40ce87b898 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.h +++ b/drivers/media/rc/img-ir/img-ir-hw.h @@ -162,7 +162,8 @@ struct img_ir_decoder { struct img_ir_control control; /* scancode logic */ - int (*scancode)(int len, u64 raw, int *scancode, u64 protocols); + int (*scancode)(int len, u64 raw, enum rc_type *protocol, + u32 *scancode, u64 enabled_protocols); int (*filter)(const struct rc_scancode_filter *in, struct img_ir_filter *out, u64 protocols); }; diff --git a/drivers/media/rc/img-ir/img-ir-jvc.c b/drivers/media/rc/img-ir/img-ir-jvc.c index 85ee90f53242..a60dda8bf706 100644 --- a/drivers/media/rc/img-ir/img-ir-jvc.c +++ b/drivers/media/rc/img-ir/img-ir-jvc.c @@ -12,7 +12,8 @@ #include "img-ir-hw.h" /* Convert JVC data to a scancode */ -static int img_ir_jvc_scancode(int len, u64 raw, int *scancode, u64 protocols) +static int img_ir_jvc_scancode(int len, u64 raw, enum rc_type *protocol, + u32 *scancode, u64 enabled_protocols) { unsigned int cust, data; @@ -22,6 +23,7 @@ static int img_ir_jvc_scancode(int len, u64 raw, int *scancode, u64 protocols) cust = (raw >> 0) & 0xff; data = (raw >> 8) & 0xff; + *protocol = RC_TYPE_JVC; *scancode = cust << 8 | data; return IMG_IR_SCANCODE; } diff --git a/drivers/media/rc/img-ir/img-ir-nec.c b/drivers/media/rc/img-ir/img-ir-nec.c index f7520e25fbdb..739897549b5b 100644 --- a/drivers/media/rc/img-ir/img-ir-nec.c +++ b/drivers/media/rc/img-ir/img-ir-nec.c @@ -13,7 +13,8 @@ #include /* Convert NEC data to a scancode */ -static int img_ir_nec_scancode(int len, u64 raw, int *scancode, u64 protocols) +static int img_ir_nec_scancode(int len, u64 raw, enum rc_type *protocol, + u32 *scancode, u64 enabled_protocols) { unsigned int addr, addr_inv, data, data_inv; /* a repeat code has no data */ @@ -45,6 +46,7 @@ static int img_ir_nec_scancode(int len, u64 raw, int *scancode, u64 protocols) *scancode = addr << 8 | data; } + *protocol = RC_TYPE_NEC; return IMG_IR_SCANCODE; } diff --git a/drivers/media/rc/img-ir/img-ir-sanyo.c b/drivers/media/rc/img-ir/img-ir-sanyo.c index 6755c945bce8..6b0653ecdf5a 100644 --- a/drivers/media/rc/img-ir/img-ir-sanyo.c +++ b/drivers/media/rc/img-ir/img-ir-sanyo.c @@ -23,7 +23,8 @@ #include "img-ir-hw.h" /* Convert Sanyo data to a scancode */ -static int img_ir_sanyo_scancode(int len, u64 raw, int *scancode, u64 protocols) +static int img_ir_sanyo_scancode(int len, u64 raw, enum rc_type *protocol, + u32 *scancode, u64 enabled_protocols) { unsigned int addr, addr_inv, data, data_inv; /* a repeat code has no data */ @@ -43,6 +44,7 @@ static int img_ir_sanyo_scancode(int len, u64 raw, int *scancode, u64 protocols) return -EINVAL; /* Normal Sanyo */ + *protocol = RC_TYPE_SANYO; *scancode = addr << 8 | data; return IMG_IR_SCANCODE; } diff --git a/drivers/media/rc/img-ir/img-ir-sharp.c b/drivers/media/rc/img-ir/img-ir-sharp.c index 5867be088cce..3300a38802ac 100644 --- a/drivers/media/rc/img-ir/img-ir-sharp.c +++ b/drivers/media/rc/img-ir/img-ir-sharp.c @@ -12,7 +12,8 @@ #include "img-ir-hw.h" /* Convert Sharp data to a scancode */ -static int img_ir_sharp_scancode(int len, u64 raw, int *scancode, u64 protocols) +static int img_ir_sharp_scancode(int len, u64 raw, enum rc_type *protocol, + u32 *scancode, u64 enabled_protocols) { unsigned int addr, cmd, exp, chk; @@ -31,6 +32,7 @@ static int img_ir_sharp_scancode(int len, u64 raw, int *scancode, u64 protocols) /* probably the second half of the message */ return -EINVAL; + *protocol = RC_TYPE_SHARP; *scancode = addr << 8 | cmd; return IMG_IR_SCANCODE; } diff --git a/drivers/media/rc/img-ir/img-ir-sony.c b/drivers/media/rc/img-ir/img-ir-sony.c index b9029aecceac..3a0f17b0752c 100644 --- a/drivers/media/rc/img-ir/img-ir-sony.c +++ b/drivers/media/rc/img-ir/img-ir-sony.c @@ -12,35 +12,39 @@ #include "img-ir-hw.h" /* Convert Sony data to a scancode */ -static int img_ir_sony_scancode(int len, u64 raw, int *scancode, u64 protocols) +static int img_ir_sony_scancode(int len, u64 raw, enum rc_type *protocol, + u32 *scancode, u64 enabled_protocols) { unsigned int dev, subdev, func; switch (len) { case 12: - if (!(protocols & RC_BIT_SONY12)) + if (!(enabled_protocols & RC_BIT_SONY12)) return -EINVAL; func = raw & 0x7f; /* first 7 bits */ raw >>= 7; dev = raw & 0x1f; /* next 5 bits */ subdev = 0; + *protocol = RC_TYPE_SONY12; break; case 15: - if (!(protocols & RC_BIT_SONY15)) + if (!(enabled_protocols & RC_BIT_SONY15)) return -EINVAL; func = raw & 0x7f; /* first 7 bits */ raw >>= 7; dev = raw & 0xff; /* next 8 bits */ subdev = 0; + *protocol = RC_TYPE_SONY15; break; case 20: - if (!(protocols & RC_BIT_SONY20)) + if (!(enabled_protocols & RC_BIT_SONY20)) return -EINVAL; func = raw & 0x7f; /* first 7 bits */ raw >>= 7; dev = raw & 0x1f; /* next 5 bits */ raw >>= 5; subdev = raw & 0xff; /* next 8 bits */ + *protocol = RC_TYPE_SONY20; break; default: return -EINVAL; diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index 6f24e77b1488..eb37f2622364 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -1579,7 +1579,10 @@ static void imon_incoming_packet(struct imon_context *ictx, if (press_type == 0) rc_keyup(ictx->rdev); else { - rc_keydown(ictx->rdev, ictx->rc_scancode, ictx->rc_toggle); + if (ictx->rc_type == RC_BIT_RC6_MCE) + rc_keydown(ictx->rdev, + ictx->rc_type == RC_BIT_RC6_MCE ? RC_TYPE_RC6_MCE : RC_TYPE_OTHER, + ictx->rc_scancode, ictx->rc_toggle); spin_lock_irqsave(&ictx->kc_lock, flags); ictx->last_keycode = ictx->kc; spin_unlock_irqrestore(&ictx->kc_lock, flags); diff --git a/drivers/media/rc/ir-jvc-decoder.c b/drivers/media/rc/ir-jvc-decoder.c index 4ea62a1dcfda..7b79eca83449 100644 --- a/drivers/media/rc/ir-jvc-decoder.c +++ b/drivers/media/rc/ir-jvc-decoder.c @@ -140,7 +140,7 @@ again: scancode = (bitrev8((data->bits >> 8) & 0xff) << 8) | (bitrev8((data->bits >> 0) & 0xff) << 0); IR_dprintk(1, "JVC scancode 0x%04x\n", scancode); - rc_keydown(dev, scancode, data->toggle); + rc_keydown(dev, RC_TYPE_JVC, scancode, data->toggle); data->first = false; data->old_bits = data->bits; } else if (data->bits == data->old_bits) { diff --git a/drivers/media/rc/ir-nec-decoder.c b/drivers/media/rc/ir-nec-decoder.c index 35c42e5e270b..c4333d5c0046 100644 --- a/drivers/media/rc/ir-nec-decoder.c +++ b/drivers/media/rc/ir-nec-decoder.c @@ -189,7 +189,7 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev) if (data->is_nec_x) data->necx_repeat = true; - rc_keydown(dev, scancode, 0); + rc_keydown(dev, RC_TYPE_NEC, scancode, 0); data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/ir-rc5-decoder.c b/drivers/media/rc/ir-rc5-decoder.c index 4295d9b250c8..3d38cbce5667 100644 --- a/drivers/media/rc/ir-rc5-decoder.c +++ b/drivers/media/rc/ir-rc5-decoder.c @@ -51,6 +51,7 @@ static int ir_rc5_decode(struct rc_dev *dev, struct ir_raw_event ev) struct rc5_dec *data = &dev->raw->rc5; u8 toggle; u32 scancode; + enum rc_type protocol; if (!rc_protocols_enabled(dev, RC_BIT_RC5 | RC_BIT_RC5X)) return 0; @@ -138,6 +139,7 @@ again: toggle = (data->bits & 0x20000) ? 1 : 0; command += (data->bits & 0x01000) ? 0 : 0x40; scancode = system << 16 | command << 8 | xdata; + protocol = RC_TYPE_RC5X; IR_dprintk(1, "RC5X scancode 0x%06x (toggle: %u)\n", scancode, toggle); @@ -154,12 +156,13 @@ again: toggle = (data->bits & 0x00800) ? 1 : 0; command += (data->bits & 0x01000) ? 0 : 0x40; scancode = system << 8 | command; + protocol = RC_TYPE_RC5; IR_dprintk(1, "RC5 scancode 0x%04x (toggle: %u)\n", scancode, toggle); } - rc_keydown(dev, scancode, toggle); + rc_keydown(dev, protocol, scancode, toggle); data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/ir-rc5-sz-decoder.c b/drivers/media/rc/ir-rc5-sz-decoder.c index dc18b7434db8..85c771112cf5 100644 --- a/drivers/media/rc/ir-rc5-sz-decoder.c +++ b/drivers/media/rc/ir-rc5-sz-decoder.c @@ -115,7 +115,7 @@ again: IR_dprintk(1, "RC5-sz scancode 0x%04x (toggle: %u)\n", scancode, toggle); - rc_keydown(dev, scancode, toggle); + rc_keydown(dev, RC_TYPE_RC5_SZ, scancode, toggle); data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c index cfbd64e3999c..1dc97a7b92a5 100644 --- a/drivers/media/rc/ir-rc6-decoder.c +++ b/drivers/media/rc/ir-rc6-decoder.c @@ -88,6 +88,7 @@ static int ir_rc6_decode(struct rc_dev *dev, struct ir_raw_event ev) struct rc6_dec *data = &dev->raw->rc6; u32 scancode; u8 toggle; + enum rc_type protocol; if (!rc_protocols_enabled(dev, RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 | RC_BIT_RC6_6A_32 | @@ -233,9 +234,11 @@ again: case RC6_MODE_0: scancode = data->body; toggle = data->toggle; + protocol = RC_TYPE_RC6_0; IR_dprintk(1, "RC6(0) scancode 0x%04x (toggle: %u)\n", scancode, toggle); break; + case RC6_MODE_6A: if (data->count > CHAR_BIT * sizeof data->body) { IR_dprintk(1, "RC6 too many (%u) data bits\n", @@ -244,23 +247,39 @@ again: } scancode = data->body; - if (data->count == RC6_6A_32_NBITS && - (scancode & RC6_6A_LCC_MASK) == RC6_6A_MCE_CC) { - /* MCE RC */ - toggle = (scancode & RC6_6A_MCE_TOGGLE_MASK) ? 1 : 0; - scancode &= ~RC6_6A_MCE_TOGGLE_MASK; - } else { + switch (data->count) { + case 20: + protocol = RC_TYPE_RC6_6A_20; + toggle = 0; + break; + case 24: + protocol = RC_BIT_RC6_6A_24; toggle = 0; + break; + case 32: + if ((scancode & RC6_6A_LCC_MASK) == RC6_6A_MCE_CC) { + protocol = RC_TYPE_RC6_MCE; + scancode &= ~RC6_6A_MCE_TOGGLE_MASK; + toggle = !!(scancode & RC6_6A_MCE_TOGGLE_MASK); + } else { + protocol = RC_BIT_RC6_6A_32; + toggle = 0; + } + break; + default: + IR_dprintk(1, "RC6(6A) unsupported length\n"); + goto out; } - IR_dprintk(1, "RC6(6A) scancode 0x%08x (toggle: %u)\n", - scancode, toggle); + + IR_dprintk(1, "RC6(6A) proto 0x%04x, scancode 0x%08x (toggle: %u)\n", + protocol, scancode, toggle); break; default: IR_dprintk(1, "RC6 unknown mode\n"); goto out; } - rc_keydown(dev, scancode, toggle); + rc_keydown(dev, protocol, scancode, toggle); data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/ir-sanyo-decoder.c b/drivers/media/rc/ir-sanyo-decoder.c index eb715f04dc27..5f7702261d23 100644 --- a/drivers/media/rc/ir-sanyo-decoder.c +++ b/drivers/media/rc/ir-sanyo-decoder.c @@ -167,7 +167,7 @@ static int ir_sanyo_decode(struct rc_dev *dev, struct ir_raw_event ev) scancode = address << 8 | command; IR_dprintk(1, "SANYO scancode: 0x%06x\n", scancode); - rc_keydown(dev, scancode, 0); + rc_keydown(dev, RC_TYPE_SANYO, scancode, 0); data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/ir-sharp-decoder.c b/drivers/media/rc/ir-sharp-decoder.c index 66d20394ceaa..c8f251939143 100644 --- a/drivers/media/rc/ir-sharp-decoder.c +++ b/drivers/media/rc/ir-sharp-decoder.c @@ -162,7 +162,7 @@ static int ir_sharp_decode(struct rc_dev *dev, struct ir_raw_event ev) scancode = address << 8 | command; IR_dprintk(1, "Sharp scancode 0x%04x\n", scancode); - rc_keydown(dev, scancode, 0); + rc_keydown(dev, RC_TYPE_SHARP, scancode, 0); data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/ir-sony-decoder.c b/drivers/media/rc/ir-sony-decoder.c index 599c19a73360..f485f9fe1e90 100644 --- a/drivers/media/rc/ir-sony-decoder.c +++ b/drivers/media/rc/ir-sony-decoder.c @@ -42,6 +42,7 @@ enum sony_state { static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) { struct sony_dec *data = &dev->raw->sony; + enum rc_type protocol; u32 scancode; u8 device, subdevice, function; @@ -131,6 +132,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) device = bitrev8((data->bits << 3) & 0xF8); subdevice = 0; function = bitrev8((data->bits >> 4) & 0xFE); + protocol = RC_TYPE_SONY12; break; case 15: if (!rc_protocols_enabled(dev, RC_BIT_SONY15)) { @@ -140,6 +142,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) device = bitrev8((data->bits >> 0) & 0xFF); subdevice = 0; function = bitrev8((data->bits >> 7) & 0xFE); + protocol = RC_TYPE_SONY15; break; case 20: if (!rc_protocols_enabled(dev, RC_BIT_SONY20)) { @@ -149,6 +152,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) device = bitrev8((data->bits >> 5) & 0xF8); subdevice = bitrev8((data->bits >> 0) & 0xFF); function = bitrev8((data->bits >> 12) & 0xFE); + protocol = RC_TYPE_SONY20; break; default: IR_dprintk(1, "Sony invalid bitcount %u\n", data->count); @@ -157,7 +161,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) scancode = device << 16 | subdevice << 8 | function; IR_dprintk(1, "Sony(%u) scancode 0x%05x\n", data->count, scancode); - rc_keydown(dev, scancode, 0); + rc_keydown(dev, protocol, scancode, 0); data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 970b93d6f399..9abcf77246fa 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -623,6 +623,7 @@ EXPORT_SYMBOL_GPL(rc_repeat); /** * ir_do_keydown() - internal function to process a keypress * @dev: the struct rc_dev descriptor of the device + * @protocol: the protocol of the keypress * @scancode: the scancode of the keypress * @keycode: the keycode of the keypress * @toggle: the toggle value of the keypress @@ -630,12 +631,13 @@ EXPORT_SYMBOL_GPL(rc_repeat); * This function is used internally to register a keypress, it must be * called with keylock held. */ -static void ir_do_keydown(struct rc_dev *dev, int scancode, - u32 keycode, u8 toggle) +static void ir_do_keydown(struct rc_dev *dev, enum rc_type protocol, + u32 scancode, u32 keycode, u8 toggle) { bool new_event = (!dev->keypressed || + dev->last_protocol != protocol || dev->last_scancode != scancode || - dev->last_toggle != toggle); + dev->last_toggle != toggle); if (new_event && dev->keypressed) ir_do_keyup(dev, false); @@ -645,13 +647,14 @@ static void ir_do_keydown(struct rc_dev *dev, int scancode, if (new_event && keycode != KEY_RESERVED) { /* Register a keypress */ dev->keypressed = true; + dev->last_protocol = protocol; dev->last_scancode = scancode; dev->last_toggle = toggle; dev->last_keycode = keycode; IR_dprintk(1, "%s: key down event, " - "key 0x%04x, scancode 0x%04x\n", - dev->input_name, keycode, scancode); + "key 0x%04x, protocol 0x%04x, scancode 0x%08x\n", + dev->input_name, keycode, protocol, scancode); input_report_key(dev->input_dev, keycode, 1); led_trigger_event(led_feedback, LED_FULL); @@ -663,20 +666,21 @@ static void ir_do_keydown(struct rc_dev *dev, int scancode, /** * rc_keydown() - generates input event for a key press * @dev: the struct rc_dev descriptor of the device - * @scancode: the scancode that we're seeking + * @protocol: the protocol for the keypress + * @scancode: the scancode for the keypress * @toggle: the toggle value (protocol dependent, if the protocol doesn't * support toggle values, this should be set to zero) * * This routine is used to signal that a key has been pressed on the * remote control. */ -void rc_keydown(struct rc_dev *dev, int scancode, u8 toggle) +void rc_keydown(struct rc_dev *dev, enum rc_type protocol, u32 scancode, u8 toggle) { unsigned long flags; u32 keycode = rc_g_keycode_from_table(dev, scancode); spin_lock_irqsave(&dev->keylock, flags); - ir_do_keydown(dev, scancode, keycode, toggle); + ir_do_keydown(dev, protocol, scancode, keycode, toggle); if (dev->keypressed) { dev->keyup_jiffies = jiffies + msecs_to_jiffies(IR_KEYPRESS_TIMEOUT); @@ -690,20 +694,22 @@ EXPORT_SYMBOL_GPL(rc_keydown); * rc_keydown_notimeout() - generates input event for a key press without * an automatic keyup event at a later time * @dev: the struct rc_dev descriptor of the device - * @scancode: the scancode that we're seeking + * @protocol: the protocol for the keypress + * @scancode: the scancode for the keypress * @toggle: the toggle value (protocol dependent, if the protocol doesn't * support toggle values, this should be set to zero) * * This routine is used to signal that a key has been pressed on the * remote control. The driver must manually call rc_keyup() at a later stage. */ -void rc_keydown_notimeout(struct rc_dev *dev, int scancode, u8 toggle) +void rc_keydown_notimeout(struct rc_dev *dev, enum rc_type protocol, + u32 scancode, u8 toggle) { unsigned long flags; u32 keycode = rc_g_keycode_from_table(dev, scancode); spin_lock_irqsave(&dev->keylock, flags); - ir_do_keydown(dev, scancode, keycode, toggle); + ir_do_keydown(dev, protocol, scancode, keycode, toggle); spin_unlock_irqrestore(&dev->keylock, flags); } EXPORT_SYMBOL_GPL(rc_keydown_notimeout); @@ -1315,7 +1321,7 @@ int rc_register_device(struct rc_dev *dev) dev->dev.groups = dev->sysfs_groups; dev->sysfs_groups[attr++] = &rc_dev_protocol_attr_grp; if (dev->s_filter) - dev->sysfs_groups[attr++] = &rc_dev_filter_attr_grp; + dev->sysfs_groups[attr++] = &rc_dev_filter_attr_grp; if (dev->s_wakeup_filter) dev->sysfs_groups[attr++] = &rc_dev_wakeup_filter_attr_grp; if (dev->change_wakeup_protocol) diff --git a/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c index da47d2392f2a..5ca738ab44e0 100644 --- a/drivers/media/usb/dvb-usb-v2/af9015.c +++ b/drivers/media/usb/dvb-usb-v2/af9015.c @@ -1213,7 +1213,7 @@ static int af9015_rc_query(struct dvb_usb_device *d) if ((state->rc_repeat != buf[6] || buf[0]) && !memcmp(&buf[12], state->rc_last, 4)) { dev_dbg(&d->udev->dev, "%s: key repeated\n", __func__); - rc_keydown(d->rc_dev, state->rc_keycode, 0); + rc_repeat(d->rc_dev); state->rc_repeat = buf[6]; return ret; } @@ -1233,18 +1233,22 @@ static int af9015_rc_query(struct dvb_usb_device *d) if (buf[14] == (u8) ~buf[15]) { if (buf[12] == (u8) ~buf[13]) { /* NEC */ - state->rc_keycode = buf[12] << 8 | buf[14]; + state->rc_keycode = RC_SCANCODE_NEC(buf[12], + buf[14]); } else { /* NEC extended*/ - state->rc_keycode = buf[12] << 16 | - buf[13] << 8 | buf[14]; + state->rc_keycode = RC_SCANCODE_NECX(buf[12] << 8 | + buf[13], + buf[14]); } } else { /* 32 bit NEC */ - state->rc_keycode = buf[12] << 24 | buf[13] << 16 | - buf[14] << 8 | buf[15]; + state->rc_keycode = RC_SCANCODE_NEC32(buf[12] << 24 | + buf[13] << 16 | + buf[14] << 8 | + buf[15]); } - rc_keydown(d->rc_dev, state->rc_keycode, 0); + rc_keydown(d->rc_dev, RC_TYPE_NEC, state->rc_keycode, 0); } else { dev_dbg(&d->udev->dev, "%s: no key press\n", __func__); /* Invalidate last keypress */ diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 7b9b75f60774..221af2b6e6cf 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -1313,19 +1313,20 @@ static int af9035_rc_query(struct dvb_usb_device *d) if ((buf[2] + buf[3]) == 0xff) { if ((buf[0] + buf[1]) == 0xff) { /* NEC standard 16bit */ - key = buf[0] << 8 | buf[2]; + key = RC_SCANCODE_NEC(buf[0], buf[2]); } else { /* NEC extended 24bit */ - key = buf[0] << 16 | buf[1] << 8 | buf[2]; + key = RC_SCANCODE_NECX(buf[0] << 8 | buf[1], buf[2]); } } else { /* NEC full code 32bit */ - key = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; + key = RC_SCANCODE_NEC32(buf[0] << 24 | buf[1] << 16 | + buf[2] << 8 | buf[3]); } dev_dbg(&d->udev->dev, "%s: %*ph\n", __func__, 4, buf); - rc_keydown(d->rc_dev, key, 0); + rc_keydown(d->rc_dev, RC_TYPE_NEC, key, 0); return 0; diff --git a/drivers/media/usb/dvb-usb-v2/anysee.c b/drivers/media/usb/dvb-usb-v2/anysee.c index eeab79bdd2aa..e4a2382196f0 100644 --- a/drivers/media/usb/dvb-usb-v2/anysee.c +++ b/drivers/media/usb/dvb-usb-v2/anysee.c @@ -1038,7 +1038,8 @@ static int anysee_rc_query(struct dvb_usb_device *d) if (ircode[0]) { dev_dbg(&d->udev->dev, "%s: key pressed %02x\n", __func__, ircode[1]); - rc_keydown(d->rc_dev, 0x08 << 8 | ircode[1], 0); + rc_keydown(d->rc_dev, RC_TYPE_NEC, + RC_SCANCODE_NEC(0x08, ircode[1]), 0); } return 0; diff --git a/drivers/media/usb/dvb-usb-v2/az6007.c b/drivers/media/usb/dvb-usb-v2/az6007.c index c3c4b98733bf..935dbaa80ef0 100644 --- a/drivers/media/usb/dvb-usb-v2/az6007.c +++ b/drivers/media/usb/dvb-usb-v2/az6007.c @@ -207,24 +207,27 @@ static int az6007_streaming_ctrl(struct dvb_frontend *fe, int onoff) static int az6007_rc_query(struct dvb_usb_device *d) { struct az6007_device_state *st = d_to_priv(d); - unsigned code = 0; + unsigned code; az6007_read(d, AZ6007_READ_IR, 0, 0, st->data, 10); if (st->data[1] == 0x44) return 0; - if ((st->data[1] ^ st->data[2]) == 0xff) - code = st->data[1]; - else - code = st->data[1] << 8 | st->data[2]; - - if ((st->data[3] ^ st->data[4]) == 0xff) - code = code << 8 | st->data[3]; - else - code = code << 16 | st->data[3] << 8 | st->data[4]; + if ((st->data[3] ^ st->data[4]) == 0xff) { + if ((st->data[1] ^ st->data[2]) == 0xff) + code = RC_SCANCODE_NEC(st->data[1], st->data[3]); + else + code = RC_SCANCODE_NECX(st->data[1] << 8 | st->data[2], + st->data[3]); + } else { + code = RC_SCANCODE_NEC32(st->data[1] << 24 | + st->data[2] << 16 | + st->data[3] << 8 | + st->data[4]); + } - rc_keydown(d->rc_dev, code, st->data[5]); + rc_keydown(d->rc_dev, RC_TYPE_NEC, code, st->data[5]); return 0; } diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c index 7d685bc8c2c0..e332af731187 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -286,14 +286,13 @@ static void lme2510_int_response(struct urb *lme_urb) case 0xaa: debug_data_snipet(1, "INT Remote data snipet", ibuf); if ((ibuf[4] + ibuf[5]) == 0xff) { - key = ibuf[5]; - key += (ibuf[3] > 0) - ? (ibuf[3] ^ 0xff) << 8 : 0; - key += (ibuf[2] ^ 0xff) << 16; + key = RC_SCANCODE_NECX((ibuf[2] ^ 0xff) << 8 | + (ibuf[3] > 0) ? (ibuf[3] ^ 0xff) : 0, + ibuf[5]); deb_info(1, "INT Key =%08x", key); if (adap_to_d(adap)->rc_dev != NULL) rc_keydown(adap_to_d(adap)->rc_dev, - key, 0); + RC_TYPE_NEC, key, 0); } break; case 0xbb: diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index a676e4452847..57ac62d6fa1f 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1287,19 +1287,19 @@ static int rtl2831u_rc_query(struct dvb_usb_device *d) if (buf[2] == (u8) ~buf[3]) { if (buf[0] == (u8) ~buf[1]) { /* NEC standard (16 bit) */ - rc_code = buf[0] << 8 | buf[2]; + rc_code = RC_SCANCODE_NEC(buf[0], buf[2]); } else { /* NEC extended (24 bit) */ - rc_code = buf[0] << 16 | - buf[1] << 8 | buf[2]; + rc_code = RC_SCANCODE_NECX(buf[0] << 8 | buf[1], + buf[2]); } } else { /* NEC full (32 bit) */ - rc_code = buf[0] << 24 | buf[1] << 16 | - buf[2] << 8 | buf[3]; + rc_code = RC_SCANCODE_NEC32(buf[0] << 24 | buf[1] << 16 | + buf[2] << 8 | buf[3]); } - rc_keydown(d->rc_dev, rc_code, 0); + rc_keydown(d->rc_dev, RC_TYPE_NEC, rc_code, 0); ret = rtl28xx_wr_reg(d, SYS_IRRC_SR, 1); if (ret) diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c index f379f7e1fcb0..16bc579d1404 100644 --- a/drivers/media/usb/dvb-usb/cxusb.c +++ b/drivers/media/usb/dvb-usb/cxusb.c @@ -541,7 +541,7 @@ static int cxusb_tt_ct2_4400_rc_query(struct dvb_usb_device *d) rc5_addr = (cmd & 0x07C0) >> 6; /* bits 7-11 for address */ rc5_toggle = (cmd & 0x0800) >> 11; /* bit 12 for toggle */ keycode = (rc5_addr << 8) | rc5_cmd; - rc_keydown(d->rc_dev, keycode, rc5_toggle); + rc_keydown(d->rc_dev, RC_BIT_RC5, keycode, rc5_toggle); } return 0; diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c index 38b151f7ceac..50856dbf5496 100644 --- a/drivers/media/usb/dvb-usb/dib0700_core.c +++ b/drivers/media/usb/dvb-usb/dib0700_core.c @@ -669,6 +669,7 @@ static void dib0700_rc_urb_completion(struct urb *purb) { struct dvb_usb_device *d = purb->context; struct dib0700_rc_response *poll_reply; + enum rc_type protocol; u32 uninitialized_var(keycode); u8 toggle; @@ -702,6 +703,7 @@ static void dib0700_rc_urb_completion(struct urb *purb) switch (d->props.rc.core.protocol) { case RC_BIT_NEC: + protocol = RC_TYPE_NEC; toggle = 0; /* NEC protocol sends repeat code as 0 0 0 FF */ @@ -724,6 +726,7 @@ static void dib0700_rc_urb_completion(struct urb *purb) keycode = RC_SCANCODE_NECX(poll_reply->system << 8 | poll_reply->not_system, poll_reply->data); + } else { deb_data("NEC normal protocol\n"); keycode = RC_SCANCODE_NEC(poll_reply->system, @@ -733,9 +736,9 @@ static void dib0700_rc_urb_completion(struct urb *purb) break; default: deb_data("RC5 protocol\n"); - /* RC5 Protocol */ + protocol = RC_TYPE_RC5; toggle = poll_reply->report_id; - keycode = poll_reply->system << 8 | poll_reply->data; + keycode = RC_SCANCODE_RC5(poll_reply->system, poll_reply->data); break; } @@ -748,7 +751,7 @@ static void dib0700_rc_urb_completion(struct urb *purb) goto resubmit; } - rc_keydown(d->rc_dev, keycode, toggle); + rc_keydown(d->rc_dev, protocol, keycode, toggle); resubmit: /* Clean the buffer before we requeue */ diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c index 501947eaacfe..91e5ebb0f2c7 100644 --- a/drivers/media/usb/dvb-usb/dib0700_devices.c +++ b/drivers/media/usb/dvb-usb/dib0700_devices.c @@ -512,7 +512,8 @@ static u8 rc_request[] = { REQUEST_POLL_RC, 0 }; static int dib0700_rc_query_old_firmware(struct dvb_usb_device *d) { u8 key[4]; - u32 keycode; + enum rc_type protocol; + u32 scancode; u8 toggle; int i; struct dib0700_state *st = d->priv; @@ -539,28 +540,29 @@ static int dib0700_rc_query_old_firmware(struct dvb_usb_device *d) dib0700_rc_setup(d, NULL); /* reset ir sensor data to prevent false events */ - d->last_event = 0; switch (d->props.rc.core.protocol) { case RC_BIT_NEC: /* NEC protocol sends repeat code as 0 0 0 FF */ if ((key[3-2] == 0x00) && (key[3-3] == 0x00) && - (key[3] == 0xff)) - keycode = d->last_event; - else { - keycode = key[3-2] << 8 | key[3-3]; - d->last_event = keycode; + (key[3] == 0xff)) { + rc_repeat(d->rc_dev); + return 0; } - rc_keydown(d->rc_dev, keycode, 0); + protocol = RC_TYPE_NEC; + scancode = RC_SCANCODE_NEC(key[3-2], key[3-3]); + toggle = 0; break; + default: /* RC-5 protocol changes toggle bit on new keypress */ - keycode = key[3-2] << 8 | key[3-3]; + protocol = RC_TYPE_RC5; + scancode = RC_SCANCODE_RC5(key[3-2], key[3-3]); toggle = key[3-1]; - rc_keydown(d->rc_dev, keycode, toggle); - break; } + + rc_keydown(d->rc_dev, protocol, scancode, toggle); return 0; } diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index 7135a3e87b86..2add8c507ec9 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -1490,7 +1490,7 @@ static int dw2102_rc_query(struct dvb_usb_device *d) if (msg.buf[0] != 0xff) { deb_rc("%s: rc code: %x, %x\n", __func__, key[0], key[1]); - rc_keydown(d->rc_dev, key[0], 1); + rc_keydown(d->rc_dev, RC_TYPE_UNKNOWN, key[0], 0); } } @@ -1511,7 +1511,7 @@ static int prof_rc_query(struct dvb_usb_device *d) if (msg.buf[0] != 0xff) { deb_rc("%s: rc code: %x, %x\n", __func__, key[0], key[1]); - rc_keydown(d->rc_dev, key[0]^0xff, 1); + rc_keydown(d->rc_dev, RC_TYPE_UNKNOWN, key[0]^0xff, 0); } } @@ -1532,7 +1532,8 @@ static int su3000_rc_query(struct dvb_usb_device *d) if (msg.buf[0] != 0xff) { deb_rc("%s: rc code: %x, %x\n", __func__, key[0], key[1]); - rc_keydown(d->rc_dev, key[1] << 8 | key[0], 1); + rc_keydown(d->rc_dev, RC_TYPE_RC5, + RC_SCANCODE_RC5(key[1], key[0]), 0); } } diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c index 0306cb778df4..abf8ab2e02e5 100644 --- a/drivers/media/usb/dvb-usb/m920x.c +++ b/drivers/media/usb/dvb-usb/m920x.c @@ -245,7 +245,7 @@ static int m920x_rc_core_query(struct dvb_usb_device *d) else if (state == REMOTE_KEY_REPEAT) rc_repeat(d->rc_dev); else - rc_keydown(d->rc_dev, rc_state[1], 0); + rc_keydown(d->rc_dev, RC_TYPE_UNKNOWN, rc_state[1], 0); out: kfree(rc_state); diff --git a/drivers/media/usb/dvb-usb/pctv452e.c b/drivers/media/usb/dvb-usb/pctv452e.c index 449a99605a87..bdfe8963591c 100644 --- a/drivers/media/usb/dvb-usb/pctv452e.c +++ b/drivers/media/usb/dvb-usb/pctv452e.c @@ -565,12 +565,12 @@ static int pctv452e_rc_query(struct dvb_usb_device *d) if ((rx[3] == 9) && (rx[12] & 0x01)) { /* got a "press" event */ - state->last_rc_key = (rx[7] << 8) | rx[6]; + state->last_rc_key = RC_SCANCODE_RC5(rx[7], rx[6]); if (debug > 2) info("%s: cmd=0x%02x sys=0x%02x\n", __func__, rx[6], rx[7]); - rc_keydown(d->rc_dev, state->last_rc_key, 0); + rc_keydown(d->rc_dev, RC_TYPE_RC5, state->last_rc_key, 0); } else if (state->last_rc_key) { rc_keyup(d->rc_dev); state->last_rc_key = 0; @@ -927,7 +927,7 @@ static struct dvb_usb_device_properties pctv452e_properties = { .rc.core = { .rc_codes = RC_MAP_DIB0700_RC5_TABLE, - .allowed_protos = RC_BIT_UNKNOWN, + .allowed_protos = RC_BIT_RC5, .rc_query = pctv452e_rc_query, .rc_interval = 100, }, @@ -980,7 +980,7 @@ static struct dvb_usb_device_properties tt_connect_s2_3600_properties = { .rc.core = { .rc_codes = RC_MAP_TT_1500, - .allowed_protos = RC_BIT_UNKNOWN, + .allowed_protos = RC_BIT_RC5, .rc_query = pctv452e_rc_query, .rc_interval = 100, }, diff --git a/drivers/media/usb/dvb-usb/ttusb2.c b/drivers/media/usb/dvb-usb/ttusb2.c index 2ce3d19c58ef..f10717311e05 100644 --- a/drivers/media/usb/dvb-usb/ttusb2.c +++ b/drivers/media/usb/dvb-usb/ttusb2.c @@ -438,9 +438,9 @@ static int tt3650_rc_query(struct dvb_usb_device *d) if (rx[8] & 0x01) { /* got a "press" event */ - st->last_rc_key = (rx[3] << 8) | rx[2]; + st->last_rc_key = RC_SCANCODE_RC5(rx[3], rx[2]); deb_info("%s: cmd=0x%02x sys=0x%02x\n", __func__, rx[2], rx[3]); - rc_keydown(d->rc_dev, st->last_rc_key, rx[1]); + rc_keydown(d->rc_dev, RC_TYPE_RC5, st->last_rc_key, rx[1]); } else if (st->last_rc_key) { rc_keyup(d->rc_dev); st->last_rc_key = 0; @@ -747,7 +747,7 @@ static struct dvb_usb_device_properties ttusb2_properties_ct3650 = { .rc_interval = 150, /* Less than IR_KEYPRESS_TIMEOUT */ .rc_codes = RC_MAP_TT_1500, .rc_query = tt3650_rc_query, - .allowed_protos = RC_BIT_UNKNOWN, + .allowed_protos = RC_BIT_RC5, }, .num_adapters = 1, diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 56ef49df4f8d..7f06ae59eca6 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "em28xx.h" @@ -53,6 +54,7 @@ struct em28xx_ir_poll_result { unsigned int toggle_bit:1; unsigned int read_count:7; + enum rc_type protocol; u32 scancode; }; @@ -72,7 +74,7 @@ struct em28xx_IR { /* i2c slave address of external device (if used) */ u16 i2c_dev_addr; - int (*get_key_i2c)(struct i2c_client *, u32 *); + int (*get_key_i2c)(struct i2c_client *ir, enum rc_type *protocol, u32 *scancode); int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *); }; @@ -80,7 +82,8 @@ struct em28xx_IR { I2C IR based get keycodes - should be used with ir-kbd-i2c **********************************************************/ -static int em28xx_get_key_terratec(struct i2c_client *i2c_dev, u32 *ir_key) +static int em28xx_get_key_terratec(struct i2c_client *i2c_dev, + enum rc_type *protocol, u32 *scancode) { unsigned char b; @@ -98,14 +101,15 @@ static int em28xx_get_key_terratec(struct i2c_client *i2c_dev, u32 *ir_key) /* keep old data */ return 1; - *ir_key = b; + *protocol = RC_TYPE_UNKNOWN; + *scancode = b; return 1; } -static int em28xx_get_key_em_haup(struct i2c_client *i2c_dev, u32 *ir_key) +static int em28xx_get_key_em_haup(struct i2c_client *i2c_dev, + enum rc_type *protocol, u32 *scancode) { unsigned char buf[2]; - u16 code; int size; /* poll IR chip */ @@ -127,26 +131,13 @@ static int em28xx_get_key_em_haup(struct i2c_client *i2c_dev, u32 *ir_key) * So, the code translation is not complete. Yet, it is enough to * work with the provided RC5 IR. */ - code = - ((buf[0] & 0x01) ? 0x0020 : 0) | /* 0010 0000 */ - ((buf[0] & 0x02) ? 0x0010 : 0) | /* 0001 0000 */ - ((buf[0] & 0x04) ? 0x0008 : 0) | /* 0000 1000 */ - ((buf[0] & 0x08) ? 0x0004 : 0) | /* 0000 0100 */ - ((buf[0] & 0x10) ? 0x0002 : 0) | /* 0000 0010 */ - ((buf[0] & 0x20) ? 0x0001 : 0) | /* 0000 0001 */ - ((buf[1] & 0x08) ? 0x1000 : 0) | /* 0001 0000 */ - ((buf[1] & 0x10) ? 0x0800 : 0) | /* 0000 1000 */ - ((buf[1] & 0x20) ? 0x0400 : 0) | /* 0000 0100 */ - ((buf[1] & 0x40) ? 0x0200 : 0) | /* 0000 0010 */ - ((buf[1] & 0x80) ? 0x0100 : 0); /* 0000 0001 */ - - /* return key */ - *ir_key = code; + *protocol = RC_TYPE_RC5; + *scancode = (bitrev8(buf[1]) & 0x1f) << 8 | bitrev8(buf[0]) >> 2; return 1; } static int em28xx_get_key_pinnacle_usb_grey(struct i2c_client *i2c_dev, - u32 *ir_key) + enum rc_type *protocol, u32 *scancode) { unsigned char buf[3]; @@ -158,13 +149,13 @@ static int em28xx_get_key_pinnacle_usb_grey(struct i2c_client *i2c_dev, if (buf[0] != 0x00) return 0; - *ir_key = buf[2]&0x3f; - + *protocol = RC_TYPE_UNKNOWN; + *scancode = buf[2] & 0x3f; return 1; } static int em28xx_get_key_winfast_usbii_deluxe(struct i2c_client *i2c_dev, - u32 *ir_key) + enum rc_type *protocol, u32 *scancode) { unsigned char subaddr, keydetect, key; @@ -184,7 +175,8 @@ static int em28xx_get_key_winfast_usbii_deluxe(struct i2c_client *i2c_dev, if (key == 0x00) return 0; - *ir_key = key; + *protocol = RC_TYPE_UNKNOWN; + *scancode = key; return 1; } @@ -215,7 +207,22 @@ static int default_polling_getkey(struct em28xx_IR *ir, poll_result->read_count = (msg[0] & 0x7f); /* Remote Control Address/Data (Regs 0x46/0x47) */ - poll_result->scancode = msg[1] << 8 | msg[2]; + switch (ir->rc_type) { + case RC_BIT_RC5: + poll_result->protocol = RC_TYPE_RC5; + poll_result->scancode = RC_SCANCODE_RC5(msg[1], msg[2]); + break; + + case RC_BIT_NEC: + poll_result->protocol = RC_TYPE_NEC; + poll_result->scancode = RC_SCANCODE_NEC(msg[1], msg[2]); + break; + + default: + poll_result->protocol = RC_TYPE_UNKNOWN; + poll_result->scancode = msg[1] << 8 | msg[2]; + break; + } return 0; } @@ -247,25 +254,32 @@ static int em2874_polling_getkey(struct em28xx_IR *ir, */ switch (ir->rc_type) { case RC_BIT_RC5: - poll_result->scancode = msg[1] << 8 | msg[2]; + poll_result->protocol = RC_TYPE_RC5; + poll_result->scancode = RC_SCANCODE_RC5(msg[1], msg[2]); break; + case RC_BIT_NEC: + poll_result->protocol = RC_TYPE_RC5; + poll_result->scancode = msg[1] << 8 | msg[2]; if ((msg[3] ^ msg[4]) != 0xff) /* 32 bits NEC */ - poll_result->scancode = (msg[1] << 24) | - (msg[2] << 16) | - (msg[3] << 8) | - msg[4]; + poll_result->scancode = RC_SCANCODE_NEC32((msg[1] << 24) | + (msg[2] << 16) | + (msg[3] << 8) | + (msg[4])); else if ((msg[1] ^ msg[2]) != 0xff) /* 24 bits NEC */ - poll_result->scancode = (msg[1] << 16) | - (msg[2] << 8) | - msg[3]; + poll_result->scancode = RC_SCANCODE_NECX(msg[1] << 8 | + msg[2], msg[3]); else /* Normal NEC */ - poll_result->scancode = msg[1] << 8 | msg[3]; + poll_result->scancode = RC_SCANCODE_NEC(msg[1], msg[3]); break; + case RC_BIT_RC6_0: - poll_result->scancode = msg[1] << 8 | msg[2]; + poll_result->protocol = RC_TYPE_RC6_0; + poll_result->scancode = RC_SCANCODE_RC6_0(msg[1], msg[2]); break; + default: + poll_result->protocol = RC_TYPE_UNKNOWN; poll_result->scancode = (msg[1] << 24) | (msg[2] << 16) | (msg[3] << 8) | msg[4]; break; @@ -281,22 +295,24 @@ static int em2874_polling_getkey(struct em28xx_IR *ir, static int em28xx_i2c_ir_handle_key(struct em28xx_IR *ir) { struct em28xx *dev = ir->dev; - static u32 ir_key; + static u32 scancode; + enum rc_type protocol; int rc; struct i2c_client client; client.adapter = &ir->dev->i2c_adap[dev->def_i2c_bus]; client.addr = ir->i2c_dev_addr; - rc = ir->get_key_i2c(&client, &ir_key); + rc = ir->get_key_i2c(&client, &protocol, &scancode); if (rc < 0) { dprintk("ir->get_key_i2c() failed: %d\n", rc); return rc; } if (rc) { - dprintk("%s: keycode = 0x%04x\n", __func__, ir_key); - rc_keydown(ir->rc, ir_key, 0); + dprintk("%s: proto = 0x%04x, scancode = 0x%04x\n", + __func__, protocol, scancode); + rc_keydown(ir->rc, protocol, scancode, 0); } return 0; } @@ -319,10 +335,12 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir) poll_result.scancode); if (ir->full_code) rc_keydown(ir->rc, + poll_result.protocol, poll_result.scancode, poll_result.toggle_bit); else rc_keydown(ir->rc, + RC_TYPE_UNKNOWN, poll_result.scancode & 0xff, poll_result.toggle_bit); diff --git a/drivers/media/usb/tm6000/tm6000-input.c b/drivers/media/usb/tm6000/tm6000-input.c index d1af5438c168..676c0232060e 100644 --- a/drivers/media/usb/tm6000/tm6000-input.c +++ b/drivers/media/usb/tm6000/tm6000-input.c @@ -162,11 +162,42 @@ static int tm6000_ir_config(struct tm6000_IR *ir) return 0; } +static void tm6000_ir_keydown(struct tm6000_IR *ir, + const char *buf, unsigned int len) +{ + u8 device, command; + u32 scancode; + enum rc_type protocol; + + if (len < 1) + return; + + command = buf[0]; + device = (len > 1 ? buf[1] : 0x0); + switch (ir->rc_type) { + case RC_BIT_RC5: + protocol = RC_TYPE_RC5; + scancode = RC_SCANCODE_RC5(device, command); + break; + case RC_BIT_NEC: + protocol = RC_TYPE_NEC; + scancode = RC_SCANCODE_NEC(device, command); + break; + default: + protocol = RC_TYPE_OTHER; + scancode = RC_SCANCODE_OTHER(device << 8 | command); + break; + } + + dprintk(1, "%s, protocol: 0x%04x, scancode: 0x%08x\n", + __func__, protocol, scancode); + rc_keydown(ir->rc, protocol, scancode, 0); +} + static void tm6000_ir_urb_received(struct urb *urb) { struct tm6000_core *dev = urb->context; struct tm6000_IR *ir = dev->ir; - struct tm6000_ir_poll_result poll_result; char *buf; dprintk(2, "%s\n",__func__); @@ -184,12 +215,7 @@ static void tm6000_ir_urb_received(struct urb *urb) DUMP_PREFIX_OFFSET,16, 1, buf, urb->actual_length, false); - poll_result.rc_data = buf[0]; - if (urb->actual_length > 1) - poll_result.rc_data |= buf[1] << 8; - - dprintk(1, "%s, scancode: 0x%04x\n",__func__, poll_result.rc_data); - rc_keydown(ir->rc, poll_result.rc_data, 0); + tm6000_ir_keydown(ir, urb->transfer_buffer, urb->actual_length); usb_submit_urb(urb, GFP_ATOMIC); /* @@ -204,7 +230,6 @@ static void tm6000_ir_handle_key(struct work_struct *work) { struct tm6000_IR *ir = container_of(work, struct tm6000_IR, work.work); struct tm6000_core *dev = ir->dev; - struct tm6000_ir_poll_result poll_result; int rc; u8 buf[2]; @@ -219,13 +244,8 @@ static void tm6000_ir_handle_key(struct work_struct *work) if (rc < 0) return; - if (rc > 1) - poll_result.rc_data = buf[0] | buf[1] << 8; - else - poll_result.rc_data = buf[0]; - /* Check if something was read */ - if ((poll_result.rc_data & 0xff) == 0xff) { + if ((buf[0] & 0xff) == 0xff) { if (!ir->pwled) { tm6000_flash_led(dev, 1); ir->pwled = 1; @@ -233,8 +253,7 @@ static void tm6000_ir_handle_key(struct work_struct *work) return; } - dprintk(1, "%s, scancode: 0x%04x\n",__func__, poll_result.rc_data); - rc_keydown(ir->rc, poll_result.rc_data, 0); + tm6000_ir_keydown(ir, buf, rc); tm6000_flash_led(dev, 0); ir->pwled = 0; diff --git a/include/media/rc-core.h b/include/media/rc-core.h index fde142e5f25a..00811c9e22b0 100644 --- a/include/media/rc-core.h +++ b/include/media/rc-core.h @@ -89,6 +89,7 @@ enum rc_filter_type { * @keyup_jiffies: time (in jiffies) when the current keypress should be released * @timer_keyup: timer for releasing a keypress * @last_keycode: keycode of last keypress + * @last_protocol: protocol of last keypress * @last_scancode: scancode of last keypress * @last_toggle: toggle value of last command * @timeout: optional time after which device stops sending data @@ -113,7 +114,7 @@ enum rc_filter_type { * device doesn't interrupt host until it sees IR pulses * @s_learning_mode: enable wide band receiver used for learning * @s_carrier_report: enable carrier reports - * @s_filter: set the scancode filter + * @s_filter: set the scancode filter * @s_wakeup_filter: set the wakeup scancode filter */ struct rc_dev { @@ -141,6 +142,7 @@ struct rc_dev { unsigned long keyup_jiffies; struct timer_list timer_keyup; u32 last_keycode; + enum rc_type last_protocol; u32 last_scancode; u8 last_toggle; u32 timeout; @@ -221,8 +223,8 @@ int rc_open(struct rc_dev *rdev); void rc_close(struct rc_dev *rdev); void rc_repeat(struct rc_dev *dev); -void rc_keydown(struct rc_dev *dev, int scancode, u8 toggle); -void rc_keydown_notimeout(struct rc_dev *dev, int scancode, u8 toggle); +void rc_keydown(struct rc_dev *dev, enum rc_type protocol, u32 scancode, u8 toggle); +void rc_keydown_notimeout(struct rc_dev *dev, enum rc_type protocol, u32 scancode, u8 toggle); void rc_keyup(struct rc_dev *dev); u32 rc_g_keycode_from_table(struct rc_dev *dev, u32 scancode); -- cgit v1.2.3 From 5cd667b0a4567048bb555927d6ee564f4e5620a9 Mon Sep 17 00:00:00 2001 From: Alex Wang Date: Thu, 17 Jul 2014 15:14:13 -0700 Subject: openvswitch: Allow each vport to have an array of 'port_id's. In order to allow handlers directly read upcalls from datapath, we need to support per-handler netlink socket for each vport in datapath. This commit makes this happen. Also, it is guaranteed to be backward compatible with previous branch. Signed-off-by: Alex Wang Acked-by: Thomas Graf Signed-off-by: Pravin B Shelar --- include/uapi/linux/openvswitch.h | 13 +++-- net/openvswitch/datapath.c | 23 ++++++--- net/openvswitch/vport.c | 101 ++++++++++++++++++++++++++++++++++++++- net/openvswitch/vport.h | 27 +++++++++-- 4 files changed, 148 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h index 0b979ee4bfc0..a794d1dd7b40 100644 --- a/include/uapi/linux/openvswitch.h +++ b/include/uapi/linux/openvswitch.h @@ -118,6 +118,9 @@ struct ovs_vport_stats { /* Allow last Netlink attribute to be unaligned */ #define OVS_DP_F_UNALIGNED (1 << 0) +/* Allow datapath to associate multiple Netlink PIDs to each vport */ +#define OVS_DP_F_VPORT_PIDS (1 << 1) + /* Fixed logical ports. */ #define OVSP_LOCAL ((__u32)0) @@ -203,9 +206,10 @@ enum ovs_vport_type { * this is the name of the network device. Maximum length %IFNAMSIZ-1 bytes * plus a null terminator. * @OVS_VPORT_ATTR_OPTIONS: Vport-specific configuration information. - * @OVS_VPORT_ATTR_UPCALL_PID: The Netlink socket in userspace that - * OVS_PACKET_CMD_MISS upcalls will be directed to for packets received on - * this port. A value of zero indicates that upcalls should not be sent. + * @OVS_VPORT_ATTR_UPCALL_PID: The array of Netlink socket pids in userspace + * among which OVS_PACKET_CMD_MISS upcalls will be distributed for packets + * received on this port. If this is a single-element array of value 0, + * upcalls should not be sent. * @OVS_VPORT_ATTR_STATS: A &struct ovs_vport_stats giving statistics for * packets sent or received through the vport. * @@ -228,7 +232,8 @@ enum ovs_vport_attr { OVS_VPORT_ATTR_TYPE, /* u32 OVS_VPORT_TYPE_* constant. */ OVS_VPORT_ATTR_NAME, /* string name, up to IFNAMSIZ bytes long */ OVS_VPORT_ATTR_OPTIONS, /* nested attributes, varies by vport type */ - OVS_VPORT_ATTR_UPCALL_PID, /* u32 Netlink PID to receive upcalls */ + OVS_VPORT_ATTR_UPCALL_PID, /* array of u32 Netlink socket PIDs for */ + /* receiving upcalls */ OVS_VPORT_ATTR_STATS, /* struct ovs_vport_stats */ __OVS_VPORT_ATTR_MAX }; diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 20f59b62721a..65a8e5c089e4 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -266,7 +266,7 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb) upcall.cmd = OVS_PACKET_CMD_MISS; upcall.key = &key; upcall.userdata = NULL; - upcall.portid = p->upcall_portid; + upcall.portid = ovs_vport_find_upcall_portid(p, skb); ovs_dp_upcall(dp, skb, &upcall); consume_skb(skb); stats_counter = &stats->n_missed; @@ -1373,7 +1373,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) parms.options = NULL; parms.dp = dp; parms.port_no = OVSP_LOCAL; - parms.upcall_portid = nla_get_u32(a[OVS_DP_ATTR_UPCALL_PID]); + parms.upcall_portids = a[OVS_DP_ATTR_UPCALL_PID]; ovs_dp_change(dp, a); @@ -1632,8 +1632,8 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb, if (nla_put_u32(skb, OVS_VPORT_ATTR_PORT_NO, vport->port_no) || nla_put_u32(skb, OVS_VPORT_ATTR_TYPE, vport->ops->type) || - nla_put_string(skb, OVS_VPORT_ATTR_NAME, vport->ops->get_name(vport)) || - nla_put_u32(skb, OVS_VPORT_ATTR_UPCALL_PID, vport->upcall_portid)) + nla_put_string(skb, OVS_VPORT_ATTR_NAME, + vport->ops->get_name(vport))) goto nla_put_failure; ovs_vport_get_stats(vport, &vport_stats); @@ -1641,6 +1641,9 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb, &vport_stats)) goto nla_put_failure; + if (ovs_vport_get_upcall_portids(vport, skb)) + goto nla_put_failure; + err = ovs_vport_get_options(vport, skb); if (err == -EMSGSIZE) goto error; @@ -1762,7 +1765,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info) parms.options = a[OVS_VPORT_ATTR_OPTIONS]; parms.dp = dp; parms.port_no = port_no; - parms.upcall_portid = nla_get_u32(a[OVS_VPORT_ATTR_UPCALL_PID]); + parms.upcall_portids = a[OVS_VPORT_ATTR_UPCALL_PID]; vport = new_vport(&parms); err = PTR_ERR(vport); @@ -1812,8 +1815,14 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info) goto exit_unlock_free; } - if (a[OVS_VPORT_ATTR_UPCALL_PID]) - vport->upcall_portid = nla_get_u32(a[OVS_VPORT_ATTR_UPCALL_PID]); + + if (a[OVS_VPORT_ATTR_UPCALL_PID]) { + struct nlattr *ids = a[OVS_VPORT_ATTR_UPCALL_PID]; + + err = ovs_vport_set_upcall_portids(vport, ids); + if (err) + goto exit_unlock_free; + } err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid, info->snd_seq, 0, OVS_VPORT_CMD_NEW); diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c index 42c0f4a0b78c..702fb21bfe15 100644 --- a/net/openvswitch/vport.c +++ b/net/openvswitch/vport.c @@ -134,10 +134,12 @@ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops, vport->dp = parms->dp; vport->port_no = parms->port_no; - vport->upcall_portid = parms->upcall_portid; vport->ops = ops; INIT_HLIST_NODE(&vport->dp_hash_node); + if (ovs_vport_set_upcall_portids(vport, parms->upcall_portids)) + return ERR_PTR(-EINVAL); + vport->percpu_stats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); if (!vport->percpu_stats) { kfree(vport); @@ -161,6 +163,10 @@ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops, */ void ovs_vport_free(struct vport *vport) { + /* vport is freed from RCU callback or error path, Therefore + * it is safe to use raw dereference. + */ + kfree(rcu_dereference_raw(vport->upcall_portids)); free_percpu(vport->percpu_stats); kfree(vport); } @@ -326,6 +332,99 @@ int ovs_vport_get_options(const struct vport *vport, struct sk_buff *skb) return 0; } +/** + * ovs_vport_set_upcall_portids - set upcall portids of @vport. + * + * @vport: vport to modify. + * @ids: new configuration, an array of port ids. + * + * Sets the vport's upcall_portids to @ids. + * + * Returns 0 if successful, -EINVAL if @ids is zero length or cannot be parsed + * as an array of U32. + * + * Must be called with ovs_mutex. + */ +int ovs_vport_set_upcall_portids(struct vport *vport, struct nlattr *ids) +{ + struct vport_portids *old, *vport_portids; + + if (!nla_len(ids) || nla_len(ids) % sizeof(u32)) + return -EINVAL; + + old = ovsl_dereference(vport->upcall_portids); + + vport_portids = kmalloc(sizeof(*vport_portids) + nla_len(ids), + GFP_KERNEL); + if (!vport_portids) + return -ENOMEM; + + vport_portids->n_ids = nla_len(ids) / sizeof(u32); + vport_portids->rn_ids = reciprocal_value(vport_portids->n_ids); + nla_memcpy(vport_portids->ids, ids, nla_len(ids)); + + rcu_assign_pointer(vport->upcall_portids, vport_portids); + + if (old) + kfree_rcu(old, rcu); + return 0; +} + +/** + * ovs_vport_get_upcall_portids - get the upcall_portids of @vport. + * + * @vport: vport from which to retrieve the portids. + * @skb: sk_buff where portids should be appended. + * + * Retrieves the configuration of the given vport, appending the + * %OVS_VPORT_ATTR_UPCALL_PID attribute which is the array of upcall + * portids to @skb. + * + * Returns 0 if successful, -EMSGSIZE if @skb has insufficient room. + * If an error occurs, @skb is left unmodified. Must be called with + * ovs_mutex or rcu_read_lock. + */ +int ovs_vport_get_upcall_portids(const struct vport *vport, + struct sk_buff *skb) +{ + struct vport_portids *ids; + + ids = rcu_dereference_ovsl(vport->upcall_portids); + + if (vport->dp->user_features & OVS_DP_F_VPORT_PIDS) + return nla_put(skb, OVS_VPORT_ATTR_UPCALL_PID, + ids->n_ids * sizeof(u32), (void *)ids->ids); + else + return nla_put_u32(skb, OVS_VPORT_ATTR_UPCALL_PID, ids->ids[0]); +} + +/** + * ovs_vport_find_upcall_portid - find the upcall portid to send upcall. + * + * @vport: vport from which the missed packet is received. + * @skb: skb that the missed packet was received. + * + * Uses the skb_get_hash() to select the upcall portid to send the + * upcall. + * + * Returns the portid of the target socket. Must be called with rcu_read_lock. + */ +u32 ovs_vport_find_upcall_portid(const struct vport *p, struct sk_buff *skb) +{ + struct vport_portids *ids; + u32 ids_index; + u32 hash; + + ids = rcu_dereference(p->upcall_portids); + + if (ids->n_ids == 1 && ids->ids[0] == 0) + return 0; + + hash = skb_get_hash(skb); + ids_index = hash - ids->n_ids * reciprocal_divide(hash, ids->rn_ids); + return ids->ids[ids_index]; +} + /** * ovs_vport_receive - pass up received packet to the datapath for processing * diff --git a/net/openvswitch/vport.h b/net/openvswitch/vport.h index 8d721e62f388..35f89d84b45e 100644 --- a/net/openvswitch/vport.h +++ b/net/openvswitch/vport.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +53,10 @@ void ovs_vport_get_stats(struct vport *, struct ovs_vport_stats *); int ovs_vport_set_options(struct vport *, struct nlattr *options); int ovs_vport_get_options(const struct vport *, struct sk_buff *); +int ovs_vport_set_upcall_portids(struct vport *, struct nlattr *pids); +int ovs_vport_get_upcall_portids(const struct vport *, struct sk_buff *); +u32 ovs_vport_find_upcall_portid(const struct vport *, struct sk_buff *); + int ovs_vport_send(struct vport *, struct sk_buff *); /* The following definitions are for implementers of vport devices: */ @@ -62,13 +67,27 @@ struct vport_err_stats { u64 tx_dropped; u64 tx_errors; }; +/** + * struct vport_portids - array of netlink portids of a vport. + * must be protected by rcu. + * @rn_ids: The reciprocal value of @n_ids. + * @rcu: RCU callback head for deferred destruction. + * @n_ids: Size of @ids array. + * @ids: Array storing the Netlink socket pids to be used for packets received + * on this port that miss the flow table. + */ +struct vport_portids { + struct reciprocal_value rn_ids; + struct rcu_head rcu; + u32 n_ids; + u32 ids[]; +}; /** * struct vport - one port within a datapath * @rcu: RCU callback head for deferred destruction. * @dp: Datapath to which this port belongs. - * @upcall_portid: The Netlink port to use for packets received on this port that - * miss the flow table. + * @upcall_portids: RCU protected 'struct vport_portids'. * @port_no: Index into @dp's @ports array. * @hash_node: Element in @dev_table hash table in vport.c. * @dp_hash_node: Element in @datapath->ports hash table in datapath.c. @@ -80,7 +99,7 @@ struct vport_err_stats { struct vport { struct rcu_head rcu; struct datapath *dp; - u32 upcall_portid; + struct vport_portids __rcu *upcall_portids; u16 port_no; struct hlist_node hash_node; @@ -111,7 +130,7 @@ struct vport_parms { /* For ovs_vport_alloc(). */ struct datapath *dp; u16 port_no; - u32 upcall_portid; + struct nlattr *upcall_portids; }; /** -- cgit v1.2.3 From f6eec614d2252a99b861e288b6301599d2d58da4 Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Thu, 17 Jul 2014 15:14:15 -0700 Subject: openvswitch: Enable tunnel GSO for OVS bridge. Following patch enables all available tunnel GSO features for OVS bridge device so that ovs can use hardware offloads available to underling device. Signed-off-by: Pravin B Shelar Acked-by: Andy Zhou --- include/linux/netdev_features.h | 8 ++++++++ net/openvswitch/vport-internal_dev.c | 5 ++++- 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h index d99800cbdcf3..dcfdecbfa0b7 100644 --- a/include/linux/netdev_features.h +++ b/include/linux/netdev_features.h @@ -176,4 +176,12 @@ enum { NETIF_F_HW_VLAN_STAG_RX | \ NETIF_F_HW_VLAN_STAG_TX) +#define NETIF_F_GSO_ENCAP_ALL (NETIF_F_GSO_GRE | \ + NETIF_F_GSO_GRE_CSUM | \ + NETIF_F_GSO_IPIP | \ + NETIF_F_GSO_SIT | \ + NETIF_F_GSO_UDP_TUNNEL | \ + NETIF_F_GSO_UDP_TUNNEL_CSUM | \ + NETIF_F_GSO_MPLS) + #endif /* _LINUX_NETDEV_FEATURES_H */ diff --git a/net/openvswitch/vport-internal_dev.c b/net/openvswitch/vport-internal_dev.c index bd658555afdf..84516126e5f3 100644 --- a/net/openvswitch/vport-internal_dev.c +++ b/net/openvswitch/vport-internal_dev.c @@ -140,11 +140,14 @@ static void do_setup(struct net_device *netdev) netdev->tx_queue_len = 0; netdev->features = NETIF_F_LLTX | NETIF_F_SG | NETIF_F_FRAGLIST | - NETIF_F_HIGHDMA | NETIF_F_HW_CSUM | NETIF_F_GSO_SOFTWARE; + NETIF_F_HIGHDMA | NETIF_F_HW_CSUM | + NETIF_F_GSO_SOFTWARE | NETIF_F_GSO_ENCAP_ALL; netdev->vlan_features = netdev->features; + netdev->hw_enc_features = netdev->features; netdev->features |= NETIF_F_HW_VLAN_CTAG_TX; netdev->hw_features = netdev->features & ~NETIF_F_LLTX; + eth_hw_addr_random(netdev); } -- cgit v1.2.3 From f7d4ad98fdd08932ffda2354c62e2e2ee059adcc Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 22 Jul 2014 08:01:01 -0700 Subject: gpiolib: Export gpiochip_request_own_desc and gpiochip_free_own_desc Both functions were introduced to let gpio drivers request their own gpio pins. Without exporting the functions, this can however only be used by gpio drivers built into the kernel. Secondary impact is that the functions can not currently be used by platform initialization code associated with the gpio-pca953x driver. This code permits auto-export of gpio pins through platform data, but if this functionality is used, the module can no longer be unloaded due to the problem solved with the introduction of gpiochip_request_own_desc and gpiochip_free_own_desc. Export both function so they can be used from modules and from platform initialization code. Reviewed-by: Alexandre Courbot Reviewed-by: Mika Westerberg Signed-off-by: Guenter Roeck Signed-off-by: Linus Walleij --- Documentation/gpio/driver.txt | 21 +++++++++++++++++++++ drivers/gpio/gpiolib.c | 2 ++ drivers/gpio/gpiolib.h | 3 --- include/linux/gpio/driver.h | 3 +++ 4 files changed, 26 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/Documentation/gpio/driver.txt b/Documentation/gpio/driver.txt index 224dbbcd1804..18790c237977 100644 --- a/Documentation/gpio/driver.txt +++ b/Documentation/gpio/driver.txt @@ -167,3 +167,24 @@ is released: When implementing an irqchip inside a GPIO driver, these two functions should typically be called in the .startup() and .shutdown() callbacks from the irqchip. + + +Requesting self-owned GPIO pins +------------------------------- + +Sometimes it is useful to allow a GPIO chip driver to request its own GPIO +descriptors through the gpiolib API. Using gpio_request() for this purpose +does not help since it pins the module to the kernel forever (it calls +try_module_get()). A GPIO driver can use the following functions instead +to request and free descriptors without being pinned to the kernel forever. + + int gpiochip_request_own_desc(struct gpio_desc *desc, const char *label) + + void gpiochip_free_own_desc(struct gpio_desc *desc) + +Descriptors requested with gpiochip_request_own_desc() must be released with +gpiochip_free_own_desc(). + +These functions must be used with care since they do not affect module use +count. Do not use the functions to request gpio descriptors not owned by the +calling driver. diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 412d64e93cfb..768f0831db18 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -897,6 +897,7 @@ int gpiochip_request_own_desc(struct gpio_desc *desc, const char *label) return __gpiod_request(desc, label); } +EXPORT_SYMBOL_GPL(gpiochip_request_own_desc); /** * gpiochip_free_own_desc - Free GPIO requested by the chip driver @@ -910,6 +911,7 @@ void gpiochip_free_own_desc(struct gpio_desc *desc) if (desc) __gpiod_free(desc); } +EXPORT_SYMBOL_GPL(gpiochip_free_own_desc); /* Drivers MUST set GPIO direction before making get/set calls. In * some cases this is done in early boot, before IRQs are enabled. diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index acbb9335f08c..7fcb645ded4c 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -45,9 +45,6 @@ acpi_get_gpiod_by_index(struct device *dev, int index, } #endif -int gpiochip_request_own_desc(struct gpio_desc *desc, const char *label); -void gpiochip_free_own_desc(struct gpio_desc *desc); - struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np, const char *list_name, int index, enum of_gpio_flags *flags); diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index c66c91682d9e..4c463fb0155e 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -220,6 +220,9 @@ int gpiochip_irqchip_add(struct gpio_chip *gpiochip, #endif /* CONFIG_GPIO_IRQCHIP */ +int gpiochip_request_own_desc(struct gpio_desc *desc, const char *label); +void gpiochip_free_own_desc(struct gpio_desc *desc); + #else /* CONFIG_GPIOLIB */ static inline struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc) -- cgit v1.2.3 From 7d8b6c63751cfbbe5eef81a48c22978b3407a3ad Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 23 Jul 2014 15:36:26 -0400 Subject: CAPABILITIES: remove undefined caps from all processes This is effectively a revert of 7b9a7ec565505699f503b4fcf61500dceb36e744 plus fixing it a different way... We found, when trying to run an application from an application which had dropped privs that the kernel does security checks on undefined capability bits. This was ESPECIALLY difficult to debug as those undefined bits are hidden from /proc/$PID/status. Consider a root application which drops all capabilities from ALL 4 capability sets. We assume, since the application is going to set eff/perm/inh from an array that it will clear not only the defined caps less than CAP_LAST_CAP, but also the higher 28ish bits which are undefined future capabilities. The BSET gets cleared differently. Instead it is cleared one bit at a time. The problem here is that in security/commoncap.c::cap_task_prctl() we actually check the validity of a capability being read. So any task which attempts to 'read all things set in bset' followed by 'unset all things set in bset' will not even attempt to unset the undefined bits higher than CAP_LAST_CAP. So the 'parent' will look something like: CapInh: 0000000000000000 CapPrm: 0000000000000000 CapEff: 0000000000000000 CapBnd: ffffffc000000000 All of this 'should' be fine. Given that these are undefined bits that aren't supposed to have anything to do with permissions. But they do... So lets now consider a task which cleared the eff/perm/inh completely and cleared all of the valid caps in the bset (but not the invalid caps it couldn't read out of the kernel). We know that this is exactly what the libcap-ng library does and what the go capabilities library does. They both leave you in that above situation if you try to clear all of you capapabilities from all 4 sets. If that root task calls execve() the child task will pick up all caps not blocked by the bset. The bset however does not block bits higher than CAP_LAST_CAP. So now the child task has bits in eff which are not in the parent. These are 'meaningless' undefined bits, but still bits which the parent doesn't have. The problem is now in cred_cap_issubset() (or any operation which does a subset test) as the child, while a subset for valid cap bits, is not a subset for invalid cap bits! So now we set durring commit creds that the child is not dumpable. Given it is 'more priv' than its parent. It also means the parent cannot ptrace the child and other stupidity. The solution here: 1) stop hiding capability bits in status This makes debugging easier! 2) stop giving any task undefined capability bits. it's simple, it you don't put those invalid bits in CAP_FULL_SET you won't get them in init and you won't get them in any other task either. This fixes the cap_issubset() tests and resulting fallout (which made the init task in a docker container untraceable among other things) 3) mask out undefined bits when sys_capset() is called as it might use ~0, ~0 to denote 'all capabilities' for backward/forward compatibility. This lets 'capsh --caps="all=eip" -- -c /bin/bash' run. 4) mask out undefined bit when we read a file capability off of disk as again likely all bits are set in the xattr for forward/backward compatibility. This lets 'setcap all+pe /bin/bash; /bin/bash' run Signed-off-by: Eric Paris Reviewed-by: Kees Cook Cc: Andrew Vagin Cc: Andrew G. Morgan Cc: Serge E. Hallyn Cc: Kees Cook Cc: Steve Grubb Cc: Dan Walsh Cc: stable@vger.kernel.org Signed-off-by: James Morris --- fs/proc/array.c | 11 +---------- include/linux/capability.h | 5 ++++- kernel/audit.c | 2 +- kernel/capability.c | 4 ++++ security/commoncap.c | 3 +++ 5 files changed, 13 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/fs/proc/array.c b/fs/proc/array.c index 64db2bceac59..3e1290b0492e 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -297,15 +297,11 @@ static void render_cap_t(struct seq_file *m, const char *header, seq_puts(m, header); CAP_FOR_EACH_U32(__capi) { seq_printf(m, "%08x", - a->cap[(_KERNEL_CAPABILITY_U32S-1) - __capi]); + a->cap[CAP_LAST_U32 - __capi]); } seq_putc(m, '\n'); } -/* Remove non-existent capabilities */ -#define NORM_CAPS(v) (v.cap[CAP_TO_INDEX(CAP_LAST_CAP)] &= \ - CAP_TO_MASK(CAP_LAST_CAP + 1) - 1) - static inline void task_cap(struct seq_file *m, struct task_struct *p) { const struct cred *cred; @@ -319,11 +315,6 @@ static inline void task_cap(struct seq_file *m, struct task_struct *p) cap_bset = cred->cap_bset; rcu_read_unlock(); - NORM_CAPS(cap_inheritable); - NORM_CAPS(cap_permitted); - NORM_CAPS(cap_effective); - NORM_CAPS(cap_bset); - render_cap_t(m, "CapInh:\t", &cap_inheritable); render_cap_t(m, "CapPrm:\t", &cap_permitted); render_cap_t(m, "CapEff:\t", &cap_effective); diff --git a/include/linux/capability.h b/include/linux/capability.h index 84b13ad67c1c..aa93e5ef594c 100644 --- a/include/linux/capability.h +++ b/include/linux/capability.h @@ -78,8 +78,11 @@ extern const kernel_cap_t __cap_init_eff_set; # error Fix up hand-coded capability macro initializers #else /* HAND-CODED capability initializers */ +#define CAP_LAST_U32 ((_KERNEL_CAPABILITY_U32S) - 1) +#define CAP_LAST_U32_VALID_MASK (CAP_TO_MASK(CAP_LAST_CAP + 1) -1) + # define CAP_EMPTY_SET ((kernel_cap_t){{ 0, 0 }}) -# define CAP_FULL_SET ((kernel_cap_t){{ ~0, ~0 }}) +# define CAP_FULL_SET ((kernel_cap_t){{ ~0, CAP_LAST_U32_VALID_MASK }}) # define CAP_FS_SET ((kernel_cap_t){{ CAP_FS_MASK_B0 \ | CAP_TO_MASK(CAP_LINUX_IMMUTABLE), \ CAP_FS_MASK_B1 } }) diff --git a/kernel/audit.c b/kernel/audit.c index 3ef2e0e797e8..ba2ff5a5c600 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -1677,7 +1677,7 @@ void audit_log_cap(struct audit_buffer *ab, char *prefix, kernel_cap_t *cap) audit_log_format(ab, " %s=", prefix); CAP_FOR_EACH_U32(i) { audit_log_format(ab, "%08x", - cap->cap[(_KERNEL_CAPABILITY_U32S-1) - i]); + cap->cap[CAP_LAST_U32 - i]); } } diff --git a/kernel/capability.c b/kernel/capability.c index a5cf13c018ce..989f5bfc57dc 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -258,6 +258,10 @@ SYSCALL_DEFINE2(capset, cap_user_header_t, header, const cap_user_data_t, data) i++; } + effective.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK; + permitted.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK; + inheritable.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK; + new = prepare_creds(); if (!new) return -ENOMEM; diff --git a/security/commoncap.c b/security/commoncap.c index 9fe46e22c7f2..bab0611afc1e 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -421,6 +421,9 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data cpu_caps->inheritable.cap[i] = le32_to_cpu(caps.data[i].inheritable); } + cpu_caps->permitted.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK; + cpu_caps->inheritable.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK; + return 0; } -- cgit v1.2.3 From c7caf86823c71fae652cc50c7d8dd0d2b5c41229 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Thu, 24 Jul 2014 14:51:02 +0900 Subject: gpio: remove gpio_ensure_requested() gpio_ensure_requested() has been introduced in Feb. 2008 by commit d2876d08d86f2 to force users of the GPIO API to explicitly request GPIOs before using them. Hopefully by now all GPIOs are correctly requested and this extra check can be omitted ; in any case the GPIO maintainers won't feel bad if machines start failing after 6 years of warnings. This patch removes that function from the dark ages. Signed-off-by: Alexandre Courbot Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-legacy.c | 106 ------------------------------------------ include/asm-generic/gpio.h | 15 ++++-- 2 files changed, 12 insertions(+), 109 deletions(-) (limited to 'include') diff --git a/drivers/gpio/gpiolib-legacy.c b/drivers/gpio/gpiolib-legacy.c index 0f9429b2522a..078ae6c2df79 100644 --- a/drivers/gpio/gpiolib-legacy.c +++ b/drivers/gpio/gpiolib-legacy.c @@ -5,64 +5,6 @@ #include "gpiolib.h" -/* Warn when drivers omit gpio_request() calls -- legal but ill-advised - * when setting direction, and otherwise illegal. Until board setup code - * and drivers use explicit requests everywhere (which won't happen when - * those calls have no teeth) we can't avoid autorequesting. This nag - * message should motivate switching to explicit requests... so should - * the weaker cleanup after faults, compared to gpio_request(). - * - * NOTE: the autorequest mechanism is going away; at this point it's - * only "legal" in the sense that (old) code using it won't break yet, - * but instead only triggers a WARN() stack dump. - */ -static int gpio_ensure_requested(struct gpio_desc *desc) -{ - struct gpio_chip *chip = desc->chip; - unsigned long flags; - bool request = false; - int err = 0; - - spin_lock_irqsave(&gpio_lock, flags); - - if (WARN(test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0, - "autorequest GPIO-%d\n", desc_to_gpio(desc))) { - if (!try_module_get(chip->owner)) { - gpiod_err(desc, "%s: module can't be gotten\n", - __func__); - clear_bit(FLAG_REQUESTED, &desc->flags); - /* lose */ - err = -EIO; - goto end; - } - desc->label = "[auto]"; - /* caller must chip->request() w/o spinlock */ - if (chip->request) - request = true; - } - -end: - spin_unlock_irqrestore(&gpio_lock, flags); - - if (request) { - might_sleep_if(chip->can_sleep); - err = chip->request(chip, gpio_chip_hwgpio(desc)); - - if (err < 0) { - gpiod_dbg(desc, "%s: chip request fail, %d\n", - __func__, err); - spin_lock_irqsave(&gpio_lock, flags); - - desc->label = NULL; - clear_bit(FLAG_REQUESTED, &desc->flags); - - spin_unlock_irqrestore(&gpio_lock, flags); - } - } - - return err; -} - void gpio_free(unsigned gpio) { gpiod_free(gpio_to_desc(gpio)); @@ -158,51 +100,3 @@ void gpio_free_array(const struct gpio *array, size_t num) gpio_free((array++)->gpio); } EXPORT_SYMBOL_GPL(gpio_free_array); - -int gpio_direction_input(unsigned gpio) -{ - struct gpio_desc *desc = gpio_to_desc(gpio); - int err; - - if (!desc) - return -EINVAL; - - err = gpio_ensure_requested(desc); - if (err < 0) - return err; - - return gpiod_direction_input(desc); -} -EXPORT_SYMBOL_GPL(gpio_direction_input); - -int gpio_direction_output(unsigned gpio, int value) -{ - struct gpio_desc *desc = gpio_to_desc(gpio); - int err; - - if (!desc) - return -EINVAL; - - err = gpio_ensure_requested(desc); - if (err < 0) - return err; - - return gpiod_direction_output_raw(desc, value); -} -EXPORT_SYMBOL_GPL(gpio_direction_output); - -int gpio_set_debounce(unsigned gpio, unsigned debounce) -{ - struct gpio_desc *desc = gpio_to_desc(gpio); - int err; - - if (!desc) - return -EINVAL; - - err = gpio_ensure_requested(desc); - if (err < 0) - return err; - - return gpiod_set_debounce(desc, debounce); -} -EXPORT_SYMBOL_GPL(gpio_set_debounce); diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 39a1d06950d9..c1d4105e1c1d 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -63,10 +63,19 @@ static inline struct gpio_chip *gpio_to_chip(unsigned gpio) extern int gpio_request(unsigned gpio, const char *label); extern void gpio_free(unsigned gpio); -extern int gpio_direction_input(unsigned gpio); -extern int gpio_direction_output(unsigned gpio, int value); +static inline int gpio_direction_input(unsigned gpio) +{ + return gpiod_direction_input(gpio_to_desc(gpio)); +} +static inline int gpio_direction_output(unsigned gpio, int value) +{ + return gpiod_direction_output_raw(gpio_to_desc(gpio), value); +} -extern int gpio_set_debounce(unsigned gpio, unsigned debounce); +static inline int gpio_set_debounce(unsigned gpio, unsigned debounce) +{ + return gpiod_set_debounce(gpio_to_desc(gpio), debounce); +} static inline int gpio_get_value_cansleep(unsigned gpio) { -- cgit v1.2.3 From 2695fb552cbef1029aa025a98acb80cc51d66de5 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Thu, 24 Jul 2014 16:38:21 -0700 Subject: net: filter: rename 'struct sock_filter_int' into 'struct bpf_insn' eBPF is used by socket filtering, seccomp and soon by tracing and exposed to userspace, therefore 'sock_filter_int' name is not accurate. Rename it to 'bpf_insn' Signed-off-by: Alexei Starovoitov Signed-off-by: David S. Miller --- arch/x86/net/bpf_jit_comp.c | 2 +- include/linux/filter.h | 50 ++++++++++++++++++++++----------------------- kernel/bpf/core.c | 2 +- kernel/seccomp.c | 2 +- lib/test_bpf.c | 4 ++-- net/core/filter.c | 18 ++++++++-------- 6 files changed, 39 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index 99bef86ed6df..71737a83f022 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -214,7 +214,7 @@ struct jit_context { static int do_jit(struct sk_filter *bpf_prog, int *addrs, u8 *image, int oldproglen, struct jit_context *ctx) { - struct sock_filter_int *insn = bpf_prog->insnsi; + struct bpf_insn *insn = bpf_prog->insnsi; int insn_cnt = bpf_prog->len; u8 temp[64]; int i; diff --git a/include/linux/filter.h b/include/linux/filter.h index c43c8258e682..20dd50ef7271 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -82,7 +82,7 @@ enum { /* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */ #define BPF_ALU64_REG(OP, DST, SRC) \ - ((struct sock_filter_int) { \ + ((struct bpf_insn) { \ .code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \ .dst_reg = DST, \ .src_reg = SRC, \ @@ -90,7 +90,7 @@ enum { .imm = 0 }) #define BPF_ALU32_REG(OP, DST, SRC) \ - ((struct sock_filter_int) { \ + ((struct bpf_insn) { \ .code = BPF_ALU | BPF_OP(OP) | BPF_X, \ .dst_reg = DST, \ .src_reg = SRC, \ @@ -100,7 +100,7 @@ enum { /* ALU ops on immediates, bpf_add|sub|...: dst_reg += imm32 */ #define BPF_ALU64_IMM(OP, DST, IMM) \ - ((struct sock_filter_int) { \ + ((struct bpf_insn) { \ .code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \ .dst_reg = DST, \ .src_reg = 0, \ @@ -108,7 +108,7 @@ enum { .imm = IMM }) #define BPF_ALU32_IMM(OP, DST, IMM) \ - ((struct sock_filter_int) { \ + ((struct bpf_insn) { \ .code = BPF_ALU | BPF_OP(OP) | BPF_K, \ .dst_reg = DST, \ .src_reg = 0, \ @@ -118,7 +118,7 @@ enum { /* Endianess conversion, cpu_to_{l,b}e(), {l,b}e_to_cpu() */ #define BPF_ENDIAN(TYPE, DST, LEN) \ - ((struct sock_filter_int) { \ + ((struct bpf_insn) { \ .code = BPF_ALU | BPF_END | BPF_SRC(TYPE), \ .dst_reg = DST, \ .src_reg = 0, \ @@ -128,7 +128,7 @@ enum { /* Short form of mov, dst_reg = src_reg */ #define BPF_MOV64_REG(DST, SRC) \ - ((struct sock_filter_int) { \ + ((struct bpf_insn) { \ .code = BPF_ALU64 | BPF_MOV | BPF_X, \ .dst_reg = DST, \ .src_reg = SRC, \ @@ -136,7 +136,7 @@ enum { .imm = 0 }) #define BPF_MOV32_REG(DST, SRC) \ - ((struct sock_filter_int) { \ + ((struct bpf_insn) { \ .code = BPF_ALU | BPF_MOV | BPF_X, \ .dst_reg = DST, \ .src_reg = SRC, \ @@ -146,7 +146,7 @@ enum { /* Short form of mov, dst_reg = imm32 */ #define BPF_MOV64_IMM(DST, IMM) \ - ((struct sock_filter_int) { \ + ((struct bpf_insn) { \ .code = BPF_ALU64 | BPF_MOV | BPF_K, \ .dst_reg = DST, \ .src_reg = 0, \ @@ -154,7 +154,7 @@ enum { .imm = IMM }) #define BPF_MOV32_IMM(DST, IMM) \ - ((struct sock_filter_int) { \ + ((struct bpf_insn) { \ .code = BPF_ALU | BPF_MOV | BPF_K, \ .dst_reg = DST, \ .src_reg = 0, \ @@ -164,7 +164,7 @@ enum { /* Short form of mov based on type, BPF_X: dst_reg = src_reg, BPF_K: dst_reg = imm32 */ #define BPF_MOV64_RAW(TYPE, DST, SRC, IMM) \ - ((struct sock_filter_int) { \ + ((struct bpf_insn) { \ .code = BPF_ALU64 | BPF_MOV | BPF_SRC(TYPE), \ .dst_reg = DST, \ .src_reg = SRC, \ @@ -172,7 +172,7 @@ enum { .imm = IMM }) #define BPF_MOV32_RAW(TYPE, DST, SRC, IMM) \ - ((struct sock_filter_int) { \ + ((struct bpf_insn) { \ .code = BPF_ALU | BPF_MOV | BPF_SRC(TYPE), \ .dst_reg = DST, \ .src_reg = SRC, \ @@ -182,7 +182,7 @@ enum { /* Direct packet access, R0 = *(uint *) (skb->data + imm32) */ #define BPF_LD_ABS(SIZE, IMM) \ - ((struct sock_filter_int) { \ + ((struct bpf_insn) { \ .code = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS, \ .dst_reg = 0, \ .src_reg = 0, \ @@ -192,7 +192,7 @@ enum { /* Indirect packet access, R0 = *(uint *) (skb->data + src_reg + imm32) */ #define BPF_LD_IND(SIZE, SRC, IMM) \ - ((struct sock_filter_int) { \ + ((struct bpf_insn) { \ .code = BPF_LD | BPF_SIZE(SIZE) | BPF_IND, \ .dst_reg = 0, \ .src_reg = SRC, \ @@ -202,7 +202,7 @@ enum { /* Memory load, dst_reg = *(uint *) (src_reg + off16) */ #define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \ - ((struct sock_filter_int) { \ + ((struct bpf_insn) { \ .code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \ .dst_reg = DST, \ .src_reg = SRC, \ @@ -212,7 +212,7 @@ enum { /* Memory store, *(uint *) (dst_reg + off16) = src_reg */ #define BPF_STX_MEM(SIZE, DST, SRC, OFF) \ - ((struct sock_filter_int) { \ + ((struct bpf_insn) { \ .code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \ .dst_reg = DST, \ .src_reg = SRC, \ @@ -222,7 +222,7 @@ enum { /* Memory store, *(uint *) (dst_reg + off16) = imm32 */ #define BPF_ST_MEM(SIZE, DST, OFF, IMM) \ - ((struct sock_filter_int) { \ + ((struct bpf_insn) { \ .code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \ .dst_reg = DST, \ .src_reg = 0, \ @@ -232,7 +232,7 @@ enum { /* Conditional jumps against registers, if (dst_reg 'op' src_reg) goto pc + off16 */ #define BPF_JMP_REG(OP, DST, SRC, OFF) \ - ((struct sock_filter_int) { \ + ((struct bpf_insn) { \ .code = BPF_JMP | BPF_OP(OP) | BPF_X, \ .dst_reg = DST, \ .src_reg = SRC, \ @@ -242,7 +242,7 @@ enum { /* Conditional jumps against immediates, if (dst_reg 'op' imm32) goto pc + off16 */ #define BPF_JMP_IMM(OP, DST, IMM, OFF) \ - ((struct sock_filter_int) { \ + ((struct bpf_insn) { \ .code = BPF_JMP | BPF_OP(OP) | BPF_K, \ .dst_reg = DST, \ .src_reg = 0, \ @@ -252,7 +252,7 @@ enum { /* Function call */ #define BPF_EMIT_CALL(FUNC) \ - ((struct sock_filter_int) { \ + ((struct bpf_insn) { \ .code = BPF_JMP | BPF_CALL, \ .dst_reg = 0, \ .src_reg = 0, \ @@ -262,7 +262,7 @@ enum { /* Raw code statement block */ #define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM) \ - ((struct sock_filter_int) { \ + ((struct bpf_insn) { \ .code = CODE, \ .dst_reg = DST, \ .src_reg = SRC, \ @@ -272,7 +272,7 @@ enum { /* Program exit */ #define BPF_EXIT_INSN() \ - ((struct sock_filter_int) { \ + ((struct bpf_insn) { \ .code = BPF_JMP | BPF_EXIT, \ .dst_reg = 0, \ .src_reg = 0, \ @@ -298,7 +298,7 @@ enum { /* Macro to invoke filter function. */ #define SK_RUN_FILTER(filter, ctx) (*filter->bpf_func)(ctx, filter->insnsi) -struct sock_filter_int { +struct bpf_insn { __u8 code; /* opcode */ __u8 dst_reg:4; /* dest register */ __u8 src_reg:4; /* source register */ @@ -330,10 +330,10 @@ struct sk_filter { struct sock_fprog_kern *orig_prog; /* Original BPF program */ struct rcu_head rcu; unsigned int (*bpf_func)(const struct sk_buff *skb, - const struct sock_filter_int *filter); + const struct bpf_insn *filter); union { struct sock_filter insns[0]; - struct sock_filter_int insnsi[0]; + struct bpf_insn insnsi[0]; struct work_struct work; }; }; @@ -353,7 +353,7 @@ void sk_filter_select_runtime(struct sk_filter *fp); void sk_filter_free(struct sk_filter *fp); int sk_convert_filter(struct sock_filter *prog, int len, - struct sock_filter_int *new_prog, int *new_len); + struct bpf_insn *new_prog, int *new_len); int sk_unattached_filter_create(struct sk_filter **pfp, struct sock_fprog_kern *fprog); diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 77a240a1ce11..265a02cc822d 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -81,7 +81,7 @@ noinline u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5) * keep, 0 for none. @ctx is the data we are operating on, @insn is the * array of filter instructions. */ -static unsigned int __sk_run_filter(void *ctx, const struct sock_filter_int *insn) +static unsigned int __sk_run_filter(void *ctx, const struct bpf_insn *insn) { u64 stack[MAX_BPF_STACK / sizeof(u64)]; u64 regs[MAX_BPF_REG], tmp; diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 301bbc24739c..565743db5384 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -248,7 +248,7 @@ static long seccomp_attach_filter(struct sock_fprog *fprog) if (ret) goto free_prog; - /* Convert 'sock_filter' insns to 'sock_filter_int' insns */ + /* Convert 'sock_filter' insns to 'bpf_insn' insns */ ret = sk_convert_filter(fp, fprog->len, NULL, &new_len); if (ret) goto free_prog; diff --git a/lib/test_bpf.c b/lib/test_bpf.c index c579e0f58818..5f48623ee1a7 100644 --- a/lib/test_bpf.c +++ b/lib/test_bpf.c @@ -66,7 +66,7 @@ struct bpf_test { const char *descr; union { struct sock_filter insns[MAX_INSNS]; - struct sock_filter_int insns_int[MAX_INSNS]; + struct bpf_insn insns_int[MAX_INSNS]; } u; __u8 aux; __u8 data[MAX_DATA]; @@ -1807,7 +1807,7 @@ static struct sk_filter *generate_filter(int which, int *err) fp->len = flen; memcpy(fp->insnsi, tests[which].u.insns_int, - fp->len * sizeof(struct sock_filter_int)); + fp->len * sizeof(struct bpf_insn)); sk_filter_select_runtime(fp); break; diff --git a/net/core/filter.c b/net/core/filter.c index 1d0e9492e4fa..f3b2d5e9fe5f 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -174,9 +174,9 @@ static u64 __get_random_u32(u64 ctx, u64 a, u64 x, u64 r4, u64 r5) } static bool convert_bpf_extensions(struct sock_filter *fp, - struct sock_filter_int **insnp) + struct bpf_insn **insnp) { - struct sock_filter_int *insn = *insnp; + struct bpf_insn *insn = *insnp; switch (fp->k) { case SKF_AD_OFF + SKF_AD_PROTOCOL: @@ -326,7 +326,7 @@ static bool convert_bpf_extensions(struct sock_filter *fp, * * 2) 2nd pass to remap in two passes: 1st pass finds new * jump offsets, 2nd pass remapping: - * new_prog = kmalloc(sizeof(struct sock_filter_int) * new_len); + * new_prog = kmalloc(sizeof(struct bpf_insn) * new_len); * sk_convert_filter(old_prog, old_len, new_prog, &new_len); * * User BPF's register A is mapped to our BPF register 6, user BPF @@ -336,10 +336,10 @@ static bool convert_bpf_extensions(struct sock_filter *fp, * ctx == 'struct seccomp_data *'. */ int sk_convert_filter(struct sock_filter *prog, int len, - struct sock_filter_int *new_prog, int *new_len) + struct bpf_insn *new_prog, int *new_len) { int new_flen = 0, pass = 0, target, i; - struct sock_filter_int *new_insn; + struct bpf_insn *new_insn; struct sock_filter *fp; int *addrs = NULL; u8 bpf_src; @@ -365,8 +365,8 @@ do_pass: new_insn++; for (i = 0; i < len; fp++, i++) { - struct sock_filter_int tmp_insns[6] = { }; - struct sock_filter_int *insn = tmp_insns; + struct bpf_insn tmp_insns[6] = { }; + struct bpf_insn *insn = tmp_insns; if (addrs) addrs[i] = new_insn - new_prog; @@ -913,7 +913,7 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp, * representation. */ BUILD_BUG_ON(sizeof(struct sock_filter) != - sizeof(struct sock_filter_int)); + sizeof(struct bpf_insn)); /* Conversion cannot happen on overlapping memory areas, * so we need to keep the user BPF around until the 2nd @@ -945,7 +945,7 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp, fp->len = new_len; - /* 2nd pass: remap sock_filter insns into sock_filter_int insns. */ + /* 2nd pass: remap sock_filter insns into bpf_insn insns. */ err = sk_convert_filter(old_prog, old_len, fp->insnsi, &new_len); if (err) /* 2nd sk_convert_filter() can fail only if it fails -- cgit v1.2.3 From f892afb07eeecf575179c4747952644a82a92a36 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Mon, 16 Jun 2014 11:31:05 +0800 Subject: dmaengine: imx-sdma: Add a new DMATYPE for Shared Peripheral ASRC Shared Peripheral ASRC, running on SPBA, needs to use shp sciprts for DMA transfer. So this patch just adds a new DMATYPE for it. Signed-off-by: Nicolin Chen Acked-by: Shawn Guo Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/fsl-imx-sdma.txt | 1 + drivers/dma/imx-sdma.c | 5 +++++ include/linux/platform_data/dma-imx.h | 1 + 3 files changed, 7 insertions(+) (limited to 'include') diff --git a/Documentation/devicetree/bindings/dma/fsl-imx-sdma.txt b/Documentation/devicetree/bindings/dma/fsl-imx-sdma.txt index e577196a12c0..4659fd952301 100644 --- a/Documentation/devicetree/bindings/dma/fsl-imx-sdma.txt +++ b/Documentation/devicetree/bindings/dma/fsl-imx-sdma.txt @@ -47,6 +47,7 @@ The full ID of peripheral types can be found below. 20 ASRC 21 ESAI 22 SSI Dual FIFO (needs firmware ver >= 2) + 23 Shared ASRC The third cell specifies the transfer priority as below. diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 8269c200b53b..de584e605db5 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -750,6 +750,11 @@ static void sdma_get_pc(struct sdma_channel *sdmac, emi_2_per = sdma->script_addrs->asrc_2_mcu_addr; per_2_per = sdma->script_addrs->per_2_per_addr; break; + case IMX_DMATYPE_ASRC_SP: + per_2_emi = sdma->script_addrs->shp_2_mcu_addr; + emi_2_per = sdma->script_addrs->mcu_2_shp_addr; + per_2_per = sdma->script_addrs->per_2_per_addr; + break; case IMX_DMATYPE_MSHC: per_2_emi = sdma->script_addrs->mshc_2_mcu_addr; emi_2_per = sdma->script_addrs->mcu_2_mshc_addr; diff --git a/include/linux/platform_data/dma-imx.h b/include/linux/platform_data/dma-imx.h index bcbc6c3c14c0..7aa0e89d1bcc 100644 --- a/include/linux/platform_data/dma-imx.h +++ b/include/linux/platform_data/dma-imx.h @@ -40,6 +40,7 @@ enum sdma_peripheral_type { IMX_DMATYPE_ASRC, /* ASRC */ IMX_DMATYPE_ESAI, /* ESAI */ IMX_DMATYPE_SSI_DUAL, /* SSI Dual FIFO */ + IMX_DMATYPE_ASRC_SP, /* Shared ASRC */ }; enum imx_dma_prio { -- cgit v1.2.3 From 0c9dbebdb6611d2cd75d025ec09035c3e8ce2160 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 11 Jul 2014 18:18:26 +0200 Subject: dmaengine: Remove unused definition of DMA_MAX_COOKIE As of commit commit f04cd40701deace2efb9edd7120e59366bda2118 ("fsldma: fix controller lockups"), its last (and only ever) user is gone. Signed-off-by: Geert Uytterhoeven Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index d2c5cc7c583c..4eb2f82aed1d 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -37,7 +37,6 @@ */ typedef s32 dma_cookie_t; #define DMA_MIN_COOKIE 1 -#define DMA_MAX_COOKIE INT_MAX static inline int dma_submit_error(dma_cookie_t cookie) { -- cgit v1.2.3 From 7ae65c0f9646c29432b69580b80e08632e6cd813 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 22 Jan 2014 14:49:41 +0100 Subject: scsi: convert target_busy to an atomic_t Avoid taking the host-wide host_lock to check the per-target queue limit. Instead we do an atomic_inc_return early on to grab our slot in the queue, and if necessary decrement it after finishing all checks. Signed-off-by: Christoph Hellwig Reviewed-by: Martin K. Petersen Reviewed-by: Hannes Reinecke Reviewed-by: Webb Scales Acked-by: Jens Axboe Tested-by: Bart Van Assche Tested-by: Robert Elliott --- drivers/scsi/scsi_lib.c | 53 ++++++++++++++++++++++++++++------------------ include/scsi/scsi_device.h | 4 ++-- 2 files changed, 34 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 112c737884c3..0580711c2c57 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -294,7 +294,7 @@ void scsi_device_unbusy(struct scsi_device *sdev) spin_lock_irqsave(shost->host_lock, flags); shost->host_busy--; - starget->target_busy--; + atomic_dec(&starget->target_busy); if (unlikely(scsi_host_in_recovery(shost) && (shost->host_failed || shost->host_eh_scheduled))) scsi_eh_wakeup(shost); @@ -361,7 +361,7 @@ static inline int scsi_device_is_busy(struct scsi_device *sdev) static inline int scsi_target_is_busy(struct scsi_target *starget) { return ((starget->can_queue > 0 && - starget->target_busy >= starget->can_queue) || + atomic_read(&starget->target_busy) >= starget->can_queue) || starget->target_blocked); } @@ -1279,37 +1279,50 @@ static inline int scsi_target_queue_ready(struct Scsi_Host *shost, struct scsi_device *sdev) { struct scsi_target *starget = scsi_target(sdev); - int ret = 0; + unsigned int busy; - spin_lock_irq(shost->host_lock); if (starget->single_lun) { + spin_lock_irq(shost->host_lock); if (starget->starget_sdev_user && - starget->starget_sdev_user != sdev) - goto out; + starget->starget_sdev_user != sdev) { + spin_unlock_irq(shost->host_lock); + return 0; + } starget->starget_sdev_user = sdev; + spin_unlock_irq(shost->host_lock); } - if (starget->target_busy == 0 && starget->target_blocked) { + busy = atomic_inc_return(&starget->target_busy) - 1; + if (starget->target_blocked) { + if (busy) + goto starved; + /* * unblock after target_blocked iterates to zero */ - if (--starget->target_blocked != 0) - goto out; + spin_lock_irq(shost->host_lock); + if (--starget->target_blocked != 0) { + spin_unlock_irq(shost->host_lock); + goto out_dec; + } + spin_unlock_irq(shost->host_lock); SCSI_LOG_MLQUEUE(3, starget_printk(KERN_INFO, starget, "unblocking target at zero depth\n")); } - if (scsi_target_is_busy(starget)) { - list_move_tail(&sdev->starved_entry, &shost->starved_list); - goto out; - } + if (starget->can_queue > 0 && busy >= starget->can_queue) + goto starved; - scsi_target(sdev)->target_busy++; - ret = 1; -out: + return 1; + +starved: + spin_lock_irq(shost->host_lock); + list_move_tail(&sdev->starved_entry, &shost->starved_list); spin_unlock_irq(shost->host_lock); - return ret; +out_dec: + atomic_dec(&starget->target_busy); + return 0; } /* @@ -1419,7 +1432,7 @@ static void scsi_kill_request(struct request *req, struct request_queue *q) spin_unlock(sdev->request_queue->queue_lock); spin_lock(shost->host_lock); shost->host_busy++; - starget->target_busy++; + atomic_inc(&starget->target_busy); spin_unlock(shost->host_lock); spin_lock(sdev->request_queue->queue_lock); @@ -1589,9 +1602,7 @@ static void scsi_request_fn(struct request_queue *q) return; host_not_ready: - spin_lock_irq(shost->host_lock); - scsi_target(sdev)->target_busy--; - spin_unlock_irq(shost->host_lock); + atomic_dec(&scsi_target(sdev)->target_busy); not_ready: /* * lock q, handle tag, requeue req, and decrement device_busy. We diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 9aa38f7b303b..4e078b63a9e5 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -291,8 +291,8 @@ struct scsi_target { unsigned int expecting_lun_change:1; /* A device has reported * a 3F/0E UA, other devices on * the same target will also. */ - /* commands actually active on LLD. protected by host lock. */ - unsigned int target_busy; + /* commands actually active on LLD. */ + atomic_t target_busy; /* * LLDs should set this in the slave_alloc host template callout. * If set to zero then there is not limit. -- cgit v1.2.3 From 74665016086615bbaa3fa6f83af410a0a4e029ee Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 22 Jan 2014 15:29:29 +0100 Subject: scsi: convert host_busy to atomic_t Avoid taking the host-wide host_lock to check the per-host queue limit. Instead we do an atomic_inc_return early on to grab our slot in the queue, and if necessary decrement it after finishing all checks. Signed-off-by: Christoph Hellwig Reviewed-by: Martin K. Petersen Reviewed-by: Hannes Reinecke Reviewed-by: Webb Scales Acked-by: Jens Axboe Tested-by: Bart Van Assche Tested-by: Robert Elliott --- drivers/scsi/advansys.c | 4 +- drivers/scsi/libiscsi.c | 4 +- drivers/scsi/libsas/sas_scsi_host.c | 5 ++- drivers/scsi/qlogicpti.c | 2 +- drivers/scsi/scsi.c | 2 +- drivers/scsi/scsi_error.c | 7 ++-- drivers/scsi/scsi_lib.c | 74 ++++++++++++++++++++++--------------- drivers/scsi/scsi_sysfs.c | 9 ++++- include/scsi/scsi_host.h | 10 ++--- 9 files changed, 69 insertions(+), 48 deletions(-) (limited to 'include') diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c index e716d0aef194..43761c1c46f0 100644 --- a/drivers/scsi/advansys.c +++ b/drivers/scsi/advansys.c @@ -2512,7 +2512,7 @@ static void asc_prt_scsi_host(struct Scsi_Host *s) printk("Scsi_Host at addr 0x%p, device %s\n", s, dev_name(boardp->dev)); printk(" host_busy %u, host_no %d,\n", - s->host_busy, s->host_no); + atomic_read(&s->host_busy), s->host_no); printk(" base 0x%lx, io_port 0x%lx, irq %d,\n", (ulong)s->base, (ulong)s->io_port, boardp->irq); @@ -3346,7 +3346,7 @@ static void asc_prt_driver_conf(struct seq_file *m, struct Scsi_Host *shost) seq_printf(m, " host_busy %u, max_id %u, max_lun %llu, max_channel %u\n", - shost->host_busy, shost->max_id, + atomic_read(&shost->host_busy), shost->max_id, shost->max_lun, shost->max_channel); seq_printf(m, diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index f2db82beb646..f9f3a1224dfa 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -2971,7 +2971,7 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn) */ for (;;) { spin_lock_irqsave(session->host->host_lock, flags); - if (!session->host->host_busy) { /* OK for ERL == 0 */ + if (!atomic_read(&session->host->host_busy)) { /* OK for ERL == 0 */ spin_unlock_irqrestore(session->host->host_lock, flags); break; } @@ -2979,7 +2979,7 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn) msleep_interruptible(500); iscsi_conn_printk(KERN_INFO, conn, "iscsi conn_destroy(): " "host_busy %d host_failed %d\n", - session->host->host_busy, + atomic_read(&session->host->host_busy), session->host->host_failed); /* * force eh_abort() to unblock diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 7d02a19419a7..24e477d2ea70 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -813,7 +813,7 @@ retry: spin_unlock_irq(shost->host_lock); SAS_DPRINTK("Enter %s busy: %d failed: %d\n", - __func__, shost->host_busy, shost->host_failed); + __func__, atomic_read(&shost->host_busy), shost->host_failed); /* * Deal with commands that still have SAS tasks (i.e. they didn't * complete via the normal sas_task completion mechanism), @@ -858,7 +858,8 @@ out: goto retry; SAS_DPRINTK("--- Exit %s: busy: %d failed: %d tries: %d\n", - __func__, shost->host_busy, shost->host_failed, tries); + __func__, atomic_read(&shost->host_busy), + shost->host_failed, tries); } enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd) diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c index 6d48d30bed05..740ae495aa77 100644 --- a/drivers/scsi/qlogicpti.c +++ b/drivers/scsi/qlogicpti.c @@ -959,7 +959,7 @@ static inline void update_can_queue(struct Scsi_Host *host, u_int in_ptr, u_int /* Temporary workaround until bug is found and fixed (one bug has been found already, but fixing it makes things even worse) -jj */ int num_free = QLOGICPTI_REQ_QUEUE_LEN - REQ_QUEUE_DEPTH(in_ptr, out_ptr) - 64; - host->can_queue = host->host_busy + num_free; + host->can_queue = atomic_read(&host->host_busy) + num_free; host->sg_tablesize = QLOGICPTI_MAX_SG(num_free); } diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 6200a2615436..21fb97b01dd6 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -600,7 +600,7 @@ void scsi_log_completion(struct scsi_cmnd *cmd, int disposition) if (level > 3) scmd_printk(KERN_INFO, cmd, "scsi host busy %d failed %d\n", - cmd->device->host->host_busy, + atomic_read(&cmd->device->host->host_busy), cmd->device->host->host_failed); } } diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index e4a532463f9a..5db8454474ee 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -59,7 +59,7 @@ static int scsi_try_to_abort_cmd(struct scsi_host_template *, /* called with shost->host_lock held */ void scsi_eh_wakeup(struct Scsi_Host *shost) { - if (shost->host_busy == shost->host_failed) { + if (atomic_read(&shost->host_busy) == shost->host_failed) { trace_scsi_eh_wakeup(shost); wake_up_process(shost->ehandler); SCSI_LOG_ERROR_RECOVERY(5, shost_printk(KERN_INFO, shost, @@ -2164,7 +2164,7 @@ int scsi_error_handler(void *data) while (!kthread_should_stop()) { set_current_state(TASK_INTERRUPTIBLE); if ((shost->host_failed == 0 && shost->host_eh_scheduled == 0) || - shost->host_failed != shost->host_busy) { + shost->host_failed != atomic_read(&shost->host_busy)) { SCSI_LOG_ERROR_RECOVERY(1, shost_printk(KERN_INFO, shost, "scsi_eh_%d: sleeping\n", @@ -2178,7 +2178,8 @@ int scsi_error_handler(void *data) shost_printk(KERN_INFO, shost, "scsi_eh_%d: waking up %d/%d/%d\n", shost->host_no, shost->host_eh_scheduled, - shost->host_failed, shost->host_busy)); + shost->host_failed, + atomic_read(&shost->host_busy))); /* * We have a host that is failing for some reason. Figure out diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 0580711c2c57..d0bd7e0ab7a8 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -292,14 +292,17 @@ void scsi_device_unbusy(struct scsi_device *sdev) struct scsi_target *starget = scsi_target(sdev); unsigned long flags; - spin_lock_irqsave(shost->host_lock, flags); - shost->host_busy--; + atomic_dec(&shost->host_busy); atomic_dec(&starget->target_busy); + if (unlikely(scsi_host_in_recovery(shost) && - (shost->host_failed || shost->host_eh_scheduled))) + (shost->host_failed || shost->host_eh_scheduled))) { + spin_lock_irqsave(shost->host_lock, flags); scsi_eh_wakeup(shost); - spin_unlock(shost->host_lock); - spin_lock(sdev->request_queue->queue_lock); + spin_unlock_irqrestore(shost->host_lock, flags); + } + + spin_lock_irqsave(sdev->request_queue->queue_lock, flags); sdev->device_busy--; spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); } @@ -367,7 +370,8 @@ static inline int scsi_target_is_busy(struct scsi_target *starget) static inline int scsi_host_is_busy(struct Scsi_Host *shost) { - if ((shost->can_queue > 0 && shost->host_busy >= shost->can_queue) || + if ((shost->can_queue > 0 && + atomic_read(&shost->host_busy) >= shost->can_queue) || shost->host_blocked || shost->host_self_blocked) return 1; @@ -1334,38 +1338,54 @@ static inline int scsi_host_queue_ready(struct request_queue *q, struct Scsi_Host *shost, struct scsi_device *sdev) { - int ret = 0; - - spin_lock_irq(shost->host_lock); + unsigned int busy; if (scsi_host_in_recovery(shost)) - goto out; - if (shost->host_busy == 0 && shost->host_blocked) { + return 0; + + busy = atomic_inc_return(&shost->host_busy) - 1; + if (shost->host_blocked) { + if (busy) + goto starved; + /* * unblock after host_blocked iterates to zero */ - if (--shost->host_blocked != 0) - goto out; + spin_lock_irq(shost->host_lock); + if (--shost->host_blocked != 0) { + spin_unlock_irq(shost->host_lock); + goto out_dec; + } + spin_unlock_irq(shost->host_lock); SCSI_LOG_MLQUEUE(3, shost_printk(KERN_INFO, shost, "unblocking host at zero depth\n")); } - if (scsi_host_is_busy(shost)) { - if (list_empty(&sdev->starved_entry)) - list_add_tail(&sdev->starved_entry, &shost->starved_list); - goto out; - } + + if (shost->can_queue > 0 && busy >= shost->can_queue) + goto starved; + if (shost->host_self_blocked) + goto starved; /* We're OK to process the command, so we can't be starved */ - if (!list_empty(&sdev->starved_entry)) - list_del_init(&sdev->starved_entry); + if (!list_empty(&sdev->starved_entry)) { + spin_lock_irq(shost->host_lock); + if (!list_empty(&sdev->starved_entry)) + list_del_init(&sdev->starved_entry); + spin_unlock_irq(shost->host_lock); + } - shost->host_busy++; - ret = 1; -out: + return 1; + +starved: + spin_lock_irq(shost->host_lock); + if (list_empty(&sdev->starved_entry)) + list_add_tail(&sdev->starved_entry, &shost->starved_list); spin_unlock_irq(shost->host_lock); - return ret; +out_dec: + atomic_dec(&shost->host_busy); + return 0; } /* @@ -1429,12 +1449,8 @@ static void scsi_kill_request(struct request *req, struct request_queue *q) * with the locks as normal issue path does. */ sdev->device_busy++; - spin_unlock(sdev->request_queue->queue_lock); - spin_lock(shost->host_lock); - shost->host_busy++; + atomic_inc(&shost->host_busy); atomic_inc(&starget->target_busy); - spin_unlock(shost->host_lock); - spin_lock(sdev->request_queue->queue_lock); blk_complete_request(req); } diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 5f36788705ba..de57b8bca7be 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -334,7 +334,6 @@ store_shost_eh_deadline(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(eh_deadline, S_IRUGO | S_IWUSR, show_shost_eh_deadline, store_shost_eh_deadline); shost_rd_attr(unique_id, "%u\n"); -shost_rd_attr(host_busy, "%hu\n"); shost_rd_attr(cmd_per_lun, "%hd\n"); shost_rd_attr(can_queue, "%hd\n"); shost_rd_attr(sg_tablesize, "%hu\n"); @@ -344,6 +343,14 @@ shost_rd_attr(prot_capabilities, "%u\n"); shost_rd_attr(prot_guard_type, "%hd\n"); shost_rd_attr2(proc_name, hostt->proc_name, "%s\n"); +static ssize_t +show_host_busy(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + return snprintf(buf, 20, "%d\n", atomic_read(&shost->host_busy)); +} +static DEVICE_ATTR(host_busy, S_IRUGO, show_host_busy, NULL); + static struct attribute *scsi_sysfs_shost_attrs[] = { &dev_attr_unique_id.attr, &dev_attr_host_busy.attr, diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index b2bc5198b7fb..51f7911b1cbb 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -582,13 +582,9 @@ struct Scsi_Host { */ struct blk_queue_tag *bqt; - /* - * The following two fields are protected with host_lock; - * however, eh routines can safely access during eh processing - * without acquiring the lock. - */ - unsigned int host_busy; /* commands actually active on low-level */ - unsigned int host_failed; /* commands that failed. */ + atomic_t host_busy; /* commands actually active on low-level */ + unsigned int host_failed; /* commands that failed. + protected by host_lock */ unsigned int host_eh_scheduled; /* EH scheduled without command */ unsigned int host_no; /* Used for IOCTL_GET_IDLUN, /proc/scsi et al. */ -- cgit v1.2.3 From 71e75c97f97a9645d25fbf3d8e4165a558f18747 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 11 Apr 2014 19:07:01 +0200 Subject: scsi: convert device_busy to atomic_t Avoid taking the queue_lock to check the per-device queue limit. Instead we do an atomic_inc_return early on to grab our slot in the queue, and if necessary decrement it after finishing all checks. Unlike the host and target busy counters this doesn't allow us to avoid the queue_lock in the request_fn due to the way the interface works, but it'll allow us to prepare for using the blk-mq code, which doesn't use the queue_lock at all, and it at least avoids a queue_lock round trip in scsi_device_unbusy, which is still important given how busy the queue_lock is. Signed-off-by: Christoph Hellwig Reviewed-by: Hannes Reinecke Reviewed-by: Webb Scales Acked-by: Jens Axboe Tested-by: Bart Van Assche Tested-by: Robert Elliott --- drivers/message/fusion/mptsas.c | 2 +- drivers/scsi/scsi_lib.c | 50 +++++++++++++++++++++++------------------ drivers/scsi/scsi_sysfs.c | 10 ++++++++- drivers/scsi/sg.c | 2 +- include/scsi/scsi_device.h | 4 +--- 5 files changed, 40 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c index 711fcb5cec87..d636dbe172a3 100644 --- a/drivers/message/fusion/mptsas.c +++ b/drivers/message/fusion/mptsas.c @@ -3763,7 +3763,7 @@ mptsas_send_link_status_event(struct fw_event_work *fw_event) printk(MYIOC_s_DEBUG_FMT "SDEV OUTSTANDING CMDS" "%d\n", ioc->name, - sdev->device_busy)); + atomic_read(&sdev->device_busy))); } } diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index d0bd7e0ab7a8..1ddf0fb43b59 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -302,9 +302,7 @@ void scsi_device_unbusy(struct scsi_device *sdev) spin_unlock_irqrestore(shost->host_lock, flags); } - spin_lock_irqsave(sdev->request_queue->queue_lock, flags); - sdev->device_busy--; - spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); + atomic_dec(&sdev->device_busy); } /* @@ -355,9 +353,9 @@ static void scsi_single_lun_run(struct scsi_device *current_sdev) static inline int scsi_device_is_busy(struct scsi_device *sdev) { - if (sdev->device_busy >= sdev->queue_depth || sdev->device_blocked) + if (atomic_read(&sdev->device_busy) >= sdev->queue_depth || + sdev->device_blocked) return 1; - return 0; } @@ -1204,7 +1202,7 @@ scsi_prep_return(struct request_queue *q, struct request *req, int ret) * queue must be restarted, so we schedule a callback to happen * shortly. */ - if (sdev->device_busy == 0) + if (atomic_read(&sdev->device_busy) == 0) blk_delay_queue(q, SCSI_QUEUE_DELAY); break; default: @@ -1255,26 +1253,33 @@ static void scsi_unprep_fn(struct request_queue *q, struct request *req) static inline int scsi_dev_queue_ready(struct request_queue *q, struct scsi_device *sdev) { - if (sdev->device_busy == 0 && sdev->device_blocked) { + unsigned int busy; + + busy = atomic_inc_return(&sdev->device_busy) - 1; + if (sdev->device_blocked) { + if (busy) + goto out_dec; + /* * unblock after device_blocked iterates to zero */ - if (--sdev->device_blocked == 0) { - SCSI_LOG_MLQUEUE(3, - sdev_printk(KERN_INFO, sdev, - "unblocking device at zero depth\n")); - } else { + if (--sdev->device_blocked != 0) { blk_delay_queue(q, SCSI_QUEUE_DELAY); - return 0; + goto out_dec; } + SCSI_LOG_MLQUEUE(3, sdev_printk(KERN_INFO, sdev, + "unblocking device at zero depth\n")); } - if (scsi_device_is_busy(sdev)) - return 0; + + if (busy >= sdev->queue_depth) + goto out_dec; return 1; +out_dec: + atomic_dec(&sdev->device_busy); + return 0; } - /* * scsi_target_queue_ready: checks if there we can send commands to target * @sdev: scsi device on starget to check. @@ -1448,7 +1453,7 @@ static void scsi_kill_request(struct request *req, struct request_queue *q) * bump busy counts. To bump the counters, we need to dance * with the locks as normal issue path does. */ - sdev->device_busy++; + atomic_inc(&sdev->device_busy); atomic_inc(&shost->host_busy); atomic_inc(&starget->target_busy); @@ -1544,7 +1549,7 @@ static void scsi_request_fn(struct request_queue *q) * accept it. */ req = blk_peek_request(q); - if (!req || !scsi_dev_queue_ready(q, sdev)) + if (!req) break; if (unlikely(!scsi_device_online(sdev))) { @@ -1554,13 +1559,14 @@ static void scsi_request_fn(struct request_queue *q) continue; } + if (!scsi_dev_queue_ready(q, sdev)) + break; /* * Remove the request from the request list. */ if (!(blk_queue_tagged(q) && !blk_queue_start_tag(q, req))) blk_start_request(req); - sdev->device_busy++; spin_unlock_irq(q->queue_lock); cmd = req->special; @@ -1630,9 +1636,9 @@ static void scsi_request_fn(struct request_queue *q) */ spin_lock_irq(q->queue_lock); blk_requeue_request(q, req); - sdev->device_busy--; + atomic_dec(&sdev->device_busy); out_delay: - if (sdev->device_busy == 0 && !scsi_device_blocked(sdev)) + if (atomic_read(&sdev->device_busy) && !scsi_device_blocked(sdev)) blk_delay_queue(q, SCSI_QUEUE_DELAY); } @@ -2371,7 +2377,7 @@ scsi_device_quiesce(struct scsi_device *sdev) return err; scsi_run_queue(sdev->request_queue); - while (sdev->device_busy) { + while (atomic_read(&sdev->device_busy)) { msleep_interruptible(200); scsi_run_queue(sdev->request_queue); } diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index de57b8bca7be..79df9847edef 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -585,13 +585,21 @@ static int scsi_sdev_check_buf_bit(const char *buf) * Create the actual show/store functions and data structures. */ sdev_rd_attr (device_blocked, "%d\n"); -sdev_rd_attr (device_busy, "%d\n"); sdev_rd_attr (type, "%d\n"); sdev_rd_attr (scsi_level, "%d\n"); sdev_rd_attr (vendor, "%.8s\n"); sdev_rd_attr (model, "%.16s\n"); sdev_rd_attr (rev, "%.4s\n"); +static ssize_t +sdev_show_device_busy(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct scsi_device *sdev = to_scsi_device(dev); + return snprintf(buf, 20, "%d\n", atomic_read(&sdev->device_busy)); +} +static DEVICE_ATTR(device_busy, S_IRUGO, sdev_show_device_busy, NULL); + /* * TODO: can we make these symlinks to the block layer ones? */ diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 7a291f5c7227..01cf88888797 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -2574,7 +2574,7 @@ static int sg_proc_seq_show_dev(struct seq_file *s, void *v) scsidp->id, scsidp->lun, (int) scsidp->type, 1, (int) scsidp->queue_depth, - (int) scsidp->device_busy, + (int) atomic_read(&scsidp->device_busy), (int) scsi_device_online(scsidp)); } read_unlock_irqrestore(&sg_index_lock, iflags); diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 4e078b63a9e5..3329901c7243 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -81,9 +81,7 @@ struct scsi_device { struct list_head siblings; /* list of all devices on this host */ struct list_head same_target_siblings; /* just the devices sharing same target id */ - /* this is now protected by the request_queue->queue_lock */ - unsigned int device_busy; /* commands actually active on - * low-level. protected by queue_lock. */ + atomic_t device_busy; /* commands actually active on LLDD */ spinlock_t list_lock; struct list_head cmd_list; /* queue of in use SCSI Command structures */ struct list_head starved_entry; -- cgit v1.2.3 From 72c5839515260dce966cd24f54436e6583288e6c Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Thu, 24 Jul 2014 14:14:42 +0100 Subject: arm64: gicv3: Allow GICv3 compilation with older binutils GICv3 introduces new system registers accessible with the full msr/mrs syntax (e.g. mrs x0, Sop0_op1_CRm_CRn_op2). However, only recent binutils understand the new syntax. This patch introduces msr_s/mrs_s assembly macros which generate the equivalent instructions above and converts the existing GICv3 code (both drivers/irqchip/ and arch/arm64/kernel/). Signed-off-by: Catalin Marinas Reported-by: Olof Johansson Tested-by: Olof Johansson Suggested-by: Mark Rutland Acked-by: Mark Rutland Acked-by: Jason Cooper Cc: Will Deacon Cc: Marc Zyngier --- arch/arm64/include/asm/sysreg.h | 60 ++++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/head.S | 6 ++-- drivers/irqchip/irq-gic-v3.c | 16 +++++----- include/linux/irqchip/arm-gic-v3.h | 42 +++++++++++++------------- 4 files changed, 93 insertions(+), 31 deletions(-) create mode 100644 arch/arm64/include/asm/sysreg.h (limited to 'include') diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h new file mode 100644 index 000000000000..5c89df0acbcb --- /dev/null +++ b/arch/arm64/include/asm/sysreg.h @@ -0,0 +1,60 @@ +/* + * Macros for accessing system registers with older binutils. + * + * Copyright (C) 2014 ARM Ltd. + * Author: Catalin Marinas + * + * 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 __ASM_SYSREG_H +#define __ASM_SYSREG_H + +#define sys_reg(op0, op1, crn, crm, op2) \ + ((((op0)-2)<<19)|((op1)<<16)|((crn)<<12)|((crm)<<8)|((op2)<<5)) + +#ifdef __ASSEMBLY__ + + .irp num,0,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 + .equ __reg_num_x\num, \num + .endr + .equ __reg_num_xzr, 31 + + .macro mrs_s, rt, sreg + .inst 0xd5300000|(\sreg)|(__reg_num_\rt) + .endm + + .macro msr_s, sreg, rt + .inst 0xd5100000|(\sreg)|(__reg_num_\rt) + .endm + +#else + +asm( +" .irp num,0,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\n" +" .equ __reg_num_x\\num, \\num\n" +" .endr\n" +" .equ __reg_num_xzr, 31\n" +"\n" +" .macro mrs_s, rt, sreg\n" +" .inst 0xd5300000|(\\sreg)|(__reg_num_\\rt)\n" +" .endm\n" +"\n" +" .macro msr_s, sreg, rt\n" +" .inst 0xd5100000|(\\sreg)|(__reg_num_\\rt)\n" +" .endm\n" +); + +#endif + +#endif /* __ASM_SYSREG_H */ diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S index c99e3a879ebc..144f10567f82 100644 --- a/arch/arm64/kernel/head.S +++ b/arch/arm64/kernel/head.S @@ -297,12 +297,12 @@ CPU_LE( bic x0, x0, #(3 << 24) ) // Clear the EE and E0E bits for EL1 cmp x0, #1 b.ne 3f - mrs x0, ICC_SRE_EL2 + mrs_s x0, ICC_SRE_EL2 orr x0, x0, #ICC_SRE_EL2_SRE // Set ICC_SRE_EL2.SRE==1 orr x0, x0, #ICC_SRE_EL2_ENABLE // Set ICC_SRE_EL2.Enable==1 - msr ICC_SRE_EL2, x0 + msr_s ICC_SRE_EL2, x0 isb // Make sure SRE is now set - msr ICH_HCR_EL2, xzr // Reset ICC_HCR_EL2 to defaults + msr_s ICH_HCR_EL2, xzr // Reset ICC_HCR_EL2 to defaults 3: #endif diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 81519bae0453..57eaa5a0b1e3 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -108,39 +108,39 @@ static u64 gic_read_iar(void) { u64 irqstat; - asm volatile("mrs %0, " __stringify(ICC_IAR1_EL1) : "=r" (irqstat)); + asm volatile("mrs_s %0, " __stringify(ICC_IAR1_EL1) : "=r" (irqstat)); return irqstat; } static void gic_write_pmr(u64 val) { - asm volatile("msr " __stringify(ICC_PMR_EL1) ", %0" : : "r" (val)); + asm volatile("msr_s " __stringify(ICC_PMR_EL1) ", %0" : : "r" (val)); } static void gic_write_ctlr(u64 val) { - asm volatile("msr " __stringify(ICC_CTLR_EL1) ", %0" : : "r" (val)); + asm volatile("msr_s " __stringify(ICC_CTLR_EL1) ", %0" : : "r" (val)); isb(); } static void gic_write_grpen1(u64 val) { - asm volatile("msr " __stringify(ICC_GRPEN1_EL1) ", %0" : : "r" (val)); + asm volatile("msr_s " __stringify(ICC_GRPEN1_EL1) ", %0" : : "r" (val)); isb(); } static void gic_write_sgi1r(u64 val) { - asm volatile("msr " __stringify(ICC_SGI1R_EL1) ", %0" : : "r" (val)); + asm volatile("msr_s " __stringify(ICC_SGI1R_EL1) ", %0" : : "r" (val)); } static void gic_enable_sre(void) { u64 val; - asm volatile("mrs %0, " __stringify(ICC_SRE_EL1) : "=r" (val)); + asm volatile("mrs_s %0, " __stringify(ICC_SRE_EL1) : "=r" (val)); val |= ICC_SRE_EL1_SRE; - asm volatile("msr " __stringify(ICC_SRE_EL1) ", %0" : : "r" (val)); + asm volatile("msr_s " __stringify(ICC_SRE_EL1) ", %0" : : "r" (val)); isb(); /* @@ -150,7 +150,7 @@ static void gic_enable_sre(void) * * Kindly inform the luser. */ - asm volatile("mrs %0, " __stringify(ICC_SRE_EL1) : "=r" (val)); + asm volatile("mrs_s %0, " __stringify(ICC_SRE_EL1) : "=r" (val)); if (!(val & ICC_SRE_EL1_SRE)) pr_err("GIC: unable to set SRE (disabled at EL2), panic ahead\n"); } diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h index 30cb7556d43f..03a4ea37ba86 100644 --- a/include/linux/irqchip/arm-gic-v3.h +++ b/include/linux/irqchip/arm-gic-v3.h @@ -18,6 +18,8 @@ #ifndef __LINUX_IRQCHIP_ARM_GIC_V3_H #define __LINUX_IRQCHIP_ARM_GIC_V3_H +#include + /* * Distributor registers. We assume we're running non-secure, with ARE * being set. Secure-only and non-ARE registers are not described. @@ -125,17 +127,17 @@ #define ICH_VMCR_PMR_SHIFT 24 #define ICH_VMCR_PMR_MASK (0xffUL << ICH_VMCR_PMR_SHIFT) -#define ICC_EOIR1_EL1 S3_0_C12_C12_1 -#define ICC_IAR1_EL1 S3_0_C12_C12_0 -#define ICC_SGI1R_EL1 S3_0_C12_C11_5 -#define ICC_PMR_EL1 S3_0_C4_C6_0 -#define ICC_CTLR_EL1 S3_0_C12_C12_4 -#define ICC_SRE_EL1 S3_0_C12_C12_5 -#define ICC_GRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_EOIR1_EL1 sys_reg(3, 0, 12, 12, 1) +#define ICC_IAR1_EL1 sys_reg(3, 0, 12, 12, 0) +#define ICC_SGI1R_EL1 sys_reg(3, 0, 12, 11, 5) +#define ICC_PMR_EL1 sys_reg(3, 0, 4, 6, 0) +#define ICC_CTLR_EL1 sys_reg(3, 0, 12, 12, 4) +#define ICC_SRE_EL1 sys_reg(3, 0, 12, 12, 5) +#define ICC_GRPEN1_EL1 sys_reg(3, 0, 12, 12, 7) #define ICC_IAR1_EL1_SPURIOUS 0x3ff -#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL2 sys_reg(3, 4, 12, 9, 5) #define ICC_SRE_EL2_SRE (1 << 0) #define ICC_SRE_EL2_ENABLE (1 << 3) @@ -143,16 +145,16 @@ /* * System register definitions */ -#define ICH_VSEIR_EL2 S3_4_C12_C9_4 -#define ICH_HCR_EL2 S3_4_C12_C11_0 -#define ICH_VTR_EL2 S3_4_C12_C11_1 -#define ICH_MISR_EL2 S3_4_C12_C11_2 -#define ICH_EISR_EL2 S3_4_C12_C11_3 -#define ICH_ELSR_EL2 S3_4_C12_C11_5 -#define ICH_VMCR_EL2 S3_4_C12_C11_7 +#define ICH_VSEIR_EL2 sys_reg(3, 4, 12, 9, 4) +#define ICH_HCR_EL2 sys_reg(3, 4, 12, 11, 0) +#define ICH_VTR_EL2 sys_reg(3, 4, 12, 11, 1) +#define ICH_MISR_EL2 sys_reg(3, 4, 12, 11, 2) +#define ICH_EISR_EL2 sys_reg(3, 4, 12, 11, 3) +#define ICH_ELSR_EL2 sys_reg(3, 4, 12, 11, 5) +#define ICH_VMCR_EL2 sys_reg(3, 4, 12, 11, 7) -#define __LR0_EL2(x) S3_4_C12_C12_ ## x -#define __LR8_EL2(x) S3_4_C12_C13_ ## x +#define __LR0_EL2(x) sys_reg(3, 4, 12, 12, x) +#define __LR8_EL2(x) sys_reg(3, 4, 12, 13, x) #define ICH_LR0_EL2 __LR0_EL2(0) #define ICH_LR1_EL2 __LR0_EL2(1) @@ -171,13 +173,13 @@ #define ICH_LR14_EL2 __LR8_EL2(6) #define ICH_LR15_EL2 __LR8_EL2(7) -#define __AP0Rx_EL2(x) S3_4_C12_C8_ ## x +#define __AP0Rx_EL2(x) sys_reg(3, 4, 12, 8, x) #define ICH_AP0R0_EL2 __AP0Rx_EL2(0) #define ICH_AP0R1_EL2 __AP0Rx_EL2(1) #define ICH_AP0R2_EL2 __AP0Rx_EL2(2) #define ICH_AP0R3_EL2 __AP0Rx_EL2(3) -#define __AP1Rx_EL2(x) S3_4_C12_C9_ ## x +#define __AP1Rx_EL2(x) sys_reg(3, 4, 12, 9, x) #define ICH_AP1R0_EL2 __AP1Rx_EL2(0) #define ICH_AP1R1_EL2 __AP1Rx_EL2(1) #define ICH_AP1R2_EL2 __AP1Rx_EL2(2) @@ -189,7 +191,7 @@ static inline void gic_write_eoir(u64 irq) { - asm volatile("msr " __stringify(ICC_EOIR1_EL1) ", %0" : : "r" (irq)); + asm volatile("msr_s " __stringify(ICC_EOIR1_EL1) ", %0" : : "r" (irq)); isb(); } -- cgit v1.2.3 From d4da843e6fad4f278fe82b075d8e394cff05c95c Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Fri, 25 Jul 2014 14:25:31 +0200 Subject: netfilter: kill remnants of ulog targets The ulog targets were recently killed. A few references to the Kconfig macros CONFIG_IP_NF_TARGET_ULOG and CONFIG_BRIDGE_EBT_ULOG were left untouched. Kill these too. Signed-off-by: Paul Bolle Signed-off-by: Pablo Neira Ayuso --- include/net/netns/x_tables.h | 6 ------ net/bridge/netfilter/Makefile | 1 - net/ipv4/netfilter/Makefile | 1 - 3 files changed, 8 deletions(-) (limited to 'include') diff --git a/include/net/netns/x_tables.h b/include/net/netns/x_tables.h index 02fe40f8c8fd..c24060ee411e 100644 --- a/include/net/netns/x_tables.h +++ b/include/net/netns/x_tables.h @@ -15,11 +15,5 @@ struct netns_xt { struct ebt_table *frame_filter; struct ebt_table *frame_nat; #endif -#if IS_ENABLED(CONFIG_IP_NF_TARGET_ULOG) - bool ulog_warn_deprecated; -#endif -#if IS_ENABLED(CONFIG_BRIDGE_EBT_ULOG) - bool ebt_ulog_warn_deprecated; -#endif }; #endif diff --git a/net/bridge/netfilter/Makefile b/net/bridge/netfilter/Makefile index 061d121cf8b9..be4d0cea78ce 100644 --- a/net/bridge/netfilter/Makefile +++ b/net/bridge/netfilter/Makefile @@ -37,5 +37,4 @@ obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o # watchers obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o -obj-$(CONFIG_BRIDGE_EBT_ULOG) += ebt_ulog.o obj-$(CONFIG_BRIDGE_EBT_NFLOG) += ebt_nflog.o diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index 245db9df3337..33001621465b 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile @@ -57,7 +57,6 @@ obj-$(CONFIG_IP_NF_TARGET_ECN) += ipt_ECN.o obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o obj-$(CONFIG_IP_NF_TARGET_SYNPROXY) += ipt_SYNPROXY.o -obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o # generic ARP tables obj-$(CONFIG_IP_NF_ARPTABLES) += arp_tables.o -- cgit v1.2.3 From a259f3896a39ec7cbcd5f630a6ec95bdcbc080d2 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 24 Jul 2014 14:39:24 +0200 Subject: mfd: max77686: Add Maxim 77802 PMIC support Maxim MAX77802 is a power management chip that contains 10 high efficiency Buck regulators, 32 Low-dropout (LDO) regulators used to power up application processors and peripherals, a 2-channel 32kHz clock outputs, a Real-Time-Clock (RTC) and a I2C interface to program the individual regulators, clocks outputs and the RTC. This patch adds support for MAX77802 to the MAX77686 driver and is based on a driver added to the Chrome OS kernel 3.8 by Simon Glass. Signed-off-by: Javier Martinez Canillas Reviewed-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 6 +- drivers/mfd/max77686.c | 197 ++++++++++++++++++++++++++++----- include/linux/mfd/max77686-private.h | 208 ++++++++++++++++++++++++++++++++++- include/linux/mfd/max77686.h | 57 +++++++++- 4 files changed, 436 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 30102042dcaf..de5abf244746 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -380,15 +380,15 @@ config MFD_MAX14577 of the device. config MFD_MAX77686 - bool "Maxim Semiconductor MAX77686 PMIC Support" + bool "Maxim Semiconductor MAX77686/802 PMIC Support" depends on I2C=y select MFD_CORE select REGMAP_I2C select REGMAP_IRQ select IRQ_DOMAIN help - Say yes here to add support for Maxim Semiconductor MAX77686. - This is a Power Management IC with RTC on chip. + Say yes here to add support for Maxim Semiconductor MAX77686 and + MAX77802 which are Power Management IC with an 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. diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c index f2bd69915987..c65332291bb4 100644 --- a/drivers/mfd/max77686.c +++ b/drivers/mfd/max77686.c @@ -1,5 +1,5 @@ /* - * max77686.c - mfd core driver for the Maxim 77686 + * max77686.c - mfd core driver for the Maxim 77686/802 * * Copyright (C) 2012 Samsung Electronics * Chiwoong Byun @@ -43,6 +43,74 @@ static const struct mfd_cell max77686_devs[] = { { .name = "max77686-clk", }, }; +static const struct mfd_cell max77802_devs[] = { + { .name = "max77802-pmic", }, + { .name = "max77802-clk", }, + { .name = "max77802-rtc", }, +}; + +static bool max77802_pmic_is_accessible_reg(struct device *dev, + unsigned int reg) +{ + return (reg >= MAX77802_REG_DEVICE_ID && reg < MAX77802_REG_PMIC_END); +} + +static bool max77802_rtc_is_accessible_reg(struct device *dev, + unsigned int reg) +{ + return (reg >= MAX77802_RTC_INT && reg < MAX77802_RTC_END); +} + +static bool max77802_is_accessible_reg(struct device *dev, unsigned int reg) +{ + return (max77802_pmic_is_accessible_reg(dev, reg) || + max77802_rtc_is_accessible_reg(dev, reg)); +} + +static bool max77802_pmic_is_precious_reg(struct device *dev, unsigned int reg) +{ + return (reg == MAX77802_REG_INTSRC || reg == MAX77802_REG_INT1 || + reg == MAX77802_REG_INT2); +} + +static bool max77802_rtc_is_precious_reg(struct device *dev, unsigned int reg) +{ + return (reg == MAX77802_RTC_INT || + reg == MAX77802_RTC_UPDATE0 || + reg == MAX77802_RTC_UPDATE1); +} + +static bool max77802_is_precious_reg(struct device *dev, unsigned int reg) +{ + return (max77802_pmic_is_precious_reg(dev, reg) || + max77802_rtc_is_precious_reg(dev, reg)); +} + +static bool max77802_pmic_is_volatile_reg(struct device *dev, unsigned int reg) +{ + return (max77802_is_precious_reg(dev, reg) || + reg == MAX77802_REG_STATUS1 || reg == MAX77802_REG_STATUS2 || + reg == MAX77802_REG_PWRON); +} + +static bool max77802_rtc_is_volatile_reg(struct device *dev, unsigned int reg) +{ + return (max77802_rtc_is_precious_reg(dev, reg) || + reg == MAX77802_RTC_SEC || + reg == MAX77802_RTC_MIN || + reg == MAX77802_RTC_HOUR || + reg == MAX77802_RTC_WEEKDAY || + reg == MAX77802_RTC_MONTH || + reg == MAX77802_RTC_YEAR || + reg == MAX77802_RTC_DATE); +} + +static bool max77802_is_volatile_reg(struct device *dev, unsigned int reg) +{ + return (max77802_pmic_is_volatile_reg(dev, reg) || + max77802_rtc_is_volatile_reg(dev, reg)); +} + static struct regmap_config max77686_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -53,6 +121,17 @@ static struct regmap_config max77686_rtc_regmap_config = { .val_bits = 8, }; +static struct regmap_config max77802_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = max77802_is_accessible_reg, + .readable_reg = max77802_is_accessible_reg, + .precious_reg = max77802_is_precious_reg, + .volatile_reg = max77802_is_volatile_reg, + .name = "max77802-pmic", + .cache_type = REGCACHE_RBTREE, +}; + static const struct regmap_irq max77686_irqs[] = { /* INT1 interrupts */ { .reg_offset = 0, .mask = MAX77686_INT1_PWRONF_MSK, }, @@ -96,9 +175,34 @@ static const struct regmap_irq_chip max77686_rtc_irq_chip = { .num_irqs = ARRAY_SIZE(max77686_rtc_irqs), }; +static const struct regmap_irq_chip max77802_irq_chip = { + .name = "max77802-pmic", + .status_base = MAX77802_REG_INT1, + .mask_base = MAX77802_REG_INT1MSK, + .num_regs = 2, + .irqs = max77686_irqs, /* same masks as 77686 */ + .num_irqs = ARRAY_SIZE(max77686_irqs), +}; + +static const struct regmap_irq_chip max77802_rtc_irq_chip = { + .name = "max77802-rtc", + .status_base = MAX77802_RTC_INT, + .mask_base = MAX77802_RTC_INTM, + .num_regs = 1, + .irqs = max77686_rtc_irqs, /* same masks as 77686 */ + .num_irqs = ARRAY_SIZE(max77686_rtc_irqs), +}; + static const struct of_device_id max77686_pmic_dt_match[] = { - {.compatible = "maxim,max77686", .data = NULL}, - {}, + { + .compatible = "maxim,max77686", + .data = (void *)TYPE_MAX77686, + }, + { + .compatible = "maxim,max77802", + .data = (void *)TYPE_MAX77802, + }, + { }, }; static struct max77686_platform_data *max77686_i2c_parse_dt_pdata(struct device @@ -119,8 +223,15 @@ static int max77686_i2c_probe(struct i2c_client *i2c, { struct max77686_dev *max77686 = NULL; struct max77686_platform_data *pdata = dev_get_platdata(&i2c->dev); + const struct of_device_id *match; unsigned int data; int ret = 0; + const struct regmap_config *config; + const struct regmap_irq_chip *irq_chip; + const struct regmap_irq_chip *rtc_irq_chip; + struct regmap **rtc_regmap; + const struct mfd_cell *cells; + int n_devs; if (IS_ENABLED(CONFIG_OF) && i2c->dev.of_node && !pdata) pdata = max77686_i2c_parse_dt_pdata(&i2c->dev); @@ -135,15 +246,40 @@ static int max77686_i2c_probe(struct i2c_client *i2c, if (!max77686) return -ENOMEM; + if (i2c->dev.of_node) { + match = of_match_node(max77686_pmic_dt_match, i2c->dev.of_node); + if (!match) + return -EINVAL; + + max77686->type = (int)match->data; + } else { + max77686->type = id->driver_data; + } + i2c_set_clientdata(i2c, max77686); max77686->dev = &i2c->dev; max77686->i2c = i2c; - max77686->type = id->driver_data; max77686->wakeup = pdata->wakeup; max77686->irq = i2c->irq; - max77686->regmap = devm_regmap_init_i2c(i2c, &max77686_regmap_config); + if (max77686->type == TYPE_MAX77686) { + config = &max77686_regmap_config; + irq_chip = &max77686_irq_chip; + rtc_irq_chip = &max77686_rtc_irq_chip; + rtc_regmap = &max77686->rtc_regmap; + cells = max77686_devs; + n_devs = ARRAY_SIZE(max77686_devs); + } else { + config = &max77802_regmap_config; + irq_chip = &max77802_irq_chip; + rtc_irq_chip = &max77802_rtc_irq_chip; + rtc_regmap = &max77686->regmap; + cells = max77802_devs; + n_devs = ARRAY_SIZE(max77802_devs); + } + + max77686->regmap = devm_regmap_init_i2c(i2c, config); if (IS_ERR(max77686->regmap)) { ret = PTR_ERR(max77686->regmap); dev_err(max77686->dev, "Failed to allocate register map: %d\n", @@ -158,41 +294,46 @@ static int max77686_i2c_probe(struct i2c_client *i2c, return -ENODEV; } - max77686->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC); - if (!max77686->rtc) { - dev_err(max77686->dev, "Failed to allocate I2C device for RTC\n"); - return -ENODEV; - } - i2c_set_clientdata(max77686->rtc, max77686); - - max77686->rtc_regmap = devm_regmap_init_i2c(max77686->rtc, - &max77686_rtc_regmap_config); - if (IS_ERR(max77686->rtc_regmap)) { - ret = PTR_ERR(max77686->rtc_regmap); - dev_err(max77686->dev, "failed to allocate RTC regmap: %d\n", - ret); - goto err_unregister_i2c; + if (max77686->type == TYPE_MAX77686) { + max77686->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC); + if (!max77686->rtc) { + dev_err(max77686->dev, + "Failed to allocate I2C device for RTC\n"); + return -ENODEV; + } + i2c_set_clientdata(max77686->rtc, max77686); + + max77686->rtc_regmap = + devm_regmap_init_i2c(max77686->rtc, + &max77686_rtc_regmap_config); + if (IS_ERR(max77686->rtc_regmap)) { + ret = PTR_ERR(max77686->rtc_regmap); + dev_err(max77686->dev, + "failed to allocate RTC regmap: %d\n", + ret); + goto err_unregister_i2c; + } } ret = regmap_add_irq_chip(max77686->regmap, max77686->irq, IRQF_TRIGGER_FALLING | IRQF_ONESHOT | - IRQF_SHARED, 0, &max77686_irq_chip, + IRQF_SHARED, 0, irq_chip, &max77686->irq_data); if (ret) { dev_err(&i2c->dev, "failed to add PMIC irq chip: %d\n", ret); goto err_unregister_i2c; } - ret = regmap_add_irq_chip(max77686->rtc_regmap, max77686->irq, + + ret = regmap_add_irq_chip(*rtc_regmap, max77686->irq, IRQF_TRIGGER_FALLING | IRQF_ONESHOT | - IRQF_SHARED, 0, &max77686_rtc_irq_chip, + IRQF_SHARED, 0, rtc_irq_chip, &max77686->rtc_irq_data); if (ret) { dev_err(&i2c->dev, "failed to add RTC irq chip: %d\n", ret); goto err_del_irqc; } - ret = mfd_add_devices(max77686->dev, -1, max77686_devs, - ARRAY_SIZE(max77686_devs), NULL, 0, NULL); + ret = mfd_add_devices(max77686->dev, -1, cells, n_devs, NULL, 0, NULL); if (ret < 0) { dev_err(&i2c->dev, "failed to add MFD devices: %d\n", ret); goto err_del_rtc_irqc; @@ -205,7 +346,8 @@ err_del_rtc_irqc: err_del_irqc: regmap_del_irq_chip(max77686->irq, max77686->irq_data); err_unregister_i2c: - i2c_unregister_device(max77686->rtc); + if (max77686->type == TYPE_MAX77686) + i2c_unregister_device(max77686->rtc); return ret; } @@ -219,7 +361,8 @@ static int max77686_i2c_remove(struct i2c_client *i2c) regmap_del_irq_chip(max77686->irq, max77686->rtc_irq_data); regmap_del_irq_chip(max77686->irq, max77686->irq_data); - i2c_unregister_device(max77686->rtc); + if (max77686->type == TYPE_MAX77686) + i2c_unregister_device(max77686->rtc); return 0; } @@ -294,6 +437,6 @@ static void __exit max77686_i2c_exit(void) } module_exit(max77686_i2c_exit); -MODULE_DESCRIPTION("MAXIM 77686 multi-function core driver"); +MODULE_DESCRIPTION("MAXIM 77686/802 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 index 8e177806cba1..0d60b38e5b5c 100644 --- a/include/linux/mfd/max77686-private.h +++ b/include/linux/mfd/max77686-private.h @@ -1,5 +1,5 @@ /* - * max77686-private.h - Voltage regulator driver for the Maxim 77686 + * max77686-private.h - Voltage regulator driver for the Maxim 77686/802 * * Copyright (C) 2012 Samsung Electrnoics * Chiwoong Byun @@ -28,6 +28,7 @@ #define MAX77686_REG_INVALID (0xff) +/* MAX77686 PMIC registers */ enum max77686_pmic_reg { MAX77686_REG_DEVICE_ID = 0x00, MAX77686_REG_INTSRC = 0x01, @@ -181,6 +182,210 @@ enum max77686_rtc_reg { MAX77686_ALARM2_DATE = 0x1B, }; +/* MAX77802 PMIC registers */ +enum max77802_pmic_reg { + MAX77802_REG_DEVICE_ID = 0x00, + MAX77802_REG_INTSRC = 0x01, + MAX77802_REG_INT1 = 0x02, + MAX77802_REG_INT2 = 0x03, + + MAX77802_REG_INT1MSK = 0x04, + MAX77802_REG_INT2MSK = 0x05, + + MAX77802_REG_STATUS1 = 0x06, + MAX77802_REG_STATUS2 = 0x07, + + MAX77802_REG_PWRON = 0x08, + /* Reserved: 0x09 */ + MAX77802_REG_MRSTB = 0x0A, + MAX77802_REG_EPWRHOLD = 0x0B, + /* Reserved: 0x0C-0x0D */ + MAX77802_REG_BOOSTCTRL = 0x0E, + MAX77802_REG_BOOSTOUT = 0x0F, + + MAX77802_REG_BUCK1CTRL = 0x10, + MAX77802_REG_BUCK1DVS1 = 0x11, + MAX77802_REG_BUCK1DVS2 = 0x12, + MAX77802_REG_BUCK1DVS3 = 0x13, + MAX77802_REG_BUCK1DVS4 = 0x14, + MAX77802_REG_BUCK1DVS5 = 0x15, + MAX77802_REG_BUCK1DVS6 = 0x16, + MAX77802_REG_BUCK1DVS7 = 0x17, + MAX77802_REG_BUCK1DVS8 = 0x18, + /* Reserved: 0x19 */ + MAX77802_REG_BUCK2CTRL1 = 0x1A, + MAX77802_REG_BUCK2CTRL2 = 0x1B, + MAX77802_REG_BUCK2PHTRAN = 0x1C, + MAX77802_REG_BUCK2DVS1 = 0x1D, + MAX77802_REG_BUCK2DVS2 = 0x1E, + MAX77802_REG_BUCK2DVS3 = 0x1F, + MAX77802_REG_BUCK2DVS4 = 0x20, + MAX77802_REG_BUCK2DVS5 = 0x21, + MAX77802_REG_BUCK2DVS6 = 0x22, + MAX77802_REG_BUCK2DVS7 = 0x23, + MAX77802_REG_BUCK2DVS8 = 0x24, + /* Reserved: 0x25-0x26 */ + MAX77802_REG_BUCK3CTRL1 = 0x27, + MAX77802_REG_BUCK3DVS1 = 0x28, + MAX77802_REG_BUCK3DVS2 = 0x29, + MAX77802_REG_BUCK3DVS3 = 0x2A, + MAX77802_REG_BUCK3DVS4 = 0x2B, + MAX77802_REG_BUCK3DVS5 = 0x2C, + MAX77802_REG_BUCK3DVS6 = 0x2D, + MAX77802_REG_BUCK3DVS7 = 0x2E, + MAX77802_REG_BUCK3DVS8 = 0x2F, + /* Reserved: 0x30-0x36 */ + MAX77802_REG_BUCK4CTRL1 = 0x37, + MAX77802_REG_BUCK4DVS1 = 0x38, + MAX77802_REG_BUCK4DVS2 = 0x39, + MAX77802_REG_BUCK4DVS3 = 0x3A, + MAX77802_REG_BUCK4DVS4 = 0x3B, + MAX77802_REG_BUCK4DVS5 = 0x3C, + MAX77802_REG_BUCK4DVS6 = 0x3D, + MAX77802_REG_BUCK4DVS7 = 0x3E, + MAX77802_REG_BUCK4DVS8 = 0x3F, + /* Reserved: 0x40 */ + MAX77802_REG_BUCK5CTRL = 0x41, + MAX77802_REG_BUCK5OUT = 0x42, + /* Reserved: 0x43 */ + MAX77802_REG_BUCK6CTRL = 0x44, + MAX77802_REG_BUCK6DVS1 = 0x45, + MAX77802_REG_BUCK6DVS2 = 0x46, + MAX77802_REG_BUCK6DVS3 = 0x47, + MAX77802_REG_BUCK6DVS4 = 0x48, + MAX77802_REG_BUCK6DVS5 = 0x49, + MAX77802_REG_BUCK6DVS6 = 0x4A, + MAX77802_REG_BUCK6DVS7 = 0x4B, + MAX77802_REG_BUCK6DVS8 = 0x4C, + /* Reserved: 0x4D */ + MAX77802_REG_BUCK7CTRL = 0x4E, + MAX77802_REG_BUCK7OUT = 0x4F, + /* Reserved: 0x50 */ + MAX77802_REG_BUCK8CTRL = 0x51, + MAX77802_REG_BUCK8OUT = 0x52, + /* Reserved: 0x53 */ + MAX77802_REG_BUCK9CTRL = 0x54, + MAX77802_REG_BUCK9OUT = 0x55, + /* Reserved: 0x56 */ + MAX77802_REG_BUCK10CTRL = 0x57, + MAX77802_REG_BUCK10OUT = 0x58, + + /* Reserved: 0x59-0x5F */ + + MAX77802_REG_LDO1CTRL1 = 0x60, + MAX77802_REG_LDO2CTRL1 = 0x61, + MAX77802_REG_LDO3CTRL1 = 0x62, + MAX77802_REG_LDO4CTRL1 = 0x63, + MAX77802_REG_LDO5CTRL1 = 0x64, + MAX77802_REG_LDO6CTRL1 = 0x65, + MAX77802_REG_LDO7CTRL1 = 0x66, + MAX77802_REG_LDO8CTRL1 = 0x67, + MAX77802_REG_LDO9CTRL1 = 0x68, + MAX77802_REG_LDO10CTRL1 = 0x69, + MAX77802_REG_LDO11CTRL1 = 0x6A, + MAX77802_REG_LDO12CTRL1 = 0x6B, + MAX77802_REG_LDO13CTRL1 = 0x6C, + MAX77802_REG_LDO14CTRL1 = 0x6D, + MAX77802_REG_LDO15CTRL1 = 0x6E, + /* Reserved: 0x6F */ + MAX77802_REG_LDO17CTRL1 = 0x70, + MAX77802_REG_LDO18CTRL1 = 0x71, + MAX77802_REG_LDO19CTRL1 = 0x72, + MAX77802_REG_LDO20CTRL1 = 0x73, + MAX77802_REG_LDO21CTRL1 = 0x74, + MAX77802_REG_LDO22CTRL1 = 0x75, + MAX77802_REG_LDO23CTRL1 = 0x76, + MAX77802_REG_LDO24CTRL1 = 0x77, + MAX77802_REG_LDO25CTRL1 = 0x78, + MAX77802_REG_LDO26CTRL1 = 0x79, + MAX77802_REG_LDO27CTRL1 = 0x7A, + MAX77802_REG_LDO28CTRL1 = 0x7B, + MAX77802_REG_LDO29CTRL1 = 0x7C, + MAX77802_REG_LDO30CTRL1 = 0x7D, + /* Reserved: 0x7E */ + MAX77802_REG_LDO32CTRL1 = 0x7F, + MAX77802_REG_LDO33CTRL1 = 0x80, + MAX77802_REG_LDO34CTRL1 = 0x81, + MAX77802_REG_LDO35CTRL1 = 0x82, + /* Reserved: 0x83-0x8F */ + MAX77802_REG_LDO1CTRL2 = 0x90, + MAX77802_REG_LDO2CTRL2 = 0x91, + MAX77802_REG_LDO3CTRL2 = 0x92, + MAX77802_REG_LDO4CTRL2 = 0x93, + MAX77802_REG_LDO5CTRL2 = 0x94, + MAX77802_REG_LDO6CTRL2 = 0x95, + MAX77802_REG_LDO7CTRL2 = 0x96, + MAX77802_REG_LDO8CTRL2 = 0x97, + MAX77802_REG_LDO9CTRL2 = 0x98, + MAX77802_REG_LDO10CTRL2 = 0x99, + MAX77802_REG_LDO11CTRL2 = 0x9A, + MAX77802_REG_LDO12CTRL2 = 0x9B, + MAX77802_REG_LDO13CTRL2 = 0x9C, + MAX77802_REG_LDO14CTRL2 = 0x9D, + MAX77802_REG_LDO15CTRL2 = 0x9E, + /* Reserved: 0x9F */ + MAX77802_REG_LDO17CTRL2 = 0xA0, + MAX77802_REG_LDO18CTRL2 = 0xA1, + MAX77802_REG_LDO19CTRL2 = 0xA2, + MAX77802_REG_LDO20CTRL2 = 0xA3, + MAX77802_REG_LDO21CTRL2 = 0xA4, + MAX77802_REG_LDO22CTRL2 = 0xA5, + MAX77802_REG_LDO23CTRL2 = 0xA6, + MAX77802_REG_LDO24CTRL2 = 0xA7, + MAX77802_REG_LDO25CTRL2 = 0xA8, + MAX77802_REG_LDO26CTRL2 = 0xA9, + MAX77802_REG_LDO27CTRL2 = 0xAA, + MAX77802_REG_LDO28CTRL2 = 0xAB, + MAX77802_REG_LDO29CTRL2 = 0xAC, + MAX77802_REG_LDO30CTRL2 = 0xAD, + /* Reserved: 0xAE */ + MAX77802_REG_LDO32CTRL2 = 0xAF, + MAX77802_REG_LDO33CTRL2 = 0xB0, + MAX77802_REG_LDO34CTRL2 = 0xB1, + MAX77802_REG_LDO35CTRL2 = 0xB2, + /* Reserved: 0xB3 */ + + MAX77802_REG_BBAT_CHG = 0xB4, + MAX77802_REG_32KHZ = 0xB5, + + MAX77802_REG_PMIC_END = 0xB6, +}; + +enum max77802_rtc_reg { + MAX77802_RTC_INT = 0xC0, + MAX77802_RTC_INTM = 0xC1, + MAX77802_RTC_CONTROLM = 0xC2, + MAX77802_RTC_CONTROL = 0xC3, + MAX77802_RTC_UPDATE0 = 0xC4, + MAX77802_RTC_UPDATE1 = 0xC5, + MAX77802_WTSR_SMPL_CNTL = 0xC6, + MAX77802_RTC_SEC = 0xC7, + MAX77802_RTC_MIN = 0xC8, + MAX77802_RTC_HOUR = 0xC9, + MAX77802_RTC_WEEKDAY = 0xCA, + MAX77802_RTC_MONTH = 0xCB, + MAX77802_RTC_YEAR = 0xCC, + MAX77802_RTC_DATE = 0xCD, + MAX77802_RTC_AE1 = 0xCE, + MAX77802_ALARM1_SEC = 0xCF, + MAX77802_ALARM1_MIN = 0xD0, + MAX77802_ALARM1_HOUR = 0xD1, + MAX77802_ALARM1_WEEKDAY = 0xD2, + MAX77802_ALARM1_MONTH = 0xD3, + MAX77802_ALARM1_YEAR = 0xD4, + MAX77802_ALARM1_DATE = 0xD5, + MAX77802_RTC_AE2 = 0xD6, + MAX77802_ALARM2_SEC = 0xD7, + MAX77802_ALARM2_MIN = 0xD8, + MAX77802_ALARM2_HOUR = 0xD9, + MAX77802_ALARM2_WEEKDAY = 0xDA, + MAX77802_ALARM2_MONTH = 0xDB, + MAX77802_ALARM2_YEAR = 0xDC, + MAX77802_ALARM2_DATE = 0xDD, + + MAX77802_RTC_END = 0xDF, +}; + enum max77686_irq_source { PMIC_INT1 = 0, PMIC_INT2, @@ -250,6 +455,7 @@ struct max77686_dev { enum max77686_types { TYPE_MAX77686, + TYPE_MAX77802, }; extern int max77686_irq_init(struct max77686_dev *max77686); diff --git a/include/linux/mfd/max77686.h b/include/linux/mfd/max77686.h index 4cbcc13e8a2a..7e6dc4b2b795 100644 --- a/include/linux/mfd/max77686.h +++ b/include/linux/mfd/max77686.h @@ -1,5 +1,5 @@ /* - * max77686.h - Driver for the Maxim 77686 + * max77686.h - Driver for the Maxim 77686/802 * * Copyright (C) 2012 Samsung Electrnoics * Chiwoong Byun @@ -71,6 +71,54 @@ enum max77686_regulators { MAX77686_REG_MAX, }; +/* MAX77802 regulator IDs */ +enum max77802_regulators { + MAX77802_BUCK1 = 0, + MAX77802_BUCK2, + MAX77802_BUCK3, + MAX77802_BUCK4, + MAX77802_BUCK5, + MAX77802_BUCK6, + MAX77802_BUCK7, + MAX77802_BUCK8, + MAX77802_BUCK9, + MAX77802_BUCK10, + MAX77802_LDO1, + MAX77802_LDO2, + MAX77802_LDO3, + MAX77802_LDO4, + MAX77802_LDO5, + MAX77802_LDO6, + MAX77802_LDO7, + MAX77802_LDO8, + MAX77802_LDO9, + MAX77802_LDO10, + MAX77802_LDO11, + MAX77802_LDO12, + MAX77802_LDO13, + MAX77802_LDO14, + MAX77802_LDO15, + MAX77802_LDO17, + MAX77802_LDO18, + MAX77802_LDO19, + MAX77802_LDO20, + MAX77802_LDO21, + MAX77802_LDO23, + MAX77802_LDO24, + MAX77802_LDO25, + MAX77802_LDO26, + MAX77802_LDO27, + MAX77802_LDO28, + MAX77802_LDO29, + MAX77802_LDO30, + MAX77802_LDO32, + MAX77802_LDO33, + MAX77802_LDO34, + MAX77802_LDO35, + + MAX77802_REG_MAX, +}; + struct max77686_regulator_data { int id; struct regulator_init_data *initdata; @@ -83,6 +131,13 @@ enum max77686_opmode { MAX77686_OPMODE_STANDBY, }; +enum max77802_opmode { + MAX77802_OPMODE_OFF, + MAX77802_OPMODE_STANDBY, + MAX77802_OPMODE_LP, + MAX77802_OPMODE_NORMAL, +}; + struct max77686_opmode_data { int id; int mode; -- cgit v1.2.3 From ec8bd56699cb4371994437583a285b855b6f5e3a Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 24 Jul 2014 17:07:16 +0100 Subject: mfd: max77686: Ensure device type IDs are architecture agnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extinguishes: ../drivers/mfd/max77686.c: In function ‘max77686_i2c_probe’: ../drivers/mfd/max77686.c:254:20: warning: cast from pointer to integer of different size Signed-off-by: Lee Jones --- drivers/mfd/max77686.c | 5 ++--- include/linux/mfd/max77686-private.h | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c index c65332291bb4..86e552348db4 100644 --- a/drivers/mfd/max77686.c +++ b/drivers/mfd/max77686.c @@ -251,10 +251,9 @@ static int max77686_i2c_probe(struct i2c_client *i2c, if (!match) return -EINVAL; - max77686->type = (int)match->data; - } else { + max77686->type = (unsigned long)match->data; + } else max77686->type = id->driver_data; - } i2c_set_clientdata(i2c, max77686); max77686->dev = &i2c->dev; diff --git a/include/linux/mfd/max77686-private.h b/include/linux/mfd/max77686-private.h index 0d60b38e5b5c..960b92ad450d 100644 --- a/include/linux/mfd/max77686-private.h +++ b/include/linux/mfd/max77686-private.h @@ -439,7 +439,7 @@ struct max77686_dev { struct i2c_client *i2c; /* 0xcc / PMIC, Battery Control, and FLASH */ struct i2c_client *rtc; /* slave addr 0x0c */ - int type; + unsigned long type; struct regmap *regmap; /* regmap for mfd */ struct regmap *rtc_regmap; /* regmap for rtc */ -- cgit v1.2.3 From 8d7d3972a9ae962bbf8ce49c83f4a40082708f69 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Mon, 21 Jul 2014 18:38:47 +0300 Subject: regmap: Add regmap_get_device Add a new function regmap_get_device to obtain the underlying struct device from a regmap. Signed-off-by: Tuomas Tynkkynen Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 12 ++++++++++++ include/linux/regmap.h | 7 +++++++ 2 files changed, 19 insertions(+) (limited to 'include') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 74d8c0672cf6..5ba0263fce21 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1073,6 +1073,18 @@ struct regmap *dev_get_regmap(struct device *dev, const char *name) } EXPORT_SYMBOL_GPL(dev_get_regmap); +/** + * regmap_get_device(): Obtain the device from a regmap + * + * @map: Register map to operate on. + * + * Returns the underlying device that the regmap has been created for. + */ +struct device *regmap_get_device(struct regmap *map) +{ + return map->dev; +} + static int _regmap_select_page(struct regmap *map, unsigned int *reg, struct regmap_range_node *range, unsigned int val_num) diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 7b0e4b425cdf..cd480fd59795 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -396,6 +396,7 @@ void regmap_exit(struct regmap *map); int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config); struct regmap *dev_get_regmap(struct device *dev, const char *name); +struct device *regmap_get_device(struct regmap *map); int regmap_write(struct regmap *map, unsigned int reg, unsigned int val); int regmap_write_async(struct regmap *map, unsigned int reg, unsigned int val); int regmap_raw_write(struct regmap *map, unsigned int reg, @@ -729,6 +730,12 @@ static inline struct regmap *dev_get_regmap(struct device *dev, return NULL; } +static inline struct device *regmap_get_device(struct regmap *map) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + #endif #endif -- cgit v1.2.3 From 04eca28cde52cdf9eb91e127cc358ad79a8ec53b Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Mon, 21 Jul 2014 18:38:48 +0300 Subject: regulator: Add helpers for low-level register access Add helper functions that allow regulator consumers to obtain low-level details about the regulator hardware, like the voltage selector register address and such. These details can be useful when configuring hardware or firmware that want to do low-level access to regulators, with no involvement from the kernel. The use-case for Tegra is a voltage-controlled oscillator clocksource which has control logic to change the supply voltage via I2C to achieve a desired output clock rate. Signed-off-by: Tuomas Tynkkynen Signed-off-by: Mark Brown --- Documentation/power/regulator/consumer.txt | 35 +++++++++++++++ drivers/regulator/core.c | 71 ++++++++++++++++++++++++++++++ include/linux/regulator/consumer.h | 26 +++++++++++ 3 files changed, 132 insertions(+) (limited to 'include') diff --git a/Documentation/power/regulator/consumer.txt b/Documentation/power/regulator/consumer.txt index 55c4175d8099..81c0e2b49cd8 100644 --- a/Documentation/power/regulator/consumer.txt +++ b/Documentation/power/regulator/consumer.txt @@ -180,3 +180,38 @@ int regulator_unregister_notifier(struct regulator *regulator, Regulators use the kernel notifier framework to send event to their interested consumers. + +7. Regulator Direct Register Access +=================================== +Some kinds of power management hardware or firmware are designed such that +they need to do low-level hardware access to regulators, with no involvement +from the kernel. Examples of such devices are: + +- clocksource with a voltage-controlled oscillator and control logic to change + the supply voltage over I2C to achieve a desired output clock rate +- thermal management firmware that can issue an arbitrary I2C transaction to + perform system poweroff during overtemperature conditions + +To set up such a device/firmware, various parameters like I2C address of the +regulator, addresses of various regulator registers etc. need to be configured +to it. The regulator framework provides the following helpers for querying +these details. + +Bus-specific details, like I2C addresses or transfer rates are handled by the +regmap framework. To get the regulator's regmap (if supported), use :- + +struct regmap *regulator_get_regmap(struct regulator *regulator); + +To obtain the hardware register offset and bitmask for the regulator's voltage +selector register, use :- + +int regulator_get_hardware_vsel_register(struct regulator *regulator, + unsigned *vsel_reg, + unsigned *vsel_mask); + +To convert a regulator framework voltage selector code (used by +regulator_list_voltage) to a hardware-specific voltage selector that can be +directly written to the voltage selector register, use :- + +int regulator_list_hardware_vsel(struct regulator *regulator, + unsigned selector); diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 4c1f999041dd..486b5908469e 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2221,6 +2221,77 @@ int regulator_list_voltage(struct regulator *regulator, unsigned selector) } EXPORT_SYMBOL_GPL(regulator_list_voltage); +/** + * regulator_get_regmap - get the regulator's register map + * @regulator: regulator source + * + * Returns the register map for the given regulator, or an ERR_PTR value + * if the regulator doesn't use regmap. + */ +struct regmap *regulator_get_regmap(struct regulator *regulator) +{ + struct regmap *map = regulator->rdev->regmap; + + return map ? map : ERR_PTR(-EOPNOTSUPP); +} + +/** + * regulator_get_hardware_vsel_register - get the HW voltage selector register + * @regulator: regulator source + * @vsel_reg: voltage selector register, output parameter + * @vsel_mask: mask for voltage selector bitfield, output parameter + * + * Returns the hardware register offset and bitmask used for setting the + * regulator voltage. This might be useful when configuring voltage-scaling + * hardware or firmware that can make I2C requests behind the kernel's back, + * for example. + * + * On success, the output parameters @vsel_reg and @vsel_mask are filled in + * and 0 is returned, otherwise a negative errno is returned. + */ +int regulator_get_hardware_vsel_register(struct regulator *regulator, + unsigned *vsel_reg, + unsigned *vsel_mask) +{ + struct regulator_dev *rdev = regulator->rdev; + struct regulator_ops *ops = rdev->desc->ops; + + if (ops->set_voltage_sel != regulator_set_voltage_sel_regmap) + return -EOPNOTSUPP; + + *vsel_reg = rdev->desc->vsel_reg; + *vsel_mask = rdev->desc->vsel_mask; + + return 0; +} +EXPORT_SYMBOL_GPL(regulator_get_hardware_vsel_register); + +/** + * regulator_list_hardware_vsel - get the HW-specific register value for a selector + * @regulator: regulator source + * @selector: identify voltage to list + * + * Converts the selector to a hardware-specific voltage selector that can be + * directly written to the regulator registers. The address of the voltage + * register can be determined by calling @regulator_get_hardware_vsel_register. + * + * On error a negative errno is returned. + */ +int regulator_list_hardware_vsel(struct regulator *regulator, + unsigned selector) +{ + struct regulator_dev *rdev = regulator->rdev; + struct regulator_ops *ops = rdev->desc->ops; + + if (selector >= rdev->desc->n_voltages) + return -EINVAL; + if (ops->set_voltage_sel != regulator_set_voltage_sel_regmap) + return -EOPNOTSUPP; + + return selector; +} +EXPORT_SYMBOL_GPL(regulator_list_hardware_vsel); + /** * regulator_get_linear_step - return the voltage step size between VSEL values * @regulator: regulator source diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index a2d9d81038d1..0b1c8d09a6b1 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -37,6 +37,7 @@ struct device; struct notifier_block; +struct regmap; /* * Regulator operating modes. @@ -215,6 +216,13 @@ int regulator_set_optimum_mode(struct regulator *regulator, int load_uA); int regulator_allow_bypass(struct regulator *regulator, bool allow); +struct regmap *regulator_get_regmap(struct regulator *regulator); +int regulator_get_hardware_vsel_register(struct regulator *regulator, + unsigned *vsel_reg, + unsigned *vsel_mask); +int regulator_list_hardware_vsel(struct regulator *regulator, + unsigned selector); + /* regulator notifier block */ int regulator_register_notifier(struct regulator *regulator, struct notifier_block *nb); @@ -452,6 +460,24 @@ static inline int regulator_allow_bypass(struct regulator *regulator, return 0; } +struct regmap *regulator_get_regmap(struct regulator *regulator) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +int regulator_get_hardware_vsel_register(struct regulator *regulator, + unsigned *vsel_reg, + unsigned *vsel_mask) +{ + return -EOPNOTSUPP; +} + +int regulator_list_hardware_vsel(struct regulator *regulator, + unsigned selector) +{ + return -EOPNOTSUPP; +} + static inline int regulator_register_notifier(struct regulator *regulator, struct notifier_block *nb) { -- cgit v1.2.3 From 1d33dc6b0f0fd1a1f65011f54165c558daf46638 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 25 Jul 2014 19:01:53 +0100 Subject: regmap: Fix return code for stub regmap_get_device() We return a pointer, not an int. Signed-off-by: Mark Brown --- include/linux/regmap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/regmap.h b/include/linux/regmap.h index cd480fd59795..c5ed83f49c4e 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -733,7 +733,7 @@ static inline struct regmap *dev_get_regmap(struct device *dev, static inline struct device *regmap_get_device(struct regmap *map) { WARN_ONCE(1, "regmap API is disabled"); - return -EINVAL; + return NULL; } #endif -- cgit v1.2.3 From 13752fe2d7f2d41c2fd92a5d1b1c6e38c4de0c05 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 25 Feb 2014 10:28:04 -0800 Subject: security: introduce kernel_fw_from_file hook In order to validate the contents of firmware being loaded, there must be a hook to evaluate any loaded firmware that wasn't built into the kernel itself. Without this, there is a risk that a root user could load malicious firmware designed to mount an attack against kernel memory (e.g. via DMA). Signed-off-by: Kees Cook Reviewed-by: Takashi Iwai --- include/linux/security.h | 17 +++++++++++++++++ security/capability.c | 6 ++++++ security/security.c | 6 ++++++ 3 files changed, 29 insertions(+) (limited to 'include') diff --git a/include/linux/security.h b/include/linux/security.h index 59820f8782a1..0ae4b147718a 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -702,6 +702,15 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * @inode points to the inode to use as a reference. * The current task must be the one that nominated @inode. * Return 0 if successful. + * @kernel_fw_from_file: + * Load firmware from userspace (not called for built-in firmware). + * @file contains the file structure pointing to the file containing + * the firmware to load. This argument will be NULL if the firmware + * was loaded via the uevent-triggered blob-based interface exposed + * by CONFIG_FW_LOADER_USER_HELPER. + * @buf pointer to buffer containing firmware contents. + * @size length of the firmware contents. + * Return 0 if permission is granted. * @kernel_module_request: * Ability to trigger the kernel to automatically upcall to userspace for * userspace to load a kernel module with the given name. @@ -1568,6 +1577,7 @@ struct security_operations { void (*cred_transfer)(struct cred *new, const struct cred *old); int (*kernel_act_as)(struct cred *new, u32 secid); int (*kernel_create_files_as)(struct cred *new, struct inode *inode); + int (*kernel_fw_from_file)(struct file *file, char *buf, size_t size); int (*kernel_module_request)(char *kmod_name); int (*kernel_module_from_file)(struct file *file); int (*task_fix_setuid) (struct cred *new, const struct cred *old, @@ -1840,6 +1850,7 @@ int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp); void security_transfer_creds(struct cred *new, const struct cred *old); int security_kernel_act_as(struct cred *new, u32 secid); int security_kernel_create_files_as(struct cred *new, struct inode *inode); +int security_kernel_fw_from_file(struct file *file, char *buf, size_t size); int security_kernel_module_request(char *kmod_name); int security_kernel_module_from_file(struct file *file); int security_task_fix_setuid(struct cred *new, const struct cred *old, @@ -2366,6 +2377,12 @@ static inline int security_kernel_create_files_as(struct cred *cred, return 0; } +static inline int security_kernel_fw_from_file(struct file *file, + char *buf, size_t size) +{ + return 0; +} + static inline int security_kernel_module_request(char *kmod_name) { return 0; diff --git a/security/capability.c b/security/capability.c index e76373de3129..a74fde6a7468 100644 --- a/security/capability.c +++ b/security/capability.c @@ -401,6 +401,11 @@ static int cap_kernel_create_files_as(struct cred *new, struct inode *inode) return 0; } +static int cap_kernel_fw_from_file(struct file *file, char *buf, size_t size) +{ + return 0; +} + static int cap_kernel_module_request(char *kmod_name) { return 0; @@ -1015,6 +1020,7 @@ void __init security_fixup_ops(struct security_operations *ops) set_to_cap_if_null(ops, cred_transfer); set_to_cap_if_null(ops, kernel_act_as); set_to_cap_if_null(ops, kernel_create_files_as); + set_to_cap_if_null(ops, kernel_fw_from_file); set_to_cap_if_null(ops, kernel_module_request); set_to_cap_if_null(ops, kernel_module_from_file); set_to_cap_if_null(ops, task_fix_setuid); diff --git a/security/security.c b/security/security.c index 31614e9e96e5..35d37d0f0d49 100644 --- a/security/security.c +++ b/security/security.c @@ -845,6 +845,12 @@ int security_kernel_create_files_as(struct cred *new, struct inode *inode) return security_ops->kernel_create_files_as(new, inode); } +int security_kernel_fw_from_file(struct file *file, char *buf, size_t size) +{ + return security_ops->kernel_fw_from_file(file, buf, size); +} +EXPORT_SYMBOL_GPL(security_kernel_fw_from_file); + int security_kernel_module_request(char *kmod_name) { return security_ops->kernel_module_request(kmod_name); -- cgit v1.2.3 From 5a9196d715607f76d6b7d96a0970d6065335e62b Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Tue, 22 Jul 2014 10:39:48 -0400 Subject: ima: add support for measuring and appraising firmware The "security: introduce kernel_fw_from_file hook" patch defined a new security hook to evaluate any loaded firmware that wasn't built into the kernel. This patch defines ima_fw_from_file(), which is called from the new security hook, to measure and/or appraise the loaded firmware's integrity. Signed-off-by: Mimi Zohar Signed-off-by: Kees Cook --- Documentation/ABI/testing/ima_policy | 4 +++- include/linux/ima.h | 6 ++++++ security/integrity/ima/ima.h | 3 ++- security/integrity/ima/ima_appraise.c | 8 ++++++++ security/integrity/ima/ima_main.c | 11 +++++++++++ security/integrity/ima/ima_policy.c | 7 +++++++ security/integrity/integrity.h | 9 +++++++-- security/security.c | 7 ++++++- 8 files changed, 50 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy index 4c3efe434806..d0d0c578324c 100644 --- a/Documentation/ABI/testing/ima_policy +++ b/Documentation/ABI/testing/ima_policy @@ -26,6 +26,7 @@ Description: option: [[appraise_type=]] [permit_directio] base: func:= [BPRM_CHECK][MMAP_CHECK][FILE_CHECK][MODULE_CHECK] + [FIRMWARE_CHECK] mask:= [MAY_READ] [MAY_WRITE] [MAY_APPEND] [MAY_EXEC] fsmagic:= hex value fsuuid:= file system UUID (e.g 8bcbe394-4f13-4144-be8e-5aa9ea2ce2f6) @@ -57,7 +58,8 @@ Description: measure func=BPRM_CHECK measure func=FILE_MMAP mask=MAY_EXEC measure func=FILE_CHECK mask=MAY_READ uid=0 - measure func=MODULE_CHECK uid=0 + measure func=MODULE_CHECK + measure func=FIRMWARE_CHECK appraise fowner=0 The default policy measures all executables in bprm_check, diff --git a/include/linux/ima.h b/include/linux/ima.h index 1b7f268cddce..7cf5e9b32550 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -19,6 +19,7 @@ extern int ima_file_check(struct file *file, int mask); extern void ima_file_free(struct file *file); extern int ima_file_mmap(struct file *file, unsigned long prot); extern int ima_module_check(struct file *file); +extern int ima_fw_from_file(struct file *file, char *buf, size_t size); #else static inline int ima_bprm_check(struct linux_binprm *bprm) @@ -46,6 +47,11 @@ static inline int ima_module_check(struct file *file) return 0; } +static inline int ima_fw_from_file(struct file *file, char *buf, size_t size) +{ + return 0; +} + #endif /* CONFIG_IMA */ #ifdef CONFIG_IMA_APPRAISE diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index c42056edfc97..57da4bd7ba0c 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -158,7 +158,7 @@ struct integrity_iint_cache *integrity_iint_insert(struct inode *inode); struct integrity_iint_cache *integrity_iint_find(struct inode *inode); /* IMA policy related functions */ -enum ima_hooks { FILE_CHECK = 1, MMAP_CHECK, BPRM_CHECK, MODULE_CHECK, POST_SETATTR }; +enum ima_hooks { FILE_CHECK = 1, MMAP_CHECK, BPRM_CHECK, MODULE_CHECK, FIRMWARE_CHECK, POST_SETATTR }; int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, int flags); @@ -171,6 +171,7 @@ void ima_delete_rules(void); #define IMA_APPRAISE_ENFORCE 0x01 #define IMA_APPRAISE_FIX 0x02 #define IMA_APPRAISE_MODULES 0x04 +#define IMA_APPRAISE_FIRMWARE 0x08 #ifdef CONFIG_IMA_APPRAISE int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 59ac90275070..86bfd5c5df85 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -75,6 +75,8 @@ enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint, return iint->ima_bprm_status; case MODULE_CHECK: return iint->ima_module_status; + case FIRMWARE_CHECK: + return iint->ima_firmware_status; case FILE_CHECK: default: return iint->ima_file_status; @@ -94,6 +96,9 @@ static void ima_set_cache_status(struct integrity_iint_cache *iint, case MODULE_CHECK: iint->ima_module_status = status; break; + case FIRMWARE_CHECK: + iint->ima_firmware_status = status; + break; case FILE_CHECK: default: iint->ima_file_status = status; @@ -113,6 +118,9 @@ static void ima_cache_flags(struct integrity_iint_cache *iint, int func) case MODULE_CHECK: iint->flags |= (IMA_MODULE_APPRAISED | IMA_APPRAISED); break; + case FIRMWARE_CHECK: + iint->flags |= (IMA_FIRMWARE_APPRAISED | IMA_APPRAISED); + break; case FILE_CHECK: default: iint->flags |= (IMA_FILE_APPRAISED | IMA_APPRAISED); diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 0d696431209c..2917f980bf30 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -319,6 +319,17 @@ int ima_module_check(struct file *file) return process_measurement(file, NULL, MAY_EXEC, MODULE_CHECK); } +int ima_fw_from_file(struct file *file, char *buf, size_t size) +{ + if (!file) { + if ((ima_appraise & IMA_APPRAISE_FIRMWARE) && + (ima_appraise & IMA_APPRAISE_ENFORCE)) + return -EACCES; /* INTEGRITY_UNKNOWN */ + return 0; + } + return process_measurement(file, NULL, MAY_EXEC, FIRMWARE_CHECK); +} + static int __init init_ima(void) { int error; diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index cea84d8bd7be..07099a8bc283 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -84,6 +84,7 @@ static struct ima_rule_entry default_rules[] = { {.action = MEASURE, .func = FILE_CHECK, .mask = MAY_READ, .uid = GLOBAL_ROOT_UID, .flags = IMA_FUNC | IMA_MASK | IMA_UID}, {.action = MEASURE, .func = MODULE_CHECK, .flags = IMA_FUNC}, + {.action = MEASURE, .func = FIRMWARE_CHECK, .flags = IMA_FUNC}, }; static struct ima_rule_entry default_appraise_rules[] = { @@ -241,6 +242,8 @@ static int get_subaction(struct ima_rule_entry *rule, int func) return IMA_BPRM_APPRAISE; case MODULE_CHECK: return IMA_MODULE_APPRAISE; + case FIRMWARE_CHECK: + return IMA_FIRMWARE_APPRAISE; case FILE_CHECK: default: return IMA_FILE_APPRAISE; @@ -486,6 +489,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) entry->func = FILE_CHECK; else if (strcmp(args[0].from, "MODULE_CHECK") == 0) entry->func = MODULE_CHECK; + else if (strcmp(args[0].from, "FIRMWARE_CHECK") == 0) + entry->func = FIRMWARE_CHECK; else if ((strcmp(args[0].from, "FILE_MMAP") == 0) || (strcmp(args[0].from, "MMAP_CHECK") == 0)) entry->func = MMAP_CHECK; @@ -636,6 +641,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) result = -EINVAL; else if (entry->func == MODULE_CHECK) ima_appraise |= IMA_APPRAISE_MODULES; + else if (entry->func == FIRMWARE_CHECK) + ima_appraise |= IMA_APPRAISE_FIRMWARE; audit_log_format(ab, "res=%d", !result); audit_log_end(ab); return result; diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 09c440d9aaee..19b8e314ca96 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -46,10 +46,14 @@ #define IMA_BPRM_APPRAISED 0x00002000 #define IMA_MODULE_APPRAISE 0x00004000 #define IMA_MODULE_APPRAISED 0x00008000 +#define IMA_FIRMWARE_APPRAISE 0x00010000 +#define IMA_FIRMWARE_APPRAISED 0x00020000 #define IMA_APPRAISE_SUBMASK (IMA_FILE_APPRAISE | IMA_MMAP_APPRAISE | \ - IMA_BPRM_APPRAISE | IMA_MODULE_APPRAISE) + IMA_BPRM_APPRAISE | IMA_MODULE_APPRAISE | \ + IMA_FIRMWARE_APPRAISE) #define IMA_APPRAISED_SUBMASK (IMA_FILE_APPRAISED | IMA_MMAP_APPRAISED | \ - IMA_BPRM_APPRAISED | IMA_MODULE_APPRAISED) + IMA_BPRM_APPRAISED | IMA_MODULE_APPRAISED | \ + IMA_FIRMWARE_APPRAISED) enum evm_ima_xattr_type { IMA_XATTR_DIGEST = 0x01, @@ -104,6 +108,7 @@ struct integrity_iint_cache { enum integrity_status ima_mmap_status:4; enum integrity_status ima_bprm_status:4; enum integrity_status ima_module_status:4; + enum integrity_status ima_firmware_status:4; enum integrity_status evm_status:4; struct ima_digest_data *ima_hash; }; diff --git a/security/security.c b/security/security.c index 35d37d0f0d49..e41b1a8d7644 100644 --- a/security/security.c +++ b/security/security.c @@ -847,7 +847,12 @@ int security_kernel_create_files_as(struct cred *new, struct inode *inode) int security_kernel_fw_from_file(struct file *file, char *buf, size_t size) { - return security_ops->kernel_fw_from_file(file, buf, size); + int ret; + + ret = security_ops->kernel_fw_from_file(file, buf, size); + if (ret) + return ret; + return ima_fw_from_file(file, buf, size); } EXPORT_SYMBOL_GPL(security_kernel_fw_from_file); -- cgit v1.2.3 From 16369efb1f6006ec79babe53f388eed431533596 Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Wed, 25 Jun 2014 14:52:59 +0400 Subject: dmaengine: of: add common xlate function for matching by channel id This patch adds a new common OF dma xlate callback function which will match a channel by it's id. The binding expects one integer argument which it will use to lookup the channel by the id. Unlike of_dma_simple_xlate this function is able to handle a system with multiple DMA controllers. When registering the of dma provider with of_dma_controller_register a pointer to the dma_device struct which is associated with the dt node needs to passed as the data parameter. New function will use this pointer to match only channels which belong to the specified DMA controller. Signed-off-by: Alexander Popov Signed-off-by: Vinod Koul --- drivers/dma/of-dma.c | 35 +++++++++++++++++++++++++++++++++++ include/linux/of_dma.h | 4 ++++ 2 files changed, 39 insertions(+) (limited to 'include') diff --git a/drivers/dma/of-dma.c b/drivers/dma/of-dma.c index e8fe9dc455f4..d5fbeaa1e7ba 100644 --- a/drivers/dma/of-dma.c +++ b/drivers/dma/of-dma.c @@ -218,3 +218,38 @@ struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec, &dma_spec->args[0]); } EXPORT_SYMBOL_GPL(of_dma_simple_xlate); + +/** + * of_dma_xlate_by_chan_id - Translate dt property to DMA channel by channel id + * @dma_spec: pointer to DMA specifier as found in the device tree + * @of_dma: pointer to DMA controller data + * + * This function can be used as the of xlate callback for DMA driver which wants + * to match the channel based on the channel id. When using this xlate function + * the #dma-cells propety of the DMA controller dt node needs to be set to 1. + * The data parameter of of_dma_controller_register must be a pointer to the + * dma_device struct the function should match upon. + * + * Returns pointer to appropriate dma channel on success or NULL on error. + */ +struct dma_chan *of_dma_xlate_by_chan_id(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct dma_device *dev = ofdma->of_dma_data; + struct dma_chan *chan, *candidate = NULL; + + if (!dev || dma_spec->args_count != 1) + return NULL; + + list_for_each_entry(chan, &dev->channels, device_node) + if (chan->chan_id == dma_spec->args[0]) { + candidate = chan; + break; + } + + if (!candidate) + return NULL; + + return dma_get_slave_channel(candidate); +} +EXPORT_SYMBOL_GPL(of_dma_xlate_by_chan_id); diff --git a/include/linux/of_dma.h b/include/linux/of_dma.h index ae36298ba076..56bc026c143f 100644 --- a/include/linux/of_dma.h +++ b/include/linux/of_dma.h @@ -41,6 +41,8 @@ extern struct dma_chan *of_dma_request_slave_channel(struct device_node *np, const char *name); extern struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec, struct of_dma *ofdma); +extern struct dma_chan *of_dma_xlate_by_chan_id(struct of_phandle_args *dma_spec, + struct of_dma *ofdma); #else static inline int of_dma_controller_register(struct device_node *np, struct dma_chan *(*of_dma_xlate) @@ -66,6 +68,8 @@ static inline struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_s return NULL; } +#define of_dma_xlate_by_chan_id NULL + #endif #endif /* __LINUX_OF_DMA_H */ -- cgit v1.2.3 From cd9070c9c512ff7995f9019392e0ae548df3a088 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 23 Jan 2014 12:07:41 +0100 Subject: scsi: fix the {host,target,device}_blocked counter mess Seems like these counters are missing any sort of synchronization for updates, as a over 10 year old comment from me noted. Fix this by using atomic counters, and while we're at it also make sure they are in the same cacheline as the _busy counters and not needlessly stored to in every I/O completion. With the new model the _busy counters can temporarily go negative, so all the readers are updated to check for > 0 values. Longer term every successful I/O completion will reset the counters to zero, so the temporarily negative values will not cause any harm. Signed-off-by: Christoph Hellwig Reviewed-by: Martin K. Petersen Reviewed-by: Webb Scales Acked-by: Jens Axboe Tested-by: Bart Van Assche Tested-by: Robert Elliott --- drivers/scsi/scsi.c | 21 +++++++-------- drivers/scsi/scsi_lib.c | 66 +++++++++++++++++++++++----------------------- drivers/scsi/scsi_sysfs.c | 10 ++++++- include/scsi/scsi_device.h | 7 ++--- include/scsi/scsi_host.h | 7 ++--- 5 files changed, 58 insertions(+), 53 deletions(-) (limited to 'include') diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 21fb97b01dd6..3dde8a35493f 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -726,17 +726,16 @@ void scsi_finish_command(struct scsi_cmnd *cmd) scsi_device_unbusy(sdev); - /* - * Clear the flags which say that the device/host is no longer - * capable of accepting new commands. These are set in scsi_queue.c - * for both the queue full condition on a device, and for a - * host full condition on the host. - * - * XXX(hch): What about locking? - */ - shost->host_blocked = 0; - starget->target_blocked = 0; - sdev->device_blocked = 0; + /* + * Clear the flags that say that the device/target/host is no longer + * capable of accepting new commands. + */ + if (atomic_read(&shost->host_blocked)) + atomic_set(&shost->host_blocked, 0); + if (atomic_read(&starget->target_blocked)) + atomic_set(&starget->target_blocked, 0); + if (atomic_read(&sdev->device_blocked)) + atomic_set(&sdev->device_blocked, 0); /* * If we have valid sense information, then some kind of recovery diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 1ddf0fb43b59..69da4cb5cb13 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -99,14 +99,16 @@ scsi_set_blocked(struct scsi_cmnd *cmd, int reason) */ switch (reason) { case SCSI_MLQUEUE_HOST_BUSY: - host->host_blocked = host->max_host_blocked; + atomic_set(&host->host_blocked, host->max_host_blocked); break; case SCSI_MLQUEUE_DEVICE_BUSY: case SCSI_MLQUEUE_EH_RETRY: - device->device_blocked = device->max_device_blocked; + atomic_set(&device->device_blocked, + device->max_device_blocked); break; case SCSI_MLQUEUE_TARGET_BUSY: - starget->target_blocked = starget->max_target_blocked; + atomic_set(&starget->target_blocked, + starget->max_target_blocked); break; } } @@ -351,29 +353,35 @@ static void scsi_single_lun_run(struct scsi_device *current_sdev) spin_unlock_irqrestore(shost->host_lock, flags); } -static inline int scsi_device_is_busy(struct scsi_device *sdev) +static inline bool scsi_device_is_busy(struct scsi_device *sdev) { - if (atomic_read(&sdev->device_busy) >= sdev->queue_depth || - sdev->device_blocked) - return 1; - return 0; + if (atomic_read(&sdev->device_busy) >= sdev->queue_depth) + return true; + if (atomic_read(&sdev->device_blocked) > 0) + return true; + return false; } -static inline int scsi_target_is_busy(struct scsi_target *starget) +static inline bool scsi_target_is_busy(struct scsi_target *starget) { - return ((starget->can_queue > 0 && - atomic_read(&starget->target_busy) >= starget->can_queue) || - starget->target_blocked); + if (starget->can_queue > 0 && + atomic_read(&starget->target_busy) >= starget->can_queue) + return true; + if (atomic_read(&starget->target_blocked) > 0) + return true; + return false; } -static inline int scsi_host_is_busy(struct Scsi_Host *shost) +static inline bool scsi_host_is_busy(struct Scsi_Host *shost) { - if ((shost->can_queue > 0 && - atomic_read(&shost->host_busy) >= shost->can_queue) || - shost->host_blocked || shost->host_self_blocked) - return 1; - - return 0; + if (shost->can_queue > 0 && + atomic_read(&shost->host_busy) >= shost->can_queue) + return true; + if (atomic_read(&shost->host_blocked) > 0) + return true; + if (shost->host_self_blocked) + return true; + return false; } static void scsi_starved_list_run(struct Scsi_Host *shost) @@ -1256,14 +1264,14 @@ static inline int scsi_dev_queue_ready(struct request_queue *q, unsigned int busy; busy = atomic_inc_return(&sdev->device_busy) - 1; - if (sdev->device_blocked) { + if (atomic_read(&sdev->device_blocked)) { if (busy) goto out_dec; /* * unblock after device_blocked iterates to zero */ - if (--sdev->device_blocked != 0) { + if (atomic_dec_return(&sdev->device_blocked) > 0) { blk_delay_queue(q, SCSI_QUEUE_DELAY); goto out_dec; } @@ -1302,19 +1310,15 @@ static inline int scsi_target_queue_ready(struct Scsi_Host *shost, } busy = atomic_inc_return(&starget->target_busy) - 1; - if (starget->target_blocked) { + if (atomic_read(&starget->target_blocked) > 0) { if (busy) goto starved; /* * unblock after target_blocked iterates to zero */ - spin_lock_irq(shost->host_lock); - if (--starget->target_blocked != 0) { - spin_unlock_irq(shost->host_lock); + if (atomic_dec_return(&starget->target_blocked) > 0) goto out_dec; - } - spin_unlock_irq(shost->host_lock); SCSI_LOG_MLQUEUE(3, starget_printk(KERN_INFO, starget, "unblocking target at zero depth\n")); @@ -1349,19 +1353,15 @@ static inline int scsi_host_queue_ready(struct request_queue *q, return 0; busy = atomic_inc_return(&shost->host_busy) - 1; - if (shost->host_blocked) { + if (atomic_read(&shost->host_blocked) > 0) { if (busy) goto starved; /* * unblock after host_blocked iterates to zero */ - spin_lock_irq(shost->host_lock); - if (--shost->host_blocked != 0) { - spin_unlock_irq(shost->host_lock); + if (atomic_dec_return(&shost->host_blocked) > 0) goto out_dec; - } - spin_unlock_irq(shost->host_lock); SCSI_LOG_MLQUEUE(3, shost_printk(KERN_INFO, shost, diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 79df9847edef..209cae3097ea 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -584,7 +584,6 @@ static int scsi_sdev_check_buf_bit(const char *buf) /* * Create the actual show/store functions and data structures. */ -sdev_rd_attr (device_blocked, "%d\n"); sdev_rd_attr (type, "%d\n"); sdev_rd_attr (scsi_level, "%d\n"); sdev_rd_attr (vendor, "%.8s\n"); @@ -600,6 +599,15 @@ sdev_show_device_busy(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR(device_busy, S_IRUGO, sdev_show_device_busy, NULL); +static ssize_t +sdev_show_device_blocked(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct scsi_device *sdev = to_scsi_device(dev); + return snprintf(buf, 20, "%d\n", atomic_read(&sdev->device_blocked)); +} +static DEVICE_ATTR(device_blocked, S_IRUGO, sdev_show_device_blocked, NULL); + /* * TODO: can we make these symlinks to the block layer ones? */ diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 3329901c7243..0f853f2c9dc7 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -82,6 +82,8 @@ struct scsi_device { struct list_head same_target_siblings; /* just the devices sharing same target id */ atomic_t device_busy; /* commands actually active on LLDD */ + atomic_t device_blocked; /* Device returned QUEUE_FULL. */ + spinlock_t list_lock; struct list_head cmd_list; /* queue of in use SCSI Command structures */ struct list_head starved_entry; @@ -180,8 +182,6 @@ struct scsi_device { struct list_head event_list; /* asserted events */ struct work_struct event_work; - unsigned int device_blocked; /* Device returned QUEUE_FULL. */ - unsigned int max_device_blocked; /* what device_blocked counts down from */ #define SCSI_DEFAULT_DEVICE_BLOCKED 3 @@ -291,12 +291,13 @@ struct scsi_target { * the same target will also. */ /* commands actually active on LLD. */ atomic_t target_busy; + atomic_t target_blocked; + /* * LLDs should set this in the slave_alloc host template callout. * If set to zero then there is not limit. */ unsigned int can_queue; - unsigned int target_blocked; unsigned int max_target_blocked; #define SCSI_DEFAULT_TARGET_BLOCKED 3 diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 51f7911b1cbb..5e8ebc1ac12b 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -583,6 +583,8 @@ struct Scsi_Host { struct blk_queue_tag *bqt; atomic_t host_busy; /* commands actually active on low-level */ + atomic_t host_blocked; + unsigned int host_failed; /* commands that failed. protected by host_lock */ unsigned int host_eh_scheduled; /* EH scheduled without command */ @@ -681,11 +683,6 @@ struct Scsi_Host { */ struct workqueue_struct *tmf_work_q; - /* - * Host has rejected a command because it was busy. - */ - unsigned int host_blocked; - /* * Value host_blocked counts down from */ -- cgit v1.2.3 From c53c6d6a68b13b1dff2892551b56cfdc07887d9e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 15 Apr 2014 14:38:31 +0200 Subject: scatterlist: allow chaining to preallocated chunks Blk-mq drivers usually preallocate their S/G list as part of the request, but if we want to support the very large S/G lists currently supported by the SCSI code that would tie up a lot of memory in the preallocated request pool. Add support to the scatterlist code so that it can initialize a S/G list that uses a preallocated first chunks and dynamically allocated additional chunks. That way the scsi-mq code can preallocate a first page worth of S/G entries as part of the request, and dynamically extend the S/G list when needed. Signed-off-by: Christoph Hellwig Reviewed-by: Martin K. Petersen Reviewed-by: Hannes Reinecke Reviewed-by: Webb Scales Acked-by: Jens Axboe Tested-by: Bart Van Assche Tested-by: Robert Elliott --- drivers/scsi/scsi_lib.c | 16 +++++++--------- include/linux/scatterlist.h | 6 +++--- lib/scatterlist.c | 25 +++++++++++++++++-------- 3 files changed, 27 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 8723abeb018e..bbd7a0a08692 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -564,6 +564,11 @@ static struct scatterlist *scsi_sg_alloc(unsigned int nents, gfp_t gfp_mask) return mempool_alloc(sgp->pool, gfp_mask); } +static void scsi_free_sgtable(struct scsi_data_buffer *sdb) +{ + __sg_free_table(&sdb->table, SCSI_MAX_SG_SEGMENTS, false, scsi_sg_free); +} + static int scsi_alloc_sgtable(struct scsi_data_buffer *sdb, int nents, gfp_t gfp_mask) { @@ -572,19 +577,12 @@ static int scsi_alloc_sgtable(struct scsi_data_buffer *sdb, int nents, BUG_ON(!nents); ret = __sg_alloc_table(&sdb->table, nents, SCSI_MAX_SG_SEGMENTS, - gfp_mask, scsi_sg_alloc); + NULL, gfp_mask, scsi_sg_alloc); if (unlikely(ret)) - __sg_free_table(&sdb->table, SCSI_MAX_SG_SEGMENTS, - scsi_sg_free); - + scsi_free_sgtable(sdb); return ret; } -static void scsi_free_sgtable(struct scsi_data_buffer *sdb) -{ - __sg_free_table(&sdb->table, SCSI_MAX_SG_SEGMENTS, scsi_sg_free); -} - /* * Function: scsi_release_buffers() * diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h index a964f7285600..f4ec8bbcb372 100644 --- a/include/linux/scatterlist.h +++ b/include/linux/scatterlist.h @@ -229,10 +229,10 @@ void sg_init_one(struct scatterlist *, const void *, unsigned int); typedef struct scatterlist *(sg_alloc_fn)(unsigned int, gfp_t); typedef void (sg_free_fn)(struct scatterlist *, unsigned int); -void __sg_free_table(struct sg_table *, unsigned int, sg_free_fn *); +void __sg_free_table(struct sg_table *, unsigned int, bool, sg_free_fn *); void sg_free_table(struct sg_table *); -int __sg_alloc_table(struct sg_table *, unsigned int, unsigned int, gfp_t, - sg_alloc_fn *); +int __sg_alloc_table(struct sg_table *, unsigned int, unsigned int, + struct scatterlist *, gfp_t, sg_alloc_fn *); int sg_alloc_table(struct sg_table *, unsigned int, gfp_t); int sg_alloc_table_from_pages(struct sg_table *sgt, struct page **pages, unsigned int n_pages, diff --git a/lib/scatterlist.c b/lib/scatterlist.c index 3a8e8e8fb2a5..b4415fceb7e7 100644 --- a/lib/scatterlist.c +++ b/lib/scatterlist.c @@ -165,6 +165,7 @@ static void sg_kfree(struct scatterlist *sg, unsigned int nents) * __sg_free_table - Free a previously mapped sg table * @table: The sg table header to use * @max_ents: The maximum number of entries per single scatterlist + * @skip_first_chunk: don't free the (preallocated) first scatterlist chunk * @free_fn: Free function * * Description: @@ -174,7 +175,7 @@ static void sg_kfree(struct scatterlist *sg, unsigned int nents) * **/ void __sg_free_table(struct sg_table *table, unsigned int max_ents, - sg_free_fn *free_fn) + bool skip_first_chunk, sg_free_fn *free_fn) { struct scatterlist *sgl, *next; @@ -202,7 +203,10 @@ void __sg_free_table(struct sg_table *table, unsigned int max_ents, } table->orig_nents -= sg_size; - free_fn(sgl, alloc_size); + if (!skip_first_chunk) { + free_fn(sgl, alloc_size); + skip_first_chunk = false; + } sgl = next; } @@ -217,7 +221,7 @@ EXPORT_SYMBOL(__sg_free_table); **/ void sg_free_table(struct sg_table *table) { - __sg_free_table(table, SG_MAX_SINGLE_ALLOC, sg_kfree); + __sg_free_table(table, SG_MAX_SINGLE_ALLOC, false, sg_kfree); } EXPORT_SYMBOL(sg_free_table); @@ -241,8 +245,8 @@ EXPORT_SYMBOL(sg_free_table); * **/ int __sg_alloc_table(struct sg_table *table, unsigned int nents, - unsigned int max_ents, gfp_t gfp_mask, - sg_alloc_fn *alloc_fn) + unsigned int max_ents, struct scatterlist *first_chunk, + gfp_t gfp_mask, sg_alloc_fn *alloc_fn) { struct scatterlist *sg, *prv; unsigned int left; @@ -269,7 +273,12 @@ int __sg_alloc_table(struct sg_table *table, unsigned int nents, left -= sg_size; - sg = alloc_fn(alloc_size, gfp_mask); + if (first_chunk) { + sg = first_chunk; + first_chunk = NULL; + } else { + sg = alloc_fn(alloc_size, gfp_mask); + } if (unlikely(!sg)) { /* * Adjust entry count to reflect that the last @@ -324,9 +333,9 @@ int sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask) int ret; ret = __sg_alloc_table(table, nents, SG_MAX_SINGLE_ALLOC, - gfp_mask, sg_kmalloc); + NULL, gfp_mask, sg_kmalloc); if (unlikely(ret)) - __sg_free_table(table, SG_MAX_SINGLE_ALLOC, sg_kfree); + __sg_free_table(table, SG_MAX_SINGLE_ALLOC, false, sg_kfree); return ret; } -- cgit v1.2.3 From d285203cf647d7c97db3a1c33794315c9008593f Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 17 Jan 2014 12:06:53 +0100 Subject: scsi: add support for a blk-mq based I/O path. This patch adds support for an alternate I/O path in the scsi midlayer which uses the blk-mq infrastructure instead of the legacy request code. Use of blk-mq is fully transparent to drivers, although for now a host template field is provided to opt out of blk-mq usage in case any unforseen incompatibilities arise. In general replacing the legacy request code with blk-mq is a simple and mostly mechanical transformation. The biggest exception is the new code that deals with the fact the I/O submissions in blk-mq must happen from process context, which slightly complicates the I/O completion handler. The second biggest differences is that blk-mq is build around the concept of preallocated requests that also include driver specific data, which in SCSI context means the scsi_cmnd structure. This completely avoids dynamic memory allocations for the fast path through I/O submission. Due the preallocated requests the MQ code path exclusively uses the host-wide shared tag allocator instead of a per-LUN one. This only affects drivers actually using the block layer provided tag allocator instead of their own. Unlike the old path blk-mq always provides a tag, although drivers don't have to use it. For now the blk-mq path is disable by defauly and must be enabled using the "use_blk_mq" module parameter. Once the remaining work in the block layer to make blk-mq more suitable for slow devices is complete I hope to make it the default and eventually even remove the old code path. Based on the earlier scsi-mq prototype by Nicholas Bellinger. Thanks to Bart Van Assche and Robert Elliot for testing, benchmarking and various sugestions and code contributions. Signed-off-by: Christoph Hellwig Reviewed-by: Martin K. Petersen Reviewed-by: Hannes Reinecke Reviewed-by: Webb Scales Acked-by: Jens Axboe Tested-by: Bart Van Assche Tested-by: Robert Elliott --- drivers/scsi/hosts.c | 35 +++- drivers/scsi/scsi.c | 5 +- drivers/scsi/scsi_lib.c | 464 ++++++++++++++++++++++++++++++++++++++++------ drivers/scsi/scsi_priv.h | 3 + drivers/scsi/scsi_scan.c | 5 +- drivers/scsi/scsi_sysfs.c | 2 + include/scsi/scsi_host.h | 18 +- include/scsi/scsi_tcq.h | 28 ++- 8 files changed, 488 insertions(+), 72 deletions(-) (limited to 'include') diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 0632eee82620..6de80e352871 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -213,9 +213,24 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev, goto fail; } + if (shost_use_blk_mq(shost)) { + error = scsi_mq_setup_tags(shost); + if (error) + goto fail; + } + + /* + * Note that we allocate the freelist even for the MQ case for now, + * as we need a command set aside for scsi_reset_provider. Having + * the full host freelist and one command available for that is a + * little heavy-handed, but avoids introducing a special allocator + * just for this. Eventually the structure of scsi_reset_provider + * will need a major overhaul. + */ error = scsi_setup_command_freelist(shost); if (error) - goto fail; + goto out_destroy_tags; + if (!shost->shost_gendev.parent) shost->shost_gendev.parent = dev ? dev : &platform_bus; @@ -226,7 +241,7 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev, error = device_add(&shost->shost_gendev); if (error) - goto out; + goto out_destroy_freelist; pm_runtime_set_active(&shost->shost_gendev); pm_runtime_enable(&shost->shost_gendev); @@ -279,8 +294,11 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev, device_del(&shost->shost_dev); out_del_gendev: device_del(&shost->shost_gendev); - out: + out_destroy_freelist: scsi_destroy_command_freelist(shost); + out_destroy_tags: + if (shost_use_blk_mq(shost)) + scsi_mq_destroy_tags(shost); fail: return error; } @@ -309,8 +327,13 @@ static void scsi_host_dev_release(struct device *dev) } scsi_destroy_command_freelist(shost); - if (shost->bqt) - blk_free_tags(shost->bqt); + if (shost_use_blk_mq(shost)) { + if (shost->tag_set.tags) + scsi_mq_destroy_tags(shost); + } else { + if (shost->bqt) + blk_free_tags(shost->bqt); + } kfree(shost->shost_data); @@ -436,6 +459,8 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) else shost->dma_boundary = 0xffffffff; + shost->use_blk_mq = scsi_use_blk_mq && !shost->hostt->disable_blk_mq; + device_initialize(&shost->shost_gendev); dev_set_name(&shost->shost_gendev, "host%d", shost->host_no); shost->shost_gendev.bus = &scsi_bus_type; diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 3dde8a35493f..013709f11bf1 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -805,7 +805,7 @@ void scsi_adjust_queue_depth(struct scsi_device *sdev, int tagged, int tags) * is more IO than the LLD's can_queue (so there are not enuogh * tags) request_fn's host queue ready check will handle it. */ - if (!sdev->host->bqt) { + if (!shost_use_blk_mq(sdev->host) && !sdev->host->bqt) { if (blk_queue_tagged(sdev->request_queue) && blk_queue_resize_tags(sdev->request_queue, tags) != 0) goto out; @@ -1361,6 +1361,9 @@ MODULE_LICENSE("GPL"); module_param(scsi_logging_level, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(scsi_logging_level, "a bit mask of logging levels"); +bool scsi_use_blk_mq = false; +module_param_named(use_blk_mq, scsi_use_blk_mq, bool, S_IWUSR | S_IRUGO); + static int __init init_scsi(void) { int error; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index bbd7a0a08692..9c44392b748f 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1,5 +1,6 @@ /* - * scsi_lib.c Copyright (C) 1999 Eric Youngdale + * Copyright (C) 1999 Eric Youngdale + * Copyright (C) 2014 Christoph Hellwig * * SCSI queueing library. * Initial versions: Eric Youngdale (eric@andante.org). @@ -20,6 +21,7 @@ #include #include #include +#include #include #include @@ -113,6 +115,16 @@ scsi_set_blocked(struct scsi_cmnd *cmd, int reason) } } +static void scsi_mq_requeue_cmd(struct scsi_cmnd *cmd) +{ + struct scsi_device *sdev = cmd->device; + struct request_queue *q = cmd->request->q; + + blk_mq_requeue_request(cmd->request); + blk_mq_kick_requeue_list(q); + put_device(&sdev->sdev_gendev); +} + /** * __scsi_queue_insert - private queue insertion * @cmd: The SCSI command being requeued @@ -150,6 +162,10 @@ static void __scsi_queue_insert(struct scsi_cmnd *cmd, int reason, int unbusy) * before blk_cleanup_queue() finishes. */ cmd->result = 0; + if (q->mq_ops) { + scsi_mq_requeue_cmd(cmd); + return; + } spin_lock_irqsave(q->queue_lock, flags); blk_requeue_request(q, cmd->request); kblockd_schedule_work(&device->requeue_work); @@ -308,6 +324,14 @@ void scsi_device_unbusy(struct scsi_device *sdev) atomic_dec(&sdev->device_busy); } +static void scsi_kick_queue(struct request_queue *q) +{ + if (q->mq_ops) + blk_mq_start_hw_queues(q); + else + blk_run_queue(q); +} + /* * Called for single_lun devices on IO completion. Clear starget_sdev_user, * and call blk_run_queue for all the scsi_devices on the target - @@ -332,7 +356,7 @@ static void scsi_single_lun_run(struct scsi_device *current_sdev) * but in most cases, we will be first. Ideally, each LU on the * target would get some limited time or requests on the target. */ - blk_run_queue(current_sdev->request_queue); + scsi_kick_queue(current_sdev->request_queue); spin_lock_irqsave(shost->host_lock, flags); if (starget->starget_sdev_user) @@ -345,7 +369,7 @@ static void scsi_single_lun_run(struct scsi_device *current_sdev) continue; spin_unlock_irqrestore(shost->host_lock, flags); - blk_run_queue(sdev->request_queue); + scsi_kick_queue(sdev->request_queue); spin_lock_irqsave(shost->host_lock, flags); scsi_device_put(sdev); @@ -435,7 +459,7 @@ static void scsi_starved_list_run(struct Scsi_Host *shost) continue; spin_unlock_irqrestore(shost->host_lock, flags); - blk_run_queue(slq); + scsi_kick_queue(slq); blk_put_queue(slq); spin_lock_irqsave(shost->host_lock, flags); @@ -466,7 +490,10 @@ static void scsi_run_queue(struct request_queue *q) if (!list_empty(&sdev->host->starved_list)) scsi_starved_list_run(sdev->host); - blk_run_queue(q); + if (q->mq_ops) + blk_mq_start_stopped_hw_queues(q, false); + else + blk_run_queue(q); } void scsi_requeue_run_queue(struct work_struct *work) @@ -564,25 +591,72 @@ static struct scatterlist *scsi_sg_alloc(unsigned int nents, gfp_t gfp_mask) return mempool_alloc(sgp->pool, gfp_mask); } -static void scsi_free_sgtable(struct scsi_data_buffer *sdb) +static void scsi_free_sgtable(struct scsi_data_buffer *sdb, bool mq) { - __sg_free_table(&sdb->table, SCSI_MAX_SG_SEGMENTS, false, scsi_sg_free); + if (mq && sdb->table.nents <= SCSI_MAX_SG_SEGMENTS) + return; + __sg_free_table(&sdb->table, SCSI_MAX_SG_SEGMENTS, mq, scsi_sg_free); } static int scsi_alloc_sgtable(struct scsi_data_buffer *sdb, int nents, - gfp_t gfp_mask) + gfp_t gfp_mask, bool mq) { + struct scatterlist *first_chunk = NULL; int ret; BUG_ON(!nents); + if (mq) { + if (nents <= SCSI_MAX_SG_SEGMENTS) { + sdb->table.nents = nents; + sg_init_table(sdb->table.sgl, sdb->table.nents); + return 0; + } + first_chunk = sdb->table.sgl; + } + ret = __sg_alloc_table(&sdb->table, nents, SCSI_MAX_SG_SEGMENTS, - NULL, gfp_mask, scsi_sg_alloc); + first_chunk, gfp_mask, scsi_sg_alloc); if (unlikely(ret)) - scsi_free_sgtable(sdb); + scsi_free_sgtable(sdb, mq); return ret; } +static void scsi_uninit_cmd(struct scsi_cmnd *cmd) +{ + if (cmd->request->cmd_type == REQ_TYPE_FS) { + struct scsi_driver *drv = scsi_cmd_to_driver(cmd); + + if (drv->uninit_command) + drv->uninit_command(cmd); + } +} + +static void scsi_mq_free_sgtables(struct scsi_cmnd *cmd) +{ + if (cmd->sdb.table.nents) + scsi_free_sgtable(&cmd->sdb, true); + if (cmd->request->next_rq && cmd->request->next_rq->special) + scsi_free_sgtable(cmd->request->next_rq->special, true); + if (scsi_prot_sg_count(cmd)) + scsi_free_sgtable(cmd->prot_sdb, true); +} + +static void scsi_mq_uninit_cmd(struct scsi_cmnd *cmd) +{ + struct scsi_device *sdev = cmd->device; + unsigned long flags; + + BUG_ON(list_empty(&cmd->list)); + + scsi_mq_free_sgtables(cmd); + scsi_uninit_cmd(cmd); + + spin_lock_irqsave(&sdev->list_lock, flags); + list_del_init(&cmd->list); + spin_unlock_irqrestore(&sdev->list_lock, flags); +} + /* * Function: scsi_release_buffers() * @@ -602,19 +676,19 @@ static int scsi_alloc_sgtable(struct scsi_data_buffer *sdb, int nents, static void scsi_release_buffers(struct scsi_cmnd *cmd) { if (cmd->sdb.table.nents) - scsi_free_sgtable(&cmd->sdb); + scsi_free_sgtable(&cmd->sdb, false); memset(&cmd->sdb, 0, sizeof(cmd->sdb)); if (scsi_prot_sg_count(cmd)) - scsi_free_sgtable(cmd->prot_sdb); + scsi_free_sgtable(cmd->prot_sdb, false); } static void scsi_release_bidi_buffers(struct scsi_cmnd *cmd) { struct scsi_data_buffer *bidi_sdb = cmd->request->next_rq->special; - scsi_free_sgtable(bidi_sdb); + scsi_free_sgtable(bidi_sdb, false); kmem_cache_free(scsi_sdb_cache, bidi_sdb); cmd->request->next_rq->special = NULL; } @@ -625,8 +699,6 @@ static bool scsi_end_request(struct request *req, int error, struct scsi_cmnd *cmd = req->special; struct scsi_device *sdev = cmd->device; struct request_queue *q = sdev->request_queue; - unsigned long flags; - if (blk_update_request(req, error, bytes)) return true; @@ -639,14 +711,38 @@ static bool scsi_end_request(struct request *req, int error, if (blk_queue_add_random(q)) add_disk_randomness(req->rq_disk); - spin_lock_irqsave(q->queue_lock, flags); - blk_finish_request(req, error); - spin_unlock_irqrestore(q->queue_lock, flags); + if (req->mq_ctx) { + /* + * In the MQ case the command gets freed by __blk_mq_end_io, + * so we have to do all cleanup that depends on it earlier. + * + * We also can't kick the queues from irq context, so we + * will have to defer it to a workqueue. + */ + scsi_mq_uninit_cmd(cmd); + + __blk_mq_end_io(req, error); + + if (scsi_target(sdev)->single_lun || + !list_empty(&sdev->host->starved_list)) + kblockd_schedule_work(&sdev->requeue_work); + else + blk_mq_start_stopped_hw_queues(q, true); + + put_device(&sdev->sdev_gendev); + } else { + unsigned long flags; + + spin_lock_irqsave(q->queue_lock, flags); + blk_finish_request(req, error); + spin_unlock_irqrestore(q->queue_lock, flags); + + if (bidi_bytes) + scsi_release_bidi_buffers(cmd); + scsi_release_buffers(cmd); + scsi_next_command(cmd); + } - if (bidi_bytes) - scsi_release_bidi_buffers(cmd); - scsi_release_buffers(cmd); - scsi_next_command(cmd); return false; } @@ -953,8 +1049,14 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) /* Unprep the request and put it back at the head of the queue. * A new command will be prepared and issued. */ - scsi_release_buffers(cmd); - scsi_requeue_command(q, cmd); + if (q->mq_ops) { + cmd->request->cmd_flags &= ~REQ_DONTPREP; + scsi_mq_uninit_cmd(cmd); + scsi_mq_requeue_cmd(cmd); + } else { + scsi_release_buffers(cmd); + scsi_requeue_command(q, cmd); + } break; case ACTION_RETRY: /* Retry the same command immediately */ @@ -976,9 +1078,8 @@ static int scsi_init_sgtable(struct request *req, struct scsi_data_buffer *sdb, * If sg table allocation fails, requeue request later. */ if (unlikely(scsi_alloc_sgtable(sdb, req->nr_phys_segments, - gfp_mask))) { + gfp_mask, req->mq_ctx != NULL))) return BLKPREP_DEFER; - } /* * Next, walk the list, and fill in the addresses and sizes of @@ -1006,6 +1107,7 @@ int scsi_init_io(struct scsi_cmnd *cmd, gfp_t gfp_mask) { struct scsi_device *sdev = cmd->device; struct request *rq = cmd->request; + bool is_mq = (rq->mq_ctx != NULL); int error; BUG_ON(!rq->nr_phys_segments); @@ -1015,15 +1117,19 @@ int scsi_init_io(struct scsi_cmnd *cmd, gfp_t gfp_mask) goto err_exit; if (blk_bidi_rq(rq)) { - struct scsi_data_buffer *bidi_sdb = kmem_cache_zalloc( - scsi_sdb_cache, GFP_ATOMIC); - if (!bidi_sdb) { - error = BLKPREP_DEFER; - goto err_exit; + if (!rq->q->mq_ops) { + struct scsi_data_buffer *bidi_sdb = + kmem_cache_zalloc(scsi_sdb_cache, GFP_ATOMIC); + if (!bidi_sdb) { + error = BLKPREP_DEFER; + goto err_exit; + } + + rq->next_rq->special = bidi_sdb; } - rq->next_rq->special = bidi_sdb; - error = scsi_init_sgtable(rq->next_rq, bidi_sdb, GFP_ATOMIC); + error = scsi_init_sgtable(rq->next_rq, rq->next_rq->special, + GFP_ATOMIC); if (error) goto err_exit; } @@ -1035,7 +1141,7 @@ int scsi_init_io(struct scsi_cmnd *cmd, gfp_t gfp_mask) BUG_ON(prot_sdb == NULL); ivecs = blk_rq_count_integrity_sg(rq->q, rq->bio); - if (scsi_alloc_sgtable(prot_sdb, ivecs, gfp_mask)) { + if (scsi_alloc_sgtable(prot_sdb, ivecs, gfp_mask, is_mq)) { error = BLKPREP_DEFER; goto err_exit; } @@ -1049,13 +1155,16 @@ int scsi_init_io(struct scsi_cmnd *cmd, gfp_t gfp_mask) cmd->prot_sdb->table.nents = count; } - return BLKPREP_OK ; - + return BLKPREP_OK; err_exit: - scsi_release_buffers(cmd); - cmd->request->special = NULL; - scsi_put_command(cmd); - put_device(&sdev->sdev_gendev); + if (is_mq) { + scsi_mq_free_sgtables(cmd); + } else { + scsi_release_buffers(cmd); + cmd->request->special = NULL; + scsi_put_command(cmd); + put_device(&sdev->sdev_gendev); + } return error; } EXPORT_SYMBOL(scsi_init_io); @@ -1266,13 +1375,7 @@ out: static void scsi_unprep_fn(struct request_queue *q, struct request *req) { - if (req->cmd_type == REQ_TYPE_FS) { - struct scsi_cmnd *cmd = req->special; - struct scsi_driver *drv = scsi_cmd_to_driver(cmd); - - if (drv->uninit_command) - drv->uninit_command(cmd); - } + scsi_uninit_cmd(req->special); } /* @@ -1295,7 +1398,11 @@ static inline int scsi_dev_queue_ready(struct request_queue *q, * unblock after device_blocked iterates to zero */ if (atomic_dec_return(&sdev->device_blocked) > 0) { - blk_delay_queue(q, SCSI_QUEUE_DELAY); + /* + * For the MQ case we take care of this in the caller. + */ + if (!q->mq_ops) + blk_delay_queue(q, SCSI_QUEUE_DELAY); goto out_dec; } SCSI_LOG_MLQUEUE(3, sdev_printk(KERN_INFO, sdev, @@ -1671,6 +1778,180 @@ out_delay: blk_delay_queue(q, SCSI_QUEUE_DELAY); } +static inline int prep_to_mq(int ret) +{ + switch (ret) { + case BLKPREP_OK: + return 0; + case BLKPREP_DEFER: + return BLK_MQ_RQ_QUEUE_BUSY; + default: + return BLK_MQ_RQ_QUEUE_ERROR; + } +} + +static int scsi_mq_prep_fn(struct request *req) +{ + struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(req); + struct scsi_device *sdev = req->q->queuedata; + struct Scsi_Host *shost = sdev->host; + unsigned char *sense_buf = cmd->sense_buffer; + struct scatterlist *sg; + + memset(cmd, 0, sizeof(struct scsi_cmnd)); + + req->special = cmd; + + cmd->request = req; + cmd->device = sdev; + cmd->sense_buffer = sense_buf; + + cmd->tag = req->tag; + + req->cmd = req->__cmd; + cmd->cmnd = req->cmd; + cmd->prot_op = SCSI_PROT_NORMAL; + + INIT_LIST_HEAD(&cmd->list); + INIT_DELAYED_WORK(&cmd->abort_work, scmd_eh_abort_handler); + cmd->jiffies_at_alloc = jiffies; + + /* + * XXX: cmd_list lookups are only used by two drivers, try to get + * rid of this list in common code. + */ + spin_lock_irq(&sdev->list_lock); + list_add_tail(&cmd->list, &sdev->cmd_list); + spin_unlock_irq(&sdev->list_lock); + + sg = (void *)cmd + sizeof(struct scsi_cmnd) + shost->hostt->cmd_size; + cmd->sdb.table.sgl = sg; + + if (scsi_host_get_prot(shost)) { + cmd->prot_sdb = (void *)sg + + shost->sg_tablesize * sizeof(struct scatterlist); + memset(cmd->prot_sdb, 0, sizeof(struct scsi_data_buffer)); + + cmd->prot_sdb->table.sgl = + (struct scatterlist *)(cmd->prot_sdb + 1); + } + + if (blk_bidi_rq(req)) { + struct request *next_rq = req->next_rq; + struct scsi_data_buffer *bidi_sdb = blk_mq_rq_to_pdu(next_rq); + + memset(bidi_sdb, 0, sizeof(struct scsi_data_buffer)); + bidi_sdb->table.sgl = + (struct scatterlist *)(bidi_sdb + 1); + + next_rq->special = bidi_sdb; + } + + return scsi_setup_cmnd(sdev, req); +} + +static void scsi_mq_done(struct scsi_cmnd *cmd) +{ + trace_scsi_dispatch_cmd_done(cmd); + blk_mq_complete_request(cmd->request); +} + +static int scsi_queue_rq(struct blk_mq_hw_ctx *hctx, struct request *req) +{ + struct request_queue *q = req->q; + struct scsi_device *sdev = q->queuedata; + struct Scsi_Host *shost = sdev->host; + struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(req); + int ret; + int reason; + + ret = prep_to_mq(scsi_prep_state_check(sdev, req)); + if (ret) + goto out; + + ret = BLK_MQ_RQ_QUEUE_BUSY; + if (!get_device(&sdev->sdev_gendev)) + goto out; + + if (!scsi_dev_queue_ready(q, sdev)) + goto out_put_device; + if (!scsi_target_queue_ready(shost, sdev)) + goto out_dec_device_busy; + if (!scsi_host_queue_ready(q, shost, sdev)) + goto out_dec_target_busy; + + if (!(req->cmd_flags & REQ_DONTPREP)) { + ret = prep_to_mq(scsi_mq_prep_fn(req)); + if (ret) + goto out_dec_host_busy; + req->cmd_flags |= REQ_DONTPREP; + } + + scsi_init_cmd_errh(cmd); + cmd->scsi_done = scsi_mq_done; + + reason = scsi_dispatch_cmd(cmd); + if (reason) { + scsi_set_blocked(cmd, reason); + ret = BLK_MQ_RQ_QUEUE_BUSY; + goto out_dec_host_busy; + } + + return BLK_MQ_RQ_QUEUE_OK; + +out_dec_host_busy: + atomic_dec(&shost->host_busy); +out_dec_target_busy: + if (scsi_target(sdev)->can_queue > 0) + atomic_dec(&scsi_target(sdev)->target_busy); +out_dec_device_busy: + atomic_dec(&sdev->device_busy); +out_put_device: + put_device(&sdev->sdev_gendev); +out: + switch (ret) { + case BLK_MQ_RQ_QUEUE_BUSY: + blk_mq_stop_hw_queue(hctx); + if (atomic_read(&sdev->device_busy) == 0 && + !scsi_device_blocked(sdev)) + blk_mq_delay_queue(hctx, SCSI_QUEUE_DELAY); + break; + case BLK_MQ_RQ_QUEUE_ERROR: + /* + * Make sure to release all allocated ressources when + * we hit an error, as we will never see this command + * again. + */ + if (req->cmd_flags & REQ_DONTPREP) + scsi_mq_uninit_cmd(cmd); + break; + default: + break; + } + return ret; +} + +static int scsi_init_request(void *data, struct request *rq, + unsigned int hctx_idx, unsigned int request_idx, + unsigned int numa_node) +{ + struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(rq); + + cmd->sense_buffer = kzalloc_node(SCSI_SENSE_BUFFERSIZE, GFP_KERNEL, + numa_node); + if (!cmd->sense_buffer) + return -ENOMEM; + return 0; +} + +static void scsi_exit_request(void *data, struct request *rq, + unsigned int hctx_idx, unsigned int request_idx) +{ + struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(rq); + + kfree(cmd->sense_buffer); +} + static u64 scsi_calculate_bounce_limit(struct Scsi_Host *shost) { struct device *host_dev; @@ -1692,16 +1973,10 @@ static u64 scsi_calculate_bounce_limit(struct Scsi_Host *shost) return bounce_limit; } -struct request_queue *__scsi_alloc_queue(struct Scsi_Host *shost, - request_fn_proc *request_fn) +static void __scsi_init_queue(struct Scsi_Host *shost, struct request_queue *q) { - struct request_queue *q; struct device *dev = shost->dma_dev; - q = blk_init_queue(request_fn, NULL); - if (!q) - return NULL; - /* * this limit is imposed by hardware restrictions */ @@ -1732,7 +2007,17 @@ struct request_queue *__scsi_alloc_queue(struct Scsi_Host *shost, * blk_queue_update_dma_alignment() later. */ blk_queue_dma_alignment(q, 0x03); +} +struct request_queue *__scsi_alloc_queue(struct Scsi_Host *shost, + request_fn_proc *request_fn) +{ + struct request_queue *q; + + q = blk_init_queue(request_fn, NULL); + if (!q) + return NULL; + __scsi_init_queue(shost, q); return q; } EXPORT_SYMBOL(__scsi_alloc_queue); @@ -1753,6 +2038,55 @@ struct request_queue *scsi_alloc_queue(struct scsi_device *sdev) return q; } +static struct blk_mq_ops scsi_mq_ops = { + .map_queue = blk_mq_map_queue, + .queue_rq = scsi_queue_rq, + .complete = scsi_softirq_done, + .timeout = scsi_times_out, + .init_request = scsi_init_request, + .exit_request = scsi_exit_request, +}; + +struct request_queue *scsi_mq_alloc_queue(struct scsi_device *sdev) +{ + sdev->request_queue = blk_mq_init_queue(&sdev->host->tag_set); + if (IS_ERR(sdev->request_queue)) + return NULL; + + sdev->request_queue->queuedata = sdev; + __scsi_init_queue(sdev->host, sdev->request_queue); + return sdev->request_queue; +} + +int scsi_mq_setup_tags(struct Scsi_Host *shost) +{ + unsigned int cmd_size, sgl_size, tbl_size; + + tbl_size = shost->sg_tablesize; + if (tbl_size > SCSI_MAX_SG_SEGMENTS) + tbl_size = SCSI_MAX_SG_SEGMENTS; + sgl_size = tbl_size * sizeof(struct scatterlist); + cmd_size = sizeof(struct scsi_cmnd) + shost->hostt->cmd_size + sgl_size; + if (scsi_host_get_prot(shost)) + cmd_size += sizeof(struct scsi_data_buffer) + sgl_size; + + memset(&shost->tag_set, 0, sizeof(shost->tag_set)); + shost->tag_set.ops = &scsi_mq_ops; + shost->tag_set.nr_hw_queues = 1; + shost->tag_set.queue_depth = shost->can_queue; + shost->tag_set.cmd_size = cmd_size; + shost->tag_set.numa_node = NUMA_NO_NODE; + shost->tag_set.flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_SG_MERGE; + shost->tag_set.driver_data = shost; + + return blk_mq_alloc_tag_set(&shost->tag_set); +} + +void scsi_mq_destroy_tags(struct Scsi_Host *shost) +{ + blk_mq_free_tag_set(&shost->tag_set); +} + /* * Function: scsi_block_requests() * @@ -2498,9 +2832,13 @@ scsi_internal_device_block(struct scsi_device *sdev) * block layer from calling the midlayer with this device's * request queue. */ - spin_lock_irqsave(q->queue_lock, flags); - blk_stop_queue(q); - spin_unlock_irqrestore(q->queue_lock, flags); + if (q->mq_ops) { + blk_mq_stop_hw_queues(q); + } else { + spin_lock_irqsave(q->queue_lock, flags); + blk_stop_queue(q); + spin_unlock_irqrestore(q->queue_lock, flags); + } return 0; } @@ -2546,9 +2884,13 @@ scsi_internal_device_unblock(struct scsi_device *sdev, sdev->sdev_state != SDEV_OFFLINE) return -EINVAL; - spin_lock_irqsave(q->queue_lock, flags); - blk_start_queue(q); - spin_unlock_irqrestore(q->queue_lock, flags); + if (q->mq_ops) { + blk_mq_start_stopped_hw_queues(q, false); + } else { + spin_lock_irqsave(q->queue_lock, flags); + blk_start_queue(q); + spin_unlock_irqrestore(q->queue_lock, flags); + } return 0; } diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index a45d1c2eb414..12b8e1bee7f0 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -88,6 +88,9 @@ extern void scsi_next_command(struct scsi_cmnd *cmd); extern void scsi_io_completion(struct scsi_cmnd *, unsigned int); extern void scsi_run_host_queues(struct Scsi_Host *shost); extern struct request_queue *scsi_alloc_queue(struct scsi_device *sdev); +extern struct request_queue *scsi_mq_alloc_queue(struct scsi_device *sdev); +extern int scsi_mq_setup_tags(struct Scsi_Host *shost); +extern void scsi_mq_destroy_tags(struct Scsi_Host *shost); extern int scsi_init_queue(void); extern void scsi_exit_queue(void); struct request_queue; diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 4a6e4ba5a400..b91cfaf033aa 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -273,7 +273,10 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget, */ sdev->borken = 1; - sdev->request_queue = scsi_alloc_queue(sdev); + if (shost_use_blk_mq(shost)) + sdev->request_queue = scsi_mq_alloc_queue(sdev); + else + sdev->request_queue = scsi_alloc_queue(sdev); if (!sdev->request_queue) { /* release fn is set up in scsi_sysfs_device_initialise, so * have to free and put manually here */ diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 209cae3097ea..406b3038bbad 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -333,6 +333,7 @@ store_shost_eh_deadline(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(eh_deadline, S_IRUGO | S_IWUSR, show_shost_eh_deadline, store_shost_eh_deadline); +shost_rd_attr(use_blk_mq, "%d\n"); shost_rd_attr(unique_id, "%u\n"); shost_rd_attr(cmd_per_lun, "%hd\n"); shost_rd_attr(can_queue, "%hd\n"); @@ -352,6 +353,7 @@ show_host_busy(struct device *dev, struct device_attribute *attr, char *buf) static DEVICE_ATTR(host_busy, S_IRUGO, show_host_busy, NULL); static struct attribute *scsi_sysfs_shost_attrs[] = { + &dev_attr_use_blk_mq.attr, &dev_attr_unique_id.attr, &dev_attr_host_busy.attr, &dev_attr_cmd_per_lun.attr, diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 5e8ebc1ac12b..ba2034779961 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -7,6 +7,7 @@ #include #include #include +#include #include struct request_queue; @@ -510,6 +511,9 @@ struct scsi_host_template { */ unsigned int cmd_size; struct scsi_host_cmd_pool *cmd_pool; + + /* temporary flag to disable blk-mq I/O path */ + bool disable_blk_mq; }; /* @@ -580,7 +584,10 @@ struct Scsi_Host { * Area to keep a shared tag map (if needed, will be * NULL if not). */ - struct blk_queue_tag *bqt; + union { + struct blk_queue_tag *bqt; + struct blk_mq_tag_set tag_set; + }; atomic_t host_busy; /* commands actually active on low-level */ atomic_t host_blocked; @@ -672,6 +679,8 @@ struct Scsi_Host { /* The controller does not support WRITE SAME */ unsigned no_write_same:1; + unsigned use_blk_mq:1; + /* * Optional work queue to be utilized by the transport */ @@ -772,6 +781,13 @@ static inline int scsi_host_in_recovery(struct Scsi_Host *shost) shost->tmf_in_progress; } +extern bool scsi_use_blk_mq; + +static inline bool shost_use_blk_mq(struct Scsi_Host *shost) +{ + return shost->use_blk_mq; +} + extern int scsi_queue_work(struct Scsi_Host *, struct work_struct *); extern void scsi_flush_work(struct Scsi_Host *); diff --git a/include/scsi/scsi_tcq.h b/include/scsi/scsi_tcq.h index 81dd12edc38c..cdcc90b07ecb 100644 --- a/include/scsi/scsi_tcq.h +++ b/include/scsi/scsi_tcq.h @@ -67,7 +67,8 @@ static inline void scsi_activate_tcq(struct scsi_device *sdev, int depth) if (!sdev->tagged_supported) return; - if (!blk_queue_tagged(sdev->request_queue)) + if (!shost_use_blk_mq(sdev->host) && + blk_queue_tagged(sdev->request_queue)) blk_queue_init_tags(sdev->request_queue, depth, sdev->host->bqt); @@ -80,7 +81,8 @@ static inline void scsi_activate_tcq(struct scsi_device *sdev, int depth) **/ static inline void scsi_deactivate_tcq(struct scsi_device *sdev, int depth) { - if (blk_queue_tagged(sdev->request_queue)) + if (!shost_use_blk_mq(sdev->host) && + blk_queue_tagged(sdev->request_queue)) blk_queue_free_tags(sdev->request_queue); scsi_adjust_queue_depth(sdev, 0, depth); } @@ -108,6 +110,15 @@ static inline int scsi_populate_tag_msg(struct scsi_cmnd *cmd, char *msg) return 0; } +static inline struct scsi_cmnd *scsi_mq_find_tag(struct Scsi_Host *shost, + unsigned int hw_ctx, int tag) +{ + struct request *req; + + req = blk_mq_tag_to_rq(shost->tag_set.tags[hw_ctx], tag); + return req ? (struct scsi_cmnd *)req->special : NULL; +} + /** * scsi_find_tag - find a tagged command by device * @SDpnt: pointer to the ScSI device @@ -118,10 +129,12 @@ static inline int scsi_populate_tag_msg(struct scsi_cmnd *cmd, char *msg) **/ static inline struct scsi_cmnd *scsi_find_tag(struct scsi_device *sdev, int tag) { - struct request *req; if (tag != SCSI_NO_TAG) { + if (shost_use_blk_mq(sdev->host)) + return scsi_mq_find_tag(sdev->host, 0, tag); + req = blk_queue_find_tag(sdev->request_queue, tag); return req ? (struct scsi_cmnd *)req->special : NULL; } @@ -130,6 +143,7 @@ static inline struct scsi_cmnd *scsi_find_tag(struct scsi_device *sdev, int tag) return sdev->current_cmnd; } + /** * scsi_init_shared_tag_map - create a shared tag map * @shost: the host to share the tag map among all devices @@ -137,6 +151,12 @@ static inline struct scsi_cmnd *scsi_find_tag(struct scsi_device *sdev, int tag) */ static inline int scsi_init_shared_tag_map(struct Scsi_Host *shost, int depth) { + /* + * We always have a shared tag map around when using blk-mq. + */ + if (shost_use_blk_mq(shost)) + return 0; + /* * If the shared tag map isn't already initialized, do it now. * This saves callers from having to check ->bqt when setting up @@ -165,6 +185,8 @@ static inline struct scsi_cmnd *scsi_host_find_tag(struct Scsi_Host *shost, struct request *req; if (tag != SCSI_NO_TAG) { + if (shost_use_blk_mq(shost)) + return scsi_mq_find_tag(shost, 0, tag); req = blk_map_queue_find_tag(shost->bqt, tag); return req ? (struct scsi_cmnd *)req->special : NULL; } -- cgit v1.2.3 From 7a14c1c3319608154da8712e4174d56ffb2f7b8d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 18 Jul 2014 16:51:51 +0200 Subject: scsi: add a symbolic name for the ZBC device type Make sure we have a symbolic name for the ZBC type available, so that e.g. patch for a SATA to translate ZAC commands can make use of it. Signed-off-by: Christoph Hellwig Reviewed-by: Hannes Reinecke Reviewed-by: Martin K. Petersen --- include/scsi/scsi.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h index 91e2e4215ba0..e6df23cae7be 100644 --- a/include/scsi/scsi.h +++ b/include/scsi/scsi.h @@ -332,6 +332,7 @@ static inline int scsi_status_is_good(int status) #define TYPE_ENCLOSURE 0x0d /* Enclosure Services Device */ #define TYPE_RBC 0x0e #define TYPE_OSD 0x11 +#define TYPE_ZBC 0x14 #define TYPE_NO_LUN 0x7f /* SCSI protocols; these are taken from SPC-3 section 7.5 */ -- cgit v1.2.3 From fd2eb9034e48cdca358dc06a833a736e7c6f68dd Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 18 Jul 2014 16:59:19 +0200 Subject: scsi: move the writeable field from struct scsi_device to struct scsi_cd We currently set the field in common code based on the device type, but then only use it in the cdrom driver which also overrides the value previously set in the generic code. Just leave this entirely to the CDROM driver to make everyones life simpler. Signed-off-by: Christoph Hellwig Reviewed-by: Hannes Reinecke Reviewed-by: Martin K. Petersen --- drivers/scsi/scsi_scan.c | 24 ------------------------ drivers/scsi/sd.c | 3 --- drivers/scsi/sr.c | 4 ++-- drivers/scsi/sr.h | 1 + include/scsi/scsi_device.h | 1 - 5 files changed, 3 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index b91cfaf033aa..a5a0bdeba857 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -807,30 +807,6 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, sdev->removable = (inq_result[1] & 0x80) >> 7; } - switch (sdev->type) { - case TYPE_RBC: - case TYPE_TAPE: - case TYPE_DISK: - case TYPE_PRINTER: - case TYPE_MOD: - case TYPE_PROCESSOR: - case TYPE_SCANNER: - case TYPE_MEDIUM_CHANGER: - case TYPE_ENCLOSURE: - case TYPE_COMM: - case TYPE_RAID: - case TYPE_OSD: - sdev->writeable = 1; - break; - case TYPE_ROM: - case TYPE_WORM: - sdev->writeable = 0; - break; - default: - sdev_printk(KERN_INFO, sdev, "unknown device type %d\n", - sdev->type); - } - if (sdev->type == TYPE_RBC || sdev->type == TYPE_ROM) { /* RBC and MMC devices can return SCSI-3 compliance and yet * still not support REPORT LUNS, so make them act as diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 3663e38ba4df..377a5206017e 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -992,9 +992,6 @@ static int sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) } } if (rq_data_dir(rq) == WRITE) { - if (!sdp->writeable) { - goto out; - } SCpnt->cmnd[0] = WRITE_6; if (blk_integrity_rq(rq)) diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index cce4771281d9..7eeb93627beb 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -435,7 +435,7 @@ static int sr_init_command(struct scsi_cmnd *SCpnt) } if (rq_data_dir(rq) == WRITE) { - if (!cd->device->writeable) + if (!cd->writeable) goto out; SCpnt->cmnd[0] = WRITE_10; cd->cdi.media_written = 1; @@ -927,7 +927,7 @@ static void get_capabilities(struct scsi_cd *cd) */ if ((cd->cdi.mask & (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)) != (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)) { - cd->device->writeable = 1; + cd->writeable = 1; } kfree(buffer); diff --git a/drivers/scsi/sr.h b/drivers/scsi/sr.h index 5334e988480d..1d1f6f416c59 100644 --- a/drivers/scsi/sr.h +++ b/drivers/scsi/sr.h @@ -36,6 +36,7 @@ typedef struct scsi_cd { struct scsi_device *device; unsigned int vendor; /* vendor code, see sr_vendor.c */ unsigned long ms_offset; /* for reading multisession-CD's */ + unsigned writeable : 1; unsigned use:1; /* is this device still supportable */ unsigned xa_flag:1; /* CD has XA sectors ? */ unsigned readcd_known:1; /* drive supports READ_CD (0xbe) */ diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 0f853f2c9dc7..b895784e2313 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -127,7 +127,6 @@ struct scsi_device { * pass settings from slave_alloc to scsi * core. */ unsigned int eh_timeout; /* Error handling timeout */ - unsigned writeable:1; unsigned removable:1; unsigned changed:1; /* Data invalid due to media change */ unsigned busy:1; /* Used to prevent races */ -- cgit v1.2.3 From c1d40a527e885a40bb9ea6c46a1b1145d42b66a0 Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Tue, 15 Jul 2014 12:49:17 -0400 Subject: scsi: add a blacklist flag which enables VPD page inquiries Despite supporting modern SCSI features some storage devices continue to claim conformance to an older version of the SPC spec. This is done for compatibility with legacy operating systems. Linux by default will not attempt to read VPD pages on devices that claim SPC-2 or older. Introduce a blacklist flag that can be used to trigger VPD page inquiries on devices that are known to support them. Reported-by: KY Srinivasan Tested-by: KY Srinivasan Reviewed-by: KY Srinivasan Signed-off-by: Martin K. Petersen CC: Signed-off-by: Christoph Hellwig --- drivers/scsi/scsi_scan.c | 4 +++- drivers/scsi/sd.c | 5 +++++ include/scsi/scsi_device.h | 1 + include/scsi/scsi_devinfo.h | 1 + 4 files changed, 10 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index a5a0bdeba857..50536cd6b3f2 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -928,7 +928,9 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, sdev->eh_timeout = SCSI_DEFAULT_EH_TIMEOUT; - if (*bflags & BLIST_SKIP_VPD_PAGES) + if (*bflags & BLIST_TRY_VPD_PAGES) + sdev->try_vpd_pages = 1; + else if (*bflags & BLIST_SKIP_VPD_PAGES) sdev->skip_vpd_pages = 1; transport_configure_device(&sdev->sdev_gendev); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 377a5206017e..4d72831eafe5 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -2726,6 +2726,11 @@ static void sd_read_write_same(struct scsi_disk *sdkp, unsigned char *buffer) static int sd_try_extended_inquiry(struct scsi_device *sdp) { + /* Attempt VPD inquiry if the device blacklist explicitly calls + * for it. + */ + if (sdp->try_vpd_pages) + return 1; /* * Although VPD inquiries can go to SCSI-2 type devices, * some USB ones crash on receiving them, and the pages diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index b895784e2313..1a0d1842962e 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -154,6 +154,7 @@ struct scsi_device { unsigned skip_ms_page_8:1; /* do not use MODE SENSE page 0x08 */ unsigned skip_ms_page_3f:1; /* do not use MODE SENSE page 0x3f */ unsigned skip_vpd_pages:1; /* do not read VPD pages */ + unsigned try_vpd_pages:1; /* attempt to read VPD pages */ unsigned use_192_bytes_for_3f:1; /* ask for 192 bytes from page 0x3f */ unsigned no_start_on_add:1; /* do not issue start on add */ unsigned allow_restart:1; /* issue START_UNIT in error handler */ diff --git a/include/scsi/scsi_devinfo.h b/include/scsi/scsi_devinfo.h index 8670c04e199e..1fdd6fc5492b 100644 --- a/include/scsi/scsi_devinfo.h +++ b/include/scsi/scsi_devinfo.h @@ -34,4 +34,5 @@ #define BLIST_SKIP_VPD_PAGES 0x4000000 /* Ignore SBC-3 VPD pages */ #define BLIST_SCSI3LUN 0x8000000 /* Scan more than 256 LUNs for sequential scan */ +#define BLIST_TRY_VPD_PAGES 0x10000000 /* Attempt to read VPD pages */ #endif -- cgit v1.2.3 From c21a2c1a4973c8dde32557970fdb44eaa9489aeb Mon Sep 17 00:00:00 2001 From: Dick Kennedy Date: Fri, 13 Jun 2014 16:40:36 +0000 Subject: scsi: add defines for new FC port speeds. These speeds are to support the next generation of FCoE port speeds. Signed-off-by: Dick Kennedy Reviewed-by: Ewan D. Milne Signed-off-by: Christoph Hellwig --- drivers/scsi/scsi_transport_fc.c | 4 ++++ include/scsi/scsi_transport_fc.h | 4 ++++ 2 files changed, 8 insertions(+) (limited to 'include') diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 4b0f167e8c21..5d6f348eb3d8 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -261,6 +261,10 @@ static const struct { { FC_PORTSPEED_8GBIT, "8 Gbit" }, { FC_PORTSPEED_16GBIT, "16 Gbit" }, { FC_PORTSPEED_32GBIT, "32 Gbit" }, + { FC_PORTSPEED_20GBIT, "20 Gbit" }, + { FC_PORTSPEED_40GBIT, "40 Gbit" }, + { FC_PORTSPEED_50GBIT, "50 Gbit" }, + { FC_PORTSPEED_100GBIT, "100 Gbit" }, { FC_PORTSPEED_NOT_NEGOTIATED, "Not Negotiated" }, }; fc_bitfield_name_search(port_speed, fc_port_speed_names) diff --git a/include/scsi/scsi_transport_fc.h b/include/scsi/scsi_transport_fc.h index 8c79980dc8f2..007a0bc01b74 100644 --- a/include/scsi/scsi_transport_fc.h +++ b/include/scsi/scsi_transport_fc.h @@ -131,6 +131,10 @@ enum fc_vport_state { #define FC_PORTSPEED_8GBIT 0x10 #define FC_PORTSPEED_16GBIT 0x20 #define FC_PORTSPEED_32GBIT 0x40 +#define FC_PORTSPEED_20GBIT 0x80 +#define FC_PORTSPEED_40GBIT 0x100 +#define FC_PORTSPEED_50GBIT 0x200 +#define FC_PORTSPEED_100GBIT 0x400 #define FC_PORTSPEED_NOT_NEGOTIATED (1 << 15) /* Speed not established */ /* -- cgit v1.2.3 From c5540fbb9de39ceec108a889133664a887c2f55a Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Thu, 3 Apr 2014 20:32:21 -0300 Subject: [media] rc-core: remove protocol arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The basic API of rc-core used to be: dev = rc_allocate_device(); dev->x = a; dev->y = b; dev->z = c; rc_register_device(); which is a pretty common pattern in the kernel, after the introduction of protocol arrays the API looks something like: dev = rc_allocate_device(); dev->x = a; rc_set_allowed_protocols(dev, RC_BIT_X); dev->z = c; rc_register_device(); There's no real need for the protocols to be an array, so change it back to be consistent (and in preparation for the following patches). [m.chehab@samsung.com: added missing changes at some files] Signed-off-by: David Härdeman Signed-off-by: Mauro Carvalho Chehab --- drivers/hid/hid-picolcd_cir.c | 2 +- drivers/media/common/siano/smsir.c | 2 +- drivers/media/i2c/ir-kbd-i2c.c | 4 +-- drivers/media/pci/cx23885/cx23885-input.c | 2 +- drivers/media/pci/cx88/cx88-input.c | 2 +- drivers/media/rc/ati_remote.c | 2 +- drivers/media/rc/ene_ir.c | 2 +- drivers/media/rc/fintek-cir.c | 2 +- drivers/media/rc/gpio-ir-recv.c | 4 +-- drivers/media/rc/iguanair.c | 2 +- drivers/media/rc/img-ir/img-ir-hw.c | 16 ++++----- drivers/media/rc/imon.c | 7 ++-- drivers/media/rc/ir-jvc-decoder.c | 2 +- drivers/media/rc/ir-lirc-codec.c | 2 +- drivers/media/rc/ir-mce_kbd-decoder.c | 2 +- drivers/media/rc/ir-nec-decoder.c | 2 +- drivers/media/rc/ir-raw.c | 2 +- drivers/media/rc/ir-rc5-decoder.c | 6 ++-- drivers/media/rc/ir-rc5-sz-decoder.c | 2 +- drivers/media/rc/ir-rc6-decoder.c | 6 ++-- drivers/media/rc/ir-sanyo-decoder.c | 2 +- drivers/media/rc/ir-sharp-decoder.c | 2 +- drivers/media/rc/ir-sony-decoder.c | 10 +++--- drivers/media/rc/ite-cir.c | 2 +- drivers/media/rc/mceusb.c | 2 +- drivers/media/rc/nuvoton-cir.c | 2 +- drivers/media/rc/rc-loopback.c | 2 +- drivers/media/rc/rc-main.c | 30 ++++++++-------- drivers/media/rc/redrat3.c | 2 +- drivers/media/rc/st_rc.c | 2 +- drivers/media/rc/streamzap.c | 2 +- drivers/media/rc/sunxi-cir.c | 2 +- drivers/media/rc/ttusbir.c | 2 +- drivers/media/rc/winbond-cir.c | 2 +- drivers/media/usb/dvb-usb-v2/dvb_usb_core.c | 2 +- drivers/media/usb/dvb-usb/dvb-usb-remote.c | 2 +- drivers/media/usb/em28xx/em28xx-input.c | 8 ++--- drivers/media/usb/tm6000/tm6000-input.c | 2 +- include/media/rc-core.h | 56 +++++++---------------------- 39 files changed, 86 insertions(+), 119 deletions(-) (limited to 'include') diff --git a/drivers/hid/hid-picolcd_cir.c b/drivers/hid/hid-picolcd_cir.c index cf1a9f1c1217..045f8ebf16b5 100644 --- a/drivers/hid/hid-picolcd_cir.c +++ b/drivers/hid/hid-picolcd_cir.c @@ -114,7 +114,7 @@ int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report) rdev->priv = data; rdev->driver_type = RC_DRIVER_IR_RAW; - rc_set_allowed_protocols(rdev, RC_BIT_ALL); + rdev->allowed_protocols = RC_BIT_ALL; rdev->open = picolcd_cir_open; rdev->close = picolcd_cir_close; rdev->input_name = data->hdev->name; diff --git a/drivers/media/common/siano/smsir.c b/drivers/media/common/siano/smsir.c index 6d7c0c858bd0..273043ea8f47 100644 --- a/drivers/media/common/siano/smsir.c +++ b/drivers/media/common/siano/smsir.c @@ -88,7 +88,7 @@ int sms_ir_init(struct smscore_device_t *coredev) dev->priv = coredev; dev->driver_type = RC_DRIVER_IR_RAW; - rc_set_allowed_protocols(dev, RC_BIT_ALL); + dev->allowed_protocols = RC_BIT_ALL; dev->map_name = sms_get_board(board_id)->rc_codes; dev->driver_name = MODULE_NAME; diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c index f9c4233ba96e..8311f1a9a38e 100644 --- a/drivers/media/i2c/ir-kbd-i2c.c +++ b/drivers/media/i2c/ir-kbd-i2c.c @@ -432,8 +432,8 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) * Initialize the other fields of rc_dev */ rc->map_name = ir->ir_codes; - rc_set_allowed_protocols(rc, rc_type); - rc_set_enabled_protocols(rc, rc_type); + rc->allowed_protocols = rc_type; + rc->enabled_protocols = rc_type; if (!rc->driver_name) rc->driver_name = MODULE_NAME; diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c index 097d0a0b5f57..1940c18e186c 100644 --- a/drivers/media/pci/cx23885/cx23885-input.c +++ b/drivers/media/pci/cx23885/cx23885-input.c @@ -346,7 +346,7 @@ int cx23885_input_init(struct cx23885_dev *dev) } rc->dev.parent = &dev->pci->dev; rc->driver_type = driver_type; - rc_set_allowed_protocols(rc, allowed_protos); + rc->allowed_protocols = allowed_protos; rc->priv = kernel_ir; rc->open = cx23885_input_ir_open; rc->close = cx23885_input_ir_close; diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c index 9bf48ca619c7..93ff6a7f2354 100644 --- a/drivers/media/pci/cx88/cx88-input.c +++ b/drivers/media/pci/cx88/cx88-input.c @@ -485,7 +485,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) dev->timeout = 10 * 1000 * 1000; /* 10 ms */ } else { dev->driver_type = RC_DRIVER_SCANCODE; - rc_set_allowed_protocols(dev, rc_type); + dev->allowed_protocols = rc_type; } ir->core = core; diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index 8730b32a067c..2e1335306eb3 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -784,7 +784,7 @@ static void ati_remote_rc_init(struct ati_remote *ati_remote) rdev->priv = ati_remote; rdev->driver_type = RC_DRIVER_SCANCODE; - rc_set_allowed_protocols(rdev, RC_BIT_OTHER); + rdev->allowed_protocols = RC_BIT_OTHER; rdev->driver_name = "ati_remote"; rdev->open = ati_remote_rc_open; diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c index fc9d23f2ed3f..d16d9b496b92 100644 --- a/drivers/media/rc/ene_ir.c +++ b/drivers/media/rc/ene_ir.c @@ -1059,7 +1059,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) learning_mode_force = false; rdev->driver_type = RC_DRIVER_IR_RAW; - rc_set_allowed_protocols(rdev, RC_BIT_ALL); + rdev->allowed_protocols = RC_BIT_ALL; rdev->priv = dev; rdev->open = ene_open; rdev->close = ene_close; diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c index 46b66e59438f..f000faf7340b 100644 --- a/drivers/media/rc/fintek-cir.c +++ b/drivers/media/rc/fintek-cir.c @@ -541,7 +541,7 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id /* Set up the rc device */ rdev->priv = fintek; rdev->driver_type = RC_DRIVER_IR_RAW; - rc_set_allowed_protocols(rdev, RC_BIT_ALL); + rdev->allowed_protocols = RC_BIT_ALL; rdev->open = fintek_open; rdev->close = fintek_close; rdev->input_name = FINTEK_DESCRIPTION; diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c index 29b5f89813b4..59853085bc88 100644 --- a/drivers/media/rc/gpio-ir-recv.c +++ b/drivers/media/rc/gpio-ir-recv.c @@ -145,9 +145,9 @@ static int gpio_ir_recv_probe(struct platform_device *pdev) rcdev->dev.parent = &pdev->dev; rcdev->driver_name = GPIO_IR_DRIVER_NAME; if (pdata->allowed_protos) - rc_set_allowed_protocols(rcdev, pdata->allowed_protos); + rcdev->allowed_protocols = pdata->allowed_protos; else - rc_set_allowed_protocols(rcdev, RC_BIT_ALL); + rcdev->allowed_protocols = RC_BIT_ALL; rcdev->map_name = pdata->map_name ?: RC_MAP_EMPTY; gpio_dev->rcdev = rcdev; diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c index 627ddfd61980..ee60e17fba05 100644 --- a/drivers/media/rc/iguanair.c +++ b/drivers/media/rc/iguanair.c @@ -495,7 +495,7 @@ static int iguanair_probe(struct usb_interface *intf, usb_to_input_id(ir->udev, &rc->input_id); rc->dev.parent = &intf->dev; rc->driver_type = RC_DRIVER_IR_RAW; - rc_set_allowed_protocols(rc, RC_BIT_ALL); + rc->allowed_protocols = RC_BIT_ALL; rc->priv = ir; rc->open = iguanair_open; rc->close = iguanair_close; diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c index b0ec55f52228..bfb282a714e8 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.c +++ b/drivers/media/rc/img-ir/img-ir-hw.c @@ -556,8 +556,8 @@ static void img_ir_set_decoder(struct img_ir_priv *priv, hw->mode = IMG_IR_M_NORMAL; /* clear the wakeup scancode filter */ - rdev->scancode_filters[RC_FILTER_WAKEUP].data = 0; - rdev->scancode_filters[RC_FILTER_WAKEUP].mask = 0; + rdev->scancode_wakeup_filter.data = 0; + rdev->scancode_wakeup_filter.mask = 0; /* clear raw filters */ _img_ir_set_filter(priv, NULL); @@ -661,8 +661,8 @@ success: wakeup_protocols = *ir_type; if (!hw->decoder || !hw->decoder->filter) wakeup_protocols = 0; - rc_set_allowed_wakeup_protocols(rdev, wakeup_protocols); - rc_set_enabled_wakeup_protocols(rdev, wakeup_protocols); + rdev->allowed_wakeup_protocols = wakeup_protocols; + rdev->enabled_wakeup_protocols = wakeup_protocols; return 0; } @@ -676,9 +676,9 @@ static void img_ir_set_protocol(struct img_ir_priv *priv, u64 proto) spin_unlock_irq(&rdev->rc_map.lock); mutex_lock(&rdev->lock); - rc_set_enabled_protocols(rdev, proto); - rc_set_allowed_wakeup_protocols(rdev, proto); - rc_set_enabled_wakeup_protocols(rdev, proto); + rdev->enabled_protocols = proto; + rdev->allowed_wakeup_protocols = proto; + rdev->enabled_wakeup_protocols = proto; mutex_unlock(&rdev->lock); } @@ -1003,7 +1003,7 @@ int img_ir_probe_hw(struct img_ir_priv *priv) } rdev->priv = priv; rdev->map_name = RC_MAP_EMPTY; - rc_set_allowed_protocols(rdev, img_ir_allowed_protos(priv)); + rdev->allowed_protocols = img_ir_allowed_protos(priv); rdev->input_name = "IMG Infrared Decoder"; rdev->s_filter = img_ir_set_normal_filter; rdev->s_wakeup_filter = img_ir_set_wakeup_filter; diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index eb37f2622364..ac05c364f663 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -1017,7 +1017,7 @@ static int imon_ir_change_protocol(struct rc_dev *rc, u64 *rc_type) unsigned char ir_proto_packet[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86 }; - if (*rc_type && !rc_protocols_allowed(rc, *rc_type)) + if (*rc_type && !(*rc_type & rc->allowed_protocols)) dev_warn(dev, "Looks like you're trying to use an IR protocol " "this device does not support\n"); @@ -1870,8 +1870,7 @@ static struct rc_dev *imon_init_rdev(struct imon_context *ictx) rdev->priv = ictx; rdev->driver_type = RC_DRIVER_SCANCODE; - /* iMON PAD or MCE */ - rc_set_allowed_protocols(rdev, RC_BIT_OTHER | RC_BIT_RC6_MCE); + rdev->allowed_protocols = RC_BIT_OTHER | RC_BIT_RC6_MCE; /* iMON PAD or MCE */ rdev->change_protocol = imon_ir_change_protocol; rdev->driver_name = MOD_NAME; @@ -1884,7 +1883,7 @@ static struct rc_dev *imon_init_rdev(struct imon_context *ictx) if (ictx->product == 0xffdc) { imon_get_ffdc_type(ictx); - rc_set_allowed_protocols(rdev, ictx->rc_type); + rdev->allowed_protocols = ictx->rc_type; } imon_set_display_type(ictx); diff --git a/drivers/media/rc/ir-jvc-decoder.c b/drivers/media/rc/ir-jvc-decoder.c index 7b79eca83449..30bcf188d377 100644 --- a/drivers/media/rc/ir-jvc-decoder.c +++ b/drivers/media/rc/ir-jvc-decoder.c @@ -47,7 +47,7 @@ static int ir_jvc_decode(struct rc_dev *dev, struct ir_raw_event ev) { struct jvc_dec *data = &dev->raw->jvc; - if (!rc_protocols_enabled(dev, RC_BIT_JVC)) + if (!(dev->enabled_protocols & RC_BIT_JVC)) return 0; if (!is_timing_event(ev)) { diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c index d731da6c414d..ed2c8a1ed8ca 100644 --- a/drivers/media/rc/ir-lirc-codec.c +++ b/drivers/media/rc/ir-lirc-codec.c @@ -35,7 +35,7 @@ static int ir_lirc_decode(struct rc_dev *dev, struct ir_raw_event ev) struct lirc_codec *lirc = &dev->raw->lirc; int sample; - if (!rc_protocols_enabled(dev, RC_BIT_LIRC)) + if (!(dev->enabled_protocols & RC_BIT_LIRC)) return 0; if (!dev->raw->lirc.drv || !dev->raw->lirc.drv->rbuf) diff --git a/drivers/media/rc/ir-mce_kbd-decoder.c b/drivers/media/rc/ir-mce_kbd-decoder.c index 0c55f794c8cf..9f3c9b59f30c 100644 --- a/drivers/media/rc/ir-mce_kbd-decoder.c +++ b/drivers/media/rc/ir-mce_kbd-decoder.c @@ -216,7 +216,7 @@ static int ir_mce_kbd_decode(struct rc_dev *dev, struct ir_raw_event ev) u32 scancode; unsigned long delay; - if (!rc_protocols_enabled(dev, RC_BIT_MCE_KBD)) + if (!(dev->enabled_protocols & RC_BIT_MCE_KBD)) return 0; if (!is_timing_event(ev)) { diff --git a/drivers/media/rc/ir-nec-decoder.c b/drivers/media/rc/ir-nec-decoder.c index c4333d5c0046..7b81fec0820f 100644 --- a/drivers/media/rc/ir-nec-decoder.c +++ b/drivers/media/rc/ir-nec-decoder.c @@ -52,7 +52,7 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev) u8 address, not_address, command, not_command; bool send_32bits = false; - if (!rc_protocols_enabled(dev, RC_BIT_NEC)) + if (!(dev->enabled_protocols & RC_BIT_NEC)) return 0; if (!is_timing_event(ev)) { diff --git a/drivers/media/rc/ir-raw.c b/drivers/media/rc/ir-raw.c index c08718e17671..c72f57cebfd2 100644 --- a/drivers/media/rc/ir-raw.c +++ b/drivers/media/rc/ir-raw.c @@ -262,7 +262,7 @@ int ir_raw_event_register(struct rc_dev *dev) return -ENOMEM; dev->raw->dev = dev; - rc_set_enabled_protocols(dev, ~0); + dev->enabled_protocols = ~0; dev->change_protocol = change_protocol; rc = kfifo_alloc(&dev->raw->kfifo, sizeof(struct ir_raw_event) * MAX_IR_EVENT_SIZE, diff --git a/drivers/media/rc/ir-rc5-decoder.c b/drivers/media/rc/ir-rc5-decoder.c index 3d38cbce5667..04ce42fa9eec 100644 --- a/drivers/media/rc/ir-rc5-decoder.c +++ b/drivers/media/rc/ir-rc5-decoder.c @@ -53,7 +53,7 @@ static int ir_rc5_decode(struct rc_dev *dev, struct ir_raw_event ev) u32 scancode; enum rc_type protocol; - if (!rc_protocols_enabled(dev, RC_BIT_RC5 | RC_BIT_RC5X)) + if (!(dev->enabled_protocols & (RC_BIT_RC5 | RC_BIT_RC5X))) return 0; if (!is_timing_event(ev)) { @@ -129,7 +129,7 @@ again: if (data->wanted_bits == RC5X_NBITS) { /* RC5X */ u8 xdata, command, system; - if (!rc_protocols_enabled(dev, RC_BIT_RC5X)) { + if (!(dev->enabled_protocols & RC_BIT_RC5X)) { data->state = STATE_INACTIVE; return 0; } @@ -147,7 +147,7 @@ again: } else { /* RC5 */ u8 command, system; - if (!rc_protocols_enabled(dev, RC_BIT_RC5)) { + if (!(dev->enabled_protocols & RC_BIT_RC5)) { data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/ir-rc5-sz-decoder.c b/drivers/media/rc/ir-rc5-sz-decoder.c index 85c771112cf5..771e9fcbd770 100644 --- a/drivers/media/rc/ir-rc5-sz-decoder.c +++ b/drivers/media/rc/ir-rc5-sz-decoder.c @@ -48,7 +48,7 @@ static int ir_rc5_sz_decode(struct rc_dev *dev, struct ir_raw_event ev) u8 toggle, command, system; u32 scancode; - if (!rc_protocols_enabled(dev, RC_BIT_RC5_SZ)) + if (!(dev->enabled_protocols & RC_BIT_RC5_SZ)) return 0; if (!is_timing_event(ev)) { diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c index 1dc97a7b92a5..f1f098e22f7e 100644 --- a/drivers/media/rc/ir-rc6-decoder.c +++ b/drivers/media/rc/ir-rc6-decoder.c @@ -90,9 +90,9 @@ static int ir_rc6_decode(struct rc_dev *dev, struct ir_raw_event ev) u8 toggle; enum rc_type protocol; - if (!rc_protocols_enabled(dev, RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | - RC_BIT_RC6_6A_24 | RC_BIT_RC6_6A_32 | - RC_BIT_RC6_MCE)) + if (!(dev->enabled_protocols & + (RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 | + RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE))) return 0; if (!is_timing_event(ev)) { diff --git a/drivers/media/rc/ir-sanyo-decoder.c b/drivers/media/rc/ir-sanyo-decoder.c index 5f7702261d23..ad1dc6ae21fc 100644 --- a/drivers/media/rc/ir-sanyo-decoder.c +++ b/drivers/media/rc/ir-sanyo-decoder.c @@ -58,7 +58,7 @@ static int ir_sanyo_decode(struct rc_dev *dev, struct ir_raw_event ev) u32 scancode; u8 address, command, not_command; - if (!rc_protocols_enabled(dev, RC_BIT_SANYO)) + if (!(dev->enabled_protocols & RC_BIT_SANYO)) return 0; if (!is_timing_event(ev)) { diff --git a/drivers/media/rc/ir-sharp-decoder.c b/drivers/media/rc/ir-sharp-decoder.c index c8f251939143..b7acdbae8159 100644 --- a/drivers/media/rc/ir-sharp-decoder.c +++ b/drivers/media/rc/ir-sharp-decoder.c @@ -48,7 +48,7 @@ static int ir_sharp_decode(struct rc_dev *dev, struct ir_raw_event ev) struct sharp_dec *data = &dev->raw->sharp; u32 msg, echo, address, command, scancode; - if (!rc_protocols_enabled(dev, RC_BIT_SHARP)) + if (!(dev->enabled_protocols & RC_BIT_SHARP)) return 0; if (!is_timing_event(ev)) { diff --git a/drivers/media/rc/ir-sony-decoder.c b/drivers/media/rc/ir-sony-decoder.c index f485f9fe1e90..d12dc3da5931 100644 --- a/drivers/media/rc/ir-sony-decoder.c +++ b/drivers/media/rc/ir-sony-decoder.c @@ -46,8 +46,8 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) u32 scancode; u8 device, subdevice, function; - if (!rc_protocols_enabled(dev, RC_BIT_SONY12 | RC_BIT_SONY15 | - RC_BIT_SONY20)) + if (!(dev->enabled_protocols & + (RC_BIT_SONY12 | RC_BIT_SONY15 | RC_BIT_SONY20))) return 0; if (!is_timing_event(ev)) { @@ -125,7 +125,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) switch (data->count) { case 12: - if (!rc_protocols_enabled(dev, RC_BIT_SONY12)) { + if (!(dev->enabled_protocols & RC_BIT_SONY12)) { data->state = STATE_INACTIVE; return 0; } @@ -135,7 +135,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) protocol = RC_TYPE_SONY12; break; case 15: - if (!rc_protocols_enabled(dev, RC_BIT_SONY15)) { + if (!(dev->enabled_protocols & RC_BIT_SONY15)) { data->state = STATE_INACTIVE; return 0; } @@ -145,7 +145,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) protocol = RC_TYPE_SONY15; break; case 20: - if (!rc_protocols_enabled(dev, RC_BIT_SONY20)) { + if (!(dev->enabled_protocols & RC_BIT_SONY20)) { data->state = STATE_INACTIVE; return 0; } diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c index ab24cc6d3655..32fd5f4614c8 100644 --- a/drivers/media/rc/ite-cir.c +++ b/drivers/media/rc/ite-cir.c @@ -1563,7 +1563,7 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id /* set up ir-core props */ rdev->priv = itdev; rdev->driver_type = RC_DRIVER_IR_RAW; - rc_set_allowed_protocols(rdev, RC_BIT_ALL); + rdev->allowed_protocols = RC_BIT_ALL; rdev->open = ite_open; rdev->close = ite_close; rdev->s_idle = ite_s_idle; diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index d5c1df3c9db1..b1be81fc6bd7 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -1219,7 +1219,7 @@ static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir) rc->dev.parent = dev; rc->priv = ir; rc->driver_type = RC_DRIVER_IR_RAW; - rc_set_allowed_protocols(rc, RC_BIT_ALL); + rc->allowed_protocols = RC_BIT_ALL; rc->timeout = MS_TO_NS(100); if (!ir->flags.no_tx) { rc->s_tx_mask = mceusb_set_tx_mask; diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index d244e1a83f43..8c6008f81332 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -1044,7 +1044,7 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) /* Set up the rc device */ rdev->priv = nvt; rdev->driver_type = RC_DRIVER_IR_RAW; - rc_set_allowed_protocols(rdev, RC_BIT_ALL); + rdev->allowed_protocols = RC_BIT_ALL; rdev->open = nvt_open; rdev->close = nvt_close; rdev->tx_ir = nvt_tx_ir; diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c index 0a88e0cf964f..63dace8198b0 100644 --- a/drivers/media/rc/rc-loopback.c +++ b/drivers/media/rc/rc-loopback.c @@ -195,7 +195,7 @@ static int __init loop_init(void) rc->map_name = RC_MAP_EMPTY; rc->priv = &loopdev; rc->driver_type = RC_DRIVER_IR_RAW; - rc_set_allowed_protocols(rc, RC_BIT_ALL); + rc->allowed_protocols = RC_BIT_ALL; rc->timeout = 100 * 1000 * 1000; /* 100 ms */ rc->min_timeout = 1; rc->max_timeout = UINT_MAX; diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index d5bfe6442459..c3fb8f8f192b 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -857,14 +857,14 @@ static ssize_t show_protocols(struct device *device, mutex_lock(&dev->lock); if (fattr->type == RC_FILTER_NORMAL) { - enabled = dev->enabled_protocols[RC_FILTER_NORMAL]; + enabled = dev->enabled_protocols; if (dev->raw) allowed = ir_raw_get_allowed_protocols(); else - allowed = dev->allowed_protocols[RC_FILTER_NORMAL]; + allowed = dev->allowed_protocols; } else { - enabled = dev->enabled_protocols[RC_FILTER_WAKEUP]; - allowed = dev->allowed_protocols[RC_FILTER_WAKEUP]; + enabled = dev->enabled_wakeup_protocols; + allowed = dev->allowed_wakeup_protocols; } mutex_unlock(&dev->lock); @@ -989,15 +989,15 @@ static ssize_t store_protocols(struct device *device, if (fattr->type == RC_FILTER_NORMAL) { IR_dprintk(1, "Normal protocol change requested\n"); - current_protocols = &dev->enabled_protocols[RC_FILTER_NORMAL]; + current_protocols = &dev->enabled_protocols; change_protocol = dev->change_protocol; - filter = &dev->scancode_filters[RC_FILTER_NORMAL]; + filter = &dev->scancode_filter; set_filter = dev->s_filter; } else { IR_dprintk(1, "Wakeup protocol change requested\n"); - current_protocols = &dev->enabled_protocols[RC_FILTER_WAKEUP]; + current_protocols = &dev->enabled_wakeup_protocols; change_protocol = dev->change_wakeup_protocol; - filter = &dev->scancode_filters[RC_FILTER_WAKEUP]; + filter = &dev->scancode_wakeup_filter; set_filter = dev->s_wakeup_filter; } @@ -1085,9 +1085,9 @@ static ssize_t show_filter(struct device *device, return -EINVAL; if (fattr->type == RC_FILTER_NORMAL) - filter = &dev->scancode_filters[RC_FILTER_NORMAL]; + filter = &dev->scancode_filter; else - filter = &dev->scancode_filters[RC_FILTER_WAKEUP]; + filter = &dev->scancode_wakeup_filter; mutex_lock(&dev->lock); if (fattr->mask) @@ -1140,12 +1140,12 @@ static ssize_t store_filter(struct device *device, if (fattr->type == RC_FILTER_NORMAL) { set_filter = dev->s_filter; - enabled_protocols = &dev->enabled_protocols[RC_FILTER_NORMAL]; - filter = &dev->scancode_filters[RC_FILTER_NORMAL]; + enabled_protocols = &dev->enabled_protocols; + filter = &dev->scancode_filter; } else { set_filter = dev->s_wakeup_filter; - enabled_protocols = &dev->enabled_protocols[RC_FILTER_WAKEUP]; - filter = &dev->scancode_filters[RC_FILTER_WAKEUP]; + enabled_protocols = &dev->enabled_wakeup_protocols; + filter = &dev->scancode_wakeup_filter; } if (!set_filter) @@ -1424,7 +1424,7 @@ int rc_register_device(struct rc_dev *dev) rc = dev->change_protocol(dev, &rc_type); if (rc < 0) goto out_raw; - dev->enabled_protocols[RC_FILTER_NORMAL] = rc_type; + dev->enabled_protocols = rc_type; } mutex_unlock(&dev->lock); diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c index 79abbc8d9600..795b394a5d84 100644 --- a/drivers/media/rc/redrat3.c +++ b/drivers/media/rc/redrat3.c @@ -878,7 +878,7 @@ static struct rc_dev *redrat3_init_rc_dev(struct redrat3_dev *rr3) rc->dev.parent = dev; rc->priv = rr3; rc->driver_type = RC_DRIVER_IR_RAW; - rc_set_allowed_protocols(rc, RC_BIT_ALL); + rc->allowed_protocols = RC_BIT_ALL; rc->timeout = US_TO_NS(2750); rc->tx_ir = redrat3_transmit_ir; rc->s_tx_carrier = redrat3_set_tx_carrier; diff --git a/drivers/media/rc/st_rc.c b/drivers/media/rc/st_rc.c index 22e4c1f28ab4..5c151351afa4 100644 --- a/drivers/media/rc/st_rc.c +++ b/drivers/media/rc/st_rc.c @@ -287,7 +287,7 @@ static int st_rc_probe(struct platform_device *pdev) st_rc_hardware_init(rc_dev); rdev->driver_type = RC_DRIVER_IR_RAW; - rc_set_allowed_protocols(rdev, RC_BIT_ALL); + rdev->allowed_protocols = RC_BIT_ALL; /* rx sampling rate is 10Mhz */ rdev->rx_resolution = 100; rdev->timeout = US_TO_NS(MAX_SYMB_TIME); diff --git a/drivers/media/rc/streamzap.c b/drivers/media/rc/streamzap.c index bd5e4ff9e0ba..828bbb886882 100644 --- a/drivers/media/rc/streamzap.c +++ b/drivers/media/rc/streamzap.c @@ -316,7 +316,7 @@ static struct rc_dev *streamzap_init_rc_dev(struct streamzap_ir *sz) rdev->dev.parent = dev; rdev->priv = sz; rdev->driver_type = RC_DRIVER_IR_RAW; - rc_set_allowed_protocols(rdev, RC_BIT_ALL); + rdev->allowed_protocols = RC_BIT_ALL; rdev->driver_name = DRIVER_NAME; rdev->map_name = RC_MAP_STREAMZAP; diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c index 13dcd800eb41..bcee8e1a4e9e 100644 --- a/drivers/media/rc/sunxi-cir.c +++ b/drivers/media/rc/sunxi-cir.c @@ -210,7 +210,7 @@ static int sunxi_ir_probe(struct platform_device *pdev) ir->rc->map_name = ir->map_name ?: RC_MAP_EMPTY; ir->rc->dev.parent = dev; ir->rc->driver_type = RC_DRIVER_IR_RAW; - rc_set_allowed_protocols(ir->rc, RC_BIT_ALL); + ir->rc->allowed_protocols = RC_BIT_ALL; ir->rc->rx_resolution = SUNXI_IR_SAMPLE; ir->rc->timeout = MS_TO_NS(SUNXI_IR_TIMEOUT); ir->rc->driver_name = SUNXI_IR_DEV; diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c index c5be38e2a2fe..bc214e2b3a36 100644 --- a/drivers/media/rc/ttusbir.c +++ b/drivers/media/rc/ttusbir.c @@ -318,7 +318,7 @@ static int ttusbir_probe(struct usb_interface *intf, usb_to_input_id(tt->udev, &rc->input_id); rc->dev.parent = &intf->dev; rc->driver_type = RC_DRIVER_IR_RAW; - rc_set_allowed_protocols(rc, RC_BIT_ALL); + rc->allowed_protocols = RC_BIT_ALL; rc->priv = tt; rc->driver_name = DRIVER_NAME; rc->map_name = RC_MAP_TT_1500; diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c index a8b981f5ce2e..d839f73f6a05 100644 --- a/drivers/media/rc/winbond-cir.c +++ b/drivers/media/rc/winbond-cir.c @@ -1082,7 +1082,7 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) data->dev->dev.parent = &device->dev; data->dev->timeout = MS_TO_NS(100); data->dev->rx_resolution = US_TO_NS(2); - rc_set_allowed_protocols(data->dev, RC_BIT_ALL); + data->dev->allowed_protocols = RC_BIT_ALL; err = rc_register_device(data->dev); if (err) diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index e35580618936..45f5ee9f46fc 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -164,7 +164,7 @@ static int dvb_usbv2_remote_init(struct dvb_usb_device *d) dev->driver_name = (char *) d->props->driver_name; dev->map_name = d->rc.map_name; dev->driver_type = d->rc.driver_type; - rc_set_allowed_protocols(dev, d->rc.allowed_protos); + dev->allowed_protocols = d->rc.allowed_protos; dev->change_protocol = d->rc.change_protocol; dev->priv = d; diff --git a/drivers/media/usb/dvb-usb/dvb-usb-remote.c b/drivers/media/usb/dvb-usb/dvb-usb-remote.c index 4058aea9272f..7b5dae3077f6 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb-remote.c +++ b/drivers/media/usb/dvb-usb/dvb-usb-remote.c @@ -272,7 +272,7 @@ static int rc_core_dvb_usb_remote_init(struct dvb_usb_device *d) dev->driver_name = d->props.rc.core.module_name; dev->map_name = d->props.rc.core.rc_codes; dev->change_protocol = d->props.rc.core.change_protocol; - rc_set_allowed_protocols(dev, d->props.rc.core.allowed_protos); + dev->allowed_protocols = d->props.rc.core.allowed_protos; dev->driver_type = d->props.rc.core.driver_type; usb_to_input_id(d->udev, &dev->input_id); dev->input_name = "IR-receiver inside an USB DVB receiver"; diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 7f06ae59eca6..ed843bd221ea 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -745,7 +745,7 @@ static int em28xx_ir_init(struct em28xx *dev) case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: rc->map_name = RC_MAP_HAUPPAUGE; ir->get_key_i2c = em28xx_get_key_em_haup; - rc_set_allowed_protocols(rc, RC_BIT_RC5); + rc->allowed_protocols = RC_BIT_RC5; break; case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE: rc->map_name = RC_MAP_WINFAST_USBII_DELUXE; @@ -761,7 +761,7 @@ static int em28xx_ir_init(struct em28xx *dev) switch (dev->chip_id) { case CHIP_ID_EM2860: case CHIP_ID_EM2883: - rc_set_allowed_protocols(rc, RC_BIT_RC5 | RC_BIT_NEC); + rc->allowed_protocols = RC_BIT_RC5 | RC_BIT_NEC; ir->get_key = default_polling_getkey; break; case CHIP_ID_EM2884: @@ -769,8 +769,8 @@ static int em28xx_ir_init(struct em28xx *dev) case CHIP_ID_EM28174: case CHIP_ID_EM28178: ir->get_key = em2874_polling_getkey; - rc_set_allowed_protocols(rc, RC_BIT_RC5 | RC_BIT_NEC | - RC_BIT_RC6_0); + rc->allowed_protocols = RC_BIT_RC5 | RC_BIT_NEC | + RC_BIT_RC6_0; break; default: err = -ENODEV; diff --git a/drivers/media/usb/tm6000/tm6000-input.c b/drivers/media/usb/tm6000/tm6000-input.c index 676c0232060e..8a519f51609a 100644 --- a/drivers/media/usb/tm6000/tm6000-input.c +++ b/drivers/media/usb/tm6000/tm6000-input.c @@ -441,7 +441,7 @@ int tm6000_ir_init(struct tm6000_core *dev) ir->rc = rc; /* input setup */ - rc_set_allowed_protocols(rc, RC_BIT_RC5 | RC_BIT_NEC); + rc->allowed_protocols = RC_BIT_RC5 | RC_BIT_NEC; /* Neded, in order to support NEC remotes with 24 or 32 bits */ rc->scanmask = 0xffff; rc->priv = ir; diff --git a/include/media/rc-core.h b/include/media/rc-core.h index 00811c9e22b0..4f9e187d05bf 100644 --- a/include/media/rc-core.h +++ b/include/media/rc-core.h @@ -74,10 +74,12 @@ enum rc_filter_type { * @input_dev: the input child device used to communicate events to userspace * @driver_type: specifies if protocol decoding is done in hardware or software * @idle: used to keep track of RX state - * @allowed_protocols: bitmask with the supported RC_BIT_* protocols for each - * filter type - * @enabled_protocols: bitmask with the enabled RC_BIT_* protocols for each - * filter type + * @allowed_protocols: bitmask with the supported RC_BIT_* protocols + * @enabled_protocols: bitmask with the enabled RC_BIT_* protocols + * @allowed_wakeup_protocols: bitmask with the supported RC_BIT_* wakeup protocols + * @enabled_wakeup_protocols: bitmask with the enabled RC_BIT_* wakeup protocols + * @scancode_filter: scancode filter + * @scancode_wakeup_filter: scancode wakeup filters * @scanmask: some hardware decoders are not capable of providing the full * scancode to the application. As this is a hardware limit, we can't do * anything with it. Yet, as the same keycode table can be used with other @@ -97,7 +99,6 @@ enum rc_filter_type { * @max_timeout: maximum timeout supported by device * @rx_resolution : resolution (in ns) of input sampler * @tx_resolution: resolution (in ns) of output sampler - * @scancode_filters: scancode filters (indexed by enum rc_filter_type) * @change_protocol: allow changing the protocol used on hardware decoders * @change_wakeup_protocol: allow changing the protocol used for wakeup * filtering @@ -132,8 +133,12 @@ struct rc_dev { struct input_dev *input_dev; enum rc_driver_type driver_type; bool idle; - u64 allowed_protocols[RC_FILTER_MAX]; - u64 enabled_protocols[RC_FILTER_MAX]; + u64 allowed_protocols; + u64 enabled_protocols; + u64 allowed_wakeup_protocols; + u64 enabled_wakeup_protocols; + struct rc_scancode_filter scancode_filter; + struct rc_scancode_filter scancode_wakeup_filter; u32 users; u32 scanmask; void *priv; @@ -150,7 +155,6 @@ struct rc_dev { u32 max_timeout; u32 rx_resolution; u32 tx_resolution; - struct rc_scancode_filter scancode_filters[RC_FILTER_MAX]; int (*change_protocol)(struct rc_dev *dev, u64 *rc_type); int (*change_wakeup_protocol)(struct rc_dev *dev, u64 *rc_type); int (*open)(struct rc_dev *dev); @@ -171,42 +175,6 @@ struct rc_dev { #define to_rc_dev(d) container_of(d, struct rc_dev, dev) -static inline bool rc_protocols_allowed(struct rc_dev *rdev, u64 protos) -{ - return rdev->allowed_protocols[RC_FILTER_NORMAL] & protos; -} - -/* should be called prior to registration or with mutex held */ -static inline void rc_set_allowed_protocols(struct rc_dev *rdev, u64 protos) -{ - rdev->allowed_protocols[RC_FILTER_NORMAL] = protos; -} - -static inline bool rc_protocols_enabled(struct rc_dev *rdev, u64 protos) -{ - return rdev->enabled_protocols[RC_FILTER_NORMAL] & protos; -} - -/* should be called prior to registration or with mutex held */ -static inline void rc_set_enabled_protocols(struct rc_dev *rdev, u64 protos) -{ - rdev->enabled_protocols[RC_FILTER_NORMAL] = protos; -} - -/* should be called prior to registration or with mutex held */ -static inline void rc_set_allowed_wakeup_protocols(struct rc_dev *rdev, - u64 protos) -{ - rdev->allowed_protocols[RC_FILTER_WAKEUP] = protos; -} - -/* should be called prior to registration or with mutex held */ -static inline void rc_set_enabled_wakeup_protocols(struct rc_dev *rdev, - u64 protos) -{ - rdev->enabled_protocols[RC_FILTER_WAKEUP] = protos; -} - /* * From rc-main.c * Those functions can be used on any type of Remote Controller. They -- cgit v1.2.3 From 9d2f1d3cdedd4d3efff8d14f1f49cf73e2f5cc36 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Thu, 3 Apr 2014 20:32:26 -0300 Subject: [media] rc-core: rename dev->scanmask to dev->scancode_mask MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We already have dev->scancode_filter and dev->scancode_wakeup_filter so rename dev->scanmask to dev->scancode_mask for consistency. Signed-off-by: David Härdeman Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx88/cx88-input.c | 2 +- drivers/media/pci/ttpci/budget-ci.c | 2 +- drivers/media/rc/rc-main.c | 4 ++-- drivers/media/usb/cx231xx/cx231xx-input.c | 2 +- drivers/media/usb/tm6000/tm6000-input.c | 2 +- include/media/rc-core.h | 5 +++-- 6 files changed, 9 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c index 93ff6a7f2354..3f1342c98b46 100644 --- a/drivers/media/pci/cx88/cx88-input.c +++ b/drivers/media/pci/cx88/cx88-input.c @@ -478,7 +478,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) dev->priv = core; dev->open = cx88_ir_open; dev->close = cx88_ir_close; - dev->scanmask = hardware_mask; + dev->scancode_mask = hardware_mask; if (ir->sampling) { dev->driver_type = RC_DRIVER_IR_RAW; diff --git a/drivers/media/pci/ttpci/budget-ci.c b/drivers/media/pci/ttpci/budget-ci.c index 41ce7dec60e6..1feeeff3681b 100644 --- a/drivers/media/pci/ttpci/budget-ci.c +++ b/drivers/media/pci/ttpci/budget-ci.c @@ -234,7 +234,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci) break; } if (!budget_ci->ir.full_rc5) - dev->scanmask = 0xff; + dev->scancode_mask = 0xff; error = rc_register_device(dev); if (error) { diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index c3fb8f8f192b..39e4b5ffe141 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -285,8 +285,8 @@ static unsigned int ir_establish_scancode(struct rc_dev *dev, * IR tables from other remotes. So, we support specifying a mask to * indicate the valid bits of the scancodes. */ - if (dev->scanmask) - scancode &= dev->scanmask; + if (dev->scancode_mask) + scancode &= dev->scancode_mask; /* First check if we already have a mapping for this ir command */ for (i = 0; i < rc_map->len; i++) { diff --git a/drivers/media/usb/cx231xx/cx231xx-input.c b/drivers/media/usb/cx231xx/cx231xx-input.c index adcdd92179b2..05f0434919d4 100644 --- a/drivers/media/usb/cx231xx/cx231xx-input.c +++ b/drivers/media/usb/cx231xx/cx231xx-input.c @@ -91,7 +91,7 @@ int cx231xx_ir_init(struct cx231xx *dev) dev->init_data.get_key = get_key_isdbt; dev->init_data.ir_codes = cx231xx_boards[dev->model].rc_map_name; /* The i2c micro-controller only outputs the cmd part of NEC protocol */ - dev->init_data.rc_dev->scanmask = 0xff; + dev->init_data.rc_dev->scancode_mask = 0xff; dev->init_data.rc_dev->driver_name = "cx231xx"; dev->init_data.type = RC_BIT_NEC; info.addr = 0x30; diff --git a/drivers/media/usb/tm6000/tm6000-input.c b/drivers/media/usb/tm6000/tm6000-input.c index 8a519f51609a..26b2ebb62547 100644 --- a/drivers/media/usb/tm6000/tm6000-input.c +++ b/drivers/media/usb/tm6000/tm6000-input.c @@ -443,7 +443,7 @@ int tm6000_ir_init(struct tm6000_core *dev) /* input setup */ rc->allowed_protocols = RC_BIT_RC5 | RC_BIT_NEC; /* Neded, in order to support NEC remotes with 24 or 32 bits */ - rc->scanmask = 0xffff; + rc->scancode_mask = 0xffff; rc->priv = ir; rc->change_protocol = tm6000_ir_change_protocol; if (dev->int_in.endp) { diff --git a/include/media/rc-core.h b/include/media/rc-core.h index 4f9e187d05bf..3047837db1cc 100644 --- a/include/media/rc-core.h +++ b/include/media/rc-core.h @@ -80,11 +80,12 @@ enum rc_filter_type { * @enabled_wakeup_protocols: bitmask with the enabled RC_BIT_* wakeup protocols * @scancode_filter: scancode filter * @scancode_wakeup_filter: scancode wakeup filters - * @scanmask: some hardware decoders are not capable of providing the full + * @scancode_mask: some hardware decoders are not capable of providing the full * scancode to the application. As this is a hardware limit, we can't do * anything with it. Yet, as the same keycode table can be used with other * devices, a mask is provided to allow its usage. Drivers should generally * leave this field in blank + * @users: number of current users of the device * @priv: driver-specific data * @keylock: protects the remaining members of the struct * @keypressed: whether a key is currently pressed @@ -139,8 +140,8 @@ struct rc_dev { u64 enabled_wakeup_protocols; struct rc_scancode_filter scancode_filter; struct rc_scancode_filter scancode_wakeup_filter; + u32 scancode_mask; u32 users; - u32 scanmask; void *priv; spinlock_t keylock; bool keypressed; -- cgit v1.2.3 From 86be408bfbd846fab3c4ac21d6f9298bd2e4b790 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 18 Jun 2014 17:29:32 +0200 Subject: clk: Support for clock parents and rates assigned from device tree This patch adds helper functions to configure clock parents and rates as specified through 'assigned-clock-parents', 'assigned-clock-rates' DT properties for a clock provider or clock consumer device. The helpers are now being called by the bus code for the platform, I2C and SPI busses, before the driver probing and also in the clock core after registration of a clock provider. Signed-off-by: Sylwester Nawrocki Acked-by: Kyungmin Park Signed-off-by: Mike Turquette --- .../devicetree/bindings/clock/clock-bindings.txt | 36 ++++++ drivers/base/platform.c | 5 + drivers/clk/Makefile | 3 + drivers/clk/clk-conf.c | 143 +++++++++++++++++++++ drivers/clk/clk.c | 12 +- drivers/i2c/i2c-core.c | 5 + drivers/spi/spi.c | 5 + include/linux/clk/clk-conf.h | 20 +++ 8 files changed, 227 insertions(+), 2 deletions(-) create mode 100644 drivers/clk/clk-conf.c create mode 100644 include/linux/clk/clk-conf.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/clock-bindings.txt b/Documentation/devicetree/bindings/clock/clock-bindings.txt index f15787817d6b..06fc6d541c89 100644 --- a/Documentation/devicetree/bindings/clock/clock-bindings.txt +++ b/Documentation/devicetree/bindings/clock/clock-bindings.txt @@ -131,3 +131,39 @@ clock signal, and a UART. ("pll" and "pll-switched"). * The UART has its baud clock connected the external oscillator and its register clock connected to the PLL clock (the "pll-switched" signal) + +==Assigned clock parents and rates== + +Some platforms may require initial configuration of default parent clocks +and clock frequencies. Such a configuration can be specified in a device tree +node through assigned-clocks, assigned-clock-parents and assigned-clock-rates +properties. The assigned-clock-parents property should contain a list of parent +clocks in form of phandle and clock specifier pairs, the assigned-clock-parents +property the list of assigned clock frequency values - corresponding to clocks +listed in the assigned-clocks property. + +To skip setting parent or rate of a clock its corresponding entry should be +set to 0, or can be omitted if it is not followed by any non-zero entry. + + uart@a000 { + compatible = "fsl,imx-uart"; + reg = <0xa000 0x1000>; + ... + clocks = <&osc 0>, <&pll 1>; + clock-names = "baud", "register"; + + assigned-clocks = <&clkcon 0>, <&pll 2>; + assigned-clock-parents = <&pll 2>; + assigned-clock-rates = <0>, <460800>; + }; + +In this example the <&pll 2> clock is set as parent of clock <&clkcon 0> and +the <&pll 2> clock is assigned a frequency value of 460800 Hz. + +Configuring a clock's parent and rate through the device node that consumes +the clock can be done only for clocks that have a single user. Specifying +conflicting parent or rate configuration in multiple consumer nodes for +a shared clock is forbidden. + +Configuration of common clocks, which affect multiple consumer devices can +be similarly specified in the clock provider node. diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 9e9227e1762d..ac47643b1b69 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "base.h" #include "power/power.h" @@ -489,6 +490,10 @@ static int platform_drv_probe(struct device *_dev) struct platform_device *dev = to_platform_device(_dev); int ret; + ret = of_clk_set_defaults(_dev->of_node, false); + if (ret < 0) + return ret; + acpi_dev_pm_attach(_dev, true); ret = drv->probe(dev); diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 312742c10661..d5d325f28016 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -9,6 +9,9 @@ obj-$(CONFIG_COMMON_CLK) += clk-gate.o obj-$(CONFIG_COMMON_CLK) += clk-mux.o obj-$(CONFIG_COMMON_CLK) += clk-composite.o obj-$(CONFIG_COMMON_CLK) += clk-fractional-divider.o +ifeq ($(CONFIG_OF), y) +obj-$(CONFIG_COMMON_CLK) += clk-conf.o +endif # hardware specific clock types # please keep this section sorted lexicographically by file/directory path name diff --git a/drivers/clk/clk-conf.c b/drivers/clk/clk-conf.c new file mode 100644 index 000000000000..1f73019a27c8 --- /dev/null +++ b/drivers/clk/clk-conf.c @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2014 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki + * + * 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 "clk.h" + +static int __set_clk_parents(struct device_node *node, bool clk_supplier) +{ + struct of_phandle_args clkspec; + int index, rc, num_parents; + struct clk *clk, *pclk; + + num_parents = of_count_phandle_with_args(node, "assigned-clock-parents", + "#clock-cells"); + if (num_parents == -EINVAL) + pr_err("clk: invalid value of clock-parents property at %s\n", + node->full_name); + + for (index = 0; index < num_parents; index++) { + rc = of_parse_phandle_with_args(node, "assigned-clock-parents", + "#clock-cells", index, &clkspec); + if (rc < 0) { + /* skip empty (null) phandles */ + if (rc == -ENOENT) + continue; + else + return rc; + } + if (clkspec.np == node && !clk_supplier) + return 0; + pclk = of_clk_get_by_clkspec(&clkspec); + if (IS_ERR(pclk)) { + pr_warn("clk: couldn't get parent clock %d for %s\n", + index, node->full_name); + return PTR_ERR(pclk); + } + + rc = of_parse_phandle_with_args(node, "assigned-clocks", + "#clock-cells", index, &clkspec); + if (rc < 0) + goto err; + if (clkspec.np == node && !clk_supplier) { + rc = 0; + goto err; + } + clk = of_clk_get_by_clkspec(&clkspec); + if (IS_ERR(pclk)) { + pr_warn("clk: couldn't get parent clock %d for %s\n", + index, node->full_name); + rc = PTR_ERR(pclk); + goto err; + } + + rc = clk_set_parent(clk, pclk); + if (rc < 0) + pr_err("clk: failed to reparent %s to %s: %d\n", + __clk_get_name(clk), __clk_get_name(pclk), rc); + clk_put(clk); + clk_put(pclk); + } + return 0; +err: + clk_put(pclk); + return rc; +} + +static int __set_clk_rates(struct device_node *node, bool clk_supplier) +{ + struct of_phandle_args clkspec; + struct property *prop; + const __be32 *cur; + int rc, index = 0; + struct clk *clk; + u32 rate; + + of_property_for_each_u32(node, "assigned-clock-rates", prop, cur, rate) { + if (rate) { + rc = of_parse_phandle_with_args(node, "assigned-clocks", + "#clock-cells", index, &clkspec); + if (rc < 0) { + /* skip empty (null) phandles */ + if (rc == -ENOENT) + continue; + else + return rc; + } + if (clkspec.np == node && !clk_supplier) + return 0; + + clk = of_clk_get_by_clkspec(&clkspec); + if (IS_ERR(clk)) { + pr_warn("clk: couldn't get clock %d for %s\n", + index, node->full_name); + return PTR_ERR(clk); + } + + rc = clk_set_rate(clk, rate); + if (rc < 0) + pr_err("clk: couldn't set %s clock rate: %d\n", + __clk_get_name(clk), rc); + clk_put(clk); + } + index++; + } + return 0; +} + +/** + * of_clk_set_defaults() - parse and set assigned clocks configuration + * @node: device node to apply clock settings for + * @clk_supplier: true if clocks supplied by @node should also be considered + * + * This function parses 'assigned-{clocks/clock-parents/clock-rates}' properties + * and sets any specified clock parents and rates. The @clk_supplier argument + * should be set to true if @node may be also a clock supplier of any clock + * listed in its 'assigned-clocks' or 'assigned-clock-parents' properties. + * If @clk_supplier is false the function exits returnning 0 as soon as it + * determines the @node is also a supplier of any of the clocks. + */ +int of_clk_set_defaults(struct device_node *node, bool clk_supplier) +{ + int rc; + + if (!node) + return 0; + + rc = __set_clk_parents(node, clk_supplier); + if (rc < 0) + return rc; + + return __set_clk_rates(node, clk_supplier); +} diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 9ad397050471..f95590a1e28e 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -10,6 +10,7 @@ */ #include +#include #include #include #include @@ -2382,6 +2383,7 @@ int of_clk_add_provider(struct device_node *np, void *data) { struct of_clk_provider *cp; + int ret; cp = kzalloc(sizeof(struct of_clk_provider), GFP_KERNEL); if (!cp) @@ -2396,7 +2398,11 @@ int of_clk_add_provider(struct device_node *np, mutex_unlock(&of_clk_mutex); pr_debug("Added clock from %s\n", np->full_name); - return 0; + ret = of_clk_set_defaults(np, true); + if (ret < 0) + of_clk_del_provider(np); + + return ret; } EXPORT_SYMBOL_GPL(of_clk_add_provider); @@ -2573,7 +2579,10 @@ void __init of_clk_init(const struct of_device_id *matches) list_for_each_entry_safe(clk_provider, next, &clk_provider_list, node) { if (force || parent_ready(clk_provider->np)) { + clk_provider->clk_init_cb(clk_provider->np); + of_clk_set_defaults(clk_provider->np, true); + list_del(&clk_provider->node); kfree(clk_provider); is_init_done = true; @@ -2588,7 +2597,6 @@ void __init of_clk_init(const struct of_device_id *matches) */ if (!is_init_done) force = true; - } } #endif diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 7c7f4b856bad..66aa83b99383 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -274,6 +275,10 @@ static int i2c_device_probe(struct device *dev) client->flags & I2C_CLIENT_WAKE); dev_dbg(dev, "probe\n"); + status = of_clk_set_defaults(dev->of_node, false); + if (status < 0) + return status; + acpi_dev_pm_attach(&client->dev, true); status = driver->probe(client, i2c_match_id(driver->id_table, client)); if (status) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index d4f9670b51bc..22aa41cace82 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -259,6 +260,10 @@ static int spi_drv_probe(struct device *dev) const struct spi_driver *sdrv = to_spi_driver(dev->driver); int ret; + ret = of_clk_set_defaults(dev->of_node, false); + if (ret) + return ret; + acpi_dev_pm_attach(dev, true); ret = sdrv->probe(to_spi_device(dev)); if (ret) diff --git a/include/linux/clk/clk-conf.h b/include/linux/clk/clk-conf.h new file mode 100644 index 000000000000..f3050e15f833 --- /dev/null +++ b/include/linux/clk/clk-conf.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2014 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki + * + * 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 device_node; + +#if defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK) +int of_clk_set_defaults(struct device_node *node, bool clk_supplier); +#else +static inline int of_clk_set_defaults(struct device_node *node, + bool clk_supplier) +{ + return 0; +} +#endif -- cgit v1.2.3 From 5d0360a4f027576e5419d4a7c711c9ca0f1be8ca Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jul 2014 10:45:42 -0300 Subject: [media] v4l2-ctrls: add support for setting string controls Rather than always having to use a v4l2_ext_control struct to set a control value from within a driver, switch to just setting the new value. This is faster and it makes it possible to set more complex types such as a string control as is added by this patch. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 47 +++++++++++++++--------------------- include/media/v4l2-ctrls.h | 24 ++++++++++++++++++ 2 files changed, 44 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 004e7e82e1e0..5db038525f99 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -3121,26 +3121,22 @@ static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, struct v4l2_ctrl *master = ctrl->cluster[0]; int i; - /* Compound controls are not supported. The user_to_new() and - * cur_to_user() calls below would need to be modified not to access - * userspace memory when called from set_ctrl(). - */ - if (ctrl->is_ptr) - return -EINVAL; - /* Reset the 'is_new' flags of the cluster */ for (i = 0; i < master->ncontrols; i++) if (master->cluster[i]) master->cluster[i]->is_new = 0; + if (c) + user_to_new(c, ctrl); + /* For autoclusters with volatiles that are switched from auto to manual mode we have to update the current volatile values since those will become the initial manual values after such a switch. */ if (master->is_auto && master->has_volatiles && ctrl == master && - !is_cur_manual(master) && c->value == master->manual_mode_value) + !is_cur_manual(master) && ctrl->val == master->manual_mode_value) update_from_auto_cluster(master); - user_to_new(c, ctrl); + ctrl->is_new = 1; return try_or_set_cluster(fh, master, true, ch_flags); } @@ -3188,40 +3184,37 @@ EXPORT_SYMBOL(v4l2_subdev_s_ctrl); int __v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val) { - struct v4l2_ext_control c; - int rval; - lockdep_assert_held(ctrl->handler->lock); /* It's a driver bug if this happens. */ WARN_ON(!ctrl->is_int); - c.value = val; - rval = set_ctrl(NULL, ctrl, &c, 0); - if (!rval) - cur_to_user(&c, ctrl); - - return rval; + ctrl->val = val; + return set_ctrl(NULL, ctrl, NULL, 0); } EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl); int __v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val) { - struct v4l2_ext_control c; - int rval; - lockdep_assert_held(ctrl->handler->lock); /* It's a driver bug if this happens. */ WARN_ON(ctrl->is_ptr || ctrl->type != V4L2_CTRL_TYPE_INTEGER64); - c.value64 = val; - rval = set_ctrl(NULL, ctrl, &c, 0); - if (!rval) - cur_to_user(&c, ctrl); - - return rval; + *ctrl->p_new.p_s64 = val; + return set_ctrl(NULL, ctrl, NULL, 0); } EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_int64); +int __v4l2_ctrl_s_ctrl_string(struct v4l2_ctrl *ctrl, const char *s) +{ + lockdep_assert_held(ctrl->handler->lock); + + /* It's a driver bug if this happens. */ + WARN_ON(ctrl->type != V4L2_CTRL_TYPE_STRING); + strlcpy(ctrl->p_new.p_char, s, ctrl->maximum + 1); + return set_ctrl(NULL, ctrl, NULL, 0); +} +EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_string); + void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void *priv) { if (ctrl == NULL) diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 8c4edd69fa4b..d6540d201764 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -737,6 +737,30 @@ static inline int v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val) return rval; } +/** __v4l2_ctrl_s_ctrl_string() - Unlocked variant of v4l2_ctrl_s_ctrl_string(). */ +int __v4l2_ctrl_s_ctrl_string(struct v4l2_ctrl *ctrl, const char *s); + +/** v4l2_ctrl_s_ctrl_string() - Helper function to set a control's string value from within a driver. + * @ctrl: The control. + * @s: The new string. + * + * This set the control's new string safely by going through the control + * framework. This function will lock the control's handler, so it cannot be + * used from within the &v4l2_ctrl_ops functions. + * + * This function is for string type controls only. + */ +static inline int v4l2_ctrl_s_ctrl_string(struct v4l2_ctrl *ctrl, const char *s) +{ + int rval; + + v4l2_ctrl_lock(ctrl); + rval = __v4l2_ctrl_s_ctrl_string(ctrl, s); + v4l2_ctrl_unlock(ctrl); + + return rval; +} + /* Internal helper functions that deal with control events. */ extern const struct v4l2_subscribed_event_ops v4l2_ctrl_sub_ev_ops; void v4l2_ctrl_replace(struct v4l2_event *old, const struct v4l2_event *new); -- cgit v1.2.3 From ccf58cb4eac050ebe777cfcb209bc53d58f5fd79 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 23 Jul 2014 03:17:06 -0300 Subject: [media] vb2: fix videobuf2-core.h comments A lot of work was done in vb2 to regulate how drivers and the vb2 core handle buffer ownership, but inexplicably the videobuf2-core.h comments were never updated. Do so now. The same was true for the replacement of the -ENOBUFS mechanism by the min_buffers_needed field. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/videobuf2-core.h | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 1a262aeb1741..fc910a622451 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -294,15 +294,19 @@ struct vb2_buffer { * of already queued buffers in count parameter; driver * can return an error if hardware fails, in that case all * buffers that have been already given by the @buf_queue - * callback are invalidated. - * If there were not enough queued buffers to start - * streaming, then this callback returns -ENOBUFS, and the - * vb2 core will retry calling @start_streaming when a new - * buffer is queued. + * callback are to be returned by the driver by calling + * @vb2_buffer_done(VB2_BUF_STATE_DEQUEUED). + * If you need a minimum number of buffers before you can + * start streaming, then set @min_buffers_needed in the + * vb2_queue structure. If that is non-zero then + * start_streaming won't be called until at least that + * many buffers have been queued up by userspace. * @stop_streaming: called when 'streaming' state must be disabled; driver * should stop any DMA transactions or wait until they * finish and give back all buffers it got from buf_queue() - * callback; may use vb2_wait_for_all_buffers() function + * callback by calling @vb2_buffer_done() with either + * VB2_BUF_STATE_DONE or VB2_BUF_STATE_ERROR; may use + * vb2_wait_for_all_buffers() function * @buf_queue: passes buffer vb to the driver; driver may start * hardware operation on this buffer; driver should give * the buffer back by calling vb2_buffer_done() function; -- cgit v1.2.3 From 811c508104d092683ea5fe86cdcfd23dded22934 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jul 2014 10:45:37 -0300 Subject: [media] v4l2-ctrls: add new RDS TX controls The si4713 supports several RDS features not yet implemented in the driver. This patch adds the missing RDS functionality to the list of RDS controls. The ALT_FREQS control is a compound control containing an array of up to 25 (the maximum according to the RDS standard) frequencies. To support that the V4L2_CTRL_TYPE_U32 was added. Signed-off-by: Hans Verkuil Cc: Eduardo Valentin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 35 +++++++++++++++++++++++++++++++++++ include/media/v4l2-ctrls.h | 2 ++ include/uapi/linux/v4l2-controls.h | 9 +++++++++ include/uapi/linux/videodev2.h | 2 ++ 4 files changed, 48 insertions(+) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 5db038525f99..4863cf05175d 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -805,6 +805,15 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_RDS_TX_PTY: return "RDS Program Type"; case V4L2_CID_RDS_TX_PS_NAME: return "RDS PS Name"; case V4L2_CID_RDS_TX_RADIO_TEXT: return "RDS Radio Text"; + case V4L2_CID_RDS_TX_MONO_STEREO: return "RDS Stereo"; + case V4L2_CID_RDS_TX_ARTIFICIAL_HEAD: return "RDS Artificial Head"; + case V4L2_CID_RDS_TX_COMPRESSED: return "RDS Compressed"; + case V4L2_CID_RDS_TX_DYNAMIC_PTY: return "RDS Dynamic PTY"; + case V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT: return "RDS Traffic Announcement"; + case V4L2_CID_RDS_TX_TRAFFIC_PROGRAM: return "RDS Traffic Program"; + case V4L2_CID_RDS_TX_MUSIC_SPEECH: return "RDS Music"; + case V4L2_CID_RDS_TX_ALT_FREQS_ENABLE: return "RDS Enable Alt Frequencies"; + case V4L2_CID_RDS_TX_ALT_FREQS: return "RDS Alternate Frequencies"; case V4L2_CID_AUDIO_LIMITER_ENABLED: return "Audio Limiter Feature Enabled"; case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: return "Audio Limiter Release Time"; case V4L2_CID_AUDIO_LIMITER_DEVIATION: return "Audio Limiter Deviation"; @@ -946,6 +955,14 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_RF_TUNER_IF_GAIN_AUTO: case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO: case V4L2_CID_RF_TUNER_PLL_LOCK: + case V4L2_CID_RDS_TX_MONO_STEREO: + case V4L2_CID_RDS_TX_ARTIFICIAL_HEAD: + case V4L2_CID_RDS_TX_COMPRESSED: + case V4L2_CID_RDS_TX_DYNAMIC_PTY: + case V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT: + case V4L2_CID_RDS_TX_TRAFFIC_PROGRAM: + case V4L2_CID_RDS_TX_MUSIC_SPEECH: + case V4L2_CID_RDS_TX_ALT_FREQS_ENABLE: *type = V4L2_CTRL_TYPE_BOOLEAN; *min = 0; *max = *step = 1; @@ -1089,6 +1106,9 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_DETECT_MD_THRESHOLD_GRID: *type = V4L2_CTRL_TYPE_U16; break; + case V4L2_CID_RDS_TX_ALT_FREQS: + *type = V4L2_CTRL_TYPE_U32; + break; default: *type = V4L2_CTRL_TYPE_INTEGER; break; @@ -1209,6 +1229,8 @@ static bool std_equal(const struct v4l2_ctrl *ctrl, u32 idx, return ptr1.p_u8[idx] == ptr2.p_u8[idx]; case V4L2_CTRL_TYPE_U16: return ptr1.p_u16[idx] == ptr2.p_u16[idx]; + case V4L2_CTRL_TYPE_U32: + return ptr1.p_u32[idx] == ptr2.p_u32[idx]; default: if (ctrl->is_int) return ptr1.p_s32[idx] == ptr2.p_s32[idx]; @@ -1242,6 +1264,9 @@ static void std_init(const struct v4l2_ctrl *ctrl, u32 idx, case V4L2_CTRL_TYPE_U16: ptr.p_u16[idx] = ctrl->default_value; break; + case V4L2_CTRL_TYPE_U32: + ptr.p_u32[idx] = ctrl->default_value; + break; default: idx *= ctrl->elem_size; memset(ptr.p + idx, 0, ctrl->elem_size); @@ -1289,6 +1314,9 @@ static void std_log(const struct v4l2_ctrl *ctrl) case V4L2_CTRL_TYPE_U16: pr_cont("%u", (unsigned)*ptr.p_u16); break; + case V4L2_CTRL_TYPE_U32: + pr_cont("%u", (unsigned)*ptr.p_u32); + break; default: pr_cont("unknown type %d", ctrl->type); break; @@ -1346,6 +1374,8 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, return ROUND_TO_RANGE(ptr.p_u8[idx], u8, ctrl); case V4L2_CTRL_TYPE_U16: return ROUND_TO_RANGE(ptr.p_u16[idx], u16, ctrl); + case V4L2_CTRL_TYPE_U32: + return ROUND_TO_RANGE(ptr.p_u32[idx], u32, ctrl); case V4L2_CTRL_TYPE_BOOLEAN: ptr.p_s32[idx] = !!ptr.p_s32[idx]; @@ -1578,6 +1608,7 @@ static int check_range(enum v4l2_ctrl_type type, /* fall through */ case V4L2_CTRL_TYPE_U8: case V4L2_CTRL_TYPE_U16: + case V4L2_CTRL_TYPE_U32: case V4L2_CTRL_TYPE_INTEGER: case V4L2_CTRL_TYPE_INTEGER64: if (step == 0 || min > max || def < min || def > max) @@ -1893,6 +1924,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, case V4L2_CTRL_TYPE_U16: elem_size = sizeof(u16); break; + case V4L2_CTRL_TYPE_U32: + elem_size = sizeof(u32); + break; default: if (type < V4L2_CTRL_COMPOUND_TYPES) elem_size = sizeof(s32); @@ -3248,6 +3282,7 @@ int __v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, case V4L2_CTRL_TYPE_BITMASK: case V4L2_CTRL_TYPE_U8: case V4L2_CTRL_TYPE_U16: + case V4L2_CTRL_TYPE_U32: if (ctrl->is_array) return -EINVAL; ret = check_range(ctrl->type, min, max, step, def); diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index d6540d201764..b7cd7a665e35 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -41,6 +41,7 @@ struct poll_table_struct; * @p_s64: Pointer to a 64-bit signed value. * @p_u8: Pointer to a 8-bit unsigned value. * @p_u16: Pointer to a 16-bit unsigned value. + * @p_u32: Pointer to a 32-bit unsigned value. * @p_char: Pointer to a string. * @p: Pointer to a compound value. */ @@ -49,6 +50,7 @@ union v4l2_ctrl_ptr { s64 *p_s64; u8 *p_u8; u16 *p_u16; + u32 *p_u32; char *p_char; void *p; }; diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index db526d1c8309..a13e6fef987e 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -757,6 +757,15 @@ enum v4l2_auto_focus_range { #define V4L2_CID_RDS_TX_PTY (V4L2_CID_FM_TX_CLASS_BASE + 3) #define V4L2_CID_RDS_TX_PS_NAME (V4L2_CID_FM_TX_CLASS_BASE + 5) #define V4L2_CID_RDS_TX_RADIO_TEXT (V4L2_CID_FM_TX_CLASS_BASE + 6) +#define V4L2_CID_RDS_TX_MONO_STEREO (V4L2_CID_FM_TX_CLASS_BASE + 7) +#define V4L2_CID_RDS_TX_ARTIFICIAL_HEAD (V4L2_CID_FM_TX_CLASS_BASE + 8) +#define V4L2_CID_RDS_TX_COMPRESSED (V4L2_CID_FM_TX_CLASS_BASE + 9) +#define V4L2_CID_RDS_TX_DYNAMIC_PTY (V4L2_CID_FM_TX_CLASS_BASE + 10) +#define V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT (V4L2_CID_FM_TX_CLASS_BASE + 11) +#define V4L2_CID_RDS_TX_TRAFFIC_PROGRAM (V4L2_CID_FM_TX_CLASS_BASE + 12) +#define V4L2_CID_RDS_TX_MUSIC_SPEECH (V4L2_CID_FM_TX_CLASS_BASE + 13) +#define V4L2_CID_RDS_TX_ALT_FREQS_ENABLE (V4L2_CID_FM_TX_CLASS_BASE + 14) +#define V4L2_CID_RDS_TX_ALT_FREQS (V4L2_CID_FM_TX_CLASS_BASE + 15) #define V4L2_CID_AUDIO_LIMITER_ENABLED (V4L2_CID_FM_TX_CLASS_BASE + 64) #define V4L2_CID_AUDIO_LIMITER_RELEASE_TIME (V4L2_CID_FM_TX_CLASS_BASE + 65) diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 5fd42027877a..778a3298fb34 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1288,6 +1288,7 @@ struct v4l2_ext_control { char *string; __u8 *p_u8; __u16 *p_u16; + __u32 *p_u32; void *ptr; }; } __attribute__ ((packed)); @@ -1320,6 +1321,7 @@ enum v4l2_ctrl_type { V4L2_CTRL_COMPOUND_TYPES = 0x0100, V4L2_CTRL_TYPE_U8 = 0x0100, V4L2_CTRL_TYPE_U16 = 0x0101, + V4L2_CTRL_TYPE_U32 = 0x0102, }; /* Used in the VIDIOC_QUERYCTRL ioctl for querying controls */ -- cgit v1.2.3 From 9570a14847a9d334a0baf5a5921da2dbc6b9f6d2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jul 2014 10:45:40 -0300 Subject: [media] v4l2-ctrls: add RX RDS controls The radio-miropcm20 driver has firmware that decodes the RDS signals. So in that case the RDS data becomes available in the form of controls. Add support for these controls to the control framework, allowing the miro driver to use them. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 19 +++++++++++++++++-- include/uapi/linux/v4l2-controls.h | 6 ++++++ 2 files changed, 23 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 4863cf05175d..2d8ced8ad80b 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -881,7 +881,6 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_FM_RX_CLASS: return "FM Radio Receiver Controls"; case V4L2_CID_TUNE_DEEMPHASIS: return "De-Emphasis"; case V4L2_CID_RDS_RECEPTION: return "RDS Reception"; - case V4L2_CID_RF_TUNER_CLASS: return "RF Tuner Controls"; case V4L2_CID_RF_TUNER_LNA_GAIN_AUTO: return "LNA Gain, Auto"; case V4L2_CID_RF_TUNER_LNA_GAIN: return "LNA Gain"; @@ -892,6 +891,12 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO: return "Bandwidth, Auto"; case V4L2_CID_RF_TUNER_BANDWIDTH: return "Bandwidth"; case V4L2_CID_RF_TUNER_PLL_LOCK: return "PLL Lock"; + case V4L2_CID_RDS_RX_PTY: return "RDS Program Type"; + case V4L2_CID_RDS_RX_PS_NAME: return "RDS PS Name"; + case V4L2_CID_RDS_RX_RADIO_TEXT: return "RDS Radio Text"; + case V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT: return "RDS Traffic Announcement"; + case V4L2_CID_RDS_RX_TRAFFIC_PROGRAM: return "RDS Traffic Program"; + case V4L2_CID_RDS_RX_MUSIC_SPEECH: return "RDS Music"; /* Detection controls */ /* Keep the order of the 'case's the same as in v4l2-controls.h! */ @@ -900,7 +905,6 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD: return "MD Global Threshold"; case V4L2_CID_DETECT_MD_THRESHOLD_GRID: return "MD Threshold Grid"; case V4L2_CID_DETECT_MD_REGION_GRID: return "MD Region Grid"; - default: return NULL; } @@ -963,6 +967,9 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_RDS_TX_TRAFFIC_PROGRAM: case V4L2_CID_RDS_TX_MUSIC_SPEECH: case V4L2_CID_RDS_TX_ALT_FREQS_ENABLE: + case V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT: + case V4L2_CID_RDS_RX_TRAFFIC_PROGRAM: + case V4L2_CID_RDS_RX_MUSIC_SPEECH: *type = V4L2_CTRL_TYPE_BOOLEAN; *min = 0; *max = *step = 1; @@ -1035,6 +1042,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, break; case V4L2_CID_RDS_TX_PS_NAME: case V4L2_CID_RDS_TX_RADIO_TEXT: + case V4L2_CID_RDS_RX_PS_NAME: + case V4L2_CID_RDS_RX_RADIO_TEXT: *type = V4L2_CTRL_TYPE_STRING; break; case V4L2_CID_ISO_SENSITIVITY: @@ -1166,6 +1175,12 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_DV_TX_RXSENSE: case V4L2_CID_DV_TX_EDID_PRESENT: case V4L2_CID_DV_RX_POWER_PRESENT: + case V4L2_CID_RDS_RX_PTY: + case V4L2_CID_RDS_RX_PS_NAME: + case V4L2_CID_RDS_RX_RADIO_TEXT: + case V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT: + case V4L2_CID_RDS_RX_TRAFFIC_PROGRAM: + case V4L2_CID_RDS_RX_MUSIC_SPEECH: *flags |= V4L2_CTRL_FLAG_READ_ONLY; break; case V4L2_CID_RF_TUNER_PLL_LOCK: diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index a13e6fef987e..e946e43fb8d5 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -910,6 +910,12 @@ enum v4l2_deemphasis { }; #define V4L2_CID_RDS_RECEPTION (V4L2_CID_FM_RX_CLASS_BASE + 2) +#define V4L2_CID_RDS_RX_PTY (V4L2_CID_FM_RX_CLASS_BASE + 3) +#define V4L2_CID_RDS_RX_PS_NAME (V4L2_CID_FM_RX_CLASS_BASE + 4) +#define V4L2_CID_RDS_RX_RADIO_TEXT (V4L2_CID_FM_RX_CLASS_BASE + 5) +#define V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT (V4L2_CID_FM_RX_CLASS_BASE + 6) +#define V4L2_CID_RDS_RX_TRAFFIC_PROGRAM (V4L2_CID_FM_RX_CLASS_BASE + 7) +#define V4L2_CID_RDS_RX_MUSIC_SPEECH (V4L2_CID_FM_RX_CLASS_BASE + 8) #define V4L2_CID_RF_TUNER_CLASS_BASE (V4L2_CTRL_CLASS_RF_TUNER | 0x900) #define V4L2_CID_RF_TUNER_CLASS (V4L2_CTRL_CLASS_RF_TUNER | 1) -- cgit v1.2.3 From 01f7ec260ab35291f23bf42b1a43367649392646 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Tue, 24 Jun 2014 18:08:25 +0200 Subject: clk: samsung: exynos4: Add CLKOUT clock hierarchy This patch adds definitions of clocks that are used to drive clock output signals of particular CMU sub-blocks that are then fed to PMU and handled by Exynos CLKOUT driver added in further patch. Signed-off-by: Tomasz Figa --- drivers/clk/samsung/clk-exynos4.c | 112 ++++++++++++++++++++++++++++++++++++ include/dt-bindings/clock/exynos4.h | 5 ++ 2 files changed, 117 insertions(+) (limited to 'include') diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 70bca8d81de6..3effe7abb2e2 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -25,10 +25,12 @@ #define DIV_LEFTBUS 0x4500 #define GATE_IP_LEFTBUS 0x4800 #define E4X12_GATE_IP_IMAGE 0x4930 +#define CLKOUT_CMU_LEFTBUS 0x4a00 #define SRC_RIGHTBUS 0x8200 #define DIV_RIGHTBUS 0x8500 #define GATE_IP_RIGHTBUS 0x8800 #define E4X12_GATE_IP_PERIR 0x8960 +#define CLKOUT_CMU_RIGHTBUS 0x8a00 #define EPLL_LOCK 0xc010 #define VPLL_LOCK 0xc020 #define EPLL_CON0 0xc110 @@ -98,6 +100,7 @@ #define GATE_IP_PERIL 0xc950 #define E4210_GATE_IP_PERIR 0xc960 #define GATE_BLOCK 0xc970 +#define CLKOUT_CMU_TOP 0xca00 #define E4X12_MPLL_LOCK 0x10008 #define E4X12_MPLL_CON0 0x10108 #define SRC_DMC 0x10200 @@ -105,6 +108,7 @@ #define DIV_DMC0 0x10500 #define DIV_DMC1 0x10504 #define GATE_IP_DMC 0x10900 +#define CLKOUT_CMU_DMC 0x10a00 #define APLL_LOCK 0x14000 #define E4210_MPLL_LOCK 0x14008 #define APLL_CON0 0x14100 @@ -114,6 +118,7 @@ #define DIV_CPU1 0x14504 #define GATE_SCLK_CPU 0x14800 #define GATE_IP_CPU 0x14900 +#define CLKOUT_CMU_CPU 0x14a00 #define E4X12_DIV_ISP0 0x18300 #define E4X12_DIV_ISP1 0x18304 #define E4X12_GATE_ISP0 0x18800 @@ -242,6 +247,11 @@ static unsigned long exynos4_clk_regs[] __initdata = { DIV_CPU1, GATE_SCLK_CPU, GATE_IP_CPU, + CLKOUT_CMU_LEFTBUS, + CLKOUT_CMU_RIGHTBUS, + CLKOUT_CMU_TOP, + CLKOUT_CMU_DMC, + CLKOUT_CMU_CPU, }; static const struct samsung_clk_reg_dump src_mask_suspend[] = { @@ -400,6 +410,23 @@ PNAME(mout_dac_p4210) = { "sclk_vpll", "sclk_hdmiphy", }; PNAME(mout_pwi_p4210) = { "xxti", "xusbxti", "sclk_hdmi24m", "sclk_usbphy0", "sclk_usbphy1", "sclk_hdmiphy", "none", "sclk_epll", "sclk_vpll" }; +PNAME(clkout_left_p4210) = { "sclk_mpll_div_2", "sclk_apll_div_2", + "div_gdl", "div_gpl" }; +PNAME(clkout_right_p4210) = { "sclk_mpll_div_2", "sclk_apll_div_2", + "div_gdr", "div_gpr" }; +PNAME(clkout_top_p4210) = { "fout_epll", "fout_vpll", "sclk_hdmi24m", + "sclk_usbphy0", "sclk_usbphy1", "sclk_hdmiphy", + "cdclk0", "cdclk1", "cdclk2", "spdif_extclk", + "aclk160", "aclk133", "aclk200", "aclk100", + "sclk_mfc", "sclk_g3d", "sclk_g2d", + "cam_a_pclk", "cam_b_pclk", "s_rxbyteclkhs0_2l", + "s_rxbyteclkhs0_4l" }; +PNAME(clkout_dmc_p4210) = { "div_dmcd", "div_dmcp", "div_acp_pclk", "div_dmc", + "div_dphy", "none", "div_pwi" }; +PNAME(clkout_cpu_p4210) = { "fout_apll_div_2", "none", "fout_mpll_div_2", + "none", "arm_clk_div_2", "div_corem0", + "div_corem1", "div_corem0", "div_atb", + "div_periph", "div_pclk_dbg", "div_hpm" }; /* Exynos 4x12-specific parent groups */ PNAME(mout_mpll_user_p4x12) = { "fin_pll", "sclk_mpll", }; @@ -426,6 +453,29 @@ PNAME(mout_user_aclk266_gps_p4x12) = {"fin_pll", "div_aclk266_gps", }; PNAME(mout_pwi_p4x12) = { "xxti", "xusbxti", "sclk_hdmi24m", "sclk_usbphy0", "none", "sclk_hdmiphy", "sclk_mpll", "sclk_epll", "sclk_vpll" }; +PNAME(clkout_left_p4x12) = { "sclk_mpll_user_l_div_2", "sclk_apll_div_2", + "div_gdl", "div_gpl" }; +PNAME(clkout_right_p4x12) = { "sclk_mpll_user_r_div_2", "sclk_apll_div_2", + "div_gdr", "div_gpr" }; +PNAME(clkout_top_p4x12) = { "fout_epll", "fout_vpll", "sclk_hdmi24m", + "sclk_usbphy0", "none", "sclk_hdmiphy", + "cdclk0", "cdclk1", "cdclk2", "spdif_extclk", + "aclk160", "aclk133", "aclk200", "aclk100", + "sclk_mfc", "sclk_g3d", "aclk400_mcuisp", + "cam_a_pclk", "cam_b_pclk", "s_rxbyteclkhs0_2l", + "s_rxbyteclkhs0_4l", "rx_half_byte_clk_csis0", + "rx_half_byte_clk_csis1", "div_jpeg", + "sclk_pwm_isp", "sclk_spi0_isp", + "sclk_spi1_isp", "sclk_uart_isp", + "sclk_mipihsi", "sclk_hdmi", "sclk_fimd0", + "sclk_pcm0" }; +PNAME(clkout_dmc_p4x12) = { "div_dmcd", "div_dmcp", "aclk_acp", "div_acp_pclk", + "div_dmc", "div_dphy", "fout_mpll_div_2", + "div_pwi", "none", "div_c2c", "div_c2c_aclk" }; +PNAME(clkout_cpu_p4x12) = { "fout_apll_div_2", "none", "none", "none", + "arm_clk_div_2", "div_corem0", "div_corem1", + "div_cores", "div_atb", "div_periph", + "div_pclk_dbg", "div_hpm" }; /* fixed rate clocks generated outside the soc */ static struct samsung_fixed_rate_clock exynos4_fixed_rate_ext_clks[] __initdata = { @@ -444,6 +494,24 @@ static struct samsung_fixed_rate_clock exynos4210_fixed_rate_clks[] __initdata = FRATE(0, "sclk_usbphy1", NULL, CLK_IS_ROOT, 48000000), }; +static struct samsung_fixed_factor_clock exynos4_fixed_factor_clks[] __initdata = { + FFACTOR(0, "sclk_apll_div_2", "sclk_apll", 1, 2, 0), + FFACTOR(0, "fout_mpll_div_2", "fout_mpll", 1, 2, 0), + FFACTOR(0, "fout_apll_div_2", "fout_apll", 1, 2, 0), + FFACTOR(0, "arm_clk_div_2", "arm_clk", 1, 2, 0), +}; + +static struct samsung_fixed_factor_clock exynos4210_fixed_factor_clks[] __initdata = { + FFACTOR(0, "sclk_mpll_div_2", "sclk_mpll", 1, 2, 0), +}; + +static struct samsung_fixed_factor_clock exynos4x12_fixed_factor_clks[] __initdata = { + FFACTOR(0, "sclk_mpll_user_l_div_2", "mout_mpll_user_l", 1, 2, 0), + FFACTOR(0, "sclk_mpll_user_r_div_2", "mout_mpll_user_r", 1, 2, 0), + FFACTOR(0, "sclk_mpll_user_t_div_2", "mout_mpll_user_t", 1, 2, 0), + FFACTOR(0, "sclk_mpll_user_c_div_2", "mout_mpll_user_c", 1, 2, 0), +}; + /* list of mux clocks supported in all exynos4 soc's */ static struct samsung_mux_clock exynos4_mux_clks[] __initdata = { MUX_FA(CLK_MOUT_APLL, "mout_apll", mout_apll_p, SRC_CPU, 0, 1, @@ -471,8 +539,12 @@ static struct samsung_mux_clock exynos4210_mux_early[] __initdata = { static struct samsung_mux_clock exynos4210_mux_clks[] __initdata = { MUX(0, "mout_gdl", sclk_ampll_p4210, SRC_LEFTBUS, 0, 1), + MUX(0, "mout_clkout_leftbus", clkout_left_p4210, + CLKOUT_CMU_LEFTBUS, 0, 5), MUX(0, "mout_gdr", sclk_ampll_p4210, SRC_RIGHTBUS, 0, 1), + MUX(0, "mout_clkout_rightbus", clkout_right_p4210, + CLKOUT_CMU_RIGHTBUS, 0, 5), MUX(0, "mout_aclk200", sclk_ampll_p4210, SRC_TOP0, 12, 1), MUX(0, "mout_aclk100", sclk_ampll_p4210, SRC_TOP0, 16, 1), @@ -519,20 +591,30 @@ static struct samsung_mux_clock exynos4210_mux_clks[] __initdata = { MUX(0, "mout_spi0", group1_p4210, SRC_PERIL1, 16, 4), MUX(0, "mout_spi1", group1_p4210, SRC_PERIL1, 20, 4), MUX(0, "mout_spi2", group1_p4210, SRC_PERIL1, 24, 4), + MUX(0, "mout_clkout_top", clkout_top_p4210, CLKOUT_CMU_TOP, 0, 5), MUX(0, "mout_pwi", mout_pwi_p4210, SRC_DMC, 16, 4), + MUX(0, "mout_clkout_dmc", clkout_dmc_p4210, CLKOUT_CMU_DMC, 0, 5), + + MUX(0, "mout_clkout_cpu", clkout_cpu_p4210, CLKOUT_CMU_CPU, 0, 5), }; /* list of mux clocks supported in exynos4x12 soc */ static struct samsung_mux_clock exynos4x12_mux_clks[] __initdata = { MUX(0, "mout_mpll_user_l", mout_mpll_p, SRC_LEFTBUS, 4, 1), MUX(0, "mout_gdl", mout_gdl_p4x12, SRC_LEFTBUS, 0, 1), + MUX(0, "mout_clkout_leftbus", clkout_left_p4x12, + CLKOUT_CMU_LEFTBUS, 0, 5), MUX(0, "mout_mpll_user_r", mout_mpll_p, SRC_RIGHTBUS, 4, 1), MUX(0, "mout_gdr", mout_gdr_p4x12, SRC_RIGHTBUS, 0, 1), + MUX(0, "mout_clkout_rightbus", clkout_right_p4x12, + CLKOUT_CMU_RIGHTBUS, 0, 5), MUX(CLK_MOUT_MPLL_USER_C, "mout_mpll_user_c", mout_mpll_user_p4x12, SRC_CPU, 24, 1), + MUX(0, "mout_clkout_cpu", clkout_cpu_p4x12, CLKOUT_CMU_CPU, 0, 5), + MUX(0, "mout_aclk266_gps", aclk_p4412, SRC_TOP1, 4, 1), MUX(0, "mout_aclk400_mcuisp", aclk_p4412, SRC_TOP1, 8, 1), MUX(CLK_MOUT_MPLL_USER_T, "mout_mpll_user_t", mout_mpll_user_p4x12, @@ -590,20 +672,27 @@ static struct samsung_mux_clock exynos4x12_mux_clks[] __initdata = { MUX(0, "mout_spi0_isp", group1_p4x12, E4X12_SRC_ISP, 4, 4), MUX(0, "mout_spi1_isp", group1_p4x12, E4X12_SRC_ISP, 8, 4), MUX(0, "mout_uart_isp", group1_p4x12, E4X12_SRC_ISP, 12, 4), + MUX(0, "mout_clkout_top", clkout_top_p4x12, CLKOUT_CMU_TOP, 0, 5), + MUX(0, "mout_c2c", sclk_ampll_p4210, SRC_DMC, 0, 1), MUX(0, "mout_pwi", mout_pwi_p4x12, SRC_DMC, 16, 4), MUX(0, "mout_g2d0", sclk_ampll_p4210, SRC_DMC, 20, 1), MUX(0, "mout_g2d1", sclk_evpll_p, SRC_DMC, 24, 1), MUX(0, "mout_g2d", mout_g2d_p, SRC_DMC, 28, 1), + MUX(0, "mout_clkout_dmc", clkout_dmc_p4x12, CLKOUT_CMU_DMC, 0, 5), }; /* list of divider clocks supported in all exynos4 soc's */ static struct samsung_div_clock exynos4_div_clks[] __initdata = { DIV(0, "div_gdl", "mout_gdl", DIV_LEFTBUS, 0, 3), DIV(0, "div_gpl", "div_gdl", DIV_LEFTBUS, 4, 3), + DIV(0, "div_clkout_leftbus", "mout_clkout_leftbus", + CLKOUT_CMU_LEFTBUS, 8, 6), DIV(0, "div_gdr", "mout_gdr", DIV_RIGHTBUS, 0, 3), DIV(0, "div_gpr", "div_gdr", DIV_RIGHTBUS, 4, 3), + DIV(0, "div_clkout_rightbus", "mout_clkout_rightbus", + CLKOUT_CMU_RIGHTBUS, 8, 6), DIV(0, "div_core", "mout_core", DIV_CPU0, 0, 3), DIV(0, "div_corem0", "div_core2", DIV_CPU0, 4, 3), @@ -614,6 +703,8 @@ static struct samsung_div_clock exynos4_div_clks[] __initdata = { DIV(0, "div_core2", "div_core", DIV_CPU0, 28, 3), DIV(0, "div_copy", "mout_hpm", DIV_CPU1, 0, 3), DIV(0, "div_hpm", "div_copy", DIV_CPU1, 4, 3), + DIV(0, "div_clkout_cpu", "mout_clkout_cpu", CLKOUT_CMU_CPU, 8, 6), + DIV(0, "div_fimc0", "mout_fimc0", DIV_CAM, 0, 4), DIV(0, "div_fimc1", "mout_fimc1", DIV_CAM, 4, 4), DIV(0, "div_fimc2", "mout_fimc2", DIV_CAM, 8, 4), @@ -671,6 +762,7 @@ static struct samsung_div_clock exynos4_div_clks[] __initdata = { CLK_SET_RATE_PARENT, 0), DIV_F(0, "div_mmc_pre3", "div_mmc3", DIV_FSYS2, 24, 8, CLK_SET_RATE_PARENT, 0), + DIV(0, "div_clkout_top", "mout_clkout_top", CLKOUT_CMU_TOP, 8, 6), DIV(0, "div_acp", "mout_dmc_bus", DIV_DMC0, 0, 3), DIV(0, "div_acp_pclk", "div_acp", DIV_DMC0, 4, 3), @@ -679,6 +771,7 @@ static struct samsung_div_clock exynos4_div_clks[] __initdata = { DIV(0, "div_dmcd", "div_dmc", DIV_DMC0, 16, 3), DIV(0, "div_dmcp", "div_dmcd", DIV_DMC0, 20, 3), DIV(0, "div_pwi", "mout_pwi", DIV_DMC1, 8, 4), + DIV(0, "div_clkout_dmc", "mout_clkout_dmc", CLKOUT_CMU_DMC, 8, 6), }; /* list of divider clocks supported in exynos4210 soc */ @@ -916,6 +1009,17 @@ static struct samsung_gate_clock exynos4_gate_clks[] __initdata = { GATE(CLK_PPMUDMC1, "ppmudmc1", "aclk133", GATE_IP_DMC, 9, 0, 0), GATE(CLK_PPMUCPU, "ppmucpu", "aclk133", GATE_IP_DMC, 10, 0, 0), GATE(CLK_PPMUACP, "ppmuacp", "aclk133", GATE_IP_DMC, 16, 0, 0), + + GATE(CLK_OUT_LEFTBUS, "clkout_leftbus", "div_clkout_leftbus", + CLKOUT_CMU_LEFTBUS, 16, CLK_SET_RATE_PARENT, 0), + GATE(CLK_OUT_RIGHTBUS, "clkout_rightbus", "div_clkout_rightbus", + CLKOUT_CMU_RIGHTBUS, 16, CLK_SET_RATE_PARENT, 0), + GATE(CLK_OUT_TOP, "clkout_top", "div_clkout_top", + CLKOUT_CMU_TOP, 16, CLK_SET_RATE_PARENT, 0), + GATE(CLK_OUT_DMC, "clkout_dmc", "div_clkout_dmc", + CLKOUT_CMU_DMC, 16, CLK_SET_RATE_PARENT, 0), + GATE(CLK_OUT_CPU, "clkout_cpu", "div_clkout_cpu", + CLKOUT_CMU_CPU, 16, CLK_SET_RATE_PARENT, 0), }; /* list of gate clocks supported in exynos4210 soc */ @@ -1293,6 +1397,8 @@ static void __init exynos4_clk_init(struct device_node *np, ARRAY_SIZE(exynos4_div_clks)); samsung_clk_register_gate(ctx, exynos4_gate_clks, ARRAY_SIZE(exynos4_gate_clks)); + samsung_clk_register_fixed_factor(ctx, exynos4_fixed_factor_clks, + ARRAY_SIZE(exynos4_fixed_factor_clks)); if (exynos4_soc == EXYNOS4210) { samsung_clk_register_fixed_rate(ctx, exynos4210_fixed_rate_clks, @@ -1305,6 +1411,9 @@ static void __init exynos4_clk_init(struct device_node *np, ARRAY_SIZE(exynos4210_gate_clks)); samsung_clk_register_alias(ctx, exynos4210_aliases, ARRAY_SIZE(exynos4210_aliases)); + samsung_clk_register_fixed_factor(ctx, + exynos4210_fixed_factor_clks, + ARRAY_SIZE(exynos4210_fixed_factor_clks)); } else { samsung_clk_register_mux(ctx, exynos4x12_mux_clks, ARRAY_SIZE(exynos4x12_mux_clks)); @@ -1314,6 +1423,9 @@ static void __init exynos4_clk_init(struct device_node *np, ARRAY_SIZE(exynos4x12_gate_clks)); samsung_clk_register_alias(ctx, exynos4x12_aliases, ARRAY_SIZE(exynos4x12_aliases)); + samsung_clk_register_fixed_factor(ctx, + exynos4x12_fixed_factor_clks, + ARRAY_SIZE(exynos4x12_fixed_factor_clks)); } samsung_clk_register_alias(ctx, exynos4_aliases, diff --git a/include/dt-bindings/clock/exynos4.h b/include/dt-bindings/clock/exynos4.h index 60fadfc8d6ba..5a9f50225e4a 100644 --- a/include/dt-bindings/clock/exynos4.h +++ b/include/dt-bindings/clock/exynos4.h @@ -34,6 +34,11 @@ #define CLK_MOUT_CORE 19 #define CLK_MOUT_APLL 20 #define CLK_SCLK_HDMIPHY 22 +#define CLK_OUT_DMC 23 +#define CLK_OUT_TOP 24 +#define CLK_OUT_LEFTBUS 25 +#define CLK_OUT_RIGHTBUS 26 +#define CLK_OUT_CPU 27 /* gate for special clocks (sclk) */ #define CLK_SCLK_FIMC0 128 -- cgit v1.2.3 From f65d518942325d4bfa74b5c9d42ea5a89e4f6943 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Sat, 26 Jul 2014 02:57:20 +0200 Subject: clk: samsung: trivial: Correct typo in author's name This patch corrects mistyped author's name in four header files. While at it, a copy/paste error in author's e-mail in one of the headers is also fixed. Signed-off-by: Tomasz Figa --- include/dt-bindings/clock/exynos4.h | 2 +- include/dt-bindings/clock/exynos5250.h | 2 +- include/dt-bindings/clock/exynos5420.h | 2 +- include/dt-bindings/clock/exynos5440.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/dt-bindings/clock/exynos4.h b/include/dt-bindings/clock/exynos4.h index 5a9f50225e4a..459bd2bd411f 100644 --- a/include/dt-bindings/clock/exynos4.h +++ b/include/dt-bindings/clock/exynos4.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2013 Samsung Electronics Co., Ltd. - * Author: Andrzej Haja + * Author: Andrzej Hajda * * 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 diff --git a/include/dt-bindings/clock/exynos5250.h b/include/dt-bindings/clock/exynos5250.h index be6e97c54f54..4273891dc78e 100644 --- a/include/dt-bindings/clock/exynos5250.h +++ b/include/dt-bindings/clock/exynos5250.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2013 Samsung Electronics Co., Ltd. - * Author: Andrzej Haja + * Author: Andrzej Hajda * * 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 diff --git a/include/dt-bindings/clock/exynos5420.h b/include/dt-bindings/clock/exynos5420.h index 14e1c8f9640c..cea56606d04d 100644 --- a/include/dt-bindings/clock/exynos5420.h +++ b/include/dt-bindings/clock/exynos5420.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2013 Samsung Electronics Co., Ltd. - * Author: Andrzej Haja + * Author: Andrzej Hajda * * 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 diff --git a/include/dt-bindings/clock/exynos5440.h b/include/dt-bindings/clock/exynos5440.h index 70cd85077fa9..c66fc405a79a 100644 --- a/include/dt-bindings/clock/exynos5440.h +++ b/include/dt-bindings/clock/exynos5440.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2013 Samsung Electronics Co., Ltd. - * Author: Andrzej Haja + * Author: Andrzej Hajda * * 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 -- cgit v1.2.3 From 29b4739134c73a2873adec93346f09bb76d6a794 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 24 Jul 2014 12:52:23 -0700 Subject: Input: wacom - switch from an USB driver to a HID driver All USB Wacom tablets are actually HID devices. For historical reasons, they are handled as plain USB devices. The current code makes more and more reference to the HID subsystem like implementing its own HID report descriptor parser to handle new devices. From the user point of view, we can transparently switch from this state to a driver handled in the HID subsystem and clean up a lot of USB specific code in the wacom.ko driver. The other benefit once the USB dependecies have been removed is that we can use a tool like uhid to make regression tests and allow further cleanup or new implementations without risking breaking current behaviors. To match the current handling of devices in wacom_wac.c, we rely on the hid_type set by usbhid. usbhid sets the hid_type to HID_TYPE_USBMOUSE when it sees a USB boot mouse protocol declared and HID_TYPE_USBNONE when the device is plain HID. There is thus a one to one matching between the list of supported devices before and after the switch from USB to HID. Signed-off-by: Benjamin Tissoires Reviewed-by: Jason Gerecke Tested-by: Jason Gerecke Signed-off-by: Dmitry Torokhov --- drivers/hid/hid-core.c | 10 +- drivers/hid/hid-wacom.c | 2 +- drivers/input/tablet/wacom.h | 6 +- drivers/input/tablet/wacom_sys.c | 222 ++++++++++++++------------------------- drivers/input/tablet/wacom_wac.c | 80 +++++++------- drivers/input/tablet/wacom_wac.h | 4 +- include/linux/hid.h | 5 + 7 files changed, 141 insertions(+), 188 deletions(-) (limited to 'include') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 8ed66fd1ea87..1ce751db5a7a 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -787,6 +787,15 @@ static int hid_scan_report(struct hid_device *hid) /* hid-rmi should take care of them, not hid-generic */ hid->group = HID_GROUP_RMI; + /* + * Vendor specific handlings + */ + switch (hid->vendor) { + case USB_VENDOR_ID_WACOM: + hid->group = HID_GROUP_WACOM; + break; + } + vfree(parser); return 0; } @@ -2339,7 +2348,6 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) }, { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) }, { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LCSPEC) }, - { HID_USB_DEVICE(USB_VENDOR_ID_WACOM, HID_ANY_ID) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT) }, diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c index 902013ec041b..4874f4ec43f5 100644 --- a/drivers/hid/hid-wacom.c +++ b/drivers/hid/hid-wacom.c @@ -960,7 +960,7 @@ static const struct hid_device_id wacom_devices[] = { MODULE_DEVICE_TABLE(hid, wacom_devices); static struct hid_driver wacom_driver = { - .name = "wacom", + .name = "hid-wacom", .id_table = wacom_devices, .probe = wacom_probe, .remove = wacom_remove, diff --git a/drivers/input/tablet/wacom.h b/drivers/input/tablet/wacom.h index caa59cab2c5f..b9212390db71 100644 --- a/drivers/input/tablet/wacom.h +++ b/drivers/input/tablet/wacom.h @@ -106,14 +106,12 @@ MODULE_LICENSE(DRIVER_LICENSE); #define USB_VENDOR_ID_LENOVO 0x17ef struct wacom { - dma_addr_t data_dma; struct usb_device *usbdev; struct usb_interface *intf; - struct urb *irq; struct wacom_wac wacom_wac; + struct hid_device *hdev; struct mutex lock; struct work_struct work; - bool open; char phys[32]; struct wacom_led { u8 select[2]; /* status led selector (0..3) */ @@ -130,7 +128,7 @@ static inline void wacom_schedule_work(struct wacom_wac *wacom_wac) schedule_work(&wacom->work); } -extern const struct usb_device_id wacom_ids[]; +extern const struct hid_device_id wacom_ids[]; void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len); void wacom_setup_device_quirks(struct wacom_features *features); diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c index c34791e7f0b9..5ceeab6e95f1 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/input/tablet/wacom_sys.c @@ -83,86 +83,40 @@ static int wacom_set_report(struct usb_interface *intf, u8 type, u8 id, return retval; } -static void wacom_sys_irq(struct urb *urb) +static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report, + u8 *raw_data, int size) { - struct wacom *wacom = urb->context; - struct device *dev = &wacom->intf->dev; - int retval; + struct wacom *wacom = hid_get_drvdata(hdev); - switch (urb->status) { - case 0: - /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dev_dbg(dev, "%s - urb shutting down with status: %d\n", - __func__, urb->status); - return; - default: - dev_dbg(dev, "%s - nonzero urb status received: %d\n", - __func__, urb->status); - goto exit; - } + if (size > WACOM_PKGLEN_MAX) + return 1; - wacom_wac_irq(&wacom->wacom_wac, urb->actual_length); + memcpy(wacom->wacom_wac.data, raw_data, size); - exit: - usb_mark_last_busy(wacom->usbdev); - retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval) - dev_err(dev, "%s - usb_submit_urb failed with result %d\n", - __func__, retval); + wacom_wac_irq(&wacom->wacom_wac, size); + + return 0; } static int wacom_open(struct input_dev *dev) { struct wacom *wacom = input_get_drvdata(dev); - int retval = 0; - - if (usb_autopm_get_interface(wacom->intf) < 0) - return -EIO; + int retval; mutex_lock(&wacom->lock); - - if (wacom->open) - goto out; - - if (usb_submit_urb(wacom->irq, GFP_KERNEL)) { - retval = -EIO; - goto out; - } - - wacom->open = true; - wacom->intf->needs_remote_wakeup = 1; - -out: + retval = hid_hw_open(wacom->hdev); mutex_unlock(&wacom->lock); - usb_autopm_put_interface(wacom->intf); + return retval; } static void wacom_close(struct input_dev *dev) { struct wacom *wacom = input_get_drvdata(dev); - int autopm_error; - - autopm_error = usb_autopm_get_interface(wacom->intf); mutex_lock(&wacom->lock); - if (!wacom->open) - goto out; - - usb_kill_urb(wacom->irq); - wacom->open = false; - wacom->intf->needs_remote_wakeup = 0; - -out: + hid_hw_close(wacom->hdev); mutex_unlock(&wacom->lock); - - if (!autopm_error) - usb_autopm_put_interface(wacom->intf); } /* @@ -807,7 +761,8 @@ out: static ssize_t wacom_led_select_store(struct device *dev, int set_id, const char *buf, size_t count) { - struct wacom *wacom = dev_get_drvdata(dev); + struct hid_device *hdev = dev_get_drvdata(dev); + struct wacom *wacom = hid_get_drvdata(hdev); unsigned int id; int err; @@ -834,7 +789,8 @@ static ssize_t wacom_led##SET_ID##_select_store(struct device *dev, \ static ssize_t wacom_led##SET_ID##_select_show(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ - struct wacom *wacom = dev_get_drvdata(dev); \ + struct hid_device *hdev = dev_get_drvdata(dev); \ + struct wacom *wacom = hid_get_drvdata(hdev); \ return snprintf(buf, 2, "%d\n", wacom->led.select[SET_ID]); \ } \ static DEVICE_ATTR(status_led##SET_ID##_select, S_IWUSR | S_IRUSR, \ @@ -868,7 +824,8 @@ static ssize_t wacom_luminance_store(struct wacom *wacom, u8 *dest, static ssize_t wacom_##name##_luminance_store(struct device *dev, \ struct device_attribute *attr, const char *buf, size_t count) \ { \ - struct wacom *wacom = dev_get_drvdata(dev); \ + struct hid_device *hdev = dev_get_drvdata(dev); \ + struct wacom *wacom = hid_get_drvdata(hdev); \ \ return wacom_luminance_store(wacom, &wacom->led.field, \ buf, count); \ @@ -883,7 +840,8 @@ DEVICE_LUMINANCE_ATTR(buttons, img_lum); static ssize_t wacom_button_image_store(struct device *dev, int button_id, const char *buf, size_t count) { - struct wacom *wacom = dev_get_drvdata(dev); + struct hid_device *hdev = dev_get_drvdata(dev); + struct wacom *wacom = hid_get_drvdata(hdev); int err; if (count != 1024) @@ -1201,6 +1159,7 @@ static void wacom_wireless_work(struct work_struct *work) struct wacom *wacom = container_of(work, struct wacom, work); struct usb_device *usbdev = wacom->usbdev; struct wacom_wac *wacom_wac = &wacom->wacom_wac; + struct hid_device *hdev1, *hdev2; struct wacom *wacom1, *wacom2; struct wacom_wac *wacom_wac1, *wacom_wac2; int error; @@ -1213,32 +1172,34 @@ static void wacom_wireless_work(struct work_struct *work) wacom_destroy_battery(wacom); /* Stylus interface */ - wacom1 = usb_get_intfdata(usbdev->config->interface[1]); + hdev1 = usb_get_intfdata(usbdev->config->interface[1]); + wacom1 = hid_get_drvdata(hdev1); wacom_wac1 = &(wacom1->wacom_wac); wacom_unregister_inputs(wacom1); /* Touch interface */ - wacom2 = usb_get_intfdata(usbdev->config->interface[2]); + hdev2 = usb_get_intfdata(usbdev->config->interface[2]); + wacom2 = hid_get_drvdata(hdev2); wacom_wac2 = &(wacom2->wacom_wac); wacom_unregister_inputs(wacom2); if (wacom_wac->pid == 0) { dev_info(&wacom->intf->dev, "wireless tablet disconnected\n"); } else { - const struct usb_device_id *id = wacom_ids; + const struct hid_device_id *id = wacom_ids; dev_info(&wacom->intf->dev, "wireless tablet connected with PID %x\n", wacom_wac->pid); - while (id->match_flags) { - if (id->idVendor == USB_VENDOR_ID_WACOM && - id->idProduct == wacom_wac->pid) + while (id->bus) { + if (id->vendor == USB_VENDOR_ID_WACOM && + id->product == wacom_wac->pid) break; id++; } - if (!id->match_flags) { + if (!id->bus) { dev_info(&wacom->intf->dev, "ignoring unknown PID.\n"); return; @@ -1246,7 +1207,7 @@ static void wacom_wireless_work(struct work_struct *work) /* Stylus interface */ wacom_wac1->features = - *((struct wacom_features *)id->driver_info); + *((struct wacom_features *)id->driver_data); wacom_wac1->features.device_type = BTN_TOOL_PEN; snprintf(wacom_wac1->name, WACOM_NAME_MAX, "%s (WL) Pen", wacom_wac1->features.name); @@ -1262,7 +1223,7 @@ static void wacom_wireless_work(struct work_struct *work) if (wacom_wac1->features.touch_max || wacom_wac1->features.type == INTUOSHT) { wacom_wac2->features = - *((struct wacom_features *)id->driver_info); + *((struct wacom_features *)id->driver_data); wacom_wac2->features.pktlen = WACOM_PKGLEN_BBTOUCH3; wacom_wac2->features.device_type = BTN_TOOL_FINGER; wacom_wac2->features.x_max = wacom_wac2->features.y_max = 4096; @@ -1323,8 +1284,10 @@ static void wacom_calculate_res(struct wacom_features *features) features->unitExpo); } -static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id) +static int wacom_probe(struct hid_device *hdev, + const struct hid_device_id *id) { + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); struct usb_device *dev = interface_to_usbdev(intf); struct usb_endpoint_descriptor *endpoint; struct wacom *wacom; @@ -1332,34 +1295,29 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i struct wacom_features *features; int error; - if (!id->driver_info) + if (!id->driver_data) return -EINVAL; wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL); if (!wacom) return -ENOMEM; + hid_set_drvdata(hdev, wacom); + wacom->hdev = hdev; + wacom_wac = &wacom->wacom_wac; - wacom_wac->features = *((struct wacom_features *)id->driver_info); + wacom_wac->features = *((struct wacom_features *)id->driver_data); features = &wacom_wac->features; if (features->pktlen > WACOM_PKGLEN_MAX) { error = -EINVAL; goto fail1; } - wacom_wac->data = usb_alloc_coherent(dev, WACOM_PKGLEN_MAX, - GFP_KERNEL, &wacom->data_dma); - if (!wacom_wac->data) { - error = -ENOMEM; + if (features->check_for_hid_type && features->hid_type != hdev->type) { + error = -ENODEV; goto fail1; } - wacom->irq = usb_alloc_urb(0, GFP_KERNEL); - if (!wacom->irq) { - error = -ENOMEM; - goto fail2; - } - wacom->usbdev = dev; wacom->intf = intf; mutex_init(&wacom->lock); @@ -1375,7 +1333,7 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i /* Retrieve the physical and logical size for touch devices */ error = wacom_retrieve_hid_descriptor(intf, features); if (error) - goto fail3; + goto fail1; /* * Intuos5 has no useful data about its touch interface in its @@ -1423,38 +1381,38 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i other_dev = dev; error = wacom_add_shared_data(wacom_wac, other_dev); if (error) - goto fail3; + goto fail1; } - usb_fill_int_urb(wacom->irq, dev, - usb_rcvintpipe(dev, endpoint->bEndpointAddress), - wacom_wac->data, features->pktlen, - wacom_sys_irq, wacom, endpoint->bInterval); - wacom->irq->transfer_dma = wacom->data_dma; - wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - error = wacom_initialize_leds(wacom); if (error) - goto fail4; + goto fail2; if (!(features->quirks & WACOM_QUIRK_NO_INPUT)) { error = wacom_register_inputs(wacom); if (error) - goto fail5; + goto fail3; } /* Note that if query fails it is not a hard failure */ wacom_query_tablet_data(intf, features); - usb_set_intfdata(intf, wacom); + /* Regular HID work starts now */ + error = hid_parse(hdev); + if (error) { + hid_err(hdev, "parse failed\n"); + goto fail4; + } - if (features->quirks & WACOM_QUIRK_MONITOR) { - if (usb_submit_urb(wacom->irq, GFP_KERNEL)) { - error = -EIO; - goto fail5; - } + error = hid_hw_start(hdev, HID_CONNECT_HIDRAW); + if (error) { + hid_err(hdev, "hw start failed\n"); + goto fail4; } + if (features->quirks & WACOM_QUIRK_MONITOR) + error = hid_hw_open(hdev); + if (wacom_wac->features.type == INTUOSHT && wacom_wac->features.touch_max) { if (wacom_wac->features.device_type == BTN_TOOL_FINGER) wacom_wac->shared->touch_input = wacom_wac->input; @@ -1462,78 +1420,60 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i return 0; - fail5: wacom_destroy_leds(wacom); - fail4: wacom_remove_shared_data(wacom_wac); - fail3: usb_free_urb(wacom->irq); - fail2: usb_free_coherent(dev, WACOM_PKGLEN_MAX, wacom_wac->data, wacom->data_dma); + fail4: wacom_unregister_inputs(wacom); + fail3: wacom_destroy_leds(wacom); + fail2: wacom_remove_shared_data(wacom_wac); fail1: kfree(wacom); + hid_set_drvdata(hdev, NULL); return error; } -static void wacom_disconnect(struct usb_interface *intf) +static void wacom_remove(struct hid_device *hdev) { - struct wacom *wacom = usb_get_intfdata(intf); + struct wacom *wacom = hid_get_drvdata(hdev); - usb_set_intfdata(intf, NULL); + hid_hw_stop(hdev); - usb_kill_urb(wacom->irq); cancel_work_sync(&wacom->work); wacom_unregister_inputs(wacom); wacom_destroy_battery(wacom); wacom_destroy_leds(wacom); - usb_free_urb(wacom->irq); - usb_free_coherent(interface_to_usbdev(intf), WACOM_PKGLEN_MAX, - wacom->wacom_wac.data, wacom->data_dma); wacom_remove_shared_data(&wacom->wacom_wac); - kfree(wacom); -} - -static int wacom_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct wacom *wacom = usb_get_intfdata(intf); - - mutex_lock(&wacom->lock); - usb_kill_urb(wacom->irq); - mutex_unlock(&wacom->lock); - return 0; + hid_set_drvdata(hdev, NULL); + kfree(wacom); } -static int wacom_resume(struct usb_interface *intf) +static int wacom_resume(struct hid_device *hdev) { - struct wacom *wacom = usb_get_intfdata(intf); + struct wacom *wacom = hid_get_drvdata(hdev); struct wacom_features *features = &wacom->wacom_wac.features; - int rv = 0; mutex_lock(&wacom->lock); /* switch to wacom mode first */ - wacom_query_tablet_data(intf, features); + wacom_query_tablet_data(wacom->intf, features); wacom_led_control(wacom); - if ((wacom->open || (features->quirks & WACOM_QUIRK_MONITOR)) && - usb_submit_urb(wacom->irq, GFP_NOIO) < 0) - rv = -EIO; - mutex_unlock(&wacom->lock); - return rv; + return 0; } -static int wacom_reset_resume(struct usb_interface *intf) +static int wacom_reset_resume(struct hid_device *hdev) { - return wacom_resume(intf); + return wacom_resume(hdev); } -static struct usb_driver wacom_driver = { +static struct hid_driver wacom_driver = { .name = "wacom", .id_table = wacom_ids, .probe = wacom_probe, - .disconnect = wacom_disconnect, - .suspend = wacom_suspend, + .remove = wacom_remove, +#ifdef CONFIG_PM .resume = wacom_resume, .reset_resume = wacom_reset_resume, - .supports_autosuspend = 1, +#endif + .raw_event = wacom_raw_event, }; - -module_usb_driver(wacom_driver); +module_hid_driver(wacom_driver); diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index f170277b6f28..1c95ce78d749 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -2197,15 +2197,18 @@ static const struct wacom_features wacom_features_0x2A = static const struct wacom_features wacom_features_0x314 = { "Wacom Intuos Pro S", WACOM_PKGLEN_INTUOS, 31496, 19685, 2047, 63, INTUOSPS, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, - .touch_max = 16 }; + .touch_max = 16, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; static const struct wacom_features wacom_features_0x315 = { "Wacom Intuos Pro M", WACOM_PKGLEN_INTUOS, 44704, 27940, 2047, 63, INTUOSPM, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, - .touch_max = 16 }; + .touch_max = 16, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; static const struct wacom_features wacom_features_0x317 = { "Wacom Intuos Pro L", WACOM_PKGLEN_INTUOS, 65024, 40640, 2047, 63, INTUOSPL, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, - .touch_max = 16 }; + .touch_max = 16, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; static const struct wacom_features wacom_features_0xF4 = { "Wacom Cintiq 24HD", WACOM_PKGLEN_INTUOS, 104280, 65400, 2047, 63, WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 }; @@ -2215,7 +2218,8 @@ static const struct wacom_features wacom_features_0xF8 = .oVid = USB_VENDOR_ID_WACOM, .oPid = 0xf6 }; static const struct wacom_features wacom_features_0xF6 = { "Wacom Cintiq 24HD touch", .type = WACOM_24HDT, /* Touch */ - .oVid = USB_VENDOR_ID_WACOM, .oPid = 0xf8, .touch_max = 10 }; + .oVid = USB_VENDOR_ID_WACOM, .oPid = 0xf8, .touch_max = 10, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; static const struct wacom_features wacom_features_0x3F = { "Wacom Cintiq 21UX", WACOM_PKGLEN_INTUOS, 87200, 65600, 1023, 63, CINTIQ, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; @@ -2233,7 +2237,8 @@ static const struct wacom_features wacom_features_0xC7 = 0, PL, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0xCE = { "Wacom DTU2231", WACOM_PKGLEN_GRAPHIRE, 47864, 27011, 511, - 0, DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + 0, DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBMOUSE }; static const struct wacom_features wacom_features_0xF0 = { "Wacom DTU1631", WACOM_PKGLEN_GRAPHIRE, 34623, 19553, 511, 0, DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; @@ -2249,7 +2254,8 @@ static const struct wacom_features wacom_features_0x59 = /* Pen */ .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5D }; static const struct wacom_features wacom_features_0x5D = /* Touch */ { "Wacom DTH2242", .type = WACOM_24HDT, - .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x59, .touch_max = 10 }; + .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x59, .touch_max = 10, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; static const struct wacom_features wacom_features_0xCC = { "Wacom Cintiq 21UX2", WACOM_PKGLEN_INTUOS, 87000, 65400, 2047, 63, WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 }; @@ -2262,7 +2268,8 @@ static const struct wacom_features wacom_features_0x5B = .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5e }; static const struct wacom_features wacom_features_0x5E = { "Wacom Cintiq 22HDT", .type = WACOM_24HDT, - .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5b, .touch_max = 10 }; + .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5b, .touch_max = 10, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; static const struct wacom_features wacom_features_0x90 = { "Wacom ISDv4 90", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; @@ -2400,14 +2407,17 @@ static const struct wacom_features wacom_features_0x301 = static const struct wacom_features wacom_features_0x302 = { "Wacom Intuos PT S", WACOM_PKGLEN_BBPEN, 15200, 9500, 1023, 31, INTUOSHT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 16 }; + .touch_max = 16, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; static const struct wacom_features wacom_features_0x303 = { "Wacom Intuos PT M", WACOM_PKGLEN_BBPEN, 21600, 13500, 1023, 31, INTUOSHT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 16 }; + .touch_max = 16, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; static const struct wacom_features wacom_features_0x30E = { "Wacom Intuos S", WACOM_PKGLEN_BBPEN, 15200, 9500, 1023, - 31, INTUOSHT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; + 31, INTUOSHT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; static const struct wacom_features wacom_features_0x6004 = { "ISD-V4", WACOM_PKGLEN_GRAPHIRE, 12800, 8000, 255, 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; @@ -2417,22 +2427,18 @@ static const struct wacom_features wacom_features_0x0307 = .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x309 }; static const struct wacom_features wacom_features_0x0309 = { "Wacom ISDv5 309", .type = WACOM_24HDT, /* Touch */ - .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x0307, .touch_max = 10 }; - -#define USB_DEVICE_WACOM(prod) \ - USB_DEVICE(USB_VENDOR_ID_WACOM, prod), \ - .driver_info = (kernel_ulong_t)&wacom_features_##prod + .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x0307, .touch_max = 10, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; -#define USB_DEVICE_DETAILED(prod, class, sub, proto) \ - USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_WACOM, prod, class, \ - sub, proto), \ - .driver_info = (kernel_ulong_t)&wacom_features_##prod +#define USB_DEVICE_WACOM(prod) \ + HID_DEVICE(BUS_USB, HID_GROUP_WACOM, USB_VENDOR_ID_WACOM, prod),\ + .driver_data = (kernel_ulong_t)&wacom_features_##prod #define USB_DEVICE_LENOVO(prod) \ - USB_DEVICE(USB_VENDOR_ID_LENOVO, prod), \ - .driver_info = (kernel_ulong_t)&wacom_features_##prod + HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, prod), \ + .driver_data = (kernel_ulong_t)&wacom_features_##prod -const struct usb_device_id wacom_ids[] = { +const struct hid_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0x00) }, { USB_DEVICE_WACOM(0x10) }, { USB_DEVICE_WACOM(0x11) }, @@ -2478,9 +2484,9 @@ const struct usb_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0x45) }, { USB_DEVICE_WACOM(0x57) }, { USB_DEVICE_WACOM(0x59) }, - { USB_DEVICE_DETAILED(0x5D, USB_CLASS_HID, 0, 0) }, + { USB_DEVICE_WACOM(0x5D) }, { USB_DEVICE_WACOM(0x5B) }, - { USB_DEVICE_DETAILED(0x5E, USB_CLASS_HID, 0, 0) }, + { USB_DEVICE_WACOM(0x5E) }, { USB_DEVICE_WACOM(0xB0) }, { USB_DEVICE_WACOM(0xB1) }, { USB_DEVICE_WACOM(0xB2) }, @@ -2502,13 +2508,7 @@ const struct usb_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0xC5) }, { USB_DEVICE_WACOM(0xC6) }, { USB_DEVICE_WACOM(0xC7) }, - /* - * DTU-2231 has two interfaces on the same configuration, - * only one is used. - */ - { USB_DEVICE_DETAILED(0xCE, USB_CLASS_HID, - USB_INTERFACE_SUBCLASS_BOOT, - USB_INTERFACE_PROTOCOL_MOUSE) }, + { USB_DEVICE_WACOM(0xCE) }, { USB_DEVICE_WACOM(0x84) }, { USB_DEVICE_WACOM(0xD0) }, { USB_DEVICE_WACOM(0xD1) }, @@ -2546,13 +2546,13 @@ const struct usb_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0x116) }, { USB_DEVICE_WACOM(0x300) }, { USB_DEVICE_WACOM(0x301) }, - { USB_DEVICE_DETAILED(0x302, USB_CLASS_HID, 0, 0) }, - { USB_DEVICE_DETAILED(0x303, USB_CLASS_HID, 0, 0) }, - { USB_DEVICE_DETAILED(0x30E, USB_CLASS_HID, 0, 0) }, + { USB_DEVICE_WACOM(0x302) }, + { USB_DEVICE_WACOM(0x303) }, + { USB_DEVICE_WACOM(0x30E) }, { USB_DEVICE_WACOM(0x304) }, - { USB_DEVICE_DETAILED(0x314, USB_CLASS_HID, 0, 0) }, - { USB_DEVICE_DETAILED(0x315, USB_CLASS_HID, 0, 0) }, - { USB_DEVICE_DETAILED(0x317, USB_CLASS_HID, 0, 0) }, + { USB_DEVICE_WACOM(0x314) }, + { USB_DEVICE_WACOM(0x315) }, + { USB_DEVICE_WACOM(0x317) }, { USB_DEVICE_WACOM(0x4001) }, { USB_DEVICE_WACOM(0x4004) }, { USB_DEVICE_WACOM(0x5000) }, @@ -2560,12 +2560,12 @@ const struct usb_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0x47) }, { USB_DEVICE_WACOM(0xF4) }, { USB_DEVICE_WACOM(0xF8) }, - { USB_DEVICE_DETAILED(0xF6, USB_CLASS_HID, 0, 0) }, + { USB_DEVICE_WACOM(0xF6) }, { USB_DEVICE_WACOM(0xFA) }, { USB_DEVICE_WACOM(0xFB) }, { USB_DEVICE_WACOM(0x0307) }, - { USB_DEVICE_DETAILED(0x0309, USB_CLASS_HID, 0, 0) }, + { USB_DEVICE_WACOM(0x0309) }, { USB_DEVICE_LENOVO(0x6004) }, { } }; -MODULE_DEVICE_TABLE(usb, wacom_ids); +MODULE_DEVICE_TABLE(hid, wacom_ids); diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index f48164c780f4..8821a518abf6 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h @@ -137,6 +137,8 @@ struct wacom_features { unsigned touch_max; int oVid; int oPid; + bool check_for_hid_type; + int hid_type; }; struct wacom_shared { @@ -151,7 +153,7 @@ struct wacom_shared { struct wacom_wac { char name[WACOM_NAME_MAX]; char pad_name[WACOM_NAME_MAX]; - unsigned char *data; + unsigned char data[WACOM_PKGLEN_MAX]; int tool[2]; int id[2]; __u32 serial[2]; diff --git a/include/linux/hid.h b/include/linux/hid.h index 77632cf159c0..07fa80671db0 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -310,6 +310,11 @@ struct hid_item { */ #define HID_GROUP_RMI 0x0100 +/* + * Vendor specific HID device groups + */ +#define HID_GROUP_WACOM 0x0101 + /* * This is the global environment of the parser. This information is * persistent for main-items. The global environment can be saved and -- cgit v1.2.3 From 7cec72ce00c174fd74d1183599d3d3dcc5dedc26 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 25 Jul 2014 23:25:36 -0300 Subject: [media] rc-core: don't use dynamic_pr_debug for IR_dprintk() The hole point of IR_dprintk() is that, once a level is given at debug parameter, all enabled IR parsers will show their debug messages. While converting it to dynamic_printk might be a good idea, right now it just makes very hard to debug the drivers, as one needs to both pass debug=1 or debug=2 to rc-core and to use the dynamic printk to enable all the desired lines. That doesn't make sense! So, revert to the old way, as a single line is changed, and the debug parameter will now work as expected. Signed-off-by: Mauro Carvalho Chehab --- include/media/rc-core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/media/rc-core.h b/include/media/rc-core.h index 3047837db1cc..2c7fbca40b69 100644 --- a/include/media/rc-core.h +++ b/include/media/rc-core.h @@ -26,7 +26,7 @@ extern int rc_core_debug; #define IR_dprintk(level, fmt, ...) \ do { \ if (rc_core_debug >= level) \ - pr_debug("%s: " fmt, __func__, ##__VA_ARGS__); \ + printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__); \ } while (0) enum rc_driver_type { -- cgit v1.2.3 From 7b4eeed174b71c325705ff8c53f333bc79d0ee7a Mon Sep 17 00:00:00 2001 From: James Harper Date: Thu, 12 Jun 2014 06:53:38 -0300 Subject: [media] vmalloc_sg: make sure all pages in vmalloc area are really DMA-ready Patch originally written by Konrad. Rebased on current linux media tree. Under Xen, vmalloc_32() isn't guaranteed to return pages which are really under 4G in machine physical addresses (only in virtual pseudo-physical addresses). To work around this, implement a vmalloc variant which allocates each page with dma_alloc_coherent() to guarantee that each page is suitable for the device in question. Signed-off-by: Konrad Rzeszutek Wilk Signed-off-by: James Harper Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/videobuf-dma-sg.c | 62 +++++++++++++++++++++++++++++-- include/media/videobuf-dma-sg.h | 3 ++ 2 files changed, 61 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c b/drivers/media/v4l2-core/videobuf-dma-sg.c index 828e7c10bd70..3c8cc023a5a5 100644 --- a/drivers/media/v4l2-core/videobuf-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf-dma-sg.c @@ -211,13 +211,36 @@ EXPORT_SYMBOL_GPL(videobuf_dma_init_user); int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, int nr_pages) { + int i; + dprintk(1, "init kernel [%d pages]\n", nr_pages); dma->direction = direction; - dma->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT); + dma->vaddr_pages = kcalloc(nr_pages, sizeof(*dma->vaddr_pages), + GFP_KERNEL); + if (!dma->vaddr_pages) + return -ENOMEM; + + dma->dma_addr = kcalloc(nr_pages, sizeof(*dma->dma_addr), GFP_KERNEL); + if (!dma->dma_addr) { + kfree(dma->vaddr_pages); + return -ENOMEM; + } + for (i = 0; i < nr_pages; i++) { + void *addr; + + addr = dma_alloc_coherent(dma->dev, PAGE_SIZE, + &(dma->dma_addr[i]), GFP_KERNEL); + if (addr == NULL) + goto out_free_pages; + + dma->vaddr_pages[i] = virt_to_page(addr); + } + dma->vaddr = vmap(dma->vaddr_pages, nr_pages, VM_MAP | VM_IOREMAP, + PAGE_KERNEL); if (NULL == dma->vaddr) { dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages); - return -ENOMEM; + goto out_free_pages; } dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n", @@ -228,6 +251,19 @@ int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, dma->nr_pages = nr_pages; return 0; +out_free_pages: + while (i > 0) { + void *addr = page_address(dma->vaddr_pages[i]); + dma_free_coherent(dma->dev, PAGE_SIZE, addr, dma->dma_addr[i]); + i--; + } + kfree(dma->dma_addr); + dma->dma_addr = NULL; + kfree(dma->vaddr_pages); + dma->vaddr_pages = NULL; + + return -ENOMEM; + } EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel); @@ -322,8 +358,21 @@ int videobuf_dma_free(struct videobuf_dmabuf *dma) dma->pages = NULL; } - vfree(dma->vaddr); - dma->vaddr = NULL; + if (dma->dma_addr) { + for (i = 0; i < dma->nr_pages; i++) { + void *addr; + + addr = page_address(dma->vaddr_pages[i]); + dma_free_coherent(dma->dev, PAGE_SIZE, addr, + dma->dma_addr[i]); + } + kfree(dma->dma_addr); + dma->dma_addr = NULL; + kfree(dma->vaddr_pages); + dma->vaddr_pages = NULL; + vunmap(dma->vaddr); + dma->vaddr = NULL; + } if (dma->bus_addr) dma->bus_addr = 0; @@ -461,6 +510,11 @@ static int __videobuf_iolock(struct videobuf_queue *q, MAGIC_CHECK(mem->magic, MAGIC_SG_MEM); + if (!mem->dma.dev) + mem->dma.dev = q->dev; + else + WARN_ON(mem->dma.dev != q->dev); + switch (vb->memory) { case V4L2_MEMORY_MMAP: case V4L2_MEMORY_USERPTR: diff --git a/include/media/videobuf-dma-sg.h b/include/media/videobuf-dma-sg.h index d8fb6012c10d..fb6fd4d8f4ed 100644 --- a/include/media/videobuf-dma-sg.h +++ b/include/media/videobuf-dma-sg.h @@ -53,6 +53,9 @@ struct videobuf_dmabuf { /* for kernel buffers */ void *vaddr; + struct page **vaddr_pages; + dma_addr_t *dma_addr; + struct device *dev; /* for overlay buffers (pci-pci dma) */ dma_addr_t bus_addr; -- cgit v1.2.3 From 628531c9e971f1bd023d9fbd00faff014ca22440 Mon Sep 17 00:00:00 2001 From: Georg Lukas Date: Sat, 26 Jul 2014 13:59:57 +0200 Subject: Bluetooth: Provide defaults for LE advertising interval Store the default values for minimum and maximum advertising interval with all the other controller defaults. These vaules are sent to the adapter whenever advertising is (re)enabled. Signed-off-by: Georg Lukas Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_core.c | 2 ++ net/bluetooth/mgmt.c | 4 ++-- 3 files changed, 6 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 747a0c3d9947..b5d5af3aa469 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -203,6 +203,8 @@ struct hci_dev { __u16 page_scan_window; __u8 page_scan_type; __u8 le_adv_channel_map; + __u16 le_adv_min_interval; + __u16 le_adv_max_interval; __u8 le_scan_type; __u16 le_scan_interval; __u16 le_scan_window; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 2e2961a3cf6f..475d6003ed15 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3923,6 +3923,8 @@ struct hci_dev *hci_alloc_dev(void) hdev->sniff_min_interval = 80; hdev->le_adv_channel_map = 0x07; + hdev->le_adv_min_interval = 0x0800; + hdev->le_adv_max_interval = 0x0800; hdev->le_scan_interval = 0x0060; hdev->le_scan_window = 0x0030; hdev->le_conn_min_interval = 0x0028; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index ccc4653ce658..ff874580d989 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1086,8 +1086,8 @@ static void enable_advertising(struct hci_request *req) return; memset(&cp, 0, sizeof(cp)); - cp.min_interval = cpu_to_le16(0x0800); - cp.max_interval = cpu_to_le16(0x0800); + cp.min_interval = cpu_to_le16(hdev->le_adv_min_interval); + cp.max_interval = cpu_to_le16(hdev->le_adv_max_interval); cp.type = connectable ? LE_ADV_IND : LE_ADV_NONCONN_IND; cp.own_address_type = own_addr_type; cp.channel_map = hdev->le_adv_channel_map; -- cgit v1.2.3 From f7d0e6d67fdc300c316dfb7ca1d4aaa234171053 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Wed, 23 Jul 2014 06:57:14 -0300 Subject: [media] media: blackfin: ppi: Pass device pointer to request peripheral pins if the pinctrl driver is enabled. Signed-off-by: Sonic Zhang Acked-by: Scott Jiang Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/blackfin/bfin_capture.c | 2 +- drivers/media/platform/blackfin/ppi.c | 8 +++++--- include/media/blackfin/ppi.h | 3 ++- 3 files changed, 8 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c index 6ef986657dda..7c70d5104417 100644 --- a/drivers/media/platform/blackfin/bfin_capture.c +++ b/drivers/media/platform/blackfin/bfin_capture.c @@ -939,7 +939,7 @@ static int bcap_probe(struct platform_device *pdev) bcap_dev->cfg = config; - bcap_dev->ppi = ppi_create_instance(config->ppi_info); + bcap_dev->ppi = ppi_create_instance(pdev, config->ppi_info); if (!bcap_dev->ppi) { v4l2_err(pdev->dev.driver, "Unable to create ppi\n"); ret = -ENODEV; diff --git a/drivers/media/platform/blackfin/ppi.c b/drivers/media/platform/blackfin/ppi.c index 15e9c2bac2b1..90c4a936e3ae 100644 --- a/drivers/media/platform/blackfin/ppi.c +++ b/drivers/media/platform/blackfin/ppi.c @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -307,7 +308,8 @@ static void ppi_update_addr(struct ppi_if *ppi, unsigned long addr) set_dma_start_addr(ppi->info->dma_ch, addr); } -struct ppi_if *ppi_create_instance(const struct ppi_info *info) +struct ppi_if *ppi_create_instance(struct platform_device *pdev, + const struct ppi_info *info) { struct ppi_if *ppi; @@ -315,14 +317,14 @@ struct ppi_if *ppi_create_instance(const struct ppi_info *info) return NULL; if (peripheral_request_list(info->pin_req, KBUILD_MODNAME)) { - pr_err("request peripheral failed\n"); + dev_err(&pdev->dev, "request peripheral failed\n"); return NULL; } ppi = kzalloc(sizeof(*ppi), GFP_KERNEL); if (!ppi) { peripheral_free_list(info->pin_req); - pr_err("unable to allocate memory for ppi handle\n"); + dev_err(&pdev->dev, "unable to allocate memory for ppi handle\n"); return NULL; } ppi->ops = &ppi_ops; diff --git a/include/media/blackfin/ppi.h b/include/media/blackfin/ppi.h index d0697f4edf87..61a283f2bd4b 100644 --- a/include/media/blackfin/ppi.h +++ b/include/media/blackfin/ppi.h @@ -91,6 +91,7 @@ struct ppi_if { void *priv; }; -struct ppi_if *ppi_create_instance(const struct ppi_info *info); +struct ppi_if *ppi_create_instance(struct platform_device *pdev, + const struct ppi_info *info); void ppi_delete_instance(struct ppi_if *ppi); #endif -- cgit v1.2.3 From 0a6824bc10de58dac8beca4ad6744753f1cf9a6f Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Wed, 23 Jul 2014 06:57:16 -0300 Subject: [media] v4l2: blackfin: select proper pinctrl state in ppi_set_params if CONFIG_PINCTRL is enabled Multiple pinctrl states are defined for 8, 16 and 24 data pin groups in PPI peripheral. The driver should select correct group before set up further PPI parameters. Signed-off-by: Sonic Zhang Acked-by: Scott Jiang Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/blackfin/ppi.c | 17 +++++++++++++++++ include/media/blackfin/ppi.h | 1 + 2 files changed, 18 insertions(+) (limited to 'include') diff --git a/drivers/media/platform/blackfin/ppi.c b/drivers/media/platform/blackfin/ppi.c index 90c4a936e3ae..cff63e511e6d 100644 --- a/drivers/media/platform/blackfin/ppi.c +++ b/drivers/media/platform/blackfin/ppi.c @@ -206,6 +206,20 @@ static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params) int dma_config, bytes_per_line; int hcount, hdelay, samples_per_line; +#ifdef CONFIG_PINCTRL + static const char * const pin_state[] = {"8bit", "16bit", "24bit"}; + struct pinctrl *pctrl; + struct pinctrl_state *pstate; + + if (params->dlen > 24 || params->dlen <= 0) + return -EINVAL; + pctrl = devm_pinctrl_get(ppi->dev); + pstate = pinctrl_lookup_state(pctrl, + pin_state[(params->dlen + 7) / 8 - 1]); + if (pinctrl_select_state(pctrl, pstate)) + return -EINVAL; +#endif + bytes_per_line = params->width * params->bpp / 8; /* convert parameters unit from pixels to samples */ hcount = params->width * params->bpp / params->dlen; @@ -316,10 +330,12 @@ struct ppi_if *ppi_create_instance(struct platform_device *pdev, if (!info || !info->pin_req) return NULL; +#ifndef CONFIG_PINCTRL if (peripheral_request_list(info->pin_req, KBUILD_MODNAME)) { dev_err(&pdev->dev, "request peripheral failed\n"); return NULL; } +#endif ppi = kzalloc(sizeof(*ppi), GFP_KERNEL); if (!ppi) { @@ -329,6 +345,7 @@ struct ppi_if *ppi_create_instance(struct platform_device *pdev, } ppi->ops = &ppi_ops; ppi->info = info; + ppi->dev = &pdev->dev; pr_info("ppi probe success\n"); return ppi; diff --git a/include/media/blackfin/ppi.h b/include/media/blackfin/ppi.h index 61a283f2bd4b..4900baedd55a 100644 --- a/include/media/blackfin/ppi.h +++ b/include/media/blackfin/ppi.h @@ -83,6 +83,7 @@ struct ppi_info { }; struct ppi_if { + struct device *dev; unsigned long ppi_control; const struct ppi_ops *ops; const struct ppi_info *info; -- cgit v1.2.3 From 1dee9b59d69a15d566c16ee6fbd7216108ad5cac Mon Sep 17 00:00:00 2001 From: "Marcel J.E. Mol" Date: Sat, 26 Jul 2014 17:28:26 -0300 Subject: [media] rc: Add support for decoding XMP protocol This protocol is found on Dreambox remotes [m.chehab@samsung.com: CodingStyle fixes and conflict fix] Signed-off-by: Marcel Mol Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/Kconfig | 10 ++ drivers/media/rc/Makefile | 1 + drivers/media/rc/ir-xmp-decoder.c | 225 ++++++++++++++++++++++++++++++++++++++ drivers/media/rc/rc-core-priv.h | 12 ++ drivers/media/rc/rc-ir-raw.c | 1 + drivers/media/rc/rc-main.c | 1 + include/media/rc-map.h | 6 +- 7 files changed, 255 insertions(+), 1 deletion(-) create mode 100644 drivers/media/rc/ir-xmp-decoder.c (limited to 'include') diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index 9d15a57ac590..5e626af8e313 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -113,6 +113,16 @@ config IR_MCE_KBD_DECODER Enable this option if you have a Microsoft Remote Keyboard for Windows Media Center Edition, which you would like to use with a raw IR receiver in your system. + +config IR_XMP_DECODER + tristate "Enable IR raw decoder for the XMP protocol" + depends on RC_CORE + select BITREVERSE + default y + + ---help--- + Enable this option if you have IR with XMP protocol, and + if the IR is decoded in software endif #RC_DECODERS menuconfig RC_DEVICES diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile index 096eb0692a04..9f9843a1af5f 100644 --- a/drivers/media/rc/Makefile +++ b/drivers/media/rc/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_IR_SANYO_DECODER) += ir-sanyo-decoder.o obj-$(CONFIG_IR_SHARP_DECODER) += ir-sharp-decoder.o obj-$(CONFIG_IR_MCE_KBD_DECODER) += ir-mce_kbd-decoder.o obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o +obj-$(CONFIG_IR_XMP_DECODER) += ir-xmp-decoder.o # stand-alone IR receivers/transmitters obj-$(CONFIG_RC_ATI_REMOTE) += ati_remote.o diff --git a/drivers/media/rc/ir-xmp-decoder.c b/drivers/media/rc/ir-xmp-decoder.c new file mode 100644 index 000000000000..1017d4816e8d --- /dev/null +++ b/drivers/media/rc/ir-xmp-decoder.c @@ -0,0 +1,225 @@ +/* ir-xmp-decoder.c - handle XMP IR Pulse/Space protocol + * + * Copyright (C) 2014 by Marcel Mol + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2 of the License. + * + * This 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. + * + * - Based on info from http://www.hifi-remote.com + * - Ignore Toggle=9 frames + * - Ignore XMP-1 XMP-2 difference, always store 16 bit OBC + */ + +#include +#include +#include "rc-core-priv.h" + +#define XMP_UNIT 136000 /* ns */ +#define XMP_LEADER 210000 /* ns */ +#define XMP_NIBBLE_PREFIX 760000 /* ns */ +#define XMP_HALFFRAME_SPACE 13800000 /* ns */ +#define XMP_TRAILER_SPACE 20000000 /* should be 80ms but not all dureation supliers can go that high */ + +enum xmp_state { + STATE_INACTIVE, + STATE_LEADER_PULSE, + STATE_NIBBLE_SPACE, +}; + +/** + * ir_xmp_decode() - Decode one XMP pulse or space + * @dev: the struct rc_dev descriptor of the device + * @duration: the struct ir_raw_event descriptor of the pulse/space + * + * This function returns -EINVAL if the pulse violates the state machine + */ +static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev) +{ + struct xmp_dec *data = &dev->raw->xmp; + + if (!(dev->enabled_protocols & RC_BIT_XMP)) + return 0; + + if (!is_timing_event(ev)) { + if (ev.reset) + data->state = STATE_INACTIVE; + return 0; + } + + IR_dprintk(2, "XMP decode started at state %d %d (%uus %s)\n", + data->state, data->count, TO_US(ev.duration), TO_STR(ev.pulse)); + + switch (data->state) { + + case STATE_INACTIVE: + if (!ev.pulse) + break; + + if (eq_margin(ev.duration, XMP_LEADER, XMP_UNIT / 2)) { + data->count = 0; + data->state = STATE_NIBBLE_SPACE; + } + + return 0; + + case STATE_LEADER_PULSE: + if (!ev.pulse) + break; + + if (eq_margin(ev.duration, XMP_LEADER, XMP_UNIT / 2)) + data->state = STATE_NIBBLE_SPACE; + + return 0; + + case STATE_NIBBLE_SPACE: + if (ev.pulse) + break; + + if (geq_margin(ev.duration, XMP_TRAILER_SPACE, XMP_NIBBLE_PREFIX)) { + int divider, i; + u8 addr, subaddr, subaddr2, toggle, oem, obc1, obc2, sum1, sum2; + u32 *n; + u32 scancode; + + if (data->count != 16) { + IR_dprintk(2, "received TRAILER period at index %d: %u\n", + data->count, ev.duration); + data->state = STATE_INACTIVE; + return -EINVAL; + } + + n = data->durations; + /* + * the 4th nibble should be 15 so base the divider on this + * to transform durations into nibbles. Substract 2000 from + * the divider to compensate for fluctuations in the signal + */ + divider = (n[3] - XMP_NIBBLE_PREFIX) / 15 - 2000; + if (divider < 50) { + IR_dprintk(2, "divider to small %d.\n", divider); + data->state = STATE_INACTIVE; + return -EINVAL; + } + + /* convert to nibbles and do some sanity checks */ + for (i = 0; i < 16; i++) + n[i] = (n[i] - XMP_NIBBLE_PREFIX) / divider; + sum1 = (15 + n[0] + n[1] + n[2] + n[3] + + n[4] + n[5] + n[6] + n[7]) % 16; + sum2 = (15 + n[8] + n[9] + n[10] + n[11] + + n[12] + n[13] + n[14] + n[15]) % 16; + + if (sum1 != 15 || sum2 != 15) { + IR_dprintk(2, "checksum errors sum1=0x%X sum2=0x%X\n", + sum1, sum2); + data->state = STATE_INACTIVE; + return -EINVAL; + } + + subaddr = n[0] << 4 | n[2]; + subaddr2 = n[8] << 4 | n[11]; + oem = n[4] << 4 | n[5]; + addr = n[6] << 4 | n[7]; + toggle = n[10]; + obc1 = n[12] << 4 | n[13]; + obc2 = n[14] << 4 | n[15]; + if (subaddr != subaddr2) { + IR_dprintk(2, "subaddress nibbles mismatch 0x%02X != 0x%02X\n", + subaddr, subaddr2); + data->state = STATE_INACTIVE; + return -EINVAL; + } + if (oem != 0x44) + IR_dprintk(1, "Warning: OEM nibbles 0x%02X. Expected 0x44\n", + oem); + + scancode = addr << 24 | subaddr << 16 | + obc1 << 8 | obc2; + IR_dprintk(1, "XMP scancode 0x%06x\n", scancode); + + if (toggle == 0) { + rc_keydown(dev, RC_TYPE_XMP, scancode, 0); + } else { + rc_repeat(dev); + IR_dprintk(1, "Repeat last key\n"); + } + data->state = STATE_INACTIVE; + + return 0; + + } else if (geq_margin(ev.duration, XMP_HALFFRAME_SPACE, XMP_NIBBLE_PREFIX)) { + /* Expect 8 or 16 nibble pulses. 16 in case of 'final' frame */ + if (data->count == 16) { + IR_dprintk(2, "received half frame pulse at index %d. Probably a final frame key-up event: %u\n", + data->count, ev.duration); + /* + * TODO: for now go back to half frame position + * so trailer can be found and key press + * can be handled. + */ + data->count = 8; + } + + else if (data->count != 8) + IR_dprintk(2, "received half frame pulse at index %d: %u\n", + data->count, ev.duration); + data->state = STATE_LEADER_PULSE; + + return 0; + + } else if (geq_margin(ev.duration, XMP_NIBBLE_PREFIX, XMP_UNIT)) { + /* store nibble raw data, decode after trailer */ + if (data->count == 16) { + IR_dprintk(2, "to many pulses (%d) ignoring: %u\n", + data->count, ev.duration); + data->state = STATE_INACTIVE; + return -EINVAL; + } + data->durations[data->count] = ev.duration; + data->count++; + data->state = STATE_LEADER_PULSE; + + return 0; + + } + + break; + } + + IR_dprintk(1, "XMP decode failed at count %d state %d (%uus %s)\n", + data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + data->state = STATE_INACTIVE; + return -EINVAL; +} + +static struct ir_raw_handler xmp_handler = { + .protocols = RC_BIT_XMP, + .decode = ir_xmp_decode, +}; + +static int __init ir_xmp_decode_init(void) +{ + ir_raw_handler_register(&xmp_handler); + + printk(KERN_INFO "IR XMP protocol handler initialized\n"); + return 0; +} + +static void __exit ir_xmp_decode_exit(void) +{ + ir_raw_handler_unregister(&xmp_handler); +} + +module_init(ir_xmp_decode_init); +module_exit(ir_xmp_decode_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Marcel Mol "); +MODULE_AUTHOR("MESA Consulting (http://www.mesa.nl)"); +MODULE_DESCRIPTION("XMP IR protocol decoder"); diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h index dea7aff15a35..b68d4f762734 100644 --- a/drivers/media/rc/rc-core-priv.h +++ b/drivers/media/rc/rc-core-priv.h @@ -110,6 +110,11 @@ struct ir_raw_event_ctrl { bool send_timeout_reports; } lirc; + struct xmp_dec { + int state; + unsigned count; + u32 durations[16]; + } xmp; }; /* macros for IR decoders */ @@ -225,5 +230,12 @@ static inline void load_mce_kbd_decode(void) { } static inline void load_lirc_codec(void) { } #endif +/* from ir-xmp-decoder.c */ +#ifdef CONFIG_IR_XMP_DECODER_MODULE +#define load_xmp_decode() request_module_nowait("ir-xmp-decoder") +#else +static inline void load_xmp_decode(void) { } +#endif + #endif /* _RC_CORE_PRIV */ diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c index e772c4cf9f61..e8fff2add265 100644 --- a/drivers/media/rc/rc-ir-raw.c +++ b/drivers/media/rc/rc-ir-raw.c @@ -362,6 +362,7 @@ void ir_raw_init(void) load_sharp_decode(); load_mce_kbd_decode(); load_lirc_codec(); + load_xmp_decode(); /* If needed, we may later add some init code. In this case, it is needed to change the CONFIG_MODULE test at rc-core.h diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 39e4b5ffe141..dfceeb4e34a8 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -800,6 +800,7 @@ static struct { { RC_BIT_SHARP, "sharp" }, { RC_BIT_MCE_KBD, "mce_kbd" }, { RC_BIT_LIRC, "lirc" }, + { RC_BIT_XMP, "xmp" }, }; /** diff --git a/include/media/rc-map.h b/include/media/rc-map.h index 894c7e4c409b..80f951890b4c 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -31,6 +31,7 @@ enum rc_type { RC_TYPE_RC6_6A_32 = 16, /* Philips RC6-6A-32 protocol */ RC_TYPE_RC6_MCE = 17, /* MCE (Philips RC6-6A-32 subtype) protocol */ RC_TYPE_SHARP = 18, /* Sharp protocol */ + RC_TYPE_XMP = 19, /* XMP protocol */ }; #define RC_BIT_NONE 0 @@ -53,6 +54,7 @@ enum rc_type { #define RC_BIT_RC6_6A_32 (1 << RC_TYPE_RC6_6A_32) #define RC_BIT_RC6_MCE (1 << RC_TYPE_RC6_MCE) #define RC_BIT_SHARP (1 << RC_TYPE_SHARP) +#define RC_BIT_XMP (1 << RC_TYPE_XMP) #define RC_BIT_ALL (RC_BIT_UNKNOWN | RC_BIT_OTHER | RC_BIT_LIRC | \ RC_BIT_RC5 | RC_BIT_RC5X | RC_BIT_RC5_SZ | \ @@ -60,7 +62,9 @@ enum rc_type { RC_BIT_SONY12 | RC_BIT_SONY15 | RC_BIT_SONY20 | \ RC_BIT_NEC | RC_BIT_SANYO | RC_BIT_MCE_KBD | \ RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 | \ - RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE | RC_BIT_SHARP) + RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE | RC_BIT_SHARP | \ + RC_BIT_XMP) + #define RC_SCANCODE_UNKNOWN(x) (x) #define RC_SCANCODE_OTHER(x) (x) -- cgit v1.2.3 From 3a611c3cfba2106aed3187b90903855e776e2761 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 27 Jul 2014 07:23:01 +0930 Subject: modules: Fix build error in moduleloader.h Fengguang Wu's build bot detected that if moduleloader.h is included in a C file (used by ftrace and kprobes to access module_alloc() when available), that it can fail to build if CONFIG_MODULES and CONFIG_MODULES_USE_ELF_REL is not defined. This is because there's a printk() that dereferences struct module to print the name of the module. But as struct module does not exist when CONFIG_MODULES is not defined we get this error: include/linux/moduleloader.h: In function 'apply_relocate': >> include/linux/moduleloader.h:48:63: error: dereferencing pointer to >> incomplete type printk(KERN_ERR "module %s: REL relocation unsupported\n", me->name); ^ Reported-by: kbuild test robot Based-on-the-true-story-by: Steven Rostedt Confirms-rustys-story-ends-the-same-by: Steven Rostedt Signed-off-by: Rusty Russell --- include/linux/moduleloader.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/moduleloader.h b/include/linux/moduleloader.h index 560ca53a75fa..7eeb9bbfb816 100644 --- a/include/linux/moduleloader.h +++ b/include/linux/moduleloader.h @@ -45,7 +45,8 @@ static inline int apply_relocate(Elf_Shdr *sechdrs, unsigned int relsec, struct module *me) { - printk(KERN_ERR "module %s: REL relocation unsupported\n", me->name); + printk(KERN_ERR "module %s: REL relocation unsupported\n", + module_name(me)); return -ENOEXEC; } #endif @@ -67,7 +68,8 @@ static inline int apply_relocate_add(Elf_Shdr *sechdrs, unsigned int relsec, struct module *me) { - printk(KERN_ERR "module %s: REL relocation unsupported\n", me->name); + printk(KERN_ERR "module %s: REL relocation unsupported\n", + module_name(me)); return -ENOEXEC; } #endif -- cgit v1.2.3 From 9b20a352d78a7651aa68a9220f77ccb03009d892 Mon Sep 17 00:00:00 2001 From: Petr Mladek Date: Sun, 27 Jul 2014 07:24:01 +0930 Subject: module: add within_module() function It is just a small optimization that allows to replace few occurrences of within_module_init() || within_module_core() with a single call. Signed-off-by: Petr Mladek Signed-off-by: Rusty Russell --- include/linux/module.h | 5 +++++ kernel/module.c | 12 ++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/module.h b/include/linux/module.h index f520a767c86c..61d8fb2d0873 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -408,6 +408,11 @@ static inline int within_module_init(unsigned long addr, const struct module *mo addr < (unsigned long)mod->module_init + mod->init_size; } +static inline int within_module(unsigned long addr, const struct module *mod) +{ + return within_module_init(addr, mod) || within_module_core(addr, mod); +} + /* Search for module by name: must hold module_mutex. */ struct module *find_module(const char *name); diff --git a/kernel/module.c b/kernel/module.c index 81e727cf6df9..e87fdd2fc3c2 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -3448,8 +3448,7 @@ const char *module_address_lookup(unsigned long addr, list_for_each_entry_rcu(mod, &modules, list) { if (mod->state == MODULE_STATE_UNFORMED) continue; - if (within_module_init(addr, mod) || - within_module_core(addr, mod)) { + if (within_module(addr, mod)) { if (modname) *modname = mod->name; ret = get_ksymbol(mod, addr, size, offset); @@ -3473,8 +3472,7 @@ int lookup_module_symbol_name(unsigned long addr, char *symname) list_for_each_entry_rcu(mod, &modules, list) { if (mod->state == MODULE_STATE_UNFORMED) continue; - if (within_module_init(addr, mod) || - within_module_core(addr, mod)) { + if (within_module(addr, mod)) { const char *sym; sym = get_ksymbol(mod, addr, NULL, NULL); @@ -3499,8 +3497,7 @@ int lookup_module_symbol_attrs(unsigned long addr, unsigned long *size, list_for_each_entry_rcu(mod, &modules, list) { if (mod->state == MODULE_STATE_UNFORMED) continue; - if (within_module_init(addr, mod) || - within_module_core(addr, mod)) { + if (within_module(addr, mod)) { const char *sym; sym = get_ksymbol(mod, addr, size, offset); @@ -3764,8 +3761,7 @@ struct module *__module_address(unsigned long addr) list_for_each_entry_rcu(mod, &modules, list) { if (mod->state == MODULE_STATE_UNFORMED) continue; - if (within_module_core(addr, mod) - || within_module_init(addr, mod)) + if (within_module(addr, mod)) return mod; } return NULL; -- cgit v1.2.3 From 76681c8faa07f9e07caa3cc69f235c8719b2a6ea Mon Sep 17 00:00:00 2001 From: Petr Mladek Date: Sun, 27 Jul 2014 07:25:01 +0930 Subject: module: return bool from within_module*() The within_module*() functions return only true or false. Let's use bool as the return type. Note that it should not change kABI because these are inline functions. Signed-off-by: Petr Mladek Signed-off-by: Rusty Russell --- include/linux/module.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/module.h b/include/linux/module.h index 61d8fb2d0873..71f282a4e307 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -396,19 +396,21 @@ bool is_module_address(unsigned long addr); bool is_module_percpu_address(unsigned long addr); bool is_module_text_address(unsigned long addr); -static inline int within_module_core(unsigned long addr, const struct module *mod) +static inline bool within_module_core(unsigned long addr, + const struct module *mod) { return (unsigned long)mod->module_core <= addr && addr < (unsigned long)mod->module_core + mod->core_size; } -static inline int within_module_init(unsigned long addr, const struct module *mod) +static inline bool within_module_init(unsigned long addr, + const struct module *mod) { return (unsigned long)mod->module_init <= addr && addr < (unsigned long)mod->module_init + mod->init_size; } -static inline int within_module(unsigned long addr, const struct module *mod) +static inline bool within_module(unsigned long addr, const struct module *mod) { return within_module_init(addr, mod) || within_module_core(addr, mod); } -- cgit v1.2.3 From 37549e94c77a94a9c32b5ae3313a3801cb66adf9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 27 Jul 2014 07:26:01 +0930 Subject: sysfs: disallow world-writable files. This check was introduced in 2006 by Alexey Dobriyan (9774a1f54f173) for module parameters; we removed it when we unified the check into VERIFY_OCTAL_PERMISSIONS() as sysfs didn't have the same requirement. Now all those users are fixed, reintroduce it. Cc: Alexey Dobriyan Cc: Dave Jones Cc: Joe Perches Signed-off-by: Rusty Russell --- include/linux/kernel.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 4c52907a6d8b..43e1c6a9683e 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -849,5 +849,7 @@ static inline void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) { } /* User perms >= group perms >= other perms */ \ BUILD_BUG_ON_ZERO(((perms) >> 6) < (((perms) >> 3) & 7)) + \ BUILD_BUG_ON_ZERO((((perms) >> 3) & 7) < ((perms) & 7)) + \ + /* Other writable? Generally considered a bad idea. */ \ + BUILD_BUG_ON_ZERO((perms) & 2) + \ (perms)) #endif -- cgit v1.2.3 From 36c7778218b93d96d88d68f116a711f6a598b72f Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 24 Jul 2014 16:50:29 +0200 Subject: inet: frag: constify match, hashfn and constructor arguments Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- include/net/inet_frag.h | 11 ++++++----- include/net/ipv6.h | 4 ++-- net/ieee802154/reassembly.c | 14 +++++++------- net/ipv4/ip_fragment.c | 14 +++++++------- net/ipv6/netfilter/nf_conntrack_reasm.c | 2 +- net/ipv6/reassembly.c | 14 +++++++------- 6 files changed, 30 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h index 6f59de98dabd..15033057d44e 100644 --- a/include/net/inet_frag.h +++ b/include/net/inet_frag.h @@ -71,10 +71,11 @@ struct inet_frags { u32 rnd; int qsize; - unsigned int (*hashfn)(struct inet_frag_queue *); - bool (*match)(struct inet_frag_queue *q, void *arg); + unsigned int (*hashfn)(const struct inet_frag_queue *); + bool (*match)(const struct inet_frag_queue *q, + const void *arg); void (*constructor)(struct inet_frag_queue *q, - void *arg); + const void *arg); void (*destructor)(struct inet_frag_queue *); void (*skb_free)(struct sk_buff *); void (*frag_expire)(unsigned long data); @@ -131,9 +132,9 @@ static inline void init_frag_mem_limit(struct netns_frags *nf) percpu_counter_init(&nf->mem, 0); } -static inline int sum_frag_mem_limit(struct netns_frags *nf) +static inline unsigned int sum_frag_mem_limit(struct netns_frags *nf) { - int res; + unsigned int res; local_bh_disable(); res = percpu_counter_sum_positive(&nf->mem); diff --git a/include/net/ipv6.h b/include/net/ipv6.h index a25017247457..25c2170e1298 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -496,8 +496,8 @@ struct ip6_create_arg { u8 ecn; }; -void ip6_frag_init(struct inet_frag_queue *q, void *a); -bool ip6_frag_match(struct inet_frag_queue *q, void *a); +void ip6_frag_init(struct inet_frag_queue *q, const void *a); +bool ip6_frag_match(const struct inet_frag_queue *q, const void *a); /* * Equivalent of ipv4 struct ip diff --git a/net/ieee802154/reassembly.c b/net/ieee802154/reassembly.c index b85bd3f7048e..a5588be18024 100644 --- a/net/ieee802154/reassembly.c +++ b/net/ieee802154/reassembly.c @@ -61,18 +61,18 @@ static unsigned int lowpan_hash_frag(__be16 tag, u16 d_size, return c & (INETFRAGS_HASHSZ - 1); } -static unsigned int lowpan_hashfn(struct inet_frag_queue *q) +static unsigned int lowpan_hashfn(const struct inet_frag_queue *q) { - struct lowpan_frag_queue *fq; + const struct lowpan_frag_queue *fq; fq = container_of(q, struct lowpan_frag_queue, q); return lowpan_hash_frag(fq->tag, fq->d_size, &fq->saddr, &fq->daddr); } -static bool lowpan_frag_match(struct inet_frag_queue *q, void *a) +static bool lowpan_frag_match(const struct inet_frag_queue *q, const void *a) { - struct lowpan_frag_queue *fq; - struct lowpan_create_arg *arg = a; + const struct lowpan_frag_queue *fq; + const struct lowpan_create_arg *arg = a; fq = container_of(q, struct lowpan_frag_queue, q); return fq->tag == arg->tag && fq->d_size == arg->d_size && @@ -80,10 +80,10 @@ static bool lowpan_frag_match(struct inet_frag_queue *q, void *a) ieee802154_addr_equal(&fq->daddr, arg->dst); } -static void lowpan_frag_init(struct inet_frag_queue *q, void *a) +static void lowpan_frag_init(struct inet_frag_queue *q, const void *a) { + const struct lowpan_create_arg *arg = a; struct lowpan_frag_queue *fq; - struct lowpan_create_arg *arg = a; fq = container_of(q, struct lowpan_frag_queue, q); diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index ed32313e307c..586e6aaf9e6e 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -112,18 +112,18 @@ static unsigned int ipqhashfn(__be16 id, __be32 saddr, __be32 daddr, u8 prot) ip4_frags.rnd) & (INETFRAGS_HASHSZ - 1); } -static unsigned int ip4_hashfn(struct inet_frag_queue *q) +static unsigned int ip4_hashfn(const struct inet_frag_queue *q) { - struct ipq *ipq; + const struct ipq *ipq; ipq = container_of(q, struct ipq, q); return ipqhashfn(ipq->id, ipq->saddr, ipq->daddr, ipq->protocol); } -static bool ip4_frag_match(struct inet_frag_queue *q, void *a) +static bool ip4_frag_match(const struct inet_frag_queue *q, const void *a) { - struct ipq *qp; - struct ip4_create_arg *arg = a; + const struct ipq *qp; + const struct ip4_create_arg *arg = a; qp = container_of(q, struct ipq, q); return qp->id == arg->iph->id && @@ -133,14 +133,14 @@ static bool ip4_frag_match(struct inet_frag_queue *q, void *a) qp->user == arg->user; } -static void ip4_frag_init(struct inet_frag_queue *q, void *a) +static void ip4_frag_init(struct inet_frag_queue *q, const 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; + const struct ip4_create_arg *arg = a; qp->protocol = arg->iph->protocol; qp->id = arg->iph->id; diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 0d5279fd852a..c2c3f2116bc5 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -156,7 +156,7 @@ static unsigned int nf_hash_frag(__be32 id, const struct in6_addr *saddr, } -static unsigned int nf_hashfn(struct inet_frag_queue *q) +static unsigned int nf_hashfn(const struct inet_frag_queue *q) { const struct frag_queue *nq; diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index cc85a9ba5010..0c6932cc08cb 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -94,18 +94,18 @@ static unsigned int inet6_hash_frag(__be32 id, const struct in6_addr *saddr, return c & (INETFRAGS_HASHSZ - 1); } -static unsigned int ip6_hashfn(struct inet_frag_queue *q) +static unsigned int ip6_hashfn(const struct inet_frag_queue *q) { - struct frag_queue *fq; + const struct frag_queue *fq; fq = container_of(q, struct frag_queue, q); return inet6_hash_frag(fq->id, &fq->saddr, &fq->daddr); } -bool ip6_frag_match(struct inet_frag_queue *q, void *a) +bool ip6_frag_match(const struct inet_frag_queue *q, const void *a) { - struct frag_queue *fq; - struct ip6_create_arg *arg = a; + const struct frag_queue *fq; + const struct ip6_create_arg *arg = a; fq = container_of(q, struct frag_queue, q); return fq->id == arg->id && @@ -115,10 +115,10 @@ bool ip6_frag_match(struct inet_frag_queue *q, void *a) } EXPORT_SYMBOL(ip6_frag_match); -void ip6_frag_init(struct inet_frag_queue *q, void *a) +void ip6_frag_init(struct inet_frag_queue *q, const void *a) { struct frag_queue *fq = container_of(q, struct frag_queue, q); - struct ip6_create_arg *arg = a; + const struct ip6_create_arg *arg = a; fq->id = arg->id; fq->user = arg->user; -- cgit v1.2.3 From 86e93e470cadedda9181a2bd9aee1d9d2e5e9c0f Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 24 Jul 2014 16:50:31 +0200 Subject: inet: frag: move evictor calls into frag_find function First step to move eviction handling into a work queue. We lose two spots that accounted evicted fragments in MIB counters. Accounting will be restored since the upcoming work-queue evictor invokes the frag queue timer callbacks instead. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- include/net/inet_frag.h | 1 - net/ieee802154/reassembly.c | 2 -- net/ipv4/inet_fragment.c | 16 +++++++++------- net/ipv4/ip_fragment.c | 15 --------------- net/ipv6/netfilter/nf_conntrack_reasm.c | 4 ---- net/ipv6/reassembly.c | 6 ------ 6 files changed, 9 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h index 15033057d44e..9fe644d1a26e 100644 --- a/include/net/inet_frag.h +++ b/include/net/inet_frag.h @@ -90,7 +90,6 @@ void inet_frags_exit_net(struct netns_frags *nf, struct inet_frags *f); void inet_frag_kill(struct inet_frag_queue *q, struct inet_frags *f); void inet_frag_destroy(struct inet_frag_queue *q, struct inet_frags *f, int *work); -int inet_frag_evictor(struct netns_frags *nf, struct inet_frags *f, bool force); struct inet_frag_queue *inet_frag_find(struct netns_frags *nf, struct inet_frags *f, void *key, unsigned int hash) __releases(&f->lock); diff --git a/net/ieee802154/reassembly.c b/net/ieee802154/reassembly.c index a707995fd4d7..9503a48556f7 100644 --- a/net/ieee802154/reassembly.c +++ b/net/ieee802154/reassembly.c @@ -369,8 +369,6 @@ int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type) if (frag_info->d_size > ieee802154_lowpan->max_dsize) goto err; - inet_frag_evictor(&ieee802154_lowpan->frags, &lowpan_frags, false); - fq = fq_find(net, frag_info, &source, &dest); if (fq != NULL) { int ret; diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index 930d23870811..535636017534 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -46,6 +46,8 @@ const u8 ip_frag_ecn_table[16] = { }; EXPORT_SYMBOL(ip_frag_ecn_table); +static int inet_frag_evictor(struct netns_frags *nf, struct inet_frags *f, bool force); + static unsigned int inet_frag_hashfn(const struct inet_frags *f, const struct inet_frag_queue *q) { @@ -203,16 +205,11 @@ void inet_frag_destroy(struct inet_frag_queue *q, struct inet_frags *f, } EXPORT_SYMBOL(inet_frag_destroy); -int inet_frag_evictor(struct netns_frags *nf, struct inet_frags *f, bool force) +static int inet_frag_evictor(struct netns_frags *nf, struct inet_frags *f, bool force) { struct inet_frag_queue *q; int work, evicted = 0; - if (!force) { - if (frag_mem_limit(nf) <= nf->high_thresh) - return 0; - } - work = frag_mem_limit(nf) - nf->low_thresh; while (work > 0 || force) { spin_lock(&nf->lru_lock); @@ -242,7 +239,6 @@ int inet_frag_evictor(struct netns_frags *nf, struct inet_frags *f, bool force) return evicted; } -EXPORT_SYMBOL(inet_frag_evictor); static struct inet_frag_queue *inet_frag_intern(struct netns_frags *nf, struct inet_frag_queue *qp_in, struct inet_frags *f, @@ -296,6 +292,9 @@ static struct inet_frag_queue *inet_frag_alloc(struct netns_frags *nf, { struct inet_frag_queue *q; + if (frag_mem_limit(nf) > nf->high_thresh) + return NULL; + q = kzalloc(f->qsize, GFP_ATOMIC); if (q == NULL) return NULL; @@ -332,6 +331,9 @@ struct inet_frag_queue *inet_frag_find(struct netns_frags *nf, struct inet_frag_queue *q; int depth = 0; + if (frag_mem_limit(nf) > nf->high_thresh) + inet_frag_evictor(nf, f, false); + hash &= (INETFRAGS_HASHSZ - 1); hb = &f->hash[hash]; diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index b769eb6c83c0..54988672d00d 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -177,18 +177,6 @@ static void ipq_kill(struct ipq *ipq) inet_frag_kill(&ipq->q, &ip4_frags); } -/* Memory limiting on fragments. Evictor trashes the oldest - * fragment queue until we are back under the threshold. - */ -static void ip_evictor(struct net *net) -{ - int evicted; - - evicted = inet_frag_evictor(&net->ipv4.frags, &ip4_frags, false); - if (evicted) - IP_ADD_STATS_BH(net, IPSTATS_MIB_REASMFAILS, evicted); -} - /* * Oops, a fragment queue timed out. Kill it and send an ICMP reply. */ @@ -655,9 +643,6 @@ int ip_defrag(struct sk_buff *skb, u32 user) net = skb->dev ? dev_net(skb->dev) : dev_net(skb_dst(skb)->dev); IP_INC_STATS_BH(net, IPSTATS_MIB_REASMREQDS); - /* Start by cleaning up the memory. */ - ip_evictor(net); - /* Lookup (or create) queue header */ if ((qp = ip_find(net, ip_hdr(skb), user)) != NULL) { int ret; diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 607e4a94ef41..58e32cf91c95 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -594,10 +594,6 @@ struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb, u32 user) hdr = ipv6_hdr(clone); fhdr = (struct frag_hdr *)skb_transport_header(clone); - local_bh_disable(); - inet_frag_evictor(&net->nf_frag.frags, &nf_frags, false); - local_bh_enable(); - fq = fq_find(net, fhdr->identification, user, &hdr->saddr, &hdr->daddr, ip6_frag_ecn(hdr)); if (fq == NULL) { diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 2b76549a1016..97acbc490d9e 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -519,7 +519,6 @@ static int ipv6_frag_rcv(struct sk_buff *skb) struct frag_queue *fq; const struct ipv6hdr *hdr = ipv6_hdr(skb); struct net *net = dev_net(skb_dst(skb)->dev); - int evicted; if (IP6CB(skb)->flags & IP6SKB_FRAGMENTED) goto fail_hdr; @@ -548,11 +547,6 @@ static int ipv6_frag_rcv(struct sk_buff *skb) return 1; } - evicted = inet_frag_evictor(&net->ipv6.frags, &ip6_frags, false); - if (evicted) - IP6_ADD_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), - IPSTATS_MIB_REASMFAILS, evicted); - fq = fq_find(net, fhdr->identification, &hdr->saddr, &hdr->daddr, ip6_frag_ecn(hdr)); if (fq != NULL) { -- cgit v1.2.3 From b13d3cbfb8e8a8f53930af67d1ebf05149f32c24 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 24 Jul 2014 16:50:32 +0200 Subject: inet: frag: move eviction of queues to work queue When the high_thresh limit is reached we try to toss the 'oldest' incomplete fragment queues until memory limits are below the low_thresh value. This happens in softirq/packet processing context. This has two drawbacks: 1) processors might evict a queue that was about to be completed by another cpu, because they will compete wrt. resource usage and resource reclaim. 2) LRU list maintenance is expensive. But when constantly overloaded, even the 'least recently used' element is recent, so removing 'lru' queue first is not 'fairer' than removing any other fragment queue. This moves eviction out of the fast path: When the low threshold is reached, a work queue is scheduled which then iterates over the table and removes the queues that exceed the memory limits of the namespace. It sets a new flag called INET_FRAG_EVICTED on the evicted queues so the proper counters will get incremented when the queue is forcefully expired. When the high threshold is reached, no more fragment queues are created until we're below the limit again. The LRU list is now unused and will be removed in a followup patch. Joint work with Nikolay Aleksandrov. Suggested-by: Eric Dumazet Signed-off-by: Florian Westphal Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 4 +- include/net/inet_frag.h | 6 +- net/ipv4/inet_fragment.c | 142 +++++++++++++++++++++++---------- net/ipv4/ip_fragment.c | 3 +- net/ipv6/reassembly.c | 4 +- 5 files changed, 112 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index f35bfe43bf7a..625c8dda4be7 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -104,7 +104,9 @@ ipfrag_high_thresh - INTEGER is reached. ipfrag_low_thresh - INTEGER - See ipfrag_high_thresh + Maximum memory used to reassemble IP fragments before the kernel + begins to remove incomplete fragment queues to free up resources. + The kernel still accepts new fragments for defragmentation. ipfrag_time - INTEGER Time in seconds to keep an IP fragment in memory. diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h index 9fe644d1a26e..e975032ea11b 100644 --- a/include/net/inet_frag.h +++ b/include/net/inet_frag.h @@ -32,6 +32,7 @@ struct inet_frag_queue { int meat; __u8 last_in; /* first/last segment arrived? */ +#define INET_FRAG_EVICTED 8 #define INET_FRAG_COMPLETE 4 #define INET_FRAG_FIRST_IN 2 #define INET_FRAG_LAST_IN 1 @@ -48,7 +49,7 @@ struct inet_frag_queue { * rounded up (SKB_TRUELEN(0) + sizeof(struct ipq or * struct frag_queue)) */ -#define INETFRAGS_MAXDEPTH 128 +#define INETFRAGS_MAXDEPTH 128 struct inet_frag_bucket { struct hlist_head chain; @@ -65,6 +66,9 @@ struct inet_frags { int secret_interval; struct timer_list secret_timer; + struct work_struct frags_work; + unsigned int next_bucket; + /* The first call to hashfn is responsible to initialize * rnd. This is best done with net_get_random_once. */ diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index 535636017534..43315ecb9400 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -25,6 +25,9 @@ #include #include +#define INETFRAGS_EVICT_BUCKETS 128 +#define INETFRAGS_EVICT_MAX 512 + /* Given the OR values of all fragments, apply RFC 3168 5.3 requirements * Value : 0xff if frame should be dropped. * 0 or INET_ECN_CE value, to be ORed in to final iph->tos field @@ -46,8 +49,6 @@ const u8 ip_frag_ecn_table[16] = { }; EXPORT_SYMBOL(ip_frag_ecn_table); -static int inet_frag_evictor(struct netns_frags *nf, struct inet_frags *f, bool force); - static unsigned int inet_frag_hashfn(const struct inet_frags *f, const struct inet_frag_queue *q) { @@ -89,10 +90,92 @@ static void inet_frag_secret_rebuild(unsigned long dummy) mod_timer(&f->secret_timer, now + f->secret_interval); } +static bool inet_fragq_should_evict(const struct inet_frag_queue *q) +{ + return q->net->low_thresh == 0 || + frag_mem_limit(q->net) >= q->net->low_thresh; +} + +static unsigned int +inet_evict_bucket(struct inet_frags *f, struct inet_frag_bucket *hb) +{ + struct inet_frag_queue *fq; + struct hlist_node *n; + unsigned int evicted = 0; + HLIST_HEAD(expired); + +evict_again: + spin_lock(&hb->chain_lock); + + hlist_for_each_entry_safe(fq, n, &hb->chain, list) { + if (!inet_fragq_should_evict(fq)) + continue; + + if (!del_timer(&fq->timer)) { + /* q expiring right now thus increment its refcount so + * it won't be freed under us and wait until the timer + * has finished executing then destroy it + */ + atomic_inc(&fq->refcnt); + spin_unlock(&hb->chain_lock); + del_timer_sync(&fq->timer); + WARN_ON(atomic_read(&fq->refcnt) != 1); + inet_frag_put(fq, f); + goto evict_again; + } + + /* suppress xmit of (icmp) error packet */ + fq->last_in &= ~INET_FRAG_FIRST_IN; + fq->last_in |= INET_FRAG_EVICTED; + hlist_del(&fq->list); + hlist_add_head(&fq->list, &expired); + ++evicted; + } + + spin_unlock(&hb->chain_lock); + + hlist_for_each_entry_safe(fq, n, &expired, list) + f->frag_expire((unsigned long) fq); + + return evicted; +} + +static void inet_frag_worker(struct work_struct *work) +{ + unsigned int budget = INETFRAGS_EVICT_BUCKETS; + unsigned int i, evicted = 0; + struct inet_frags *f; + + f = container_of(work, struct inet_frags, frags_work); + + BUILD_BUG_ON(INETFRAGS_EVICT_BUCKETS >= INETFRAGS_HASHSZ); + + read_lock_bh(&f->lock); + + for (i = ACCESS_ONCE(f->next_bucket); budget; --budget) { + evicted += inet_evict_bucket(f, &f->hash[i]); + i = (i + 1) & (INETFRAGS_HASHSZ - 1); + if (evicted > INETFRAGS_EVICT_MAX) + break; + } + + f->next_bucket = i; + + read_unlock_bh(&f->lock); +} + +static void inet_frag_schedule_worker(struct inet_frags *f) +{ + if (unlikely(!work_pending(&f->frags_work))) + schedule_work(&f->frags_work); +} + void inet_frags_init(struct inet_frags *f) { int i; + INIT_WORK(&f->frags_work, inet_frag_worker); + for (i = 0; i < INETFRAGS_HASHSZ; i++) { struct inet_frag_bucket *hb = &f->hash[i]; @@ -120,16 +203,22 @@ EXPORT_SYMBOL(inet_frags_init_net); void inet_frags_fini(struct inet_frags *f) { del_timer(&f->secret_timer); + cancel_work_sync(&f->frags_work); } EXPORT_SYMBOL(inet_frags_fini); void inet_frags_exit_net(struct netns_frags *nf, struct inet_frags *f) { + int i; + nf->low_thresh = 0; - local_bh_disable(); - inet_frag_evictor(nf, f, true); - local_bh_enable(); + read_lock_bh(&f->lock); + + for (i = 0; i < INETFRAGS_HASHSZ ; i++) + inet_evict_bucket(f, &f->hash[i]); + + read_unlock_bh(&f->lock); percpu_counter_destroy(&nf->mem); } @@ -205,41 +294,6 @@ void inet_frag_destroy(struct inet_frag_queue *q, struct inet_frags *f, } EXPORT_SYMBOL(inet_frag_destroy); -static int inet_frag_evictor(struct netns_frags *nf, struct inet_frags *f, bool force) -{ - struct inet_frag_queue *q; - int work, evicted = 0; - - work = frag_mem_limit(nf) - nf->low_thresh; - while (work > 0 || force) { - spin_lock(&nf->lru_lock); - - if (list_empty(&nf->lru_list)) { - spin_unlock(&nf->lru_lock); - break; - } - - q = list_first_entry(&nf->lru_list, - struct inet_frag_queue, lru_list); - atomic_inc(&q->refcnt); - /* Remove q from list to avoid several CPUs grabbing it */ - list_del_init(&q->lru_list); - - spin_unlock(&nf->lru_lock); - - spin_lock(&q->lock); - if (!(q->last_in & INET_FRAG_COMPLETE)) - inet_frag_kill(q, f); - spin_unlock(&q->lock); - - if (atomic_dec_and_test(&q->refcnt)) - inet_frag_destroy(q, f, &work); - evicted++; - } - - return evicted; -} - static struct inet_frag_queue *inet_frag_intern(struct netns_frags *nf, struct inet_frag_queue *qp_in, struct inet_frags *f, void *arg) @@ -292,8 +346,10 @@ static struct inet_frag_queue *inet_frag_alloc(struct netns_frags *nf, { struct inet_frag_queue *q; - if (frag_mem_limit(nf) > nf->high_thresh) + if (frag_mem_limit(nf) > nf->high_thresh) { + inet_frag_schedule_worker(f); return NULL; + } q = kzalloc(f->qsize, GFP_ATOMIC); if (q == NULL) @@ -331,8 +387,8 @@ struct inet_frag_queue *inet_frag_find(struct netns_frags *nf, struct inet_frag_queue *q; int depth = 0; - if (frag_mem_limit(nf) > nf->high_thresh) - inet_frag_evictor(nf, f, false); + if (frag_mem_limit(nf) > nf->low_thresh) + inet_frag_schedule_worker(f); hash &= (INETFRAGS_HASHSZ - 1); hb = &f->hash[hash]; diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 54988672d00d..54bd170c5eb4 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -195,7 +195,8 @@ static void ip_expire(unsigned long arg) ipq_kill(qp); - IP_INC_STATS_BH(net, IPSTATS_MIB_REASMTIMEOUT); + if (!(qp->q.last_in & INET_FRAG_EVICTED)) + IP_INC_STATS_BH(net, IPSTATS_MIB_REASMTIMEOUT); IP_INC_STATS_BH(net, IPSTATS_MIB_REASMFAILS); if ((qp->q.last_in & INET_FRAG_FIRST_IN) && qp->q.fragments != NULL) { diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 97acbc490d9e..b3924b10dff3 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -141,7 +141,9 @@ void ip6_expire_frag_queue(struct net *net, struct frag_queue *fq, if (!dev) goto out_rcu_unlock; - IP6_INC_STATS_BH(net, __in6_dev_get(dev), IPSTATS_MIB_REASMTIMEOUT); + if (!(fq->q.last_in & INET_FRAG_EVICTED)) + IP6_INC_STATS_BH(net, __in6_dev_get(dev), + IPSTATS_MIB_REASMTIMEOUT); IP6_INC_STATS_BH(net, __in6_dev_get(dev), IPSTATS_MIB_REASMFAILS); /* Don't send error if the first segment did not arrive. */ -- cgit v1.2.3 From 434d305405ab86414f6ea3f261307d443a2c3506 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 24 Jul 2014 16:50:33 +0200 Subject: inet: frag: don't account number of fragment queues The 'nqueues' counter is protected by the lru list lock, once thats removed this needs to be converted to atomic counter. Given this isn't used for anything except for reporting it to userspace via /proc, just remove it. We still report the memory currently used by fragment reassembly queues. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- include/net/inet_frag.h | 3 --- include/net/ip.h | 1 - include/net/ipv6.h | 5 ----- net/ipv4/inet_fragment.c | 1 - net/ipv4/ip_fragment.c | 5 ----- net/ipv4/proc.c | 5 +++-- net/ipv6/proc.c | 4 ++-- 7 files changed, 5 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h index e975032ea11b..68de33765705 100644 --- a/include/net/inet_frag.h +++ b/include/net/inet_frag.h @@ -4,7 +4,6 @@ #include struct netns_frags { - int nqueues; struct list_head lru_list; spinlock_t lru_lock; @@ -158,7 +157,6 @@ static inline void inet_frag_lru_del(struct inet_frag_queue *q) { spin_lock(&q->net->lru_lock); list_del_init(&q->lru_list); - q->net->nqueues--; spin_unlock(&q->net->lru_lock); } @@ -167,7 +165,6 @@ static inline void inet_frag_lru_add(struct netns_frags *nf, { spin_lock(&nf->lru_lock); list_add_tail(&q->lru_list, &nf->lru_list); - q->net->nqueues++; spin_unlock(&nf->lru_lock); } diff --git a/include/net/ip.h b/include/net/ip.h index 2e8f055989c3..ca14799545fd 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -495,7 +495,6 @@ static inline struct sk_buff *ip_check_defrag(struct sk_buff *skb, u32 user) } #endif int ip_frag_mem(struct net *net); -int ip_frag_nqueues(struct net *net); /* * Functions provided by ip_forward.c diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 25c2170e1298..a2db816e8461 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -299,11 +299,6 @@ static inline bool ipv6_accept_ra(struct inet6_dev *idev) } #if IS_ENABLED(CONFIG_IPV6) -static inline int ip6_frag_nqueues(struct net *net) -{ - return net->ipv6.frags.nqueues; -} - static inline int ip6_frag_mem(struct net *net) { return sum_frag_mem_limit(&net->ipv6.frags); diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index 43315ecb9400..231ca0b40811 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -193,7 +193,6 @@ EXPORT_SYMBOL(inet_frags_init); void inet_frags_init_net(struct netns_frags *nf) { - nf->nqueues = 0; init_frag_mem_limit(nf); INIT_LIST_HEAD(&nf->lru_list); spin_lock_init(&nf->lru_lock); diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 54bd170c5eb4..1f42c2e3966b 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -86,11 +86,6 @@ static inline u8 ip4_frag_ecn(u8 tos) static struct inet_frags ip4_frags; -int ip_frag_nqueues(struct net *net) -{ - return net->ipv4.frags.nqueues; -} - int ip_frag_mem(struct net *net) { return sum_frag_mem_limit(&net->ipv4.frags); diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index ae0af9386f7c..8e3eb39f84e7 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -52,6 +52,7 @@ static int sockstat_seq_show(struct seq_file *seq, void *v) { struct net *net = seq->private; + unsigned int frag_mem; int orphans, sockets; local_bh_disable(); @@ -71,8 +72,8 @@ static int sockstat_seq_show(struct seq_file *seq, void *v) sock_prot_inuse_get(net, &udplite_prot)); seq_printf(seq, "RAW: inuse %d\n", sock_prot_inuse_get(net, &raw_prot)); - seq_printf(seq, "FRAG: inuse %d memory %d\n", - ip_frag_nqueues(net), ip_frag_mem(net)); + frag_mem = ip_frag_mem(net); + seq_printf(seq, "FRAG: inuse %u memory %u\n", !!frag_mem, frag_mem); return 0; } diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c index 3317440ea341..2d6f860e5c1e 100644 --- a/net/ipv6/proc.c +++ b/net/ipv6/proc.c @@ -33,6 +33,7 @@ static int sockstat6_seq_show(struct seq_file *seq, void *v) { struct net *net = seq->private; + unsigned int frag_mem = ip6_frag_mem(net); seq_printf(seq, "TCP6: inuse %d\n", sock_prot_inuse_get(net, &tcpv6_prot)); @@ -42,8 +43,7 @@ static int sockstat6_seq_show(struct seq_file *seq, void *v) sock_prot_inuse_get(net, &udplitev6_prot)); seq_printf(seq, "RAW6: inuse %d\n", sock_prot_inuse_get(net, &rawv6_prot)); - seq_printf(seq, "FRAG6: inuse %d memory %d\n", - ip6_frag_nqueues(net), ip6_frag_mem(net)); + seq_printf(seq, "FRAG6: inuse %u memory %u\n", !!frag_mem, frag_mem); return 0; } -- cgit v1.2.3 From 3fd588eb90bfbba17091381006ecafe29c45db4a Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 24 Jul 2014 16:50:34 +0200 Subject: inet: frag: remove lru list no longer used. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- include/net/inet_frag.h | 32 ++------------------------------ net/ieee802154/reassembly.c | 1 - net/ipv4/inet_fragment.c | 12 ++---------- net/ipv4/ip_fragment.c | 1 - net/ipv6/netfilter/nf_conntrack_reasm.c | 1 - net/ipv6/reassembly.c | 1 - 6 files changed, 4 insertions(+), 44 deletions(-) (limited to 'include') diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h index 68de33765705..90d21ea62c59 100644 --- a/include/net/inet_frag.h +++ b/include/net/inet_frag.h @@ -4,9 +4,6 @@ #include struct netns_frags { - struct list_head lru_list; - spinlock_t lru_lock; - /* The percpu_counter "mem" need to be cacheline aligned. * mem.count must not share cacheline with other writers */ @@ -21,7 +18,6 @@ struct netns_frags { struct inet_frag_queue { spinlock_t lock; struct timer_list timer; /* when will this queue expire? */ - struct list_head lru_list; /* lru list member */ struct hlist_node list; atomic_t refcnt; struct sk_buff *fragments; /* list of received fragments */ @@ -91,8 +87,7 @@ void inet_frags_init_net(struct netns_frags *nf); void inet_frags_exit_net(struct netns_frags *nf, struct inet_frags *f); void inet_frag_kill(struct inet_frag_queue *q, struct inet_frags *f); -void inet_frag_destroy(struct inet_frag_queue *q, - struct inet_frags *f, int *work); +void inet_frag_destroy(struct inet_frag_queue *q, struct inet_frags *f); struct inet_frag_queue *inet_frag_find(struct netns_frags *nf, struct inet_frags *f, void *key, unsigned int hash) __releases(&f->lock); @@ -102,7 +97,7 @@ void inet_frag_maybe_warn_overflow(struct inet_frag_queue *q, static inline void inet_frag_put(struct inet_frag_queue *q, struct inet_frags *f) { if (atomic_dec_and_test(&q->refcnt)) - inet_frag_destroy(q, f, NULL); + inet_frag_destroy(q, f); } /* Memory Tracking Functions. */ @@ -145,29 +140,6 @@ static inline unsigned int sum_frag_mem_limit(struct netns_frags *nf) return res; } -static inline void inet_frag_lru_move(struct inet_frag_queue *q) -{ - spin_lock(&q->net->lru_lock); - if (!list_empty(&q->lru_list)) - list_move_tail(&q->lru_list, &q->net->lru_list); - spin_unlock(&q->net->lru_lock); -} - -static inline void inet_frag_lru_del(struct inet_frag_queue *q) -{ - spin_lock(&q->net->lru_lock); - list_del_init(&q->lru_list); - spin_unlock(&q->net->lru_lock); -} - -static inline void inet_frag_lru_add(struct netns_frags *nf, - struct inet_frag_queue *q) -{ - spin_lock(&nf->lru_lock); - list_add_tail(&q->lru_list, &nf->lru_list); - spin_unlock(&nf->lru_lock); -} - /* RFC 3168 support : * We want to check ECN values of all fragments, do detect invalid combinations. * In ipq->ecn, we store the OR value of each ip4_frag_ecn() fragment value. diff --git a/net/ieee802154/reassembly.c b/net/ieee802154/reassembly.c index 9503a48556f7..b4bc7a50eccf 100644 --- a/net/ieee802154/reassembly.c +++ b/net/ieee802154/reassembly.c @@ -219,7 +219,6 @@ found: return res; } - inet_frag_lru_move(&fq->q); return -1; err: kfree_skb(skb); diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index 231ca0b40811..198a5ed7a815 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -194,8 +194,6 @@ EXPORT_SYMBOL(inet_frags_init); void inet_frags_init_net(struct netns_frags *nf) { init_frag_mem_limit(nf); - INIT_LIST_HEAD(&nf->lru_list); - spin_lock_init(&nf->lru_lock); } EXPORT_SYMBOL(inet_frags_init_net); @@ -237,7 +235,6 @@ static inline void fq_unlink(struct inet_frag_queue *fq, struct inet_frags *f) spin_unlock(&hb->chain_lock); read_unlock(&f->lock); - inet_frag_lru_del(fq); } void inet_frag_kill(struct inet_frag_queue *fq, struct inet_frags *f) @@ -261,8 +258,7 @@ static inline void frag_kfree_skb(struct netns_frags *nf, struct inet_frags *f, kfree_skb(skb); } -void inet_frag_destroy(struct inet_frag_queue *q, struct inet_frags *f, - int *work) +void inet_frag_destroy(struct inet_frag_queue *q, struct inet_frags *f) { struct sk_buff *fp; struct netns_frags *nf; @@ -282,14 +278,11 @@ void inet_frag_destroy(struct inet_frag_queue *q, struct inet_frags *f, fp = xp; } sum = sum_truesize + f->qsize; - if (work) - *work -= sum; sub_frag_mem_limit(q, sum); if (f->destructor) f->destructor(q); kfree(q); - } EXPORT_SYMBOL(inet_frag_destroy); @@ -333,7 +326,7 @@ static struct inet_frag_queue *inet_frag_intern(struct netns_frags *nf, atomic_inc(&qp->refcnt); hlist_add_head(&qp->list, &hb->chain); - inet_frag_lru_add(nf, qp); + spin_unlock(&hb->chain_lock); read_unlock(&f->lock); @@ -361,7 +354,6 @@ static struct inet_frag_queue *inet_frag_alloc(struct netns_frags *nf, setup_timer(&q->timer, f->frag_expire, (unsigned long)q); spin_lock_init(&q->lock); atomic_set(&q->refcnt, 1); - INIT_LIST_HEAD(&q->lru_list); return q; } diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 1f42c2e3966b..8fbeee495037 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -489,7 +489,6 @@ found: } skb_dst_drop(skb); - inet_frag_lru_move(&qp->q); return -EINPROGRESS; err: diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 58e32cf91c95..fb0f72a0ff31 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -349,7 +349,6 @@ found: fq->q.last_in |= INET_FRAG_FIRST_IN; } - inet_frag_lru_move(&fq->q); return 0; discard_fq: diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index b3924b10dff3..af85551682c2 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -351,7 +351,6 @@ found: } skb_dst_drop(skb); - inet_frag_lru_move(&fq->q); return -1; discard_fq: -- cgit v1.2.3 From e3a57d18b06179d68fcf7a0a06ad844493c65e06 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 24 Jul 2014 16:50:35 +0200 Subject: inet: frag: remove periodic secret rebuild timer merge functionality into the eviction workqueue. Instead of rebuilding every n seconds, take advantage of the upper hash chain length limit. If we hit it, mark table for rebuild and schedule workqueue. To prevent frequent rebuilds when we're completely overloaded, don't rebuild more than once every 5 seconds. ipfrag_secret_interval sysctl is now obsolete and has been marked as deprecated, it still can be changed so scripts won't be broken but it won't have any effect. A comment is left above each unused secret_timer variable to avoid confusion. Joint work with Nikolay Aleksandrov. Signed-off-by: Florian Westphal Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 10 -------- include/net/inet_frag.h | 4 +-- net/ieee802154/reassembly.c | 5 ++-- net/ipv4/inet_fragment.c | 43 ++++++++++++++++++++++----------- net/ipv4/ip_fragment.c | 5 ++-- net/ipv6/netfilter/nf_conntrack_reasm.c | 1 - net/ipv6/reassembly.c | 5 ++-- 7 files changed, 40 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 625c8dda4be7..e8c304e37831 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -111,11 +111,6 @@ ipfrag_low_thresh - INTEGER ipfrag_time - INTEGER Time in seconds to keep an IP fragment in memory. -ipfrag_secret_interval - INTEGER - Regeneration interval (in seconds) of the hash secret (or lifetime - for the hash secret) for IP fragments. - Default: 600 - ipfrag_max_dist - INTEGER ipfrag_max_dist is a non-negative integer value which defines the maximum "disorder" which is allowed among fragments which share a @@ -1164,11 +1159,6 @@ ip6frag_low_thresh - INTEGER ip6frag_time - INTEGER Time in seconds to keep an IPv6 fragment in memory. -ip6frag_secret_interval - INTEGER - Regeneration interval (in seconds) of the hash secret (or lifetime - for the hash secret) for IPv6 fragments. - Default: 600 - conf/default/*: Change the interface-specific default settings. diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h index 90d21ea62c59..d9cc5bb64854 100644 --- a/include/net/inet_frag.h +++ b/include/net/inet_frag.h @@ -58,11 +58,11 @@ struct inet_frags { * Its primarily a rebuild protection rwlock. */ rwlock_t lock ____cacheline_aligned_in_smp; - int secret_interval; - struct timer_list secret_timer; struct work_struct frags_work; unsigned int next_bucket; + unsigned long last_rebuild_jiffies; + bool rebuild; /* The first call to hashfn is responsible to initialize * rnd. This is best done with net_get_random_once. diff --git a/net/ieee802154/reassembly.c b/net/ieee802154/reassembly.c index b4bc7a50eccf..20d219682d84 100644 --- a/net/ieee802154/reassembly.c +++ b/net/ieee802154/reassembly.c @@ -419,10 +419,12 @@ static struct ctl_table lowpan_frags_ns_ctl_table[] = { { } }; +/* secret interval has been deprecated */ +static int lowpan_frags_secret_interval_unused; static struct ctl_table lowpan_frags_ctl_table[] = { { .procname = "6lowpanfrag_secret_interval", - .data = &lowpan_frags.secret_interval, + .data = &lowpan_frags_secret_interval_unused, .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, @@ -562,7 +564,6 @@ int __init lowpan_net_frag_init(void) lowpan_frags.qsize = sizeof(struct frag_queue); lowpan_frags.match = lowpan_frag_match; lowpan_frags.frag_expire = lowpan_frag_expire; - lowpan_frags.secret_interval = 10 * 60 * HZ; inet_frags_init(&lowpan_frags); return ret; diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index 198a5ed7a815..58d4c38534f6 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -28,6 +28,9 @@ #define INETFRAGS_EVICT_BUCKETS 128 #define INETFRAGS_EVICT_MAX 512 +/* don't rebuild inetfrag table with new secret more often than this */ +#define INETFRAGS_MIN_REBUILD_INTERVAL (5 * HZ) + /* Given the OR values of all fragments, apply RFC 3168 5.3 requirements * Value : 0xff if frame should be dropped. * 0 or INET_ECN_CE value, to be ORed in to final iph->tos field @@ -55,16 +58,24 @@ inet_frag_hashfn(const struct inet_frags *f, const struct inet_frag_queue *q) return f->hashfn(q) & (INETFRAGS_HASHSZ - 1); } -static void inet_frag_secret_rebuild(unsigned long dummy) +static bool inet_frag_may_rebuild(struct inet_frags *f) +{ + return time_after(jiffies, + f->last_rebuild_jiffies + INETFRAGS_MIN_REBUILD_INTERVAL); +} + +static void inet_frag_secret_rebuild(struct inet_frags *f) { - struct inet_frags *f = (struct inet_frags *)dummy; - unsigned long now = jiffies; int i; /* Per bucket lock NOT needed here, due to write lock protection */ - write_lock(&f->lock); + write_lock_bh(&f->lock); + + if (!inet_frag_may_rebuild(f)) + goto out; get_random_bytes(&f->rnd, sizeof(u32)); + for (i = 0; i < INETFRAGS_HASHSZ; i++) { struct inet_frag_bucket *hb; struct inet_frag_queue *q; @@ -85,9 +96,11 @@ static void inet_frag_secret_rebuild(unsigned long dummy) } } } - write_unlock(&f->lock); - mod_timer(&f->secret_timer, now + f->secret_interval); + f->rebuild = false; + f->last_rebuild_jiffies = jiffies; +out: + write_unlock_bh(&f->lock); } static bool inet_fragq_should_evict(const struct inet_frag_queue *q) @@ -162,6 +175,8 @@ static void inet_frag_worker(struct work_struct *work) f->next_bucket = i; read_unlock_bh(&f->lock); + if (f->rebuild && inet_frag_may_rebuild(f)) + inet_frag_secret_rebuild(f); } static void inet_frag_schedule_worker(struct inet_frags *f) @@ -183,11 +198,7 @@ void inet_frags_init(struct inet_frags *f) INIT_HLIST_HEAD(&hb->chain); } rwlock_init(&f->lock); - - setup_timer(&f->secret_timer, inet_frag_secret_rebuild, - (unsigned long)f); - f->secret_timer.expires = jiffies + f->secret_interval; - add_timer(&f->secret_timer); + f->last_rebuild_jiffies = 0; } EXPORT_SYMBOL(inet_frags_init); @@ -199,7 +210,6 @@ EXPORT_SYMBOL(inet_frags_init_net); void inet_frags_fini(struct inet_frags *f) { - del_timer(&f->secret_timer); cancel_work_sync(&f->frags_work); } EXPORT_SYMBOL(inet_frags_fini); @@ -399,8 +409,13 @@ struct inet_frag_queue *inet_frag_find(struct netns_frags *nf, if (depth <= INETFRAGS_MAXDEPTH) return inet_frag_create(nf, f, key); - else - return ERR_PTR(-ENOBUFS); + + if (inet_frag_may_rebuild(f)) { + f->rebuild = true; + inet_frag_schedule_worker(f); + } + + return ERR_PTR(-ENOBUFS); } EXPORT_SYMBOL(inet_frag_find); diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 8fbeee495037..44e591a7e03f 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -720,10 +720,12 @@ static struct ctl_table ip4_frags_ns_ctl_table[] = { { } }; +/* secret interval has been deprecated */ +static int ip4_frags_secret_interval_unused; static struct ctl_table ip4_frags_ctl_table[] = { { .procname = "ipfrag_secret_interval", - .data = &ip4_frags.secret_interval, + .data = &ip4_frags_secret_interval_unused, .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, @@ -853,6 +855,5 @@ void __init ipfrag_init(void) ip4_frags.qsize = sizeof(struct ipq); ip4_frags.match = ip4_frag_match; ip4_frags.frag_expire = ip_expire; - ip4_frags.secret_interval = 10 * 60 * HZ; inet_frags_init(&ip4_frags); } diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index fb0f72a0ff31..3b3ef9774cc2 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -669,7 +669,6 @@ int nf_ct_frag6_init(void) nf_frags.qsize = sizeof(struct frag_queue); nf_frags.match = ip6_frag_match; nf_frags.frag_expire = nf_ct_frag6_expire; - nf_frags.secret_interval = 10 * 60 * HZ; inet_frags_init(&nf_frags); ret = register_pernet_subsys(&nf_ct_net_ops); diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index af85551682c2..987fea46b915 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -604,10 +604,12 @@ static struct ctl_table ip6_frags_ns_ctl_table[] = { { } }; +/* secret interval has been deprecated */ +static int ip6_frags_secret_interval_unused; static struct ctl_table ip6_frags_ctl_table[] = { { .procname = "ip6frag_secret_interval", - .data = &ip6_frags.secret_interval, + .data = &ip6_frags_secret_interval_unused, .maxlen = sizeof(int), .mode = 0644, .proc_handler = proc_dointvec_jiffies, @@ -737,7 +739,6 @@ int __init ipv6_frag_init(void) ip6_frags.qsize = sizeof(struct frag_queue); ip6_frags.match = ip6_frag_match; ip6_frags.frag_expire = ip6_frag_expire; - ip6_frags.secret_interval = 10 * 60 * HZ; inet_frags_init(&ip6_frags); out: return ret; -- cgit v1.2.3 From ab1c724f633080ed2e8a0cfe61654599b55cf8f9 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 24 Jul 2014 16:50:36 +0200 Subject: inet: frag: use seqlock for hash rebuild rehash is rare operation, don't force readers to take the read-side rwlock. Instead, we only have to detect the (rare) case where the secret was altered while we are trying to insert a new inetfrag queue into the table. If it was changed, drop the bucket lock and recompute the hash to get the 'new' chain bucket that we have to insert into. Joint work with Nikolay Aleksandrov. Signed-off-by: Florian Westphal Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- include/net/inet_frag.h | 13 +++-- net/ieee802154/reassembly.c | 1 - net/ipv4/inet_fragment.c | 88 ++++++++++++++++++++------------- net/ipv4/ip_fragment.c | 1 - net/ipv6/netfilter/nf_conntrack_reasm.c | 2 +- net/ipv6/reassembly.c | 1 - 6 files changed, 62 insertions(+), 44 deletions(-) (limited to 'include') diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h index d9cc5bb64854..6f4930a0b660 100644 --- a/include/net/inet_frag.h +++ b/include/net/inet_frag.h @@ -53,11 +53,6 @@ struct inet_frag_bucket { struct inet_frags { struct inet_frag_bucket hash[INETFRAGS_HASHSZ]; - /* This rwlock is a global lock (seperate per IPv4, IPv6 and - * netfilter). Important to keep this on a seperate cacheline. - * Its primarily a rebuild protection rwlock. - */ - rwlock_t lock ____cacheline_aligned_in_smp; struct work_struct frags_work; unsigned int next_bucket; @@ -66,8 +61,12 @@ struct inet_frags { /* The first call to hashfn is responsible to initialize * rnd. This is best done with net_get_random_once. + * + * rnd_seqlock is used to let hash insertion detect + * when it needs to re-lookup the hash chain to use. */ u32 rnd; + seqlock_t rnd_seqlock; int qsize; unsigned int (*hashfn)(const struct inet_frag_queue *); @@ -89,8 +88,8 @@ void inet_frags_exit_net(struct netns_frags *nf, struct inet_frags *f); void inet_frag_kill(struct inet_frag_queue *q, struct inet_frags *f); void inet_frag_destroy(struct inet_frag_queue *q, struct inet_frags *f); struct inet_frag_queue *inet_frag_find(struct netns_frags *nf, - struct inet_frags *f, void *key, unsigned int hash) - __releases(&f->lock); + struct inet_frags *f, void *key, unsigned int hash); + void inet_frag_maybe_warn_overflow(struct inet_frag_queue *q, const char *prefix); diff --git a/net/ieee802154/reassembly.c b/net/ieee802154/reassembly.c index 20d219682d84..8da635d92a58 100644 --- a/net/ieee802154/reassembly.c +++ b/net/ieee802154/reassembly.c @@ -124,7 +124,6 @@ fq_find(struct net *net, const struct lowpan_frag_info *frag_info, arg.src = src; arg.dst = dst; - read_lock(&lowpan_frags.lock); hash = lowpan_hash_frag(frag_info->d_tag, frag_info->d_size, src, dst); q = inet_frag_find(&ieee802154_lowpan->frags, diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index 58d4c38534f6..62b1f73749dc 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -68,8 +68,7 @@ static void inet_frag_secret_rebuild(struct inet_frags *f) { int i; - /* Per bucket lock NOT needed here, due to write lock protection */ - write_lock_bh(&f->lock); + write_seqlock_bh(&f->rnd_seqlock); if (!inet_frag_may_rebuild(f)) goto out; @@ -82,6 +81,8 @@ static void inet_frag_secret_rebuild(struct inet_frags *f) struct hlist_node *n; hb = &f->hash[i]; + spin_lock(&hb->chain_lock); + hlist_for_each_entry_safe(q, n, &hb->chain, list) { unsigned int hval = inet_frag_hashfn(f, q); @@ -92,15 +93,28 @@ static void inet_frag_secret_rebuild(struct inet_frags *f) /* Relink to new hash chain. */ hb_dest = &f->hash[hval]; + + /* This is the only place where we take + * another chain_lock while already holding + * one. As this will not run concurrently, + * we cannot deadlock on hb_dest lock below, if its + * already locked it will be released soon since + * other caller cannot be waiting for hb lock + * that we've taken above. + */ + spin_lock_nested(&hb_dest->chain_lock, + SINGLE_DEPTH_NESTING); hlist_add_head(&q->list, &hb_dest->chain); + spin_unlock(&hb_dest->chain_lock); } } + spin_unlock(&hb->chain_lock); } f->rebuild = false; f->last_rebuild_jiffies = jiffies; out: - write_unlock_bh(&f->lock); + write_sequnlock_bh(&f->rnd_seqlock); } static bool inet_fragq_should_evict(const struct inet_frag_queue *q) @@ -163,7 +177,7 @@ static void inet_frag_worker(struct work_struct *work) BUILD_BUG_ON(INETFRAGS_EVICT_BUCKETS >= INETFRAGS_HASHSZ); - read_lock_bh(&f->lock); + local_bh_disable(); for (i = ACCESS_ONCE(f->next_bucket); budget; --budget) { evicted += inet_evict_bucket(f, &f->hash[i]); @@ -174,7 +188,8 @@ static void inet_frag_worker(struct work_struct *work) f->next_bucket = i; - read_unlock_bh(&f->lock); + local_bh_enable(); + if (f->rebuild && inet_frag_may_rebuild(f)) inet_frag_secret_rebuild(f); } @@ -197,7 +212,8 @@ void inet_frags_init(struct inet_frags *f) spin_lock_init(&hb->chain_lock); INIT_HLIST_HEAD(&hb->chain); } - rwlock_init(&f->lock); + + seqlock_init(&f->rnd_seqlock); f->last_rebuild_jiffies = 0; } EXPORT_SYMBOL(inet_frags_init); @@ -216,35 +232,56 @@ EXPORT_SYMBOL(inet_frags_fini); void inet_frags_exit_net(struct netns_frags *nf, struct inet_frags *f) { + unsigned int seq; int i; nf->low_thresh = 0; + local_bh_disable(); - read_lock_bh(&f->lock); +evict_again: + seq = read_seqbegin(&f->rnd_seqlock); for (i = 0; i < INETFRAGS_HASHSZ ; i++) inet_evict_bucket(f, &f->hash[i]); - read_unlock_bh(&f->lock); + if (read_seqretry(&f->rnd_seqlock, seq)) + goto evict_again; + + local_bh_enable(); percpu_counter_destroy(&nf->mem); } EXPORT_SYMBOL(inet_frags_exit_net); -static inline void fq_unlink(struct inet_frag_queue *fq, struct inet_frags *f) +static struct inet_frag_bucket * +get_frag_bucket_locked(struct inet_frag_queue *fq, struct inet_frags *f) +__acquires(hb->chain_lock) { struct inet_frag_bucket *hb; - unsigned int hash; + unsigned int seq, hash; + + restart: + seq = read_seqbegin(&f->rnd_seqlock); - read_lock(&f->lock); hash = inet_frag_hashfn(f, fq); hb = &f->hash[hash]; spin_lock(&hb->chain_lock); + if (read_seqretry(&f->rnd_seqlock, seq)) { + spin_unlock(&hb->chain_lock); + goto restart; + } + + return hb; +} + +static inline void fq_unlink(struct inet_frag_queue *fq, struct inet_frags *f) +{ + struct inet_frag_bucket *hb; + + hb = get_frag_bucket_locked(fq, f); hlist_del(&fq->list); spin_unlock(&hb->chain_lock); - - read_unlock(&f->lock); } void inet_frag_kill(struct inet_frag_queue *fq, struct inet_frags *f) @@ -300,30 +337,18 @@ static struct inet_frag_queue *inet_frag_intern(struct netns_frags *nf, struct inet_frag_queue *qp_in, struct inet_frags *f, void *arg) { - struct inet_frag_bucket *hb; + struct inet_frag_bucket *hb = get_frag_bucket_locked(qp_in, f); struct inet_frag_queue *qp; - unsigned int hash; - - read_lock(&f->lock); /* Protects against hash rebuild */ - /* - * While we stayed w/o the lock other CPU could update - * the rnd seed, so we need to re-calculate the hash - * chain. Fortunatelly the qp_in can be used to get one. - */ - hash = inet_frag_hashfn(f, qp_in); - hb = &f->hash[hash]; - spin_lock(&hb->chain_lock); #ifdef CONFIG_SMP /* With SMP race we have to recheck hash table, because - * such entry could be created on other cpu, while we - * released the hash bucket lock. + * such entry could have been created on other cpu before + * we acquired hash bucket lock. */ hlist_for_each_entry(qp, &hb->chain, list) { if (qp->net == nf && f->match(qp, arg)) { atomic_inc(&qp->refcnt); spin_unlock(&hb->chain_lock); - read_unlock(&f->lock); qp_in->last_in |= INET_FRAG_COMPLETE; inet_frag_put(qp_in, f); return qp; @@ -338,7 +363,6 @@ static struct inet_frag_queue *inet_frag_intern(struct netns_frags *nf, hlist_add_head(&qp->list, &hb->chain); spin_unlock(&hb->chain_lock); - read_unlock(&f->lock); return qp; } @@ -382,7 +406,6 @@ static struct inet_frag_queue *inet_frag_create(struct netns_frags *nf, struct inet_frag_queue *inet_frag_find(struct netns_frags *nf, struct inet_frags *f, void *key, unsigned int hash) - __releases(&f->lock) { struct inet_frag_bucket *hb; struct inet_frag_queue *q; @@ -399,19 +422,18 @@ struct inet_frag_queue *inet_frag_find(struct netns_frags *nf, if (q->net == nf && f->match(q, key)) { atomic_inc(&q->refcnt); spin_unlock(&hb->chain_lock); - read_unlock(&f->lock); return q; } depth++; } spin_unlock(&hb->chain_lock); - read_unlock(&f->lock); if (depth <= INETFRAGS_MAXDEPTH) return inet_frag_create(nf, f, key); if (inet_frag_may_rebuild(f)) { - f->rebuild = true; + if (!f->rebuild) + f->rebuild = true; inet_frag_schedule_worker(f); } diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 44e591a7e03f..ccee68dffd6e 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -244,7 +244,6 @@ static inline struct ipq *ip_find(struct net *net, struct iphdr *iph, u32 user) arg.iph = iph; arg.user = user; - read_lock(&ip4_frags.lock); hash = ipqhashfn(iph->id, iph->saddr, iph->daddr, iph->protocol); q = inet_frag_find(&net->ipv4.frags, &ip4_frags, &arg, hash); diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 3b3ef9774cc2..4d9da1e35f8c 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -193,7 +193,7 @@ static inline struct frag_queue *fq_find(struct net *net, __be32 id, arg.dst = dst; arg.ecn = ecn; - read_lock_bh(&nf_frags.lock); + local_bh_disable(); hash = nf_hash_frag(id, src, dst); q = inet_frag_find(&net->nf_frag.frags, &nf_frags, &arg, hash); diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 987fea46b915..57a9707b2032 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -190,7 +190,6 @@ fq_find(struct net *net, __be32 id, const struct in6_addr *src, arg.dst = dst; arg.ecn = ecn; - read_lock(&ip6_frags.lock); hash = inet6_hash_frag(id, src, dst); q = inet_frag_find(&net->ipv6.frags, &ip6_frags, &arg, hash); -- cgit v1.2.3 From 9cb42e2a8ed06e91ce9d2c59fbae8d1185ebe2f7 Mon Sep 17 00:00:00 2001 From: "Opensource [Steve Twiss]" Date: Mon, 21 Jul 2014 11:39:33 +0100 Subject: mfd: da9063: Add support for AD silicon variant Add register definitions for DA9063 AD (0x3) silicon variant ID the ability to choose the silicon variant at run-time using regmap configuration. This patch also adds RTC support for the AD silicon changes. It adds both BB and AD support as regmap ranges and then makes the distinction between the two tables at run-time. This allows both AD and BB silicon variants to be supported at the same time. Suggested-by: Philipp Zabel Signed-off-by: Opensource [Steve Twiss] Signed-off-by: Lee Jones --- drivers/mfd/da9063-core.c | 6 +- drivers/mfd/da9063-i2c.c | 134 ++++++++++++++++++++++++++++------- drivers/rtc/rtc-da9063.c | 54 +++++++++----- include/linux/mfd/da9063/core.h | 3 +- include/linux/mfd/da9063/registers.h | 129 +++++++++++++++++++++------------ 5 files changed, 236 insertions(+), 90 deletions(-) (limited to 'include') diff --git a/drivers/mfd/da9063-core.c b/drivers/mfd/da9063-core.c index e70ae315abc7..93db8bb8c8f0 100644 --- a/drivers/mfd/da9063-core.c +++ b/drivers/mfd/da9063-core.c @@ -153,9 +153,9 @@ int da9063_device_init(struct da9063 *da9063, unsigned int irq) "Device detected (chip-ID: 0x%02X, var-ID: 0x%02X)\n", model, variant_id); - if (variant_code != PMIC_DA9063_BB) { - dev_err(da9063->dev, "Unknown chip variant code: 0x%02X\n", - variant_code); + if (variant_code < PMIC_DA9063_BB && variant_code != PMIC_DA9063_AD) { + dev_err(da9063->dev, + "Cannot support variant code: 0x%02X\n", variant_code); return -ENODEV; } diff --git a/drivers/mfd/da9063-i2c.c b/drivers/mfd/da9063-i2c.c index 8db5c805c64f..21fd8d9a217b 100644 --- a/drivers/mfd/da9063-i2c.c +++ b/drivers/mfd/da9063-i2c.c @@ -25,10 +25,10 @@ #include #include -static const struct regmap_range da9063_readable_ranges[] = { +static const struct regmap_range da9063_ad_readable_ranges[] = { { .range_min = DA9063_REG_PAGE_CON, - .range_max = DA9063_REG_SECOND_D, + .range_max = DA9063_AD_REG_SECOND_D, }, { .range_min = DA9063_REG_SEQ, .range_max = DA9063_REG_ID_32_31, @@ -37,14 +37,14 @@ static const struct regmap_range da9063_readable_ranges[] = { .range_max = DA9063_REG_AUTO3_LOW, }, { .range_min = DA9063_REG_T_OFFSET, - .range_max = DA9063_REG_GP_ID_19, + .range_max = DA9063_AD_REG_GP_ID_19, }, { .range_min = DA9063_REG_CHIP_ID, .range_max = DA9063_REG_CHIP_VARIANT, }, }; -static const struct regmap_range da9063_writeable_ranges[] = { +static const struct regmap_range da9063_ad_writeable_ranges[] = { { .range_min = DA9063_REG_PAGE_CON, .range_max = DA9063_REG_PAGE_CON, @@ -53,7 +53,7 @@ static const struct regmap_range da9063_writeable_ranges[] = { .range_max = DA9063_REG_VSYS_MON, }, { .range_min = DA9063_REG_COUNT_S, - .range_max = DA9063_REG_ALARM_Y, + .range_max = DA9063_AD_REG_ALARM_Y, }, { .range_min = DA9063_REG_SEQ, .range_max = DA9063_REG_ID_32_31, @@ -62,14 +62,14 @@ static const struct regmap_range da9063_writeable_ranges[] = { .range_max = DA9063_REG_AUTO3_LOW, }, { .range_min = DA9063_REG_CONFIG_I, - .range_max = DA9063_REG_MON_REG_4, + .range_max = DA9063_AD_REG_MON_REG_4, }, { - .range_min = DA9063_REG_GP_ID_0, - .range_max = DA9063_REG_GP_ID_19, + .range_min = DA9063_AD_REG_GP_ID_0, + .range_max = DA9063_AD_REG_GP_ID_19, }, }; -static const struct regmap_range da9063_volatile_ranges[] = { +static const struct regmap_range da9063_ad_volatile_ranges[] = { { .range_min = DA9063_REG_STATUS_A, .range_max = DA9063_REG_EVENT_D, @@ -81,26 +81,104 @@ static const struct regmap_range da9063_volatile_ranges[] = { .range_max = DA9063_REG_ADC_MAN, }, { .range_min = DA9063_REG_ADC_RES_L, - .range_max = DA9063_REG_SECOND_D, + .range_max = DA9063_AD_REG_SECOND_D, }, { - .range_min = DA9063_REG_MON_REG_5, - .range_max = DA9063_REG_MON_REG_6, + .range_min = DA9063_AD_REG_MON_REG_5, + .range_max = DA9063_AD_REG_MON_REG_6, }, }; -static const struct regmap_access_table da9063_readable_table = { - .yes_ranges = da9063_readable_ranges, - .n_yes_ranges = ARRAY_SIZE(da9063_readable_ranges), +static const struct regmap_access_table da9063_ad_readable_table = { + .yes_ranges = da9063_ad_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(da9063_ad_readable_ranges), }; -static const struct regmap_access_table da9063_writeable_table = { - .yes_ranges = da9063_writeable_ranges, - .n_yes_ranges = ARRAY_SIZE(da9063_writeable_ranges), +static const struct regmap_access_table da9063_ad_writeable_table = { + .yes_ranges = da9063_ad_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(da9063_ad_writeable_ranges), }; -static const struct regmap_access_table da9063_volatile_table = { - .yes_ranges = da9063_volatile_ranges, - .n_yes_ranges = ARRAY_SIZE(da9063_volatile_ranges), +static const struct regmap_access_table da9063_ad_volatile_table = { + .yes_ranges = da9063_ad_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(da9063_ad_volatile_ranges), +}; + +static const struct regmap_range da9063_bb_readable_ranges[] = { + { + .range_min = DA9063_REG_PAGE_CON, + .range_max = DA9063_BB_REG_SECOND_D, + }, { + .range_min = DA9063_REG_SEQ, + .range_max = DA9063_REG_ID_32_31, + }, { + .range_min = DA9063_REG_SEQ_A, + .range_max = DA9063_REG_AUTO3_LOW, + }, { + .range_min = DA9063_REG_T_OFFSET, + .range_max = DA9063_BB_REG_GP_ID_19, + }, { + .range_min = DA9063_REG_CHIP_ID, + .range_max = DA9063_REG_CHIP_VARIANT, + }, +}; + +static const struct regmap_range da9063_bb_writeable_ranges[] = { + { + .range_min = DA9063_REG_PAGE_CON, + .range_max = DA9063_REG_PAGE_CON, + }, { + .range_min = DA9063_REG_FAULT_LOG, + .range_max = DA9063_REG_VSYS_MON, + }, { + .range_min = DA9063_REG_COUNT_S, + .range_max = DA9063_BB_REG_ALARM_Y, + }, { + .range_min = DA9063_REG_SEQ, + .range_max = DA9063_REG_ID_32_31, + }, { + .range_min = DA9063_REG_SEQ_A, + .range_max = DA9063_REG_AUTO3_LOW, + }, { + .range_min = DA9063_REG_CONFIG_I, + .range_max = DA9063_BB_REG_MON_REG_4, + }, { + .range_min = DA9063_BB_REG_GP_ID_0, + .range_max = DA9063_BB_REG_GP_ID_19, + }, +}; + +static const struct regmap_range da9063_bb_volatile_ranges[] = { + { + .range_min = DA9063_REG_STATUS_A, + .range_max = DA9063_REG_EVENT_D, + }, { + .range_min = DA9063_REG_CONTROL_F, + .range_max = DA9063_REG_CONTROL_F, + }, { + .range_min = DA9063_REG_ADC_MAN, + .range_max = DA9063_REG_ADC_MAN, + }, { + .range_min = DA9063_REG_ADC_RES_L, + .range_max = DA9063_BB_REG_SECOND_D, + }, { + .range_min = DA9063_BB_REG_MON_REG_5, + .range_max = DA9063_BB_REG_MON_REG_6, + }, +}; + +static const struct regmap_access_table da9063_bb_readable_table = { + .yes_ranges = da9063_bb_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(da9063_bb_readable_ranges), +}; + +static const struct regmap_access_table da9063_bb_writeable_table = { + .yes_ranges = da9063_bb_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(da9063_bb_writeable_ranges), +}; + +static const struct regmap_access_table da9063_bb_volatile_table = { + .yes_ranges = da9063_bb_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(da9063_bb_volatile_ranges), }; static const struct regmap_range_cfg da9063_range_cfg[] = { @@ -123,10 +201,6 @@ static struct regmap_config da9063_regmap_config = { .max_register = DA9063_REG_CHIP_VARIANT, .cache_type = REGCACHE_RBTREE, - - .rd_table = &da9063_readable_table, - .wr_table = &da9063_writeable_table, - .volatile_table = &da9063_volatile_table, }; static int da9063_i2c_probe(struct i2c_client *i2c, @@ -143,6 +217,16 @@ static int da9063_i2c_probe(struct i2c_client *i2c, da9063->dev = &i2c->dev; da9063->chip_irq = i2c->irq; + if (da9063->variant_code == PMIC_DA9063_AD) { + da9063_regmap_config.rd_table = &da9063_ad_readable_table; + da9063_regmap_config.wr_table = &da9063_ad_writeable_table; + da9063_regmap_config.volatile_table = &da9063_ad_volatile_table; + } else { + da9063_regmap_config.rd_table = &da9063_bb_readable_table; + da9063_regmap_config.wr_table = &da9063_bb_writeable_table; + da9063_regmap_config.volatile_table = &da9063_bb_volatile_table; + } + da9063->regmap = devm_regmap_init_i2c(i2c, &da9063_regmap_config); if (IS_ERR(da9063->regmap)) { ret = PTR_ERR(da9063->regmap); diff --git a/drivers/rtc/rtc-da9063.c b/drivers/rtc/rtc-da9063.c index 595393098b09..731ed1a97f59 100644 --- a/drivers/rtc/rtc-da9063.c +++ b/drivers/rtc/rtc-da9063.c @@ -29,6 +29,8 @@ #define YEARS_FROM_DA9063(year) ((year) + 100) #define MONTHS_FROM_DA9063(month) ((month) - 1) +#define RTC_ALARM_DATA_LEN (DA9063_AD_REG_ALARM_Y - DA9063_AD_REG_ALARM_MI + 1) + #define RTC_DATA_LEN (DA9063_REG_COUNT_Y - DA9063_REG_COUNT_S + 1) #define RTC_SEC 0 #define RTC_MIN 1 @@ -42,6 +44,10 @@ struct da9063_rtc { struct da9063 *hw; struct rtc_time alarm_time; bool rtc_sync; + int alarm_year; + int alarm_start; + int alarm_len; + int data_start; }; static void da9063_data_to_tm(u8 *data, struct rtc_time *tm) @@ -83,7 +89,7 @@ static int da9063_rtc_stop_alarm(struct device *dev) { struct da9063_rtc *rtc = dev_get_drvdata(dev); - return regmap_update_bits(rtc->hw->regmap, DA9063_REG_ALARM_Y, + return regmap_update_bits(rtc->hw->regmap, rtc->alarm_year, DA9063_ALARM_ON, 0); } @@ -91,7 +97,7 @@ static int da9063_rtc_start_alarm(struct device *dev) { struct da9063_rtc *rtc = dev_get_drvdata(dev); - return regmap_update_bits(rtc->hw->regmap, DA9063_REG_ALARM_Y, + return regmap_update_bits(rtc->hw->regmap, rtc->alarm_year, DA9063_ALARM_ON, DA9063_ALARM_ON); } @@ -151,8 +157,9 @@ static int da9063_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) int ret; unsigned int val; - ret = regmap_bulk_read(rtc->hw->regmap, DA9063_REG_ALARM_S, - &data[RTC_SEC], RTC_DATA_LEN); + data[RTC_SEC] = 0; + ret = regmap_bulk_read(rtc->hw->regmap, rtc->alarm_start, + &data[rtc->data_start], rtc->alarm_len); if (ret < 0) return ret; @@ -186,14 +193,14 @@ static int da9063_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) return ret; } - ret = regmap_bulk_write(rtc->hw->regmap, DA9063_REG_ALARM_S, - data, RTC_DATA_LEN); + ret = regmap_bulk_write(rtc->hw->regmap, rtc->alarm_start, + &data[rtc->data_start], rtc->alarm_len); if (ret < 0) { dev_err(dev, "Failed to write alarm: %d\n", ret); return ret; } - rtc->alarm_time = alrm->time; + da9063_data_to_tm(data, &rtc->alarm_time); if (alrm->enabled) { ret = da9063_rtc_start_alarm(dev); @@ -218,7 +225,7 @@ static irqreturn_t da9063_alarm_event(int irq, void *data) { struct da9063_rtc *rtc = data; - regmap_update_bits(rtc->hw->regmap, DA9063_REG_ALARM_Y, + regmap_update_bits(rtc->hw->regmap, rtc->alarm_year, DA9063_ALARM_ON, 0); rtc->rtc_sync = true; @@ -257,7 +264,23 @@ static int da9063_rtc_probe(struct platform_device *pdev) goto err; } - ret = regmap_update_bits(da9063->regmap, DA9063_REG_ALARM_S, + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + if (da9063->variant_code == PMIC_DA9063_AD) { + rtc->alarm_year = DA9063_AD_REG_ALARM_Y; + rtc->alarm_start = DA9063_AD_REG_ALARM_MI; + rtc->alarm_len = RTC_ALARM_DATA_LEN; + rtc->data_start = RTC_MIN; + } else { + rtc->alarm_year = DA9063_BB_REG_ALARM_Y; + rtc->alarm_start = DA9063_BB_REG_ALARM_S; + rtc->alarm_len = RTC_DATA_LEN; + rtc->data_start = RTC_SEC; + } + + ret = regmap_update_bits(da9063->regmap, rtc->alarm_start, DA9063_ALARM_STATUS_TICK | DA9063_ALARM_STATUS_ALARM, 0); if (ret < 0) { @@ -265,7 +288,7 @@ static int da9063_rtc_probe(struct platform_device *pdev) goto err; } - ret = regmap_update_bits(da9063->regmap, DA9063_REG_ALARM_S, + ret = regmap_update_bits(da9063->regmap, rtc->alarm_start, DA9063_ALARM_STATUS_ALARM, DA9063_ALARM_STATUS_ALARM); if (ret < 0) { @@ -273,25 +296,22 @@ static int da9063_rtc_probe(struct platform_device *pdev) goto err; } - ret = regmap_update_bits(da9063->regmap, DA9063_REG_ALARM_Y, + ret = regmap_update_bits(da9063->regmap, rtc->alarm_year, DA9063_TICK_ON, 0); if (ret < 0) { dev_err(&pdev->dev, "Failed to disable TICKs\n"); goto err; } - ret = regmap_bulk_read(da9063->regmap, DA9063_REG_ALARM_S, - data, RTC_DATA_LEN); + data[RTC_SEC] = 0; + ret = regmap_bulk_read(da9063->regmap, rtc->alarm_start, + &data[rtc->data_start], rtc->alarm_len); if (ret < 0) { dev_err(&pdev->dev, "Failed to read initial alarm data: %d\n", ret); goto err; } - rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); - if (!rtc) - return -ENOMEM; - platform_set_drvdata(pdev, rtc); irq_alarm = platform_get_irq_byname(pdev, "ALARM"); diff --git a/include/linux/mfd/da9063/core.h b/include/linux/mfd/da9063/core.h index 00a9aac5d1e8..b92a3262f8f6 100644 --- a/include/linux/mfd/da9063/core.h +++ b/include/linux/mfd/da9063/core.h @@ -34,7 +34,8 @@ enum da9063_models { }; enum da9063_variant_codes { - PMIC_DA9063_BB = 0x5 + PMIC_DA9063_AD = 0x3, + PMIC_DA9063_BB = 0x5, }; /* Interrupts */ diff --git a/include/linux/mfd/da9063/registers.h b/include/linux/mfd/da9063/registers.h index 09a85c699da1..2e0ba6d5fbc3 100644 --- a/include/linux/mfd/da9063/registers.h +++ b/include/linux/mfd/da9063/registers.h @@ -104,16 +104,27 @@ #define DA9063_REG_COUNT_D 0x43 #define DA9063_REG_COUNT_MO 0x44 #define DA9063_REG_COUNT_Y 0x45 -#define DA9063_REG_ALARM_S 0x46 -#define DA9063_REG_ALARM_MI 0x47 -#define DA9063_REG_ALARM_H 0x48 -#define DA9063_REG_ALARM_D 0x49 -#define DA9063_REG_ALARM_MO 0x4A -#define DA9063_REG_ALARM_Y 0x4B -#define DA9063_REG_SECOND_A 0x4C -#define DA9063_REG_SECOND_B 0x4D -#define DA9063_REG_SECOND_C 0x4E -#define DA9063_REG_SECOND_D 0x4F + +#define DA9063_AD_REG_ALARM_MI 0x46 +#define DA9063_AD_REG_ALARM_H 0x47 +#define DA9063_AD_REG_ALARM_D 0x48 +#define DA9063_AD_REG_ALARM_MO 0x49 +#define DA9063_AD_REG_ALARM_Y 0x4A +#define DA9063_AD_REG_SECOND_A 0x4B +#define DA9063_AD_REG_SECOND_B 0x4C +#define DA9063_AD_REG_SECOND_C 0x4D +#define DA9063_AD_REG_SECOND_D 0x4E + +#define DA9063_BB_REG_ALARM_S 0x46 +#define DA9063_BB_REG_ALARM_MI 0x47 +#define DA9063_BB_REG_ALARM_H 0x48 +#define DA9063_BB_REG_ALARM_D 0x49 +#define DA9063_BB_REG_ALARM_MO 0x4A +#define DA9063_BB_REG_ALARM_Y 0x4B +#define DA9063_BB_REG_SECOND_A 0x4C +#define DA9063_BB_REG_SECOND_B 0x4D +#define DA9063_BB_REG_SECOND_C 0x4E +#define DA9063_BB_REG_SECOND_D 0x4F /* Sequencer Control Registers */ #define DA9063_REG_SEQ 0x81 @@ -223,37 +234,67 @@ #define DA9063_REG_CONFIG_J 0x10F #define DA9063_REG_CONFIG_K 0x110 #define DA9063_REG_CONFIG_L 0x111 -#define DA9063_REG_CONFIG_M 0x112 -#define DA9063_REG_CONFIG_N 0x113 - -#define DA9063_REG_MON_REG_1 0x114 -#define DA9063_REG_MON_REG_2 0x115 -#define DA9063_REG_MON_REG_3 0x116 -#define DA9063_REG_MON_REG_4 0x117 -#define DA9063_REG_MON_REG_5 0x11E -#define DA9063_REG_MON_REG_6 0x11F -#define DA9063_REG_TRIM_CLDR 0x120 + +#define DA9063_AD_REG_MON_REG_1 0x112 +#define DA9063_AD_REG_MON_REG_2 0x113 +#define DA9063_AD_REG_MON_REG_3 0x114 +#define DA9063_AD_REG_MON_REG_4 0x115 +#define DA9063_AD_REG_MON_REG_5 0x116 +#define DA9063_AD_REG_MON_REG_6 0x117 +#define DA9063_AD_REG_TRIM_CLDR 0x118 + +#define DA9063_AD_REG_GP_ID_0 0x119 +#define DA9063_AD_REG_GP_ID_1 0x11A +#define DA9063_AD_REG_GP_ID_2 0x11B +#define DA9063_AD_REG_GP_ID_3 0x11C +#define DA9063_AD_REG_GP_ID_4 0x11D +#define DA9063_AD_REG_GP_ID_5 0x11E +#define DA9063_AD_REG_GP_ID_6 0x11F +#define DA9063_AD_REG_GP_ID_7 0x120 +#define DA9063_AD_REG_GP_ID_8 0x121 +#define DA9063_AD_REG_GP_ID_9 0x122 +#define DA9063_AD_REG_GP_ID_10 0x123 +#define DA9063_AD_REG_GP_ID_11 0x124 +#define DA9063_AD_REG_GP_ID_12 0x125 +#define DA9063_AD_REG_GP_ID_13 0x126 +#define DA9063_AD_REG_GP_ID_14 0x127 +#define DA9063_AD_REG_GP_ID_15 0x128 +#define DA9063_AD_REG_GP_ID_16 0x129 +#define DA9063_AD_REG_GP_ID_17 0x12A +#define DA9063_AD_REG_GP_ID_18 0x12B +#define DA9063_AD_REG_GP_ID_19 0x12C + +#define DA9063_BB_REG_CONFIG_M 0x112 +#define DA9063_BB_REG_CONFIG_N 0x113 + +#define DA9063_BB_REG_MON_REG_1 0x114 +#define DA9063_BB_REG_MON_REG_2 0x115 +#define DA9063_BB_REG_MON_REG_3 0x116 +#define DA9063_BB_REG_MON_REG_4 0x117 +#define DA9063_BB_REG_MON_REG_5 0x11E +#define DA9063_BB_REG_MON_REG_6 0x11F +#define DA9063_BB_REG_TRIM_CLDR 0x120 /* General Purpose Registers */ -#define DA9063_REG_GP_ID_0 0x121 -#define DA9063_REG_GP_ID_1 0x122 -#define DA9063_REG_GP_ID_2 0x123 -#define DA9063_REG_GP_ID_3 0x124 -#define DA9063_REG_GP_ID_4 0x125 -#define DA9063_REG_GP_ID_5 0x126 -#define DA9063_REG_GP_ID_6 0x127 -#define DA9063_REG_GP_ID_7 0x128 -#define DA9063_REG_GP_ID_8 0x129 -#define DA9063_REG_GP_ID_9 0x12A -#define DA9063_REG_GP_ID_10 0x12B -#define DA9063_REG_GP_ID_11 0x12C -#define DA9063_REG_GP_ID_12 0x12D -#define DA9063_REG_GP_ID_13 0x12E -#define DA9063_REG_GP_ID_14 0x12F -#define DA9063_REG_GP_ID_15 0x130 -#define DA9063_REG_GP_ID_16 0x131 -#define DA9063_REG_GP_ID_17 0x132 -#define DA9063_REG_GP_ID_18 0x133 -#define DA9063_REG_GP_ID_19 0x134 +#define DA9063_BB_REG_GP_ID_0 0x121 +#define DA9063_BB_REG_GP_ID_1 0x122 +#define DA9063_BB_REG_GP_ID_2 0x123 +#define DA9063_BB_REG_GP_ID_3 0x124 +#define DA9063_BB_REG_GP_ID_4 0x125 +#define DA9063_BB_REG_GP_ID_5 0x126 +#define DA9063_BB_REG_GP_ID_6 0x127 +#define DA9063_BB_REG_GP_ID_7 0x128 +#define DA9063_BB_REG_GP_ID_8 0x129 +#define DA9063_BB_REG_GP_ID_9 0x12A +#define DA9063_BB_REG_GP_ID_10 0x12B +#define DA9063_BB_REG_GP_ID_11 0x12C +#define DA9063_BB_REG_GP_ID_12 0x12D +#define DA9063_BB_REG_GP_ID_13 0x12E +#define DA9063_BB_REG_GP_ID_14 0x12F +#define DA9063_BB_REG_GP_ID_15 0x130 +#define DA9063_BB_REG_GP_ID_16 0x131 +#define DA9063_BB_REG_GP_ID_17 0x132 +#define DA9063_BB_REG_GP_ID_18 0x133 +#define DA9063_BB_REG_GP_ID_19 0x134 /* Chip ID and variant */ #define DA9063_REG_CHIP_ID 0x181 @@ -404,10 +445,10 @@ /* DA9063_REG_CONTROL_B (addr=0x0F) */ #define DA9063_CHG_SEL 0x01 #define DA9063_WATCHDOG_PD 0x02 -#define DA9063_RESET_BLINKING 0x04 +#define DA9063_BB_RESET_BLINKING 0x04 #define DA9063_NRES_MODE 0x08 #define DA9063_NONKEY_LOCK 0x10 -#define DA9063_BUCK_SLOWSTART 0x80 +#define DA9063_BB_BUCK_SLOWSTART 0x80 /* DA9063_REG_CONTROL_C (addr=0x10) */ #define DA9063_DEBOUNCING_MASK 0x07 @@ -467,7 +508,7 @@ #define DA9063_GPADC_PAUSE 0x02 #define DA9063_PMIF_DIS 0x04 #define DA9063_HS2WIRE_DIS 0x08 -#define DA9063_CLDR_PAUSE 0x10 +#define DA9063_BB_CLDR_PAUSE 0x10 #define DA9063_BBAT_DIS 0x20 #define DA9063_OUT_32K_PAUSE 0x40 #define DA9063_PMCONT_DIS 0x80 @@ -844,7 +885,7 @@ #define DA9063_MONITOR 0x40 /* DA9063_REG_ALARM_S (addr=0x46) */ -#define DA9063_ALARM_S_MASK 0x3F +#define DA9063_BB_ALARM_S_MASK 0x3F #define DA9063_ALARM_STATUS_ALARM 0x80 #define DA9063_ALARM_STATUS_TICK 0x40 /* DA9063_REG_ALARM_MI (addr=0x47) */ -- cgit v1.2.3 From 3d2108dae4e1768c06718cdce19f8f0089ce310e Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 15 Jul 2014 11:21:46 +0100 Subject: mfd: wm5110: Add in the output done interrupts wm5110 has interrupts to signal that an output has fully enabled. This patch adds in these interrupts although use is not made of them yet. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/wm5110-tables.c | 18 +++++ include/linux/mfd/arizona/core.h | 10 ++- include/linux/mfd/arizona/registers.h | 120 ++++++++++++++++++++++++++++++++++ 3 files changed, 146 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c index b26be6e5e3f4..4729007055d8 100644 --- a/drivers/mfd/wm5110-tables.c +++ b/drivers/mfd/wm5110-tables.c @@ -416,6 +416,24 @@ static const struct regmap_irq wm5110_irqs[ARIZONA_NUM_IRQ] = { [ARIZONA_IRQ_ISRC2_CFG_ERR] = { .reg_offset = 3, .mask = ARIZONA_ISRC2_CFG_ERR_EINT1 }, + [ARIZONA_IRQ_HP3R_DONE] = { + .reg_offset = 3, .mask = ARIZONA_HP3R_DONE_EINT1 + }, + [ARIZONA_IRQ_HP3L_DONE] = { + .reg_offset = 3, .mask = ARIZONA_HP3L_DONE_EINT1 + }, + [ARIZONA_IRQ_HP2R_DONE] = { + .reg_offset = 3, .mask = ARIZONA_HP2R_DONE_EINT1 + }, + [ARIZONA_IRQ_HP2L_DONE] = { + .reg_offset = 3, .mask = ARIZONA_HP2L_DONE_EINT1 + }, + [ARIZONA_IRQ_HP1R_DONE] = { + .reg_offset = 3, .mask = ARIZONA_HP1R_DONE_EINT1 + }, + [ARIZONA_IRQ_HP1L_DONE] = { + .reg_offset = 3, .mask = ARIZONA_HP1L_DONE_EINT1 + }, [ARIZONA_IRQ_BOOT_DONE] = { .reg_offset = 4, .mask = ARIZONA_BOOT_DONE_EINT1 diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h index 70854d892760..6a62fc99f399 100644 --- a/include/linux/mfd/arizona/core.h +++ b/include/linux/mfd/arizona/core.h @@ -78,8 +78,14 @@ enum arizona_type { #define ARIZONA_IRQ_FLL1_CLOCK_OK 49 #define ARIZONA_IRQ_MICD_CLAMP_RISE 50 #define ARIZONA_IRQ_MICD_CLAMP_FALL 51 - -#define ARIZONA_NUM_IRQ 52 +#define ARIZONA_IRQ_HP3R_DONE 52 +#define ARIZONA_IRQ_HP3L_DONE 53 +#define ARIZONA_IRQ_HP2R_DONE 54 +#define ARIZONA_IRQ_HP2L_DONE 55 +#define ARIZONA_IRQ_HP1R_DONE 56 +#define ARIZONA_IRQ_HP1L_DONE 57 + +#define ARIZONA_NUM_IRQ 58 struct snd_soc_dapm_context; diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h index 7204d8138b24..df93563ea8c5 100644 --- a/include/linux/mfd/arizona/registers.h +++ b/include/linux/mfd/arizona/registers.h @@ -4795,6 +4795,30 @@ #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 */ +#define ARIZONA_HP3R_DONE_EINT1 0x0020 /* HP3R_DONE_EINT1 */ +#define ARIZONA_HP3R_DONE_EINT1_MASK 0x0020 /* HP3R_DONE_EINT1 */ +#define ARIZONA_HP3R_DONE_EINT1_SHIFT 5 /* HP3R_DONE_EINT1 */ +#define ARIZONA_HP3R_DONE_EINT1_WIDTH 1 /* HP3R_DONE_EINT1 */ +#define ARIZONA_HP3L_DONE_EINT1 0x0010 /* HP3L_DONE_EINT1 */ +#define ARIZONA_HP3L_DONE_EINT1_MASK 0x0010 /* HP3L_DONE_EINT1 */ +#define ARIZONA_HP3L_DONE_EINT1_SHIFT 4 /* HP3L_DONE_EINT1 */ +#define ARIZONA_HP3L_DONE_EINT1_WIDTH 1 /* HP3L_DONE_EINT1 */ +#define ARIZONA_HP2R_DONE_EINT1 0x0008 /* HP2R_DONE_EINT1 */ +#define ARIZONA_HP2R_DONE_EINT1_MASK 0x0008 /* HP2R_DONE_EINT1 */ +#define ARIZONA_HP2R_DONE_EINT1_SHIFT 3 /* HP2R_DONE_EINT1 */ +#define ARIZONA_HP2R_DONE_EINT1_WIDTH 1 /* HP2R_DONE_EINT1 */ +#define ARIZONA_HP2L_DONE_EINT1 0x0004 /* HP2L_DONE_EINT1 */ +#define ARIZONA_HP2L_DONE_EINT1_MASK 0x0004 /* HP2L_DONE_EINT1 */ +#define ARIZONA_HP2L_DONE_EINT1_SHIFT 2 /* HP2L_DONE_EINT1 */ +#define ARIZONA_HP2L_DONE_EINT1_WIDTH 1 /* HP2L_DONE_EINT1 */ +#define ARIZONA_HP1R_DONE_EINT1 0x0002 /* HP1R_DONE_EINT1 */ +#define ARIZONA_HP1R_DONE_EINT1_MASK 0x0002 /* HP1R_DONE_EINT1 */ +#define ARIZONA_HP1R_DONE_EINT1_SHIFT 1 /* HP1R_DONE_EINT1 */ +#define ARIZONA_HP1R_DONE_EINT1_WIDTH 1 /* HP1R_DONE_EINT1 */ +#define ARIZONA_HP1L_DONE_EINT1 0x0001 /* HP1L_DONE_EINT1 */ +#define ARIZONA_HP1L_DONE_EINT1_MASK 0x0001 /* HP1L_DONE_EINT1 */ +#define ARIZONA_HP1L_DONE_EINT1_SHIFT 0 /* HP1L_DONE_EINT1 */ +#define ARIZONA_HP1L_DONE_EINT1_WIDTH 1 /* HP1L_DONE_EINT1 */ /* * R3332 (0xD04) - Interrupt Status 5 @@ -4963,6 +4987,30 @@ #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 */ +#define ARIZONA_IM_HP3R_DONE_EINT1 0x0020 /* IM_HP3R_DONE_EINT1 */ +#define ARIZONA_IM_HP3R_DONE_EINT1_MASK 0x0020 /* IM_HP3R_DONE_EINT1 */ +#define ARIZONA_IM_HP3R_DONE_EINT1_SHIFT 5 /* IM_HP3R_DONE_EINT1 */ +#define ARIZONA_IM_HP3R_DONE_EINT1_WIDTH 1 /* IM_HP3R_DONE_EINT1 */ +#define ARIZONA_IM_HP3L_DONE_EINT1 0x0010 /* IM_HP3L_DONE_EINT1 */ +#define ARIZONA_IM_HP3L_DONE_EINT1_MASK 0x0010 /* IM_HP3L_DONE_EINT1 */ +#define ARIZONA_IM_HP3L_DONE_EINT1_SHIFT 4 /* IM_HP3L_DONE_EINT1 */ +#define ARIZONA_IM_HP3L_DONE_EINT1_WIDTH 1 /* IM_HP3L_DONE_EINT1 */ +#define ARIZONA_IM_HP2R_DONE_EINT1 0x0008 /* IM_HP2R_DONE_EINT1 */ +#define ARIZONA_IM_HP2R_DONE_EINT1_MASK 0x0008 /* IM_HP2R_DONE_EINT1 */ +#define ARIZONA_IM_HP2R_DONE_EINT1_SHIFT 3 /* IM_HP2R_DONE_EINT1 */ +#define ARIZONA_IM_HP2R_DONE_EINT1_WIDTH 1 /* IM_HP2R_DONE_EINT1 */ +#define ARIZONA_IM_HP2L_DONE_EINT1 0x0004 /* IM_HP2L_DONE_EINT1 */ +#define ARIZONA_IM_HP2L_DONE_EINT1_MASK 0x0004 /* IM_HP2L_DONE_EINT1 */ +#define ARIZONA_IM_HP2L_DONE_EINT1_SHIFT 2 /* IM_HP2L_DONE_EINT1 */ +#define ARIZONA_IM_HP2L_DONE_EINT1_WIDTH 1 /* IM_HP2L_DONE_EINT1 */ +#define ARIZONA_IM_HP1R_DONE_EINT1 0x0002 /* IM_HP1R_DONE_EINT1 */ +#define ARIZONA_IM_HP1R_DONE_EINT1_MASK 0x0002 /* IM_HP1R_DONE_EINT1 */ +#define ARIZONA_IM_HP1R_DONE_EINT1_SHIFT 1 /* IM_HP1R_DONE_EINT1 */ +#define ARIZONA_IM_HP1R_DONE_EINT1_WIDTH 1 /* IM_HP1R_DONE_EINT1 */ +#define ARIZONA_IM_HP1L_DONE_EINT1 0x0001 /* IM_HP1L_DONE_EINT1 */ +#define ARIZONA_IM_HP1L_DONE_EINT1_MASK 0x0001 /* IM_HP1L_DONE_EINT1 */ +#define ARIZONA_IM_HP1L_DONE_EINT1_SHIFT 0 /* IM_HP1L_DONE_EINT1 */ +#define ARIZONA_IM_HP1L_DONE_EINT1_WIDTH 1 /* IM_HP1L_DONE_EINT1 */ /* * R3340 (0xD0C) - Interrupt Status 5 Mask @@ -5139,6 +5187,30 @@ #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 */ +#define ARIZONA_HP3R_DONE_EINT2 0x0020 /* HP3R_DONE_EINT2 */ +#define ARIZONA_HP3R_DONE_EINT2_MASK 0x0020 /* HP3R_DONE_EINT2 */ +#define ARIZONA_HP3R_DONE_EINT2_SHIFT 5 /* HP3R_DONE_EINT2 */ +#define ARIZONA_HP3R_DONE_EINT2_WIDTH 1 /* HP3R_DONE_EINT2 */ +#define ARIZONA_HP3L_DONE_EINT2 0x0010 /* HP3L_DONE_EINT2 */ +#define ARIZONA_HP3L_DONE_EINT2_MASK 0x0010 /* HP3L_DONE_EINT2 */ +#define ARIZONA_HP3L_DONE_EINT2_SHIFT 4 /* HP3L_DONE_EINT2 */ +#define ARIZONA_HP3L_DONE_EINT2_WIDTH 1 /* HP3L_DONE_EINT2 */ +#define ARIZONA_HP2R_DONE_EINT2 0x0008 /* HP2R_DONE_EINT2 */ +#define ARIZONA_HP2R_DONE_EINT2_MASK 0x0008 /* HP2R_DONE_EINT2 */ +#define ARIZONA_HP2R_DONE_EINT2_SHIFT 3 /* HP2R_DONE_EINT2 */ +#define ARIZONA_HP2R_DONE_EINT2_WIDTH 1 /* HP2R_DONE_EINT2 */ +#define ARIZONA_HP2L_DONE_EINT2 0x0004 /* HP2L_DONE_EINT2 */ +#define ARIZONA_HP2L_DONE_EINT2_MASK 0x0004 /* HP2L_DONE_EINT2 */ +#define ARIZONA_HP2L_DONE_EINT2_SHIFT 2 /* HP2L_DONE_EINT2 */ +#define ARIZONA_HP2L_DONE_EINT2_WIDTH 1 /* HP2L_DONE_EINT2 */ +#define ARIZONA_HP1R_DONE_EINT2 0x0002 /* HP1R_DONE_EINT2 */ +#define ARIZONA_HP1R_DONE_EINT2_MASK 0x0002 /* HP1R_DONE_EINT2 */ +#define ARIZONA_HP1R_DONE_EINT2_SHIFT 1 /* HP1R_DONE_EINT2 */ +#define ARIZONA_HP1R_DONE_EINT2_WIDTH 1 /* HP1R_DONE_EINT2 */ +#define ARIZONA_HP1L_DONE_EINT2 0x0001 /* HP1L_DONE_EINT2 */ +#define ARIZONA_HP1L_DONE_EINT2_MASK 0x0001 /* HP1L_DONE_EINT2 */ +#define ARIZONA_HP1L_DONE_EINT2_SHIFT 0 /* HP1L_DONE_EINT2 */ +#define ARIZONA_HP1L_DONE_EINT2_WIDTH 1 /* HP1L_DONE_EINT2 */ /* * R3348 (0xD14) - IRQ2 Status 5 @@ -5307,6 +5379,30 @@ #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 */ +#define ARIZONA_IM_HP3R_DONE_EINT2 0x0020 /* IM_HP3R_DONE_EINT2 */ +#define ARIZONA_IM_HP3R_DONE_EINT2_MASK 0x0020 /* IM_HP3R_DONE_EINT2 */ +#define ARIZONA_IM_HP3R_DONE_EINT2_SHIFT 5 /* IM_HP3R_DONE_EINT2 */ +#define ARIZONA_IM_HP3R_DONE_EINT2_WIDTH 1 /* IM_HP3R_DONE_EINT2 */ +#define ARIZONA_IM_HP3L_DONE_EINT2 0x0010 /* IM_HP3L_DONE_EINT2 */ +#define ARIZONA_IM_HP3L_DONE_EINT2_MASK 0x0010 /* IM_HP3L_DONE_EINT2 */ +#define ARIZONA_IM_HP3L_DONE_EINT2_SHIFT 4 /* IM_HP3L_DONE_EINT2 */ +#define ARIZONA_IM_HP3L_DONE_EINT2_WIDTH 1 /* IM_HP3L_DONE_EINT2 */ +#define ARIZONA_IM_HP2R_DONE_EINT2 0x0008 /* IM_HP2R_DONE_EINT2 */ +#define ARIZONA_IM_HP2R_DONE_EINT2_MASK 0x0008 /* IM_HP2R_DONE_EINT2 */ +#define ARIZONA_IM_HP2R_DONE_EINT2_SHIFT 3 /* IM_HP2R_DONE_EINT2 */ +#define ARIZONA_IM_HP2R_DONE_EINT2_WIDTH 1 /* IM_HP2R_DONE_EINT2 */ +#define ARIZONA_IM_HP2L_DONE_EINT2 0x0004 /* IM_HP2L_DONE_EINT2 */ +#define ARIZONA_IM_HP2L_DONE_EINT2_MASK 0x0004 /* IM_HP2L_DONE_EINT2 */ +#define ARIZONA_IM_HP2L_DONE_EINT2_SHIFT 2 /* IM_HP2L_DONE_EINT2 */ +#define ARIZONA_IM_HP2L_DONE_EINT2_WIDTH 1 /* IM_HP2L_DONE_EINT2 */ +#define ARIZONA_IM_HP1R_DONE_EINT2 0x0002 /* IM_HP1R_DONE_EINT2 */ +#define ARIZONA_IM_HP1R_DONE_EINT2_MASK 0x0002 /* IM_HP1R_DONE_EINT2 */ +#define ARIZONA_IM_HP1R_DONE_EINT2_SHIFT 1 /* IM_HP1R_DONE_EINT2 */ +#define ARIZONA_IM_HP1R_DONE_EINT2_WIDTH 1 /* IM_HP1R_DONE_EINT2 */ +#define ARIZONA_IM_HP1L_DONE_EINT2 0x0001 /* IM_HP1L_DONE_EINT2 */ +#define ARIZONA_IM_HP1L_DONE_EINT2_MASK 0x0001 /* IM_HP1L_DONE_EINT2 */ +#define ARIZONA_IM_HP1L_DONE_EINT2_SHIFT 0 /* IM_HP1L_DONE_EINT2 */ +#define ARIZONA_IM_HP1L_DONE_EINT2_WIDTH 1 /* IM_HP1L_DONE_EINT2 */ /* * R3356 (0xD1C) - IRQ2 Status 5 Mask @@ -5464,6 +5560,30 @@ #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 */ +#define ARIZONA_HP3R_DONE_STS 0x0020 /* HP3R_DONE_STS */ +#define ARIZONA_HP3R_DONE_STS_MASK 0x0020 /* HP3R_DONE_STS */ +#define ARIZONA_HP3R_DONE_STS_SHIFT 5 /* HP3R_DONE_STS */ +#define ARIZONA_HP3R_DONE_STS_WIDTH 1 /* HP3R_DONE_STS */ +#define ARIZONA_HP3L_DONE_STS 0x0010 /* HP3L_DONE_STS */ +#define ARIZONA_HP3L_DONE_STS_MASK 0x0010 /* HP3L_DONE_STS */ +#define ARIZONA_HP3L_DONE_STS_SHIFT 4 /* HP3L_DONE_STS */ +#define ARIZONA_HP3L_DONE_STS_WIDTH 1 /* HP3L_DONE_STS */ +#define ARIZONA_HP2R_DONE_STS 0x0008 /* HP2R_DONE_STS */ +#define ARIZONA_HP2R_DONE_STS_MASK 0x0008 /* HP2R_DONE_STS */ +#define ARIZONA_HP2R_DONE_STS_SHIFT 3 /* HP2R_DONE_STS */ +#define ARIZONA_HP2R_DONE_STS_WIDTH 1 /* HP2R_DONE_STS */ +#define ARIZONA_HP2L_DONE_STS 0x0004 /* HP2L_DONE_STS */ +#define ARIZONA_HP2L_DONE_STS_MASK 0x0004 /* HP2L_DONE_STS */ +#define ARIZONA_HP2L_DONE_STS_SHIFT 2 /* HP2L_DONE_STS */ +#define ARIZONA_HP2L_DONE_STS_WIDTH 1 /* HP2L_DONE_STS */ +#define ARIZONA_HP1R_DONE_STS 0x0002 /* HP1R_DONE_STS */ +#define ARIZONA_HP1R_DONE_STS_MASK 0x0002 /* HP1R_DONE_STS */ +#define ARIZONA_HP1R_DONE_STS_SHIFT 1 /* HP1R_DONE_STS */ +#define ARIZONA_HP1R_DONE_STS_WIDTH 1 /* HP1R_DONE_STS */ +#define ARIZONA_HP1L_DONE_STS 0x0001 /* HP1L_DONE_STS */ +#define ARIZONA_HP1L_DONE_STS_MASK 0x0001 /* HP1L_DONE_STS */ +#define ARIZONA_HP1L_DONE_STS_SHIFT 0 /* HP1L_DONE_STS */ +#define ARIZONA_HP1L_DONE_STS_WIDTH 1 /* HP1L_DONE_STS */ /* * R3363 (0xD23) - Interrupt Raw Status 5 -- cgit v1.2.3 From c0fe2c5b3f730e3d56d37f7b731a5b1191a4e8bf Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 15 Jul 2014 11:21:47 +0100 Subject: mfd: arizona: Rename thermal shutdown interrupt Newer versions of the IP introduce short circuit protection which will also shutdown the speaker. Rename the interrupt and associated register bits associated with thermal events to better fit the function and avoid conflict with future interrupt additions. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/wm5102-tables.c | 8 ++-- drivers/mfd/wm5110-tables.c | 8 ++-- drivers/mfd/wm8997-tables.c | 8 ++-- include/linux/mfd/arizona/core.h | 4 +- include/linux/mfd/arizona/registers.h | 80 +++++++++++++++++------------------ sound/soc/codecs/arizona.c | 10 ++--- 6 files changed, 59 insertions(+), 59 deletions(-) (limited to 'include') diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index c8a993bd17ae..fb4d4bb0f47d 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -138,11 +138,11 @@ static const struct regmap_irq wm5102_irqs[ARIZONA_NUM_IRQ] = { .reg_offset = 1, .mask = ARIZONA_DSP_IRQ1_EINT1 }, - [ARIZONA_IRQ_SPK_SHUTDOWN_WARN] = { - .reg_offset = 2, .mask = ARIZONA_SPK_SHUTDOWN_WARN_EINT1 + [ARIZONA_IRQ_SPK_OVERHEAT_WARN] = { + .reg_offset = 2, .mask = ARIZONA_SPK_OVERHEAT_WARN_EINT1 }, - [ARIZONA_IRQ_SPK_SHUTDOWN] = { - .reg_offset = 2, .mask = ARIZONA_SPK_SHUTDOWN_EINT1 + [ARIZONA_IRQ_SPK_OVERHEAT] = { + .reg_offset = 2, .mask = ARIZONA_SPK_OVERHEAT_EINT1 }, [ARIZONA_IRQ_HPDET] = { .reg_offset = 2, .mask = ARIZONA_HPDET_EINT1 diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c index 4729007055d8..2822768f2df1 100644 --- a/drivers/mfd/wm5110-tables.c +++ b/drivers/mfd/wm5110-tables.c @@ -340,11 +340,11 @@ static const struct regmap_irq wm5110_irqs[ARIZONA_NUM_IRQ] = { .reg_offset = 1, .mask = ARIZONA_DSP_IRQ1_EINT1 }, - [ARIZONA_IRQ_SPK_SHUTDOWN_WARN] = { - .reg_offset = 2, .mask = ARIZONA_SPK_SHUTDOWN_WARN_EINT1 + [ARIZONA_IRQ_SPK_OVERHEAT_WARN] = { + .reg_offset = 2, .mask = ARIZONA_SPK_OVERHEAT_WARN_EINT1 }, - [ARIZONA_IRQ_SPK_SHUTDOWN] = { - .reg_offset = 2, .mask = ARIZONA_SPK_SHUTDOWN_EINT1 + [ARIZONA_IRQ_SPK_OVERHEAT] = { + .reg_offset = 2, .mask = ARIZONA_SPK_OVERHEAT_EINT1 }, [ARIZONA_IRQ_HPDET] = { .reg_offset = 2, .mask = ARIZONA_HPDET_EINT1 diff --git a/drivers/mfd/wm8997-tables.c b/drivers/mfd/wm8997-tables.c index 04bc6d5ff285..510da3b52324 100644 --- a/drivers/mfd/wm8997-tables.c +++ b/drivers/mfd/wm8997-tables.c @@ -65,11 +65,11 @@ static const struct regmap_irq wm8997_irqs[ARIZONA_NUM_IRQ] = { [ARIZONA_IRQ_GP2] = { .reg_offset = 0, .mask = ARIZONA_GP2_EINT1 }, [ARIZONA_IRQ_GP1] = { .reg_offset = 0, .mask = ARIZONA_GP1_EINT1 }, - [ARIZONA_IRQ_SPK_SHUTDOWN_WARN] = { - .reg_offset = 2, .mask = ARIZONA_SPK_SHUTDOWN_WARN_EINT1 + [ARIZONA_IRQ_SPK_OVERHEAT_WARN] = { + .reg_offset = 2, .mask = ARIZONA_SPK_OVERHEAT_WARN_EINT1 }, - [ARIZONA_IRQ_SPK_SHUTDOWN] = { - .reg_offset = 2, .mask = ARIZONA_SPK_SHUTDOWN_EINT1 + [ARIZONA_IRQ_SPK_OVERHEAT] = { + .reg_offset = 2, .mask = ARIZONA_SPK_OVERHEAT_EINT1 }, [ARIZONA_IRQ_HPDET] = { .reg_offset = 2, .mask = ARIZONA_HPDET_EINT1 diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h index 6a62fc99f399..819edf5d1edf 100644 --- a/include/linux/mfd/arizona/core.h +++ b/include/linux/mfd/arizona/core.h @@ -46,8 +46,8 @@ enum arizona_type { #define ARIZONA_IRQ_DSP_IRQ6 17 #define ARIZONA_IRQ_DSP_IRQ7 18 #define ARIZONA_IRQ_DSP_IRQ8 19 -#define ARIZONA_IRQ_SPK_SHUTDOWN_WARN 20 -#define ARIZONA_IRQ_SPK_SHUTDOWN 21 +#define ARIZONA_IRQ_SPK_OVERHEAT_WARN 20 +#define ARIZONA_IRQ_SPK_OVERHEAT 21 #define ARIZONA_IRQ_MICDET 22 #define ARIZONA_IRQ_HPDET 23 #define ARIZONA_IRQ_WSEQ_DONE 24 diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h index df93563ea8c5..f7d6f9e91da1 100644 --- a/include/linux/mfd/arizona/registers.h +++ b/include/linux/mfd/arizona/registers.h @@ -4691,14 +4691,14 @@ /* * 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_SPK_OVERHEAT_WARN_EINT1 0x8000 /* SPK_OVERHEAT_WARN_EINT1 */ +#define ARIZONA_SPK_OVERHEAT_WARN_EINT1_MASK 0x8000 /* SPK_OVERHEAD_WARN_EINT1 */ +#define ARIZONA_SPK_OVERHEAT_WARN_EINT1_SHIFT 15 /* SPK_OVERHEAT_WARN_EINT1 */ +#define ARIZONA_SPK_OVERHEAT_WARN_EINT1_WIDTH 1 /* SPK_OVERHEAT_WARN_EINT1 */ +#define ARIZONA_SPK_OVERHEAT_EINT1 0x4000 /* SPK_OVERHEAT_EINT1 */ +#define ARIZONA_SPK_OVERHEAT_EINT1_MASK 0x4000 /* SPK_OVERHEAT_EINT1 */ +#define ARIZONA_SPK_OVERHEAT_EINT1_SHIFT 14 /* SPK_OVERHEAT_EINT1 */ +#define ARIZONA_SPK_OVERHEAT_EINT1_WIDTH 1 /* SPK_OVERHEAT_EINT1 */ #define ARIZONA_HPDET_EINT1 0x2000 /* HPDET_EINT1 */ #define ARIZONA_HPDET_EINT1_MASK 0x2000 /* HPDET_EINT1 */ #define ARIZONA_HPDET_EINT1_SHIFT 13 /* HPDET_EINT1 */ @@ -4883,14 +4883,14 @@ /* * 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_SPK_OVERHEAT_WARN_EINT1 0x8000 /* IM_SPK_OVERHEAT_WARN_EINT1 */ +#define ARIZONA_IM_SPK_OVERHEAT_WARN_EINT1_MASK 0x8000 /* IM_SPK_OVERHEAT_WARN_EINT1 */ +#define ARIZONA_IM_SPK_OVERHEAT_WARN_EINT1_SHIFT 15 /* IM_SPK_OVERHEAT_WARN_EINT1 */ +#define ARIZONA_IM_SPK_OVERHEAT_WARN_EINT1_WIDTH 1 /* IM_SPK_OVERHEAT_WARN_EINT1 */ +#define ARIZONA_IM_SPK_OVERHEAT_EINT1 0x4000 /* IM_SPK_OVERHEAT_EINT1 */ +#define ARIZONA_IM_SPK_OVERHEAT_EINT1_MASK 0x4000 /* IM_SPK_OVERHEAT_EINT1 */ +#define ARIZONA_IM_SPK_OVERHEAT_EINT1_SHIFT 14 /* IM_SPK_OVERHEAT_EINT1 */ +#define ARIZONA_IM_SPK_OVERHEAT_EINT1_WIDTH 1 /* IM_SPK_OVERHEAT_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 */ @@ -5083,14 +5083,14 @@ /* * 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_SPK_OVERHEAT_WARN_EINT2 0x8000 /* SPK_OVERHEAT_WARN_EINT2 */ +#define ARIZONA_SPK_OVERHEAT_WARN_EINT2_MASK 0x8000 /* SPK_OVERHEAT_WARN_EINT2 */ +#define ARIZONA_SPK_OVERHEAT_WARN_EINT2_SHIFT 15 /* SPK_OVERHEAT_WARN_EINT2 */ +#define ARIZONA_SPK_OVERHEAT_WARN_EINT2_WIDTH 1 /* SPK_OVERHEAT_WARN_EINT2 */ +#define ARIZONA_SPK_OVERHEAT_EINT2 0x4000 /* SPK_OVERHEAT_EINT2 */ +#define ARIZONA_SPK_OVERHEAT_EINT2_MASK 0x4000 /* SPK_OVERHEAT_EINT2 */ +#define ARIZONA_SPK_OVERHEAT_EINT2_SHIFT 14 /* SPK_OVERHEAT_EINT2 */ +#define ARIZONA_SPK_OVERHEAT_EINT2_WIDTH 1 /* SPK_OVERHEAT_EINT2 */ #define ARIZONA_HPDET_EINT2 0x2000 /* HPDET_EINT2 */ #define ARIZONA_HPDET_EINT2_MASK 0x2000 /* HPDET_EINT2 */ #define ARIZONA_HPDET_EINT2_SHIFT 13 /* HPDET_EINT2 */ @@ -5275,14 +5275,14 @@ /* * 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_SPK_OVERHEAT_WARN_EINT2 0x8000 /* IM_SPK_OVERHEAT_WARN_EINT2 */ +#define ARIZONA_IM_SPK_OVERHEAT_WARN_EINT2_MASK 0x8000 /* IM_SPK_OVERHEAT_WARN_EINT2 */ +#define ARIZONA_IM_SPK_OVERHEAT_WARN_EINT2_SHIFT 15 /* IM_SPK_OVERHEAT_WARN_EINT2 */ +#define ARIZONA_IM_SPK_OVERHEAT_WARN_EINT2_WIDTH 1 /* IM_SPK_OVERHEAT_WARN_EINT2 */ +#define ARIZONA_IM_SPK_OVERHEAT_EINT2 0x4000 /* IM_SPK_OVERHEAT_EINT2 */ +#define ARIZONA_IM_SPK_OVERHEAT_EINT2_MASK 0x4000 /* IM_SPK_OVERHEAT_EINT2 */ +#define ARIZONA_IM_SPK_OVERHEAT_EINT2_SHIFT 14 /* IM_SPK_OVERHEAT_EINT2 */ +#define ARIZONA_IM_SPK_OVERHEAT_EINT2_WIDTH 1 /* IM_SPK_OVERHEAT_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 */ @@ -5456,14 +5456,14 @@ /* * 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_SPK_OVERHEAT_WARN_STS 0x8000 /* SPK_OVERHEAT_WARN_STS */ +#define ARIZONA_SPK_OVERHEAT_WARN_STS_MASK 0x8000 /* SPK_OVERHEAT_WARN_STS */ +#define ARIZONA_SPK_OVERHEAT_WARN_STS_SHIFT 15 /* SPK_OVERHEAT_WARN_STS */ +#define ARIZONA_SPK_OVERHEAT_WARN_STS_WIDTH 1 /* SPK_OVERHEAT_WARN_STS */ +#define ARIZONA_SPK_OVERHEAT_STS 0x4000 /* SPK_OVERHEAT_STS */ +#define ARIZONA_SPK_OVERHEAT_STS_MASK 0x4000 /* SPK_OVERHEAT_STS */ +#define ARIZONA_SPK_OVERHEAT_STS_SHIFT 14 /* SPK_OVERHEAT_STS */ +#define ARIZONA_SPK_OVERHEAT_STS_WIDTH 1 /* SPK_OVERHEAT_STS */ #define ARIZONA_HPDET_STS 0x2000 /* HPDET_STS */ #define ARIZONA_HPDET_STS_MASK 0x2000 /* HPDET_STS */ #define ARIZONA_HPDET_STS_SHIFT 13 /* HPDET_STS */ diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 29e198f57d4c..1b14105d8da3 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -107,7 +107,7 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w, break; case SND_SOC_DAPM_POST_PMU: val = snd_soc_read(codec, ARIZONA_INTERRUPT_RAW_STATUS_3); - if (val & ARIZONA_SPK_SHUTDOWN_STS) { + if (val & ARIZONA_SPK_OVERHEAT_STS) { dev_crit(arizona->dev, "Speaker not enabled due to temperature\n"); return -EBUSY; @@ -159,7 +159,7 @@ static irqreturn_t arizona_thermal_warn(int irq, void *data) if (ret != 0) { dev_err(arizona->dev, "Failed to read thermal status: %d\n", ret); - } else if (val & ARIZONA_SPK_SHUTDOWN_WARN_STS) { + } else if (val & ARIZONA_SPK_OVERHEAT_WARN_STS) { dev_crit(arizona->dev, "Thermal warning\n"); } @@ -177,7 +177,7 @@ static irqreturn_t arizona_thermal_shutdown(int irq, void *data) if (ret != 0) { dev_err(arizona->dev, "Failed to read thermal status: %d\n", ret); - } else if (val & ARIZONA_SPK_SHUTDOWN_STS) { + } else if (val & ARIZONA_SPK_OVERHEAT_STS) { dev_crit(arizona->dev, "Thermal shutdown\n"); ret = regmap_update_bits(arizona->regmap, ARIZONA_OUTPUT_ENABLES_1, @@ -223,7 +223,7 @@ int arizona_init_spk(struct snd_soc_codec *codec) break; } - ret = arizona_request_irq(arizona, ARIZONA_IRQ_SPK_SHUTDOWN_WARN, + ret = arizona_request_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT_WARN, "Thermal warning", arizona_thermal_warn, arizona); if (ret != 0) @@ -231,7 +231,7 @@ int arizona_init_spk(struct snd_soc_codec *codec) "Failed to get thermal warning IRQ: %d\n", ret); - ret = arizona_request_irq(arizona, ARIZONA_IRQ_SPK_SHUTDOWN, + ret = arizona_request_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT, "Thermal shutdown", arizona_thermal_shutdown, arizona); if (ret != 0) -- cgit v1.2.3 From 3215501fc90e109c7b854423e02eb05bc638b555 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 15 Jul 2014 11:21:48 +0100 Subject: mfd: wm5110: Add new interrupt register definitions Newer versions of the IP have a lot of new interrupts and move several existing interrupts. This patch adds the register definitions and regmap hookup for these interrupts. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/arizona-irq.c | 10 +- drivers/mfd/arizona.h | 1 + drivers/mfd/wm5110-tables.c | 213 +++++++++++++ include/linux/mfd/arizona/core.h | 21 +- include/linux/mfd/arizona/registers.h | 585 ++++++++++++++++++++++++++++++++++ 5 files changed, 827 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c index 17102f589100..e780bc40165d 100644 --- a/drivers/mfd/arizona-irq.c +++ b/drivers/mfd/arizona-irq.c @@ -203,7 +203,15 @@ int arizona_irq_init(struct arizona *arizona) #ifdef CONFIG_MFD_WM5110 case WM5110: aod = &wm5110_aod; - irq = &wm5110_irq; + + switch (arizona->rev) { + case 0 ... 2: + irq = &wm5110_irq; + break; + default: + irq = &wm5110_revd_irq; + break; + } ctrlif_error = false; break; diff --git a/drivers/mfd/arizona.h b/drivers/mfd/arizona.h index 2951498ab9a1..fbe2843271c5 100644 --- a/drivers/mfd/arizona.h +++ b/drivers/mfd/arizona.h @@ -36,6 +36,7 @@ extern const struct regmap_irq_chip wm5102_irq; extern const struct regmap_irq_chip wm5110_aod; extern const struct regmap_irq_chip wm5110_irq; +extern const struct regmap_irq_chip wm5110_revd_irq; extern const struct regmap_irq_chip wm8997_aod; extern const struct regmap_irq_chip wm8997_irq; diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c index 2822768f2df1..9b98ee559188 100644 --- a/drivers/mfd/wm5110-tables.c +++ b/drivers/mfd/wm5110-tables.c @@ -457,6 +457,209 @@ const struct regmap_irq_chip wm5110_irq = { }; EXPORT_SYMBOL_GPL(wm5110_irq); +static const struct regmap_irq wm5110_revd_irqs[ARIZONA_NUM_IRQ] = { + [ARIZONA_IRQ_GP4] = { .reg_offset = 0, .mask = ARIZONA_GP4_EINT1 }, + [ARIZONA_IRQ_GP3] = { .reg_offset = 0, .mask = ARIZONA_GP3_EINT1 }, + [ARIZONA_IRQ_GP2] = { .reg_offset = 0, .mask = ARIZONA_GP2_EINT1 }, + [ARIZONA_IRQ_GP1] = { .reg_offset = 0, .mask = ARIZONA_GP1_EINT1 }, + + [ARIZONA_IRQ_DSP4_RAM_RDY] = { + .reg_offset = 1, .mask = ARIZONA_DSP4_RAM_RDY_EINT1 + }, + [ARIZONA_IRQ_DSP3_RAM_RDY] = { + .reg_offset = 1, .mask = ARIZONA_DSP3_RAM_RDY_EINT1 + }, + [ARIZONA_IRQ_DSP2_RAM_RDY] = { + .reg_offset = 1, .mask = ARIZONA_DSP2_RAM_RDY_EINT1 + }, + [ARIZONA_IRQ_DSP1_RAM_RDY] = { + .reg_offset = 1, .mask = ARIZONA_DSP1_RAM_RDY_EINT1 + }, + [ARIZONA_IRQ_DSP_IRQ8] = { + .reg_offset = 1, .mask = ARIZONA_DSP_IRQ8_EINT1 + }, + [ARIZONA_IRQ_DSP_IRQ7] = { + .reg_offset = 1, .mask = ARIZONA_DSP_IRQ7_EINT1 + }, + [ARIZONA_IRQ_DSP_IRQ6] = { + .reg_offset = 1, .mask = ARIZONA_DSP_IRQ6_EINT1 + }, + [ARIZONA_IRQ_DSP_IRQ5] = { + .reg_offset = 1, .mask = ARIZONA_DSP_IRQ5_EINT1 + }, + [ARIZONA_IRQ_DSP_IRQ4] = { + .reg_offset = 1, .mask = ARIZONA_DSP_IRQ4_EINT1 + }, + [ARIZONA_IRQ_DSP_IRQ3] = { + .reg_offset = 1, .mask = ARIZONA_DSP_IRQ3_EINT1 + }, + [ARIZONA_IRQ_DSP_IRQ2] = { + .reg_offset = 1, .mask = ARIZONA_DSP_IRQ2_EINT1 + }, + [ARIZONA_IRQ_DSP_IRQ1] = { + .reg_offset = 1, .mask = ARIZONA_DSP_IRQ1_EINT1 + }, + + [ARIZONA_IRQ_SPK_OVERHEAT_WARN] = { + .reg_offset = 2, .mask = ARIZONA_SPK_OVERHEAT_WARN_EINT1 + }, + [ARIZONA_IRQ_SPK_OVERHEAT] = { + .reg_offset = 2, .mask = ARIZONA_SPK_OVERHEAT_EINT1 + }, + [ARIZONA_IRQ_HPDET] = { + .reg_offset = 2, .mask = ARIZONA_HPDET_EINT1 + }, + [ARIZONA_IRQ_MICDET] = { + .reg_offset = 2, .mask = ARIZONA_MICDET_EINT1 + }, + [ARIZONA_IRQ_WSEQ_DONE] = { + .reg_offset = 2, .mask = ARIZONA_WSEQ_DONE_EINT1 + }, + [ARIZONA_IRQ_DRC2_SIG_DET] = { + .reg_offset = 2, .mask = ARIZONA_DRC2_SIG_DET_EINT1 + }, + [ARIZONA_IRQ_DRC1_SIG_DET] = { + .reg_offset = 2, .mask = ARIZONA_DRC1_SIG_DET_EINT1 + }, + [ARIZONA_IRQ_ASRC2_LOCK] = { + .reg_offset = 2, .mask = ARIZONA_ASRC2_LOCK_EINT1 + }, + [ARIZONA_IRQ_ASRC1_LOCK] = { + .reg_offset = 2, .mask = ARIZONA_ASRC1_LOCK_EINT1 + }, + [ARIZONA_IRQ_UNDERCLOCKED] = { + .reg_offset = 2, .mask = ARIZONA_UNDERCLOCKED_EINT1 + }, + [ARIZONA_IRQ_OVERCLOCKED] = { + .reg_offset = 2, .mask = ARIZONA_OVERCLOCKED_EINT1 + }, + [ARIZONA_IRQ_FLL2_LOCK] = { + .reg_offset = 2, .mask = ARIZONA_FLL2_LOCK_EINT1 + }, + [ARIZONA_IRQ_FLL1_LOCK] = { + .reg_offset = 2, .mask = ARIZONA_FLL1_LOCK_EINT1 + }, + [ARIZONA_IRQ_CLKGEN_ERR] = { + .reg_offset = 2, .mask = ARIZONA_CLKGEN_ERR_EINT1 + }, + [ARIZONA_IRQ_CLKGEN_ERR_ASYNC] = { + .reg_offset = 2, .mask = ARIZONA_CLKGEN_ERR_ASYNC_EINT1 + }, + + [ARIZONA_IRQ_CTRLIF_ERR] = { + .reg_offset = 3, .mask = ARIZONA_V2_CTRLIF_ERR_EINT1 + }, + [ARIZONA_IRQ_MIXER_DROPPED_SAMPLES] = { + .reg_offset = 3, .mask = ARIZONA_V2_MIXER_DROPPED_SAMPLE_EINT1 + }, + [ARIZONA_IRQ_ASYNC_CLK_ENA_LOW] = { + .reg_offset = 3, .mask = ARIZONA_V2_ASYNC_CLK_ENA_LOW_EINT1 + }, + [ARIZONA_IRQ_SYSCLK_ENA_LOW] = { + .reg_offset = 3, .mask = ARIZONA_V2_SYSCLK_ENA_LOW_EINT1 + }, + [ARIZONA_IRQ_ISRC1_CFG_ERR] = { + .reg_offset = 3, .mask = ARIZONA_V2_ISRC1_CFG_ERR_EINT1 + }, + [ARIZONA_IRQ_ISRC2_CFG_ERR] = { + .reg_offset = 3, .mask = ARIZONA_V2_ISRC2_CFG_ERR_EINT1 + }, + [ARIZONA_IRQ_ISRC3_CFG_ERR] = { + .reg_offset = 3, .mask = ARIZONA_V2_ISRC3_CFG_ERR_EINT1 + }, + [ARIZONA_IRQ_HP3R_DONE] = { + .reg_offset = 3, .mask = ARIZONA_HP3R_DONE_EINT1 + }, + [ARIZONA_IRQ_HP3L_DONE] = { + .reg_offset = 3, .mask = ARIZONA_HP3L_DONE_EINT1 + }, + [ARIZONA_IRQ_HP2R_DONE] = { + .reg_offset = 3, .mask = ARIZONA_HP2R_DONE_EINT1 + }, + [ARIZONA_IRQ_HP2L_DONE] = { + .reg_offset = 3, .mask = ARIZONA_HP2L_DONE_EINT1 + }, + [ARIZONA_IRQ_HP1R_DONE] = { + .reg_offset = 3, .mask = ARIZONA_HP1R_DONE_EINT1 + }, + [ARIZONA_IRQ_HP1L_DONE] = { + .reg_offset = 3, .mask = ARIZONA_HP1L_DONE_EINT1 + }, + + [ARIZONA_IRQ_BOOT_DONE] = { + .reg_offset = 4, .mask = ARIZONA_BOOT_DONE_EINT1 + }, + [ARIZONA_IRQ_ASRC_CFG_ERR] = { + .reg_offset = 4, .mask = ARIZONA_V2_ASRC_CFG_ERR_EINT1 + }, + [ARIZONA_IRQ_FLL2_CLOCK_OK] = { + .reg_offset = 4, .mask = ARIZONA_FLL2_CLOCK_OK_EINT1 + }, + [ARIZONA_IRQ_FLL1_CLOCK_OK] = { + .reg_offset = 4, .mask = ARIZONA_FLL1_CLOCK_OK_EINT1 + }, + + [ARIZONA_IRQ_DSP_SHARED_WR_COLL] = { + .reg_offset = 5, .mask = ARIZONA_DSP_SHARED_WR_COLL_EINT1 + }, + [ARIZONA_IRQ_SPK_SHUTDOWN] = { + .reg_offset = 5, .mask = ARIZONA_SPK_SHUTDOWN_EINT1 + }, + [ARIZONA_IRQ_SPK1R_SHORT] = { + .reg_offset = 5, .mask = ARIZONA_SPK1R_SHORT_EINT1 + }, + [ARIZONA_IRQ_SPK1L_SHORT] = { + .reg_offset = 5, .mask = ARIZONA_SPK1L_SHORT_EINT1 + }, + [ARIZONA_IRQ_HP3R_SC_NEG] = { + .reg_offset = 5, .mask = ARIZONA_HP3R_SC_NEG_EINT1 + }, + [ARIZONA_IRQ_HP3R_SC_POS] = { + .reg_offset = 5, .mask = ARIZONA_HP3R_SC_POS_EINT1 + }, + [ARIZONA_IRQ_HP3L_SC_NEG] = { + .reg_offset = 5, .mask = ARIZONA_HP3L_SC_NEG_EINT1 + }, + [ARIZONA_IRQ_HP3L_SC_POS] = { + .reg_offset = 5, .mask = ARIZONA_HP3L_SC_POS_EINT1 + }, + [ARIZONA_IRQ_HP2R_SC_NEG] = { + .reg_offset = 5, .mask = ARIZONA_HP2R_SC_NEG_EINT1 + }, + [ARIZONA_IRQ_HP2R_SC_POS] = { + .reg_offset = 5, .mask = ARIZONA_HP2R_SC_POS_EINT1 + }, + [ARIZONA_IRQ_HP2L_SC_NEG] = { + .reg_offset = 5, .mask = ARIZONA_HP2L_SC_NEG_EINT1 + }, + [ARIZONA_IRQ_HP2L_SC_POS] = { + .reg_offset = 5, .mask = ARIZONA_HP2L_SC_POS_EINT1 + }, + [ARIZONA_IRQ_HP1R_SC_NEG] = { + .reg_offset = 5, .mask = ARIZONA_HP1R_SC_NEG_EINT1 + }, + [ARIZONA_IRQ_HP1R_SC_POS] = { + .reg_offset = 5, .mask = ARIZONA_HP1R_SC_POS_EINT1 + }, + [ARIZONA_IRQ_HP1L_SC_NEG] = { + .reg_offset = 5, .mask = ARIZONA_HP1L_SC_NEG_EINT1 + }, + [ARIZONA_IRQ_HP1L_SC_POS] = { + .reg_offset = 5, .mask = ARIZONA_HP1L_SC_POS_EINT1 + }, +}; + +const struct regmap_irq_chip wm5110_revd_irq = { + .name = "wm5110 IRQ", + .status_base = ARIZONA_INTERRUPT_STATUS_1, + .mask_base = ARIZONA_INTERRUPT_STATUS_1_MASK, + .ack_base = ARIZONA_INTERRUPT_STATUS_1, + .num_regs = 6, + .irqs = wm5110_revd_irqs, + .num_irqs = ARRAY_SIZE(wm5110_revd_irqs), +}; +EXPORT_SYMBOL_GPL(wm5110_revd_irq); + static const struct reg_default wm5110_reg_default[] = { { 0x00000008, 0x0019 }, /* R8 - Ctrl IF SPI CFG 1 */ { 0x00000009, 0x0001 }, /* R9 - Ctrl IF I2C1 CFG 1 */ @@ -1286,12 +1489,14 @@ static const struct reg_default wm5110_reg_default[] = { { 0x00000D0A, 0xFFFF }, /* R3338 - Interrupt Status 3 Mask */ { 0x00000D0B, 0xFFFF }, /* R3339 - Interrupt Status 4 Mask */ { 0x00000D0C, 0xFEFF }, /* R3340 - Interrupt Status 5 Mask */ + { 0x00000D0D, 0xFFFF }, /* R3341 - Interrupt Status 6 Mask */ { 0x00000D0F, 0x0000 }, /* R3343 - Interrupt Control */ { 0x00000D18, 0xFFFF }, /* R3352 - IRQ2 Status 1 Mask */ { 0x00000D19, 0xFFFF }, /* R3353 - IRQ2 Status 2 Mask */ { 0x00000D1A, 0xFFFF }, /* R3354 - IRQ2 Status 3 Mask */ { 0x00000D1B, 0xFFFF }, /* R3355 - IRQ2 Status 4 Mask */ { 0x00000D1C, 0xFFFF }, /* R3356 - IRQ2 Status 5 Mask */ + { 0x00000D1D, 0xFFFF }, /* R3357 - IRQ2 Status 6 Mask */ { 0x00000D1F, 0x0000 }, /* R3359 - IRQ2 Control */ { 0x00000D53, 0xFFFF }, /* R3411 - AOD IRQ Mask IRQ1 */ { 0x00000D54, 0xFFFF }, /* R3412 - AOD IRQ Mask IRQ2 */ @@ -2323,22 +2528,26 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg) case ARIZONA_INTERRUPT_STATUS_3: case ARIZONA_INTERRUPT_STATUS_4: case ARIZONA_INTERRUPT_STATUS_5: + case ARIZONA_INTERRUPT_STATUS_6: case ARIZONA_INTERRUPT_STATUS_1_MASK: case ARIZONA_INTERRUPT_STATUS_2_MASK: case ARIZONA_INTERRUPT_STATUS_3_MASK: case ARIZONA_INTERRUPT_STATUS_4_MASK: case ARIZONA_INTERRUPT_STATUS_5_MASK: + case ARIZONA_INTERRUPT_STATUS_6_MASK: case ARIZONA_INTERRUPT_CONTROL: case ARIZONA_IRQ2_STATUS_1: case ARIZONA_IRQ2_STATUS_2: case ARIZONA_IRQ2_STATUS_3: case ARIZONA_IRQ2_STATUS_4: case ARIZONA_IRQ2_STATUS_5: + case ARIZONA_IRQ2_STATUS_6: case ARIZONA_IRQ2_STATUS_1_MASK: case ARIZONA_IRQ2_STATUS_2_MASK: case ARIZONA_IRQ2_STATUS_3_MASK: case ARIZONA_IRQ2_STATUS_4_MASK: case ARIZONA_IRQ2_STATUS_5_MASK: + case ARIZONA_IRQ2_STATUS_6_MASK: case ARIZONA_IRQ2_CONTROL: case ARIZONA_INTERRUPT_RAW_STATUS_2: case ARIZONA_INTERRUPT_RAW_STATUS_3: @@ -2347,6 +2556,7 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg) case ARIZONA_INTERRUPT_RAW_STATUS_6: case ARIZONA_INTERRUPT_RAW_STATUS_7: case ARIZONA_INTERRUPT_RAW_STATUS_8: + case ARIZONA_INTERRUPT_RAW_STATUS_9: case ARIZONA_IRQ_PIN_STATUS: case ARIZONA_AOD_WKUP_AND_TRIG: case ARIZONA_AOD_IRQ1: @@ -2622,11 +2832,13 @@ static bool wm5110_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_INTERRUPT_STATUS_3: case ARIZONA_INTERRUPT_STATUS_4: case ARIZONA_INTERRUPT_STATUS_5: + case ARIZONA_INTERRUPT_STATUS_6: case ARIZONA_IRQ2_STATUS_1: case ARIZONA_IRQ2_STATUS_2: case ARIZONA_IRQ2_STATUS_3: case ARIZONA_IRQ2_STATUS_4: case ARIZONA_IRQ2_STATUS_5: + case ARIZONA_IRQ2_STATUS_6: case ARIZONA_INTERRUPT_RAW_STATUS_2: case ARIZONA_INTERRUPT_RAW_STATUS_3: case ARIZONA_INTERRUPT_RAW_STATUS_4: @@ -2634,6 +2846,7 @@ static bool wm5110_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_INTERRUPT_RAW_STATUS_6: case ARIZONA_INTERRUPT_RAW_STATUS_7: case ARIZONA_INTERRUPT_RAW_STATUS_8: + case ARIZONA_INTERRUPT_RAW_STATUS_9: case ARIZONA_IRQ_PIN_STATUS: case ARIZONA_AOD_WKUP_AND_TRIG: case ARIZONA_AOD_IRQ1: diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h index 819edf5d1edf..8bc7601cca68 100644 --- a/include/linux/mfd/arizona/core.h +++ b/include/linux/mfd/arizona/core.h @@ -84,8 +84,25 @@ enum arizona_type { #define ARIZONA_IRQ_HP2L_DONE 55 #define ARIZONA_IRQ_HP1R_DONE 56 #define ARIZONA_IRQ_HP1L_DONE 57 - -#define ARIZONA_NUM_IRQ 58 +#define ARIZONA_IRQ_ISRC3_CFG_ERR 58 +#define ARIZONA_IRQ_DSP_SHARED_WR_COLL 59 +#define ARIZONA_IRQ_SPK_SHUTDOWN 60 +#define ARIZONA_IRQ_SPK1R_SHORT 61 +#define ARIZONA_IRQ_SPK1L_SHORT 62 +#define ARIZONA_IRQ_HP3R_SC_NEG 63 +#define ARIZONA_IRQ_HP3R_SC_POS 64 +#define ARIZONA_IRQ_HP3L_SC_NEG 65 +#define ARIZONA_IRQ_HP3L_SC_POS 66 +#define ARIZONA_IRQ_HP2R_SC_NEG 67 +#define ARIZONA_IRQ_HP2R_SC_POS 68 +#define ARIZONA_IRQ_HP2L_SC_NEG 69 +#define ARIZONA_IRQ_HP2L_SC_POS 70 +#define ARIZONA_IRQ_HP1R_SC_NEG 71 +#define ARIZONA_IRQ_HP1R_SC_POS 72 +#define ARIZONA_IRQ_HP1L_SC_NEG 73 +#define ARIZONA_IRQ_HP1L_SC_POS 74 + +#define ARIZONA_NUM_IRQ 75 struct snd_soc_dapm_context; diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h index f7d6f9e91da1..dbd23c36de21 100644 --- a/include/linux/mfd/arizona/registers.h +++ b/include/linux/mfd/arizona/registers.h @@ -878,22 +878,26 @@ #define ARIZONA_INTERRUPT_STATUS_3 0xD02 #define ARIZONA_INTERRUPT_STATUS_4 0xD03 #define ARIZONA_INTERRUPT_STATUS_5 0xD04 +#define ARIZONA_INTERRUPT_STATUS_6 0xD05 #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_STATUS_6_MASK 0xD0D #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_6 0xD15 #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_STATUS_6_MASK 0xD1D #define ARIZONA_IRQ2_CONTROL 0xD1F #define ARIZONA_INTERRUPT_RAW_STATUS_2 0xD20 #define ARIZONA_INTERRUPT_RAW_STATUS_3 0xD21 @@ -902,6 +906,7 @@ #define ARIZONA_INTERRUPT_RAW_STATUS_6 0xD24 #define ARIZONA_INTERRUPT_RAW_STATUS_7 0xD25 #define ARIZONA_INTERRUPT_RAW_STATUS_8 0xD26 +#define ARIZONA_INTERRUPT_RAW_STATUS_9 0xD28 #define ARIZONA_IRQ_PIN_STATUS 0xD40 #define ARIZONA_ADSP2_IRQ0 0xD41 #define ARIZONA_AOD_WKUP_AND_TRIG 0xD50 @@ -4820,6 +4825,53 @@ #define ARIZONA_HP1L_DONE_EINT1_SHIFT 0 /* HP1L_DONE_EINT1 */ #define ARIZONA_HP1L_DONE_EINT1_WIDTH 1 /* HP1L_DONE_EINT1 */ +/* + * R3331 (0xD03) - Interrupt Status 4 (Alternate layout) + * + * Alternate layout used on later devices, note only fields that have moved + * are specified + */ +#define ARIZONA_V2_AIF3_ERR_EINT1 0x8000 /* AIF3_ERR_EINT1 */ +#define ARIZONA_V2_AIF3_ERR_EINT1_MASK 0x8000 /* AIF3_ERR_EINT1 */ +#define ARIZONA_V2_AIF3_ERR_EINT1_SHIFT 15 /* AIF3_ERR_EINT1 */ +#define ARIZONA_V2_AIF3_ERR_EINT1_WIDTH 1 /* AIF3_ERR_EINT1 */ +#define ARIZONA_V2_AIF2_ERR_EINT1 0x4000 /* AIF2_ERR_EINT1 */ +#define ARIZONA_V2_AIF2_ERR_EINT1_MASK 0x4000 /* AIF2_ERR_EINT1 */ +#define ARIZONA_V2_AIF2_ERR_EINT1_SHIFT 14 /* AIF2_ERR_EINT1 */ +#define ARIZONA_V2_AIF2_ERR_EINT1_WIDTH 1 /* AIF2_ERR_EINT1 */ +#define ARIZONA_V2_AIF1_ERR_EINT1 0x2000 /* AIF1_ERR_EINT1 */ +#define ARIZONA_V2_AIF1_ERR_EINT1_MASK 0x2000 /* AIF1_ERR_EINT1 */ +#define ARIZONA_V2_AIF1_ERR_EINT1_SHIFT 13 /* AIF1_ERR_EINT1 */ +#define ARIZONA_V2_AIF1_ERR_EINT1_WIDTH 1 /* AIF1_ERR_EINT1 */ +#define ARIZONA_V2_CTRLIF_ERR_EINT1 0x1000 /* CTRLIF_ERR_EINT1 */ +#define ARIZONA_V2_CTRLIF_ERR_EINT1_MASK 0x1000 /* CTRLIF_ERR_EINT1 */ +#define ARIZONA_V2_CTRLIF_ERR_EINT1_SHIFT 12 /* CTRLIF_ERR_EINT1 */ +#define ARIZONA_V2_CTRLIF_ERR_EINT1_WIDTH 1 /* CTRLIF_ERR_EINT1 */ +#define ARIZONA_V2_MIXER_DROPPED_SAMPLE_EINT1 0x0800 /* MIXER_DROPPED_SAMPLE_EINT1 */ +#define ARIZONA_V2_MIXER_DROPPED_SAMPLE_EINT1_MASK 0x0800 /* MIXER_DROPPED_SAMPLE_EINT1 */ +#define ARIZONA_V2_MIXER_DROPPED_SAMPLE_EINT1_SHIFT 11 /* MIXER_DROPPED_SAMPLE_EINT1 */ +#define ARIZONA_V2_MIXER_DROPPED_SAMPLE_EINT1_WIDTH 1 /* MIXER_DROPPED_SAMPLE_EINT1 */ +#define ARIZONA_V2_ASYNC_CLK_ENA_LOW_EINT1 0x0400 /* ASYNC_CLK_ENA_LOW_EINT1 */ +#define ARIZONA_V2_ASYNC_CLK_ENA_LOW_EINT1_MASK 0x0400 /* ASYNC_CLK_ENA_LOW_EINT1 */ +#define ARIZONA_V2_ASYNC_CLK_ENA_LOW_EINT1_SHIFT 10 /* ASYNC_CLK_ENA_LOW_EINT1 */ +#define ARIZONA_V2_ASYNC_CLK_ENA_LOW_EINT1_WIDTH 1 /* ASYNC_CLK_ENA_LOW_EINT1 */ +#define ARIZONA_V2_SYSCLK_ENA_LOW_EINT1 0x0200 /* SYSCLK_ENA_LOW_EINT1 */ +#define ARIZONA_V2_SYSCLK_ENA_LOW_EINT1_MASK 0x0200 /* SYSCLK_ENA_LOW_EINT1 */ +#define ARIZONA_V2_SYSCLK_ENA_LOW_EINT1_SHIFT 9 /* SYSCLK_ENA_LOW_EINT1 */ +#define ARIZONA_V2_SYSCLK_ENA_LOW_EINT1_WIDTH 1 /* SYSCLK_ENA_LOW_EINT1 */ +#define ARIZONA_V2_ISRC1_CFG_ERR_EINT1 0x0100 /* ISRC1_CFG_ERR_EINT1 */ +#define ARIZONA_V2_ISRC1_CFG_ERR_EINT1_MASK 0x0100 /* ISRC1_CFG_ERR_EINT1 */ +#define ARIZONA_V2_ISRC1_CFG_ERR_EINT1_SHIFT 8 /* ISRC1_CFG_ERR_EINT1 */ +#define ARIZONA_V2_ISRC1_CFG_ERR_EINT1_WIDTH 1 /* ISRC1_CFG_ERR_EINT1 */ +#define ARIZONA_V2_ISRC2_CFG_ERR_EINT1 0x0080 /* ISRC2_CFG_ERR_EINT1 */ +#define ARIZONA_V2_ISRC2_CFG_ERR_EINT1_MASK 0x0080 /* ISRC2_CFG_ERR_EINT1 */ +#define ARIZONA_V2_ISRC2_CFG_ERR_EINT1_SHIFT 7 /* ISRC2_CFG_ERR_EINT1 */ +#define ARIZONA_V2_ISRC2_CFG_ERR_EINT1_WIDTH 1 /* ISRC2_CFG_ERR_EINT1 */ +#define ARIZONA_V2_ISRC3_CFG_ERR_EINT1 0x0040 /* ISRC3_CFG_ERR_EINT1 */ +#define ARIZONA_V2_ISRC3_CFG_ERR_EINT1_MASK 0x0040 /* ISRC3_CFG_ERR_EINT1 */ +#define ARIZONA_V2_ISRC3_CFG_ERR_EINT1_SHIFT 6 /* ISRC3_CFG_ERR_EINT1 */ +#define ARIZONA_V2_ISRC3_CFG_ERR_EINT1_WIDTH 1 /* ISRC3_CFG_ERR_EINT1 */ + /* * R3332 (0xD04) - Interrupt Status 5 */ @@ -4844,6 +4896,85 @@ #define ARIZONA_FLL1_CLOCK_OK_EINT1_SHIFT 0 /* FLL1_CLOCK_OK_EINT1 */ #define ARIZONA_FLL1_CLOCK_OK_EINT1_WIDTH 1 /* FLL1_CLOCK_OK_EINT1 */ +/* + * R3332 (0xD05) - Interrupt Status 5 (Alternate layout) + * + * Alternate layout used on later devices, note only fields that have moved + * are specified + */ +#define ARIZONA_V2_ASRC_CFG_ERR_EINT1 0x0008 /* ASRC_CFG_ERR_EINT1 */ +#define ARIZONA_V2_ASRC_CFG_ERR_EINT1_MASK 0x0008 /* ASRC_CFG_ERR_EINT1 */ +#define ARIZONA_V2_ASRC_CFG_ERR_EINT1_SHIFT 3 /* ASRC_CFG_ERR_EINT1 */ +#define ARIZONA_V2_ASRC_CFG_ERR_EINT1_WIDTH 1 /* ASRC_CFG_ERR_EINT1 */ + +/* + * R3333 (0xD05) - Interrupt Status 6 + */ +#define ARIZONA_DSP_SHARED_WR_COLL_EINT1 0x8000 /* DSP_SHARED_WR_COLL_EINT1 */ +#define ARIZONA_DSP_SHARED_WR_COLL_EINT1_MASK 0x8000 /* DSP_SHARED_WR_COLL_EINT1 */ +#define ARIZONA_DSP_SHARED_WR_COLL_EINT1_SHIFT 15 /* DSP_SHARED_WR_COLL_EINT1 */ +#define ARIZONA_DSP_SHARED_WR_COLL_EINT1_WIDTH 1 /* DSP_SHARED_WR_COLL_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_SPK1R_SHORT_EINT1 0x2000 /* SPK1R_SHORT_EINT1 */ +#define ARIZONA_SPK1R_SHORT_EINT1_MASK 0x2000 /* SPK1R_SHORT_EINT1 */ +#define ARIZONA_SPK1R_SHORT_EINT1_SHIFT 13 /* SPK1R_SHORT_EINT1 */ +#define ARIZONA_SPK1R_SHORT_EINT1_WIDTH 1 /* SPK1R_SHORT_EINT1 */ +#define ARIZONA_SPK1L_SHORT_EINT1 0x1000 /* SPK1L_SHORT_EINT1 */ +#define ARIZONA_SPK1L_SHORT_EINT1_MASK 0x1000 /* SPK1L_SHORT_EINT1 */ +#define ARIZONA_SPK1L_SHORT_EINT1_SHIFT 12 /* SPK1L_SHORT_EINT1 */ +#define ARIZONA_SPK1L_SHORT_EINT1_WIDTH 1 /* SPK1L_SHORT_EINT1 */ +#define ARIZONA_HP3R_SC_NEG_EINT1 0x0800 /* HP3R_SC_NEG_EINT1 */ +#define ARIZONA_HP3R_SC_NEG_EINT1_MASK 0x0800 /* HP3R_SC_NEG_EINT1 */ +#define ARIZONA_HP3R_SC_NEG_EINT1_SHIFT 11 /* HP3R_SC_NEG_EINT1 */ +#define ARIZONA_HP3R_SC_NEG_EINT1_WIDTH 1 /* HP3R_SC_NEG_EINT1 */ +#define ARIZONA_HP3R_SC_POS_EINT1 0x0400 /* HP3R_SC_POS_EINT1 */ +#define ARIZONA_HP3R_SC_POS_EINT1_MASK 0x0400 /* HP3R_SC_POS_EINT1 */ +#define ARIZONA_HP3R_SC_POS_EINT1_SHIFT 10 /* HP3R_SC_POS_EINT1 */ +#define ARIZONA_HP3R_SC_POS_EINT1_WIDTH 1 /* HP3R_SC_POS_EINT1 */ +#define ARIZONA_HP3L_SC_NEG_EINT1 0x0200 /* HP3L_SC_NEG_EINT1 */ +#define ARIZONA_HP3L_SC_NEG_EINT1_MASK 0x0200 /* HP3L_SC_NEG_EINT1 */ +#define ARIZONA_HP3L_SC_NEG_EINT1_SHIFT 9 /* HP3L_SC_NEG_EINT1 */ +#define ARIZONA_HP3L_SC_NEG_EINT1_WIDTH 1 /* HP3L_SC_NEG_EINT1 */ +#define ARIZONA_HP3L_SC_POS_EINT1 0x0100 /* HP3L_SC_POS_EINT1 */ +#define ARIZONA_HP3L_SC_POS_EINT1_MASK 0x0100 /* HP3L_SC_POS_EINT1 */ +#define ARIZONA_HP3L_SC_POS_EINT1_SHIFT 8 /* HP3L_SC_POS_EINT1 */ +#define ARIZONA_HP3L_SC_POS_EINT1_WIDTH 1 /* HP3L_SC_POS_EINT1 */ +#define ARIZONA_HP2R_SC_NEG_EINT1 0x0080 /* HP2R_SC_NEG_EINT1 */ +#define ARIZONA_HP2R_SC_NEG_EINT1_MASK 0x0080 /* HP2R_SC_NEG_EINT1 */ +#define ARIZONA_HP2R_SC_NEG_EINT1_SHIFT 7 /* HP2R_SC_NEG_EINT1 */ +#define ARIZONA_HP2R_SC_NEG_EINT1_WIDTH 1 /* HP2R_SC_NEG_EINT1 */ +#define ARIZONA_HP2R_SC_POS_EINT1 0x0040 /* HP2R_SC_POS_EINT1 */ +#define ARIZONA_HP2R_SC_POS_EINT1_MASK 0x0040 /* HP2R_SC_POS_EINT1 */ +#define ARIZONA_HP2R_SC_POS_EINT1_SHIFT 6 /* HP2R_SC_POS_EINT1 */ +#define ARIZONA_HP2R_SC_POS_EINT1_WIDTH 1 /* HP2R_SC_POS_EINT1 */ +#define ARIZONA_HP2L_SC_NEG_EINT1 0x0020 /* HP2L_SC_NEG_EINT1 */ +#define ARIZONA_HP2L_SC_NEG_EINT1_MASK 0x0020 /* HP2L_SC_NEG_EINT1 */ +#define ARIZONA_HP2L_SC_NEG_EINT1_SHIFT 5 /* HP2L_SC_NEG_EINT1 */ +#define ARIZONA_HP2L_SC_NEG_EINT1_WIDTH 1 /* HP2L_SC_NEG_EINT1 */ +#define ARIZONA_HP2L_SC_POS_EINT1 0x0010 /* HP2L_SC_POS_EINT1 */ +#define ARIZONA_HP2L_SC_POS_EINT1_MASK 0x0010 /* HP2L_SC_POS_EINT1 */ +#define ARIZONA_HP2L_SC_POS_EINT1_SHIFT 4 /* HP2L_SC_POS_EINT1 */ +#define ARIZONA_HP2L_SC_POS_EINT1_WIDTH 1 /* HP2L_SC_POS_EINT1 */ +#define ARIZONA_HP1R_SC_NEG_EINT1 0x0008 /* HP1R_SC_NEG_EINT1 */ +#define ARIZONA_HP1R_SC_NEG_EINT1_MASK 0x0008 /* HP1R_SC_NEG_EINT1 */ +#define ARIZONA_HP1R_SC_NEG_EINT1_SHIFT 3 /* HP1R_SC_NEG_EINT1 */ +#define ARIZONA_HP1R_SC_NEG_EINT1_WIDTH 1 /* HP1R_SC_NEG_EINT1 */ +#define ARIZONA_HP1R_SC_POS_EINT1 0x0004 /* HP1R_SC_POS_EINT1 */ +#define ARIZONA_HP1R_SC_POS_EINT1_MASK 0x0004 /* HP1R_SC_POS_EINT1 */ +#define ARIZONA_HP1R_SC_POS_EINT1_SHIFT 2 /* HP1R_SC_POS_EINT1 */ +#define ARIZONA_HP1R_SC_POS_EINT1_WIDTH 1 /* HP1R_SC_POS_EINT1 */ +#define ARIZONA_HP1L_SC_NEG_EINT1 0x0002 /* HP1L_SC_NEG_EINT1 */ +#define ARIZONA_HP1L_SC_NEG_EINT1_MASK 0x0002 /* HP1L_SC_NEG_EINT1 */ +#define ARIZONA_HP1L_SC_NEG_EINT1_SHIFT 1 /* HP1L_SC_NEG_EINT1 */ +#define ARIZONA_HP1L_SC_NEG_EINT1_WIDTH 1 /* HP1L_SC_NEG_EINT1 */ +#define ARIZONA_HP1L_SC_POS_EINT1 0x0001 /* HP1L_SC_POS_EINT1 */ +#define ARIZONA_HP1L_SC_POS_EINT1_MASK 0x0001 /* HP1L_SC_POS_EINT1 */ +#define ARIZONA_HP1L_SC_POS_EINT1_SHIFT 0 /* HP1L_SC_POS_EINT1 */ +#define ARIZONA_HP1L_SC_POS_EINT1_WIDTH 1 /* HP1L_SC_POS_EINT1 */ + /* * R3336 (0xD08) - Interrupt Status 1 Mask */ @@ -5012,6 +5143,53 @@ #define ARIZONA_IM_HP1L_DONE_EINT1_SHIFT 0 /* IM_HP1L_DONE_EINT1 */ #define ARIZONA_IM_HP1L_DONE_EINT1_WIDTH 1 /* IM_HP1L_DONE_EINT1 */ +/* + * R3339 (0xD0B) - Interrupt Status 4 Mask (Alternate layout) + * + * Alternate layout used on later devices, note only fields that have moved + * are specified + */ +#define ARIZONA_V2_IM_AIF3_ERR_EINT1 0x8000 /* IM_AIF3_ERR_EINT1 */ +#define ARIZONA_V2_IM_AIF3_ERR_EINT1_MASK 0x8000 /* IM_AIF3_ERR_EINT1 */ +#define ARIZONA_V2_IM_AIF3_ERR_EINT1_SHIFT 15 /* IM_AIF3_ERR_EINT1 */ +#define ARIZONA_V2_IM_AIF3_ERR_EINT1_WIDTH 1 /* IM_AIF3_ERR_EINT1 */ +#define ARIZONA_V2_IM_AIF2_ERR_EINT1 0x4000 /* IM_AIF2_ERR_EINT1 */ +#define ARIZONA_V2_IM_AIF2_ERR_EINT1_MASK 0x4000 /* IM_AIF2_ERR_EINT1 */ +#define ARIZONA_V2_IM_AIF2_ERR_EINT1_SHIFT 14 /* IM_AIF2_ERR_EINT1 */ +#define ARIZONA_V2_IM_AIF2_ERR_EINT1_WIDTH 1 /* IM_AIF2_ERR_EINT1 */ +#define ARIZONA_V2_IM_AIF1_ERR_EINT1 0x2000 /* IM_AIF1_ERR_EINT1 */ +#define ARIZONA_V2_IM_AIF1_ERR_EINT1_MASK 0x2000 /* IM_AIF1_ERR_EINT1 */ +#define ARIZONA_V2_IM_AIF1_ERR_EINT1_SHIFT 13 /* IM_AIF1_ERR_EINT1 */ +#define ARIZONA_V2_IM_AIF1_ERR_EINT1_WIDTH 1 /* IM_AIF1_ERR_EINT1 */ +#define ARIZONA_V2_IM_CTRLIF_ERR_EINT1 0x1000 /* IM_CTRLIF_ERR_EINT1 */ +#define ARIZONA_V2_IM_CTRLIF_ERR_EINT1_MASK 0x1000 /* IM_CTRLIF_ERR_EINT1 */ +#define ARIZONA_V2_IM_CTRLIF_ERR_EINT1_SHIFT 12 /* IM_CTRLIF_ERR_EINT1 */ +#define ARIZONA_V2_IM_CTRLIF_ERR_EINT1_WIDTH 1 /* IM_CTRLIF_ERR_EINT1 */ +#define ARIZONA_V2_IM_MIXER_DROPPED_SAMPLE_EINT1 0x0800 /* IM_MIXER_DROPPED_SAMPLE_EINT1 */ +#define ARIZONA_V2_IM_MIXER_DROPPED_SAMPLE_EINT1_MASK 0x0800 /* IM_MIXER_DROPPED_SAMPLE_EINT1 */ +#define ARIZONA_V2_IM_MIXER_DROPPED_SAMPLE_EINT1_SHIFT 11 /* IM_MIXER_DROPPED_SAMPLE_EINT1 */ +#define ARIZONA_V2_IM_MIXER_DROPPED_SAMPLE_EINT1_WIDTH 1 /* IM_MIXER_DROPPED_SAMPLE_EINT1 */ +#define ARIZONA_V2_IM_ASYNC_CLK_ENA_LOW_EINT1 0x0400 /* IM_ASYNC_CLK_ENA_LOW_EINT1 */ +#define ARIZONA_V2_IM_ASYNC_CLK_ENA_LOW_EINT1_MASK 0x0400 /* IM_ASYNC_CLK_ENA_LOW_EINT1 */ +#define ARIZONA_V2_IM_ASYNC_CLK_ENA_LOW_EINT1_SHIFT 10 /* IM_ASYNC_CLK_ENA_LOW_EINT1 */ +#define ARIZONA_V2_IM_ASYNC_CLK_ENA_LOW_EINT1_WIDTH 1 /* IM_ASYNC_CLK_ENA_LOW_EINT1 */ +#define ARIZONA_V2_IM_SYSCLK_ENA_LOW_EINT1 0x0200 /* IM_SYSCLK_ENA_LOW_EINT1 */ +#define ARIZONA_V2_IM_SYSCLK_ENA_LOW_EINT1_MASK 0x0200 /* IM_SYSCLK_ENA_LOW_EINT1 */ +#define ARIZONA_V2_IM_SYSCLK_ENA_LOW_EINT1_SHIFT 9 /* IM_SYSCLK_ENA_LOW_EINT1 */ +#define ARIZONA_V2_IM_SYSCLK_ENA_LOW_EINT1_WIDTH 1 /* IM_SYSCLK_ENA_LOW_EINT1 */ +#define ARIZONA_V2_IM_ISRC1_CFG_ERR_EINT1 0x0100 /* IM_ISRC1_CFG_ERR_EINT1 */ +#define ARIZONA_V2_IM_ISRC1_CFG_ERR_EINT1_MASK 0x0100 /* IM_ISRC1_CFG_ERR_EINT1 */ +#define ARIZONA_V2_IM_ISRC1_CFG_ERR_EINT1_SHIFT 8 /* IM_ISRC1_CFG_ERR_EINT1 */ +#define ARIZONA_V2_IM_ISRC1_CFG_ERR_EINT1_WIDTH 1 /* IM_ISRC1_CFG_ERR_EINT1 */ +#define ARIZONA_V2_IM_ISRC2_CFG_ERR_EINT1 0x0080 /* IM_ISRC2_CFG_ERR_EINT1 */ +#define ARIZONA_V2_IM_ISRC2_CFG_ERR_EINT1_MASK 0x0080 /* IM_ISRC2_CFG_ERR_EINT1 */ +#define ARIZONA_V2_IM_ISRC2_CFG_ERR_EINT1_SHIFT 7 /* IM_ISRC2_CFG_ERR_EINT1 */ +#define ARIZONA_V2_IM_ISRC2_CFG_ERR_EINT1_WIDTH 1 /* IM_ISRC2_CFG_ERR_EINT1 */ +#define ARIZONA_V2_IM_ISRC3_CFG_ERR_EINT1 0x0040 /* IM_ISRC3_CFG_ERR_EINT1 */ +#define ARIZONA_V2_IM_ISRC3_CFG_ERR_EINT1_MASK 0x0040 /* IM_ISRC3_CFG_ERR_EINT1 */ +#define ARIZONA_V2_IM_ISRC3_CFG_ERR_EINT1_SHIFT 6 /* IM_ISRC3_CFG_ERR_EINT1 */ +#define ARIZONA_V2_IM_ISRC3_CFG_ERR_EINT1_WIDTH 1 /* IM_ISRC3_CFG_ERR_EINT1 */ + /* * R3340 (0xD0C) - Interrupt Status 5 Mask */ @@ -5036,6 +5214,85 @@ #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 */ +/* + * R3340 (0xD0C) - Interrupt Status 5 Mask (Alternate layout) + * + * Alternate layout used on later devices, note only fields that have moved + * are specified + */ +#define ARIZONA_V2_IM_ASRC_CFG_ERR_EINT1 0x0008 /* IM_ASRC_CFG_ERR_EINT1 */ +#define ARIZONA_V2_IM_ASRC_CFG_ERR_EINT1_MASK 0x0008 /* IM_ASRC_CFG_ERR_EINT1 */ +#define ARIZONA_V2_IM_ASRC_CFG_ERR_EINT1_SHIFT 3 /* IM_ASRC_CFG_ERR_EINT1 */ +#define ARIZONA_V2_IM_ASRC_CFG_ERR_EINT1_WIDTH 1 /* IM_ASRC_CFG_ERR_EINT1 */ + +/* + * R3341 (0xD0D) - Interrupt Status 6 Mask + */ +#define ARIZONA_IM_DSP_SHARED_WR_COLL_EINT1 0x8000 /* IM_DSP_SHARED_WR_COLL_EINT1 */ +#define ARIZONA_IM_DSP_SHARED_WR_COLL_EINT1_MASK 0x8000 /* IM_DSP_SHARED_WR_COLL_EINT1 */ +#define ARIZONA_IM_DSP_SHARED_WR_COLL_EINT1_SHIFT 15 /* IM_DSP_SHARED_WR_COLL_EINT1 */ +#define ARIZONA_IM_DSP_SHARED_WR_COLL_EINT1_WIDTH 1 /* IM_DSP_SHARED_WR_COLL_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_SPK1R_SHORT_EINT1 0x2000 /* IM_SPK1R_SHORT_EINT1 */ +#define ARIZONA_IM_SPK1R_SHORT_EINT1_MASK 0x2000 /* IM_SPK1R_SHORT_EINT1 */ +#define ARIZONA_IM_SPK1R_SHORT_EINT1_SHIFT 13 /* IM_SPK1R_SHORT_EINT1 */ +#define ARIZONA_IM_SPK1R_SHORT_EINT1_WIDTH 1 /* IM_SPK1R_SHORT_EINT1 */ +#define ARIZONA_IM_SPK1L_SHORT_EINT1 0x1000 /* IM_SPK1L_SHORT_EINT1 */ +#define ARIZONA_IM_SPK1L_SHORT_EINT1_MASK 0x1000 /* IM_SPK1L_SHORT_EINT1 */ +#define ARIZONA_IM_SPK1L_SHORT_EINT1_SHIFT 12 /* IM_SPK1L_SHORT_EINT1 */ +#define ARIZONA_IM_SPK1L_SHORT_EINT1_WIDTH 1 /* IM_SPK1L_SHORT_EINT1 */ +#define ARIZONA_IM_HP3R_SC_NEG_EINT1 0x0800 /* IM_HP3R_SC_NEG_EINT1 */ +#define ARIZONA_IM_HP3R_SC_NEG_EINT1_MASK 0x0800 /* IM_HP3R_SC_NEG_EINT1 */ +#define ARIZONA_IM_HP3R_SC_NEG_EINT1_SHIFT 11 /* IM_HP3R_SC_NEG_EINT1 */ +#define ARIZONA_IM_HP3R_SC_NEG_EINT1_WIDTH 1 /* IM_HP3R_SC_NEG_EINT1 */ +#define ARIZONA_IM_HP3R_SC_POS_EINT1 0x0400 /* IM_HP3R_SC_POS_EINT1 */ +#define ARIZONA_IM_HP3R_SC_POS_EINT1_MASK 0x0400 /* IM_HP3R_SC_POS_EINT1 */ +#define ARIZONA_IM_HP3R_SC_POS_EINT1_SHIFT 10 /* IM_HP3R_SC_POS_EINT1 */ +#define ARIZONA_IM_HP3R_SC_POS_EINT1_WIDTH 1 /* IM_HP3R_SC_POS_EINT1 */ +#define ARIZONA_IM_HP3L_SC_NEG_EINT1 0x0200 /* IM_HP3L_SC_NEG_EINT1 */ +#define ARIZONA_IM_HP3L_SC_NEG_EINT1_MASK 0x0200 /* IM_HP3L_SC_NEG_EINT1 */ +#define ARIZONA_IM_HP3L_SC_NEG_EINT1_SHIFT 9 /* IM_HP3L_SC_NEG_EINT1 */ +#define ARIZONA_IM_HP3L_SC_NEG_EINT1_WIDTH 1 /* IM_HP3L_SC_NEG_EINT1 */ +#define ARIZONA_IM_HP3L_SC_POS_EINT1 0x0100 /* IM_HP3L_SC_POS_EINT1 */ +#define ARIZONA_IM_HP3L_SC_POS_EINT1_MASK 0x0100 /* IM_HP3L_SC_POS_EINT1 */ +#define ARIZONA_IM_HP3L_SC_POS_EINT1_SHIFT 8 /* IM_HP3L_SC_POS_EINT1 */ +#define ARIZONA_IM_HP3L_SC_POS_EINT1_WIDTH 1 /* IM_HP3L_SC_POS_EINT1 */ +#define ARIZONA_IM_HP2R_SC_NEG_EINT1 0x0080 /* IM_HP2R_SC_NEG_EINT1 */ +#define ARIZONA_IM_HP2R_SC_NEG_EINT1_MASK 0x0080 /* IM_HP2R_SC_NEG_EINT1 */ +#define ARIZONA_IM_HP2R_SC_NEG_EINT1_SHIFT 7 /* IM_HP2R_SC_NEG_EINT1 */ +#define ARIZONA_IM_HP2R_SC_NEG_EINT1_WIDTH 1 /* IM_HP2R_SC_NEG_EINT1 */ +#define ARIZONA_IM_HP2R_SC_POS_EINT1 0x0040 /* IM_HP2R_SC_POS_EINT1 */ +#define ARIZONA_IM_HP2R_SC_POS_EINT1_MASK 0x0040 /* IM_HP2R_SC_POS_EINT1 */ +#define ARIZONA_IM_HP2R_SC_POS_EINT1_SHIFT 6 /* IM_HP2R_SC_POS_EINT1 */ +#define ARIZONA_IM_HP2R_SC_POS_EINT1_WIDTH 1 /* IM_HP2R_SC_POS_EINT1 */ +#define ARIZONA_IM_HP2L_SC_NEG_EINT1 0x0020 /* IM_HP2L_SC_NEG_EINT1 */ +#define ARIZONA_IM_HP2L_SC_NEG_EINT1_MASK 0x0020 /* IM_HP2L_SC_NEG_EINT1 */ +#define ARIZONA_IM_HP2L_SC_NEG_EINT1_SHIFT 5 /* IM_HP2L_SC_NEG_EINT1 */ +#define ARIZONA_IM_HP2L_SC_NEG_EINT1_WIDTH 1 /* IM_HP2L_SC_NEG_EINT1 */ +#define ARIZONA_IM_HP2L_SC_POS_EINT1 0x0010 /* IM_HP2L_SC_POS_EINT1 */ +#define ARIZONA_IM_HP2L_SC_POS_EINT1_MASK 0x0010 /* IM_HP2L_SC_POS_EINT1 */ +#define ARIZONA_IM_HP2L_SC_POS_EINT1_SHIFT 4 /* IM_HP2L_SC_POS_EINT1 */ +#define ARIZONA_IM_HP2L_SC_POS_EINT1_WIDTH 1 /* IM_HP2L_SC_POS_EINT1 */ +#define ARIZONA_IM_HP1R_SC_NEG_EINT1 0x0008 /* IM_HP1R_SC_NEG_EINT1 */ +#define ARIZONA_IM_HP1R_SC_NEG_EINT1_MASK 0x0008 /* IM_HP1R_SC_NEG_EINT1 */ +#define ARIZONA_IM_HP1R_SC_NEG_EINT1_SHIFT 3 /* IM_HP1R_SC_NEG_EINT1 */ +#define ARIZONA_IM_HP1R_SC_NEG_EINT1_WIDTH 1 /* IM_HP1R_SC_NEG_EINT1 */ +#define ARIZONA_IM_HP1R_SC_POS_EINT1 0x0004 /* IM_HP1R_SC_POS_EINT1 */ +#define ARIZONA_IM_HP1R_SC_POS_EINT1_MASK 0x0004 /* IM_HP1R_SC_POS_EINT1 */ +#define ARIZONA_IM_HP1R_SC_POS_EINT1_SHIFT 2 /* IM_HP1R_SC_POS_EINT1 */ +#define ARIZONA_IM_HP1R_SC_POS_EINT1_WIDTH 1 /* IM_HP1R_SC_POS_EINT1 */ +#define ARIZONA_IM_HP1L_SC_NEG_EINT1 0x0002 /* IM_HP1L_SC_NEG_EINT1 */ +#define ARIZONA_IM_HP1L_SC_NEG_EINT1_MASK 0x0002 /* IM_HP1L_SC_NEG_EINT1 */ +#define ARIZONA_IM_HP1L_SC_NEG_EINT1_SHIFT 1 /* IM_HP1L_SC_NEG_EINT1 */ +#define ARIZONA_IM_HP1L_SC_NEG_EINT1_WIDTH 1 /* IM_HP1L_SC_NEG_EINT1 */ +#define ARIZONA_IM_HP1L_SC_POS_EINT1 0x0001 /* IM_HP1L_SC_POS_EINT1 */ +#define ARIZONA_IM_HP1L_SC_POS_EINT1_MASK 0x0001 /* IM_HP1L_SC_POS_EINT1 */ +#define ARIZONA_IM_HP1L_SC_POS_EINT1_SHIFT 0 /* IM_HP1L_SC_POS_EINT1 */ +#define ARIZONA_IM_HP1L_SC_POS_EINT1_WIDTH 1 /* IM_HP1L_SC_POS_EINT1 */ + /* * R3343 (0xD0F) - Interrupt Control */ @@ -5212,6 +5469,53 @@ #define ARIZONA_HP1L_DONE_EINT2_SHIFT 0 /* HP1L_DONE_EINT2 */ #define ARIZONA_HP1L_DONE_EINT2_WIDTH 1 /* HP1L_DONE_EINT2 */ +/* + * R3347 (0xD13) - IRQ2 Status 4 (Alternate layout) + * + * Alternate layout used on later devices, note only fields that have moved + * are specified + */ +#define ARIZONA_V2_AIF3_ERR_EINT2 0x8000 /* AIF3_ERR_EINT2 */ +#define ARIZONA_V2_AIF3_ERR_EINT2_MASK 0x8000 /* AIF3_ERR_EINT2 */ +#define ARIZONA_V2_AIF3_ERR_EINT2_SHIFT 15 /* AIF3_ERR_EINT2 */ +#define ARIZONA_V2_AIF3_ERR_EINT2_WIDTH 1 /* AIF3_ERR_EINT2 */ +#define ARIZONA_V2_AIF2_ERR_EINT2 0x4000 /* AIF2_ERR_EINT2 */ +#define ARIZONA_V2_AIF2_ERR_EINT2_MASK 0x4000 /* AIF2_ERR_EINT2 */ +#define ARIZONA_V2_AIF2_ERR_EINT2_SHIFT 14 /* AIF2_ERR_EINT2 */ +#define ARIZONA_V2_AIF2_ERR_EINT2_WIDTH 1 /* AIF2_ERR_EINT2 */ +#define ARIZONA_V2_AIF1_ERR_EINT2 0x2000 /* AIF1_ERR_EINT2 */ +#define ARIZONA_V2_AIF1_ERR_EINT2_MASK 0x2000 /* AIF1_ERR_EINT2 */ +#define ARIZONA_V2_AIF1_ERR_EINT2_SHIFT 13 /* AIF1_ERR_EINT2 */ +#define ARIZONA_V2_AIF1_ERR_EINT2_WIDTH 1 /* AIF1_ERR_EINT2 */ +#define ARIZONA_V2_CTRLIF_ERR_EINT2 0x1000 /* CTRLIF_ERR_EINT2 */ +#define ARIZONA_V2_CTRLIF_ERR_EINT2_MASK 0x1000 /* CTRLIF_ERR_EINT2 */ +#define ARIZONA_V2_CTRLIF_ERR_EINT2_SHIFT 12 /* CTRLIF_ERR_EINT2 */ +#define ARIZONA_V2_CTRLIF_ERR_EINT2_WIDTH 1 /* CTRLIF_ERR_EINT2 */ +#define ARIZONA_V2_MIXER_DROPPED_SAMPLE_EINT2 0x0800 /* MIXER_DROPPED_SAMPLE_EINT2 */ +#define ARIZONA_V2_MIXER_DROPPED_SAMPLE_EINT2_MASK 0x0800 /* MIXER_DROPPED_SAMPLE_EINT2 */ +#define ARIZONA_V2_MIXER_DROPPED_SAMPLE_EINT2_SHIFT 11 /* MIXER_DROPPED_SAMPLE_EINT2 */ +#define ARIZONA_V2_MIXER_DROPPED_SAMPLE_EINT2_WIDTH 1 /* MIXER_DROPPED_SAMPLE_EINT2 */ +#define ARIZONA_V2_ASYNC_CLK_ENA_LOW_EINT2 0x0400 /* ASYNC_CLK_ENA_LOW_EINT2 */ +#define ARIZONA_V2_ASYNC_CLK_ENA_LOW_EINT2_MASK 0x0400 /* ASYNC_CLK_ENA_LOW_EINT2 */ +#define ARIZONA_V2_ASYNC_CLK_ENA_LOW_EINT2_SHIFT 10 /* ASYNC_CLK_ENA_LOW_EINT2 */ +#define ARIZONA_V2_ASYNC_CLK_ENA_LOW_EINT2_WIDTH 1 /* ASYNC_CLK_ENA_LOW_EINT2 */ +#define ARIZONA_V2_SYSCLK_ENA_LOW_EINT2 0x0200 /* SYSCLK_ENA_LOW_EINT2 */ +#define ARIZONA_V2_SYSCLK_ENA_LOW_EINT2_MASK 0x0200 /* SYSCLK_ENA_LOW_EINT2 */ +#define ARIZONA_V2_SYSCLK_ENA_LOW_EINT2_SHIFT 9 /* SYSCLK_ENA_LOW_EINT2 */ +#define ARIZONA_V2_SYSCLK_ENA_LOW_EINT2_WIDTH 1 /* SYSCLK_ENA_LOW_EINT2 */ +#define ARIZONA_V2_ISRC1_CFG_ERR_EINT2 0x0100 /* ISRC1_CFG_ERR_EINT2 */ +#define ARIZONA_V2_ISRC1_CFG_ERR_EINT2_MASK 0x0100 /* ISRC1_CFG_ERR_EINT2 */ +#define ARIZONA_V2_ISRC1_CFG_ERR_EINT2_SHIFT 8 /* ISRC1_CFG_ERR_EINT2 */ +#define ARIZONA_V2_ISRC1_CFG_ERR_EINT2_WIDTH 1 /* ISRC1_CFG_ERR_EINT2 */ +#define ARIZONA_V2_ISRC2_CFG_ERR_EINT2 0x0080 /* ISRC2_CFG_ERR_EINT2 */ +#define ARIZONA_V2_ISRC2_CFG_ERR_EINT2_MASK 0x0080 /* ISRC2_CFG_ERR_EINT2 */ +#define ARIZONA_V2_ISRC2_CFG_ERR_EINT2_SHIFT 7 /* ISRC2_CFG_ERR_EINT2 */ +#define ARIZONA_V2_ISRC2_CFG_ERR_EINT2_WIDTH 1 /* ISRC2_CFG_ERR_EINT2 */ +#define ARIZONA_V2_ISRC3_CFG_ERR_EINT2 0x0040 /* ISRC3_CFG_ERR_EINT2 */ +#define ARIZONA_V2_ISRC3_CFG_ERR_EINT2_MASK 0x0040 /* ISRC3_CFG_ERR_EINT2 */ +#define ARIZONA_V2_ISRC3_CFG_ERR_EINT2_SHIFT 6 /* ISRC3_CFG_ERR_EINT2 */ +#define ARIZONA_V2_ISRC3_CFG_ERR_EINT2_WIDTH 1 /* ISRC3_CFG_ERR_EINT2 */ + /* * R3348 (0xD14) - IRQ2 Status 5 */ @@ -5236,6 +5540,85 @@ #define ARIZONA_FLL1_CLOCK_OK_EINT2_SHIFT 0 /* FLL1_CLOCK_OK_EINT2 */ #define ARIZONA_FLL1_CLOCK_OK_EINT2_WIDTH 1 /* FLL1_CLOCK_OK_EINT2 */ +/* + * R3348 (0xD14) - IRQ2 Status 5 (Alternate layout) + * + * Alternate layout used on later devices, note only fields that have moved + * are specified + */ +#define ARIZONA_V2_ASRC_CFG_ERR_EINT2 0x0008 /* ASRC_CFG_ERR_EINT2 */ +#define ARIZONA_V2_ASRC_CFG_ERR_EINT2_MASK 0x0008 /* ASRC_CFG_ERR_EINT2 */ +#define ARIZONA_V2_ASRC_CFG_ERR_EINT2_SHIFT 3 /* ASRC_CFG_ERR_EINT2 */ +#define ARIZONA_V2_ASRC_CFG_ERR_EINT2_WIDTH 1 /* ASRC_CFG_ERR_EINT2 */ + +/* + * R3349 (0xD15) - IRQ2 Status 6 + */ +#define ARIZONA_DSP_SHARED_WR_COLL_EINT2 0x8000 /* DSP_SHARED_WR_COLL_EINT2 */ +#define ARIZONA_DSP_SHARED_WR_COLL_EINT2_MASK 0x8000 /* DSP_SHARED_WR_COLL_EINT2 */ +#define ARIZONA_DSP_SHARED_WR_COLL_EINT2_SHIFT 15 /* DSP_SHARED_WR_COLL_EINT2 */ +#define ARIZONA_DSP_SHARED_WR_COLL_EINT2_WIDTH 1 /* DSP_SHARED_WR_COLL_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_SPK1R_SHORT_EINT2 0x2000 /* SPK1R_SHORT_EINT2 */ +#define ARIZONA_SPK1R_SHORT_EINT2_MASK 0x2000 /* SPK1R_SHORT_EINT2 */ +#define ARIZONA_SPK1R_SHORT_EINT2_SHIFT 13 /* SPK1R_SHORT_EINT2 */ +#define ARIZONA_SPK1R_SHORT_EINT2_WIDTH 1 /* SPK1R_SHORT_EINT2 */ +#define ARIZONA_SPK1L_SHORT_EINT2 0x1000 /* SPK1L_SHORT_EINT2 */ +#define ARIZONA_SPK1L_SHORT_EINT2_MASK 0x1000 /* SPK1L_SHORT_EINT2 */ +#define ARIZONA_SPK1L_SHORT_EINT2_SHIFT 12 /* SPK1L_SHORT_EINT2 */ +#define ARIZONA_SPK1L_SHORT_EINT2_WIDTH 1 /* SPK1L_SHORT_EINT2 */ +#define ARIZONA_HP3R_SC_NEG_EINT2 0x0800 /* HP3R_SC_NEG_EINT2 */ +#define ARIZONA_HP3R_SC_NEG_EINT2_MASK 0x0800 /* HP3R_SC_NEG_EINT2 */ +#define ARIZONA_HP3R_SC_NEG_EINT2_SHIFT 11 /* HP3R_SC_NEG_EINT2 */ +#define ARIZONA_HP3R_SC_NEG_EINT2_WIDTH 1 /* HP3R_SC_NEG_EINT2 */ +#define ARIZONA_HP3R_SC_POS_EINT2 0x0400 /* HP3R_SC_POS_EINT2 */ +#define ARIZONA_HP3R_SC_POS_EINT2_MASK 0x0400 /* HP3R_SC_POS_EINT2 */ +#define ARIZONA_HP3R_SC_POS_EINT2_SHIFT 10 /* HP3R_SC_POS_EINT2 */ +#define ARIZONA_HP3R_SC_POS_EINT2_WIDTH 1 /* HP3R_SC_POS_EINT2 */ +#define ARIZONA_HP3L_SC_NEG_EINT2 0x0200 /* HP3L_SC_NEG_EINT2 */ +#define ARIZONA_HP3L_SC_NEG_EINT2_MASK 0x0200 /* HP3L_SC_NEG_EINT2 */ +#define ARIZONA_HP3L_SC_NEG_EINT2_SHIFT 9 /* HP3L_SC_NEG_EINT2 */ +#define ARIZONA_HP3L_SC_NEG_EINT2_WIDTH 1 /* HP3L_SC_NEG_EINT2 */ +#define ARIZONA_HP3L_SC_POS_EINT2 0x0100 /* HP3L_SC_POS_EINT2 */ +#define ARIZONA_HP3L_SC_POS_EINT2_MASK 0x0100 /* HP3L_SC_POS_EINT2 */ +#define ARIZONA_HP3L_SC_POS_EINT2_SHIFT 8 /* HP3L_SC_POS_EINT2 */ +#define ARIZONA_HP3L_SC_POS_EINT2_WIDTH 1 /* HP3L_SC_POS_EINT2 */ +#define ARIZONA_HP2R_SC_NEG_EINT2 0x0080 /* HP2R_SC_NEG_EINT2 */ +#define ARIZONA_HP2R_SC_NEG_EINT2_MASK 0x0080 /* HP2R_SC_NEG_EINT2 */ +#define ARIZONA_HP2R_SC_NEG_EINT2_SHIFT 7 /* HP2R_SC_NEG_EINT2 */ +#define ARIZONA_HP2R_SC_NEG_EINT2_WIDTH 1 /* HP2R_SC_NEG_EINT2 */ +#define ARIZONA_HP2R_SC_POS_EINT2 0x0040 /* HP2R_SC_POS_EINT2 */ +#define ARIZONA_HP2R_SC_POS_EINT2_MASK 0x0040 /* HP2R_SC_POS_EINT2 */ +#define ARIZONA_HP2R_SC_POS_EINT2_SHIFT 6 /* HP2R_SC_POS_EINT2 */ +#define ARIZONA_HP2R_SC_POS_EINT2_WIDTH 1 /* HP2R_SC_POS_EINT2 */ +#define ARIZONA_HP2L_SC_NEG_EINT2 0x0020 /* HP2L_SC_NEG_EINT2 */ +#define ARIZONA_HP2L_SC_NEG_EINT2_MASK 0x0020 /* HP2L_SC_NEG_EINT2 */ +#define ARIZONA_HP2L_SC_NEG_EINT2_SHIFT 5 /* HP2L_SC_NEG_EINT2 */ +#define ARIZONA_HP2L_SC_NEG_EINT2_WIDTH 1 /* HP2L_SC_NEG_EINT2 */ +#define ARIZONA_HP2L_SC_POS_EINT2 0x0010 /* HP2L_SC_POS_EINT2 */ +#define ARIZONA_HP2L_SC_POS_EINT2_MASK 0x0010 /* HP2L_SC_POS_EINT2 */ +#define ARIZONA_HP2L_SC_POS_EINT2_SHIFT 4 /* HP2L_SC_POS_EINT2 */ +#define ARIZONA_HP2L_SC_POS_EINT2_WIDTH 1 /* HP2L_SC_POS_EINT2 */ +#define ARIZONA_HP1R_SC_NEG_EINT2 0x0008 /* HP1R_SC_NEG_EINT2 */ +#define ARIZONA_HP1R_SC_NEG_EINT2_MASK 0x0008 /* HP1R_SC_NEG_EINT2 */ +#define ARIZONA_HP1R_SC_NEG_EINT2_SHIFT 3 /* HP1R_SC_NEG_EINT2 */ +#define ARIZONA_HP1R_SC_NEG_EINT2_WIDTH 1 /* HP1R_SC_NEG_EINT2 */ +#define ARIZONA_HP1R_SC_POS_EINT2 0x0004 /* HP1R_SC_POS_EINT2 */ +#define ARIZONA_HP1R_SC_POS_EINT2_MASK 0x0004 /* HP1R_SC_POS_EINT2 */ +#define ARIZONA_HP1R_SC_POS_EINT2_SHIFT 2 /* HP1R_SC_POS_EINT2 */ +#define ARIZONA_HP1R_SC_POS_EINT2_WIDTH 1 /* HP1R_SC_POS_EINT2 */ +#define ARIZONA_HP1L_SC_NEG_EINT2 0x0002 /* HP1L_SC_NEG_EINT2 */ +#define ARIZONA_HP1L_SC_NEG_EINT2_MASK 0x0002 /* HP1L_SC_NEG_EINT2 */ +#define ARIZONA_HP1L_SC_NEG_EINT2_SHIFT 1 /* HP1L_SC_NEG_EINT2 */ +#define ARIZONA_HP1L_SC_NEG_EINT2_WIDTH 1 /* HP1L_SC_NEG_EINT2 */ +#define ARIZONA_HP1L_SC_POS_EINT2 0x0001 /* HP1L_SC_POS_EINT2 */ +#define ARIZONA_HP1L_SC_POS_EINT2_MASK 0x0001 /* HP1L_SC_POS_EINT2 */ +#define ARIZONA_HP1L_SC_POS_EINT2_SHIFT 0 /* HP1L_SC_POS_EINT2 */ +#define ARIZONA_HP1L_SC_POS_EINT2_WIDTH 1 /* HP1L_SC_POS_EINT2 */ + /* * R3352 (0xD18) - IRQ2 Status 1 Mask */ @@ -5404,6 +5787,53 @@ #define ARIZONA_IM_HP1L_DONE_EINT2_SHIFT 0 /* IM_HP1L_DONE_EINT2 */ #define ARIZONA_IM_HP1L_DONE_EINT2_WIDTH 1 /* IM_HP1L_DONE_EINT2 */ +/* + * R3355 (0xD1B) - IRQ2 Status 4 Mask (Alternate layout) + * + * Alternate layout used on later devices, note only fields that have moved + * are specified + */ +#define ARIZONA_V2_IM_AIF3_ERR_EINT2 0x8000 /* IM_AIF3_ERR_EINT2 */ +#define ARIZONA_V2_IM_AIF3_ERR_EINT2_MASK 0x8000 /* IM_AIF3_ERR_EINT2 */ +#define ARIZONA_V2_IM_AIF3_ERR_EINT2_SHIFT 15 /* IM_AIF3_ERR_EINT2 */ +#define ARIZONA_V2_IM_AIF3_ERR_EINT2_WIDTH 1 /* IM_AIF3_ERR_EINT2 */ +#define ARIZONA_V2_IM_AIF2_ERR_EINT2 0x4000 /* IM_AIF2_ERR_EINT2 */ +#define ARIZONA_V2_IM_AIF2_ERR_EINT2_MASK 0x4000 /* IM_AIF2_ERR_EINT2 */ +#define ARIZONA_V2_IM_AIF2_ERR_EINT2_SHIFT 14 /* IM_AIF2_ERR_EINT2 */ +#define ARIZONA_V2_IM_AIF2_ERR_EINT2_WIDTH 1 /* IM_AIF2_ERR_EINT2 */ +#define ARIZONA_V2_IM_AIF1_ERR_EINT2 0x2000 /* IM_AIF1_ERR_EINT2 */ +#define ARIZONA_V2_IM_AIF1_ERR_EINT2_MASK 0x2000 /* IM_AIF1_ERR_EINT2 */ +#define ARIZONA_V2_IM_AIF1_ERR_EINT2_SHIFT 13 /* IM_AIF1_ERR_EINT2 */ +#define ARIZONA_V2_IM_AIF1_ERR_EINT2_WIDTH 1 /* IM_AIF1_ERR_EINT2 */ +#define ARIZONA_V2_IM_CTRLIF_ERR_EINT2 0x1000 /* IM_CTRLIF_ERR_EINT2 */ +#define ARIZONA_V2_IM_CTRLIF_ERR_EINT2_MASK 0x1000 /* IM_CTRLIF_ERR_EINT2 */ +#define ARIZONA_V2_IM_CTRLIF_ERR_EINT2_SHIFT 12 /* IM_CTRLIF_ERR_EINT2 */ +#define ARIZONA_V2_IM_CTRLIF_ERR_EINT2_WIDTH 1 /* IM_CTRLIF_ERR_EINT2 */ +#define ARIZONA_V2_IM_MIXER_DROPPED_SAMPLE_EINT2 0x0800 /* IM_MIXER_DROPPED_SAMPLE_EINT2 */ +#define ARIZONA_V2_IM_MIXER_DROPPED_SAMPLE_EINT2_MASK 0x0800 /* IM_MIXER_DROPPED_SAMPLE_EINT2 */ +#define ARIZONA_V2_IM_MIXER_DROPPED_SAMPLE_EINT2_SHIFT 11 /* IM_MIXER_DROPPED_SAMPLE_EINT2 */ +#define ARIZONA_V2_IM_MIXER_DROPPED_SAMPLE_EINT2_WIDTH 1 /* IM_MIXER_DROPPED_SAMPLE_EINT2 */ +#define ARIZONA_V2_IM_ASYNC_CLK_ENA_LOW_EINT2 0x0400 /* IM_ASYNC_CLK_ENA_LOW_EINT2 */ +#define ARIZONA_V2_IM_ASYNC_CLK_ENA_LOW_EINT2_MASK 0x0400 /* IM_ASYNC_CLK_ENA_LOW_EINT2 */ +#define ARIZONA_V2_IM_ASYNC_CLK_ENA_LOW_EINT2_SHIFT 10 /* IM_ASYNC_CLK_ENA_LOW_EINT2 */ +#define ARIZONA_V2_IM_ASYNC_CLK_ENA_LOW_EINT2_WIDTH 1 /* IM_ASYNC_CLK_ENA_LOW_EINT2 */ +#define ARIZONA_V2_IM_SYSCLK_ENA_LOW_EINT2 0x0200 /* IM_SYSCLK_ENA_LOW_EINT2 */ +#define ARIZONA_V2_IM_SYSCLK_ENA_LOW_EINT2_MASK 0x0200 /* IM_SYSCLK_ENA_LOW_EINT2 */ +#define ARIZONA_V2_IM_SYSCLK_ENA_LOW_EINT2_SHIFT 9 /* IM_SYSCLK_ENA_LOW_EINT2 */ +#define ARIZONA_V2_IM_SYSCLK_ENA_LOW_EINT2_WIDTH 1 /* IM_SYSCLK_ENA_LOW_EINT2 */ +#define ARIZONA_V2_IM_ISRC1_CFG_ERR_EINT2 0x0100 /* IM_ISRC1_CFG_ERR_EINT2 */ +#define ARIZONA_V2_IM_ISRC1_CFG_ERR_EINT2_MASK 0x0100 /* IM_ISRC1_CFG_ERR_EINT2 */ +#define ARIZONA_V2_IM_ISRC1_CFG_ERR_EINT2_SHIFT 8 /* IM_ISRC1_CFG_ERR_EINT2 */ +#define ARIZONA_V2_IM_ISRC1_CFG_ERR_EINT2_WIDTH 1 /* IM_ISRC1_CFG_ERR_EINT2 */ +#define ARIZONA_V2_IM_ISRC2_CFG_ERR_EINT2 0x0080 /* IM_ISRC2_CFG_ERR_EINT2 */ +#define ARIZONA_V2_IM_ISRC2_CFG_ERR_EINT2_MASK 0x0080 /* IM_ISRC2_CFG_ERR_EINT2 */ +#define ARIZONA_V2_IM_ISRC2_CFG_ERR_EINT2_SHIFT 7 /* IM_ISRC2_CFG_ERR_EINT2 */ +#define ARIZONA_V2_IM_ISRC2_CFG_ERR_EINT2_WIDTH 1 /* IM_ISRC2_CFG_ERR_EINT2 */ +#define ARIZONA_V2_IM_ISRC3_CFG_ERR_EINT2 0x0040 /* IM_ISRC3_CFG_ERR_EINT2 */ +#define ARIZONA_V2_IM_ISRC3_CFG_ERR_EINT2_MASK 0x0040 /* IM_ISRC3_CFG_ERR_EINT2 */ +#define ARIZONA_V2_IM_ISRC3_CFG_ERR_EINT2_SHIFT 6 /* IM_ISRC3_CFG_ERR_EINT2 */ +#define ARIZONA_V2_IM_ISRC3_CFG_ERR_EINT2_WIDTH 1 /* IM_ISRC3_CFG_ERR_EINT2 */ + /* * R3356 (0xD1C) - IRQ2 Status 5 Mask */ @@ -5429,6 +5859,85 @@ #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 */ +/* + * R3340 (0xD0C) - Interrupt Status 5 Mask (Alternate layout) + * + * Alternate layout used on later devices, note only fields that have moved + * are specified + */ +#define ARIZONA_V2_IM_ASRC_CFG_ERR_EINT2 0x0008 /* IM_ASRC_CFG_ERR_EINT2 */ +#define ARIZONA_V2_IM_ASRC_CFG_ERR_EINT2_MASK 0x0008 /* IM_ASRC_CFG_ERR_EINT2 */ +#define ARIZONA_V2_IM_ASRC_CFG_ERR_EINT2_SHIFT 3 /* IM_ASRC_CFG_ERR_EINT2 */ +#define ARIZONA_V2_IM_ASRC_CFG_ERR_EINT2_WIDTH 1 /* IM_ASRC_CFG_ERR_EINT2 */ + +/* + * R3357 (0xD1D) - IRQ2 Status 6 Mask + */ +#define ARIZONA_IM_DSP_SHARED_WR_COLL_EINT2 0x8000 /* IM_DSP_SHARED_WR_COLL_EINT2 */ +#define ARIZONA_IM_DSP_SHARED_WR_COLL_EINT2_MASK 0x8000 /* IM_DSP_SHARED_WR_COLL_EINT2 */ +#define ARIZONA_IM_DSP_SHARED_WR_COLL_EINT2_SHIFT 15 /* IM_DSP_SHARED_WR_COLL_EINT2 */ +#define ARIZONA_IM_DSP_SHARED_WR_COLL_EINT2_WIDTH 1 /* IM_DSP_SHARED_WR_COLL_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_SPK1R_SHORT_EINT2 0x2000 /* IM_SPK1R_SHORT_EINT2 */ +#define ARIZONA_IM_SPK1R_SHORT_EINT2_MASK 0x2000 /* IM_SPK1R_SHORT_EINT2 */ +#define ARIZONA_IM_SPK1R_SHORT_EINT2_SHIFT 13 /* IM_SPK1R_SHORT_EINT2 */ +#define ARIZONA_IM_SPK1R_SHORT_EINT2_WIDTH 1 /* IM_SPK1R_SHORT_EINT2 */ +#define ARIZONA_IM_SPK1L_SHORT_EINT2 0x1000 /* IM_SPK1L_SHORT_EINT2 */ +#define ARIZONA_IM_SPK1L_SHORT_EINT2_MASK 0x1000 /* IM_SPK1L_SHORT_EINT2 */ +#define ARIZONA_IM_SPK1L_SHORT_EINT2_SHIFT 12 /* IM_SPK1L_SHORT_EINT2 */ +#define ARIZONA_IM_SPK1L_SHORT_EINT2_WIDTH 1 /* IM_SPK1L_SHORT_EINT2 */ +#define ARIZONA_IM_HP3R_SC_NEG_EINT2 0x0800 /* IM_HP3R_SC_NEG_EINT2 */ +#define ARIZONA_IM_HP3R_SC_NEG_EINT2_MASK 0x0800 /* IM_HP3R_SC_NEG_EINT2 */ +#define ARIZONA_IM_HP3R_SC_NEG_EINT2_SHIFT 11 /* IM_HP3R_SC_NEG_EINT2 */ +#define ARIZONA_IM_HP3R_SC_NEG_EINT2_WIDTH 1 /* IM_HP3R_SC_NEG_EINT2 */ +#define ARIZONA_IM_HP3R_SC_POS_EINT2 0x0400 /* IM_HP3R_SC_POS_EINT2 */ +#define ARIZONA_IM_HP3R_SC_POS_EINT2_MASK 0x0400 /* IM_HP3R_SC_POS_EINT2 */ +#define ARIZONA_IM_HP3R_SC_POS_EINT2_SHIFT 10 /* IM_HP3R_SC_POS_EINT2 */ +#define ARIZONA_IM_HP3R_SC_POS_EINT2_WIDTH 1 /* IM_HP3R_SC_POS_EINT2 */ +#define ARIZONA_IM_HP3L_SC_NEG_EINT2 0x0200 /* IM_HP3L_SC_NEG_EINT2 */ +#define ARIZONA_IM_HP3L_SC_NEG_EINT2_MASK 0x0200 /* IM_HP3L_SC_NEG_EINT2 */ +#define ARIZONA_IM_HP3L_SC_NEG_EINT2_SHIFT 9 /* IM_HP3L_SC_NEG_EINT2 */ +#define ARIZONA_IM_HP3L_SC_NEG_EINT2_WIDTH 1 /* IM_HP3L_SC_NEG_EINT2 */ +#define ARIZONA_IM_HP3L_SC_POS_EINT2 0x0100 /* IM_HP3L_SC_POS_EINT2 */ +#define ARIZONA_IM_HP3L_SC_POS_EINT2_MASK 0x0100 /* IM_HP3L_SC_POS_EINT2 */ +#define ARIZONA_IM_HP3L_SC_POS_EINT2_SHIFT 8 /* IM_HP3L_SC_POS_EINT2 */ +#define ARIZONA_IM_HP3L_SC_POS_EINT2_WIDTH 1 /* IM_HP3L_SC_POS_EINT2 */ +#define ARIZONA_IM_HP2R_SC_NEG_EINT2 0x0080 /* IM_HP2R_SC_NEG_EINT2 */ +#define ARIZONA_IM_HP2R_SC_NEG_EINT2_MASK 0x0080 /* IM_HP2R_SC_NEG_EINT2 */ +#define ARIZONA_IM_HP2R_SC_NEG_EINT2_SHIFT 7 /* IM_HP2R_SC_NEG_EINT2 */ +#define ARIZONA_IM_HP2R_SC_NEG_EINT2_WIDTH 1 /* IM_HP2R_SC_NEG_EINT2 */ +#define ARIZONA_IM_HP2R_SC_POS_EINT2 0x0040 /* IM_HP2R_SC_POS_EINT2 */ +#define ARIZONA_IM_HP2R_SC_POS_EINT2_MASK 0x0040 /* IM_HP2R_SC_POS_EINT2 */ +#define ARIZONA_IM_HP2R_SC_POS_EINT2_SHIFT 6 /* IM_HP2R_SC_POS_EINT2 */ +#define ARIZONA_IM_HP2R_SC_POS_EINT2_WIDTH 1 /* IM_HP2R_SC_POS_EINT2 */ +#define ARIZONA_IM_HP2L_SC_NEG_EINT2 0x0020 /* IM_HP2L_SC_NEG_EINT2 */ +#define ARIZONA_IM_HP2L_SC_NEG_EINT2_MASK 0x0020 /* IM_HP2L_SC_NEG_EINT2 */ +#define ARIZONA_IM_HP2L_SC_NEG_EINT2_SHIFT 5 /* IM_HP2L_SC_NEG_EINT2 */ +#define ARIZONA_IM_HP2L_SC_NEG_EINT2_WIDTH 1 /* IM_HP2L_SC_NEG_EINT2 */ +#define ARIZONA_IM_HP2L_SC_POS_EINT2 0x0010 /* IM_HP2L_SC_POS_EINT2 */ +#define ARIZONA_IM_HP2L_SC_POS_EINT2_MASK 0x0010 /* IM_HP2L_SC_POS_EINT2 */ +#define ARIZONA_IM_HP2L_SC_POS_EINT2_SHIFT 4 /* IM_HP2L_SC_POS_EINT2 */ +#define ARIZONA_IM_HP2L_SC_POS_EINT2_WIDTH 1 /* IM_HP2L_SC_POS_EINT2 */ +#define ARIZONA_IM_HP1R_SC_NEG_EINT2 0x0008 /* IM_HP1R_SC_NEG_EINT2 */ +#define ARIZONA_IM_HP1R_SC_NEG_EINT2_MASK 0x0008 /* IM_HP1R_SC_NEG_EINT2 */ +#define ARIZONA_IM_HP1R_SC_NEG_EINT2_SHIFT 3 /* IM_HP1R_SC_NEG_EINT2 */ +#define ARIZONA_IM_HP1R_SC_NEG_EINT2_WIDTH 1 /* IM_HP1R_SC_NEG_EINT2 */ +#define ARIZONA_IM_HP1R_SC_POS_EINT2 0x0004 /* IM_HP1R_SC_POS_EINT2 */ +#define ARIZONA_IM_HP1R_SC_POS_EINT2_MASK 0x0004 /* IM_HP1R_SC_POS_EINT2 */ +#define ARIZONA_IM_HP1R_SC_POS_EINT2_SHIFT 2 /* IM_HP1R_SC_POS_EINT2 */ +#define ARIZONA_IM_HP1R_SC_POS_EINT2_WIDTH 1 /* IM_HP1R_SC_POS_EINT2 */ +#define ARIZONA_IM_HP1L_SC_NEG_EINT2 0x0002 /* IM_HP1L_SC_NEG_EINT2 */ +#define ARIZONA_IM_HP1L_SC_NEG_EINT2_MASK 0x0002 /* IM_HP1L_SC_NEG_EINT2 */ +#define ARIZONA_IM_HP1L_SC_NEG_EINT2_SHIFT 1 /* IM_HP1L_SC_NEG_EINT2 */ +#define ARIZONA_IM_HP1L_SC_NEG_EINT2_WIDTH 1 /* IM_HP1L_SC_NEG_EINT2 */ +#define ARIZONA_IM_HP1L_SC_POS_EINT2 0x0001 /* IM_HP1L_SC_POS_EINT2 */ +#define ARIZONA_IM_HP1L_SC_POS_EINT2_MASK 0x0001 /* IM_HP1L_SC_POS_EINT2 */ +#define ARIZONA_IM_HP1L_SC_POS_EINT2_SHIFT 0 /* IM_HP1L_SC_POS_EINT2 */ +#define ARIZONA_IM_HP1L_SC_POS_EINT2_WIDTH 1 /* IM_HP1L_SC_POS_EINT2 */ + /* * R3359 (0xD1F) - IRQ2 Control */ @@ -5700,6 +6209,10 @@ #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_ISRC3_OVERCLOCKED_STS 0x0004 /* ISRC3_OVERCLOCKED_STS */ +#define ARIZONA_ISRC3_OVERCLOCKED_STS_MASK 0x0004 /* ISRC3_OVERCLOCKED_STS */ +#define ARIZONA_ISRC3_OVERCLOCKED_STS_SHIFT 2 /* ISRC3_OVERCLOCKED_STS */ +#define ARIZONA_ISRC3_OVERCLOCKED_STS_WIDTH 1 /* ISRC3_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 */ @@ -5724,6 +6237,10 @@ #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_ISRC3_UNDERCLOCKED_STS 0x0080 /* ISRC3_UNDERCLOCKED_STS */ +#define ARIZONA_ISRC3_UNDERCLOCKED_STS_MASK 0x0080 /* ISRC3_UNDERCLOCKED_STS */ +#define ARIZONA_ISRC3_UNDERCLOCKED_STS_SHIFT 7 /* ISRC3_UNDERCLOCKED_STS */ +#define ARIZONA_ISRC3_UNDERCLOCKED_STS_WIDTH 1 /* ISRC3_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 */ @@ -5753,6 +6270,74 @@ #define ARIZONA_MIXER_UNDERCLOCKED_STS_SHIFT 0 /* MIXER_UNDERCLOCKED_STS */ #define ARIZONA_MIXER_UNDERCLOCKED_STS_WIDTH 1 /* MIXER_UNDERCLOCKED_STS */ +/* + * R3368 (0xD28) - Interrupt Raw Status 9 + */ +#define ARIZONA_DSP_SHARED_WR_COLL_STS 0x8000 /* DSP_SHARED_WR_COLL_STS */ +#define ARIZONA_DSP_SHARED_WR_COLL_STS_MASK 0x8000 /* DSP_SHARED_WR_COLL_STS */ +#define ARIZONA_DSP_SHARED_WR_COLL_STS_SHIFT 15 /* DSP_SHARED_WR_COLL_STS */ +#define ARIZONA_DSP_SHARED_WR_COLL_STS_WIDTH 1 /* DSP_SHARED_WR_COLL_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_SPK1R_SHORT_STS 0x2000 /* SPK1R_SHORT_STS */ +#define ARIZONA_SPK1R_SHORT_STS_MASK 0x2000 /* SPK1R_SHORT_STS */ +#define ARIZONA_SPK1R_SHORT_STS_SHIFT 13 /* SPK1R_SHORT_STS */ +#define ARIZONA_SPK1R_SHORT_STS_WIDTH 1 /* SPK1R_SHORT_STS */ +#define ARIZONA_SPK1L_SHORT_STS 0x1000 /* SPK1L_SHORT_STS */ +#define ARIZONA_SPK1L_SHORT_STS_MASK 0x1000 /* SPK1L_SHORT_STS */ +#define ARIZONA_SPK1L_SHORT_STS_SHIFT 12 /* SPK1L_SHORT_STS */ +#define ARIZONA_SPK1L_SHORT_STS_WIDTH 1 /* SPK1L_SHORT_STS */ +#define ARIZONA_HP3R_SC_NEG_STS 0x0800 /* HP3R_SC_NEG_STS */ +#define ARIZONA_HP3R_SC_NEG_STS_MASK 0x0800 /* HP3R_SC_NEG_STS */ +#define ARIZONA_HP3R_SC_NEG_STS_SHIFT 11 /* HP3R_SC_NEG_STS */ +#define ARIZONA_HP3R_SC_NEG_STS_WIDTH 1 /* HP3R_SC_NEG_STS */ +#define ARIZONA_HP3R_SC_POS_STS 0x0400 /* HP3R_SC_POS_STS */ +#define ARIZONA_HP3R_SC_POS_STS_MASK 0x0400 /* HP3R_SC_POS_STS */ +#define ARIZONA_HP3R_SC_POS_STS_SHIFT 10 /* HP3R_SC_POS_STS */ +#define ARIZONA_HP3R_SC_POS_STS_WIDTH 1 /* HP3R_SC_POS_STS */ +#define ARIZONA_HP3L_SC_NEG_STS 0x0200 /* HP3L_SC_NEG_STS */ +#define ARIZONA_HP3L_SC_NEG_STS_MASK 0x0200 /* HP3L_SC_NEG_STS */ +#define ARIZONA_HP3L_SC_NEG_STS_SHIFT 9 /* HP3L_SC_NEG_STS */ +#define ARIZONA_HP3L_SC_NEG_STS_WIDTH 1 /* HP3L_SC_NEG_STS */ +#define ARIZONA_HP3L_SC_POS_STS 0x0100 /* HP3L_SC_POS_STS */ +#define ARIZONA_HP3L_SC_POS_STS_MASK 0x0100 /* HP3L_SC_POS_STS */ +#define ARIZONA_HP3L_SC_POS_STS_SHIFT 8 /* HP3L_SC_POS_STS */ +#define ARIZONA_HP3L_SC_POS_STS_WIDTH 1 /* HP3L_SC_POS_STS */ +#define ARIZONA_HP2R_SC_NEG_STS 0x0080 /* HP2R_SC_NEG_STS */ +#define ARIZONA_HP2R_SC_NEG_STS_MASK 0x0080 /* HP2R_SC_NEG_STS */ +#define ARIZONA_HP2R_SC_NEG_STS_SHIFT 7 /* HP2R_SC_NEG_STS */ +#define ARIZONA_HP2R_SC_NEG_STS_WIDTH 1 /* HP2R_SC_NEG_STS */ +#define ARIZONA_HP2R_SC_POS_STS 0x0040 /* HP2R_SC_POS_STS */ +#define ARIZONA_HP2R_SC_POS_STS_MASK 0x0040 /* HP2R_SC_POS_STS */ +#define ARIZONA_HP2R_SC_POS_STS_SHIFT 6 /* HP2R_SC_POS_STS */ +#define ARIZONA_HP2R_SC_POS_STS_WIDTH 1 /* HP2R_SC_POS_STS */ +#define ARIZONA_HP2L_SC_NEG_STS 0x0020 /* HP2L_SC_NEG_STS */ +#define ARIZONA_HP2L_SC_NEG_STS_MASK 0x0020 /* HP2L_SC_NEG_STS */ +#define ARIZONA_HP2L_SC_NEG_STS_SHIFT 5 /* HP2L_SC_NEG_STS */ +#define ARIZONA_HP2L_SC_NEG_STS_WIDTH 1 /* HP2L_SC_NEG_STS */ +#define ARIZONA_HP2L_SC_POS_STS 0x0010 /* HP2L_SC_POS_STS */ +#define ARIZONA_HP2L_SC_POS_STS_MASK 0x0010 /* HP2L_SC_POS_STS */ +#define ARIZONA_HP2L_SC_POS_STS_SHIFT 4 /* HP2L_SC_POS_STS */ +#define ARIZONA_HP2L_SC_POS_STS_WIDTH 1 /* HP2L_SC_POS_STS */ +#define ARIZONA_HP1R_SC_NEG_STS 0x0008 /* HP1R_SC_NEG_STS */ +#define ARIZONA_HP1R_SC_NEG_STS_MASK 0x0008 /* HP1R_SC_NEG_STS */ +#define ARIZONA_HP1R_SC_NEG_STS_SHIFT 3 /* HP1R_SC_NEG_STS */ +#define ARIZONA_HP1R_SC_NEG_STS_WIDTH 1 /* HP1R_SC_NEG_STS */ +#define ARIZONA_HP1R_SC_POS_STS 0x0004 /* HP1R_SC_POS_STS */ +#define ARIZONA_HP1R_SC_POS_STS_MASK 0x0004 /* HP1R_SC_POS_STS */ +#define ARIZONA_HP1R_SC_POS_STS_SHIFT 2 /* HP1R_SC_POS_STS */ +#define ARIZONA_HP1R_SC_POS_STS_WIDTH 1 /* HP1R_SC_POS_STS */ +#define ARIZONA_HP1L_SC_NEG_STS 0x0002 /* HP1L_SC_NEG_STS */ +#define ARIZONA_HP1L_SC_NEG_STS_MASK 0x0002 /* HP1L_SC_NEG_STS */ +#define ARIZONA_HP1L_SC_NEG_STS_SHIFT 1 /* HP1L_SC_NEG_STS */ +#define ARIZONA_HP1L_SC_NEG_STS_WIDTH 1 /* HP1L_SC_NEG_STS */ +#define ARIZONA_HP1L_SC_POS_STS 0x0001 /* HP1L_SC_POS_STS */ +#define ARIZONA_HP1L_SC_POS_STS_MASK 0x0001 /* HP1L_SC_POS_STS */ +#define ARIZONA_HP1L_SC_POS_STS_SHIFT 0 /* HP1L_SC_POS_STS */ +#define ARIZONA_HP1L_SC_POS_STS_WIDTH 1 /* HP1L_SC_POS_STS */ + /* * R3392 (0xD40) - IRQ Pin Status */ -- cgit v1.2.3 From 30a2af3a320d5c0598cde08ba6e5d22a724f82e4 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 15 Jul 2014 11:21:50 +0100 Subject: mfd: arizona: Only free the CTRLIF_ERR IRQ if we requested it We only request the control interface error IRQ if we set ctrlif_error, as such we should only free it in that situation. Otherwise we will attempt to free an IRQ we never requested and get a warning from the IRQ core. This patch moves the ctrlif_error variable into the arizona structure and checks it in all cases we free the control interface error IRQ. Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/arizona-irq.c | 19 ++++++++++++------- include/linux/mfd/arizona/core.h | 2 ++ 2 files changed, 14 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c index e780bc40165d..d420dbc0e2b0 100644 --- a/drivers/mfd/arizona-irq.c +++ b/drivers/mfd/arizona-irq.c @@ -188,16 +188,17 @@ int arizona_irq_init(struct arizona *arizona) int flags = IRQF_ONESHOT; int ret, i; const struct regmap_irq_chip *aod, *irq; - bool ctrlif_error = true; struct irq_data *irq_data; + arizona->ctrlif_error = true; + switch (arizona->type) { #ifdef CONFIG_MFD_WM5102 case WM5102: aod = &wm5102_aod; irq = &wm5102_irq; - ctrlif_error = false; + arizona->ctrlif_error = false; break; #endif #ifdef CONFIG_MFD_WM5110 @@ -213,7 +214,7 @@ int arizona_irq_init(struct arizona *arizona) break; } - ctrlif_error = false; + arizona->ctrlif_error = false; break; #endif #ifdef CONFIG_MFD_WM8997 @@ -221,7 +222,7 @@ int arizona_irq_init(struct arizona *arizona) aod = &wm8997_aod; irq = &wm8997_irq; - ctrlif_error = false; + arizona->ctrlif_error = false; break; #endif default: @@ -308,7 +309,7 @@ int arizona_irq_init(struct arizona *arizona) } /* Handle control interface errors in the core */ - if (ctrlif_error) { + if (arizona->ctrlif_error) { i = arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR); ret = request_threaded_irq(i, NULL, arizona_ctrlif_err, IRQF_ONESHOT, @@ -353,7 +354,9 @@ int arizona_irq_init(struct arizona *arizona) return 0; err_main_irq: - free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR), arizona); + if (arizona->ctrlif_error) + free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR), + arizona); err_ctrlif: free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE), arizona); err_boot_done: @@ -369,7 +372,9 @@ err: int arizona_irq_exit(struct arizona *arizona) { - free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR), arizona); + if (arizona->ctrlif_error) + free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR), + arizona); free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE), arizona); regmap_del_irq_chip(irq_create_mapping(arizona->virq, 1), arizona->irq_chip); diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h index 8bc7601cca68..fdd8b7b82db5 100644 --- a/include/linux/mfd/arizona/core.h +++ b/include/linux/mfd/arizona/core.h @@ -132,6 +132,8 @@ struct arizona { struct mutex clk_lock; int clk32k_ref; + bool ctrlif_error; + struct snd_soc_dapm_context *dapm; }; -- cgit v1.2.3 From 0a6d315827eedc733d404ecff3cd4cc0e6437865 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 24 Jul 2014 20:08:55 +0200 Subject: gpio: split gpiod board registration into machine header As per example from the regulator subsystem: put all defines and functions related to registering board info for GPIO descriptors into a separate header. Cc: Andrew Victor Cc: Nicolas Ferre Cc: Jean-Christophe Plagniol-Villard Cc: Ralf Baechle Cc: Thierry Reding Acked-by: Stephen Warren Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij --- Documentation/gpio/board.txt | 2 +- arch/arm/mach-at91/at91rm9200_devices.c | 2 +- arch/arm/mach-tegra/board-paz00.c | 2 +- arch/mips/jz4740/board-qi_lb60.c | 1 + drivers/gpio/gpiolib.c | 1 + include/linux/gpio/driver.h | 54 ------------------------------ include/linux/gpio/machine.h | 58 +++++++++++++++++++++++++++++++++ 7 files changed, 63 insertions(+), 57 deletions(-) create mode 100644 include/linux/gpio/machine.h (limited to 'include') diff --git a/Documentation/gpio/board.txt b/Documentation/gpio/board.txt index ba169faad5c6..4452786225b8 100644 --- a/Documentation/gpio/board.txt +++ b/Documentation/gpio/board.txt @@ -60,7 +60,7 @@ Platform Data Finally, GPIOs can be bound to devices and functions using platform data. Board files that desire to do so need to include the following header: - #include + #include GPIOs are mapped by the means of tables of lookups, containing instances of the gpiod_lookup structure. Two macros are defined to help declaring such mappings: diff --git a/arch/arm/mach-at91/at91rm9200_devices.c b/arch/arm/mach-at91/at91rm9200_devices.c index 3f4bb58aea54..74f1eaf97801 100644 --- a/arch/arm/mach-at91/at91rm9200_devices.c +++ b/arch/arm/mach-at91/at91rm9200_devices.c @@ -15,7 +15,7 @@ #include #include -#include +#include #include #include diff --git a/arch/arm/mach-tegra/board-paz00.c b/arch/arm/mach-tegra/board-paz00.c index 9c6029ba526f..91fd858ced0d 100644 --- a/arch/arm/mach-tegra/board-paz00.c +++ b/arch/arm/mach-tegra/board-paz00.c @@ -18,7 +18,7 @@ */ #include -#include +#include #include #include "board.h" diff --git a/arch/mips/jz4740/board-qi_lb60.c b/arch/mips/jz4740/board-qi_lb60.c index 088e92a79ae6..c454525e7695 100644 --- a/arch/mips/jz4740/board-qi_lb60.c +++ b/arch/mips/jz4740/board-qi_lb60.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 768f0831db18..18b069e6ba03 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "gpiolib.h" diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 4c463fb0155e..e78a2373e374 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -151,60 +151,6 @@ void gpio_unlock_as_irq(struct gpio_chip *chip, unsigned int offset); struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc); -enum gpio_lookup_flags { - GPIO_ACTIVE_HIGH = (0 << 0), - GPIO_ACTIVE_LOW = (1 << 0), - GPIO_OPEN_DRAIN = (1 << 1), - GPIO_OPEN_SOURCE = (1 << 2), -}; - -/** - * struct gpiod_lookup - lookup table - * @chip_label: name of the chip the GPIO belongs to - * @chip_hwnum: hardware number (i.e. relative to the chip) of the GPIO - * @con_id: name of the GPIO from the device's point of view - * @idx: index of the GPIO in case several GPIOs share the same name - * @flags: mask of GPIO_* values - * - * gpiod_lookup is a lookup table for associating GPIOs to specific devices and - * functions using platform data. - */ -struct gpiod_lookup { - const char *chip_label; - u16 chip_hwnum; - const char *con_id; - unsigned int idx; - enum gpio_lookup_flags flags; -}; - -struct gpiod_lookup_table { - struct list_head list; - const char *dev_id; - struct gpiod_lookup table[]; -}; - -/* - * Simple definition of a single GPIO under a con_id - */ -#define GPIO_LOOKUP(_chip_label, _chip_hwnum, _con_id, _flags) \ - GPIO_LOOKUP_IDX(_chip_label, _chip_hwnum, _con_id, 0, _flags) - -/* - * Use this macro if you need to have several GPIOs under the same con_id. - * Each GPIO needs to use a different index and can be accessed using - * gpiod_get_index() - */ -#define GPIO_LOOKUP_IDX(_chip_label, _chip_hwnum, _con_id, _idx, _flags) \ -{ \ - .chip_label = _chip_label, \ - .chip_hwnum = _chip_hwnum, \ - .con_id = _con_id, \ - .idx = _idx, \ - .flags = _flags, \ -} - -void gpiod_add_lookup_table(struct gpiod_lookup_table *table); - #ifdef CONFIG_GPIOLIB_IRQCHIP void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip, diff --git a/include/linux/gpio/machine.h b/include/linux/gpio/machine.h new file mode 100644 index 000000000000..b8ad87fab4ce --- /dev/null +++ b/include/linux/gpio/machine.h @@ -0,0 +1,58 @@ +#ifndef __LINUX_GPIO_MACHINE_H +#define __LINUX_GPIO_MACHINE_H + +enum gpio_lookup_flags { + GPIO_ACTIVE_HIGH = (0 << 0), + GPIO_ACTIVE_LOW = (1 << 0), + GPIO_OPEN_DRAIN = (1 << 1), + GPIO_OPEN_SOURCE = (1 << 2), +}; + +/** + * struct gpiod_lookup - lookup table + * @chip_label: name of the chip the GPIO belongs to + * @chip_hwnum: hardware number (i.e. relative to the chip) of the GPIO + * @con_id: name of the GPIO from the device's point of view + * @idx: index of the GPIO in case several GPIOs share the same name + * @flags: mask of GPIO_* values + * + * gpiod_lookup is a lookup table for associating GPIOs to specific devices and + * functions using platform data. + */ +struct gpiod_lookup { + const char *chip_label; + u16 chip_hwnum; + const char *con_id; + unsigned int idx; + enum gpio_lookup_flags flags; +}; + +struct gpiod_lookup_table { + struct list_head list; + const char *dev_id; + struct gpiod_lookup table[]; +}; + +/* + * Simple definition of a single GPIO under a con_id + */ +#define GPIO_LOOKUP(_chip_label, _chip_hwnum, _con_id, _flags) \ + GPIO_LOOKUP_IDX(_chip_label, _chip_hwnum, _con_id, 0, _flags) + +/* + * Use this macro if you need to have several GPIOs under the same con_id. + * Each GPIO needs to use a different index and can be accessed using + * gpiod_get_index() + */ +#define GPIO_LOOKUP_IDX(_chip_label, _chip_hwnum, _con_id, _idx, _flags) \ +{ \ + .chip_label = _chip_label, \ + .chip_hwnum = _chip_hwnum, \ + .con_id = _con_id, \ + .idx = _idx, \ + .flags = _flags, \ +} + +void gpiod_add_lookup_table(struct gpiod_lookup_table *table); + +#endif /* __LINUX_GPIO_MACHINE_H */ -- cgit v1.2.3 From 39b2bbe3d715cf5013b5c48695ccdd25bd3bf120 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 25 Jul 2014 23:38:36 +0900 Subject: gpio: add flags argument to gpiod_get*() functions The huge majority of GPIOs have their direction and initial value set right after being obtained by one of the gpiod_get() functions. The integer GPIO API had gpio_request_one() that took a convenience flags parameter allowing to specify an direction and value applied to the returned GPIO. This feature greatly simplifies client code and ensures errors are always handled properly. A similar feature has been requested for the gpiod API. Since setting the direction of a GPIO is so often the very next action done after obtaining its descriptor, we prefer to extend the existing functions instead of introducing new functions that would raise the number of gpiod getters to 16 (!). The drawback of this approach is that all gpiod clients need to be updated. To limit the pain, temporary macros are introduced that allow gpiod_get*() to be called with or without the extra flags argument. They will be removed once all consumer code has been updated. Signed-off-by: Alexandre Courbot Reviewed-by: Mark Brown Signed-off-by: Linus Walleij --- Documentation/gpio/consumer.txt | 26 ++++++++++--- drivers/gpio/devres.c | 40 ++++++++++++-------- drivers/gpio/gpiolib.c | 67 +++++++++++++++++++++++----------- include/linux/gpio/consumer.h | 81 +++++++++++++++++++++++++++++++++-------- 4 files changed, 155 insertions(+), 59 deletions(-) (limited to 'include') diff --git a/Documentation/gpio/consumer.txt b/Documentation/gpio/consumer.txt index d8abfc31abbe..76546324e968 100644 --- a/Documentation/gpio/consumer.txt +++ b/Documentation/gpio/consumer.txt @@ -29,13 +29,24 @@ gpiod_get() functions. Like many other kernel subsystems, gpiod_get() takes the device that will use the GPIO and the function the requested GPIO is supposed to fulfill: - struct gpio_desc *gpiod_get(struct device *dev, const char *con_id) + struct gpio_desc *gpiod_get(struct device *dev, const char *con_id, + enum gpiod_flags flags) If a function is implemented by using several GPIOs together (e.g. a simple LED device that displays digits), an additional index argument can be specified: struct gpio_desc *gpiod_get_index(struct device *dev, - const char *con_id, unsigned int idx) + const char *con_id, unsigned int idx, + enum gpiod_flags flags) + +The flags parameter is used to optionally specify a direction and initial value +for the GPIO. Values can be: + +* GPIOD_ASIS or 0 to not initialize the GPIO at all. The direction must be set + later with one of the dedicated functions. +* GPIOD_IN to initialize the GPIO as input. +* GPIOD_OUT_LOW to initialize the GPIO as output with a value of 0. +* GPIOD_OUT_HIGH to initialize the GPIO as output with a value of 1. Both functions return either a valid GPIO descriptor, or an error code checkable with IS_ERR() (they will never return a NULL pointer). -ENOENT will be returned @@ -46,11 +57,13 @@ errors and an absence of GPIO for optional GPIO parameters. Device-managed variants of these functions are also defined: - struct gpio_desc *devm_gpiod_get(struct device *dev, const char *con_id) + struct gpio_desc *devm_gpiod_get(struct device *dev, const char *con_id, + enum gpiod_flags flags) struct gpio_desc *devm_gpiod_get_index(struct device *dev, const char *con_id, - unsigned int idx) + unsigned int idx, + enum gpiod_flags flags) A GPIO descriptor can be disposed of using the gpiod_put() function: @@ -67,8 +80,9 @@ Using GPIOs Setting Direction ----------------- -The first thing a driver must do with a GPIO is setting its direction. This is -done by invoking one of the gpiod_direction_*() functions: +The first thing a driver must do with a GPIO is setting its direction. If no +direction-setting flags have been given to gpiod_get*(), this is done by +invoking one of the gpiod_direction_*() functions: int gpiod_direction_input(struct gpio_desc *desc) int gpiod_direction_output(struct gpio_desc *desc, int value) diff --git a/drivers/gpio/devres.c b/drivers/gpio/devres.c index 65978cf85f79..41b2f40578d5 100644 --- a/drivers/gpio/devres.c +++ b/drivers/gpio/devres.c @@ -39,47 +39,53 @@ static int devm_gpiod_match(struct device *dev, void *res, void *data) * devm_gpiod_get - Resource-managed gpiod_get() * @dev: GPIO consumer * @con_id: function within the GPIO consumer + * @flags: optional GPIO initialization flags * * Managed gpiod_get(). GPIO descriptors returned from this function are * automatically disposed on driver detach. See gpiod_get() for detailed * information about behavior and return values. */ -struct gpio_desc *__must_check devm_gpiod_get(struct device *dev, - const char *con_id) +struct gpio_desc *__must_check __devm_gpiod_get(struct device *dev, + const char *con_id, + enum gpiod_flags flags) { - return devm_gpiod_get_index(dev, con_id, 0); + return devm_gpiod_get_index(dev, con_id, 0, flags); } -EXPORT_SYMBOL(devm_gpiod_get); +EXPORT_SYMBOL(__devm_gpiod_get); /** * devm_gpiod_get_optional - Resource-managed gpiod_get_optional() * @dev: GPIO consumer * @con_id: function within the GPIO consumer + * @flags: optional GPIO initialization flags * * Managed gpiod_get_optional(). GPIO descriptors returned from this function * are automatically disposed on driver detach. See gpiod_get_optional() for * detailed information about behavior and return values. */ -struct gpio_desc *__must_check devm_gpiod_get_optional(struct device *dev, - const char *con_id) +struct gpio_desc *__must_check __devm_gpiod_get_optional(struct device *dev, + const char *con_id, + enum gpiod_flags flags) { - return devm_gpiod_get_index_optional(dev, con_id, 0); + return devm_gpiod_get_index_optional(dev, con_id, 0, flags); } -EXPORT_SYMBOL(devm_gpiod_get_optional); +EXPORT_SYMBOL(__devm_gpiod_get_optional); /** * devm_gpiod_get_index - Resource-managed gpiod_get_index() * @dev: GPIO consumer * @con_id: function within the GPIO consumer * @idx: index of the GPIO to obtain in the consumer + * @flags: optional GPIO initialization flags * * Managed gpiod_get_index(). GPIO descriptors returned from this function are * automatically disposed on driver detach. See gpiod_get_index() for detailed * information about behavior and return values. */ -struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev, +struct gpio_desc *__must_check __devm_gpiod_get_index(struct device *dev, const char *con_id, - unsigned int idx) + unsigned int idx, + enum gpiod_flags flags) { struct gpio_desc **dr; struct gpio_desc *desc; @@ -89,7 +95,7 @@ struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev, if (!dr) return ERR_PTR(-ENOMEM); - desc = gpiod_get_index(dev, con_id, idx); + desc = gpiod_get_index(dev, con_id, idx, flags); if (IS_ERR(desc)) { devres_free(dr); return desc; @@ -100,26 +106,28 @@ struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev, return desc; } -EXPORT_SYMBOL(devm_gpiod_get_index); +EXPORT_SYMBOL(__devm_gpiod_get_index); /** * devm_gpiod_get_index_optional - Resource-managed gpiod_get_index_optional() * @dev: GPIO consumer * @con_id: function within the GPIO consumer * @index: index of the GPIO to obtain in the consumer + * @flags: optional GPIO initialization flags * * Managed gpiod_get_index_optional(). GPIO descriptors returned from this * function are automatically disposed on driver detach. See * gpiod_get_index_optional() for detailed information about behavior and * return values. */ -struct gpio_desc *__must_check devm_gpiod_get_index_optional(struct device *dev, +struct gpio_desc *__must_check __devm_gpiod_get_index_optional(struct device *dev, const char *con_id, - unsigned int index) + unsigned int index, + enum gpiod_flags flags) { struct gpio_desc *desc; - desc = devm_gpiod_get_index(dev, con_id, index); + desc = devm_gpiod_get_index(dev, con_id, index, flags); if (IS_ERR(desc)) { if (PTR_ERR(desc) == -ENOENT) return NULL; @@ -127,7 +135,7 @@ struct gpio_desc *__must_check devm_gpiod_get_index_optional(struct device *dev, return desc; } -EXPORT_SYMBOL(devm_gpiod_get_index_optional); +EXPORT_SYMBOL(__devm_gpiod_get_index_optional); /** * devm_gpiod_put - Resource-managed gpiod_put() diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 330227581a25..15cc0bb65dda 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1582,38 +1582,43 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, * gpiod_get - obtain a GPIO for a given GPIO function * @dev: GPIO consumer, can be NULL for system-global GPIOs * @con_id: function within the GPIO consumer + * @flags: optional GPIO initialization flags * * Return the GPIO descriptor corresponding to the function con_id of device * dev, -ENOENT if no GPIO has been assigned to the requested function, or * another IS_ERR() code if an error occured while trying to acquire the GPIO. */ -struct gpio_desc *__must_check gpiod_get(struct device *dev, const char *con_id) +struct gpio_desc *__must_check __gpiod_get(struct device *dev, const char *con_id, + enum gpiod_flags flags) { - return gpiod_get_index(dev, con_id, 0); + return gpiod_get_index(dev, con_id, 0, flags); } -EXPORT_SYMBOL_GPL(gpiod_get); +EXPORT_SYMBOL_GPL(__gpiod_get); /** * gpiod_get_optional - obtain an optional GPIO for a given GPIO function * @dev: GPIO consumer, can be NULL for system-global GPIOs * @con_id: function within the GPIO consumer + * @flags: optional GPIO initialization flags * * This is equivalent to gpiod_get(), except that when no GPIO was assigned to * the requested function it will return NULL. This is convenient for drivers * that need to handle optional GPIOs. */ -struct gpio_desc *__must_check gpiod_get_optional(struct device *dev, - const char *con_id) +struct gpio_desc *__must_check __gpiod_get_optional(struct device *dev, + const char *con_id, + enum gpiod_flags flags) { - return gpiod_get_index_optional(dev, con_id, 0); + return gpiod_get_index_optional(dev, con_id, 0, flags); } -EXPORT_SYMBOL_GPL(gpiod_get_optional); +EXPORT_SYMBOL_GPL(__gpiod_get_optional); /** * gpiod_get_index - obtain a GPIO from a multi-index GPIO function * @dev: GPIO consumer, can be NULL for system-global GPIOs * @con_id: function within the GPIO consumer * @idx: index of the GPIO to obtain in the consumer + * @flags: optional GPIO initialization flags * * This variant of gpiod_get() allows to access GPIOs other than the first * defined one for functions that define several GPIOs. @@ -1622,23 +1627,24 @@ EXPORT_SYMBOL_GPL(gpiod_get_optional); * requested function and/or index, or another IS_ERR() code if an error * occured while trying to acquire the GPIO. */ -struct gpio_desc *__must_check gpiod_get_index(struct device *dev, +struct gpio_desc *__must_check __gpiod_get_index(struct device *dev, const char *con_id, - unsigned int idx) + unsigned int idx, + enum gpiod_flags flags) { struct gpio_desc *desc = NULL; int status; - enum gpio_lookup_flags flags = 0; + enum gpio_lookup_flags lookupflags = 0; dev_dbg(dev, "GPIO lookup for consumer %s\n", con_id); /* Using device tree? */ if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) { dev_dbg(dev, "using device tree for GPIO lookup\n"); - desc = of_find_gpio(dev, con_id, idx, &flags); + desc = of_find_gpio(dev, con_id, idx, &lookupflags); } else if (IS_ENABLED(CONFIG_ACPI) && dev && ACPI_HANDLE(dev)) { dev_dbg(dev, "using ACPI for GPIO lookup\n"); - desc = acpi_find_gpio(dev, con_id, idx, &flags); + desc = acpi_find_gpio(dev, con_id, idx, &lookupflags); } /* @@ -1647,7 +1653,7 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev, */ if (!desc || desc == ERR_PTR(-ENOENT)) { dev_dbg(dev, "using lookup tables for GPIO lookup"); - desc = gpiod_find(dev, con_id, idx, &flags); + desc = gpiod_find(dev, con_id, idx, &lookupflags); } if (IS_ERR(desc)) { @@ -1660,16 +1666,33 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev, if (status < 0) return ERR_PTR(status); - if (flags & GPIO_ACTIVE_LOW) + if (lookupflags & GPIO_ACTIVE_LOW) set_bit(FLAG_ACTIVE_LOW, &desc->flags); - if (flags & GPIO_OPEN_DRAIN) + if (lookupflags & GPIO_OPEN_DRAIN) set_bit(FLAG_OPEN_DRAIN, &desc->flags); - if (flags & GPIO_OPEN_SOURCE) + if (lookupflags & GPIO_OPEN_SOURCE) set_bit(FLAG_OPEN_SOURCE, &desc->flags); + /* No particular flag request, return here... */ + if (flags & GPIOD_FLAGS_BIT_DIR_SET) + return desc; + + /* Process flags */ + if (flags & GPIOD_FLAGS_BIT_DIR_OUT) + status = gpiod_direction_output(desc, + flags & GPIOD_FLAGS_BIT_DIR_VAL); + else + status = gpiod_direction_input(desc); + + if (status < 0) { + dev_dbg(dev, "setup of GPIO %s failed\n", con_id); + gpiod_put(desc); + return ERR_PTR(status); + } + return desc; } -EXPORT_SYMBOL_GPL(gpiod_get_index); +EXPORT_SYMBOL_GPL(__gpiod_get_index); /** * gpiod_get_index_optional - obtain an optional GPIO from a multi-index GPIO @@ -1677,18 +1700,20 @@ EXPORT_SYMBOL_GPL(gpiod_get_index); * @dev: GPIO consumer, can be NULL for system-global GPIOs * @con_id: function within the GPIO consumer * @index: index of the GPIO to obtain in the consumer + * @flags: optional GPIO initialization flags * * This is equivalent to gpiod_get_index(), except that when no GPIO with the * specified index was assigned to the requested function it will return NULL. * This is convenient for drivers that need to handle optional GPIOs. */ -struct gpio_desc *__must_check gpiod_get_index_optional(struct device *dev, +struct gpio_desc *__must_check __gpiod_get_index_optional(struct device *dev, const char *con_id, - unsigned int index) + unsigned int index, + enum gpiod_flags flags) { struct gpio_desc *desc; - desc = gpiod_get_index(dev, con_id, index); + desc = gpiod_get_index(dev, con_id, index, flags); if (IS_ERR(desc)) { if (PTR_ERR(desc) == -ENOENT) return NULL; @@ -1696,7 +1721,7 @@ struct gpio_desc *__must_check gpiod_get_index_optional(struct device *dev, return desc; } -EXPORT_SYMBOL_GPL(gpiod_get_index_optional); +EXPORT_SYMBOL_GPL(__gpiod_get_index_optional); /** * gpiod_put - dispose of a GPIO descriptor diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 05e53ccb708b..b7ce0c64c6f3 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -18,30 +18,79 @@ struct gpio_desc; #ifdef CONFIG_GPIOLIB +#define GPIOD_FLAGS_BIT_DIR_SET BIT(0) +#define GPIOD_FLAGS_BIT_DIR_OUT BIT(1) +#define GPIOD_FLAGS_BIT_DIR_VAL BIT(2) + +/** + * Optional flags that can be passed to one of gpiod_* to configure direction + * and output value. These values cannot be OR'd. + */ +enum gpiod_flags { + GPIOD_ASIS = 0, + GPIOD_IN = GPIOD_FLAGS_BIT_DIR_SET, + GPIOD_OUT_LOW = GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT, + GPIOD_OUT_HIGH = GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT | + GPIOD_FLAGS_BIT_DIR_VAL, +}; + /* Acquire and dispose GPIOs */ -struct gpio_desc *__must_check gpiod_get(struct device *dev, - const char *con_id); -struct gpio_desc *__must_check gpiod_get_index(struct device *dev, +struct gpio_desc *__must_check __gpiod_get(struct device *dev, + const char *con_id, + enum gpiod_flags flags); +#define __gpiod_get(dev, con_id, flags, ...) __gpiod_get(dev, con_id, flags) +#define gpiod_get(varargs...) __gpiod_get(varargs, 0) +struct gpio_desc *__must_check __gpiod_get_index(struct device *dev, const char *con_id, - unsigned int idx); -struct gpio_desc *__must_check gpiod_get_optional(struct device *dev, - const char *con_id); -struct gpio_desc *__must_check gpiod_get_index_optional(struct device *dev, + unsigned int idx, + enum gpiod_flags flags); +#define __gpiod_get_index(dev, con_id, index, flags, ...) \ + __gpiod_get_index(dev, con_id, index, flags) +#define gpiod_get_index(varargs...) __gpiod_get_index(varargs, 0) +struct gpio_desc *__must_check __gpiod_get_optional(struct device *dev, + const char *con_id, + enum gpiod_flags flags); +#define __gpiod_get_optional(dev, con_id, flags, ...) \ + __gpiod_get_optional(dev, con_id, flags) +#define gpiod_get_optional(varargs...) __gpiod_get_optional(varargs, 0) +struct gpio_desc *__must_check __gpiod_get_index_optional(struct device *dev, const char *con_id, - unsigned int index); + unsigned int index, + enum gpiod_flags flags); +#define __gpiod_get_index_optional(dev, con_id, index, flags, ...) \ + __gpiod_get_index_optional(dev, con_id, index, flags) +#define gpiod_get_index_optional(varargs...) \ + __gpiod_get_index_optional(varargs, 0) void gpiod_put(struct gpio_desc *desc); -struct gpio_desc *__must_check devm_gpiod_get(struct device *dev, - const char *con_id); -struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev, +struct gpio_desc *__must_check __devm_gpiod_get(struct device *dev, + const char *con_id, + enum gpiod_flags flags); +#define __devm_gpiod_get(dev, con_id, flags, ...) \ + __devm_gpiod_get(dev, con_id, flags) +#define devm_gpiod_get(varargs...) __devm_gpiod_get(varargs, 0) +struct gpio_desc *__must_check __devm_gpiod_get_index(struct device *dev, const char *con_id, - unsigned int idx); -struct gpio_desc *__must_check devm_gpiod_get_optional(struct device *dev, - const char *con_id); + unsigned int idx, + enum gpiod_flags flags); +#define __devm_gpiod_get_index(dev, con_id, index, flags, ...) \ + __devm_gpiod_get_index(dev, con_id, index, flags) +#define devm_gpiod_get_index(varargs...) __devm_gpiod_get_index(varargs, 0) +struct gpio_desc *__must_check __devm_gpiod_get_optional(struct device *dev, + const char *con_id, + enum gpiod_flags flags); +#define __devm_gpiod_get_optional(dev, con_id, flags, ...) \ + __devm_gpiod_get_optional(dev, con_id, flags) +#define devm_gpiod_get_optional(varargs...) \ + __devm_gpiod_get_optional(varargs, 0) struct gpio_desc *__must_check -devm_gpiod_get_index_optional(struct device *dev, const char *con_id, - unsigned int index); +__devm_gpiod_get_index_optional(struct device *dev, const char *con_id, + unsigned int index, enum gpiod_flags flags); +#define __devm_gpiod_get_index_optional(dev, con_id, index, flags, ...) \ + __devm_gpiod_get_index_optional(dev, con_id, index, flags) +#define devm_gpiod_get_index_optional(varargs...) \ + __devm_gpiod_get_index_optional(varargs, 0) void devm_gpiod_put(struct device *dev, struct gpio_desc *desc); -- cgit v1.2.3 From eb3fe7def66511120766c8fc05ee9631cce7fe6f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 8 Jul 2014 13:46:37 +0300 Subject: ARM: edma: Add edma_assign_channel_eventq() to move channel to a give queue In some cases it is desired to move a channel to a specific event queue. Such a use case is audio, where it is preferred that it is served with highest priority compared to other DMA clients. Signed-off-by: Peter Ujfalusi Acked-by: Sekhar Nori Signed-off-by: Vinod Koul --- arch/arm/common/edma.c | 28 ++++++++++++++++++++++++++++ include/linux/platform_data/edma.h | 2 ++ 2 files changed, 30 insertions(+) (limited to 'include') diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index f834aae7720f..88099175fc56 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -1414,6 +1414,34 @@ void edma_clear_event(unsigned channel) } EXPORT_SYMBOL(edma_clear_event); +/* + * edma_assign_channel_eventq - move given channel to desired eventq + * Arguments: + * channel - channel number + * eventq_no - queue to move the channel + * + * Can be used to move a channel to a selected event queue. + */ +void edma_assign_channel_eventq(unsigned channel, enum dma_event_q eventq_no) +{ + unsigned ctlr; + + ctlr = EDMA_CTLR(channel); + channel = EDMA_CHAN_SLOT(channel); + + if (channel >= edma_cc[ctlr]->num_channels) + return; + + /* default to low priority queue */ + if (eventq_no == EVENTQ_DEFAULT) + eventq_no = edma_cc[ctlr]->default_queue; + if (eventq_no >= edma_cc[ctlr]->num_tc) + return; + + map_dmach_queue(ctlr, channel, eventq_no); +} +EXPORT_SYMBOL(edma_assign_channel_eventq); + static int edma_setup_from_hw(struct device *dev, struct edma_soc_info *pdata, struct edma *edma_cc) { diff --git a/include/linux/platform_data/edma.h b/include/linux/platform_data/edma.h index eb8d5627d080..bdb2710e2aab 100644 --- a/include/linux/platform_data/edma.h +++ b/include/linux/platform_data/edma.h @@ -150,6 +150,8 @@ void edma_clear_event(unsigned channel); void edma_pause(unsigned channel); void edma_resume(unsigned channel); +void edma_assign_channel_eventq(unsigned channel, enum dma_event_q eventq_no); + struct edma_rsv_info { const s16 (*rsv_chans)[2]; -- cgit v1.2.3 From 699a0ea0823d32030b0666b28ff8633960f7ffa7 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Mon, 2 Jun 2014 11:02:59 +1000 Subject: KVM: PPC: Book3S: Controls for in-kernel sPAPR hypercall handling This provides a way for userspace controls which sPAPR hcalls get handled in the kernel. Each hcall can be individually enabled or disabled for in-kernel handling, except for H_RTAS. The exception for H_RTAS is because userspace can already control whether individual RTAS functions are handled in-kernel or not via the KVM_PPC_RTAS_DEFINE_TOKEN ioctl, and because the numeric value for H_RTAS is out of the normal sequence of hcall numbers. Hcalls are enabled or disabled using the KVM_ENABLE_CAP ioctl for the KVM_CAP_PPC_ENABLE_HCALL capability on the file descriptor for the VM. The args field of the struct kvm_enable_cap specifies the hcall number in args[0] and the enable/disable flag in args[1]; 0 means disable in-kernel handling (so that the hcall will always cause an exit to userspace) and 1 means enable. Enabling or disabling in-kernel handling of an hcall is effective across the whole VM. The ability for KVM_ENABLE_CAP to be used on a VM file descriptor on PowerPC is new, added by this commit. The KVM_CAP_ENABLE_CAP_VM capability advertises that this ability exists. When a VM is created, an initial set of hcalls are enabled for in-kernel handling. The set that is enabled is the set that have an in-kernel implementation at this point. Any new hcall implementations from this point onwards should not be added to the default set without a good reason. No distinction is made between real-mode and virtual-mode hcall implementations; the one setting controls them both. Signed-off-by: Paul Mackerras Signed-off-by: Alexander Graf --- Documentation/virtual/kvm/api.txt | 41 ++++++++++++++++++++++++-- arch/powerpc/include/asm/kvm_book3s.h | 1 + arch/powerpc/include/asm/kvm_host.h | 2 ++ arch/powerpc/kernel/asm-offsets.c | 1 + arch/powerpc/kvm/book3s_hv.c | 51 +++++++++++++++++++++++++++++++++ arch/powerpc/kvm/book3s_hv_rmhandlers.S | 11 +++++++ arch/powerpc/kvm/book3s_pr.c | 5 ++++ arch/powerpc/kvm/book3s_pr_papr.c | 37 ++++++++++++++++++++++++ arch/powerpc/kvm/powerpc.c | 45 +++++++++++++++++++++++++++++ include/uapi/linux/kvm.h | 1 + 10 files changed, 193 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 0fe36497642c..5c54d196f4c8 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -2863,8 +2863,8 @@ The fields in each entry are defined as follows: this function/index combination -6. Capabilities that can be enabled ------------------------------------ +6. Capabilities that can be enabled on vCPUs +-------------------------------------------- There are certain capabilities that change the behavior of the virtual CPU when enabled. To enable them, please see section 4.37. Below you can find a list of @@ -3002,3 +3002,40 @@ Parameters: args[0] is the XICS device fd args[1] is the XICS CPU number (server ID) for this vcpu This capability connects the vcpu to an in-kernel XICS device. + + +7. Capabilities that can be enabled on VMs +------------------------------------------ + +There are certain capabilities that change the behavior of the virtual +machine when enabled. To enable them, please see section 4.37. Below +you can find a list of capabilities and what their effect on the VM +is when enabling them. + +The following information is provided along with the description: + + Architectures: which instruction set architectures provide this ioctl. + x86 includes both i386 and x86_64. + + Parameters: what parameters are accepted by the capability. + + Returns: the return value. General error numbers (EBADF, ENOMEM, EINVAL) + are not detailed, but errors with specific meanings are. + + +7.1 KVM_CAP_PPC_ENABLE_HCALL + +Architectures: ppc +Parameters: args[0] is the sPAPR hcall number + args[1] is 0 to disable, 1 to enable in-kernel handling + +This capability controls whether individual sPAPR hypercalls (hcalls) +get handled by the kernel or not. Enabling or disabling in-kernel +handling of an hcall is effective across the VM. On creation, an +initial set of hcalls are enabled for in-kernel handling, which +consists of those hcalls for which in-kernel handlers were implemented +before this capability was implemented. If disabled, the kernel will +not to attempt to handle the hcall, but will always exit to userspace +to handle it. Note that it may not make sense to enable some and +disable others of a group of related hcalls, but KVM does not prevent +userspace from doing that. diff --git a/arch/powerpc/include/asm/kvm_book3s.h b/arch/powerpc/include/asm/kvm_book3s.h index a20cc0bbd048..052ab2ad49b5 100644 --- a/arch/powerpc/include/asm/kvm_book3s.h +++ b/arch/powerpc/include/asm/kvm_book3s.h @@ -187,6 +187,7 @@ extern void kvmppc_hv_entry_trampoline(void); extern u32 kvmppc_alignment_dsisr(struct kvm_vcpu *vcpu, unsigned int inst); extern ulong kvmppc_alignment_dar(struct kvm_vcpu *vcpu, unsigned int inst); extern int kvmppc_h_pr(struct kvm_vcpu *vcpu, unsigned long cmd); +extern void kvmppc_pr_init_default_hcalls(struct kvm *kvm); extern void kvmppc_copy_to_svcpu(struct kvmppc_book3s_shadow_vcpu *svcpu, struct kvm_vcpu *vcpu); extern void kvmppc_copy_from_svcpu(struct kvm_vcpu *vcpu, diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index f9ae69682ce1..62b2cee450a5 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h @@ -34,6 +34,7 @@ #include #include #include +#include #define KVM_MAX_VCPUS NR_CPUS #define KVM_MAX_VCORES NR_CPUS @@ -263,6 +264,7 @@ struct kvm_arch { #ifdef CONFIG_PPC_BOOK3S_64 struct list_head spapr_tce_tables; struct list_head rtas_tokens; + DECLARE_BITMAP(enabled_hcalls, MAX_HCALL_OPCODE/4 + 1); #endif #ifdef CONFIG_KVM_MPIC struct openpic *mpic; diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index f5995a912213..17ffcb4f27f9 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -493,6 +493,7 @@ int main(void) DEFINE(KVM_HOST_SDR1, offsetof(struct kvm, arch.host_sdr1)); DEFINE(KVM_TLBIE_LOCK, offsetof(struct kvm, arch.tlbie_lock)); DEFINE(KVM_NEED_FLUSH, offsetof(struct kvm, arch.need_tlb_flush.bits)); + DEFINE(KVM_ENABLED_HCALLS, offsetof(struct kvm, arch.enabled_hcalls)); DEFINE(KVM_LPCR, offsetof(struct kvm, arch.lpcr)); DEFINE(KVM_RMOR, offsetof(struct kvm, arch.rmor)); DEFINE(KVM_VRMA_SLB_V, offsetof(struct kvm, arch.vrma_slb_v)); diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index 1562acfa05bf..cf445d22570f 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -67,6 +67,8 @@ /* Used as a "null" value for timebase values */ #define TB_NIL (~(u64)0) +static DECLARE_BITMAP(default_enabled_hcalls, MAX_HCALL_OPCODE/4 + 1); + static void kvmppc_end_cede(struct kvm_vcpu *vcpu); static int kvmppc_hv_setup_htab_rma(struct kvm_vcpu *vcpu); @@ -562,6 +564,10 @@ int kvmppc_pseries_do_hcall(struct kvm_vcpu *vcpu) struct kvm_vcpu *tvcpu; int idx, rc; + if (req <= MAX_HCALL_OPCODE && + !test_bit(req/4, vcpu->kvm->arch.enabled_hcalls)) + return RESUME_HOST; + switch (req) { case H_ENTER: idx = srcu_read_lock(&vcpu->kvm->srcu); @@ -2269,6 +2275,10 @@ static int kvmppc_core_init_vm_hv(struct kvm *kvm) */ cpumask_setall(&kvm->arch.need_tlb_flush); + /* Start out with the default set of hcalls enabled */ + memcpy(kvm->arch.enabled_hcalls, default_enabled_hcalls, + sizeof(kvm->arch.enabled_hcalls)); + kvm->arch.rma = NULL; kvm->arch.host_sdr1 = mfspr(SPRN_SDR1); @@ -2407,6 +2417,45 @@ static long kvm_arch_vm_ioctl_hv(struct file *filp, return r; } +/* + * List of hcall numbers to enable by default. + * For compatibility with old userspace, we enable by default + * all hcalls that were implemented before the hcall-enabling + * facility was added. Note this list should not include H_RTAS. + */ +static unsigned int default_hcall_list[] = { + H_REMOVE, + H_ENTER, + H_READ, + H_PROTECT, + H_BULK_REMOVE, + H_GET_TCE, + H_PUT_TCE, + H_SET_DABR, + H_SET_XDABR, + H_CEDE, + H_PROD, + H_CONFER, + H_REGISTER_VPA, +#ifdef CONFIG_KVM_XICS + H_EOI, + H_CPPR, + H_IPI, + H_IPOLL, + H_XIRR, + H_XIRR_X, +#endif + 0 +}; + +static void init_default_hcalls(void) +{ + int i; + + for (i = 0; default_hcall_list[i]; ++i) + __set_bit(default_hcall_list[i] / 4, default_enabled_hcalls); +} + static struct kvmppc_ops kvm_ops_hv = { .get_sregs = kvm_arch_vcpu_ioctl_get_sregs_hv, .set_sregs = kvm_arch_vcpu_ioctl_set_sregs_hv, @@ -2454,6 +2503,8 @@ static int kvmppc_book3s_init_hv(void) kvm_ops_hv.owner = THIS_MODULE; kvmppc_hv_ops = &kvm_ops_hv; + init_default_hcalls(); + r = kvmppc_mmu_hv_init(); return r; } diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S index 64ac56f6c3fe..33aaadef7139 100644 --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S @@ -1909,6 +1909,17 @@ hcall_try_real_mode: clrrdi r3,r3,2 cmpldi r3,hcall_real_table_end - hcall_real_table bge guest_exit_cont + /* See if this hcall is enabled for in-kernel handling */ + ld r4, VCPU_KVM(r9) + srdi r0, r3, 8 /* r0 = (r3 / 4) >> 6 */ + sldi r0, r0, 3 /* index into kvm->arch.enabled_hcalls[] */ + add r4, r4, r0 + ld r0, KVM_ENABLED_HCALLS(r4) + rlwinm r4, r3, 32-2, 0x3f /* r4 = (r3 / 4) & 0x3f */ + srd r0, r0, r4 + andi. r0, r0, 1 + beq guest_exit_cont + /* Get pointer to handler, if any, and call it */ LOAD_REG_ADDR(r4, hcall_real_table) lwax r3,r3,r4 cmpwi r3,0 diff --git a/arch/powerpc/kvm/book3s_pr.c b/arch/powerpc/kvm/book3s_pr.c index 3b82e8616dfa..123ac7dc5e1f 100644 --- a/arch/powerpc/kvm/book3s_pr.c +++ b/arch/powerpc/kvm/book3s_pr.c @@ -1597,6 +1597,11 @@ static int kvmppc_core_init_vm_pr(struct kvm *kvm) { mutex_init(&kvm->arch.hpt_mutex); +#ifdef CONFIG_PPC_BOOK3S_64 + /* Start out with the default set of hcalls enabled */ + kvmppc_pr_init_default_hcalls(kvm); +#endif + if (firmware_has_feature(FW_FEATURE_SET_MODE)) { spin_lock(&kvm_global_user_count_lock); if (++kvm_global_user_count == 1) diff --git a/arch/powerpc/kvm/book3s_pr_papr.c b/arch/powerpc/kvm/book3s_pr_papr.c index f7c25c625a5b..eacaa6e4876e 100644 --- a/arch/powerpc/kvm/book3s_pr_papr.c +++ b/arch/powerpc/kvm/book3s_pr_papr.c @@ -267,6 +267,10 @@ static int kvmppc_h_pr_xics_hcall(struct kvm_vcpu *vcpu, u32 cmd) int kvmppc_h_pr(struct kvm_vcpu *vcpu, unsigned long cmd) { + if (cmd <= MAX_HCALL_OPCODE && + !test_bit(cmd/4, vcpu->kvm->arch.enabled_hcalls)) + return EMULATE_FAIL; + switch (cmd) { case H_ENTER: return kvmppc_h_pr_enter(vcpu); @@ -304,3 +308,36 @@ int kvmppc_h_pr(struct kvm_vcpu *vcpu, unsigned long cmd) return EMULATE_FAIL; } + + +/* + * List of hcall numbers to enable by default. + * For compatibility with old userspace, we enable by default + * all hcalls that were implemented before the hcall-enabling + * facility was added. Note this list should not include H_RTAS. + */ +static unsigned int default_hcall_list[] = { + H_ENTER, + H_REMOVE, + H_PROTECT, + H_BULK_REMOVE, + H_PUT_TCE, + H_CEDE, +#ifdef CONFIG_KVM_XICS + H_XIRR, + H_CPPR, + H_EOI, + H_IPI, + H_IPOLL, + H_XIRR_X, +#endif + 0 +}; + +void kvmppc_pr_init_default_hcalls(struct kvm *kvm) +{ + int i; + + for (i = 0; default_hcall_list[i]; ++i) + __set_bit(default_hcall_list[i] / 4, kvm->arch.enabled_hcalls); +} diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index 61c738ab1283..3222a4d08a6f 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -387,6 +387,7 @@ int kvm_dev_ioctl_check_extension(long ext) case KVM_CAP_PPC_UNSET_IRQ: case KVM_CAP_PPC_IRQ_LEVEL: case KVM_CAP_ENABLE_CAP: + case KVM_CAP_ENABLE_CAP_VM: case KVM_CAP_ONE_REG: case KVM_CAP_IOEVENTFD: case KVM_CAP_DEVICE_CTRL: @@ -417,6 +418,7 @@ int kvm_dev_ioctl_check_extension(long ext) case KVM_CAP_PPC_ALLOC_HTAB: case KVM_CAP_PPC_RTAS: case KVM_CAP_PPC_FIXUP_HCALL: + case KVM_CAP_PPC_ENABLE_HCALL: #ifdef CONFIG_KVM_XICS case KVM_CAP_IRQ_XICS: #endif @@ -1099,6 +1101,40 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_event, return 0; } + +static int kvm_vm_ioctl_enable_cap(struct kvm *kvm, + struct kvm_enable_cap *cap) +{ + int r; + + if (cap->flags) + return -EINVAL; + + switch (cap->cap) { +#ifdef CONFIG_KVM_BOOK3S_64_HANDLER + case KVM_CAP_PPC_ENABLE_HCALL: { + unsigned long hcall = cap->args[0]; + + r = -EINVAL; + if (hcall > MAX_HCALL_OPCODE || (hcall & 3) || + cap->args[1] > 1) + break; + if (cap->args[1]) + set_bit(hcall / 4, kvm->arch.enabled_hcalls); + else + clear_bit(hcall / 4, kvm->arch.enabled_hcalls); + r = 0; + break; + } +#endif + default: + r = -EINVAL; + break; + } + + return r; +} + long kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { @@ -1118,6 +1154,15 @@ long kvm_arch_vm_ioctl(struct file *filp, break; } + case KVM_ENABLE_CAP: + { + struct kvm_enable_cap cap; + r = -EFAULT; + if (copy_from_user(&cap, argp, sizeof(cap))) + goto out; + r = kvm_vm_ioctl_enable_cap(kvm, &cap); + break; + } #ifdef CONFIG_PPC_BOOK3S_64 case KVM_CREATE_SPAPR_TCE: { struct kvm_create_spapr_tce create_tce; diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index e11d8f170a62..0418b746cb68 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -758,6 +758,7 @@ struct kvm_ppc_smmu_info { #define KVM_CAP_VM_ATTRIBUTES 101 #define KVM_CAP_ARM_PSCI_0_2 102 #define KVM_CAP_PPC_FIXUP_HCALL 103 +#define KVM_CAP_PPC_ENABLE_HCALL 104 #ifdef KVM_CAP_IRQ_ROUTING -- cgit v1.2.3 From 784aa3d7fb6f729c06d5836c9d9569f58e4d05ae Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Mon, 14 Jul 2014 18:27:35 +0200 Subject: KVM: Rename and add argument to check_extension In preparation to make the check_extension function available to VM scope we add a struct kvm * argument to the function header and rename the function accordingly. It will still be called from the /dev/kvm fd, but with a NULL argument for struct kvm *. Signed-off-by: Alexander Graf Acked-by: Paolo Bonzini --- arch/arm/kvm/arm.c | 2 +- arch/ia64/kvm/kvm-ia64.c | 2 +- arch/mips/kvm/mips.c | 2 +- arch/powerpc/kvm/powerpc.c | 2 +- arch/s390/kvm/kvm-s390.c | 2 +- arch/x86/kvm/x86.c | 2 +- include/linux/kvm_host.h | 2 +- virt/kvm/kvm_main.c | 6 +++--- 8 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c index 3c82b37c0f9e..cb77f999badd 100644 --- a/arch/arm/kvm/arm.c +++ b/arch/arm/kvm/arm.c @@ -184,7 +184,7 @@ void kvm_arch_destroy_vm(struct kvm *kvm) } } -int kvm_dev_ioctl_check_extension(long ext) +int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) { int r; switch (ext) { diff --git a/arch/ia64/kvm/kvm-ia64.c b/arch/ia64/kvm/kvm-ia64.c index 6a4309bb821a..0729ba6acddf 100644 --- a/arch/ia64/kvm/kvm-ia64.c +++ b/arch/ia64/kvm/kvm-ia64.c @@ -190,7 +190,7 @@ void kvm_arch_check_processor_compat(void *rtn) *(int *)rtn = 0; } -int kvm_dev_ioctl_check_extension(long ext) +int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) { int r; diff --git a/arch/mips/kvm/mips.c b/arch/mips/kvm/mips.c index d687c6e3258d..3ca79aa011df 100644 --- a/arch/mips/kvm/mips.c +++ b/arch/mips/kvm/mips.c @@ -885,7 +885,7 @@ int kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf) return VM_FAULT_SIGBUS; } -int kvm_dev_ioctl_check_extension(long ext) +int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) { int r; diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index 8e0356835960..d870bacc2f75 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -391,7 +391,7 @@ void kvm_arch_sync_events(struct kvm *kvm) { } -int kvm_dev_ioctl_check_extension(long ext) +int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) { int r; /* FIXME!! diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 2f3e14fe91a4..00268cacdf4c 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -146,7 +146,7 @@ long kvm_arch_dev_ioctl(struct file *filp, return -EINVAL; } -int kvm_dev_ioctl_check_extension(long ext) +int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) { int r; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 5a8691b0ed76..5a62d91c96e7 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -2616,7 +2616,7 @@ out: return r; } -int kvm_dev_ioctl_check_extension(long ext) +int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) { int r; diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index ec4e3bd83d47..5065b953e6e8 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -602,7 +602,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg); int kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf); -int kvm_dev_ioctl_check_extension(long ext); +int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext); int kvm_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log, int *is_dirty); diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 4b6c01b477f9..e28f3caa539d 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -2571,7 +2571,7 @@ static int kvm_dev_ioctl_create_vm(unsigned long type) return r; } -static long kvm_dev_ioctl_check_extension_generic(long arg) +static long kvm_vm_ioctl_check_extension_generic(struct kvm *kvm, long arg) { switch (arg) { case KVM_CAP_USER_MEMORY: @@ -2595,7 +2595,7 @@ static long kvm_dev_ioctl_check_extension_generic(long arg) default: break; } - return kvm_dev_ioctl_check_extension(arg); + return kvm_vm_ioctl_check_extension(kvm, arg); } static long kvm_dev_ioctl(struct file *filp, @@ -2614,7 +2614,7 @@ static long kvm_dev_ioctl(struct file *filp, r = kvm_dev_ioctl_create_vm(arg); break; case KVM_CHECK_EXTENSION: - r = kvm_dev_ioctl_check_extension_generic(arg); + r = kvm_vm_ioctl_check_extension_generic(NULL, arg); break; case KVM_GET_VCPU_MMAP_SIZE: r = -EINVAL; -- cgit v1.2.3 From 92b591a4c46b103ebd3fc0d03a084e1efd331253 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Mon, 14 Jul 2014 18:33:08 +0200 Subject: KVM: Allow KVM_CHECK_EXTENSION on the vm fd The KVM_CHECK_EXTENSION is only available on the kvm fd today. Unfortunately on PPC some of the capabilities change depending on the way a VM was created. So instead we need a way to expose capabilities as VM ioctl, so that we can see which VM type we're using (HV or PR). To enable this, add the KVM_CHECK_EXTENSION ioctl to our vm ioctl portfolio. Signed-off-by: Alexander Graf Acked-by: Paolo Bonzini --- Documentation/virtual/kvm/api.txt | 7 +++-- include/uapi/linux/kvm.h | 1 + virt/kvm/kvm_main.c | 58 +++++++++++++++++++++------------------ 3 files changed, 37 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 884f819e1908..8898caf8c090 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -148,9 +148,9 @@ of banks, as set via the KVM_X86_SETUP_MCE ioctl. 4.4 KVM_CHECK_EXTENSION -Capability: basic +Capability: basic, KVM_CAP_CHECK_EXTENSION_VM for vm ioctl Architectures: all -Type: system ioctl +Type: system ioctl, vm ioctl Parameters: extension identifier (KVM_CAP_*) Returns: 0 if unsupported; 1 (or some other positive integer) if supported @@ -160,6 +160,9 @@ receives an integer that describes the extension availability. Generally 0 means no and 1 means yes, but some extensions may report additional information in the integer return value. +Based on their initialization different VMs may have different capabilities. +It is thus encouraged to use the vm ioctl to query for capabilities (available +with KVM_CAP_CHECK_EXTENSION_VM on the vm fd) 4.5 KVM_GET_VCPU_MMAP_SIZE diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 0418b746cb68..51776cac6a9b 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -759,6 +759,7 @@ struct kvm_ppc_smmu_info { #define KVM_CAP_ARM_PSCI_0_2 102 #define KVM_CAP_PPC_FIXUP_HCALL 103 #define KVM_CAP_PPC_ENABLE_HCALL 104 +#define KVM_CAP_CHECK_EXTENSION_VM 105 #ifdef KVM_CAP_IRQ_ROUTING diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index e28f3caa539d..1b95cc926cfc 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -2324,6 +2324,34 @@ static int kvm_ioctl_create_device(struct kvm *kvm, return 0; } +static long kvm_vm_ioctl_check_extension_generic(struct kvm *kvm, long arg) +{ + switch (arg) { + case KVM_CAP_USER_MEMORY: + case KVM_CAP_DESTROY_MEMORY_REGION_WORKS: + case KVM_CAP_JOIN_MEMORY_REGIONS_WORKS: +#ifdef CONFIG_KVM_APIC_ARCHITECTURE + case KVM_CAP_SET_BOOT_CPU_ID: +#endif + case KVM_CAP_INTERNAL_ERROR_DATA: +#ifdef CONFIG_HAVE_KVM_MSI + case KVM_CAP_SIGNAL_MSI: +#endif +#ifdef CONFIG_HAVE_KVM_IRQ_ROUTING + case KVM_CAP_IRQFD_RESAMPLE: +#endif + case KVM_CAP_CHECK_EXTENSION_VM: + return 1; +#ifdef CONFIG_HAVE_KVM_IRQ_ROUTING + case KVM_CAP_IRQ_ROUTING: + return KVM_MAX_IRQ_ROUTES; +#endif + default: + break; + } + return kvm_vm_ioctl_check_extension(kvm, arg); +} + static long kvm_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { @@ -2487,6 +2515,9 @@ static long kvm_vm_ioctl(struct file *filp, r = 0; break; } + case KVM_CHECK_EXTENSION: + r = kvm_vm_ioctl_check_extension_generic(kvm, arg); + break; default: r = kvm_arch_vm_ioctl(filp, ioctl, arg); if (r == -ENOTTY) @@ -2571,33 +2602,6 @@ static int kvm_dev_ioctl_create_vm(unsigned long type) return r; } -static long kvm_vm_ioctl_check_extension_generic(struct kvm *kvm, long arg) -{ - switch (arg) { - case KVM_CAP_USER_MEMORY: - case KVM_CAP_DESTROY_MEMORY_REGION_WORKS: - case KVM_CAP_JOIN_MEMORY_REGIONS_WORKS: -#ifdef CONFIG_KVM_APIC_ARCHITECTURE - case KVM_CAP_SET_BOOT_CPU_ID: -#endif - case KVM_CAP_INTERNAL_ERROR_DATA: -#ifdef CONFIG_HAVE_KVM_MSI - case KVM_CAP_SIGNAL_MSI: -#endif -#ifdef CONFIG_HAVE_KVM_IRQ_ROUTING - case KVM_CAP_IRQFD_RESAMPLE: -#endif - return 1; -#ifdef CONFIG_HAVE_KVM_IRQ_ROUTING - case KVM_CAP_IRQ_ROUTING: - return KVM_MAX_IRQ_ROUTES; -#endif - default: - break; - } - return kvm_vm_ioctl_check_extension(kvm, arg); -} - static long kvm_dev_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { -- cgit v1.2.3 From 2873ead7e46694910ac49c3a8ee0f54956f96e0c Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Mon, 28 Jul 2014 10:42:48 -0400 Subject: Revert "selinux: fix the default socket labeling in sock_graft()" This reverts commit 4da6daf4d3df5a977e4623963f141a627fd2efce. Unfortunately, the commit in question caused problems with Bluetooth devices, specifically it caused them to get caught in the newly created BUG_ON() check. The AF_ALG problem still exists, but will be addressed in a future patch. Cc: stable@vger.kernel.org Signed-off-by: Paul Moore --- include/linux/security.h | 5 +---- security/selinux/hooks.c | 13 ++----------- 2 files changed, 3 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/linux/security.h b/include/linux/security.h index 794be735ff4b..6478ce3252c7 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -987,10 +987,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * Retrieve the LSM-specific secid for the sock to enable caching of network * authorizations. * @sock_graft: - * This hook is called in response to a newly created sock struct being - * grafted onto an existing socket and allows the security module to - * perform whatever security attribute management is necessary for both - * the sock and socket. + * Sets the socket's isec sid to the sock's sid. * @inet_conn_request: * Sets the openreq's sid to socket's sid with MLS portion taken from peer sid. * @inet_csk_clone: diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index b3a6754e932b..336f0a04450e 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4499,18 +4499,9 @@ static void selinux_sock_graft(struct sock *sk, struct socket *parent) struct inode_security_struct *isec = SOCK_INODE(parent)->i_security; struct sk_security_struct *sksec = sk->sk_security; - switch (sk->sk_family) { - case PF_INET: - case PF_INET6: - case PF_UNIX: + if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 || + sk->sk_family == PF_UNIX) isec->sid = sksec->sid; - break; - default: - /* by default there is no special labeling mechanism for the - * sksec label so inherit the label from the parent socket */ - BUG_ON(sksec->sid != SECINITSID_UNLABELED); - sksec->sid = isec->sid; - } sksec->sclass = isec->sclass; } -- cgit v1.2.3 From 36874c7e219fa080141d49fd7bb9bbbdad0507c5 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 28 Jul 2014 10:01:07 -0700 Subject: Input: pixcir_i2c_ts - support up to 5 fingers and hardware tracking IDs Some variants of the Pixcir touch controller support up to 5 simultaneous fingers and hardware tracking IDs. Prepare the driver for that. Signed-off-by: Roger Quadros Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/pixcir_i2c_ts.c | 74 ++++++++++++++++++++++++------- include/linux/input/pixcir_ts.h | 12 +++++ 2 files changed, 69 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c index 0b016814e6b6..eddda520a1a7 100644 --- a/drivers/input/touchscreen/pixcir_i2c_ts.c +++ b/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -27,18 +27,20 @@ #include #include -#define PIXCIR_MAX_SLOTS 2 +#define PIXCIR_MAX_SLOTS 5 /* Max fingers supported by driver */ struct pixcir_i2c_ts_data { struct i2c_client *client; struct input_dev *input; - const struct pixcir_ts_platform_data *chip; + const struct pixcir_ts_platform_data *pdata; bool running; + int max_fingers; /* Max fingers supported in this instance */ }; struct pixcir_touch { int x; int y; + int id; }; struct pixcir_report_data { @@ -49,13 +51,21 @@ struct pixcir_report_data { static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata, struct pixcir_report_data *report) { - u8 rdbuf[10], wrbuf[1] = { 0 }; + u8 rdbuf[2 + PIXCIR_MAX_SLOTS * 5]; + u8 wrbuf[1] = { 0 }; u8 *bufptr; u8 touch; int ret, i; + int readsize; + const struct pixcir_i2c_chip_data *chip = &tsdata->pdata->chip; memset(report, 0, sizeof(struct pixcir_report_data)); + i = chip->has_hw_ids ? 1 : 0; + readsize = 2 + tsdata->max_fingers * (4 + i); + if (readsize > sizeof(rdbuf)) + readsize = sizeof(rdbuf); + ret = i2c_master_send(tsdata->client, wrbuf, sizeof(wrbuf)); if (ret != sizeof(wrbuf)) { dev_err(&tsdata->client->dev, @@ -64,7 +74,7 @@ static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata, return; } - ret = i2c_master_recv(tsdata->client, rdbuf, sizeof(rdbuf)); + ret = i2c_master_recv(tsdata->client, rdbuf, readsize); if (ret != sizeof(rdbuf)) { dev_err(&tsdata->client->dev, "%s: i2c_master_recv failed(), ret=%d\n", @@ -73,8 +83,8 @@ static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata, } touch = rdbuf[0] & 0x7; - if (touch > PIXCIR_MAX_SLOTS) - touch = PIXCIR_MAX_SLOTS; + if (touch > tsdata->max_fingers) + touch = tsdata->max_fingers; report->num_touches = touch; bufptr = &rdbuf[2]; @@ -83,7 +93,12 @@ static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata, report->touches[i].x = (bufptr[1] << 8) | bufptr[0]; report->touches[i].y = (bufptr[3] << 8) | bufptr[2]; - bufptr = bufptr + 4; + if (chip->has_hw_ids) { + report->touches[i].id = bufptr[4]; + bufptr = bufptr + 5; + } else { + bufptr = bufptr + 4; + } } } @@ -95,22 +110,35 @@ static void pixcir_ts_report(struct pixcir_i2c_ts_data *ts, struct pixcir_touch *touch; int n, i, slot; struct device *dev = &ts->client->dev; + const struct pixcir_i2c_chip_data *chip = &ts->pdata->chip; n = report->num_touches; if (n > PIXCIR_MAX_SLOTS) n = PIXCIR_MAX_SLOTS; - for (i = 0; i < n; i++) { - touch = &report->touches[i]; - pos[i].x = touch->x; - pos[i].y = touch->y; - } + if (!chip->has_hw_ids) { + for (i = 0; i < n; i++) { + touch = &report->touches[i]; + pos[i].x = touch->x; + pos[i].y = touch->y; + } - input_mt_assign_slots(ts->input, slots, pos, n); + input_mt_assign_slots(ts->input, slots, pos, n); + } for (i = 0; i < n; i++) { touch = &report->touches[i]; - slot = slots[i]; + + if (chip->has_hw_ids) { + slot = input_mt_get_slot_by_key(ts->input, touch->id); + if (slot < 0) { + dev_dbg(dev, "no free slot for id 0x%x\n", + touch->id); + continue; + } + } else { + slot = slots[i]; + } input_mt_slot(ts->input, slot); input_mt_report_slot_state(ts->input, @@ -130,7 +158,7 @@ static void pixcir_ts_report(struct pixcir_i2c_ts_data *ts, static irqreturn_t pixcir_ts_isr(int irq, void *dev_id) { struct pixcir_i2c_ts_data *tsdata = dev_id; - const struct pixcir_ts_platform_data *pdata = tsdata->chip; + const struct pixcir_ts_platform_data *pdata = tsdata->pdata; struct pixcir_report_data report; while (tsdata->running) { @@ -399,6 +427,11 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, return -EINVAL; } + if (!pdata->chip.max_fingers) { + dev_err(dev, "Invalid max_fingers in pdata\n"); + return -EINVAL; + } + tsdata = devm_kzalloc(dev, sizeof(*tsdata), GFP_KERNEL); if (!tsdata) return -ENOMEM; @@ -411,7 +444,7 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, tsdata->client = client; tsdata->input = input; - tsdata->chip = pdata; + tsdata->pdata = pdata; input->name = client->name; input->id.bustype = BUS_I2C; @@ -427,7 +460,14 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, input_set_abs_params(input, ABS_MT_POSITION_X, 0, pdata->x_max, 0, 0); input_set_abs_params(input, ABS_MT_POSITION_Y, 0, pdata->y_max, 0, 0); - error = input_mt_init_slots(input, PIXCIR_MAX_SLOTS, + tsdata->max_fingers = tsdata->pdata->chip.max_fingers; + if (tsdata->max_fingers > PIXCIR_MAX_SLOTS) { + tsdata->max_fingers = PIXCIR_MAX_SLOTS; + dev_info(dev, "Limiting maximum fingers to %d\n", + tsdata->max_fingers); + } + + error = input_mt_init_slots(input, tsdata->max_fingers, INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); if (error) { dev_err(dev, "Error initializing Multi-Touch slots\n"); diff --git a/include/linux/input/pixcir_ts.h b/include/linux/input/pixcir_ts.h index 160cf353aa39..7bae83b7c396 100644 --- a/include/linux/input/pixcir_ts.h +++ b/include/linux/input/pixcir_ts.h @@ -43,10 +43,22 @@ enum pixcir_int_mode { #define PIXCIR_INT_ENABLE (1UL << 3) #define PIXCIR_INT_POL_HIGH (1UL << 2) +/** + * struct pixcir_irc_chip_data - chip related data + * @max_fingers: Max number of fingers reported simultaneously by h/w + * @has_hw_ids: Hardware supports finger tracking IDs + * + */ +struct pixcir_i2c_chip_data { + u8 max_fingers; + bool has_hw_ids; +}; + struct pixcir_ts_platform_data { int x_max; int y_max; int gpio_attb; /* GPIO connected to ATTB line */ + struct pixcir_i2c_chip_data chip; }; #endif -- cgit v1.2.3 From ce91ddc471b77ec75e5b2a43c803efac605f37b3 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Mon, 28 Jul 2014 19:29:13 +0200 Subject: KVM: PPC: Remove DCR handling DCR handling was only needed for 440 KVM. Since we removed it, we can also remove handling of DCR accesses. Signed-off-by: Alexander Graf --- Documentation/virtual/kvm/api.txt | 6 +++--- arch/powerpc/include/asm/kvm_host.h | 4 ---- arch/powerpc/include/asm/kvm_ppc.h | 1 - arch/powerpc/kvm/booke.c | 5 ----- arch/powerpc/kvm/powerpc.c | 10 ---------- arch/powerpc/kvm/timing.c | 1 - arch/powerpc/kvm/timing.h | 3 --- include/uapi/linux/kvm.h | 4 ++-- 8 files changed, 5 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 8898caf8c090..a21ff2265c21 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -2613,8 +2613,8 @@ The 'data' member contains, in its first 'len' bytes, the value as it would appear if the VCPU performed a load or store of the appropriate width directly to the byte array. -NOTE: For KVM_EXIT_IO, KVM_EXIT_MMIO, KVM_EXIT_OSI, KVM_EXIT_DCR, - KVM_EXIT_PAPR and KVM_EXIT_EPR the corresponding +NOTE: For KVM_EXIT_IO, KVM_EXIT_MMIO, KVM_EXIT_OSI KVM_EXIT_PAPR and + KVM_EXIT_EPR the corresponding operations are complete (and guest state is consistent) only after userspace has re-entered the kernel with KVM_RUN. The kernel side will first finish incomplete operations and then check for pending signals. Userspace @@ -2685,7 +2685,7 @@ Principles of Operation Book in the Chapter for Dynamic Address Translation __u8 is_write; } dcr; -powerpc specific. +Deprecated - was used for 440 KVM. /* KVM_EXIT_OSI */ struct { diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index 66f5b5914e6c..98d9dd50d063 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h @@ -94,7 +94,6 @@ struct kvm_vm_stat { struct kvm_vcpu_stat { u32 sum_exits; u32 mmio_exits; - u32 dcr_exits; u32 signal_exits; u32 light_exits; /* Account for special types of light exits: */ @@ -126,7 +125,6 @@ struct kvm_vcpu_stat { enum kvm_exit_types { MMIO_EXITS, - DCR_EXITS, SIGNAL_EXITS, ITLB_REAL_MISS_EXITS, ITLB_VIRT_MISS_EXITS, @@ -601,8 +599,6 @@ struct kvm_vcpu_arch { u8 io_gpr; /* GPR used as IO source/target */ u8 mmio_is_bigendian; u8 mmio_sign_extend; - u8 dcr_needed; - u8 dcr_is_write; u8 osi_needed; u8 osi_enabled; u8 papr_enabled; diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h index cbee4538307f..8e36c1e2c631 100644 --- a/arch/powerpc/include/asm/kvm_ppc.h +++ b/arch/powerpc/include/asm/kvm_ppc.h @@ -41,7 +41,6 @@ enum emulation_result { EMULATE_DONE, /* no further processing */ EMULATE_DO_MMIO, /* kvm_run filled with MMIO request */ - EMULATE_DO_DCR, /* kvm_run filled with DCR request */ EMULATE_FAIL, /* can't emulate this instruction */ EMULATE_AGAIN, /* something went wrong. go again */ EMULATE_EXIT_USER, /* emulation requires exit to user-space */ diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index f30948a17c03..b4c89fa6f109 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -51,7 +51,6 @@ unsigned long kvmppc_booke_handlers; struct kvm_stats_debugfs_item debugfs_entries[] = { { "mmio", VCPU_STAT(mmio_exits) }, - { "dcr", VCPU_STAT(dcr_exits) }, { "sig", VCPU_STAT(signal_exits) }, { "itlb_r", VCPU_STAT(itlb_real_miss_exits) }, { "itlb_v", VCPU_STAT(itlb_virt_miss_exits) }, @@ -709,10 +708,6 @@ static int emulation_exit(struct kvm_run *run, struct kvm_vcpu *vcpu) case EMULATE_AGAIN: return RESUME_GUEST; - case EMULATE_DO_DCR: - run->exit_reason = KVM_EXIT_DCR; - return RESUME_HOST; - case EMULATE_FAIL: printk(KERN_CRIT "%s: emulation at %lx failed (%08x)\n", __func__, vcpu->arch.pc, vcpu->arch.last_inst); diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index c14ed15fd60b..288b4bb05cbd 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -743,12 +743,6 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) #endif } -static void kvmppc_complete_dcr_load(struct kvm_vcpu *vcpu, - struct kvm_run *run) -{ - kvmppc_set_gpr(vcpu, vcpu->arch.io_gpr, run->dcr.data); -} - static void kvmppc_complete_mmio_load(struct kvm_vcpu *vcpu, struct kvm_run *run) { @@ -945,10 +939,6 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) if (!vcpu->mmio_is_write) kvmppc_complete_mmio_load(vcpu, run); vcpu->mmio_needed = 0; - } else if (vcpu->arch.dcr_needed) { - if (!vcpu->arch.dcr_is_write) - kvmppc_complete_dcr_load(vcpu, run); - vcpu->arch.dcr_needed = 0; } else if (vcpu->arch.osi_needed) { u64 *gprs = run->osi.gprs; int i; diff --git a/arch/powerpc/kvm/timing.c b/arch/powerpc/kvm/timing.c index 07b6110a4bb7..e44d2b2ea97e 100644 --- a/arch/powerpc/kvm/timing.c +++ b/arch/powerpc/kvm/timing.c @@ -110,7 +110,6 @@ void kvmppc_update_timing_stats(struct kvm_vcpu *vcpu) static const char *kvm_exit_names[__NUMBER_OF_KVM_EXIT_TYPES] = { [MMIO_EXITS] = "MMIO", - [DCR_EXITS] = "DCR", [SIGNAL_EXITS] = "SIGNAL", [ITLB_REAL_MISS_EXITS] = "ITLBREAL", [ITLB_VIRT_MISS_EXITS] = "ITLBVIRT", diff --git a/arch/powerpc/kvm/timing.h b/arch/powerpc/kvm/timing.h index bf191e72b2d8..3123690c82dc 100644 --- a/arch/powerpc/kvm/timing.h +++ b/arch/powerpc/kvm/timing.h @@ -63,9 +63,6 @@ static inline void kvmppc_account_exit_stat(struct kvm_vcpu *vcpu, int type) case EMULATED_INST_EXITS: vcpu->stat.emulated_inst_exits++; break; - case DCR_EXITS: - vcpu->stat.dcr_exits++; - break; case DSI_EXITS: vcpu->stat.dsi_exits++; break; diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 51776cac6a9b..f6f24aeb9e1a 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -162,7 +162,7 @@ struct kvm_pit_config { #define KVM_EXIT_TPR_ACCESS 12 #define KVM_EXIT_S390_SIEIC 13 #define KVM_EXIT_S390_RESET 14 -#define KVM_EXIT_DCR 15 +#define KVM_EXIT_DCR 15 /* deprecated */ #define KVM_EXIT_NMI 16 #define KVM_EXIT_INTERNAL_ERROR 17 #define KVM_EXIT_OSI 18 @@ -268,7 +268,7 @@ struct kvm_run { __u64 trans_exc_code; __u32 pgm_code; } s390_ucontrol; - /* KVM_EXIT_DCR */ + /* KVM_EXIT_DCR (deprecated) */ struct { __u32 dcrn; __u32 data; -- cgit v1.2.3 From 95847e1bd34c0de86039408b24a05f07e788061d Mon Sep 17 00:00:00 2001 From: Lai Jiangshan Date: Sat, 26 Jul 2014 12:04:00 +0800 Subject: kthread_work: remove the unused wait_queue_head The wait_queue_head_t kthread_work->done is unused since flush_kthread_work() has been re-implemented. Let's remove it including the initialization code. This makes DEFINE_KTHREAD_WORK_ONSTACK() unnecessary, removed. tj: Updated description. Removed DEFINE_KTHREAD_WORK_ONSTACK(). Signed-off-by: Lai Jiangshan Signed-off-by: Tejun Heo --- include/linux/kthread.h | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/linux/kthread.h b/include/linux/kthread.h index 7dcef3317689..13d55206ccf6 100644 --- a/include/linux/kthread.h +++ b/include/linux/kthread.h @@ -73,7 +73,6 @@ struct kthread_worker { struct kthread_work { struct list_head node; kthread_work_func_t func; - wait_queue_head_t done; struct kthread_worker *worker; }; @@ -85,7 +84,6 @@ struct kthread_work { #define KTHREAD_WORK_INIT(work, fn) { \ .node = LIST_HEAD_INIT((work).node), \ .func = (fn), \ - .done = __WAIT_QUEUE_HEAD_INITIALIZER((work).done), \ } #define DEFINE_KTHREAD_WORKER(worker) \ @@ -95,22 +93,16 @@ struct kthread_work { struct kthread_work work = KTHREAD_WORK_INIT(work, fn) /* - * kthread_worker.lock and kthread_work.done need their own lockdep class - * keys if they are defined on stack with lockdep enabled. Use the - * following macros when defining them on stack. + * kthread_worker.lock needs its own lockdep class key when defined on + * stack with lockdep enabled. Use the following macros in such cases. */ #ifdef CONFIG_LOCKDEP # define KTHREAD_WORKER_INIT_ONSTACK(worker) \ ({ init_kthread_worker(&worker); worker; }) # define DEFINE_KTHREAD_WORKER_ONSTACK(worker) \ struct kthread_worker worker = KTHREAD_WORKER_INIT_ONSTACK(worker) -# define KTHREAD_WORK_INIT_ONSTACK(work, fn) \ - ({ init_kthread_work((&work), fn); work; }) -# define DEFINE_KTHREAD_WORK_ONSTACK(work, fn) \ - struct kthread_work work = KTHREAD_WORK_INIT_ONSTACK(work, fn) #else # define DEFINE_KTHREAD_WORKER_ONSTACK(worker) DEFINE_KTHREAD_WORKER(worker) -# define DEFINE_KTHREAD_WORK_ONSTACK(work, fn) DEFINE_KTHREAD_WORK(work, fn) #endif extern void __init_kthread_worker(struct kthread_worker *worker, @@ -127,7 +119,6 @@ extern void __init_kthread_worker(struct kthread_worker *worker, memset((work), 0, sizeof(struct kthread_work)); \ INIT_LIST_HEAD(&(work)->node); \ (work)->func = (fn); \ - init_waitqueue_head(&(work)->done); \ } while (0) int kthread_worker_fn(void *worker_ptr); -- cgit v1.2.3 From d87de1f3e9635b16345bde54306c949417b689ad Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Fri, 25 Jul 2014 02:18:17 -0700 Subject: netlink: Fix shadow warning on jiffies Change formal parameter name to not shadow the global jiffies. Signed-off-by: Mark Rustad Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- include/net/netlink.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/netlink.h b/include/net/netlink.h index 2b47eaadba8f..6c1076275aaa 100644 --- a/include/net/netlink.h +++ b/include/net/netlink.h @@ -949,12 +949,12 @@ static inline int nla_put_flag(struct sk_buff *skb, int attrtype) * nla_put_msecs - Add a msecs netlink attribute to a socket buffer * @skb: socket buffer to add attribute to * @attrtype: attribute type - * @jiffies: number of msecs in jiffies + * @njiffies: number of jiffies to convert to msecs */ static inline int nla_put_msecs(struct sk_buff *skb, int attrtype, - unsigned long jiffies) + unsigned long njiffies) { - u64 tmp = jiffies_to_msecs(jiffies); + u64 tmp = jiffies_to_msecs(njiffies); return nla_put(skb, attrtype, sizeof(u64), &tmp); } -- cgit v1.2.3 From 04ca6973f7c1a0d8537f2d9906a0cf8e69886d75 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 26 Jul 2014 08:58:10 +0200 Subject: ip: make IP identifiers less predictable In "Counting Packets Sent Between Arbitrary Internet Hosts", Jeffrey and Jedidiah describe ways exploiting linux IP identifier generation to infer whether two machines are exchanging packets. With commit 73f156a6e8c1 ("inetpeer: get rid of ip_id_count"), we changed IP id generation, but this does not really prevent this side-channel technique. This patch adds a random amount of perturbation so that IP identifiers for a given destination [1] are no longer monotonically increasing after an idle period. Note that prandom_u32_max(1) returns 0, so if generator is used at most once per jiffy, this patch inserts no hole in the ID suite and do not increase collision probability. This is jiffies based, so in the worst case (HZ=1000), the id can rollover after ~65 seconds of idle time, which should be fine. We also change the hash used in __ip_select_ident() to not only hash on daddr, but also saddr and protocol, so that ICMP probes can not be used to infer information for other protocols. For IPv6, adds saddr into the hash as well, but not nexthdr. If I ping the patched target, we can see ID are now hard to predict. 21:57:11.008086 IP (...) A > target: ICMP echo request, seq 1, length 64 21:57:11.010752 IP (... id 2081 ...) target > A: ICMP echo reply, seq 1, length 64 21:57:12.013133 IP (...) A > target: ICMP echo request, seq 2, length 64 21:57:12.015737 IP (... id 3039 ...) target > A: ICMP echo reply, seq 2, length 64 21:57:13.016580 IP (...) A > target: ICMP echo request, seq 3, length 64 21:57:13.019251 IP (... id 3437 ...) target > A: ICMP echo reply, seq 3, length 64 [1] TCP sessions uses a per flow ID generator not changed by this patch. Signed-off-by: Eric Dumazet Reported-by: Jeffrey Knockel Reported-by: Jedidiah R. Crandall Cc: Willy Tarreau Cc: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/net/ip.h | 11 +---------- net/ipv4/route.c | 32 +++++++++++++++++++++++++++++--- net/ipv6/ip6_output.c | 2 ++ 3 files changed, 32 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/ip.h b/include/net/ip.h index 0e795df05ec9..7596eb22e1ce 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -309,16 +309,7 @@ static inline unsigned int ip_skb_dst_mtu(const struct sk_buff *skb) } } -#define IP_IDENTS_SZ 2048u -extern atomic_t *ip_idents; - -static inline u32 ip_idents_reserve(u32 hash, int segs) -{ - atomic_t *id_ptr = ip_idents + hash % IP_IDENTS_SZ; - - return atomic_add_return(segs, id_ptr) - segs; -} - +u32 ip_idents_reserve(u32 hash, int segs); void __ip_select_ident(struct iphdr *iph, int segs); static inline void ip_select_ident_segs(struct sk_buff *skb, struct sock *sk, int segs) diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 3162ea923ded..190199851c9a 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -457,8 +457,31 @@ static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst, return neigh_create(&arp_tbl, pkey, dev); } -atomic_t *ip_idents __read_mostly; -EXPORT_SYMBOL(ip_idents); +#define IP_IDENTS_SZ 2048u +struct ip_ident_bucket { + atomic_t id; + u32 stamp32; +}; + +static struct ip_ident_bucket *ip_idents __read_mostly; + +/* In order to protect privacy, we add a perturbation to identifiers + * if one generator is seldom used. This makes hard for an attacker + * to infer how many packets were sent between two points in time. + */ +u32 ip_idents_reserve(u32 hash, int segs) +{ + struct ip_ident_bucket *bucket = ip_idents + hash % IP_IDENTS_SZ; + u32 old = ACCESS_ONCE(bucket->stamp32); + u32 now = (u32)jiffies; + u32 delta = 0; + + if (old != now && cmpxchg(&bucket->stamp32, old, now) == old) + delta = prandom_u32_max(now - old); + + return atomic_add_return(segs + delta, &bucket->id) - segs; +} +EXPORT_SYMBOL(ip_idents_reserve); void __ip_select_ident(struct iphdr *iph, int segs) { @@ -467,7 +490,10 @@ void __ip_select_ident(struct iphdr *iph, int segs) net_get_random_once(&ip_idents_hashrnd, sizeof(ip_idents_hashrnd)); - hash = jhash_1word((__force u32)iph->daddr, ip_idents_hashrnd); + hash = jhash_3words((__force u32)iph->daddr, + (__force u32)iph->saddr, + iph->protocol, + ip_idents_hashrnd); id = ip_idents_reserve(hash, segs); iph->id = htons(id); } diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index cb9df0eb4023..45702b8cd141 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -545,6 +545,8 @@ static void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt) net_get_random_once(&ip6_idents_hashrnd, sizeof(ip6_idents_hashrnd)); hash = __ipv6_addr_jhash(&rt->rt6i_dst.addr, ip6_idents_hashrnd); + hash = __ipv6_addr_jhash(&rt->rt6i_src.addr, hash); + id = ip_idents_reserve(hash, 1); fhdr->identification = htonl(id); } -- cgit v1.2.3 From 631c53478973d2de299d02de83eb9fa8a0cbc3b1 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sun, 13 Jul 2014 08:37:52 +0400 Subject: clk: Add CLPS711X clk driver This adds the clock driver for Cirrus Logic CLPS711X series SoCs using common clock infrastructure. Designed primarily for migration CLPS711X subarch for multiplatform & DT, for this as the "OF" and "non-OF" calls implemented. Signed-off-by: Alexander Shiyan Acked-by: Arnd Bergmann Signed-off-by: Mike Turquette --- drivers/clk/Makefile | 1 + drivers/clk/clk-clps711x.c | 192 +++++++++++++++++++++++++++++ include/dt-bindings/clock/clps711x-clock.h | 27 ++++ 3 files changed, 220 insertions(+) create mode 100644 drivers/clk/clk-clps711x.c create mode 100644 include/dt-bindings/clock/clps711x-clock.h (limited to 'include') diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index d5d325f28016..f537a0b1f798 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -18,6 +18,7 @@ endif obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o obj-$(CONFIG_ARCH_AXXIA) += clk-axm5516.o obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o +obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o diff --git a/drivers/clk/clk-clps711x.c b/drivers/clk/clk-clps711x.c new file mode 100644 index 000000000000..715eec1a9902 --- /dev/null +++ b/drivers/clk/clk-clps711x.c @@ -0,0 +1,192 @@ +/* + * Cirrus Logic CLPS711X CLK driver + * + * Copyright (C) 2014 Alexander Shiyan + * + * 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 + +#define CLPS711X_SYSCON1 (0x0100) +#define CLPS711X_SYSCON2 (0x1100) +#define CLPS711X_SYSFLG2 (CLPS711X_SYSCON2 + SYSFLG_OFFSET) +#define CLPS711X_PLLR (0xa5a8) + +#define CLPS711X_EXT_FREQ (13000000) +#define CLPS711X_OSC_FREQ (3686400) + +static const struct clk_div_table spi_div_table[] = { + { .val = 0, .div = 32, }, + { .val = 1, .div = 8, }, + { .val = 2, .div = 2, }, + { .val = 3, .div = 1, }, +}; + +static const struct clk_div_table timer_div_table[] = { + { .val = 0, .div = 256, }, + { .val = 1, .div = 1, }, +}; + +struct clps711x_clk { + struct clk_onecell_data clk_data; + spinlock_t lock; + struct clk *clks[CLPS711X_CLK_MAX]; +}; + +static struct clps711x_clk * __init _clps711x_clk_init(void __iomem *base, + u32 fref) +{ + u32 tmp, f_cpu, f_pll, f_bus, f_tim, f_pwm, f_spi; + struct clps711x_clk *clps711x_clk; + unsigned i; + + if (!base) + return ERR_PTR(-ENOMEM); + + clps711x_clk = kzalloc(sizeof(*clps711x_clk), GFP_KERNEL); + if (!clps711x_clk) + return ERR_PTR(-ENOMEM); + + spin_lock_init(&clps711x_clk->lock); + + /* Read PLL multiplier value and sanity check */ + tmp = readl(base + CLPS711X_PLLR) >> 24; + if (((tmp >= 10) && (tmp <= 50)) || !fref) + f_pll = DIV_ROUND_UP(CLPS711X_OSC_FREQ * tmp, 2); + else + f_pll = fref; + + tmp = readl(base + CLPS711X_SYSFLG2); + if (tmp & SYSFLG2_CKMODE) { + f_cpu = CLPS711X_EXT_FREQ; + f_bus = CLPS711X_EXT_FREQ; + f_spi = DIV_ROUND_CLOSEST(CLPS711X_EXT_FREQ, 96); + f_pll = 0; + f_pwm = DIV_ROUND_CLOSEST(CLPS711X_EXT_FREQ, 128); + } else { + f_cpu = f_pll; + if (f_cpu > 36864000) + f_bus = DIV_ROUND_UP(f_cpu, 2); + else + f_bus = 36864000 / 2; + f_spi = DIV_ROUND_CLOSEST(f_cpu, 576); + f_pwm = DIV_ROUND_CLOSEST(f_cpu, 768); + } + + if (tmp & SYSFLG2_CKMODE) { + if (readl(base + CLPS711X_SYSCON2) & SYSCON2_OSTB) + f_tim = DIV_ROUND_CLOSEST(CLPS711X_EXT_FREQ, 26); + else + f_tim = DIV_ROUND_CLOSEST(CLPS711X_EXT_FREQ, 24); + } else + f_tim = DIV_ROUND_CLOSEST(f_cpu, 144); + + tmp = readl(base + CLPS711X_SYSCON1); + /* Timer1 in free running mode. + * Counter will wrap around to 0xffff when it underflows + * and will continue to count down. + */ + tmp &= ~(SYSCON1_TC1M | SYSCON1_TC1S); + /* Timer2 in prescale mode. + * Value writen is automatically re-loaded when + * the counter underflows. + */ + tmp |= SYSCON1_TC2M | SYSCON1_TC2S; + writel(tmp, base + CLPS711X_SYSCON1); + + clps711x_clk->clks[CLPS711X_CLK_DUMMY] = + clk_register_fixed_rate(NULL, "dummy", NULL, CLK_IS_ROOT, 0); + clps711x_clk->clks[CLPS711X_CLK_CPU] = + clk_register_fixed_rate(NULL, "cpu", NULL, CLK_IS_ROOT, f_cpu); + clps711x_clk->clks[CLPS711X_CLK_BUS] = + clk_register_fixed_rate(NULL, "bus", NULL, CLK_IS_ROOT, f_bus); + clps711x_clk->clks[CLPS711X_CLK_PLL] = + clk_register_fixed_rate(NULL, "pll", NULL, CLK_IS_ROOT, f_pll); + clps711x_clk->clks[CLPS711X_CLK_TIMERREF] = + clk_register_fixed_rate(NULL, "timer_ref", NULL, CLK_IS_ROOT, + f_tim); + clps711x_clk->clks[CLPS711X_CLK_TIMER1] = + clk_register_divider_table(NULL, "timer1", "timer_ref", 0, + base + CLPS711X_SYSCON1, 5, 1, 0, + timer_div_table, &clps711x_clk->lock); + clps711x_clk->clks[CLPS711X_CLK_TIMER2] = + clk_register_divider_table(NULL, "timer2", "timer_ref", 0, + base + CLPS711X_SYSCON1, 7, 1, 0, + timer_div_table, &clps711x_clk->lock); + clps711x_clk->clks[CLPS711X_CLK_PWM] = + clk_register_fixed_rate(NULL, "pwm", NULL, CLK_IS_ROOT, f_pwm); + clps711x_clk->clks[CLPS711X_CLK_SPIREF] = + clk_register_fixed_rate(NULL, "spi_ref", NULL, CLK_IS_ROOT, + f_spi); + clps711x_clk->clks[CLPS711X_CLK_SPI] = + clk_register_divider_table(NULL, "spi", "spi_ref", 0, + base + CLPS711X_SYSCON1, 16, 2, 0, + spi_div_table, &clps711x_clk->lock); + clps711x_clk->clks[CLPS711X_CLK_UART] = + clk_register_fixed_factor(NULL, "uart", "bus", 0, 1, 10); + clps711x_clk->clks[CLPS711X_CLK_TICK] = + clk_register_fixed_rate(NULL, "tick", NULL, CLK_IS_ROOT, 64); + + for (i = 0; i < CLPS711X_CLK_MAX; i++) + if (IS_ERR(clps711x_clk->clks[i])) + pr_err("clk %i: register failed with %ld\n", + i, PTR_ERR(clps711x_clk->clks[i])); + + return clps711x_clk; +} + +void __init clps711x_clk_init(void __iomem *base) +{ + struct clps711x_clk *clps711x_clk; + + clps711x_clk = _clps711x_clk_init(base, 73728000); + + BUG_ON(IS_ERR(clps711x_clk)); + + /* Clocksource */ + clk_register_clkdev(clps711x_clk->clks[CLPS711X_CLK_TIMER1], + NULL, "clps711x-timer.0"); + clk_register_clkdev(clps711x_clk->clks[CLPS711X_CLK_TIMER2], + NULL, "clps711x-timer.1"); + + /* Drivers */ + clk_register_clkdev(clps711x_clk->clks[CLPS711X_CLK_PWM], + NULL, "clps711x-pwm"); + clk_register_clkdev(clps711x_clk->clks[CLPS711X_CLK_UART], + NULL, "clps711x-uart.0"); + clk_register_clkdev(clps711x_clk->clks[CLPS711X_CLK_UART], + NULL, "clps711x-uart.1"); +} + +#ifdef CONFIG_OF +static void __init clps711x_clk_init_dt(struct device_node *np) +{ + void __iomem *base = of_iomap(np, 0); + struct clps711x_clk *clps711x_clk; + u32 fref = 0; + + WARN_ON(of_property_read_u32(np, "startup-frequency", &fref)); + + clps711x_clk = _clps711x_clk_init(base, fref); + BUG_ON(IS_ERR(clps711x_clk)); + + clps711x_clk->clk_data.clks = clps711x_clk->clks; + clps711x_clk->clk_data.clk_num = CLPS711X_CLK_MAX; + of_clk_add_provider(np, of_clk_src_onecell_get, + &clps711x_clk->clk_data); +} +CLK_OF_DECLARE(clps711x, "cirrus,clps711x-clk", clps711x_clk_init_dt); +#endif diff --git a/include/dt-bindings/clock/clps711x-clock.h b/include/dt-bindings/clock/clps711x-clock.h new file mode 100644 index 000000000000..0c4c80b63242 --- /dev/null +++ b/include/dt-bindings/clock/clps711x-clock.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2014 Alexander Shiyan + * + * 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 __DT_BINDINGS_CLOCK_CLPS711X_H +#define __DT_BINDINGS_CLOCK_CLPS711X_H + +#define CLPS711X_CLK_DUMMY 0 +#define CLPS711X_CLK_CPU 1 +#define CLPS711X_CLK_BUS 2 +#define CLPS711X_CLK_PLL 3 +#define CLPS711X_CLK_TIMERREF 4 +#define CLPS711X_CLK_TIMER1 5 +#define CLPS711X_CLK_TIMER2 6 +#define CLPS711X_CLK_PWM 7 +#define CLPS711X_CLK_SPIREF 8 +#define CLPS711X_CLK_SPI 9 +#define CLPS711X_CLK_UART 10 +#define CLPS711X_CLK_TICK 11 +#define CLPS711X_CLK_MAX 12 + +#endif -- cgit v1.2.3 From f3d4ff0e04cc4450bdc7a4140020913b1280d205 Mon Sep 17 00:00:00 2001 From: Jamie Lentin Date: Wed, 23 Jul 2014 23:30:48 +0100 Subject: HID: lenovo: Add support for Compact (BT|USB) keyboard Add support for both ThinkPad Compact Bluetooth Keyboard with TrackPoint and ThinkPad Compact USB Keyboard with TrackPoint. Signed-off-by: Jamie Lentin Reviewed-by: Antonio Ospite Signed-off-by: Jiri Kosina --- Documentation/ABI/testing/sysfs-driver-hid-lenovo | 12 ++ drivers/hid/Kconfig | 2 + drivers/hid/hid-core.c | 2 + drivers/hid/hid-ids.h | 2 + drivers/hid/hid-lenovo.c | 208 ++++++++++++++++++++++ include/linux/hid.h | 1 + 6 files changed, 227 insertions(+) (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-driver-hid-lenovo b/Documentation/ABI/testing/sysfs-driver-hid-lenovo index 57b92cbdceae..53a0725962e1 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-lenovo +++ b/Documentation/ABI/testing/sysfs-driver-hid-lenovo @@ -4,18 +4,21 @@ Contact: linux-input@vger.kernel.org Description: This controls if mouse clicks should be generated if the trackpoint is quickly pressed. How fast this press has to be is being controlled by press_speed. Values are 0 or 1. + Applies to Thinkpad USB Keyboard with TrackPoint. What: /sys/bus/usb/devices/-:./::./dragging Date: July 2011 Contact: linux-input@vger.kernel.org Description: If this setting is enabled, it is possible to do dragging by pressing the trackpoint. This requires press_to_select to be enabled. Values are 0 or 1. + Applies to Thinkpad USB Keyboard with TrackPoint. What: /sys/bus/usb/devices/-:./::./release_to_select Date: July 2011 Contact: linux-input@vger.kernel.org Description: For details regarding this setting please refer to http://www.pc.ibm.com/ww/healthycomputing/trkpntb.html Values are 0 or 1. + Applies to Thinkpad USB Keyboard with TrackPoint. What: /sys/bus/usb/devices/-:./::./select_right Date: July 2011 @@ -23,16 +26,25 @@ Contact: linux-input@vger.kernel.org Description: This setting controls if the mouse click events generated by pressing the trackpoint (if press_to_select is enabled) generate a left or right mouse button click. Values are 0 or 1. + Applies to Thinkpad USB Keyboard with TrackPoint. What: /sys/bus/usb/devices/-:./::./sensitivity Date: July 2011 Contact: linux-input@vger.kernel.org Description: This file contains the trackpoint sensitivity. Values are decimal integers from 1 (lowest sensitivity) to 255 (highest sensitivity). + Applies to Thinkpad USB Keyboard with TrackPoint. What: /sys/bus/usb/devices/-:./::./press_speed Date: July 2011 Contact: linux-input@vger.kernel.org Description: This setting controls how fast the trackpoint needs to be pressed to generate a mouse click if press_to_select is enabled. Values are decimal integers from 1 (slowest) to 255 (fastest). + Applies to Thinkpad USB Keyboard with TrackPoint. +What: /sys/bus/usb/devices/-:./::./fn_lock +Date: July 2014 +Contact: linux-input@vger.kernel.org +Description: This setting controls whether Fn Lock is enabled on the keyboard (i.e. if F1 is Mute or F1) + Values are 0 or 1 + Applies to ThinkPad Compact (USB|Bluetooth) Keyboard with TrackPoint. diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 687d7a1b6bed..21cce19d301e 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -343,6 +343,8 @@ config HID_LENOVO Thinkpad standalone keyboards, e.g: - ThinkPad USB Keyboard with TrackPoint (supports extra LEDs and trackpoint configuration) + - ThinkPad Compact Bluetooth Keyboard with TrackPoint (supports Fn keys) + - ThinkPad Compact USB Keyboard with TrackPoint (supports Fn keys) config HID_LOGITECH tristate "Logitech devices" if EXPERT diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 55841bd5b461..81b3bb6c4fd1 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1798,6 +1798,8 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000 ) }, #if IS_ENABLED(CONFIG_HID_LENOVO) { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CUSBKBD) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CBTKBD) }, #endif { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 48b66bbffc94..315891d07521 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -561,6 +561,8 @@ #define USB_VENDOR_ID_LENOVO 0x17ef #define USB_DEVICE_ID_LENOVO_TPKBD 0x6009 +#define USB_DEVICE_ID_LENOVO_CUSBKBD 0x6047 +#define USB_DEVICE_ID_LENOVO_CBTKBD 0x6048 #define USB_VENDOR_ID_LG 0x1fd2 #define USB_DEVICE_ID_LG_MULTITOUCH 0x0064 diff --git a/drivers/hid/hid-lenovo.c b/drivers/hid/hid-lenovo.c index f96bf095670a..bf227f7679af 100644 --- a/drivers/hid/hid-lenovo.c +++ b/drivers/hid/hid-lenovo.c @@ -1,8 +1,11 @@ /* * HID driver for Lenovo: * - ThinkPad USB Keyboard with TrackPoint (tpkbd) + * - ThinkPad Compact Bluetooth Keyboard with TrackPoint (cptkbd) + * - ThinkPad Compact USB Keyboard with TrackPoint (cptkbd) * * Copyright (c) 2012 Bernhard Seibold + * Copyright (c) 2014 Jamie Lentin */ /* @@ -33,6 +36,10 @@ struct lenovo_drvdata_tpkbd { int press_speed; }; +struct lenovo_drvdata_cptkbd { + bool fn_lock; +}; + #define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c)) static int lenovo_input_mapping_tpkbd(struct hid_device *hdev, @@ -48,6 +55,49 @@ static int lenovo_input_mapping_tpkbd(struct hid_device *hdev, return 0; } +static int lenovo_input_mapping_cptkbd(struct hid_device *hdev, + struct hid_input *hi, struct hid_field *field, + struct hid_usage *usage, unsigned long **bit, int *max) +{ + /* HID_UP_LNVENDOR = USB, HID_UP_MSVENDOR = BT */ + if ((usage->hid & HID_USAGE_PAGE) == HID_UP_MSVENDOR || + (usage->hid & HID_USAGE_PAGE) == HID_UP_LNVENDOR) { + set_bit(EV_REP, hi->input->evbit); + switch (usage->hid & HID_USAGE) { + case 0x00f1: /* Fn-F4: Mic mute */ + map_key_clear(KEY_MICMUTE); + return 1; + case 0x00f2: /* Fn-F5: Brightness down */ + map_key_clear(KEY_BRIGHTNESSDOWN); + return 1; + case 0x00f3: /* Fn-F6: Brightness up */ + map_key_clear(KEY_BRIGHTNESSUP); + return 1; + case 0x00f4: /* Fn-F7: External display (projector) */ + map_key_clear(KEY_SWITCHVIDEOMODE); + return 1; + case 0x00f5: /* Fn-F8: Wireless */ + map_key_clear(KEY_WLAN); + return 1; + case 0x00f6: /* Fn-F9: Control panel */ + map_key_clear(KEY_CONFIG); + return 1; + case 0x00f8: /* Fn-F11: View open applications (3 boxes) */ + map_key_clear(KEY_SCALE); + return 1; + case 0x00fa: /* Fn-Esc: Fn-lock toggle */ + map_key_clear(KEY_FN_ESC); + return 1; + case 0x00fb: /* Fn-F12: Open My computer (6 boxes) USB-only */ + /* NB: This mapping is invented in raw_event below */ + map_key_clear(KEY_FILE); + return 1; + } + } + + return 0; +} + static int lenovo_input_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) @@ -56,6 +106,10 @@ static int lenovo_input_mapping(struct hid_device *hdev, case USB_DEVICE_ID_LENOVO_TPKBD: return lenovo_input_mapping_tpkbd(hdev, hi, field, usage, bit, max); + case USB_DEVICE_ID_LENOVO_CUSBKBD: + case USB_DEVICE_ID_LENOVO_CBTKBD: + return lenovo_input_mapping_cptkbd(hdev, hi, field, + usage, bit, max); default: return 0; } @@ -63,6 +117,103 @@ static int lenovo_input_mapping(struct hid_device *hdev, #undef map_key_clear +/* Send a config command to the keyboard */ +static int lenovo_send_cmd_cptkbd(struct hid_device *hdev, + unsigned char byte2, unsigned char byte3) +{ + int ret; + unsigned char buf[] = {0x18, byte2, byte3}; + + switch (hdev->product) { + case USB_DEVICE_ID_LENOVO_CUSBKBD: + ret = hid_hw_raw_request(hdev, 0x13, buf, sizeof(buf), + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); + break; + case USB_DEVICE_ID_LENOVO_CBTKBD: + ret = hid_hw_output_report(hdev, buf, sizeof(buf)); + break; + default: + ret = -EINVAL; + break; + } + + return ret < 0 ? ret : 0; /* BT returns 0, USB returns sizeof(buf) */ +} + +static void lenovo_features_set_cptkbd(struct hid_device *hdev) +{ + int ret; + struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev); + + ret = lenovo_send_cmd_cptkbd(hdev, 0x05, cptkbd_data->fn_lock); + if (ret) + hid_err(hdev, "Fn-lock setting failed: %d\n", ret); +} + +static ssize_t attr_fn_lock_show_cptkbd(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev); + + return snprintf(buf, PAGE_SIZE, "%u\n", cptkbd_data->fn_lock); +} + +static ssize_t attr_fn_lock_store_cptkbd(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev); + int value; + + if (kstrtoint(buf, 10, &value)) + return -EINVAL; + if (value < 0 || value > 1) + return -EINVAL; + + cptkbd_data->fn_lock = !!value; + lenovo_features_set_cptkbd(hdev); + + return count; +} + +static struct device_attribute dev_attr_fn_lock_cptkbd = + __ATTR(fn_lock, S_IWUSR | S_IRUGO, + attr_fn_lock_show_cptkbd, + attr_fn_lock_store_cptkbd); + +static struct attribute *lenovo_attributes_cptkbd[] = { + &dev_attr_fn_lock_cptkbd.attr, + NULL +}; + +static const struct attribute_group lenovo_attr_group_cptkbd = { + .attrs = lenovo_attributes_cptkbd, +}; + +static int lenovo_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *data, int size) +{ + /* + * Compact USB keyboard's Fn-F12 report holds down many other keys, and + * its own key is outside the usage page range. Remove extra + * keypresses and remap to inside usage page. + */ + if (unlikely(hdev->product == USB_DEVICE_ID_LENOVO_CUSBKBD + && size == 3 + && data[0] == 0x15 + && data[1] == 0x94 + && data[2] == 0x01)) { + data[1] = 0x0; + data[2] = 0x4; + } + + return 0; +} + static int lenovo_features_set_tpkbd(struct hid_device *hdev) { struct hid_report *report; @@ -415,6 +566,46 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev) return 0; } +static int lenovo_probe_cptkbd(struct hid_device *hdev) +{ + int ret; + struct lenovo_drvdata_cptkbd *cptkbd_data; + + /* All the custom action happens on the USBMOUSE device for USB */ + if (hdev->product == USB_DEVICE_ID_LENOVO_CUSBKBD + && hdev->type != HID_TYPE_USBMOUSE) { + hid_dbg(hdev, "Ignoring keyboard half of device\n"); + return 0; + } + + cptkbd_data = devm_kzalloc(&hdev->dev, + sizeof(*cptkbd_data), + GFP_KERNEL); + if (cptkbd_data == NULL) { + hid_err(hdev, "can't alloc keyboard descriptor\n"); + return -ENOMEM; + } + hid_set_drvdata(hdev, cptkbd_data); + + /* + * Tell the keyboard a driver understands it, and turn F7, F9, F11 into + * regular keys + */ + ret = lenovo_send_cmd_cptkbd(hdev, 0x01, 0x03); + if (ret) + hid_warn(hdev, "Failed to switch F7/9/11 mode: %d\n", ret); + + /* Turn Fn-Lock on by default */ + cptkbd_data->fn_lock = true; + lenovo_features_set_cptkbd(hdev); + + ret = sysfs_create_group(&hdev->dev.kobj, &lenovo_attr_group_cptkbd); + if (ret) + hid_warn(hdev, "Could not create sysfs group: %d\n", ret); + + return 0; +} + static int lenovo_probe(struct hid_device *hdev, const struct hid_device_id *id) { @@ -436,6 +627,10 @@ static int lenovo_probe(struct hid_device *hdev, case USB_DEVICE_ID_LENOVO_TPKBD: ret = lenovo_probe_tpkbd(hdev); break; + case USB_DEVICE_ID_LENOVO_CUSBKBD: + case USB_DEVICE_ID_LENOVO_CBTKBD: + ret = lenovo_probe_cptkbd(hdev); + break; default: ret = 0; break; @@ -470,12 +665,22 @@ static void lenovo_remove_tpkbd(struct hid_device *hdev) hid_set_drvdata(hdev, NULL); } +static void lenovo_remove_cptkbd(struct hid_device *hdev) +{ + sysfs_remove_group(&hdev->dev.kobj, + &lenovo_attr_group_cptkbd); +} + static void lenovo_remove(struct hid_device *hdev) { switch (hdev->product) { case USB_DEVICE_ID_LENOVO_TPKBD: lenovo_remove_tpkbd(hdev); break; + case USB_DEVICE_ID_LENOVO_CUSBKBD: + case USB_DEVICE_ID_LENOVO_CBTKBD: + lenovo_remove_cptkbd(hdev); + break; } hid_hw_stop(hdev); @@ -483,6 +688,8 @@ static void lenovo_remove(struct hid_device *hdev) static const struct hid_device_id lenovo_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CUSBKBD) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CBTKBD) }, { } }; @@ -494,6 +701,7 @@ static struct hid_driver lenovo_driver = { .input_mapping = lenovo_input_mapping, .probe = lenovo_probe, .remove = lenovo_remove, + .raw_event = lenovo_raw_event, }; module_hid_driver(lenovo_driver); diff --git a/include/linux/hid.h b/include/linux/hid.h index 77632cf159c0..fca74f1d5c84 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -167,6 +167,7 @@ struct hid_item { #define HID_UP_MSVENDOR 0xff000000 #define HID_UP_CUSTOM 0x00ff0000 #define HID_UP_LOGIVENDOR 0xffbc0000 +#define HID_UP_LNVENDOR 0xffa00000 #define HID_UP_SENSOR 0x00200000 #define HID_USAGE 0x0000ffff -- cgit v1.2.3 From baaa7b5d4f1e515a39f1eebd5fb16b67e00b22fb Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 18 Jul 2014 12:49:55 +0200 Subject: iommu/omap: Remove virtual memory manager The OMAP3 ISP driver was the only user of the OMAP IOVMM API. Now that is has been ported to the DMA API, remove the unused virtual memory manager. Signed-off-by: Laurent Pinchart Signed-off-by: Joerg Roedel --- drivers/iommu/Kconfig | 10 +- drivers/iommu/Makefile | 1 - drivers/iommu/omap-iommu-debug.c | 114 ------ drivers/iommu/omap-iommu.c | 13 - drivers/iommu/omap-iommu.h | 8 +- drivers/iommu/omap-iovmm.c | 791 --------------------------------------- include/linux/omap-iommu.h | 37 +- 7 files changed, 8 insertions(+), 966 deletions(-) delete mode 100644 drivers/iommu/omap-iovmm.c (limited to 'include') diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index d260605e6d5f..154e5a838257 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -143,16 +143,12 @@ config OMAP_IOMMU depends on ARCH_OMAP2PLUS select IOMMU_API -config OMAP_IOVMM - tristate "OMAP IO Virtual Memory Manager Support" - depends on OMAP_IOMMU - config OMAP_IOMMU_DEBUG - tristate "Export OMAP IOMMU/IOVMM internals in DebugFS" - depends on OMAP_IOVMM && DEBUG_FS + tristate "Export OMAP IOMMU internals in DebugFS" + depends on OMAP_IOMMU && DEBUG_FS help Select this to see extensive information about - the internal state of OMAP IOMMU/IOVMM in debugfs. + the internal state of OMAP IOMMU in debugfs. Say N unless you know you need this. diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile index 8893bad048e0..6a4a00ef088b 100644 --- a/drivers/iommu/Makefile +++ b/drivers/iommu/Makefile @@ -11,7 +11,6 @@ obj-$(CONFIG_IPMMU_VMSA) += ipmmu-vmsa.o obj-$(CONFIG_IRQ_REMAP) += intel_irq_remapping.o irq_remapping.o obj-$(CONFIG_OMAP_IOMMU) += omap-iommu.o obj-$(CONFIG_OMAP_IOMMU) += omap-iommu2.o -obj-$(CONFIG_OMAP_IOVMM) += omap-iovmm.o obj-$(CONFIG_OMAP_IOMMU_DEBUG) += omap-iommu-debug.o obj-$(CONFIG_TEGRA_IOMMU_GART) += tegra-gart.o obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o diff --git a/drivers/iommu/omap-iommu-debug.c b/drivers/iommu/omap-iommu-debug.c index 80fffba7f12d..531658d17333 100644 --- a/drivers/iommu/omap-iommu-debug.c +++ b/drivers/iommu/omap-iommu-debug.c @@ -213,116 +213,6 @@ static ssize_t debug_read_pagetable(struct file *file, char __user *userbuf, return bytes; } -static ssize_t debug_read_mmap(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct device *dev = file->private_data; - struct omap_iommu *obj = dev_to_omap_iommu(dev); - char *p, *buf; - struct iovm_struct *tmp; - int uninitialized_var(i); - ssize_t bytes; - - buf = (char *)__get_free_page(GFP_KERNEL); - if (!buf) - return -ENOMEM; - p = buf; - - p += sprintf(p, "%-3s %-8s %-8s %6s %8s\n", - "No", "start", "end", "size", "flags"); - p += sprintf(p, "-------------------------------------------------\n"); - - mutex_lock(&iommu_debug_lock); - - list_for_each_entry(tmp, &obj->mmap, list) { - size_t len; - const char *str = "%3d %08x-%08x %6x %8x\n"; - const int maxcol = 39; - - len = tmp->da_end - tmp->da_start; - p += snprintf(p, maxcol, str, - i, tmp->da_start, tmp->da_end, len, tmp->flags); - - if (PAGE_SIZE - (p - buf) < maxcol) - break; - i++; - } - - bytes = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf); - - mutex_unlock(&iommu_debug_lock); - free_page((unsigned long)buf); - - return bytes; -} - -static ssize_t debug_read_mem(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct device *dev = file->private_data; - char *p, *buf; - struct iovm_struct *area; - ssize_t bytes; - - count = min_t(ssize_t, count, PAGE_SIZE); - - buf = (char *)__get_free_page(GFP_KERNEL); - if (!buf) - return -ENOMEM; - p = buf; - - mutex_lock(&iommu_debug_lock); - - area = omap_find_iovm_area(dev, (u32)ppos); - if (!area) { - bytes = -EINVAL; - goto err_out; - } - memcpy(p, area->va, count); - p += count; - - bytes = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf); -err_out: - mutex_unlock(&iommu_debug_lock); - free_page((unsigned long)buf); - - return bytes; -} - -static ssize_t debug_write_mem(struct file *file, const char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct device *dev = file->private_data; - struct iovm_struct *area; - char *p, *buf; - - count = min_t(size_t, count, PAGE_SIZE); - - buf = (char *)__get_free_page(GFP_KERNEL); - if (!buf) - return -ENOMEM; - p = buf; - - mutex_lock(&iommu_debug_lock); - - if (copy_from_user(p, userbuf, count)) { - count = -EFAULT; - goto err_out; - } - - area = omap_find_iovm_area(dev, (u32)ppos); - if (!area) { - count = -EINVAL; - goto err_out; - } - memcpy(area->va, p, count); -err_out: - mutex_unlock(&iommu_debug_lock); - free_page((unsigned long)buf); - - return count; -} - #define DEBUG_FOPS(name) \ static const struct file_operations debug_##name##_fops = { \ .open = simple_open, \ @@ -342,8 +232,6 @@ DEBUG_FOPS_RO(ver); DEBUG_FOPS_RO(regs); DEBUG_FOPS_RO(tlb); DEBUG_FOPS(pagetable); -DEBUG_FOPS_RO(mmap); -DEBUG_FOPS(mem); #define __DEBUG_ADD_FILE(attr, mode) \ { \ @@ -389,8 +277,6 @@ static int iommu_debug_register(struct device *dev, void *data) DEBUG_ADD_FILE_RO(regs); DEBUG_ADD_FILE_RO(tlb); DEBUG_ADD_FILE(pagetable); - DEBUG_ADD_FILE_RO(mmap); - DEBUG_ADD_FILE(mem); return 0; diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c index 895af06a667f..61599e2b33ca 100644 --- a/drivers/iommu/omap-iommu.c +++ b/drivers/iommu/omap-iommu.c @@ -959,31 +959,18 @@ static int omap_iommu_probe(struct platform_device *pdev) return err; if (obj->nr_tlb_entries != 32 && obj->nr_tlb_entries != 8) return -EINVAL; - /* - * da_start and da_end are needed for omap-iovmm, so hardcode - * these values as used by OMAP3 ISP - the only user for - * omap-iovmm - */ - obj->da_start = 0; - obj->da_end = 0xfffff000; if (of_find_property(of, "ti,iommu-bus-err-back", NULL)) obj->has_bus_err_back = MMU_GP_REG_BUS_ERR_BACK_EN; } else { obj->nr_tlb_entries = pdata->nr_tlb_entries; obj->name = pdata->name; - obj->da_start = pdata->da_start; - obj->da_end = pdata->da_end; } - if (obj->da_end <= obj->da_start) - return -EINVAL; obj->dev = &pdev->dev; obj->ctx = (void *)obj + sizeof(*obj); spin_lock_init(&obj->iommu_lock); - mutex_init(&obj->mmap_lock); spin_lock_init(&obj->page_table_lock); - INIT_LIST_HEAD(&obj->mmap); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); obj->regbase = devm_ioremap_resource(obj->dev, res); diff --git a/drivers/iommu/omap-iommu.h b/drivers/iommu/omap-iommu.h index ea920c3e94ff..1275a822934b 100644 --- a/drivers/iommu/omap-iommu.h +++ b/drivers/iommu/omap-iommu.h @@ -46,12 +46,7 @@ struct omap_iommu { int nr_tlb_entries; - struct list_head mmap; - struct mutex mmap_lock; /* protect mmap */ - void *ctx; /* iommu context: registres saved area */ - u32 da_start; - u32 da_end; int has_bus_err_back; }; @@ -154,9 +149,12 @@ static inline struct omap_iommu *dev_to_omap_iommu(struct device *dev) #define MMU_RAM_PADDR_MASK \ ((~0UL >> MMU_RAM_PADDR_SHIFT) << MMU_RAM_PADDR_SHIFT) +#define MMU_RAM_ENDIAN_SHIFT 9 #define MMU_RAM_ENDIAN_MASK (1 << MMU_RAM_ENDIAN_SHIFT) +#define MMU_RAM_ENDIAN_LITTLE (0 << MMU_RAM_ENDIAN_SHIFT) #define MMU_RAM_ENDIAN_BIG (1 << MMU_RAM_ENDIAN_SHIFT) +#define MMU_RAM_ELSZ_SHIFT 7 #define MMU_RAM_ELSZ_MASK (3 << MMU_RAM_ELSZ_SHIFT) #define MMU_RAM_ELSZ_8 (0 << MMU_RAM_ELSZ_SHIFT) #define MMU_RAM_ELSZ_16 (1 << MMU_RAM_ELSZ_SHIFT) diff --git a/drivers/iommu/omap-iovmm.c b/drivers/iommu/omap-iovmm.c deleted file mode 100644 index f583ba049168..000000000000 --- a/drivers/iommu/omap-iovmm.c +++ /dev/null @@ -1,791 +0,0 @@ -/* - * omap iommu: simple virtual address space management - * - * Copyright (C) 2008-2009 Nokia Corporation - * - * Written by Hiroshi DOYU - * - * 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 "omap-iopgtable.h" -#include "omap-iommu.h" - -/* - * IOVMF_FLAGS: attribute for iommu virtual memory area(iovma) - * - * lower 16 bit is used for h/w and upper 16 bit is for s/w. - */ -#define IOVMF_SW_SHIFT 16 - -/* - * iovma: h/w flags derived from cam and ram attribute - */ -#define IOVMF_CAM_MASK (~((1 << 10) - 1)) -#define IOVMF_RAM_MASK (~IOVMF_CAM_MASK) - -#define IOVMF_PGSZ_MASK (3 << 0) -#define IOVMF_PGSZ_1M MMU_CAM_PGSZ_1M -#define IOVMF_PGSZ_64K MMU_CAM_PGSZ_64K -#define IOVMF_PGSZ_4K MMU_CAM_PGSZ_4K -#define IOVMF_PGSZ_16M MMU_CAM_PGSZ_16M - -#define IOVMF_ENDIAN_MASK (1 << 9) -#define IOVMF_ENDIAN_BIG MMU_RAM_ENDIAN_BIG - -#define IOVMF_ELSZ_MASK (3 << 7) -#define IOVMF_ELSZ_16 MMU_RAM_ELSZ_16 -#define IOVMF_ELSZ_32 MMU_RAM_ELSZ_32 -#define IOVMF_ELSZ_NONE MMU_RAM_ELSZ_NONE - -#define IOVMF_MIXED_MASK (1 << 6) -#define IOVMF_MIXED MMU_RAM_MIXED - -/* - * iovma: s/w flags, used for mapping and umapping internally. - */ -#define IOVMF_MMIO (1 << IOVMF_SW_SHIFT) -#define IOVMF_ALLOC (2 << IOVMF_SW_SHIFT) -#define IOVMF_ALLOC_MASK (3 << IOVMF_SW_SHIFT) - -/* "superpages" is supported just with physically linear pages */ -#define IOVMF_DISCONT (1 << (2 + IOVMF_SW_SHIFT)) -#define IOVMF_LINEAR (2 << (2 + IOVMF_SW_SHIFT)) -#define IOVMF_LINEAR_MASK (3 << (2 + IOVMF_SW_SHIFT)) - -#define IOVMF_DA_FIXED (1 << (4 + IOVMF_SW_SHIFT)) - -static struct kmem_cache *iovm_area_cachep; - -/* return the offset of the first scatterlist entry in a sg table */ -static unsigned int sgtable_offset(const struct sg_table *sgt) -{ - if (!sgt || !sgt->nents) - return 0; - - return sgt->sgl->offset; -} - -/* return total bytes of sg buffers */ -static size_t sgtable_len(const struct sg_table *sgt) -{ - unsigned int i, total = 0; - struct scatterlist *sg; - - if (!sgt) - return 0; - - for_each_sg(sgt->sgl, sg, sgt->nents, i) { - size_t bytes; - - bytes = sg->length + sg->offset; - - if (!iopgsz_ok(bytes)) { - pr_err("%s: sg[%d] not iommu pagesize(%u %u)\n", - __func__, i, bytes, sg->offset); - return 0; - } - - if (i && sg->offset) { - pr_err("%s: sg[%d] offset not allowed in internal entries\n", - __func__, i); - return 0; - } - - total += bytes; - } - - return total; -} -#define sgtable_ok(x) (!!sgtable_len(x)) - -static unsigned max_alignment(u32 addr) -{ - int i; - unsigned pagesize[] = { SZ_16M, SZ_1M, SZ_64K, SZ_4K, }; - for (i = 0; i < ARRAY_SIZE(pagesize) && addr & (pagesize[i] - 1); i++) - ; - return (i < ARRAY_SIZE(pagesize)) ? pagesize[i] : 0; -} - -/* - * calculate the optimal number sg elements from total bytes based on - * iommu superpages - */ -static unsigned sgtable_nents(size_t bytes, u32 da, u32 pa) -{ - unsigned nr_entries = 0, ent_sz; - - if (!PAGE_ALIGNED(bytes)) { - pr_err("%s: wrong size %08x\n", __func__, bytes); - return 0; - } - - while (bytes) { - ent_sz = max_alignment(da | pa); - ent_sz = min_t(unsigned, ent_sz, iopgsz_max(bytes)); - nr_entries++; - da += ent_sz; - pa += ent_sz; - bytes -= ent_sz; - } - - return nr_entries; -} - -/* allocate and initialize sg_table header(a kind of 'superblock') */ -static struct sg_table *sgtable_alloc(const size_t bytes, u32 flags, - u32 da, u32 pa) -{ - unsigned int nr_entries; - int err; - struct sg_table *sgt; - - if (!bytes) - return ERR_PTR(-EINVAL); - - if (!PAGE_ALIGNED(bytes)) - return ERR_PTR(-EINVAL); - - if (flags & IOVMF_LINEAR) { - nr_entries = sgtable_nents(bytes, da, pa); - if (!nr_entries) - return ERR_PTR(-EINVAL); - } else - nr_entries = bytes / PAGE_SIZE; - - sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); - if (!sgt) - return ERR_PTR(-ENOMEM); - - err = sg_alloc_table(sgt, nr_entries, GFP_KERNEL); - if (err) { - kfree(sgt); - return ERR_PTR(err); - } - - pr_debug("%s: sgt:%p(%d entries)\n", __func__, sgt, nr_entries); - - return sgt; -} - -/* free sg_table header(a kind of superblock) */ -static void sgtable_free(struct sg_table *sgt) -{ - if (!sgt) - return; - - sg_free_table(sgt); - kfree(sgt); - - pr_debug("%s: sgt:%p\n", __func__, sgt); -} - -/* map 'sglist' to a contiguous mpu virtual area and return 'va' */ -static void *vmap_sg(const struct sg_table *sgt) -{ - u32 va; - size_t total; - unsigned int i; - struct scatterlist *sg; - struct vm_struct *new; - const struct mem_type *mtype; - - mtype = get_mem_type(MT_DEVICE); - if (!mtype) - return ERR_PTR(-EINVAL); - - total = sgtable_len(sgt); - if (!total) - return ERR_PTR(-EINVAL); - - new = __get_vm_area(total, VM_IOREMAP, VMALLOC_START, VMALLOC_END); - if (!new) - return ERR_PTR(-ENOMEM); - va = (u32)new->addr; - - for_each_sg(sgt->sgl, sg, sgt->nents, i) { - size_t bytes; - u32 pa; - int err; - - pa = sg_phys(sg) - sg->offset; - bytes = sg->length + sg->offset; - - BUG_ON(bytes != PAGE_SIZE); - - err = ioremap_page(va, pa, mtype); - if (err) - goto err_out; - - va += bytes; - } - - flush_cache_vmap((unsigned long)new->addr, - (unsigned long)(new->addr + total)); - return new->addr; - -err_out: - WARN_ON(1); /* FIXME: cleanup some mpu mappings */ - vunmap(new->addr); - return ERR_PTR(-EAGAIN); -} - -static inline void vunmap_sg(const void *va) -{ - vunmap(va); -} - -static struct iovm_struct *__find_iovm_area(struct omap_iommu *obj, - const u32 da) -{ - struct iovm_struct *tmp; - - list_for_each_entry(tmp, &obj->mmap, list) { - if ((da >= tmp->da_start) && (da < tmp->da_end)) { - size_t len; - - len = tmp->da_end - tmp->da_start; - - dev_dbg(obj->dev, "%s: %08x-%08x-%08x(%x) %08x\n", - __func__, tmp->da_start, da, tmp->da_end, len, - tmp->flags); - - return tmp; - } - } - - return NULL; -} - -/** - * omap_find_iovm_area - find iovma which includes @da - * @dev: client device - * @da: iommu device virtual address - * - * Find the existing iovma starting at @da - */ -struct iovm_struct *omap_find_iovm_area(struct device *dev, u32 da) -{ - struct omap_iommu *obj = dev_to_omap_iommu(dev); - struct iovm_struct *area; - - mutex_lock(&obj->mmap_lock); - area = __find_iovm_area(obj, da); - mutex_unlock(&obj->mmap_lock); - - return area; -} -EXPORT_SYMBOL_GPL(omap_find_iovm_area); - -/* - * This finds the hole(area) which fits the requested address and len - * in iovmas mmap, and returns the new allocated iovma. - */ -static struct iovm_struct *alloc_iovm_area(struct omap_iommu *obj, u32 da, - size_t bytes, u32 flags) -{ - struct iovm_struct *new, *tmp; - u32 start, prev_end, alignment; - - if (!obj || !bytes) - return ERR_PTR(-EINVAL); - - start = da; - alignment = PAGE_SIZE; - - if (~flags & IOVMF_DA_FIXED) { - /* Don't map address 0 */ - start = obj->da_start ? obj->da_start : alignment; - - if (flags & IOVMF_LINEAR) - alignment = iopgsz_max(bytes); - start = roundup(start, alignment); - } else if (start < obj->da_start || start > obj->da_end || - obj->da_end - start < bytes) { - return ERR_PTR(-EINVAL); - } - - tmp = NULL; - if (list_empty(&obj->mmap)) - goto found; - - prev_end = 0; - list_for_each_entry(tmp, &obj->mmap, list) { - - if (prev_end > start) - break; - - if (tmp->da_start > start && (tmp->da_start - start) >= bytes) - goto found; - - if (tmp->da_end >= start && ~flags & IOVMF_DA_FIXED) - start = roundup(tmp->da_end + 1, alignment); - - prev_end = tmp->da_end; - } - - if ((start >= prev_end) && (obj->da_end - start >= bytes)) - goto found; - - dev_dbg(obj->dev, "%s: no space to fit %08x(%x) flags: %08x\n", - __func__, da, bytes, flags); - - return ERR_PTR(-EINVAL); - -found: - new = kmem_cache_zalloc(iovm_area_cachep, GFP_KERNEL); - if (!new) - return ERR_PTR(-ENOMEM); - - new->iommu = obj; - new->da_start = start; - new->da_end = start + bytes; - new->flags = flags; - - /* - * keep ascending order of iovmas - */ - if (tmp) - list_add_tail(&new->list, &tmp->list); - else - list_add(&new->list, &obj->mmap); - - dev_dbg(obj->dev, "%s: found %08x-%08x-%08x(%x) %08x\n", - __func__, new->da_start, start, new->da_end, bytes, flags); - - return new; -} - -static void free_iovm_area(struct omap_iommu *obj, struct iovm_struct *area) -{ - size_t bytes; - - BUG_ON(!obj || !area); - - bytes = area->da_end - area->da_start; - - dev_dbg(obj->dev, "%s: %08x-%08x(%x) %08x\n", - __func__, area->da_start, area->da_end, bytes, area->flags); - - list_del(&area->list); - kmem_cache_free(iovm_area_cachep, area); -} - -/** - * omap_da_to_va - convert (d) to (v) - * @dev: client device - * @da: iommu device virtual address - * @va: mpu virtual address - * - * Returns mpu virtual addr which corresponds to a given device virtual addr - */ -void *omap_da_to_va(struct device *dev, u32 da) -{ - struct omap_iommu *obj = dev_to_omap_iommu(dev); - void *va = NULL; - struct iovm_struct *area; - - mutex_lock(&obj->mmap_lock); - - area = __find_iovm_area(obj, da); - if (!area) { - dev_dbg(obj->dev, "%s: no da area(%08x)\n", __func__, da); - goto out; - } - va = area->va; -out: - mutex_unlock(&obj->mmap_lock); - - return va; -} -EXPORT_SYMBOL_GPL(omap_da_to_va); - -static void sgtable_fill_vmalloc(struct sg_table *sgt, void *_va) -{ - unsigned int i; - struct scatterlist *sg; - void *va = _va; - void *va_end; - - for_each_sg(sgt->sgl, sg, sgt->nents, i) { - struct page *pg; - const size_t bytes = PAGE_SIZE; - - /* - * iommu 'superpage' isn't supported with 'omap_iommu_vmalloc()' - */ - pg = vmalloc_to_page(va); - BUG_ON(!pg); - sg_set_page(sg, pg, bytes, 0); - - va += bytes; - } - - va_end = _va + PAGE_SIZE * i; -} - -static inline void sgtable_drain_vmalloc(struct sg_table *sgt) -{ - /* - * Actually this is not necessary at all, just exists for - * consistency of the code readability. - */ - BUG_ON(!sgt); -} - -/* create 'da' <-> 'pa' mapping from 'sgt' */ -static int map_iovm_area(struct iommu_domain *domain, struct iovm_struct *new, - const struct sg_table *sgt, u32 flags) -{ - int err; - unsigned int i, j; - struct scatterlist *sg; - u32 da = new->da_start; - - if (!domain || !sgt) - return -EINVAL; - - BUG_ON(!sgtable_ok(sgt)); - - for_each_sg(sgt->sgl, sg, sgt->nents, i) { - u32 pa; - size_t bytes; - - pa = sg_phys(sg) - sg->offset; - bytes = sg->length + sg->offset; - - flags &= ~IOVMF_PGSZ_MASK; - - if (bytes_to_iopgsz(bytes) < 0) - goto err_out; - - pr_debug("%s: [%d] %08x %08x(%x)\n", __func__, - i, da, pa, bytes); - - err = iommu_map(domain, da, pa, bytes, flags); - if (err) - goto err_out; - - da += bytes; - } - return 0; - -err_out: - da = new->da_start; - - for_each_sg(sgt->sgl, sg, i, j) { - size_t bytes; - - bytes = sg->length + sg->offset; - - /* ignore failures.. we're already handling one */ - iommu_unmap(domain, da, bytes); - - da += bytes; - } - return err; -} - -/* release 'da' <-> 'pa' mapping */ -static void unmap_iovm_area(struct iommu_domain *domain, struct omap_iommu *obj, - struct iovm_struct *area) -{ - u32 start; - size_t total = area->da_end - area->da_start; - const struct sg_table *sgt = area->sgt; - struct scatterlist *sg; - int i; - size_t unmapped; - - BUG_ON(!sgtable_ok(sgt)); - BUG_ON((!total) || !PAGE_ALIGNED(total)); - - start = area->da_start; - for_each_sg(sgt->sgl, sg, sgt->nents, i) { - size_t bytes; - - bytes = sg->length + sg->offset; - - unmapped = iommu_unmap(domain, start, bytes); - if (unmapped < bytes) - break; - - dev_dbg(obj->dev, "%s: unmap %08x(%x) %08x\n", - __func__, start, bytes, area->flags); - - BUG_ON(!PAGE_ALIGNED(bytes)); - - total -= bytes; - start += bytes; - } - BUG_ON(total); -} - -/* template function for all unmapping */ -static struct sg_table *unmap_vm_area(struct iommu_domain *domain, - struct omap_iommu *obj, const u32 da, - void (*fn)(const void *), u32 flags) -{ - struct sg_table *sgt = NULL; - struct iovm_struct *area; - - if (!PAGE_ALIGNED(da)) { - dev_err(obj->dev, "%s: alignment err(%08x)\n", __func__, da); - return NULL; - } - - mutex_lock(&obj->mmap_lock); - - area = __find_iovm_area(obj, da); - if (!area) { - dev_dbg(obj->dev, "%s: no da area(%08x)\n", __func__, da); - goto out; - } - - if ((area->flags & flags) != flags) { - dev_err(obj->dev, "%s: wrong flags(%08x)\n", __func__, - area->flags); - goto out; - } - sgt = (struct sg_table *)area->sgt; - - unmap_iovm_area(domain, obj, area); - - fn(area->va); - - dev_dbg(obj->dev, "%s: %08x-%08x-%08x(%x) %08x\n", __func__, - area->da_start, da, area->da_end, - area->da_end - area->da_start, area->flags); - - free_iovm_area(obj, area); -out: - mutex_unlock(&obj->mmap_lock); - - return sgt; -} - -static u32 map_iommu_region(struct iommu_domain *domain, struct omap_iommu *obj, - u32 da, const struct sg_table *sgt, void *va, - size_t bytes, u32 flags) -{ - int err = -ENOMEM; - struct iovm_struct *new; - - mutex_lock(&obj->mmap_lock); - - new = alloc_iovm_area(obj, da, bytes, flags); - if (IS_ERR(new)) { - err = PTR_ERR(new); - goto err_alloc_iovma; - } - new->va = va; - new->sgt = sgt; - - if (map_iovm_area(domain, new, sgt, new->flags)) - goto err_map; - - mutex_unlock(&obj->mmap_lock); - - dev_dbg(obj->dev, "%s: da:%08x(%x) flags:%08x va:%p\n", - __func__, new->da_start, bytes, new->flags, va); - - return new->da_start; - -err_map: - free_iovm_area(obj, new); -err_alloc_iovma: - mutex_unlock(&obj->mmap_lock); - return err; -} - -static inline u32 -__iommu_vmap(struct iommu_domain *domain, struct omap_iommu *obj, - u32 da, const struct sg_table *sgt, - void *va, size_t bytes, u32 flags) -{ - return map_iommu_region(domain, obj, da, sgt, va, bytes, flags); -} - -/** - * omap_iommu_vmap - (d)-(p)-(v) address mapper - * @domain: iommu domain - * @dev: client device - * @sgt: address of scatter gather table - * @flags: iovma and page property - * - * Creates 1-n-1 mapping with given @sgt and returns @da. - * All @sgt element must be io page size aligned. - */ -u32 omap_iommu_vmap(struct iommu_domain *domain, struct device *dev, u32 da, - const struct sg_table *sgt, u32 flags) -{ - struct omap_iommu *obj = dev_to_omap_iommu(dev); - size_t bytes; - void *va = NULL; - - if (!obj || !obj->dev || !sgt) - return -EINVAL; - - bytes = sgtable_len(sgt); - if (!bytes) - return -EINVAL; - bytes = PAGE_ALIGN(bytes); - - if (flags & IOVMF_MMIO) { - va = vmap_sg(sgt); - if (IS_ERR(va)) - return PTR_ERR(va); - } - - flags |= IOVMF_DISCONT; - flags |= IOVMF_MMIO; - - da = __iommu_vmap(domain, obj, da, sgt, va, bytes, flags); - if (IS_ERR_VALUE(da)) - vunmap_sg(va); - - return da + sgtable_offset(sgt); -} -EXPORT_SYMBOL_GPL(omap_iommu_vmap); - -/** - * omap_iommu_vunmap - release virtual mapping obtained by 'omap_iommu_vmap()' - * @domain: iommu domain - * @dev: client device - * @da: iommu device virtual address - * - * Free the iommu virtually contiguous memory area starting at - * @da, which was returned by 'omap_iommu_vmap()'. - */ -struct sg_table * -omap_iommu_vunmap(struct iommu_domain *domain, struct device *dev, u32 da) -{ - struct omap_iommu *obj = dev_to_omap_iommu(dev); - struct sg_table *sgt; - /* - * 'sgt' is allocated before 'omap_iommu_vmalloc()' is called. - * Just returns 'sgt' to the caller to free - */ - da &= PAGE_MASK; - sgt = unmap_vm_area(domain, obj, da, vunmap_sg, - IOVMF_DISCONT | IOVMF_MMIO); - if (!sgt) - dev_dbg(obj->dev, "%s: No sgt\n", __func__); - return sgt; -} -EXPORT_SYMBOL_GPL(omap_iommu_vunmap); - -/** - * omap_iommu_vmalloc - (d)-(p)-(v) address allocator and mapper - * @dev: client device - * @da: contiguous iommu virtual memory - * @bytes: allocation size - * @flags: iovma and page property - * - * Allocate @bytes linearly and creates 1-n-1 mapping and returns - * @da again, which might be adjusted if 'IOVMF_DA_FIXED' is not set. - */ -u32 -omap_iommu_vmalloc(struct iommu_domain *domain, struct device *dev, u32 da, - size_t bytes, u32 flags) -{ - struct omap_iommu *obj = dev_to_omap_iommu(dev); - void *va; - struct sg_table *sgt; - - if (!obj || !obj->dev || !bytes) - return -EINVAL; - - bytes = PAGE_ALIGN(bytes); - - va = vmalloc(bytes); - if (!va) - return -ENOMEM; - - flags |= IOVMF_DISCONT; - flags |= IOVMF_ALLOC; - - sgt = sgtable_alloc(bytes, flags, da, 0); - if (IS_ERR(sgt)) { - da = PTR_ERR(sgt); - goto err_sgt_alloc; - } - sgtable_fill_vmalloc(sgt, va); - - da = __iommu_vmap(domain, obj, da, sgt, va, bytes, flags); - if (IS_ERR_VALUE(da)) - goto err_iommu_vmap; - - return da; - -err_iommu_vmap: - sgtable_drain_vmalloc(sgt); - sgtable_free(sgt); -err_sgt_alloc: - vfree(va); - return da; -} -EXPORT_SYMBOL_GPL(omap_iommu_vmalloc); - -/** - * omap_iommu_vfree - release memory allocated by 'omap_iommu_vmalloc()' - * @dev: client device - * @da: iommu device virtual address - * - * Frees the iommu virtually continuous memory area starting at - * @da, as obtained from 'omap_iommu_vmalloc()'. - */ -void omap_iommu_vfree(struct iommu_domain *domain, struct device *dev, - const u32 da) -{ - struct omap_iommu *obj = dev_to_omap_iommu(dev); - struct sg_table *sgt; - - sgt = unmap_vm_area(domain, obj, da, vfree, - IOVMF_DISCONT | IOVMF_ALLOC); - if (!sgt) - dev_dbg(obj->dev, "%s: No sgt\n", __func__); - sgtable_free(sgt); -} -EXPORT_SYMBOL_GPL(omap_iommu_vfree); - -static int __init iovmm_init(void) -{ - const unsigned long flags = SLAB_HWCACHE_ALIGN; - struct kmem_cache *p; - - p = kmem_cache_create("iovm_area_cache", sizeof(struct iovm_struct), 0, - flags, NULL); - if (!p) - return -ENOMEM; - iovm_area_cachep = p; - - return 0; -} -module_init(iovmm_init); - -static void __exit iovmm_exit(void) -{ - kmem_cache_destroy(iovm_area_cachep); -} -module_exit(iovmm_exit); - -MODULE_DESCRIPTION("omap iommu: simple virtual address space management"); -MODULE_AUTHOR("Hiroshi DOYU "); -MODULE_LICENSE("GPL v2"); diff --git a/include/linux/omap-iommu.h b/include/linux/omap-iommu.h index cac78de09c07..c1aede46718b 100644 --- a/include/linux/omap-iommu.h +++ b/include/linux/omap-iommu.h @@ -10,41 +10,8 @@ * published by the Free Software Foundation. */ -#ifndef _INTEL_IOMMU_H_ -#define _INTEL_IOMMU_H_ - -struct iovm_struct { - struct omap_iommu *iommu; /* iommu object which this belongs to */ - u32 da_start; /* area definition */ - u32 da_end; - u32 flags; /* IOVMF_: see below */ - struct list_head list; /* linked in ascending order */ - const struct sg_table *sgt; /* keep 'page' <-> 'da' mapping */ - void *va; /* mpu side mapped address */ -}; - -#define MMU_RAM_ENDIAN_SHIFT 9 -#define MMU_RAM_ENDIAN_LITTLE (0 << MMU_RAM_ENDIAN_SHIFT) -#define MMU_RAM_ELSZ_8 (0 << MMU_RAM_ELSZ_SHIFT) -#define IOVMF_ENDIAN_LITTLE MMU_RAM_ENDIAN_LITTLE -#define MMU_RAM_ELSZ_SHIFT 7 -#define IOVMF_ELSZ_8 MMU_RAM_ELSZ_8 - -struct iommu_domain; - -extern struct iovm_struct *omap_find_iovm_area(struct device *dev, u32 da); -extern u32 -omap_iommu_vmap(struct iommu_domain *domain, struct device *dev, u32 da, - const struct sg_table *sgt, u32 flags); -extern struct sg_table *omap_iommu_vunmap(struct iommu_domain *domain, - struct device *dev, u32 da); -extern u32 -omap_iommu_vmalloc(struct iommu_domain *domain, struct device *dev, - u32 da, size_t bytes, u32 flags); -extern void -omap_iommu_vfree(struct iommu_domain *domain, struct device *dev, - const u32 da); -extern void *omap_da_to_va(struct device *dev, u32 da); +#ifndef _OMAP_IOMMU_H_ +#define _OMAP_IOMMU_H_ extern void omap_iommu_save_ctx(struct device *dev); extern void omap_iommu_restore_ctx(struct device *dev); -- cgit v1.2.3 From e8a6ebc71e40ec02cd0113c803e919c8329b411b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 18 Jul 2014 12:49:57 +0200 Subject: iommu/omap: Remove platform data da_start and da_end fields The fields were used by the now gone omap-iovmm driver. They're not used anymore, remove them. Signed-off-by: Laurent Pinchart Acked-by: Tony Lindgren Signed-off-by: Joerg Roedel --- include/linux/platform_data/iommu-omap.h | 6 ------ 1 file changed, 6 deletions(-) (limited to 'include') diff --git a/include/linux/platform_data/iommu-omap.h b/include/linux/platform_data/iommu-omap.h index 5b429c43a297..54a0a9582fad 100644 --- a/include/linux/platform_data/iommu-omap.h +++ b/include/linux/platform_data/iommu-omap.h @@ -31,14 +31,10 @@ struct omap_iommu_arch_data { /** * struct omap_mmu_dev_attr - OMAP mmu device attributes for omap_hwmod - * @da_start: device address where the va space starts. - * @da_end: device address where the va space ends. * @nr_tlb_entries: number of entries supported by the translation * look-aside buffer (TLB). */ struct omap_mmu_dev_attr { - u32 da_start; - u32 da_end; int nr_tlb_entries; }; @@ -46,8 +42,6 @@ struct iommu_platform_data { const char *name; const char *reset_name; int nr_tlb_entries; - u32 da_start; - u32 da_end; int (*assert_reset)(struct platform_device *pdev, const char *name); int (*deassert_reset)(struct platform_device *pdev, const char *name); -- cgit v1.2.3 From 3bc0312e67f0be679039980291f88405f20b0a95 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 25 Jul 2014 19:07:11 +0100 Subject: regulator: Add missing statics and inlines for stub functions So we don't get multiple definitions. Signed-off-by: Mark Brown --- include/linux/regulator/consumer.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index 0b1c8d09a6b1..d9e6c20ab9bb 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -460,20 +460,20 @@ static inline int regulator_allow_bypass(struct regulator *regulator, return 0; } -struct regmap *regulator_get_regmap(struct regulator *regulator) +static inline struct regmap *regulator_get_regmap(struct regulator *regulator) { return ERR_PTR(-EOPNOTSUPP); } -int regulator_get_hardware_vsel_register(struct regulator *regulator, - unsigned *vsel_reg, - unsigned *vsel_mask) +static inline int regulator_get_hardware_vsel_register(struct regulator *regulator, + unsigned *vsel_reg, + unsigned *vsel_mask) { return -EOPNOTSUPP; } -int regulator_list_hardware_vsel(struct regulator *regulator, - unsigned selector) +static inline int regulator_list_hardware_vsel(struct regulator *regulator, + unsigned selector) { return -EOPNOTSUPP; } -- cgit v1.2.3 From 5ce43ad28262115a1eab866392f8cfb985094160 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 28 Jul 2014 14:11:32 +0100 Subject: PKCS#7: Use x509_request_asymmetric_key() pkcs7_request_asymmetric_key() and x509_request_asymmetric_key() do the same thing, the latter being a copy of the former created by the IMA folks, so drop the PKCS#7 version as the X.509 location is more general. Whilst we're at it, rename the arguments of x509_request_asymmetric_key() to better reflect what the values being passed in are intended to match on an X.509 cert. Signed-off-by: David Howells Acked-by: Mimi Zohar --- crypto/asymmetric_keys/pkcs7_trust.c | 61 +++----------------------------- crypto/asymmetric_keys/x509_public_key.c | 36 +++++++++++-------- include/crypto/public_key.h | 4 +++ 3 files changed, 29 insertions(+), 72 deletions(-) (limited to 'include') diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index b6b045131403..e666eb011a85 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -20,55 +20,6 @@ #include "public_key.h" #include "pkcs7_parser.h" -/* - * Request an asymmetric key. - */ -static struct key *pkcs7_request_asymmetric_key( - struct key *keyring, - const char *signer, size_t signer_len, - const char *authority, size_t auth_len) -{ - key_ref_t key; - char *id; - - kenter(",%zu,,%zu", signer_len, auth_len); - - /* Construct an identifier. */ - id = kmalloc(signer_len + 2 + auth_len + 1, GFP_KERNEL); - if (!id) - return ERR_PTR(-ENOMEM); - - memcpy(id, signer, signer_len); - id[signer_len + 0] = ':'; - id[signer_len + 1] = ' '; - memcpy(id + signer_len + 2, authority, auth_len); - id[signer_len + 2 + auth_len] = 0; - - pr_debug("Look up: \"%s\"\n", id); - - key = keyring_search(make_key_ref(keyring, 1), - &key_type_asymmetric, id); - if (IS_ERR(key)) - pr_debug("Request for module key '%s' err %ld\n", - id, PTR_ERR(key)); - kfree(id); - - if (IS_ERR(key)) { - switch (PTR_ERR(key)) { - /* Hide some search errors */ - case -EACCES: - case -ENOTDIR: - case -EAGAIN: - return ERR_PTR(-ENOKEY); - default: - return ERR_CAST(key); - } - } - - pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key_ref_to_ptr(key))); - return key_ref_to_ptr(key); -} - /** * Check the trust on one PKCS#7 SignedInfo block. */ @@ -98,10 +49,8 @@ int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, /* Look to see if this certificate is present in the trusted * keys. */ - key = pkcs7_request_asymmetric_key( - trust_keyring, - x509->subject, strlen(x509->subject), - x509->fingerprint, strlen(x509->fingerprint)); + key = x509_request_asymmetric_key(trust_keyring, x509->subject, + x509->fingerprint); if (!IS_ERR(key)) /* One of the X.509 certificates in the PKCS#7 message * is apparently the same as one we already trust. @@ -133,10 +82,8 @@ int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, return -ENOKEY; } - key = pkcs7_request_asymmetric_key( - trust_keyring, - last->issuer, strlen(last->issuer), - last->authority, strlen(last->authority)); + key = x509_request_asymmetric_key(trust_keyring, last->issuer, + last->authority); if (IS_ERR(key)) return PTR_ERR(key) == -ENOMEM ? -ENOMEM : -ENOKEY; x509 = last; diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 4ae982234d78..da1e5fc85346 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -43,35 +43,41 @@ static int __init ca_keys_setup(char *str) __setup("ca_keys=", ca_keys_setup); #endif -/* - * Find a key in the given keyring by issuer and authority. +/** + * x509_request_asymmetric_key - Request a key by X.509 certificate params. + * @keyring: The keys to search. + * @subject: The name of the subject to whom the key belongs. + * @key_id: The subject key ID as a hex string. + * + * Find a key in the given keyring by subject name and key ID. These might, + * for instance, be the issuer name and the authority key ID of an X.509 + * certificate that needs to be verified. */ -static struct key *x509_request_asymmetric_key(struct key *keyring, - const char *signer, - const char *authority) +struct key *x509_request_asymmetric_key(struct key *keyring, + const char *subject, + const char *key_id) { key_ref_t key; - size_t signer_len = strlen(signer), auth_len = strlen(authority); + size_t subject_len = strlen(subject), key_id_len = strlen(key_id); char *id; - /* Construct an identifier. */ - id = kmalloc(signer_len + 2 + auth_len + 1, GFP_KERNEL); + /* Construct an identifier ":". */ + id = kmalloc(subject_len + 2 + key_id_len + 1, GFP_KERNEL); if (!id) return ERR_PTR(-ENOMEM); - memcpy(id, signer, signer_len); - id[signer_len + 0] = ':'; - id[signer_len + 1] = ' '; - memcpy(id + signer_len + 2, authority, auth_len); - id[signer_len + 2 + auth_len] = 0; + memcpy(id, subject, subject_len); + id[subject_len + 0] = ':'; + id[subject_len + 1] = ' '; + memcpy(id + subject_len + 2, key_id, key_id_len); + id[subject_len + 2 + key_id_len] = 0; pr_debug("Look up: \"%s\"\n", id); key = keyring_search(make_key_ref(keyring, 1), &key_type_asymmetric, id); if (IS_ERR(key)) - pr_debug("Request for module key '%s' err %ld\n", - id, PTR_ERR(key)); + pr_debug("Request for key '%s' err %ld\n", id, PTR_ERR(key)); kfree(id); if (IS_ERR(key)) { diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index fc09732613ad..0d164c6af539 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -98,4 +98,8 @@ struct key; extern int verify_signature(const struct key *key, const struct public_key_signature *sig); +extern struct key *x509_request_asymmetric_key(struct key *keyring, + const char *issuer, + const char *key_id); + #endif /* _LINUX_PUBLIC_KEY_H */ -- cgit v1.2.3 From a67d19d4c5b92853550dc20f4afce8c914a8ea0b Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Thu, 24 Jul 2014 15:29:18 +0200 Subject: b43: add support for BCM43131 chipset with N-PHY rev 17 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It contains radio 0x2057 rev 14 just like a BCM43217, so it doesn't require any magic. The main difference is that BCM4313 is 1x1:1. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 3 ++- drivers/net/wireless/b43/phy_n.c | 3 ++- include/linux/bcma/bcma.h | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index d7055febe119..2af1ac396eb4 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -2985,7 +2985,8 @@ void b43_mac_switch_freq(struct b43_wldev *dev, u8 spurmode) { u16 chip_id = dev->dev->chip_id; - if (chip_id == BCMA_CHIP_ID_BCM43217 || + if (chip_id == BCMA_CHIP_ID_BCM43131 || + chip_id == BCMA_CHIP_ID_BCM43217 || chip_id == BCMA_CHIP_ID_BCM43222 || chip_id == BCMA_CHIP_ID_BCM43224 || chip_id == BCMA_CHIP_ID_BCM43225 || diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index d269fbb27b9e..1eead7af6899 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -4982,7 +4982,8 @@ static void b43_nphy_int_pa_set_tx_dig_filters(struct b43_wldev *dev) if (dev->phy.rev == 16) b43_nphy_pa_set_tx_dig_filter(dev, 0x186, dig_filter_phy_rev16); - if (dev->dev->chip_id == BCMA_CHIP_ID_BCM43217) { + /* Verified with BCM43131 and BCM43217 */ + if (dev->phy.rev == 17) { b43_nphy_pa_set_tx_dig_filter(dev, 0x186, dig_filter_phy_rev16); b43_nphy_pa_set_tx_dig_filter(dev, 0x195, tbl_tx_filter_coef_rev4[1]); diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 969af0f2bdf9..70b8d88b3982 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -158,6 +158,7 @@ struct bcma_host_ops { /* Chip IDs of PCIe devices */ #define BCMA_CHIP_ID_BCM4313 0x4313 #define BCMA_CHIP_ID_BCM43142 43142 +#define BCMA_CHIP_ID_BCM43131 43131 #define BCMA_CHIP_ID_BCM43217 43217 #define BCMA_CHIP_ID_BCM43222 43222 #define BCMA_CHIP_ID_BCM43224 43224 -- cgit v1.2.3 From 94b912e42829b25d97b6b1f2be66c6aa81ac125f Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Tue, 29 Jul 2014 18:08:52 +0800 Subject: ARM: imx: Add the secondary request into the structure for imx-sdma SDMA supports device to device (per_2_per) scripts to handle DMA transfering between two peripheral devices. The per_2_per script, however, needs two dma requests from two sides while the current structure only defined one request. So this patch just simply adds the secondary request so as to let SDMA and its user to add its implementation later. [ Both change in the SDMA driver and its users like Freescale ASRC ASoC driver should be taken along with this change in order to truly support per_2_per sciprts. However, we here make an expediency by adding this first so that we can add either side later since this patch won't break any function and meanwhile it can make merge window more smoothly: we don't need to apply the change inside dmaengine branch via ASoC tree any more. -- Nicolin ] Signed-off-by: Nicolin Chen Acked-by: Shawn Guo Signed-off-by: Mark Brown --- include/linux/platform_data/dma-imx.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/platform_data/dma-imx.h b/include/linux/platform_data/dma-imx.h index bcbc6c3c14c0..d05542aafa3e 100644 --- a/include/linux/platform_data/dma-imx.h +++ b/include/linux/platform_data/dma-imx.h @@ -50,6 +50,7 @@ enum imx_dma_prio { struct imx_dma_data { int dma_request; /* DMA request line */ + int dma_request2; /* secondary DMA request line */ enum sdma_peripheral_type peripheral_type; int priority; }; -- cgit v1.2.3 From 68a360e82e55c9b35097e7be7f7991d8f401032f Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Fri, 25 Jul 2014 18:01:31 -0400 Subject: packet: remove deprecated syststamp timestamp No device driver will ever return an skb_shared_info structure with syststamp non-zero, so remove the branch that tests for this and optionally marks the packet timestamp as TP_STATUS_TS_SYS_HARDWARE. Do not remove the definition TP_STATUS_TS_SYS_HARDWARE, as processes may refer to it. Signed-off-by: Willem de Bruijn Signed-off-by: David S. Miller --- Documentation/networking/packet_mmap.txt | 18 ++++++------------ include/uapi/linux/if_packet.h | 2 +- net/packet/af_packet.c | 12 ++++-------- 3 files changed, 11 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/Documentation/networking/packet_mmap.txt b/Documentation/networking/packet_mmap.txt index 38112d512f47..a6d7cb91069e 100644 --- a/Documentation/networking/packet_mmap.txt +++ b/Documentation/networking/packet_mmap.txt @@ -1008,14 +1008,9 @@ hardware timestamps to be used. Note: you may need to enable the generation of hardware timestamps with SIOCSHWTSTAMP (see related information from Documentation/networking/timestamping.txt). -PACKET_TIMESTAMP accepts the same integer bit field as -SO_TIMESTAMPING. However, only the SOF_TIMESTAMPING_SYS_HARDWARE -and SOF_TIMESTAMPING_RAW_HARDWARE values are recognized by -PACKET_TIMESTAMP. SOF_TIMESTAMPING_SYS_HARDWARE takes precedence over -SOF_TIMESTAMPING_RAW_HARDWARE if both bits are set. - - int req = 0; - req |= SOF_TIMESTAMPING_SYS_HARDWARE; +PACKET_TIMESTAMP accepts the same integer bit field as SO_TIMESTAMPING: + + int req = SOF_TIMESTAMPING_RAW_HARDWARE; setsockopt(fd, SOL_PACKET, PACKET_TIMESTAMP, (void *) &req, sizeof(req)) For the mmap(2)ed ring buffers, such timestamps are stored in the @@ -1023,14 +1018,13 @@ tpacket{,2,3}_hdr structure's tp_sec and tp_{n,u}sec members. To determine what kind of timestamp has been reported, the tp_status field is binary |'ed with the following possible bits ... - TP_STATUS_TS_SYS_HARDWARE TP_STATUS_TS_RAW_HARDWARE TP_STATUS_TS_SOFTWARE ... that are equivalent to its SOF_TIMESTAMPING_* counterparts. For the -RX_RING, if none of those 3 are set (i.e. PACKET_TIMESTAMP is not set), -then this means that a software fallback was invoked *within* PF_PACKET's -processing code (less precise). +RX_RING, if neither is set (i.e. PACKET_TIMESTAMP is not set), then a +software fallback was invoked *within* PF_PACKET's processing code (less +precise). Getting timestamps for the TX_RING works as follows: i) fill the ring frames, ii) call sendto() e.g. in blocking mode, iii) wait for status of relevant diff --git a/include/uapi/linux/if_packet.h b/include/uapi/linux/if_packet.h index bac27fa05f5b..da2d668b8cf1 100644 --- a/include/uapi/linux/if_packet.h +++ b/include/uapi/linux/if_packet.h @@ -108,7 +108,7 @@ struct tpacket_auxdata { /* Rx and Tx ring - header status */ #define TP_STATUS_TS_SOFTWARE (1 << 29) -#define TP_STATUS_TS_SYS_HARDWARE (1 << 30) +#define TP_STATUS_TS_SYS_HARDWARE (1 << 30) /* deprecated, never set */ #define TP_STATUS_TS_RAW_HARDWARE (1 << 31) /* Rx ring - feature request bits */ diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 614ca91f785a..8d9f8042705a 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -441,14 +441,10 @@ static __u32 tpacket_get_timestamp(struct sk_buff *skb, struct timespec *ts, { struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); - if (shhwtstamps) { - if ((flags & SOF_TIMESTAMPING_SYS_HARDWARE) && - ktime_to_timespec_cond(shhwtstamps->syststamp, ts)) - return TP_STATUS_TS_SYS_HARDWARE; - if ((flags & SOF_TIMESTAMPING_RAW_HARDWARE) && - ktime_to_timespec_cond(shhwtstamps->hwtstamp, ts)) - return TP_STATUS_TS_RAW_HARDWARE; - } + if (shhwtstamps && + (flags & SOF_TIMESTAMPING_RAW_HARDWARE) && + ktime_to_timespec_cond(shhwtstamps->hwtstamp, ts)) + return TP_STATUS_TS_RAW_HARDWARE; if (ktime_to_timespec_cond(skb->tstamp, ts)) return TP_STATUS_TS_SOFTWARE; -- cgit v1.2.3 From 4d276eb6a478307a28ae843836c455bf04b37a3c Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Fri, 25 Jul 2014 18:01:32 -0400 Subject: net: remove deprecated syststamp timestamp The SO_TIMESTAMPING API defines three types of timestamps: software, hardware in raw format (hwtstamp) and hardware converted to system format (syststamp). The last has been deprecated in favor of combining hwtstamp with a PTP clock driver. There are no active users in the kernel. The option was device driver dependent. If set, but without hardware support, the correct behavior is to return zero in the relevant field in the SCM_TIMESTAMPING ancillary message. Without device drivers implementing the option, this field is effectively always zero. Remove the internal plumbing to dissuage new drivers from implementing the feature. Keep the SOF_TIMESTAMPING_SYS_HARDWARE flag, however, to avoid breaking existing applications that request the timestamp. Signed-off-by: Willem de Bruijn Signed-off-by: David S. Miller --- Documentation/networking/timestamping.txt | 12 ++---------- Documentation/networking/timestamping/timestamping.c | 7 +------ include/linux/skbuff.h | 14 +------------- include/net/sock.h | 11 +++-------- net/core/sock.c | 4 ---- net/socket.c | 12 ++++-------- 6 files changed, 11 insertions(+), 49 deletions(-) (limited to 'include') diff --git a/Documentation/networking/timestamping.txt b/Documentation/networking/timestamping.txt index 8b4ad809df27..897f942b976b 100644 --- a/Documentation/networking/timestamping.txt +++ b/Documentation/networking/timestamping.txt @@ -88,15 +88,8 @@ hwtimeraw is the original hardware time stamp. Filled in if SOF_TIMESTAMPING_RAW_HARDWARE is set. No assumptions about its relation to system time should be made. -hwtimetrans is the hardware time stamp transformed so that it -corresponds as good as possible to system time. This correlation is -not perfect; as a consequence, sorting packets received via different -NICs by their hwtimetrans may differ from the order in which they were -received. hwtimetrans may be non-monotonic even for the same NIC. -Filled in if SOF_TIMESTAMPING_SYS_HARDWARE is set. Requires support -by the network device and will be empty without that support. This -field is DEPRECATED. Only one driver computes this value. New device -drivers must leave this zero. Instead, they can expose the hardware +hwtimetrans is always zero. This field is deprecated. It used to hold +hw timestamps converted to system time. Instead, expose the hardware clock device on the NIC directly as a HW PTP clock source, to allow time conversion in userspace and optionally synchronize system time with a userspace PTP stack such as linuxptp. For the PTP clock API, @@ -191,7 +184,6 @@ struct skb_shared_hwtstamps { * since arbitrary point in time */ ktime_t hwtstamp; - ktime_t syststamp; /* hwtstamp transformed to system time base */ }; Time stamps for outgoing packets are to be generated as follows: diff --git a/Documentation/networking/timestamping/timestamping.c b/Documentation/networking/timestamping/timestamping.c index 8ba82bfe6a33..5cdfd743447b 100644 --- a/Documentation/networking/timestamping/timestamping.c +++ b/Documentation/networking/timestamping/timestamping.c @@ -76,7 +76,6 @@ static void usage(const char *error) " SOF_TIMESTAMPING_RX_HARDWARE - hardware time stamping of incoming packets\n" " SOF_TIMESTAMPING_RX_SOFTWARE - software fallback for incoming packets\n" " SOF_TIMESTAMPING_SOFTWARE - request reporting of software time stamps\n" - " SOF_TIMESTAMPING_SYS_HARDWARE - request reporting of transformed HW time stamps\n" " SOF_TIMESTAMPING_RAW_HARDWARE - request reporting of raw HW time stamps\n" " SIOCGSTAMP - check last socket time stamp\n" " SIOCGSTAMPNS - more accurate socket time stamp\n"); @@ -202,9 +201,7 @@ static void printpacket(struct msghdr *msg, int res, (long)stamp->tv_sec, (long)stamp->tv_nsec); stamp++; - printf("HW transformed %ld.%09ld ", - (long)stamp->tv_sec, - (long)stamp->tv_nsec); + /* skip deprecated HW transformed */ stamp++; printf("HW raw %ld.%09ld", (long)stamp->tv_sec, @@ -361,8 +358,6 @@ int main(int argc, char **argv) so_timestamping_flags |= SOF_TIMESTAMPING_RX_SOFTWARE; else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_SOFTWARE")) so_timestamping_flags |= SOF_TIMESTAMPING_SOFTWARE; - else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_SYS_HARDWARE")) - so_timestamping_flags |= SOF_TIMESTAMPING_SYS_HARDWARE; else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RAW_HARDWARE")) so_timestamping_flags |= SOF_TIMESTAMPING_RAW_HARDWARE; else diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index b613557132b9..281deced7469 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -210,20 +210,9 @@ static inline void skb_frag_size_sub(skb_frag_t *frag, int delta) * struct skb_shared_hwtstamps - hardware time stamps * @hwtstamp: hardware time stamp transformed into duration * since arbitrary point in time - * @syststamp: hwtstamp transformed to system time base (deprecated) * * Software time stamps generated by ktime_get_real() are stored in - * skb->tstamp. The relation between the different kinds of time - * stamps is as follows: - * - * syststamp and tstamp can be compared against each other in - * arbitrary combinations. The accuracy of a - * syststamp/tstamp/"syststamp from other device" comparison is - * limited by the accuracy of the transformation into system time - * base. This depends on the device driver and its underlying - * hardware. The syststamp implementation is deprecated in favor - * of hwtstamps and hw PTP clock sources exposed directly to - * userspace. + * skb->tstamp. * * hwtstamps can only be compared against other hwtstamps from * the same device. @@ -233,7 +222,6 @@ static inline void skb_frag_size_sub(skb_frag_t *frag, int delta) */ struct skb_shared_hwtstamps { ktime_t hwtstamp; - ktime_t syststamp; }; /* Definitions for tx_flags in struct skb_shared_info */ diff --git a/include/net/sock.h b/include/net/sock.h index 720773304a85..b91c8868ab8d 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -707,7 +707,6 @@ enum sock_flags { SOCK_TIMESTAMPING_RX_SOFTWARE, /* %SOF_TIMESTAMPING_RX_SOFTWARE */ SOCK_TIMESTAMPING_SOFTWARE, /* %SOF_TIMESTAMPING_SOFTWARE */ SOCK_TIMESTAMPING_RAW_HARDWARE, /* %SOF_TIMESTAMPING_RAW_HARDWARE */ - SOCK_TIMESTAMPING_SYS_HARDWARE, /* %SOF_TIMESTAMPING_SYS_HARDWARE */ SOCK_FASYNC, /* fasync() active */ SOCK_RXQ_OVFL, SOCK_ZEROCOPY, /* buffers from userspace */ @@ -2166,16 +2165,13 @@ sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb) * - software time stamp available and wanted * (SOCK_TIMESTAMPING_SOFTWARE) * - hardware time stamps available and wanted - * (SOCK_TIMESTAMPING_SYS_HARDWARE or - * SOCK_TIMESTAMPING_RAW_HARDWARE) + * SOCK_TIMESTAMPING_RAW_HARDWARE */ if (sock_flag(sk, SOCK_RCVTSTAMP) || sock_flag(sk, SOCK_TIMESTAMPING_RX_SOFTWARE) || (kt.tv64 && sock_flag(sk, SOCK_TIMESTAMPING_SOFTWARE)) || (hwtstamps->hwtstamp.tv64 && - sock_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE)) || - (hwtstamps->syststamp.tv64 && - sock_flag(sk, SOCK_TIMESTAMPING_SYS_HARDWARE))) + sock_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE))) __sock_recv_timestamp(msg, sk, skb); else sk->sk_stamp = kt; @@ -2193,8 +2189,7 @@ static inline void sock_recv_ts_and_drops(struct msghdr *msg, struct sock *sk, #define FLAGS_TS_OR_DROPS ((1UL << SOCK_RXQ_OVFL) | \ (1UL << SOCK_RCVTSTAMP) | \ (1UL << SOCK_TIMESTAMPING_SOFTWARE) | \ - (1UL << SOCK_TIMESTAMPING_RAW_HARDWARE) | \ - (1UL << SOCK_TIMESTAMPING_SYS_HARDWARE)) + (1UL << SOCK_TIMESTAMPING_RAW_HARDWARE)) if (sk->sk_flags & FLAGS_TS_OR_DROPS) __sock_recv_ts_and_drops(msg, sk, skb); diff --git a/net/core/sock.c b/net/core/sock.c index ca9b65199d28..134291d73fcd 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -862,8 +862,6 @@ set_rcvbuf: (1UL << SOCK_TIMESTAMPING_RX_SOFTWARE)); sock_valbool_flag(sk, SOCK_TIMESTAMPING_SOFTWARE, val & SOF_TIMESTAMPING_SOFTWARE); - sock_valbool_flag(sk, SOCK_TIMESTAMPING_SYS_HARDWARE, - val & SOF_TIMESTAMPING_SYS_HARDWARE); sock_valbool_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE, val & SOF_TIMESTAMPING_RAW_HARDWARE); break; @@ -1102,8 +1100,6 @@ int sock_getsockopt(struct socket *sock, int level, int optname, v.val |= SOF_TIMESTAMPING_RX_SOFTWARE; if (sock_flag(sk, SOCK_TIMESTAMPING_SOFTWARE)) v.val |= SOF_TIMESTAMPING_SOFTWARE; - if (sock_flag(sk, SOCK_TIMESTAMPING_SYS_HARDWARE)) - v.val |= SOF_TIMESTAMPING_SYS_HARDWARE; if (sock_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE)) v.val |= SOF_TIMESTAMPING_RAW_HARDWARE; break; diff --git a/net/socket.c b/net/socket.c index abf56b2a14f9..d8222c025061 100644 --- a/net/socket.c +++ b/net/socket.c @@ -725,14 +725,10 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk, if (sock_flag(sk, SOCK_TIMESTAMPING_SOFTWARE) && ktime_to_timespec_cond(skb->tstamp, ts + 0)) empty = 0; - if (shhwtstamps) { - if (sock_flag(sk, SOCK_TIMESTAMPING_SYS_HARDWARE) && - ktime_to_timespec_cond(shhwtstamps->syststamp, ts + 1)) - empty = 0; - if (sock_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE) && - ktime_to_timespec_cond(shhwtstamps->hwtstamp, ts + 2)) - empty = 0; - } + if (shhwtstamps && + sock_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE) && + ktime_to_timespec_cond(shhwtstamps->hwtstamp, ts + 2)) + empty = 0; if (!empty) put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMPING, sizeof(ts), &ts); -- cgit v1.2.3 From 20e61da7ffcfd84a1b6f797e745608572e5bc218 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Fri, 25 Jul 2014 15:25:08 -0700 Subject: ipv4: fail early when creating netdev named all or default We create a proc dir for each network device, this will cause conflicts when the devices have name "all" or "default". Rather than emitting an ugly kernel warning, we could just fail earlier by checking the device name. Reported-by: Stephane Chazelas Cc: "David S. Miller" Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- include/net/ip.h | 6 ++++++ net/ipv4/devinet.c | 36 +++++++++++++++++++++++++++--------- 2 files changed, 33 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/ip.h b/include/net/ip.h index ca14799545fd..09b32da1b929 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -216,6 +216,12 @@ static inline int inet_is_local_reserved_port(struct net *net, int port) return 0; return test_bit(port, net->ipv4.sysctl_local_reserved_ports); } + +static inline bool sysctl_dev_name_is_allowed(const char *name) +{ + return strcmp(name, "default") != 0 && strcmp(name, "all") != 0; +} + #else static inline int inet_is_local_reserved_port(struct net *net, int port) { diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index e9449376b58e..214882e7d6de 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -180,11 +180,12 @@ static BLOCKING_NOTIFIER_HEAD(inetaddr_chain); static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, int destroy); #ifdef CONFIG_SYSCTL -static void devinet_sysctl_register(struct in_device *idev); +static int devinet_sysctl_register(struct in_device *idev); static void devinet_sysctl_unregister(struct in_device *idev); #else -static void devinet_sysctl_register(struct in_device *idev) +static int devinet_sysctl_register(struct in_device *idev) { + return 0; } static void devinet_sysctl_unregister(struct in_device *idev) { @@ -232,6 +233,7 @@ EXPORT_SYMBOL(in_dev_finish_destroy); static struct in_device *inetdev_init(struct net_device *dev) { struct in_device *in_dev; + int err = -ENOMEM; ASSERT_RTNL(); @@ -252,7 +254,13 @@ static struct in_device *inetdev_init(struct net_device *dev) /* Account for reference dev->ip_ptr (below) */ in_dev_hold(in_dev); - devinet_sysctl_register(in_dev); + err = devinet_sysctl_register(in_dev); + if (err) { + in_dev->dead = 1; + in_dev_put(in_dev); + in_dev = NULL; + goto out; + } ip_mc_init_dev(in_dev); if (dev->flags & IFF_UP) ip_mc_up(in_dev); @@ -260,7 +268,7 @@ static struct in_device *inetdev_init(struct net_device *dev) /* we can receive as soon as ip_ptr is set -- do this last */ rcu_assign_pointer(dev->ip_ptr, in_dev); out: - return in_dev; + return in_dev ?: ERR_PTR(err); out_kfree: kfree(in_dev); in_dev = NULL; @@ -1347,8 +1355,8 @@ static int inetdev_event(struct notifier_block *this, unsigned long event, if (!in_dev) { if (event == NETDEV_REGISTER) { in_dev = inetdev_init(dev); - if (!in_dev) - return notifier_from_errno(-ENOMEM); + if (IS_ERR(in_dev)) + return notifier_from_errno(PTR_ERR(in_dev)); if (dev->flags & IFF_LOOPBACK) { IN_DEV_CONF_SET(in_dev, NOXFRM, 1); IN_DEV_CONF_SET(in_dev, NOPOLICY, 1); @@ -2182,11 +2190,21 @@ static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf) kfree(t); } -static void devinet_sysctl_register(struct in_device *idev) +static int devinet_sysctl_register(struct in_device *idev) { - neigh_sysctl_register(idev->dev, idev->arp_parms, NULL); - __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name, + int err; + + if (!sysctl_dev_name_is_allowed(idev->dev->name)) + return -EINVAL; + + err = neigh_sysctl_register(idev->dev, idev->arp_parms, NULL); + if (err) + return err; + err = __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name, &idev->cnf); + if (err) + neigh_sysctl_unregister(idev->arp_parms); + return err; } static void devinet_sysctl_unregister(struct in_device *idev) -- cgit v1.2.3 From 20fbe3ae990fd54fc7d1f889d61958bc8b38f254 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Mon, 28 Jul 2014 10:12:34 +0200 Subject: cdc_subset: deal with a device that needs reset for timeout This device needs to be reset to recover from a timeout. Unfortunately this can be handled only at the level of the subdrivers. Signed-off-by: Oliver Neukum Signed-off-by: David S. Miller --- drivers/net/usb/cdc_subset.c | 27 +++++++++++++++++++++++++++ drivers/net/usb/usbnet.c | 8 ++++++-- include/linux/usb/usbnet.h | 3 +++ 3 files changed, 36 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/net/usb/cdc_subset.c b/drivers/net/usb/cdc_subset.c index 91f0919fe278..3ef411efd86e 100644 --- a/drivers/net/usb/cdc_subset.c +++ b/drivers/net/usb/cdc_subset.c @@ -85,9 +85,34 @@ static int always_connected (struct usbnet *dev) * *-------------------------------------------------------------------------*/ +static void m5632_recover(struct usbnet *dev) +{ + struct usb_device *udev = dev->udev; + struct usb_interface *intf = dev->intf; + int r; + + r = usb_lock_device_for_reset(udev, intf); + if (r < 0) + return; + + usb_reset_device(udev); + usb_unlock_device(udev); +} + +static int dummy_prereset(struct usb_interface *intf) +{ + return 0; +} + +static int dummy_postreset(struct usb_interface *intf) +{ + return 0; +} + static const struct driver_info ali_m5632_info = { .description = "ALi M5632", .flags = FLAG_POINTTOPOINT, + .recover = m5632_recover, }; #endif @@ -332,6 +357,8 @@ static struct usb_driver cdc_subset_driver = { .probe = usbnet_probe, .suspend = usbnet_suspend, .resume = usbnet_resume, + .pre_reset = dummy_prereset, + .post_reset = dummy_postreset, .disconnect = usbnet_disconnect, .id_table = products, .disable_hub_initiated_lpm = 1, diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index f9e96c427558..5173821a9575 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -1218,8 +1218,12 @@ void usbnet_tx_timeout (struct net_device *net) unlink_urbs (dev, &dev->txq); tasklet_schedule (&dev->bh); - - // FIXME: device recovery -- reset? + /* this needs to be handled individually because the generic layer + * doesn't know what is sufficient and could not restore private + * information if a remedy of an unconditional reset were used. + */ + if (dev->driver_info->recover) + (dev->driver_info->recover)(dev); } EXPORT_SYMBOL_GPL(usbnet_tx_timeout); diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h index 0662e98fef72..26088feb6608 100644 --- a/include/linux/usb/usbnet.h +++ b/include/linux/usb/usbnet.h @@ -148,6 +148,9 @@ struct driver_info { struct sk_buff *(*tx_fixup)(struct usbnet *dev, struct sk_buff *skb, gfp_t flags); + /* recover from timeout */ + void (*recover)(struct usbnet *dev); + /* early initialization code, can sleep. This is for minidrivers * having 'subminidrivers' that need to do extra initialization * right after minidriver have initialized hardware. */ -- cgit v1.2.3 From 518776800c094a518ae6d303660b57f1400eb1eb Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 24 Jul 2014 23:59:33 -0400 Subject: SUNRPC: Allow svc_reserve() to notify TCP socket that space has been freed Signed-off-by: Trond Myklebust Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/svc_xprt.h | 1 + net/sunrpc/svc_xprt.c | 2 ++ net/sunrpc/svcsock.c | 9 +++++++++ 3 files changed, 12 insertions(+) (limited to 'include') diff --git a/include/linux/sunrpc/svc_xprt.h b/include/linux/sunrpc/svc_xprt.h index 5d9d6f84b382..ce6e4182a5b2 100644 --- a/include/linux/sunrpc/svc_xprt.h +++ b/include/linux/sunrpc/svc_xprt.h @@ -25,6 +25,7 @@ struct svc_xprt_ops { void (*xpo_detach)(struct svc_xprt *); void (*xpo_free)(struct svc_xprt *); int (*xpo_secure_port)(struct svc_rqst *); + void (*xpo_adjust_wspace)(struct svc_xprt *); }; struct svc_xprt_class { diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index 9cfa391e2bd0..6666c6745858 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -448,6 +448,8 @@ void svc_reserve(struct svc_rqst *rqstp, int space) atomic_sub((rqstp->rq_reserved - space), &xprt->xpt_reserved); rqstp->rq_reserved = space; + if (xprt->xpt_ops->xpo_adjust_wspace) + xprt->xpt_ops->xpo_adjust_wspace(xprt); svc_xprt_enqueue(xprt); } } diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 88db211d4264..c24a8ff33f8f 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -475,6 +475,14 @@ static void svc_tcp_write_space(struct sock *sk) svc_write_space(sk); } +static void svc_tcp_adjust_wspace(struct svc_xprt *xprt) +{ + struct svc_sock *svsk = container_of(xprt, struct svc_sock, sk_xprt); + + if (svc_tcp_has_wspace(xprt)) + clear_bit(SOCK_NOSPACE, &svsk->sk_sock->flags); +} + /* * See net/ipv6/ip_sockglue.c : ip_cmsg_recv_pktinfo */ @@ -1289,6 +1297,7 @@ static struct svc_xprt_ops svc_tcp_ops = { .xpo_has_wspace = svc_tcp_has_wspace, .xpo_accept = svc_tcp_accept, .xpo_secure_port = svc_sock_secure_port, + .xpo_adjust_wspace = svc_tcp_adjust_wspace, }; static struct svc_xprt_class svc_tcp_class = { -- cgit v1.2.3 From 8e54caf407b98efa05409e1fee0e5381abd2b088 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 21 May 2014 18:26:44 -0600 Subject: tpm: Provide a generic means to override the chip returned timeouts Some Atmel TPMs provide completely wrong timeouts from their TPM_CAP_PROP_TIS_TIMEOUT query. This patch detects that and returns new correct values via a DID/VID table in the TIS driver. Tested on ARM using an AT97SC3204T FW version 37.16 Cc: [PHuewe: without this fix these 'broken' Atmel TPMs won't function on older kernels] Signed-off-by: "Berg, Christopher" Signed-off-by: Jason Gunthorpe Signed-off-by: Peter Huewe --- drivers/char/tpm/tpm-interface.c | 62 ++++++++++++++++++++++++++-------------- drivers/char/tpm/tpm_tis.c | 31 ++++++++++++++++++++ include/linux/tpm.h | 3 ++ 3 files changed, 75 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index bef6cceffc3a..6af17002a115 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -491,11 +491,10 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type) int tpm_get_timeouts(struct tpm_chip *chip) { struct tpm_cmd_t tpm_cmd; - struct timeout_t *timeout_cap; + unsigned long new_timeout[4]; + unsigned long old_timeout[4]; struct duration_t *duration_cap; ssize_t rc; - u32 timeout; - unsigned int scale = 1; tpm_cmd.header.in = tpm_getcap_header; tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP; @@ -529,25 +528,46 @@ int tpm_get_timeouts(struct tpm_chip *chip) != sizeof(tpm_cmd.header.out) + sizeof(u32) + 4 * sizeof(u32)) return -EINVAL; - timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout; - /* Don't overwrite default if value is 0 */ - timeout = be32_to_cpu(timeout_cap->a); - if (timeout && timeout < 1000) { - /* timeouts in msec rather usec */ - scale = 1000; - chip->vendor.timeout_adjusted = true; + old_timeout[0] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.a); + old_timeout[1] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.b); + old_timeout[2] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.c); + old_timeout[3] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.d); + memcpy(new_timeout, old_timeout, sizeof(new_timeout)); + + /* + * Provide ability for vendor overrides of timeout values in case + * of misreporting. + */ + if (chip->ops->update_timeouts != NULL) + chip->vendor.timeout_adjusted = + chip->ops->update_timeouts(chip, new_timeout); + + if (!chip->vendor.timeout_adjusted) { + /* Don't overwrite default if value is 0 */ + if (new_timeout[0] != 0 && new_timeout[0] < 1000) { + int i; + + /* timeouts in msec rather usec */ + for (i = 0; i != ARRAY_SIZE(new_timeout); i++) + new_timeout[i] *= 1000; + chip->vendor.timeout_adjusted = true; + } } - if (timeout) - chip->vendor.timeout_a = usecs_to_jiffies(timeout * scale); - timeout = be32_to_cpu(timeout_cap->b); - if (timeout) - chip->vendor.timeout_b = usecs_to_jiffies(timeout * scale); - timeout = be32_to_cpu(timeout_cap->c); - if (timeout) - chip->vendor.timeout_c = usecs_to_jiffies(timeout * scale); - timeout = be32_to_cpu(timeout_cap->d); - if (timeout) - chip->vendor.timeout_d = usecs_to_jiffies(timeout * scale); + + /* Report adjusted timeouts */ + if (chip->vendor.timeout_adjusted) { + dev_info(chip->dev, + HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n", + old_timeout[0], new_timeout[0], + old_timeout[1], new_timeout[1], + old_timeout[2], new_timeout[2], + old_timeout[3], new_timeout[3]); + } + + chip->vendor.timeout_a = usecs_to_jiffies(new_timeout[0]); + chip->vendor.timeout_b = usecs_to_jiffies(new_timeout[1]); + chip->vendor.timeout_c = usecs_to_jiffies(new_timeout[2]); + chip->vendor.timeout_d = usecs_to_jiffies(new_timeout[3]); duration: tpm_cmd.header.in = tpm_getcap_header; diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index a9ed2270c25d..2c46734b266d 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -373,6 +373,36 @@ out_err: return rc; } +struct tis_vendor_timeout_override { + u32 did_vid; + unsigned long timeout_us[4]; +}; + +static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = { + /* Atmel 3204 */ + { 0x32041114, { (TIS_SHORT_TIMEOUT*1000), (TIS_LONG_TIMEOUT*1000), + (TIS_SHORT_TIMEOUT*1000), (TIS_SHORT_TIMEOUT*1000) } }, +}; + +static bool tpm_tis_update_timeouts(struct tpm_chip *chip, + unsigned long *timeout_cap) +{ + int i; + u32 did_vid; + + did_vid = ioread32(chip->vendor.iobase + TPM_DID_VID(0)); + + for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) { + if (vendor_timeout_overrides[i].did_vid != did_vid) + continue; + memcpy(timeout_cap, vendor_timeout_overrides[i].timeout_us, + sizeof(vendor_timeout_overrides[i].timeout_us)); + return true; + } + + return false; +} + /* * Early probing for iTPM with STS_DATA_EXPECT flaw. * Try sending command without itpm flag set and if that @@ -437,6 +467,7 @@ static const struct tpm_class_ops tpm_tis = { .recv = tpm_tis_recv, .send = tpm_tis_send, .cancel = tpm_tis_ready, + .update_timeouts = tpm_tis_update_timeouts, .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, .req_canceled = tpm_tis_req_canceled, diff --git a/include/linux/tpm.h b/include/linux/tpm.h index fff1d0976f80..8350c538b486 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -39,6 +39,9 @@ struct tpm_class_ops { int (*send) (struct tpm_chip *chip, u8 *buf, size_t len); void (*cancel) (struct tpm_chip *chip); u8 (*status) (struct tpm_chip *chip); + bool (*update_timeouts)(struct tpm_chip *chip, + unsigned long *timeout_cap); + }; #if defined(CONFIG_TCG_TPM) || defined(CONFIG_TCG_TPM_MODULE) -- cgit v1.2.3 From 0213436a2cc5e4a5ca2fabfaa4d3877097f3b13f Mon Sep 17 00:00:00 2001 From: Janusz Dziemidowicz Date: Thu, 24 Jul 2014 15:48:46 +0200 Subject: scsi: do not issue SCSI RSOC command to Promise Vtrak E610f Some devices don't like REPORT SUPPORTED OPERATION CODES and will simply timeout causing sd_mod init to take a very very long time. Introduce BLIST_NO_RSOC scsi scan flag, that stops RSOC from being issued. Add it to Promise Vtrak E610f entry in scsi scan blacklist. Fixes bug #79901 reported at https://bugzilla.kernel.org/show_bug.cgi?id=79901 Fixes: 98dcc2946adb ("SCSI: sd: Update WRITE SAME heuristics") Signed-off-by: Janusz Dziemidowicz Reviewed-by: Martin K. Petersen Cc: stable@vger.kernel.org Signed-off-by: Christoph Hellwig --- drivers/scsi/scsi_devinfo.c | 1 + drivers/scsi/scsi_scan.c | 6 ++++++ include/scsi/scsi_devinfo.h | 2 ++ 3 files changed, 9 insertions(+) (limited to 'include') diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c index f969aca0b54e..49014a143c6a 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c @@ -222,6 +222,7 @@ static struct { {"PIONEER", "CD-ROM DRM-602X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, {"PIONEER", "CD-ROM DRM-604X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, {"PIONEER", "CD-ROM DRM-624X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, + {"Promise", "VTrak E610f", NULL, BLIST_SPARSELUN | BLIST_NO_RSOC}, {"Promise", "", NULL, BLIST_SPARSELUN}, {"QUANTUM", "XP34301", "1071", BLIST_NOTQ}, {"REGAL", "CDC-4X", NULL, BLIST_MAX5LUN | BLIST_SINGLELUN}, diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 50536cd6b3f2..56675dbbf681 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -900,6 +900,12 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, if (*bflags & BLIST_USE_10_BYTE_MS) sdev->use_10_for_ms = 1; + /* some devices don't like REPORT SUPPORTED OPERATION CODES + * and will simply timeout causing sd_mod init to take a very + * very long time */ + if (*bflags & BLIST_NO_RSOC) + sdev->no_report_opcodes = 1; + /* set the device running here so that slave configure * may do I/O */ ret = scsi_device_set_state(sdev, SDEV_RUNNING); diff --git a/include/scsi/scsi_devinfo.h b/include/scsi/scsi_devinfo.h index 1fdd6fc5492b..183eaab7c380 100644 --- a/include/scsi/scsi_devinfo.h +++ b/include/scsi/scsi_devinfo.h @@ -35,4 +35,6 @@ #define BLIST_SCSI3LUN 0x8000000 /* Scan more than 256 LUNs for sequential scan */ #define BLIST_TRY_VPD_PAGES 0x10000000 /* Attempt to read VPD pages */ +#define BLIST_NO_RSOC 0x20000000 /* don't try to issue RSOC */ + #endif -- cgit v1.2.3 From 728dba3a39c66b3d8ac889ddbe38b5b1c264aec3 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 3 Feb 2014 19:13:49 -0800 Subject: namespaces: Use task_lock and not rcu to protect nsproxy The synchronous syncrhonize_rcu in switch_task_namespaces makes setns a sufficiently expensive system call that people have complained. Upon inspect nsproxy no longer needs rcu protection for remote reads. remote reads are rare. So optimize for same process reads and write by switching using rask_lock instead. This yields a simpler to understand lock, and a faster setns system call. In particular this fixes a performance regression observed by Rafael David Tinoco . This is effectively a revert of Pavel Emelyanov's commit cf7b708c8d1d7a27736771bcf4c457b332b0f818 Make access to task's nsproxy lighter from 2007. The race this originialy fixed no longer exists as do_notify_parent uses task_active_pid_ns(parent) instead of parent->nsproxy. Signed-off-by: "Eric W. Biederman" --- fs/namespace.c | 6 +++--- fs/proc/proc_net.c | 4 +++- fs/proc_namespace.c | 8 +++----- include/linux/nsproxy.h | 16 ++++++---------- ipc/namespace.c | 6 +++--- kernel/nsproxy.c | 15 ++++----------- kernel/utsname.c | 6 +++--- net/core/net_namespace.c | 10 ++++++---- 8 files changed, 31 insertions(+), 40 deletions(-) (limited to 'include') diff --git a/fs/namespace.c b/fs/namespace.c index 182bc41cd887..7187d01329c3 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -2972,13 +2972,13 @@ static void *mntns_get(struct task_struct *task) struct mnt_namespace *ns = NULL; struct nsproxy *nsproxy; - rcu_read_lock(); - nsproxy = task_nsproxy(task); + task_lock(task); + nsproxy = task->nsproxy; if (nsproxy) { ns = nsproxy->mnt_ns; get_mnt_ns(ns); } - rcu_read_unlock(); + task_unlock(task); return ns; } diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c index 4677bb7dc7c2..a63af3e0a612 100644 --- a/fs/proc/proc_net.c +++ b/fs/proc/proc_net.c @@ -113,9 +113,11 @@ static struct net *get_proc_task_net(struct inode *dir) rcu_read_lock(); task = pid_task(proc_pid(dir), PIDTYPE_PID); if (task != NULL) { - ns = task_nsproxy(task); + task_lock(task); + ns = task->nsproxy; if (ns != NULL) net = get_net(ns->net_ns); + task_unlock(task); } rcu_read_unlock(); diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c index 1a81373947f3..73ca1740d839 100644 --- a/fs/proc_namespace.c +++ b/fs/proc_namespace.c @@ -232,17 +232,15 @@ static int mounts_open_common(struct inode *inode, struct file *file, if (!task) goto err; - rcu_read_lock(); - nsp = task_nsproxy(task); + task_lock(task); + nsp = task->nsproxy; if (!nsp || !nsp->mnt_ns) { - rcu_read_unlock(); + task_unlock(task); put_task_struct(task); goto err; } ns = nsp->mnt_ns; get_mnt_ns(ns); - rcu_read_unlock(); - task_lock(task); if (!task->fs) { task_unlock(task); put_task_struct(task); diff --git a/include/linux/nsproxy.h b/include/linux/nsproxy.h index b4ec59d159ac..35fa08fd7739 100644 --- a/include/linux/nsproxy.h +++ b/include/linux/nsproxy.h @@ -40,32 +40,28 @@ extern struct nsproxy init_nsproxy; * the namespaces access rules are: * * 1. only current task is allowed to change tsk->nsproxy pointer or - * any pointer on the nsproxy itself + * any pointer on the nsproxy itself. Current must hold the task_lock + * when changing tsk->nsproxy. * * 2. when accessing (i.e. reading) current task's namespaces - no * precautions should be taken - just dereference the pointers * * 3. the access to other task namespaces is performed like this - * rcu_read_lock(); - * nsproxy = task_nsproxy(tsk); + * task_lock(task); + * nsproxy = task->nsproxy; * if (nsproxy != NULL) { * / * * * work with the namespaces here * * e.g. get the reference on one of them * * / * } / * - * * NULL task_nsproxy() means that this task is + * * NULL task->nsproxy means that this task is * * almost dead (zombie) * * / - * rcu_read_unlock(); + * task_unlock(task); * */ -static inline struct nsproxy *task_nsproxy(struct task_struct *tsk) -{ - return rcu_dereference(tsk->nsproxy); -} - int copy_namespaces(unsigned long flags, struct task_struct *tsk); void exit_task_namespaces(struct task_struct *tsk); void switch_task_namespaces(struct task_struct *tsk, struct nsproxy *new); diff --git a/ipc/namespace.c b/ipc/namespace.c index 59451c1e214d..b54468e48e32 100644 --- a/ipc/namespace.c +++ b/ipc/namespace.c @@ -154,11 +154,11 @@ static void *ipcns_get(struct task_struct *task) struct ipc_namespace *ns = NULL; struct nsproxy *nsproxy; - rcu_read_lock(); - nsproxy = task_nsproxy(task); + task_lock(task); + nsproxy = task->nsproxy; if (nsproxy) ns = get_ipc_ns(nsproxy->ipc_ns); - rcu_read_unlock(); + task_unlock(task); return ns; } diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c index 8e7811086b82..ef42d0ab3115 100644 --- a/kernel/nsproxy.c +++ b/kernel/nsproxy.c @@ -204,20 +204,13 @@ void switch_task_namespaces(struct task_struct *p, struct nsproxy *new) might_sleep(); + task_lock(p); ns = p->nsproxy; + p->nsproxy = new; + task_unlock(p); - rcu_assign_pointer(p->nsproxy, new); - - if (ns && atomic_dec_and_test(&ns->count)) { - /* - * wait for others to get what they want from this nsproxy. - * - * cannot release this nsproxy via the call_rcu() since - * put_mnt_ns() will want to sleep - */ - synchronize_rcu(); + if (ns && atomic_dec_and_test(&ns->count)) free_nsproxy(ns); - } } void exit_task_namespaces(struct task_struct *p) diff --git a/kernel/utsname.c b/kernel/utsname.c index fd393124e507..883aaaa7de8a 100644 --- a/kernel/utsname.c +++ b/kernel/utsname.c @@ -93,13 +93,13 @@ static void *utsns_get(struct task_struct *task) struct uts_namespace *ns = NULL; struct nsproxy *nsproxy; - rcu_read_lock(); - nsproxy = task_nsproxy(task); + task_lock(task); + nsproxy = task->nsproxy; if (nsproxy) { ns = nsproxy->uts_ns; get_uts_ns(ns); } - rcu_read_unlock(); + task_unlock(task); return ns; } diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 85b62691f4f2..7c6b51a58968 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -373,9 +373,11 @@ struct net *get_net_ns_by_pid(pid_t pid) tsk = find_task_by_vpid(pid); if (tsk) { struct nsproxy *nsproxy; - nsproxy = task_nsproxy(tsk); + task_lock(tsk); + nsproxy = tsk->nsproxy; if (nsproxy) net = get_net(nsproxy->net_ns); + task_unlock(tsk); } rcu_read_unlock(); return net; @@ -632,11 +634,11 @@ static void *netns_get(struct task_struct *task) struct net *net = NULL; struct nsproxy *nsproxy; - rcu_read_lock(); - nsproxy = task_nsproxy(task); + task_lock(task); + nsproxy = task->nsproxy; if (nsproxy) net = get_net(nsproxy->net_ns); - rcu_read_unlock(); + task_unlock(task); return net; } -- cgit v1.2.3 From 4972a74b888c6b52ca41fae6076786dbbeb746d5 Mon Sep 17 00:00:00 2001 From: Laura Abbott Date: Tue, 15 Jul 2014 10:03:34 -0700 Subject: of: Split early_init_dt_scan into two parts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, early_init_dt_scan validates the header, sets the boot params, and scans for chosen/memory all in one function. Split this up into two separate functions (validation/setting boot params in one, scanning in another) to allow for additional setup between boot params and scanning the memory. Signed-off-by: Laura Abbott Tested-by: Andreas Färber [glikely: s/early_init_dt_scan_all/early_init_dt_scan_nodes/] Signed-off-by: Grant Likely --- drivers/of/fdt.c | 18 +++++++++++++++++- include/linux/of_fdt.h | 2 ++ 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index b777d8f46bd5..ecc7a02d868e 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -937,7 +937,7 @@ int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base, } #endif -bool __init early_init_dt_scan(void *params) +bool __init early_init_dt_verify(void *params) { if (!params) return false; @@ -951,6 +951,12 @@ bool __init early_init_dt_scan(void *params) return false; } + return true; +} + + +void __init early_init_dt_scan_nodes(void) +{ /* Retrieve various information from the /chosen node */ of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line); @@ -959,7 +965,17 @@ bool __init early_init_dt_scan(void *params) /* Setup memory, calling early_init_dt_add_memory_arch */ of_scan_flat_dt(early_init_dt_scan_memory, NULL); +} + +bool __init early_init_dt_scan(void *params) +{ + bool status; + + status = early_init_dt_verify(params); + if (!status) + return false; + early_init_dt_scan_nodes(); return true; } diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index 05117899fcb4..ebb2449082fb 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h @@ -73,6 +73,8 @@ extern int early_init_dt_scan_root(unsigned long node, const char *uname, int depth, void *data); extern bool early_init_dt_scan(void *params); +extern bool early_init_dt_verify(void *params); +extern void early_init_dt_scan_nodes(void); extern const char *of_flat_dt_get_machine_name(void); extern const void *of_flat_dt_match_machine(const void *default_match, -- cgit v1.2.3 From 704033cee2e5b3c1c6eaf5bb398e465a9c3667b5 Mon Sep 17 00:00:00 2001 From: Laura Abbott Date: Tue, 15 Jul 2014 10:03:35 -0700 Subject: of: Add memory limiting function for flattened devicetrees MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Buggy bootloaders may pass bogus memory entries in the devicetree. Add of_fdt_limit_memory to add an upper bound on the number of entries that can be present in the devicetree. Signed-off-by: Laura Abbott Tested-by: Andreas Färber Signed-off-by: Grant Likely --- drivers/of/fdt.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/of_fdt.h | 1 + 2 files changed, 49 insertions(+) (limited to 'include') diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index ecc7a02d868e..9aa012e6ea0a 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -26,6 +26,54 @@ #include /* for COMMAND_LINE_SIZE */ #include +/* + * of_fdt_limit_memory - limit the number of regions in the /memory node + * @limit: maximum entries + * + * Adjust the flattened device tree to have at most 'limit' number of + * memory entries in the /memory node. This function may be called + * any time after initial_boot_param is set. + */ +void of_fdt_limit_memory(int limit) +{ + int memory; + int len; + const void *val; + int nr_address_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT; + int nr_size_cells = OF_ROOT_NODE_SIZE_CELLS_DEFAULT; + const uint32_t *addr_prop; + const uint32_t *size_prop; + int root_offset; + int cell_size; + + root_offset = fdt_path_offset(initial_boot_params, "/"); + if (root_offset < 0) + return; + + addr_prop = fdt_getprop(initial_boot_params, root_offset, + "#address-cells", NULL); + if (addr_prop) + nr_address_cells = fdt32_to_cpu(*addr_prop); + + size_prop = fdt_getprop(initial_boot_params, root_offset, + "#size-cells", NULL); + if (size_prop) + nr_size_cells = fdt32_to_cpu(*size_prop); + + cell_size = sizeof(uint32_t)*(nr_address_cells + nr_size_cells); + + memory = fdt_path_offset(initial_boot_params, "/memory"); + if (memory > 0) { + val = fdt_getprop(initial_boot_params, memory, "reg", &len); + if (len > limit*cell_size) { + len = limit*cell_size; + pr_debug("Limiting number of entries to %d\n", limit); + fdt_setprop(initial_boot_params, memory, "reg", val, + len); + } + } +} + /** * of_fdt_is_compatible - Return true if given node from the given blob has * compat in its compatible list diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index ebb2449082fb..0ff360d5b3b3 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h @@ -86,6 +86,7 @@ extern void unflatten_and_copy_device_tree(void); extern void early_init_devtree(void *); extern void early_get_first_memblock_info(void *, phys_addr_t *); extern u64 fdt_translate_address(const void *blob, int node_offset); +extern void of_fdt_limit_memory(int limit); #else /* CONFIG_OF_FLATTREE */ static inline void early_init_fdt_scan_reserved_mem(void) {} static inline const char *of_flat_dt_get_machine_name(void) { return NULL; } -- cgit v1.2.3 From 953dec21aed4038464fec02f96a2f1b8701a5bce Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 25 Jul 2014 21:37:19 -0700 Subject: timekeeping: Fixup typo in update_vsyscall_old definition In commit 4a0e637738f0 ("clocksource: Get rid of cycle_last"), currently in the -tip tree, there was a small typo where cycles_t was used intstead of cycle_t. This broke ppc64 builds. Fix this by using the proper cycle_t type for this usage, in both the definition and the ia64 implementation. Now, having both cycle_t and cycles_t types seems like a very bad idea just asking for these sorts of issues. But that will be a cleanup for another day. Reported-by: Stephen Rothwell Signed-off-by: John Stultz Cc: Ingo Molnar Cc: "H. Peter Anvin" Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1406349439-11785-1-git-send-email-john.stultz@linaro.org Signed-off-by: Thomas Gleixner --- arch/ia64/kernel/time.c | 2 +- include/linux/timekeeper_internal.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c index 11dc42da7daf..3e71ef85e439 100644 --- a/arch/ia64/kernel/time.c +++ b/arch/ia64/kernel/time.c @@ -441,7 +441,7 @@ void update_vsyscall_tz(void) } void update_vsyscall_old(struct timespec *wall, struct timespec *wtm, - struct clocksource *c, u32 mult, cycles_t cycle_last) + struct clocksource *c, u32 mult, cycle_t cycle_last) { write_seqcount_begin(&fsyscall_gtod_data.seq); diff --git a/include/linux/timekeeper_internal.h b/include/linux/timekeeper_internal.h index e9660e52dc09..95640dcd1899 100644 --- a/include/linux/timekeeper_internal.h +++ b/include/linux/timekeeper_internal.h @@ -113,7 +113,7 @@ extern void update_vsyscall_tz(void); extern void update_vsyscall_old(struct timespec *ts, struct timespec *wtm, struct clocksource *c, u32 mult, - cycles_t cycle_last); + cycle_t cycle_last); extern void update_vsyscall_tz(void); #else -- cgit v1.2.3 From b7dd0e350e0bd4c0fddcc9b8958342700b00b168 Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Fri, 11 Jul 2014 16:42:34 +0100 Subject: x86/xen: safely map and unmap grant frames when in atomic context arch_gnttab_map_frames() and arch_gnttab_unmap_frames() are called in atomic context but were calling alloc_vm_area() which might sleep. Also, if a driver attempts to allocate a grant ref from an interrupt and the table needs expanding, then the CPU may already by in lazy MMU mode and apply_to_page_range() will BUG when it tries to re-enable lazy MMU mode. These two functions are only used in PV guests. Introduce arch_gnttab_init() to allocates the virtual address space in advance. Avoid the use of apply_to_page_range() by using saving and using the array of PTE addresses from the alloc_vm_area() call (which ensures that the required page tables are pre-allocated). Signed-off-by: David Vrabel Signed-off-by: Konrad Rzeszutek Wilk --- arch/arm/xen/grant-table.c | 5 ++ arch/x86/xen/grant-table.c | 148 ++++++++++++++++++++++++++++----------------- drivers/xen/grant-table.c | 9 ++- include/xen/grant_table.h | 1 + 4 files changed, 105 insertions(+), 58 deletions(-) (limited to 'include') diff --git a/arch/arm/xen/grant-table.c b/arch/arm/xen/grant-table.c index 859a9bb002d5..91cf08ba1e95 100644 --- a/arch/arm/xen/grant-table.c +++ b/arch/arm/xen/grant-table.c @@ -51,3 +51,8 @@ int arch_gnttab_map_status(uint64_t *frames, unsigned long nr_gframes, { return -ENOSYS; } + +int arch_gnttab_init(unsigned long nr_shared, unsigned long nr_status) +{ + return 0; +} diff --git a/arch/x86/xen/grant-table.c b/arch/x86/xen/grant-table.c index c98583588580..ebfa9b2c871d 100644 --- a/arch/x86/xen/grant-table.c +++ b/arch/x86/xen/grant-table.c @@ -36,99 +36,133 @@ #include #include +#include #include #include #include #include +#include #include -static int map_pte_fn(pte_t *pte, struct page *pmd_page, - unsigned long addr, void *data) +static struct gnttab_vm_area { + struct vm_struct *area; + pte_t **ptes; +} gnttab_shared_vm_area, gnttab_status_vm_area; + +int arch_gnttab_map_shared(unsigned long *frames, unsigned long nr_gframes, + unsigned long max_nr_gframes, + void **__shared) { - unsigned long **frames = (unsigned long **)data; + void *shared = *__shared; + unsigned long addr; + unsigned long i; - set_pte_at(&init_mm, addr, pte, mfn_pte((*frames)[0], PAGE_KERNEL)); - (*frames)++; - return 0; -} + if (shared == NULL) + *__shared = shared = gnttab_shared_vm_area.area->addr; -/* - * This function is used to map shared frames to store grant status. It is - * different from map_pte_fn above, the frames type here is uint64_t. - */ -static int map_pte_fn_status(pte_t *pte, struct page *pmd_page, - unsigned long addr, void *data) -{ - uint64_t **frames = (uint64_t **)data; + addr = (unsigned long)shared; + + for (i = 0; i < nr_gframes; i++) { + set_pte_at(&init_mm, addr, gnttab_shared_vm_area.ptes[i], + mfn_pte(frames[i], PAGE_KERNEL)); + addr += PAGE_SIZE; + } - set_pte_at(&init_mm, addr, pte, mfn_pte((*frames)[0], PAGE_KERNEL)); - (*frames)++; return 0; } -static int unmap_pte_fn(pte_t *pte, struct page *pmd_page, - unsigned long addr, void *data) +int arch_gnttab_map_status(uint64_t *frames, unsigned long nr_gframes, + unsigned long max_nr_gframes, + grant_status_t **__shared) { + grant_status_t *shared = *__shared; + unsigned long addr; + unsigned long i; + + if (shared == NULL) + *__shared = shared = gnttab_status_vm_area.area->addr; + + addr = (unsigned long)shared; + + for (i = 0; i < nr_gframes; i++) { + set_pte_at(&init_mm, addr, gnttab_status_vm_area.ptes[i], + mfn_pte(frames[i], PAGE_KERNEL)); + addr += PAGE_SIZE; + } - set_pte_at(&init_mm, addr, pte, __pte(0)); return 0; } -int arch_gnttab_map_shared(unsigned long *frames, unsigned long nr_gframes, - unsigned long max_nr_gframes, - void **__shared) +void arch_gnttab_unmap(void *shared, unsigned long nr_gframes) { - int rc; - void *shared = *__shared; + pte_t **ptes; + unsigned long addr; + unsigned long i; - if (shared == NULL) { - struct vm_struct *area = - alloc_vm_area(PAGE_SIZE * max_nr_gframes, NULL); - BUG_ON(area == NULL); - shared = area->addr; - *__shared = shared; - } + if (shared == gnttab_status_vm_area.area->addr) + ptes = gnttab_status_vm_area.ptes; + else + ptes = gnttab_shared_vm_area.ptes; - rc = apply_to_page_range(&init_mm, (unsigned long)shared, - PAGE_SIZE * nr_gframes, - map_pte_fn, &frames); - return rc; + addr = (unsigned long)shared; + + for (i = 0; i < nr_gframes; i++) { + set_pte_at(&init_mm, addr, ptes[i], __pte(0)); + addr += PAGE_SIZE; + } } -int arch_gnttab_map_status(uint64_t *frames, unsigned long nr_gframes, - unsigned long max_nr_gframes, - grant_status_t **__shared) +static int arch_gnttab_valloc(struct gnttab_vm_area *area, unsigned nr_frames) { - int rc; - grant_status_t *shared = *__shared; + area->ptes = kmalloc(sizeof(pte_t *) * nr_frames, GFP_KERNEL); + if (area->ptes == NULL) + return -ENOMEM; - if (shared == NULL) { - /* No need to pass in PTE as we are going to do it - * in apply_to_page_range anyhow. */ - struct vm_struct *area = - alloc_vm_area(PAGE_SIZE * max_nr_gframes, NULL); - BUG_ON(area == NULL); - shared = area->addr; - *__shared = shared; + area->area = alloc_vm_area(PAGE_SIZE * nr_frames, area->ptes); + if (area->area == NULL) { + kfree(area->ptes); + return -ENOMEM; } - rc = apply_to_page_range(&init_mm, (unsigned long)shared, - PAGE_SIZE * nr_gframes, - map_pte_fn_status, &frames); - return rc; + return 0; } -void arch_gnttab_unmap(void *shared, unsigned long nr_gframes) +static void arch_gnttab_vfree(struct gnttab_vm_area *area) +{ + free_vm_area(area->area); + kfree(area->ptes); +} + +int arch_gnttab_init(unsigned long nr_shared, unsigned long nr_status) { - apply_to_page_range(&init_mm, (unsigned long)shared, - PAGE_SIZE * nr_gframes, unmap_pte_fn, NULL); + int ret; + + if (!xen_pv_domain()) + return 0; + + ret = arch_gnttab_valloc(&gnttab_shared_vm_area, nr_shared); + if (ret < 0) + return ret; + + /* + * Always allocate the space for the status frames in case + * we're migrated to a host with V2 support. + */ + ret = arch_gnttab_valloc(&gnttab_status_vm_area, nr_status); + if (ret < 0) + goto err; + + return 0; + err: + arch_gnttab_vfree(&gnttab_shared_vm_area); + return -ENOMEM; } + #ifdef CONFIG_XEN_PVH #include #include -#include #include static int __init xlated_setup_gnttab_pages(void) { diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index 5d4de88fe5b8..eeba7544f0cd 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c @@ -1195,18 +1195,20 @@ static int gnttab_expand(unsigned int req_entries) int gnttab_init(void) { int i; + unsigned long max_nr_grant_frames; unsigned int max_nr_glist_frames, nr_glist_frames; unsigned int nr_init_grefs; int ret; gnttab_request_version(); + max_nr_grant_frames = gnttab_max_grant_frames(); nr_grant_frames = 1; /* Determine the maximum number of frames required for the * grant reference free list on the current hypervisor. */ BUG_ON(grefs_per_grant_frame == 0); - max_nr_glist_frames = (gnttab_max_grant_frames() * + max_nr_glist_frames = (max_nr_grant_frames * grefs_per_grant_frame / RPP); gnttab_list = kmalloc(max_nr_glist_frames * sizeof(grant_ref_t *), @@ -1223,6 +1225,11 @@ int gnttab_init(void) } } + ret = arch_gnttab_init(max_nr_grant_frames, + nr_status_frames(max_nr_grant_frames)); + if (ret < 0) + goto ini_nomem; + if (gnttab_setup() < 0) { ret = -ENODEV; goto ini_nomem; diff --git a/include/xen/grant_table.h b/include/xen/grant_table.h index a5af2a26d94f..5c1aba154b64 100644 --- a/include/xen/grant_table.h +++ b/include/xen/grant_table.h @@ -170,6 +170,7 @@ gnttab_set_unmap_op(struct gnttab_unmap_grant_ref *unmap, phys_addr_t addr, unmap->dev_bus_addr = 0; } +int arch_gnttab_init(unsigned long nr_shared, unsigned long nr_status); int arch_gnttab_map_shared(xen_pfn_t *frames, unsigned long nr_gframes, unsigned long max_nr_gframes, void **__shared); -- cgit v1.2.3 From 1d8fcba1de632d7a43349788ad534c5a32c5a44c Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 30 Jul 2014 08:56:23 -0700 Subject: Revert "cdc_subset: deal with a device that needs reset for timeout" This reverts commit 20fbe3ae990fd54fc7d1f889d61958bc8b38f254. As reported by Stephen Rothwell, it causes compile failures in certain configurations: drivers/net/usb/cdc_subset.c:360:15: error: 'dummy_prereset' undeclared here (not in a function) .pre_reset = dummy_prereset, ^ drivers/net/usb/cdc_subset.c:361:16: error: 'dummy_postreset' undeclared here (not in a function) .post_reset = dummy_postreset, ^ Reported-by: Stephen Rothwell Acked-by: David Miller Cc: Oliver Neukum Signed-off-by: Linus Torvalds --- drivers/net/usb/cdc_subset.c | 27 --------------------------- drivers/net/usb/usbnet.c | 8 ++------ include/linux/usb/usbnet.h | 3 --- 3 files changed, 2 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/drivers/net/usb/cdc_subset.c b/drivers/net/usb/cdc_subset.c index 3ef411efd86e..91f0919fe278 100644 --- a/drivers/net/usb/cdc_subset.c +++ b/drivers/net/usb/cdc_subset.c @@ -85,34 +85,9 @@ static int always_connected (struct usbnet *dev) * *-------------------------------------------------------------------------*/ -static void m5632_recover(struct usbnet *dev) -{ - struct usb_device *udev = dev->udev; - struct usb_interface *intf = dev->intf; - int r; - - r = usb_lock_device_for_reset(udev, intf); - if (r < 0) - return; - - usb_reset_device(udev); - usb_unlock_device(udev); -} - -static int dummy_prereset(struct usb_interface *intf) -{ - return 0; -} - -static int dummy_postreset(struct usb_interface *intf) -{ - return 0; -} - static const struct driver_info ali_m5632_info = { .description = "ALi M5632", .flags = FLAG_POINTTOPOINT, - .recover = m5632_recover, }; #endif @@ -357,8 +332,6 @@ static struct usb_driver cdc_subset_driver = { .probe = usbnet_probe, .suspend = usbnet_suspend, .resume = usbnet_resume, - .pre_reset = dummy_prereset, - .post_reset = dummy_postreset, .disconnect = usbnet_disconnect, .id_table = products, .disable_hub_initiated_lpm = 1, diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 5173821a9575..f9e96c427558 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -1218,12 +1218,8 @@ void usbnet_tx_timeout (struct net_device *net) unlink_urbs (dev, &dev->txq); tasklet_schedule (&dev->bh); - /* this needs to be handled individually because the generic layer - * doesn't know what is sufficient and could not restore private - * information if a remedy of an unconditional reset were used. - */ - if (dev->driver_info->recover) - (dev->driver_info->recover)(dev); + + // FIXME: device recovery -- reset? } EXPORT_SYMBOL_GPL(usbnet_tx_timeout); diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h index 26088feb6608..0662e98fef72 100644 --- a/include/linux/usb/usbnet.h +++ b/include/linux/usb/usbnet.h @@ -148,9 +148,6 @@ struct driver_info { struct sk_buff *(*tx_fixup)(struct usbnet *dev, struct sk_buff *skb, gfp_t flags); - /* recover from timeout */ - void (*recover)(struct usbnet *dev); - /* early initialization code, can sleep. This is for minidrivers * having 'subminidrivers' that need to do extra initialization * right after minidriver have initialized hardware. */ -- cgit v1.2.3 From 556a5bfc03c35c6f0b4e85ef6a19d00f0eb6dd00 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Tue, 29 Jul 2014 23:47:02 +0200 Subject: 6lowpan: iphc: use ipv6 api to check address scope This patch removes the own implementation to check of link-layer, broadcast and any address type and use the IPv6 api for that. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann --- include/net/6lowpan.h | 13 ------------- net/6lowpan/iphc.c | 29 +++++++++++++++++------------ 2 files changed, 17 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h index 79b530fb2c4d..18010bce68c8 100644 --- a/include/net/6lowpan.h +++ b/include/net/6lowpan.h @@ -75,20 +75,10 @@ (((a)->s6_addr[14]) == (m)[6]) && \ (((a)->s6_addr[15]) == (m)[7])) -/* ipv6 address is unspecified */ -#define is_addr_unspecified(a) \ - ((((a)->s6_addr32[0]) == 0) && \ - (((a)->s6_addr32[1]) == 0) && \ - (((a)->s6_addr32[2]) == 0) && \ - (((a)->s6_addr32[3]) == 0)) - /* compare ipv6 addresses prefixes */ #define ipaddr_prefixcmp(addr1, addr2, length) \ (memcmp(addr1, addr2, length >> 3) == 0) -/* local link, i.e. FE80::/10 */ -#define is_addr_link_local(a) (((a)->s6_addr16[0]) == htons(0xFE80)) - /* * check whether we can compress the IID to 16 bits, * it's possible for unicast adresses with first 49 bits are zero only. @@ -100,9 +90,6 @@ (((a)->s6_addr[12]) == 0xfe) && \ (((a)->s6_addr[13]) == 0)) -/* multicast address */ -#define is_addr_mcast(a) (((a)->s6_addr[0]) == 0xFF) - /* check whether the 112-bit gid of the multicast address is mappable to: */ /* 9 bits, for FF02::1 (all nodes) and FF02::2 (all routers) addresses only. */ diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c index 636edd0f3724..d4fc2dd8ad75 100644 --- a/net/6lowpan/iphc.c +++ b/net/6lowpan/iphc.c @@ -611,6 +611,7 @@ int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev, u8 tmp, iphc0, iphc1, *hc_ptr; struct ipv6hdr *hdr; u8 head[100] = {}; + int addr_type; if (type != ETH_P_IPV6) return -EINVAL; @@ -720,23 +721,27 @@ int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev, sizeof(hdr->hop_limit)); } + addr_type = ipv6_addr_type(&hdr->saddr); /* source address compression */ - if (is_addr_unspecified(&hdr->saddr)) { + if (addr_type == IPV6_ADDR_ANY) { pr_debug("source address is unspecified, setting SAC\n"); iphc1 |= LOWPAN_IPHC_SAC; - /* TODO: context lookup */ - } else if (is_addr_link_local(&hdr->saddr)) { - iphc1 |= lowpan_compress_addr_64(&hc_ptr, - LOWPAN_IPHC_SAM_BIT, &hdr->saddr, _saddr); - pr_debug("source address unicast link-local %pI6c " - "iphc1 0x%02x\n", &hdr->saddr, iphc1); } else { - pr_debug("send the full source address\n"); - lowpan_push_hc_data(&hc_ptr, &hdr->saddr.s6_addr[0], 16); + if (addr_type & IPV6_ADDR_LINKLOCAL) { + iphc1 |= lowpan_compress_addr_64(&hc_ptr, + LOWPAN_IPHC_SAM_BIT, + &hdr->saddr, _saddr); + pr_debug("source address unicast link-local %pI6c iphc1 0x%02x\n", + &hdr->saddr, iphc1); + } else { + pr_debug("send the full source address\n"); + lowpan_push_hc_data(&hc_ptr, hdr->saddr.s6_addr, 16); + } } + addr_type = ipv6_addr_type(&hdr->daddr); /* destination address compression */ - if (is_addr_mcast(&hdr->daddr)) { + if (addr_type & IPV6_ADDR_MULTICAST) { pr_debug("destination address is multicast: "); iphc1 |= LOWPAN_IPHC_M; if (lowpan_is_mcast_addr_compressable8(&hdr->daddr)) { @@ -767,8 +772,8 @@ int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev, lowpan_push_hc_data(&hc_ptr, hdr->daddr.s6_addr, 16); } } else { - /* TODO: context lookup */ - if (is_addr_link_local(&hdr->daddr)) { + if (addr_type & IPV6_ADDR_LINKLOCAL) { + /* TODO: context lookup */ iphc1 |= lowpan_compress_addr_64(&hc_ptr, LOWPAN_IPHC_DAM_BIT, &hdr->daddr, _daddr); pr_debug("dest address unicast link-local %pI6c " -- cgit v1.2.3 From 004942445da78b6fd250dc933c5d7859fde8fb1f Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Tue, 29 Jul 2014 23:47:04 +0200 Subject: 6lowpan: remove unused LOWPAN_FRAG_SIZE define This define is unused since commit 96cb3eb7a1a5f0c3598500a2348f7d2cc76afbd2 ("6lowpan: fix fragmentation on sending side"). It is a worst case scenario for payload calculation. Since commit 96cb3eb7a1a5f0c3598500a2348f7d2cc76afbd2 we calculation the payload to use the optimal size. This define is also necessary for ieee802154 6lowpan only and the file include/net/6lowpan.h should contain generic 6lowpan things only. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann --- include/net/6lowpan.h | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'include') diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h index 18010bce68c8..3bb3503b7ff4 100644 --- a/include/net/6lowpan.h +++ b/include/net/6lowpan.h @@ -154,17 +154,6 @@ #define LOWPAN_FRAG1_HEAD_SIZE 0x4 #define LOWPAN_FRAGN_HEAD_SIZE 0x5 -/* - * According IEEE802.15.4 standard: - * - MTU is 127 octets - * - maximum MHR size is 37 octets - * - MFR size is 2 octets - * - * so minimal payload size that we may guarantee is: - * MTU - MHR - MFR = 88 octets - */ -#define LOWPAN_FRAG_SIZE 88 - /* * Values of fields within the IPHC encoding first byte * (C stands for compressed and I for inline) -- cgit v1.2.3 From 267ca9fefca70817ca3283f5ae564ee385e742b6 Mon Sep 17 00:00:00 2001 From: Varka Bhadram Date: Wed, 30 Jul 2014 11:05:11 +0530 Subject: 6lowpan: remove unused macros This patch removes the unused macros. Signed-off-by: Varka Bhadram Signed-off-by: Marcel Holtmann --- include/net/6lowpan.h | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'include') diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h index 3bb3503b7ff4..614920a39bf2 100644 --- a/include/net/6lowpan.h +++ b/include/net/6lowpan.h @@ -75,10 +75,6 @@ (((a)->s6_addr[14]) == (m)[6]) && \ (((a)->s6_addr[15]) == (m)[7])) -/* compare ipv6 addresses prefixes */ -#define ipaddr_prefixcmp(addr1, addr2, length) \ - (memcmp(addr1, addr2, length >> 3) == 0) - /* * check whether we can compress the IID to 16 bits, * it's possible for unicast adresses with first 49 bits are zero only. @@ -92,17 +88,6 @@ /* check whether the 112-bit gid of the multicast address is mappable to: */ -/* 9 bits, for FF02::1 (all nodes) and FF02::2 (all routers) addresses only. */ -#define lowpan_is_mcast_addr_compressable(a) \ - ((((a)->s6_addr16[1]) == 0) && \ - (((a)->s6_addr16[2]) == 0) && \ - (((a)->s6_addr16[3]) == 0) && \ - (((a)->s6_addr16[4]) == 0) && \ - (((a)->s6_addr16[5]) == 0) && \ - (((a)->s6_addr16[6]) == 0) && \ - (((a)->s6_addr[14]) == 0) && \ - ((((a)->s6_addr[15]) == 1) || (((a)->s6_addr[15]) == 2))) - /* 48 bits, FFXX::00XX:XXXX:XXXX */ #define lowpan_is_mcast_addr_compressable48(a) \ ((((a)->s6_addr16[1]) == 0) && \ -- cgit v1.2.3 From 233351bd66f1fadc4a69f350a9a4422c2e3d308d Mon Sep 17 00:00:00 2001 From: Varka Bhadram Date: Wed, 30 Jul 2014 11:05:12 +0530 Subject: 6lowpan: remove unused function This patch removes the unused function. Signed-off-by: Varka Bhadram Signed-off-by: Marcel Holtmann --- include/net/6lowpan.h | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'include') diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h index 614920a39bf2..d184df1d0d41 100644 --- a/include/net/6lowpan.h +++ b/include/net/6lowpan.h @@ -240,17 +240,6 @@ static inline int lowpan_fetch_skb_u8(struct sk_buff *skb, u8 *val) return 0; } -static inline int lowpan_fetch_skb_u16(struct sk_buff *skb, u16 *val) -{ - if (unlikely(!pskb_may_pull(skb, 2))) - return -EINVAL; - - *val = (skb->data[0] << 8) | skb->data[1]; - skb_pull(skb, 2); - - return 0; -} - static inline bool lowpan_fetch_skb(struct sk_buff *skb, void *data, const unsigned int len) { -- cgit v1.2.3 From b6ae8457ac5c727a2bb85eb8f2e22375d44d2b2d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 30 Jul 2014 09:22:22 +0300 Subject: Bluetooth: Rename HCI_PAIRABLE to HCI_BONDABLE The HCI_PAIRABLE flag isn't actually controlling whether we're pairable but whether we're bondable. Therefore, rename it accordingly. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 2 +- net/bluetooth/hci_core.c | 4 ++-- net/bluetooth/hci_event.c | 4 ++-- net/bluetooth/mgmt.c | 8 ++++---- net/bluetooth/smp.c | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index e3fd926df13f..3f8547f1c6f8 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -167,7 +167,7 @@ enum { HCI_AUTO_OFF, HCI_RFKILLED, HCI_MGMT, - HCI_PAIRABLE, + HCI_BONDABLE, HCI_SERVICE_CACHE, HCI_KEEP_DEBUG_KEYS, HCI_USE_DEBUG_KEYS, diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 61bd1a8c5849..32b96f1aaf42 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2521,14 +2521,14 @@ int hci_dev_open(__u16 dev) flush_workqueue(hdev->req_workqueue); /* For controllers not using the management interface and that - * are brought up using legacy ioctl, set the HCI_PAIRABLE bit + * are brought up using legacy ioctl, set the HCI_BONDABLE bit * so that pairing works for them. Once the management interface * is in use this bit will be cleared again and userspace has * to explicitly enable it. */ if (!test_bit(HCI_USER_CHANNEL, &hdev->dev_flags) && !test_bit(HCI_MGMT, &hdev->dev_flags)) - set_bit(HCI_PAIRABLE, &hdev->dev_flags); + set_bit(HCI_BONDABLE, &hdev->dev_flags); err = hci_dev_do_open(hdev); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 623501ddd1b8..c43cee4fcffd 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3119,7 +3119,7 @@ static void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_conn_drop(conn); } - if (!test_bit(HCI_PAIRABLE, &hdev->dev_flags) && + if (!test_bit(HCI_BONDABLE, &hdev->dev_flags) && !test_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags)) { hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, sizeof(ev->bdaddr), &ev->bdaddr); @@ -3652,7 +3652,7 @@ static void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb) /* Allow pairing if we're pairable, the initiators of the * pairing or if the remote is not requesting bonding. */ - if (test_bit(HCI_PAIRABLE, &hdev->dev_flags) || + if (test_bit(HCI_BONDABLE, &hdev->dev_flags) || test_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags) || (conn->remote_auth & ~0x01) == HCI_AT_NO_BONDING) { struct hci_cp_io_capability_reply cp; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 0b15b7618beb..edb1a62054c9 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -603,7 +603,7 @@ static u32 get_current_settings(struct hci_dev *hdev) if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) settings |= MGMT_SETTING_DISCOVERABLE; - if (test_bit(HCI_PAIRABLE, &hdev->dev_flags)) + if (test_bit(HCI_BONDABLE, &hdev->dev_flags)) settings |= MGMT_SETTING_PAIRABLE; if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) @@ -1152,7 +1152,7 @@ static void mgmt_init_hdev(struct sock *sk, struct hci_dev *hdev) * for mgmt we require user-space to explicitly enable * it */ - clear_bit(HCI_PAIRABLE, &hdev->dev_flags); + clear_bit(HCI_BONDABLE, &hdev->dev_flags); } static int read_controller_info(struct sock *sk, struct hci_dev *hdev, @@ -1946,9 +1946,9 @@ static int set_pairable(struct sock *sk, struct hci_dev *hdev, void *data, hci_dev_lock(hdev); if (cp->val) - changed = !test_and_set_bit(HCI_PAIRABLE, &hdev->dev_flags); + changed = !test_and_set_bit(HCI_BONDABLE, &hdev->dev_flags); else - changed = test_and_clear_bit(HCI_PAIRABLE, &hdev->dev_flags); + changed = test_and_clear_bit(HCI_BONDABLE, &hdev->dev_flags); err = send_settings_rsp(sk, MGMT_OP_SET_PAIRABLE, hdev); if (err < 0) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index eaa8e7482bb4..fd3294300803 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -307,7 +307,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn, struct hci_dev *hdev = hcon->hdev; u8 local_dist = 0, remote_dist = 0; - if (test_bit(HCI_PAIRABLE, &conn->hcon->hdev->dev_flags)) { + if (test_bit(HCI_BONDABLE, &conn->hcon->hdev->dev_flags)) { local_dist = SMP_DIST_ENC_KEY | SMP_DIST_SIGN; remote_dist = SMP_DIST_ENC_KEY | SMP_DIST_SIGN; authreq |= SMP_AUTH_BONDING; @@ -704,7 +704,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) if (!smp) return SMP_UNSPECIFIED; - if (!test_bit(HCI_PAIRABLE, &hdev->dev_flags) && + if (!test_bit(HCI_BONDABLE, &hdev->dev_flags) && (req->auth_req & SMP_AUTH_BONDING)) return SMP_PAIRING_NOTSUPP; @@ -930,7 +930,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) if (!smp) return SMP_UNSPECIFIED; - if (!test_bit(HCI_PAIRABLE, &hcon->hdev->dev_flags) && + if (!test_bit(HCI_BONDABLE, &hcon->hdev->dev_flags) && (rp->auth_req & SMP_AUTH_BONDING)) return SMP_PAIRING_NOTSUPP; -- cgit v1.2.3 From b2939475eb6a3575fe542c06f3f879b93d48ae1b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 30 Jul 2014 09:22:23 +0300 Subject: Bluetooth: Rename pairable mgmt setting to bondable This setting maps to the HCI_BONDABLE flag which tracks whether we're bondable or not. Therefore, rename the mgmt setting and respective command accordingly. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 4 ++-- net/bluetooth/mgmt.c | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 623d5203c592..414cd2f9a437 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -87,7 +87,7 @@ struct mgmt_rp_read_index_list { #define MGMT_SETTING_CONNECTABLE 0x00000002 #define MGMT_SETTING_FAST_CONNECTABLE 0x00000004 #define MGMT_SETTING_DISCOVERABLE 0x00000008 -#define MGMT_SETTING_PAIRABLE 0x00000010 +#define MGMT_SETTING_BONDABLE 0x00000010 #define MGMT_SETTING_LINK_SECURITY 0x00000020 #define MGMT_SETTING_SSP 0x00000040 #define MGMT_SETTING_BREDR 0x00000080 @@ -131,7 +131,7 @@ struct mgmt_cp_set_discoverable { #define MGMT_OP_SET_FAST_CONNECTABLE 0x0008 -#define MGMT_OP_SET_PAIRABLE 0x0009 +#define MGMT_OP_SET_BONDABLE 0x0009 #define MGMT_OP_SET_LINK_SECURITY 0x000A diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index edb1a62054c9..b8554d429d88 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -44,7 +44,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_SET_DISCOVERABLE, MGMT_OP_SET_CONNECTABLE, MGMT_OP_SET_FAST_CONNECTABLE, - MGMT_OP_SET_PAIRABLE, + MGMT_OP_SET_BONDABLE, MGMT_OP_SET_LINK_SECURITY, MGMT_OP_SET_SSP, MGMT_OP_SET_HS, @@ -553,7 +553,7 @@ static u32 get_supported_settings(struct hci_dev *hdev) u32 settings = 0; settings |= MGMT_SETTING_POWERED; - settings |= MGMT_SETTING_PAIRABLE; + settings |= MGMT_SETTING_BONDABLE; settings |= MGMT_SETTING_DEBUG_KEYS; settings |= MGMT_SETTING_CONNECTABLE; settings |= MGMT_SETTING_DISCOVERABLE; @@ -604,7 +604,7 @@ static u32 get_current_settings(struct hci_dev *hdev) settings |= MGMT_SETTING_DISCOVERABLE; if (test_bit(HCI_BONDABLE, &hdev->dev_flags)) - settings |= MGMT_SETTING_PAIRABLE; + settings |= MGMT_SETTING_BONDABLE; if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) settings |= MGMT_SETTING_BREDR; @@ -1930,7 +1930,7 @@ failed: return err; } -static int set_pairable(struct sock *sk, struct hci_dev *hdev, void *data, +static int set_bondable(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_mode *cp = data; @@ -1940,7 +1940,7 @@ static int set_pairable(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("request for %s", hdev->name); if (cp->val != 0x00 && cp->val != 0x01) - return cmd_status(sk, hdev->id, MGMT_OP_SET_PAIRABLE, + return cmd_status(sk, hdev->id, MGMT_OP_SET_BONDABLE, MGMT_STATUS_INVALID_PARAMS); hci_dev_lock(hdev); @@ -1950,7 +1950,7 @@ static int set_pairable(struct sock *sk, struct hci_dev *hdev, void *data, else changed = test_and_clear_bit(HCI_BONDABLE, &hdev->dev_flags); - err = send_settings_rsp(sk, MGMT_OP_SET_PAIRABLE, hdev); + err = send_settings_rsp(sk, MGMT_OP_SET_BONDABLE, hdev); if (err < 0) goto unlock; @@ -5679,7 +5679,7 @@ static const struct mgmt_handler { { set_discoverable, false, MGMT_SET_DISCOVERABLE_SIZE }, { set_connectable, false, MGMT_SETTING_SIZE }, { set_fast_connectable, false, MGMT_SETTING_SIZE }, - { set_pairable, false, MGMT_SETTING_SIZE }, + { set_bondable, false, MGMT_SETTING_SIZE }, { set_link_security, false, MGMT_SETTING_SIZE }, { set_ssp, false, MGMT_SETTING_SIZE }, { set_hs, false, MGMT_SETTING_SIZE }, -- cgit v1.2.3 From 5ccb8225abf2ac51cd023a99f28366ac9823bd0d Mon Sep 17 00:00:00 2001 From: Mike Qiu Date: Tue, 29 Jul 2014 10:49:25 -0700 Subject: x86/ras: Fix build warnings in MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix build warning due to a missing forward declaration in . We need struct pci_dev to be forward declared so we can define pointers to it, but we don't need to pull in the whole definition. build log: In file included from include/ras/ras_event.h:11:0, from drivers/ras/ras.c:13: include/linux/aer.h:42:129: warning: ‘struct pci_dev’ declared inside parameter list [enabled by default] include/linux/aer.h:42:129: warning: its scope is only this definition or declaration, which is probably not what you want [enabled by default] include/linux/aer.h:46:130: warning: ‘struct pci_dev’ declared inside parameter list [enabled by default] include/linux/aer.h:50:136: warning: ‘struct pci_dev’ declared inside parameter list [enabled by default] include/linux/aer.h:57:14: warning: ‘struct pci_dev’ declared inside parameter list [enabled by default] Signed-off-by: Mike Qiu Link: http://lkml.kernel.org/r/53d7dea511471321bb@agluck-desk.sc.intel.com Acked-by: Randy Dunlap Tested-by: Randy Dunlap Acked-by: Bjorn Helgaas Signed-off-by: Tony Luck Signed-off-by: H. Peter Anvin --- include/linux/aer.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/aer.h b/include/linux/aer.h index 4dbaa7081530..c826d1c28f9c 100644 --- a/include/linux/aer.h +++ b/include/linux/aer.h @@ -11,6 +11,8 @@ #define AER_FATAL 1 #define AER_CORRECTABLE 2 +struct pci_dev; + struct aer_header_log_regs { unsigned int dw0; unsigned int dw1; -- cgit v1.2.3 From 725c7b570fda4207e465ff8856c2c12c2645a685 Mon Sep 17 00:00:00 2001 From: Antoine Ténart Date: Wed, 30 Jul 2014 20:13:56 +0200 Subject: ata: libahci_platform: move port_map parameters into the AHCI structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch moves force_port_map and mask_port_map into the ahci_host_priv structure. This allows to modify them into the AHCI framework. This is needed by the new dt bindings representing ports as the port_map mask is computed automatically. Parameters modifying force_port_map, mask_port_map and flags have been removed from the ahci_platform_init_host() function, and inputs in the ahci_host_priv structure are now directly filed. Signed-off-by: Antoine Ténart Signed-off-by: Tejun Heo --- drivers/ata/acard-ahci.c | 2 +- drivers/ata/ahci.c | 3 +-- drivers/ata/ahci.h | 10 ++++++---- drivers/ata/ahci_da850.c | 3 +-- drivers/ata/ahci_imx.c | 3 +-- drivers/ata/ahci_mvebu.c | 3 +-- drivers/ata/ahci_platform.c | 6 ++---- drivers/ata/ahci_st.c | 2 +- drivers/ata/ahci_sunxi.c | 8 +++----- drivers/ata/ahci_tegra.c | 3 +-- drivers/ata/ahci_xgene.c | 6 ++---- drivers/ata/libahci.c | 19 +++++++------------ drivers/ata/libahci_platform.c | 13 +++---------- drivers/ata/sata_highbank.c | 2 +- include/linux/ahci_platform.h | 5 +---- 15 files changed, 32 insertions(+), 56 deletions(-) (limited to 'include') diff --git a/drivers/ata/acard-ahci.c b/drivers/ata/acard-ahci.c index 0cd7c7a39e5b..25d0ac32e721 100644 --- a/drivers/ata/acard-ahci.c +++ b/drivers/ata/acard-ahci.c @@ -441,7 +441,7 @@ static int acard_ahci_init_one(struct pci_dev *pdev, const struct pci_device_id hpriv->mmio = pcim_iomap_table(pdev)[AHCI_PCI_BAR]; /* save initial config */ - ahci_save_initial_config(&pdev->dev, hpriv, 0, 0); + ahci_save_initial_config(&pdev->dev, hpriv); /* prepare host */ if (hpriv->cap & HOST_CAP_NCQ) diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 4cd52a4541a9..a29f8012fb08 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -526,8 +526,7 @@ static void ahci_pci_save_initial_config(struct pci_dev *pdev, "Disabling your PATA port. Use the boot option 'ahci.marvell_enable=0' to avoid this.\n"); } - ahci_save_initial_config(&pdev->dev, hpriv, force_port_map, - mask_port_map); + ahci_save_initial_config(&pdev->dev, hpriv); } static int ahci_pci_reset_controller(struct ata_host *host) diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index 2ed84e1c70ea..15396f0330ae 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -316,8 +316,12 @@ struct ahci_port_priv { }; struct ahci_host_priv { - void __iomem * mmio; /* bus-independent mem map */ + /* Input fields */ unsigned int flags; /* AHCI_HFLAG_* */ + u32 force_port_map; /* force port map */ + u32 mask_port_map; /* mask out particular bits */ + + void __iomem * mmio; /* bus-independent mem map */ u32 cap; /* cap to use */ u32 cap2; /* cap2 to use */ u32 port_map; /* port map to use */ @@ -361,9 +365,7 @@ unsigned int ahci_dev_classify(struct ata_port *ap); void ahci_fill_cmd_slot(struct ahci_port_priv *pp, unsigned int tag, u32 opts); void ahci_save_initial_config(struct device *dev, - struct ahci_host_priv *hpriv, - unsigned int force_port_map, - unsigned int mask_port_map); + struct ahci_host_priv *hpriv); void ahci_init_controller(struct ata_host *host); int ahci_reset_controller(struct ata_host *host); diff --git a/drivers/ata/ahci_da850.c b/drivers/ata/ahci_da850.c index 2b77d53bccf8..ad1e71ec10cf 100644 --- a/drivers/ata/ahci_da850.c +++ b/drivers/ata/ahci_da850.c @@ -85,8 +85,7 @@ static int ahci_da850_probe(struct platform_device *pdev) da850_sata_init(dev, pwrdn_reg, hpriv->mmio); - rc = ahci_platform_init_host(pdev, hpriv, &ahci_da850_port_info, - 0, 0, 0); + rc = ahci_platform_init_host(pdev, hpriv, &ahci_da850_port_info); if (rc) goto disable_resources; diff --git a/drivers/ata/ahci_imx.c b/drivers/ata/ahci_imx.c index 1e5fa5f21aff..f3970b4ed889 100644 --- a/drivers/ata/ahci_imx.c +++ b/drivers/ata/ahci_imx.c @@ -620,8 +620,7 @@ static int imx_ahci_probe(struct platform_device *pdev) reg_val = clk_get_rate(imxpriv->ahb_clk) / 1000; writel(reg_val, hpriv->mmio + IMX_TIMER1MS); - ret = ahci_platform_init_host(pdev, hpriv, &ahci_imx_port_info, - 0, 0, 0); + ret = ahci_platform_init_host(pdev, hpriv, &ahci_imx_port_info); if (ret) goto disable_sata; diff --git a/drivers/ata/ahci_mvebu.c b/drivers/ata/ahci_mvebu.c index fd3dfd733b84..68672d2692ee 100644 --- a/drivers/ata/ahci_mvebu.c +++ b/drivers/ata/ahci_mvebu.c @@ -88,8 +88,7 @@ static int ahci_mvebu_probe(struct platform_device *pdev) ahci_mvebu_mbus_config(hpriv, dram); ahci_mvebu_regret_option(hpriv); - rc = ahci_platform_init_host(pdev, hpriv, &ahci_mvebu_port_info, - 0, 0, 0); + rc = ahci_platform_init_host(pdev, hpriv, &ahci_mvebu_port_info); if (rc) goto disable_resources; diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index b10d81ddb528..fb3eca5cd66c 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -34,7 +34,6 @@ static int ahci_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct ahci_platform_data *pdata = dev_get_platdata(dev); struct ahci_host_priv *hpriv; - unsigned long hflags = 0; int rc; hpriv = ahci_platform_get_resources(pdev); @@ -58,10 +57,9 @@ static int ahci_probe(struct platform_device *pdev) } if (of_device_is_compatible(dev->of_node, "hisilicon,hisi-ahci")) - hflags |= AHCI_HFLAG_NO_FBS | AHCI_HFLAG_NO_NCQ; + hpriv->flags |= AHCI_HFLAG_NO_FBS | AHCI_HFLAG_NO_NCQ; - rc = ahci_platform_init_host(pdev, hpriv, &ahci_port_info, - hflags, 0, 0); + rc = ahci_platform_init_host(pdev, hpriv, &ahci_port_info); if (rc) goto pdata_exit; diff --git a/drivers/ata/ahci_st.c b/drivers/ata/ahci_st.c index 29821b9fd13d..835d6eea84fd 100644 --- a/drivers/ata/ahci_st.c +++ b/drivers/ata/ahci_st.c @@ -166,7 +166,7 @@ static int st_ahci_probe(struct platform_device *pdev) if (err) return err; - err = ahci_platform_init_host(pdev, hpriv, &st_ahci_port_info, 0, 0, 0); + err = ahci_platform_init_host(pdev, hpriv, &st_ahci_port_info); if (err) { ahci_platform_disable_resources(hpriv); return err; diff --git a/drivers/ata/ahci_sunxi.c b/drivers/ata/ahci_sunxi.c index 02002f125bd4..e44d675a30ec 100644 --- a/drivers/ata/ahci_sunxi.c +++ b/drivers/ata/ahci_sunxi.c @@ -167,7 +167,6 @@ static int ahci_sunxi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct ahci_host_priv *hpriv; - unsigned long hflags; int rc; hpriv = ahci_platform_get_resources(pdev); @@ -184,11 +183,10 @@ static int ahci_sunxi_probe(struct platform_device *pdev) if (rc) goto disable_resources; - hflags = AHCI_HFLAG_32BIT_ONLY | AHCI_HFLAG_NO_MSI | - AHCI_HFLAG_NO_PMP | AHCI_HFLAG_YES_NCQ; + hpriv->flags = AHCI_HFLAG_32BIT_ONLY | AHCI_HFLAG_NO_MSI | + AHCI_HFLAG_NO_PMP | AHCI_HFLAG_YES_NCQ; - rc = ahci_platform_init_host(pdev, hpriv, &ahci_sunxi_port_info, - hflags, 0, 0); + rc = ahci_platform_init_host(pdev, hpriv, &ahci_sunxi_port_info); if (rc) goto disable_resources; diff --git a/drivers/ata/ahci_tegra.c b/drivers/ata/ahci_tegra.c index d30bb21afd67..fc3df47fca35 100644 --- a/drivers/ata/ahci_tegra.c +++ b/drivers/ata/ahci_tegra.c @@ -348,8 +348,7 @@ static int tegra_ahci_probe(struct platform_device *pdev) if (ret) return ret; - ret = ahci_platform_init_host(pdev, hpriv, &ahci_tegra_port_info, - 0, 0, 0); + ret = ahci_platform_init_host(pdev, hpriv, &ahci_tegra_port_info); if (ret) goto deinit_controller; diff --git a/drivers/ata/ahci_xgene.c b/drivers/ata/ahci_xgene.c index 1cfbdca638d2..bc281115490b 100644 --- a/drivers/ata/ahci_xgene.c +++ b/drivers/ata/ahci_xgene.c @@ -422,7 +422,6 @@ static int xgene_ahci_probe(struct platform_device *pdev) struct ahci_host_priv *hpriv; struct xgene_ahci_context *ctx; struct resource *res; - unsigned long hflags; int rc; hpriv = ahci_platform_get_resources(pdev); @@ -481,10 +480,9 @@ static int xgene_ahci_probe(struct platform_device *pdev) /* Configure the host controller */ xgene_ahci_hw_init(hpriv); - hflags = AHCI_HFLAG_NO_PMP | AHCI_HFLAG_YES_NCQ; + hpriv->flags = AHCI_HFLAG_NO_PMP | AHCI_HFLAG_YES_NCQ; - rc = ahci_platform_init_host(pdev, hpriv, &xgene_ahci_port_info, - hflags, 0, 0); + rc = ahci_platform_init_host(pdev, hpriv, &xgene_ahci_port_info); if (rc) goto disable_resources; diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index d72ce0470309..b784e9de426a 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -382,8 +382,6 @@ static ssize_t ahci_show_em_supported(struct device *dev, * ahci_save_initial_config - Save and fixup initial config values * @dev: target AHCI device * @hpriv: host private area to store config values - * @force_port_map: force port map to a specified value - * @mask_port_map: mask out particular bits from port map * * Some registers containing configuration info might be setup by * BIOS and might be cleared on reset. This function saves the @@ -398,10 +396,7 @@ static ssize_t ahci_show_em_supported(struct device *dev, * LOCKING: * None. */ -void ahci_save_initial_config(struct device *dev, - struct ahci_host_priv *hpriv, - unsigned int force_port_map, - unsigned int mask_port_map) +void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv) { void __iomem *mmio = hpriv->mmio; u32 cap, cap2, vers, port_map; @@ -468,17 +463,17 @@ void ahci_save_initial_config(struct device *dev, cap &= ~HOST_CAP_FBS; } - if (force_port_map && port_map != force_port_map) { + if (hpriv->force_port_map && port_map != hpriv->force_port_map) { dev_info(dev, "forcing port_map 0x%x -> 0x%x\n", - port_map, force_port_map); - port_map = force_port_map; + port_map, hpriv->force_port_map); + port_map = hpriv->force_port_map; } - if (mask_port_map) { + if (hpriv->mask_port_map) { dev_warn(dev, "masking port_map 0x%x -> 0x%x\n", port_map, - port_map & mask_port_map); - port_map &= mask_port_map; + port_map & hpriv->mask_port_map); + port_map &= hpriv->mask_port_map; } /* cross check port_map and cap.n_ports */ diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c index 28840a2f470f..00582d3a46a4 100644 --- a/drivers/ata/libahci_platform.c +++ b/drivers/ata/libahci_platform.c @@ -288,9 +288,6 @@ EXPORT_SYMBOL_GPL(ahci_platform_get_resources); * @pdev: platform device pointer for the host * @hpriv: ahci-host private data for the host * @pi_template: template for the ata_port_info to use - * @host_flags: ahci host flags used in ahci_host_priv - * @force_port_map: param passed to ahci_save_initial_config - * @mask_port_map: param passed to ahci_save_initial_config * * This function does all the usual steps needed to bring up an * ahci-platform host, note any necessary resources (ie clks, phy, etc.) @@ -301,10 +298,7 @@ EXPORT_SYMBOL_GPL(ahci_platform_get_resources); */ int ahci_platform_init_host(struct platform_device *pdev, struct ahci_host_priv *hpriv, - const struct ata_port_info *pi_template, - unsigned long host_flags, - unsigned int force_port_map, - unsigned int mask_port_map) + const struct ata_port_info *pi_template) { struct device *dev = &pdev->dev; struct ata_port_info pi = *pi_template; @@ -319,10 +313,9 @@ int ahci_platform_init_host(struct platform_device *pdev, } /* prepare host */ - pi.private_data = (void *)host_flags; - hpriv->flags |= host_flags; + pi.private_data = (void *)hpriv->flags; - ahci_save_initial_config(dev, hpriv, force_port_map, mask_port_map); + ahci_save_initial_config(dev, hpriv); if (hpriv->cap & HOST_CAP_NCQ) pi.flags |= ATA_FLAG_NCQ; diff --git a/drivers/ata/sata_highbank.c b/drivers/ata/sata_highbank.c index 65965cf5af06..da3bc2709c63 100644 --- a/drivers/ata/sata_highbank.c +++ b/drivers/ata/sata_highbank.c @@ -512,7 +512,7 @@ static int ahci_highbank_probe(struct platform_device *pdev) return rc; - ahci_save_initial_config(dev, hpriv, 0, 0); + ahci_save_initial_config(dev, hpriv); /* prepare host */ if (hpriv->cap & HOST_CAP_NCQ) diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h index 6dfd51a04d77..09a947e8bc87 100644 --- a/include/linux/ahci_platform.h +++ b/include/linux/ahci_platform.h @@ -43,10 +43,7 @@ struct ahci_host_priv *ahci_platform_get_resources( struct platform_device *pdev); int ahci_platform_init_host(struct platform_device *pdev, struct ahci_host_priv *hpriv, - const struct ata_port_info *pi_template, - unsigned long host_flags, - unsigned int force_port_map, - unsigned int mask_port_map); + const struct ata_port_info *pi_template); int ahci_platform_suspend_host(struct device *dev); int ahci_platform_resume_host(struct device *dev); -- cgit v1.2.3 From 9603b61de1eee92977d74ff42541be20c0c5b1a7 Mon Sep 17 00:00:00 2001 From: Jack Morgenstein Date: Mon, 28 Jul 2014 23:30:22 +0300 Subject: mlx5: Move pci device handling from mlx5_ib to mlx5_core In preparation for a new mlx5 device which is VPI (i.e., ports can be either IB or ETH), move the pci device functionality from mlx5_ib to mlx5_core. This involves the following changes: 1. Move mlx5_core_dev struct out of mlx5_ib_dev. mlx5_core_dev is now an independent structure maintained by mlx5_core. mlx5_ib_dev now has a pointer to that struct. This requires changing a lot of places where the core_dev struct was accessed via mlx5_ib_dev (now, this needs to be a pointer dereference). 2. All PCI initializations are now done in mlx5_core. Thus, it is now mlx5_core which does pci_register_device (and not mlx5_ib, as was previously). 3. mlx5_ib now registers itself with mlx5_core as an "interface" driver. This is very similar to the mechanism employed for the mlx4 (ConnectX) driver. Once the HCA is initialized (by mlx5_core), it invokes the interface drivers to do their initializations. 4. There is a new event handler which the core registers: mlx5_core_event(). This event handler invokes the event handlers registered by the interfaces. Based on a patch by Eli Cohen Signed-off-by: Jack Morgenstein Signed-off-by: Eli Cohen Signed-off-by: David S. Miller --- drivers/infiniband/hw/mlx5/cq.c | 46 ++-- drivers/infiniband/hw/mlx5/mad.c | 4 +- drivers/infiniband/hw/mlx5/main.c | 281 ++++++++---------------- drivers/infiniband/hw/mlx5/mlx5_ib.h | 12 +- drivers/infiniband/hw/mlx5/mr.c | 48 ++-- drivers/infiniband/hw/mlx5/qp.c | 84 +++---- drivers/infiniband/hw/mlx5/srq.c | 26 +-- drivers/net/ethernet/mellanox/mlx5/core/main.c | 290 ++++++++++++++++++++++++- include/linux/mlx5/driver.h | 17 +- 9 files changed, 498 insertions(+), 310 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/hw/mlx5/cq.c b/drivers/infiniband/hw/mlx5/cq.c index 8ae4f896cb41..3b4dc858cef9 100644 --- a/drivers/infiniband/hw/mlx5/cq.c +++ b/drivers/infiniband/hw/mlx5/cq.c @@ -180,7 +180,7 @@ static void handle_responder(struct ib_wc *wc, struct mlx5_cqe64 *cqe, struct mlx5_core_srq *msrq = NULL; if (qp->ibqp.xrcd) { - msrq = mlx5_core_get_srq(&dev->mdev, + msrq = mlx5_core_get_srq(dev->mdev, be32_to_cpu(cqe->srqn)); srq = to_mibsrq(msrq); } else { @@ -364,7 +364,7 @@ static void handle_atomics(struct mlx5_ib_qp *qp, struct mlx5_cqe64 *cqe64, static void free_cq_buf(struct mlx5_ib_dev *dev, struct mlx5_ib_cq_buf *buf) { - mlx5_buf_free(&dev->mdev, &buf->buf); + mlx5_buf_free(dev->mdev, &buf->buf); } static void get_sig_err_item(struct mlx5_sig_err_cqe *cqe, @@ -450,7 +450,7 @@ repoll: * because CQs will be locked while QPs are removed * from the table. */ - mqp = __mlx5_qp_lookup(&dev->mdev, qpn); + mqp = __mlx5_qp_lookup(dev->mdev, qpn); if (unlikely(!mqp)) { mlx5_ib_warn(dev, "CQE@CQ %06x for unknown QPN %6x\n", cq->mcq.cqn, qpn); @@ -514,11 +514,11 @@ repoll: case MLX5_CQE_SIG_ERR: sig_err_cqe = (struct mlx5_sig_err_cqe *)cqe64; - read_lock(&dev->mdev.priv.mr_table.lock); - mmr = __mlx5_mr_lookup(&dev->mdev, + read_lock(&dev->mdev->priv.mr_table.lock); + mmr = __mlx5_mr_lookup(dev->mdev, mlx5_base_mkey(be32_to_cpu(sig_err_cqe->mkey))); if (unlikely(!mmr)) { - read_unlock(&dev->mdev.priv.mr_table.lock); + read_unlock(&dev->mdev->priv.mr_table.lock); mlx5_ib_warn(dev, "CQE@CQ %06x for unknown MR %6x\n", cq->mcq.cqn, be32_to_cpu(sig_err_cqe->mkey)); return -EINVAL; @@ -536,7 +536,7 @@ repoll: mr->sig->err_item.expected, mr->sig->err_item.actual); - read_unlock(&dev->mdev.priv.mr_table.lock); + read_unlock(&dev->mdev->priv.mr_table.lock); goto repoll; } @@ -575,8 +575,8 @@ int mlx5_ib_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags) mlx5_cq_arm(&to_mcq(ibcq)->mcq, (flags & IB_CQ_SOLICITED_MASK) == IB_CQ_SOLICITED ? MLX5_CQ_DB_REQ_NOT_SOL : MLX5_CQ_DB_REQ_NOT, - to_mdev(ibcq->device)->mdev.priv.uuari.uars[0].map, - MLX5_GET_DOORBELL_LOCK(&to_mdev(ibcq->device)->mdev.priv.cq_uar_lock)); + to_mdev(ibcq->device)->mdev->priv.uuari.uars[0].map, + MLX5_GET_DOORBELL_LOCK(&to_mdev(ibcq->device)->mdev->priv.cq_uar_lock)); return 0; } @@ -586,7 +586,7 @@ static int alloc_cq_buf(struct mlx5_ib_dev *dev, struct mlx5_ib_cq_buf *buf, { int err; - err = mlx5_buf_alloc(&dev->mdev, nent * cqe_size, + err = mlx5_buf_alloc(dev->mdev, nent * cqe_size, PAGE_SIZE * 2, &buf->buf); if (err) return err; @@ -691,7 +691,7 @@ static int create_cq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_cq *cq, { int err; - err = mlx5_db_alloc(&dev->mdev, &cq->db); + err = mlx5_db_alloc(dev->mdev, &cq->db); if (err) return err; @@ -716,7 +716,7 @@ static int create_cq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_cq *cq, mlx5_fill_page_array(&cq->buf.buf, (*cqb)->pas); (*cqb)->ctx.log_pg_sz = cq->buf.buf.page_shift - MLX5_ADAPTER_PAGE_SHIFT; - *index = dev->mdev.priv.uuari.uars[0].index; + *index = dev->mdev->priv.uuari.uars[0].index; return 0; @@ -724,14 +724,14 @@ err_buf: free_cq_buf(dev, &cq->buf); err_db: - mlx5_db_free(&dev->mdev, &cq->db); + mlx5_db_free(dev->mdev, &cq->db); return err; } static void destroy_cq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_cq *cq) { free_cq_buf(dev, &cq->buf); - mlx5_db_free(&dev->mdev, &cq->db); + mlx5_db_free(dev->mdev, &cq->db); } struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev, int entries, @@ -752,7 +752,7 @@ struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev, int entries, return ERR_PTR(-EINVAL); entries = roundup_pow_of_two(entries + 1); - if (entries > dev->mdev.caps.max_cqes) + if (entries > dev->mdev->caps.max_cqes) return ERR_PTR(-EINVAL); cq = kzalloc(sizeof(*cq), GFP_KERNEL); @@ -789,7 +789,7 @@ struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev, int entries, cqb->ctx.c_eqn = cpu_to_be16(eqn); cqb->ctx.db_record_addr = cpu_to_be64(cq->db.dma); - err = mlx5_core_create_cq(&dev->mdev, &cq->mcq, cqb, inlen); + err = mlx5_core_create_cq(dev->mdev, &cq->mcq, cqb, inlen); if (err) goto err_cqb; @@ -809,7 +809,7 @@ struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev, int entries, return &cq->ibcq; err_cmd: - mlx5_core_destroy_cq(&dev->mdev, &cq->mcq); + mlx5_core_destroy_cq(dev->mdev, &cq->mcq); err_cqb: mlx5_vfree(cqb); @@ -834,7 +834,7 @@ int mlx5_ib_destroy_cq(struct ib_cq *cq) if (cq->uobject) context = cq->uobject->context; - mlx5_core_destroy_cq(&dev->mdev, &mcq->mcq); + mlx5_core_destroy_cq(dev->mdev, &mcq->mcq); if (context) destroy_cq_user(mcq, context); else @@ -919,7 +919,7 @@ int mlx5_ib_modify_cq(struct ib_cq *cq, u16 cq_count, u16 cq_period) int err; u32 fsel; - if (!(dev->mdev.caps.flags & MLX5_DEV_CAP_FLAG_CQ_MODER)) + if (!(dev->mdev->caps.flags & MLX5_DEV_CAP_FLAG_CQ_MODER)) return -ENOSYS; in = kzalloc(sizeof(*in), GFP_KERNEL); @@ -931,7 +931,7 @@ int mlx5_ib_modify_cq(struct ib_cq *cq, u16 cq_count, u16 cq_period) in->ctx.cq_period = cpu_to_be16(cq_period); in->ctx.cq_max_count = cpu_to_be16(cq_count); in->field_select = cpu_to_be32(fsel); - err = mlx5_core_modify_cq(&dev->mdev, &mcq->mcq, in, sizeof(*in)); + err = mlx5_core_modify_cq(dev->mdev, &mcq->mcq, in, sizeof(*in)); kfree(in); if (err) @@ -1074,7 +1074,7 @@ int mlx5_ib_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata) int uninitialized_var(cqe_size); unsigned long flags; - if (!(dev->mdev.caps.flags & MLX5_DEV_CAP_FLAG_RESIZE_CQ)) { + if (!(dev->mdev->caps.flags & MLX5_DEV_CAP_FLAG_RESIZE_CQ)) { pr_info("Firmware does not support resize CQ\n"); return -ENOSYS; } @@ -1083,7 +1083,7 @@ int mlx5_ib_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata) return -EINVAL; entries = roundup_pow_of_two(entries + 1); - if (entries > dev->mdev.caps.max_cqes + 1) + if (entries > dev->mdev->caps.max_cqes + 1) return -EINVAL; if (entries == ibcq->cqe + 1) @@ -1128,7 +1128,7 @@ int mlx5_ib_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata) in->hdr.opmod = cpu_to_be16(MLX5_CQ_OPMOD_RESIZE); in->cqn = cpu_to_be32(cq->mcq.cqn); - err = mlx5_core_modify_cq(&dev->mdev, &cq->mcq, in, inlen); + err = mlx5_core_modify_cq(dev->mdev, &cq->mcq, in, inlen); if (err) goto ex_alloc; diff --git a/drivers/infiniband/hw/mlx5/mad.c b/drivers/infiniband/hw/mlx5/mad.c index 5c8938be0e08..e259e7393152 100644 --- a/drivers/infiniband/hw/mlx5/mad.c +++ b/drivers/infiniband/hw/mlx5/mad.c @@ -54,7 +54,7 @@ int mlx5_MAD_IFC(struct mlx5_ib_dev *dev, int ignore_mkey, int ignore_bkey, if (ignore_bkey || !in_wc) op_modifier |= 0x2; - return mlx5_core_mad_ifc(&dev->mdev, in_mad, response_mad, op_modifier, port); + return mlx5_core_mad_ifc(dev->mdev, in_mad, response_mad, op_modifier, port); } int mlx5_ib_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num, @@ -129,7 +129,7 @@ int mlx5_query_ext_port_caps(struct mlx5_ib_dev *dev, u8 port) packet_error = be16_to_cpu(out_mad->status); - dev->mdev.caps.ext_port_cap[port - 1] = (!err && !packet_error) ? + dev->mdev->caps.ext_port_cap[port - 1] = (!err && !packet_error) ? MLX_EXT_PORT_CAP_FLAG_EXTENDED_PORT_INFO : 0; out: diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 364d4b6937f5..f2cfd363a705 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -54,96 +54,17 @@ MODULE_DESCRIPTION("Mellanox Connect-IB HCA IB driver"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_VERSION(DRIVER_VERSION); -static int prof_sel = 2; -module_param_named(prof_sel, prof_sel, int, 0444); -MODULE_PARM_DESC(prof_sel, "profile selector. Valid range 0 - 2"); +static int deprecated_prof_sel = 2; +module_param_named(prof_sel, deprecated_prof_sel, int, 0444); +MODULE_PARM_DESC(prof_sel, "profile selector. Deprecated here. Moved to module mlx5_core"); static char mlx5_version[] = DRIVER_NAME ": Mellanox Connect-IB Infiniband driver v" DRIVER_VERSION " (" DRIVER_RELDATE ")\n"; -static struct mlx5_profile profile[] = { - [0] = { - .mask = 0, - }, - [1] = { - .mask = MLX5_PROF_MASK_QP_SIZE, - .log_max_qp = 12, - }, - [2] = { - .mask = MLX5_PROF_MASK_QP_SIZE | - MLX5_PROF_MASK_MR_CACHE, - .log_max_qp = 17, - .mr_cache[0] = { - .size = 500, - .limit = 250 - }, - .mr_cache[1] = { - .size = 500, - .limit = 250 - }, - .mr_cache[2] = { - .size = 500, - .limit = 250 - }, - .mr_cache[3] = { - .size = 500, - .limit = 250 - }, - .mr_cache[4] = { - .size = 500, - .limit = 250 - }, - .mr_cache[5] = { - .size = 500, - .limit = 250 - }, - .mr_cache[6] = { - .size = 500, - .limit = 250 - }, - .mr_cache[7] = { - .size = 500, - .limit = 250 - }, - .mr_cache[8] = { - .size = 500, - .limit = 250 - }, - .mr_cache[9] = { - .size = 500, - .limit = 250 - }, - .mr_cache[10] = { - .size = 500, - .limit = 250 - }, - .mr_cache[11] = { - .size = 500, - .limit = 250 - }, - .mr_cache[12] = { - .size = 64, - .limit = 32 - }, - .mr_cache[13] = { - .size = 32, - .limit = 16 - }, - .mr_cache[14] = { - .size = 16, - .limit = 8 - }, - .mr_cache[15] = { - .size = 8, - .limit = 4 - }, - }, -}; - int mlx5_vector2eqn(struct mlx5_ib_dev *dev, int vector, int *eqn, int *irqn) { - struct mlx5_eq_table *table = &dev->mdev.priv.eq_table; + struct mlx5_eq_table *table = &dev->mdev->priv.eq_table; struct mlx5_eq *eq, *n; int err = -ENOENT; @@ -163,7 +84,7 @@ int mlx5_vector2eqn(struct mlx5_ib_dev *dev, int vector, int *eqn, int *irqn) static int alloc_comp_eqs(struct mlx5_ib_dev *dev) { - struct mlx5_eq_table *table = &dev->mdev.priv.eq_table; + struct mlx5_eq_table *table = &dev->mdev->priv.eq_table; char name[MLX5_MAX_EQ_NAME]; struct mlx5_eq *eq, *n; int ncomp_vec; @@ -182,9 +103,9 @@ static int alloc_comp_eqs(struct mlx5_ib_dev *dev) } snprintf(name, MLX5_MAX_EQ_NAME, "mlx5_comp%d", i); - err = mlx5_create_map_eq(&dev->mdev, eq, + err = mlx5_create_map_eq(dev->mdev, eq, i + MLX5_EQ_VEC_COMP_BASE, nent, 0, - name, &dev->mdev.priv.uuari.uars[0]); + name, &dev->mdev->priv.uuari.uars[0]); if (err) { kfree(eq); goto clean; @@ -204,7 +125,7 @@ clean: list_for_each_entry_safe(eq, n, &dev->eqs_list, list) { list_del(&eq->list); spin_unlock(&table->lock); - if (mlx5_destroy_unmap_eq(&dev->mdev, eq)) + if (mlx5_destroy_unmap_eq(dev->mdev, eq)) mlx5_ib_warn(dev, "failed to destroy EQ 0x%x\n", eq->eqn); kfree(eq); spin_lock(&table->lock); @@ -215,14 +136,14 @@ clean: static void free_comp_eqs(struct mlx5_ib_dev *dev) { - struct mlx5_eq_table *table = &dev->mdev.priv.eq_table; + struct mlx5_eq_table *table = &dev->mdev->priv.eq_table; struct mlx5_eq *eq, *n; spin_lock(&table->lock); list_for_each_entry_safe(eq, n, &dev->eqs_list, list) { list_del(&eq->list); spin_unlock(&table->lock); - if (mlx5_destroy_unmap_eq(&dev->mdev, eq)) + if (mlx5_destroy_unmap_eq(dev->mdev, eq)) mlx5_ib_warn(dev, "failed to destroy EQ 0x%x\n", eq->eqn); kfree(eq); spin_lock(&table->lock); @@ -255,14 +176,14 @@ static int mlx5_ib_query_device(struct ib_device *ibdev, memset(props, 0, sizeof(*props)); - props->fw_ver = ((u64)fw_rev_maj(&dev->mdev) << 32) | - (fw_rev_min(&dev->mdev) << 16) | - fw_rev_sub(&dev->mdev); + props->fw_ver = ((u64)fw_rev_maj(dev->mdev) << 32) | + (fw_rev_min(dev->mdev) << 16) | + fw_rev_sub(dev->mdev); props->device_cap_flags = IB_DEVICE_CHANGE_PHY_PORT | IB_DEVICE_PORT_ACTIVE_EVENT | IB_DEVICE_SYS_IMAGE_GUID | IB_DEVICE_RC_RNR_NAK_GEN; - flags = dev->mdev.caps.flags; + flags = dev->mdev->caps.flags; if (flags & MLX5_DEV_CAP_FLAG_BAD_PKEY_CNTR) props->device_cap_flags |= IB_DEVICE_BAD_PKEY_CNTR; if (flags & MLX5_DEV_CAP_FLAG_BAD_QKEY_CNTR) @@ -292,30 +213,30 @@ static int mlx5_ib_query_device(struct ib_device *ibdev, memcpy(&props->sys_image_guid, out_mad->data + 4, 8); props->max_mr_size = ~0ull; - props->page_size_cap = dev->mdev.caps.min_page_sz; - props->max_qp = 1 << dev->mdev.caps.log_max_qp; - props->max_qp_wr = dev->mdev.caps.max_wqes; - max_rq_sg = dev->mdev.caps.max_rq_desc_sz / sizeof(struct mlx5_wqe_data_seg); - max_sq_sg = (dev->mdev.caps.max_sq_desc_sz - sizeof(struct mlx5_wqe_ctrl_seg)) / + props->page_size_cap = dev->mdev->caps.min_page_sz; + props->max_qp = 1 << dev->mdev->caps.log_max_qp; + props->max_qp_wr = dev->mdev->caps.max_wqes; + max_rq_sg = dev->mdev->caps.max_rq_desc_sz / sizeof(struct mlx5_wqe_data_seg); + max_sq_sg = (dev->mdev->caps.max_sq_desc_sz - sizeof(struct mlx5_wqe_ctrl_seg)) / sizeof(struct mlx5_wqe_data_seg); props->max_sge = min(max_rq_sg, max_sq_sg); - props->max_cq = 1 << dev->mdev.caps.log_max_cq; - props->max_cqe = dev->mdev.caps.max_cqes - 1; - props->max_mr = 1 << dev->mdev.caps.log_max_mkey; - props->max_pd = 1 << dev->mdev.caps.log_max_pd; - props->max_qp_rd_atom = dev->mdev.caps.max_ra_req_qp; - props->max_qp_init_rd_atom = dev->mdev.caps.max_ra_res_qp; + props->max_cq = 1 << dev->mdev->caps.log_max_cq; + props->max_cqe = dev->mdev->caps.max_cqes - 1; + props->max_mr = 1 << dev->mdev->caps.log_max_mkey; + props->max_pd = 1 << dev->mdev->caps.log_max_pd; + props->max_qp_rd_atom = dev->mdev->caps.max_ra_req_qp; + props->max_qp_init_rd_atom = dev->mdev->caps.max_ra_res_qp; props->max_res_rd_atom = props->max_qp_rd_atom * props->max_qp; - props->max_srq = 1 << dev->mdev.caps.log_max_srq; - props->max_srq_wr = dev->mdev.caps.max_srq_wqes - 1; + props->max_srq = 1 << dev->mdev->caps.log_max_srq; + props->max_srq_wr = dev->mdev->caps.max_srq_wqes - 1; props->max_srq_sge = max_rq_sg - 1; props->max_fast_reg_page_list_len = (unsigned int)-1; - props->local_ca_ack_delay = dev->mdev.caps.local_ca_ack_delay; + props->local_ca_ack_delay = dev->mdev->caps.local_ca_ack_delay; props->atomic_cap = IB_ATOMIC_NONE; props->masked_atomic_cap = IB_ATOMIC_NONE; props->max_pkeys = be16_to_cpup((__be16 *)(out_mad->data + 28)); - props->max_mcast_grp = 1 << dev->mdev.caps.log_max_mcg; - props->max_mcast_qp_attach = dev->mdev.caps.max_qp_mcg; + props->max_mcast_grp = 1 << dev->mdev->caps.log_max_mcg; + props->max_mcast_qp_attach = dev->mdev->caps.max_qp_mcg; props->max_total_mcast_qp_attach = props->max_mcast_qp_attach * props->max_mcast_grp; props->max_map_per_fmr = INT_MAX; /* no limit in ConnectIB */ @@ -336,7 +257,7 @@ int mlx5_ib_query_port(struct ib_device *ibdev, u8 port, int ext_active_speed; int err = -ENOMEM; - if (port < 1 || port > dev->mdev.caps.num_ports) { + if (port < 1 || port > dev->mdev->caps.num_ports) { mlx5_ib_warn(dev, "invalid port number %d\n", port); return -EINVAL; } @@ -367,8 +288,8 @@ int mlx5_ib_query_port(struct ib_device *ibdev, u8 port, props->phys_state = out_mad->data[33] >> 4; props->port_cap_flags = be32_to_cpup((__be32 *)(out_mad->data + 20)); props->gid_tbl_len = out_mad->data[50]; - props->max_msg_sz = 1 << to_mdev(ibdev)->mdev.caps.log_max_msg; - props->pkey_tbl_len = to_mdev(ibdev)->mdev.caps.port[port - 1].pkey_table_len; + props->max_msg_sz = 1 << to_mdev(ibdev)->mdev->caps.log_max_msg; + props->pkey_tbl_len = to_mdev(ibdev)->mdev->caps.port[port - 1].pkey_table_len; props->bad_pkey_cntr = be16_to_cpup((__be16 *)(out_mad->data + 46)); props->qkey_viol_cntr = be16_to_cpup((__be16 *)(out_mad->data + 48)); props->active_width = out_mad->data[31] & 0xf; @@ -395,7 +316,7 @@ int mlx5_ib_query_port(struct ib_device *ibdev, u8 port, /* If reported active speed is QDR, check if is FDR-10 */ if (props->active_speed == 4) { - if (dev->mdev.caps.ext_port_cap[port - 1] & + if (dev->mdev->caps.ext_port_cap[port - 1] & MLX_EXT_PORT_CAP_FLAG_EXTENDED_PORT_INFO) { init_query_mad(in_mad); in_mad->attr_id = MLX5_ATTR_EXTENDED_PORT_INFO; @@ -508,7 +429,7 @@ static int mlx5_ib_modify_device(struct ib_device *ibdev, int mask, * a 144 trap. If cmd fails, just ignore. */ memcpy(&in, props->node_desc, 64); - err = mlx5_core_access_reg(&dev->mdev, &in, sizeof(in), &out, + err = mlx5_core_access_reg(dev->mdev, &in, sizeof(in), &out, sizeof(out), MLX5_REG_NODE_DESC, 0, 1); if (err) return err; @@ -535,7 +456,7 @@ static int mlx5_ib_modify_port(struct ib_device *ibdev, u8 port, int mask, tmp = (attr.port_cap_flags | props->set_port_cap_mask) & ~props->clr_port_cap_mask; - err = mlx5_set_port_caps(&dev->mdev, port, tmp); + err = mlx5_set_port_caps(dev->mdev, port, tmp); out: mutex_unlock(&dev->cap_mask_mutex); @@ -591,14 +512,14 @@ static struct ib_ucontext *mlx5_ib_alloc_ucontext(struct ib_device *ibdev, num_uars = req.total_num_uuars / MLX5_NON_FP_BF_REGS_PER_PAGE; gross_uuars = num_uars * MLX5_BF_REGS_PER_PAGE; - resp.qp_tab_size = 1 << dev->mdev.caps.log_max_qp; - resp.bf_reg_size = dev->mdev.caps.bf_reg_size; + resp.qp_tab_size = 1 << dev->mdev->caps.log_max_qp; + resp.bf_reg_size = dev->mdev->caps.bf_reg_size; resp.cache_line_size = L1_CACHE_BYTES; - resp.max_sq_desc_sz = dev->mdev.caps.max_sq_desc_sz; - resp.max_rq_desc_sz = dev->mdev.caps.max_rq_desc_sz; - resp.max_send_wqebb = dev->mdev.caps.max_wqes; - resp.max_recv_wr = dev->mdev.caps.max_wqes; - resp.max_srq_recv_wr = dev->mdev.caps.max_srq_wqes; + resp.max_sq_desc_sz = dev->mdev->caps.max_sq_desc_sz; + resp.max_rq_desc_sz = dev->mdev->caps.max_rq_desc_sz; + resp.max_send_wqebb = dev->mdev->caps.max_wqes; + resp.max_recv_wr = dev->mdev->caps.max_wqes; + resp.max_srq_recv_wr = dev->mdev->caps.max_srq_wqes; context = kzalloc(sizeof(*context), GFP_KERNEL); if (!context) @@ -635,7 +556,7 @@ static struct ib_ucontext *mlx5_ib_alloc_ucontext(struct ib_device *ibdev, } for (i = 0; i < num_uars; i++) { - err = mlx5_cmd_alloc_uar(&dev->mdev, &uars[i].index); + err = mlx5_cmd_alloc_uar(dev->mdev, &uars[i].index); if (err) goto out_count; } @@ -644,7 +565,7 @@ static struct ib_ucontext *mlx5_ib_alloc_ucontext(struct ib_device *ibdev, mutex_init(&context->db_page_mutex); resp.tot_uuars = req.total_num_uuars; - resp.num_ports = dev->mdev.caps.num_ports; + resp.num_ports = dev->mdev->caps.num_ports; err = ib_copy_to_udata(udata, &resp, sizeof(resp) - sizeof(resp.reserved)); if (err) @@ -658,7 +579,7 @@ static struct ib_ucontext *mlx5_ib_alloc_ucontext(struct ib_device *ibdev, out_uars: for (i--; i >= 0; i--) - mlx5_cmd_free_uar(&dev->mdev, uars[i].index); + mlx5_cmd_free_uar(dev->mdev, uars[i].index); out_count: kfree(uuari->count); @@ -681,7 +602,7 @@ static int mlx5_ib_dealloc_ucontext(struct ib_ucontext *ibcontext) int i; for (i = 0; i < uuari->num_uars; i++) { - if (mlx5_cmd_free_uar(&dev->mdev, uuari->uars[i].index)) + if (mlx5_cmd_free_uar(dev->mdev, uuari->uars[i].index)) mlx5_ib_warn(dev, "failed to free UAR 0x%x\n", uuari->uars[i].index); } @@ -695,7 +616,7 @@ static int mlx5_ib_dealloc_ucontext(struct ib_ucontext *ibcontext) static phys_addr_t uar_index2pfn(struct mlx5_ib_dev *dev, int index) { - return (pci_resource_start(dev->mdev.pdev, 0) >> PAGE_SHIFT) + index; + return (pci_resource_start(dev->mdev->pdev, 0) >> PAGE_SHIFT) + index; } static int get_command(unsigned long offset) @@ -773,7 +694,7 @@ static int alloc_pa_mkey(struct mlx5_ib_dev *dev, u32 *key, u32 pdn) seg->qpn_mkey7_0 = cpu_to_be32(0xffffff << 8); seg->start_addr = 0; - err = mlx5_core_create_mkey(&dev->mdev, &mr, in, sizeof(*in), + err = mlx5_core_create_mkey(dev->mdev, &mr, in, sizeof(*in), NULL, NULL, NULL); if (err) { mlx5_ib_warn(dev, "failed to create mkey, %d\n", err); @@ -798,7 +719,7 @@ static void free_pa_mkey(struct mlx5_ib_dev *dev, u32 key) memset(&mr, 0, sizeof(mr)); mr.key = key; - err = mlx5_core_destroy_mkey(&dev->mdev, &mr); + err = mlx5_core_destroy_mkey(dev->mdev, &mr); if (err) mlx5_ib_warn(dev, "failed to destroy mkey 0x%x\n", key); } @@ -815,7 +736,7 @@ static struct ib_pd *mlx5_ib_alloc_pd(struct ib_device *ibdev, if (!pd) return ERR_PTR(-ENOMEM); - err = mlx5_core_alloc_pd(&to_mdev(ibdev)->mdev, &pd->pdn); + err = mlx5_core_alloc_pd(to_mdev(ibdev)->mdev, &pd->pdn); if (err) { kfree(pd); return ERR_PTR(err); @@ -824,14 +745,14 @@ static struct ib_pd *mlx5_ib_alloc_pd(struct ib_device *ibdev, if (context) { resp.pdn = pd->pdn; if (ib_copy_to_udata(udata, &resp, sizeof(resp))) { - mlx5_core_dealloc_pd(&to_mdev(ibdev)->mdev, pd->pdn); + mlx5_core_dealloc_pd(to_mdev(ibdev)->mdev, pd->pdn); kfree(pd); return ERR_PTR(-EFAULT); } } else { err = alloc_pa_mkey(to_mdev(ibdev), &pd->pa_lkey, pd->pdn); if (err) { - mlx5_core_dealloc_pd(&to_mdev(ibdev)->mdev, pd->pdn); + mlx5_core_dealloc_pd(to_mdev(ibdev)->mdev, pd->pdn); kfree(pd); return ERR_PTR(err); } @@ -848,7 +769,7 @@ static int mlx5_ib_dealloc_pd(struct ib_pd *pd) if (!pd->uobject) free_pa_mkey(mdev, mpd->pa_lkey); - mlx5_core_dealloc_pd(&mdev->mdev, mpd->pdn); + mlx5_core_dealloc_pd(mdev->mdev, mpd->pdn); kfree(mpd); return 0; @@ -859,7 +780,7 @@ static int mlx5_ib_mcg_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) struct mlx5_ib_dev *dev = to_mdev(ibqp->device); int err; - err = mlx5_core_attach_mcg(&dev->mdev, gid, ibqp->qp_num); + err = mlx5_core_attach_mcg(dev->mdev, gid, ibqp->qp_num); if (err) mlx5_ib_warn(dev, "failed attaching QPN 0x%x, MGID %pI6\n", ibqp->qp_num, gid->raw); @@ -872,7 +793,7 @@ static int mlx5_ib_mcg_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) struct mlx5_ib_dev *dev = to_mdev(ibqp->device); int err; - err = mlx5_core_detach_mcg(&dev->mdev, gid, ibqp->qp_num); + err = mlx5_core_detach_mcg(dev->mdev, gid, ibqp->qp_num); if (err) mlx5_ib_warn(dev, "failed detaching QPN 0x%x, MGID %pI6\n", ibqp->qp_num, gid->raw); @@ -906,7 +827,7 @@ static int init_node_data(struct mlx5_ib_dev *dev) if (err) goto out; - dev->mdev.rev_id = be32_to_cpup((__be32 *)(out_mad->data + 32)); + dev->mdev->rev_id = be32_to_cpup((__be32 *)(out_mad->data + 32)); memcpy(&dev->ib_dev.node_guid, out_mad->data + 12, 8); out: @@ -921,7 +842,7 @@ static ssize_t show_fw_pages(struct device *device, struct device_attribute *att struct mlx5_ib_dev *dev = container_of(device, struct mlx5_ib_dev, ib_dev.dev); - return sprintf(buf, "%d\n", dev->mdev.priv.fw_pages); + return sprintf(buf, "%d\n", dev->mdev->priv.fw_pages); } static ssize_t show_reg_pages(struct device *device, @@ -930,7 +851,7 @@ static ssize_t show_reg_pages(struct device *device, struct mlx5_ib_dev *dev = container_of(device, struct mlx5_ib_dev, ib_dev.dev); - return sprintf(buf, "%d\n", dev->mdev.priv.reg_pages); + return sprintf(buf, "%d\n", dev->mdev->priv.reg_pages); } static ssize_t show_hca(struct device *device, struct device_attribute *attr, @@ -938,7 +859,7 @@ static ssize_t show_hca(struct device *device, struct device_attribute *attr, { struct mlx5_ib_dev *dev = container_of(device, struct mlx5_ib_dev, ib_dev.dev); - return sprintf(buf, "MT%d\n", dev->mdev.pdev->device); + return sprintf(buf, "MT%d\n", dev->mdev->pdev->device); } static ssize_t show_fw_ver(struct device *device, struct device_attribute *attr, @@ -946,8 +867,8 @@ static ssize_t show_fw_ver(struct device *device, struct device_attribute *attr, { struct mlx5_ib_dev *dev = container_of(device, struct mlx5_ib_dev, ib_dev.dev); - return sprintf(buf, "%d.%d.%d\n", fw_rev_maj(&dev->mdev), - fw_rev_min(&dev->mdev), fw_rev_sub(&dev->mdev)); + return sprintf(buf, "%d.%d.%d\n", fw_rev_maj(dev->mdev), + fw_rev_min(dev->mdev), fw_rev_sub(dev->mdev)); } static ssize_t show_rev(struct device *device, struct device_attribute *attr, @@ -955,7 +876,7 @@ static ssize_t show_rev(struct device *device, struct device_attribute *attr, { struct mlx5_ib_dev *dev = container_of(device, struct mlx5_ib_dev, ib_dev.dev); - return sprintf(buf, "%x\n", dev->mdev.rev_id); + return sprintf(buf, "%x\n", dev->mdev->rev_id); } static ssize_t show_board(struct device *device, struct device_attribute *attr, @@ -964,7 +885,7 @@ static ssize_t show_board(struct device *device, struct device_attribute *attr, struct mlx5_ib_dev *dev = container_of(device, struct mlx5_ib_dev, ib_dev.dev); return sprintf(buf, "%.*s\n", MLX5_BOARD_ID_LEN, - dev->mdev.board_id); + dev->mdev->board_id); } static DEVICE_ATTR(hw_rev, S_IRUGO, show_rev, NULL); @@ -983,11 +904,12 @@ static struct device_attribute *mlx5_class_attributes[] = { &dev_attr_reg_pages, }; -static void mlx5_ib_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event, - void *data) +static void mlx5_ib_event(struct mlx5_core_dev *dev, void *context, + enum mlx5_dev_event event, void *data) { - struct mlx5_ib_dev *ibdev = container_of(dev, struct mlx5_ib_dev, mdev); + struct mlx5_ib_dev *ibdev = (struct mlx5_ib_dev *)context; struct ib_event ibev; + u8 port = 0; switch (event) { @@ -1047,7 +969,7 @@ static void get_ext_port_caps(struct mlx5_ib_dev *dev) { int port; - for (port = 1; port <= dev->mdev.caps.num_ports; port++) + for (port = 1; port <= dev->mdev->caps.num_ports; port++) mlx5_query_ext_port_caps(dev, port); } @@ -1072,14 +994,14 @@ static int get_port_caps(struct mlx5_ib_dev *dev) goto out; } - for (port = 1; port <= dev->mdev.caps.num_ports; port++) { + for (port = 1; port <= dev->mdev->caps.num_ports; port++) { err = mlx5_ib_query_port(&dev->ib_dev, port, pprops); if (err) { mlx5_ib_warn(dev, "query_port %d failed %d\n", port, err); break; } - dev->mdev.caps.port[port - 1].pkey_table_len = dprops->max_pkeys; - dev->mdev.caps.port[port - 1].gid_table_len = pprops->gid_tbl_len; + dev->mdev->caps.port[port - 1].pkey_table_len = dprops->max_pkeys; + dev->mdev->caps.port[port - 1].gid_table_len = pprops->gid_tbl_len; mlx5_ib_dbg(dev, "pkey_table_len %d, gid_table_len %d\n", dprops->max_pkeys, pprops->gid_tbl_len); } @@ -1328,10 +1250,8 @@ static void destroy_dev_resources(struct mlx5_ib_resources *devr) mlx5_ib_dealloc_pd(devr->p0); } -static int init_one(struct pci_dev *pdev, - const struct pci_device_id *id) +static void *mlx5_ib_add(struct mlx5_core_dev *mdev) { - struct mlx5_core_dev *mdev; struct mlx5_ib_dev *dev; int err; int i; @@ -1340,28 +1260,19 @@ static int init_one(struct pci_dev *pdev, dev = (struct mlx5_ib_dev *)ib_alloc_device(sizeof(*dev)); if (!dev) - return -ENOMEM; + return NULL; - mdev = &dev->mdev; - mdev->event = mlx5_ib_event; - if (prof_sel >= ARRAY_SIZE(profile)) { - pr_warn("selected pofile out of range, selceting default\n"); - prof_sel = 0; - } - mdev->profile = &profile[prof_sel]; - err = mlx5_dev_init(mdev, pdev); - if (err) - goto err_free; + dev->mdev = mdev; err = get_port_caps(dev); if (err) - goto err_cleanup; + goto err_dealloc; get_ext_port_caps(dev); err = alloc_comp_eqs(dev); if (err) - goto err_cleanup; + goto err_dealloc; MLX5_INIT_DOORBELL_LOCK(&dev->uar_lock); @@ -1480,7 +1391,7 @@ static int init_one(struct pci_dev *pdev, dev->ib_active = true; - return 0; + return dev; err_umrc: destroy_umrc_res(dev); @@ -1494,49 +1405,39 @@ err_rsrc: err_eqs: free_comp_eqs(dev); -err_cleanup: - mlx5_dev_cleanup(mdev); - -err_free: +err_dealloc: ib_dealloc_device((struct ib_device *)dev); - return err; + return NULL; } -static void remove_one(struct pci_dev *pdev) +static void mlx5_ib_remove(struct mlx5_core_dev *mdev, void *context) { - struct mlx5_ib_dev *dev = mlx5_pci2ibdev(pdev); - + struct mlx5_ib_dev *dev = context; destroy_umrc_res(dev); ib_unregister_device(&dev->ib_dev); destroy_dev_resources(&dev->devr); free_comp_eqs(dev); - mlx5_dev_cleanup(&dev->mdev); ib_dealloc_device(&dev->ib_dev); } -static DEFINE_PCI_DEVICE_TABLE(mlx5_ib_pci_table) = { - { PCI_VDEVICE(MELLANOX, 4113) }, /* MT4113 Connect-IB */ - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, mlx5_ib_pci_table); - -static struct pci_driver mlx5_ib_driver = { - .name = DRIVER_NAME, - .id_table = mlx5_ib_pci_table, - .probe = init_one, - .remove = remove_one +static struct mlx5_interface mlx5_ib_interface = { + .add = mlx5_ib_add, + .remove = mlx5_ib_remove, + .event = mlx5_ib_event, }; static int __init mlx5_ib_init(void) { - return pci_register_driver(&mlx5_ib_driver); + if (deprecated_prof_sel != 2) + pr_warn("prof_sel is deprecated for mlx5_ib, set it for mlx5_core\n"); + + return mlx5_register_interface(&mlx5_ib_interface); } static void __exit mlx5_ib_cleanup(void) { - pci_unregister_driver(&mlx5_ib_driver); + mlx5_unregister_interface(&mlx5_ib_interface); } module_init(mlx5_ib_init); diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index f2ccf1a5a291..a0e204ffe367 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -360,7 +360,7 @@ struct mlx5_ib_resources { struct mlx5_ib_dev { struct ib_device ib_dev; - struct mlx5_core_dev mdev; + struct mlx5_core_dev *mdev; MLX5_DECLARE_DOORBELL_LOCK(uar_lock); struct list_head eqs_list; int num_ports; @@ -454,16 +454,6 @@ static inline struct mlx5_ib_ah *to_mah(struct ib_ah *ibah) return container_of(ibah, struct mlx5_ib_ah, ibah); } -static inline struct mlx5_ib_dev *mlx5_core2ibdev(struct mlx5_core_dev *dev) -{ - return container_of(dev, struct mlx5_ib_dev, mdev); -} - -static inline struct mlx5_ib_dev *mlx5_pci2ibdev(struct pci_dev *pdev) -{ - return mlx5_core2ibdev(pci2mlx5_core_dev(pdev)); -} - int mlx5_ib_db_map_user(struct mlx5_ib_ucontext *context, unsigned long virt, struct mlx5_db *db); void mlx5_ib_db_unmap_user(struct mlx5_ib_ucontext *context, struct mlx5_db *db); diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index afa873bd028e..80b3c63eab5d 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -73,7 +73,7 @@ static void reg_mr_callback(int status, void *context) struct mlx5_cache_ent *ent = &cache->ent[c]; u8 key; unsigned long flags; - struct mlx5_mr_table *table = &dev->mdev.priv.mr_table; + struct mlx5_mr_table *table = &dev->mdev->priv.mr_table; int err; spin_lock_irqsave(&ent->lock, flags); @@ -97,9 +97,9 @@ static void reg_mr_callback(int status, void *context) return; } - spin_lock_irqsave(&dev->mdev.priv.mkey_lock, flags); - key = dev->mdev.priv.mkey_key++; - spin_unlock_irqrestore(&dev->mdev.priv.mkey_lock, flags); + spin_lock_irqsave(&dev->mdev->priv.mkey_lock, flags); + key = dev->mdev->priv.mkey_key++; + spin_unlock_irqrestore(&dev->mdev->priv.mkey_lock, flags); mr->mmr.key = mlx5_idx_to_mkey(be32_to_cpu(mr->out.mkey) & 0xffffff) | key; cache->last_add = jiffies; @@ -155,7 +155,7 @@ static int add_keys(struct mlx5_ib_dev *dev, int c, int num) spin_lock_irq(&ent->lock); ent->pending++; spin_unlock_irq(&ent->lock); - err = mlx5_core_create_mkey(&dev->mdev, &mr->mmr, in, + err = mlx5_core_create_mkey(dev->mdev, &mr->mmr, in, sizeof(*in), reg_mr_callback, mr, &mr->out); if (err) { @@ -188,7 +188,7 @@ static void remove_keys(struct mlx5_ib_dev *dev, int c, int num) ent->cur--; ent->size--; spin_unlock_irq(&ent->lock); - err = mlx5_core_destroy_mkey(&dev->mdev, &mr->mmr); + err = mlx5_core_destroy_mkey(dev->mdev, &mr->mmr); if (err) mlx5_ib_warn(dev, "failed destroy mkey\n"); else @@ -479,7 +479,7 @@ static void clean_keys(struct mlx5_ib_dev *dev, int c) ent->cur--; ent->size--; spin_unlock_irq(&ent->lock); - err = mlx5_core_destroy_mkey(&dev->mdev, &mr->mmr); + err = mlx5_core_destroy_mkey(dev->mdev, &mr->mmr); if (err) mlx5_ib_warn(dev, "failed destroy mkey\n"); else @@ -496,7 +496,7 @@ static int mlx5_mr_cache_debugfs_init(struct mlx5_ib_dev *dev) if (!mlx5_debugfs_root) return 0; - cache->root = debugfs_create_dir("mr_cache", dev->mdev.priv.dbg_root); + cache->root = debugfs_create_dir("mr_cache", dev->mdev->priv.dbg_root); if (!cache->root) return -ENOMEM; @@ -571,8 +571,8 @@ int mlx5_mr_cache_init(struct mlx5_ib_dev *dev) ent->order = i + 2; ent->dev = dev; - if (dev->mdev.profile->mask & MLX5_PROF_MASK_MR_CACHE) - limit = dev->mdev.profile->mr_cache[i].limit; + if (dev->mdev->profile->mask & MLX5_PROF_MASK_MR_CACHE) + limit = dev->mdev->profile->mr_cache[i].limit; else limit = 0; @@ -610,7 +610,7 @@ int mlx5_mr_cache_cleanup(struct mlx5_ib_dev *dev) struct ib_mr *mlx5_ib_get_dma_mr(struct ib_pd *pd, int acc) { struct mlx5_ib_dev *dev = to_mdev(pd->device); - struct mlx5_core_dev *mdev = &dev->mdev; + struct mlx5_core_dev *mdev = dev->mdev; struct mlx5_create_mkey_mbox_in *in; struct mlx5_mkey_seg *seg; struct mlx5_ib_mr *mr; @@ -846,7 +846,7 @@ static struct mlx5_ib_mr *reg_create(struct ib_pd *pd, u64 virt_addr, in->seg.qpn_mkey7_0 = cpu_to_be32(0xffffff << 8); in->xlat_oct_act_size = cpu_to_be32(get_octo_len(virt_addr, length, 1 << page_shift)); - err = mlx5_core_create_mkey(&dev->mdev, &mr->mmr, in, inlen, NULL, + err = mlx5_core_create_mkey(dev->mdev, &mr->mmr, in, inlen, NULL, NULL, NULL); if (err) { mlx5_ib_warn(dev, "create mkey failed\n"); @@ -923,7 +923,7 @@ struct ib_mr *mlx5_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, mr->umem = umem; mr->npages = npages; spin_lock(&dev->mr_lock); - dev->mdev.priv.reg_pages += npages; + dev->mdev->priv.reg_pages += npages; spin_unlock(&dev->mr_lock); mr->ibmr.lkey = mr->mmr.key; mr->ibmr.rkey = mr->mmr.key; @@ -978,7 +978,7 @@ int mlx5_ib_dereg_mr(struct ib_mr *ibmr) int err; if (!umred) { - err = mlx5_core_destroy_mkey(&dev->mdev, &mr->mmr); + err = mlx5_core_destroy_mkey(dev->mdev, &mr->mmr); if (err) { mlx5_ib_warn(dev, "failed to destroy mkey 0x%x (%d)\n", mr->mmr.key, err); @@ -996,7 +996,7 @@ int mlx5_ib_dereg_mr(struct ib_mr *ibmr) if (umem) { ib_umem_release(umem); spin_lock(&dev->mr_lock); - dev->mdev.priv.reg_pages -= npages; + dev->mdev->priv.reg_pages -= npages; spin_unlock(&dev->mr_lock); } @@ -1044,7 +1044,7 @@ struct ib_mr *mlx5_ib_create_mr(struct ib_pd *pd, } /* create mem & wire PSVs */ - err = mlx5_core_create_psv(&dev->mdev, to_mpd(pd)->pdn, + err = mlx5_core_create_psv(dev->mdev, to_mpd(pd)->pdn, 2, psv_index); if (err) goto err_free_sig; @@ -1060,7 +1060,7 @@ struct ib_mr *mlx5_ib_create_mr(struct ib_pd *pd, } in->seg.flags = MLX5_PERM_UMR_EN | access_mode; - err = mlx5_core_create_mkey(&dev->mdev, &mr->mmr, in, sizeof(*in), + err = mlx5_core_create_mkey(dev->mdev, &mr->mmr, in, sizeof(*in), NULL, NULL, NULL); if (err) goto err_destroy_psv; @@ -1074,11 +1074,11 @@ struct ib_mr *mlx5_ib_create_mr(struct ib_pd *pd, err_destroy_psv: if (mr->sig) { - if (mlx5_core_destroy_psv(&dev->mdev, + if (mlx5_core_destroy_psv(dev->mdev, mr->sig->psv_memory.psv_idx)) mlx5_ib_warn(dev, "failed to destroy mem psv %d\n", mr->sig->psv_memory.psv_idx); - if (mlx5_core_destroy_psv(&dev->mdev, + if (mlx5_core_destroy_psv(dev->mdev, mr->sig->psv_wire.psv_idx)) mlx5_ib_warn(dev, "failed to destroy wire psv %d\n", mr->sig->psv_wire.psv_idx); @@ -1099,18 +1099,18 @@ int mlx5_ib_destroy_mr(struct ib_mr *ibmr) int err; if (mr->sig) { - if (mlx5_core_destroy_psv(&dev->mdev, + if (mlx5_core_destroy_psv(dev->mdev, mr->sig->psv_memory.psv_idx)) mlx5_ib_warn(dev, "failed to destroy mem psv %d\n", mr->sig->psv_memory.psv_idx); - if (mlx5_core_destroy_psv(&dev->mdev, + if (mlx5_core_destroy_psv(dev->mdev, mr->sig->psv_wire.psv_idx)) mlx5_ib_warn(dev, "failed to destroy wire psv %d\n", mr->sig->psv_wire.psv_idx); kfree(mr->sig); } - err = mlx5_core_destroy_mkey(&dev->mdev, &mr->mmr); + err = mlx5_core_destroy_mkey(dev->mdev, &mr->mmr); if (err) { mlx5_ib_warn(dev, "failed to destroy mkey 0x%x (%d)\n", mr->mmr.key, err); @@ -1149,7 +1149,7 @@ struct ib_mr *mlx5_ib_alloc_fast_reg_mr(struct ib_pd *pd, * TBD not needed - issue 197292 */ in->seg.log2_page_size = PAGE_SHIFT; - err = mlx5_core_create_mkey(&dev->mdev, &mr->mmr, in, sizeof(*in), NULL, + err = mlx5_core_create_mkey(dev->mdev, &mr->mmr, in, sizeof(*in), NULL, NULL, NULL); kfree(in); if (err) @@ -1202,7 +1202,7 @@ void mlx5_ib_free_fast_reg_page_list(struct ib_fast_reg_page_list *page_list) struct mlx5_ib_dev *dev = to_mdev(page_list->device); int size = page_list->max_page_list_len * sizeof(u64); - dma_free_coherent(&dev->mdev.pdev->dev, size, mfrpl->mapped_page_list, + dma_free_coherent(&dev->mdev->pdev->dev, size, mfrpl->mapped_page_list, mfrpl->map); kfree(mfrpl->ibfrpl.page_list); kfree(mfrpl); diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index bbbcf389272c..b8bb6ad6350c 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -162,7 +162,7 @@ static int set_rq_size(struct mlx5_ib_dev *dev, struct ib_qp_cap *cap, int wq_size; /* Sanity check RQ size before proceeding */ - if (cap->max_recv_wr > dev->mdev.caps.max_wqes) + if (cap->max_recv_wr > dev->mdev->caps.max_wqes) return -EINVAL; if (!has_rq) { @@ -182,10 +182,10 @@ static int set_rq_size(struct mlx5_ib_dev *dev, struct ib_qp_cap *cap, wq_size = roundup_pow_of_two(cap->max_recv_wr) * wqe_size; wq_size = max_t(int, wq_size, MLX5_SEND_WQE_BB); qp->rq.wqe_cnt = wq_size / wqe_size; - if (wqe_size > dev->mdev.caps.max_rq_desc_sz) { + if (wqe_size > dev->mdev->caps.max_rq_desc_sz) { mlx5_ib_dbg(dev, "wqe_size %d, max %d\n", wqe_size, - dev->mdev.caps.max_rq_desc_sz); + dev->mdev->caps.max_rq_desc_sz); return -EINVAL; } qp->rq.wqe_shift = ilog2(wqe_size); @@ -277,9 +277,9 @@ static int calc_sq_size(struct mlx5_ib_dev *dev, struct ib_qp_init_attr *attr, if (wqe_size < 0) return wqe_size; - if (wqe_size > dev->mdev.caps.max_sq_desc_sz) { + if (wqe_size > dev->mdev->caps.max_sq_desc_sz) { mlx5_ib_dbg(dev, "wqe_size(%d) > max_sq_desc_sz(%d)\n", - wqe_size, dev->mdev.caps.max_sq_desc_sz); + wqe_size, dev->mdev->caps.max_sq_desc_sz); return -EINVAL; } @@ -292,9 +292,9 @@ static int calc_sq_size(struct mlx5_ib_dev *dev, struct ib_qp_init_attr *attr, wq_size = roundup_pow_of_two(attr->cap.max_send_wr * wqe_size); qp->sq.wqe_cnt = wq_size / MLX5_SEND_WQE_BB; - if (qp->sq.wqe_cnt > dev->mdev.caps.max_wqes) { + if (qp->sq.wqe_cnt > dev->mdev->caps.max_wqes) { mlx5_ib_dbg(dev, "wqe count(%d) exceeds limits(%d)\n", - qp->sq.wqe_cnt, dev->mdev.caps.max_wqes); + qp->sq.wqe_cnt, dev->mdev->caps.max_wqes); return -ENOMEM; } qp->sq.wqe_shift = ilog2(MLX5_SEND_WQE_BB); @@ -311,9 +311,9 @@ static int set_user_buf_size(struct mlx5_ib_dev *dev, { int desc_sz = 1 << qp->sq.wqe_shift; - if (desc_sz > dev->mdev.caps.max_sq_desc_sz) { + if (desc_sz > dev->mdev->caps.max_sq_desc_sz) { mlx5_ib_warn(dev, "desc_sz %d, max_sq_desc_sz %d\n", - desc_sz, dev->mdev.caps.max_sq_desc_sz); + desc_sz, dev->mdev->caps.max_sq_desc_sz); return -EINVAL; } @@ -325,9 +325,9 @@ static int set_user_buf_size(struct mlx5_ib_dev *dev, qp->sq.wqe_cnt = ucmd->sq_wqe_count; - if (qp->sq.wqe_cnt > dev->mdev.caps.max_wqes) { + if (qp->sq.wqe_cnt > dev->mdev->caps.max_wqes) { mlx5_ib_warn(dev, "wqe_cnt %d, max_wqes %d\n", - qp->sq.wqe_cnt, dev->mdev.caps.max_wqes); + qp->sq.wqe_cnt, dev->mdev->caps.max_wqes); return -EINVAL; } @@ -674,7 +674,7 @@ static int create_kernel_qp(struct mlx5_ib_dev *dev, int uuarn; int err; - uuari = &dev->mdev.priv.uuari; + uuari = &dev->mdev->priv.uuari; if (init_attr->create_flags & ~(IB_QP_CREATE_SIGNATURE_EN | IB_QP_CREATE_BLOCK_MULTICAST_LOOPBACK)) return -EINVAL; @@ -700,7 +700,7 @@ static int create_kernel_qp(struct mlx5_ib_dev *dev, qp->sq.offset = qp->rq.wqe_cnt << qp->rq.wqe_shift; qp->buf_size = err + (qp->rq.wqe_cnt << qp->rq.wqe_shift); - err = mlx5_buf_alloc(&dev->mdev, qp->buf_size, PAGE_SIZE * 2, &qp->buf); + err = mlx5_buf_alloc(dev->mdev, qp->buf_size, PAGE_SIZE * 2, &qp->buf); if (err) { mlx5_ib_dbg(dev, "err %d\n", err); goto err_uuar; @@ -722,7 +722,7 @@ static int create_kernel_qp(struct mlx5_ib_dev *dev, mlx5_fill_page_array(&qp->buf, (*in)->pas); - err = mlx5_db_alloc(&dev->mdev, &qp->db); + err = mlx5_db_alloc(dev->mdev, &qp->db); if (err) { mlx5_ib_dbg(dev, "err %d\n", err); goto err_free; @@ -747,7 +747,7 @@ static int create_kernel_qp(struct mlx5_ib_dev *dev, return 0; err_wrid: - mlx5_db_free(&dev->mdev, &qp->db); + mlx5_db_free(dev->mdev, &qp->db); kfree(qp->sq.wqe_head); kfree(qp->sq.w_list); kfree(qp->sq.wrid); @@ -758,23 +758,23 @@ err_free: mlx5_vfree(*in); err_buf: - mlx5_buf_free(&dev->mdev, &qp->buf); + mlx5_buf_free(dev->mdev, &qp->buf); err_uuar: - free_uuar(&dev->mdev.priv.uuari, uuarn); + free_uuar(&dev->mdev->priv.uuari, uuarn); return err; } static void destroy_qp_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp) { - mlx5_db_free(&dev->mdev, &qp->db); + mlx5_db_free(dev->mdev, &qp->db); kfree(qp->sq.wqe_head); kfree(qp->sq.w_list); kfree(qp->sq.wrid); kfree(qp->sq.wr_data); kfree(qp->rq.wrid); - mlx5_buf_free(&dev->mdev, &qp->buf); - free_uuar(&dev->mdev.priv.uuari, qp->bf->uuarn); + mlx5_buf_free(dev->mdev, &qp->buf); + free_uuar(&dev->mdev->priv.uuari, qp->bf->uuarn); } static __be32 get_rx_type(struct mlx5_ib_qp *qp, struct ib_qp_init_attr *attr) @@ -812,7 +812,7 @@ static int create_qp_common(struct mlx5_ib_dev *dev, struct ib_pd *pd, spin_lock_init(&qp->rq.lock); if (init_attr->create_flags & IB_QP_CREATE_BLOCK_MULTICAST_LOOPBACK) { - if (!(dev->mdev.caps.flags & MLX5_DEV_CAP_FLAG_BLOCK_MCAST)) { + if (!(dev->mdev->caps.flags & MLX5_DEV_CAP_FLAG_BLOCK_MCAST)) { mlx5_ib_dbg(dev, "block multicast loopback isn't supported\n"); return -EINVAL; } else { @@ -851,9 +851,9 @@ static int create_qp_common(struct mlx5_ib_dev *dev, struct ib_pd *pd, mlx5_ib_dbg(dev, "invalid rq params\n"); return -EINVAL; } - if (ucmd.sq_wqe_count > dev->mdev.caps.max_wqes) { + if (ucmd.sq_wqe_count > dev->mdev->caps.max_wqes) { mlx5_ib_dbg(dev, "requested sq_wqe_count (%d) > max allowed (%d)\n", - ucmd.sq_wqe_count, dev->mdev.caps.max_wqes); + ucmd.sq_wqe_count, dev->mdev->caps.max_wqes); return -EINVAL; } err = create_user_qp(dev, pd, qp, udata, &in, &resp, &inlen); @@ -957,7 +957,7 @@ static int create_qp_common(struct mlx5_ib_dev *dev, struct ib_pd *pd, in->ctx.db_rec_addr = cpu_to_be64(qp->db.dma); - err = mlx5_core_create_qp(&dev->mdev, &qp->mqp, in, inlen); + err = mlx5_core_create_qp(dev->mdev, &qp->mqp, in, inlen); if (err) { mlx5_ib_dbg(dev, "create qp failed\n"); goto err_create; @@ -1081,7 +1081,7 @@ static void destroy_qp_common(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp) if (!in) return; if (qp->state != IB_QPS_RESET) - if (mlx5_core_qp_modify(&dev->mdev, to_mlx5_state(qp->state), + if (mlx5_core_qp_modify(dev->mdev, to_mlx5_state(qp->state), MLX5_QP_STATE_RST, in, sizeof(*in), &qp->mqp)) mlx5_ib_warn(dev, "mlx5_ib: modify QP %06x to RESET failed\n", qp->mqp.qpn); @@ -1097,7 +1097,7 @@ static void destroy_qp_common(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp) mlx5_ib_unlock_cqs(send_cq, recv_cq); } - err = mlx5_core_destroy_qp(&dev->mdev, &qp->mqp); + err = mlx5_core_destroy_qp(dev->mdev, &qp->mqp); if (err) mlx5_ib_warn(dev, "failed to destroy QP 0x%x\n", qp->mqp.qpn); kfree(in); @@ -1165,7 +1165,7 @@ struct ib_qp *mlx5_ib_create_qp(struct ib_pd *pd, switch (init_attr->qp_type) { case IB_QPT_XRC_TGT: case IB_QPT_XRC_INI: - if (!(dev->mdev.caps.flags & MLX5_DEV_CAP_FLAG_XRC)) { + if (!(dev->mdev->caps.flags & MLX5_DEV_CAP_FLAG_XRC)) { mlx5_ib_dbg(dev, "XRC not supported\n"); return ERR_PTR(-ENOSYS); } @@ -1279,7 +1279,7 @@ static int ib_rate_to_mlx5(struct mlx5_ib_dev *dev, u8 rate) } else { while (rate != IB_RATE_2_5_GBPS && !(1 << (rate + MLX5_STAT_RATE_OFFSET) & - dev->mdev.caps.stat_rate_support)) + dev->mdev->caps.stat_rate_support)) --rate; } @@ -1318,9 +1318,9 @@ static int mlx5_set_path(struct mlx5_ib_dev *dev, const struct ib_ah_attr *ah, path->port = port; if (ah->ah_flags & IB_AH_GRH) { - if (ah->grh.sgid_index >= dev->mdev.caps.port[port - 1].gid_table_len) { + if (ah->grh.sgid_index >= dev->mdev->caps.port[port - 1].gid_table_len) { pr_err(KERN_ERR "sgid_index (%u) too large. max is %d\n", - ah->grh.sgid_index, dev->mdev.caps.port[port - 1].gid_table_len); + ah->grh.sgid_index, dev->mdev->caps.port[port - 1].gid_table_len); return -EINVAL; } @@ -1539,7 +1539,7 @@ static int __mlx5_ib_modify_qp(struct ib_qp *ibqp, err = -EINVAL; goto out; } - context->mtu_msgmax = (attr->path_mtu << 5) | dev->mdev.caps.log_max_msg; + context->mtu_msgmax = (attr->path_mtu << 5) | dev->mdev->caps.log_max_msg; } if (attr_mask & IB_QP_DEST_QPN) @@ -1637,7 +1637,7 @@ static int __mlx5_ib_modify_qp(struct ib_qp *ibqp, optpar = ib_mask_to_mlx5_opt(attr_mask); optpar &= opt_mask[mlx5_cur][mlx5_new][mlx5_st]; in->optparam = cpu_to_be32(optpar); - err = mlx5_core_qp_modify(&dev->mdev, to_mlx5_state(cur_state), + err = mlx5_core_qp_modify(dev->mdev, to_mlx5_state(cur_state), to_mlx5_state(new_state), in, sqd_event, &qp->mqp); if (err) @@ -1699,21 +1699,21 @@ int mlx5_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, goto out; if ((attr_mask & IB_QP_PORT) && - (attr->port_num == 0 || attr->port_num > dev->mdev.caps.num_ports)) + (attr->port_num == 0 || attr->port_num > dev->mdev->caps.num_ports)) goto out; if (attr_mask & IB_QP_PKEY_INDEX) { port = attr_mask & IB_QP_PORT ? attr->port_num : qp->port; - if (attr->pkey_index >= dev->mdev.caps.port[port - 1].pkey_table_len) + if (attr->pkey_index >= dev->mdev->caps.port[port - 1].pkey_table_len) goto out; } if (attr_mask & IB_QP_MAX_QP_RD_ATOMIC && - attr->max_rd_atomic > dev->mdev.caps.max_ra_res_qp) + attr->max_rd_atomic > dev->mdev->caps.max_ra_res_qp) goto out; if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC && - attr->max_dest_rd_atomic > dev->mdev.caps.max_ra_req_qp) + attr->max_dest_rd_atomic > dev->mdev->caps.max_ra_req_qp) goto out; if (cur_state == new_state && cur_state == IB_QPS_RESET) { @@ -2479,7 +2479,7 @@ int mlx5_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, { struct mlx5_wqe_ctrl_seg *ctrl = NULL; /* compiler warning */ struct mlx5_ib_dev *dev = to_mdev(ibqp->device); - struct mlx5_core_dev *mdev = &dev->mdev; + struct mlx5_core_dev *mdev = dev->mdev; struct mlx5_ib_qp *qp = to_mqp(ibqp); struct mlx5_ib_mr *mr; struct mlx5_wqe_data_seg *dpseg; @@ -2888,7 +2888,7 @@ static int to_ib_qp_access_flags(int mlx5_flags) static void to_ib_ah_attr(struct mlx5_ib_dev *ibdev, struct ib_ah_attr *ib_ah_attr, struct mlx5_qp_path *path) { - struct mlx5_core_dev *dev = &ibdev->mdev; + struct mlx5_core_dev *dev = ibdev->mdev; memset(ib_ah_attr, 0, sizeof(*ib_ah_attr)); ib_ah_attr->port_num = path->port; @@ -2931,7 +2931,7 @@ int mlx5_ib_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *qp_attr, int qp_attr goto out; } context = &outb->ctx; - err = mlx5_core_qp_query(&dev->mdev, &qp->mqp, outb, sizeof(*outb)); + err = mlx5_core_qp_query(dev->mdev, &qp->mqp, outb, sizeof(*outb)); if (err) goto out_free; @@ -3014,14 +3014,14 @@ struct ib_xrcd *mlx5_ib_alloc_xrcd(struct ib_device *ibdev, struct mlx5_ib_xrcd *xrcd; int err; - if (!(dev->mdev.caps.flags & MLX5_DEV_CAP_FLAG_XRC)) + if (!(dev->mdev->caps.flags & MLX5_DEV_CAP_FLAG_XRC)) return ERR_PTR(-ENOSYS); xrcd = kmalloc(sizeof(*xrcd), GFP_KERNEL); if (!xrcd) return ERR_PTR(-ENOMEM); - err = mlx5_core_xrcd_alloc(&dev->mdev, &xrcd->xrcdn); + err = mlx5_core_xrcd_alloc(dev->mdev, &xrcd->xrcdn); if (err) { kfree(xrcd); return ERR_PTR(-ENOMEM); @@ -3036,7 +3036,7 @@ int mlx5_ib_dealloc_xrcd(struct ib_xrcd *xrcd) u32 xrcdn = to_mxrcd(xrcd)->xrcdn; int err; - err = mlx5_core_xrcd_dealloc(&dev->mdev, xrcdn); + err = mlx5_core_xrcd_dealloc(dev->mdev, xrcdn); if (err) { mlx5_ib_warn(dev, "failed to dealloc xrcdn 0x%x\n", xrcdn); return err; diff --git a/drivers/infiniband/hw/mlx5/srq.c b/drivers/infiniband/hw/mlx5/srq.c index 384af6dec5eb..70bd131ba646 100644 --- a/drivers/infiniband/hw/mlx5/srq.c +++ b/drivers/infiniband/hw/mlx5/srq.c @@ -159,7 +159,7 @@ static int create_srq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_srq *srq, int page_shift; int npages; - err = mlx5_db_alloc(&dev->mdev, &srq->db); + err = mlx5_db_alloc(dev->mdev, &srq->db); if (err) { mlx5_ib_warn(dev, "alloc dbell rec failed\n"); return err; @@ -167,7 +167,7 @@ static int create_srq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_srq *srq, *srq->db.db = 0; - if (mlx5_buf_alloc(&dev->mdev, buf_size, PAGE_SIZE * 2, &srq->buf)) { + if (mlx5_buf_alloc(dev->mdev, buf_size, PAGE_SIZE * 2, &srq->buf)) { mlx5_ib_dbg(dev, "buf alloc failed\n"); err = -ENOMEM; goto err_db; @@ -212,10 +212,10 @@ err_in: mlx5_vfree(*in); err_buf: - mlx5_buf_free(&dev->mdev, &srq->buf); + mlx5_buf_free(dev->mdev, &srq->buf); err_db: - mlx5_db_free(&dev->mdev, &srq->db); + mlx5_db_free(dev->mdev, &srq->db); return err; } @@ -229,8 +229,8 @@ static void destroy_srq_user(struct ib_pd *pd, struct mlx5_ib_srq *srq) static void destroy_srq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_srq *srq) { kfree(srq->wrid); - mlx5_buf_free(&dev->mdev, &srq->buf); - mlx5_db_free(&dev->mdev, &srq->db); + mlx5_buf_free(dev->mdev, &srq->buf); + mlx5_db_free(dev->mdev, &srq->db); } struct ib_srq *mlx5_ib_create_srq(struct ib_pd *pd, @@ -248,10 +248,10 @@ struct ib_srq *mlx5_ib_create_srq(struct ib_pd *pd, u32 flgs, xrcdn; /* Sanity check SRQ size before proceeding */ - if (init_attr->attr.max_wr >= dev->mdev.caps.max_srq_wqes) { + if (init_attr->attr.max_wr >= dev->mdev->caps.max_srq_wqes) { mlx5_ib_dbg(dev, "max_wr %d, cap %d\n", init_attr->attr.max_wr, - dev->mdev.caps.max_srq_wqes); + dev->mdev->caps.max_srq_wqes); return ERR_PTR(-EINVAL); } @@ -303,7 +303,7 @@ struct ib_srq *mlx5_ib_create_srq(struct ib_pd *pd, in->ctx.pd = cpu_to_be32(to_mpd(pd)->pdn); in->ctx.db_record = cpu_to_be64(srq->db.dma); - err = mlx5_core_create_srq(&dev->mdev, &srq->msrq, in, inlen); + err = mlx5_core_create_srq(dev->mdev, &srq->msrq, in, inlen); mlx5_vfree(in); if (err) { mlx5_ib_dbg(dev, "create SRQ failed, err %d\n", err); @@ -327,7 +327,7 @@ struct ib_srq *mlx5_ib_create_srq(struct ib_pd *pd, return &srq->ibsrq; err_core: - mlx5_core_destroy_srq(&dev->mdev, &srq->msrq); + mlx5_core_destroy_srq(dev->mdev, &srq->msrq); err_usr_kern_srq: if (pd->uobject) @@ -357,7 +357,7 @@ int mlx5_ib_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr, return -EINVAL; mutex_lock(&srq->mutex); - ret = mlx5_core_arm_srq(&dev->mdev, &srq->msrq, attr->srq_limit, 1); + ret = mlx5_core_arm_srq(dev->mdev, &srq->msrq, attr->srq_limit, 1); mutex_unlock(&srq->mutex); if (ret) @@ -378,7 +378,7 @@ int mlx5_ib_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *srq_attr) if (!out) return -ENOMEM; - ret = mlx5_core_query_srq(&dev->mdev, &srq->msrq, out); + ret = mlx5_core_query_srq(dev->mdev, &srq->msrq, out); if (ret) goto out_box; @@ -396,7 +396,7 @@ int mlx5_ib_destroy_srq(struct ib_srq *srq) struct mlx5_ib_dev *dev = to_mdev(srq->device); struct mlx5_ib_srq *msrq = to_msrq(srq); - mlx5_core_destroy_srq(&dev->mdev, &msrq->msrq); + mlx5_core_destroy_srq(dev->mdev, &msrq->msrq); if (srq->uobject) { mlx5_ib_db_unmap_user(to_mucontext(srq->uobject->context), &msrq->db); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index ee24f132e319..4b7f9da4bf11 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -58,7 +58,100 @@ int mlx5_core_debug_mask; module_param_named(debug_mask, mlx5_core_debug_mask, int, 0644); MODULE_PARM_DESC(debug_mask, "debug mask: 1 = dump cmd data, 2 = dump cmd exec time, 3 = both. Default=0"); +#define MLX5_DEFAULT_PROF 2 +static int prof_sel = MLX5_DEFAULT_PROF; +module_param_named(prof_sel, prof_sel, int, 0444); +MODULE_PARM_DESC(prof_sel, "profile selector. Valid range 0 - 2"); + struct workqueue_struct *mlx5_core_wq; +static LIST_HEAD(intf_list); +static LIST_HEAD(dev_list); +static DEFINE_MUTEX(intf_mutex); + +struct mlx5_device_context { + struct list_head list; + struct mlx5_interface *intf; + void *context; +}; + +static struct mlx5_profile profile[] = { + [0] = { + .mask = 0, + }, + [1] = { + .mask = MLX5_PROF_MASK_QP_SIZE, + .log_max_qp = 12, + }, + [2] = { + .mask = MLX5_PROF_MASK_QP_SIZE | + MLX5_PROF_MASK_MR_CACHE, + .log_max_qp = 17, + .mr_cache[0] = { + .size = 500, + .limit = 250 + }, + .mr_cache[1] = { + .size = 500, + .limit = 250 + }, + .mr_cache[2] = { + .size = 500, + .limit = 250 + }, + .mr_cache[3] = { + .size = 500, + .limit = 250 + }, + .mr_cache[4] = { + .size = 500, + .limit = 250 + }, + .mr_cache[5] = { + .size = 500, + .limit = 250 + }, + .mr_cache[6] = { + .size = 500, + .limit = 250 + }, + .mr_cache[7] = { + .size = 500, + .limit = 250 + }, + .mr_cache[8] = { + .size = 500, + .limit = 250 + }, + .mr_cache[9] = { + .size = 500, + .limit = 250 + }, + .mr_cache[10] = { + .size = 500, + .limit = 250 + }, + .mr_cache[11] = { + .size = 500, + .limit = 250 + }, + .mr_cache[12] = { + .size = 64, + .limit = 32 + }, + .mr_cache[13] = { + .size = 32, + .limit = 16 + }, + .mr_cache[14] = { + .size = 16, + .limit = 8 + }, + .mr_cache[15] = { + .size = 8, + .limit = 4 + }, + }, +}; static int set_dma_caps(struct pci_dev *pdev) { @@ -299,7 +392,7 @@ static int mlx5_core_disable_hca(struct mlx5_core_dev *dev) return 0; } -int mlx5_dev_init(struct mlx5_core_dev *dev, struct pci_dev *pdev) +static int mlx5_dev_init(struct mlx5_core_dev *dev, struct pci_dev *pdev) { struct mlx5_priv *priv = &dev->priv; int err; @@ -489,7 +582,7 @@ err_dbg: } EXPORT_SYMBOL(mlx5_dev_init); -void mlx5_dev_cleanup(struct mlx5_core_dev *dev) +static void mlx5_dev_cleanup(struct mlx5_core_dev *dev) { struct mlx5_priv *priv = &dev->priv; @@ -516,7 +609,190 @@ void mlx5_dev_cleanup(struct mlx5_core_dev *dev) pci_disable_device(dev->pdev); debugfs_remove(priv->dbg_root); } -EXPORT_SYMBOL(mlx5_dev_cleanup); + +static void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv) +{ + struct mlx5_device_context *dev_ctx; + struct mlx5_core_dev *dev = container_of(priv, struct mlx5_core_dev, priv); + + dev_ctx = kmalloc(sizeof(*dev_ctx), GFP_KERNEL); + if (!dev_ctx) { + pr_warn("mlx5_add_device: alloc context failed\n"); + return; + } + + dev_ctx->intf = intf; + dev_ctx->context = intf->add(dev); + + if (dev_ctx->context) { + spin_lock_irq(&priv->ctx_lock); + list_add_tail(&dev_ctx->list, &priv->ctx_list); + spin_unlock_irq(&priv->ctx_lock); + } else { + kfree(dev_ctx); + } +} + +static void mlx5_remove_device(struct mlx5_interface *intf, struct mlx5_priv *priv) +{ + struct mlx5_device_context *dev_ctx; + struct mlx5_core_dev *dev = container_of(priv, struct mlx5_core_dev, priv); + + list_for_each_entry(dev_ctx, &priv->ctx_list, list) + if (dev_ctx->intf == intf) { + spin_lock_irq(&priv->ctx_lock); + list_del(&dev_ctx->list); + spin_unlock_irq(&priv->ctx_lock); + + intf->remove(dev, dev_ctx->context); + kfree(dev_ctx); + return; + } +} +static int mlx5_register_device(struct mlx5_core_dev *dev) +{ + struct mlx5_priv *priv = &dev->priv; + struct mlx5_interface *intf; + + mutex_lock(&intf_mutex); + list_add_tail(&priv->dev_list, &dev_list); + list_for_each_entry(intf, &intf_list, list) + mlx5_add_device(intf, priv); + mutex_unlock(&intf_mutex); + + return 0; +} +static void mlx5_unregister_device(struct mlx5_core_dev *dev) +{ + struct mlx5_priv *priv = &dev->priv; + struct mlx5_interface *intf; + + mutex_lock(&intf_mutex); + list_for_each_entry(intf, &intf_list, list) + mlx5_remove_device(intf, priv); + list_del(&priv->dev_list); + mutex_unlock(&intf_mutex); +} + +int mlx5_register_interface(struct mlx5_interface *intf) +{ + struct mlx5_priv *priv; + + if (!intf->add || !intf->remove) + return -EINVAL; + + mutex_lock(&intf_mutex); + list_add_tail(&intf->list, &intf_list); + list_for_each_entry(priv, &dev_list, dev_list) + mlx5_add_device(intf, priv); + mutex_unlock(&intf_mutex); + + return 0; +} +EXPORT_SYMBOL(mlx5_register_interface); + +void mlx5_unregister_interface(struct mlx5_interface *intf) +{ + struct mlx5_priv *priv; + + mutex_lock(&intf_mutex); + list_for_each_entry(priv, &dev_list, dev_list) + mlx5_remove_device(intf, priv); + list_del(&intf->list); + mutex_unlock(&intf_mutex); +} +EXPORT_SYMBOL(mlx5_unregister_interface); + +static void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event, + void *data) +{ + struct mlx5_priv *priv = &dev->priv; + struct mlx5_device_context *dev_ctx; + unsigned long flags; + + spin_lock_irqsave(&priv->ctx_lock, flags); + + list_for_each_entry(dev_ctx, &priv->ctx_list, list) + if (dev_ctx->intf->event) + dev_ctx->intf->event(dev, dev_ctx->context, event, data); + + spin_unlock_irqrestore(&priv->ctx_lock, flags); +} + +struct mlx5_core_event_handler { + void (*event)(struct mlx5_core_dev *dev, + enum mlx5_dev_event event, + void *data); +}; + +static int init_one(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct mlx5_core_dev *dev; + struct mlx5_priv *priv; + int err; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + dev_err(&pdev->dev, "kzalloc failed\n"); + return -ENOMEM; + } + priv = &dev->priv; + + pci_set_drvdata(pdev, dev); + + if (prof_sel < 0 || prof_sel >= ARRAY_SIZE(profile)) { + pr_warn("selected profile out of range, selecting default (%d)\n", + MLX5_DEFAULT_PROF); + prof_sel = MLX5_DEFAULT_PROF; + } + dev->profile = &profile[prof_sel]; + dev->event = mlx5_core_event; + + err = mlx5_dev_init(dev, pdev); + if (err) { + dev_err(&pdev->dev, "mlx5_dev_init failed %d\n", err); + goto out; + } + + INIT_LIST_HEAD(&priv->ctx_list); + spin_lock_init(&priv->ctx_lock); + err = mlx5_register_device(dev); + if (err) { + dev_err(&pdev->dev, "mlx5_register_device failed %d\n", err); + goto out_init; + } + + return 0; + +out_init: + mlx5_dev_cleanup(dev); +out: + kfree(dev); + return err; +} +static void remove_one(struct pci_dev *pdev) +{ + struct mlx5_core_dev *dev = pci_get_drvdata(pdev); + + mlx5_unregister_device(dev); + mlx5_dev_cleanup(dev); + kfree(dev); +} + +static const struct pci_device_id mlx5_core_pci_table[] = { + { PCI_VDEVICE(MELLANOX, 4113) }, /* MT4113 Connect-IB */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, mlx5_core_pci_table); + +static struct pci_driver mlx5_core_driver = { + .name = DRIVER_NAME, + .id_table = mlx5_core_pci_table, + .probe = init_one, + .remove = remove_one +}; static int __init init(void) { @@ -530,8 +806,15 @@ static int __init init(void) } mlx5_health_init(); + err = pci_register_driver(&mlx5_core_driver); + if (err) + goto err_health; + return 0; +err_health: + mlx5_health_cleanup(); + destroy_workqueue(mlx5_core_wq); err_debug: mlx5_unregister_debugfs(); return err; @@ -539,6 +822,7 @@ err_debug: static void __exit cleanup(void) { + pci_unregister_driver(&mlx5_core_driver); mlx5_health_cleanup(); destroy_workqueue(mlx5_core_wq); mlx5_unregister_debugfs(); diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index 2bce4aad2570..d0cb5984a45f 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -543,6 +543,10 @@ struct mlx5_priv { /* protect mkey key part */ spinlock_t mkey_lock; u8 mkey_key; + + struct list_head dev_list; + struct list_head ctx_list; + spinlock_t ctx_lock; }; struct mlx5_core_dev { @@ -686,8 +690,6 @@ static inline u32 mlx5_base_mkey(const u32 key) return key & 0xffffff00u; } -int mlx5_dev_init(struct mlx5_core_dev *dev, struct pci_dev *pdev); -void mlx5_dev_cleanup(struct mlx5_core_dev *dev); int mlx5_cmd_init(struct mlx5_core_dev *dev); void mlx5_cmd_cleanup(struct mlx5_core_dev *dev); void mlx5_cmd_use_events(struct mlx5_core_dev *dev); @@ -811,6 +813,17 @@ enum { MAX_MR_CACHE_ENTRIES = 16, }; +struct mlx5_interface { + void * (*add)(struct mlx5_core_dev *dev); + void (*remove)(struct mlx5_core_dev *dev, void *context); + void (*event)(struct mlx5_core_dev *dev, void *context, + enum mlx5_dev_event event, void *data); + struct list_head list; +}; + +int mlx5_register_interface(struct mlx5_interface *intf); +void mlx5_unregister_interface(struct mlx5_interface *intf); + struct mlx5_profile { u64 mask; u32 log_max_qp; -- cgit v1.2.3 From f241e7497ec2d22b83002b17ae91a851d4034cb7 Mon Sep 17 00:00:00 2001 From: Jack Morgenstein Date: Mon, 28 Jul 2014 23:30:23 +0300 Subject: mlx5: minor fixes (mainly avoidance of hidden casts) There were many places where parameters which should be u8/u16 were integer type. Additionally, in 2 places, a check for a non-null pointer was added before dereferencing the pointer (this is actually a bug fix). Signed-off-by: Jack Morgenstein Signed-off-by: Eli Cohen Signed-off-by: David S. Miller --- drivers/infiniband/hw/mlx5/cq.c | 2 +- drivers/infiniband/hw/mlx5/mad.c | 2 +- drivers/infiniband/hw/mlx5/main.c | 2 +- drivers/infiniband/hw/mlx5/mem.c | 2 +- drivers/infiniband/hw/mlx5/mlx5_ib.h | 2 +- drivers/infiniband/hw/mlx5/qp.c | 4 ++-- drivers/net/ethernet/mellanox/mlx5/core/alloc.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/cmd.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/eq.c | 3 ++- drivers/net/ethernet/mellanox/mlx5/core/mad.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/main.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/port.c | 2 +- include/linux/mlx5/device.h | 4 ---- include/linux/mlx5/driver.h | 8 ++++---- 15 files changed, 19 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/hw/mlx5/cq.c b/drivers/infiniband/hw/mlx5/cq.c index 3b4dc858cef9..e4056279166d 100644 --- a/drivers/infiniband/hw/mlx5/cq.c +++ b/drivers/infiniband/hw/mlx5/cq.c @@ -348,7 +348,7 @@ static void handle_atomic(struct mlx5_ib_qp *qp, struct mlx5_cqe64 *cqe64, static void handle_atomics(struct mlx5_ib_qp *qp, struct mlx5_cqe64 *cqe64, u16 tail, u16 head) { - int idx; + u16 idx; do { idx = tail & (qp->sq.wqe_cnt - 1); diff --git a/drivers/infiniband/hw/mlx5/mad.c b/drivers/infiniband/hw/mlx5/mad.c index e259e7393152..b514bbb5610f 100644 --- a/drivers/infiniband/hw/mlx5/mad.c +++ b/drivers/infiniband/hw/mlx5/mad.c @@ -41,7 +41,7 @@ enum { }; int mlx5_MAD_IFC(struct mlx5_ib_dev *dev, int ignore_mkey, int ignore_bkey, - int port, struct ib_wc *in_wc, struct ib_grh *in_grh, + u8 port, struct ib_wc *in_wc, struct ib_grh *in_grh, void *in_mad, void *response_mad) { u8 op_modifier = 0; diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index f2cfd363a705..166335a95c59 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -478,7 +478,7 @@ static struct ib_ucontext *mlx5_ib_alloc_ucontext(struct ib_device *ibdev, int uuarn; int err; int i; - int reqlen; + size_t reqlen; if (!dev->ib_active) return ERR_PTR(-EAGAIN); diff --git a/drivers/infiniband/hw/mlx5/mem.c b/drivers/infiniband/hw/mlx5/mem.c index 8499aec94db6..a3e81444c825 100644 --- a/drivers/infiniband/hw/mlx5/mem.c +++ b/drivers/infiniband/hw/mlx5/mem.c @@ -148,7 +148,7 @@ int mlx5_ib_get_buf_offset(u64 addr, int page_shift, u32 *offset) u64 off_mask; u64 buf_off; - page_size = 1 << page_shift; + page_size = (u64)1 << page_shift; page_mask = page_size - 1; buf_off = addr & page_mask; off_size = page_size >> 6; diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index a0e204ffe367..386780f0d1e1 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -461,7 +461,7 @@ void __mlx5_ib_cq_clean(struct mlx5_ib_cq *cq, u32 qpn, struct mlx5_ib_srq *srq) void mlx5_ib_cq_clean(struct mlx5_ib_cq *cq, u32 qpn, struct mlx5_ib_srq *srq); void mlx5_ib_free_srq_wqe(struct mlx5_ib_srq *srq, int wqe_index); int mlx5_MAD_IFC(struct mlx5_ib_dev *dev, int ignore_mkey, int ignore_bkey, - int port, struct ib_wc *in_wc, struct ib_grh *in_grh, + u8 port, struct ib_wc *in_wc, struct ib_grh *in_grh, void *in_mad, void *response_mad); struct ib_ah *create_ib_ah(struct ib_ah_attr *ah_attr, struct mlx5_ib_ah *ah); diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index b8bb6ad6350c..7efe6e3f3542 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -2539,7 +2539,7 @@ int mlx5_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, case IB_WR_RDMA_WRITE_WITH_IMM: set_raddr_seg(seg, wr->wr.rdma.remote_addr, wr->wr.rdma.rkey); - seg += sizeof(struct mlx5_wqe_raddr_seg); + seg += sizeof(struct mlx5_wqe_raddr_seg); size += sizeof(struct mlx5_wqe_raddr_seg) / 16; break; @@ -2668,7 +2668,7 @@ int mlx5_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, case IB_QPT_SMI: case IB_QPT_GSI: set_datagram_seg(seg, wr); - seg += sizeof(struct mlx5_wqe_datagram_seg); + seg += sizeof(struct mlx5_wqe_datagram_seg); size += sizeof(struct mlx5_wqe_datagram_seg) / 16; if (unlikely((seg == qend))) seg = mlx5_get_send_wqe(qp, 0); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/alloc.c b/drivers/net/ethernet/mellanox/mlx5/core/alloc.c index b215742b842f..56779c1c7811 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/alloc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/alloc.c @@ -56,7 +56,7 @@ int mlx5_buf_alloc(struct mlx5_core_dev *dev, int size, int max_direct, if (size <= max_direct) { buf->nbufs = 1; buf->npages = 1; - buf->page_shift = get_order(size) + PAGE_SHIFT; + buf->page_shift = (u8)get_order(size) + PAGE_SHIFT; buf->direct.buf = dma_zalloc_coherent(&dev->pdev->dev, size, &t, GFP_KERNEL); if (!buf->direct.buf) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index 87d1b018a9c3..4671747dd365 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -464,7 +464,7 @@ static void dump_command(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *msg = input ? ent->in : ent->out; struct mlx5_cmd_mailbox *next = msg->next; int data_only; - int offset = 0; + u32 offset = 0; int dump_len; data_only = !!(mlx5_core_debug_mask & (1 << MLX5_CMD_DATA)); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c index 7f39ebcd6ad0..67cead2c079e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c @@ -252,7 +252,8 @@ static int mlx5_eq_int(struct mlx5_core_dev *dev, struct mlx5_eq *eq) case MLX5_PORT_CHANGE_SUBTYPE_GUID: case MLX5_PORT_CHANGE_SUBTYPE_CLIENT_REREG: case MLX5_PORT_CHANGE_SUBTYPE_INITIALIZED: - dev->event(dev, port_subtype_event(eqe->sub_type), &port); + if (dev->event) + dev->event(dev, port_subtype_event(eqe->sub_type), &port); break; default: mlx5_core_warn(dev, "Port event with unrecognized subtype: port %d, sub_type %d\n", diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mad.c b/drivers/net/ethernet/mellanox/mlx5/core/mad.c index 18d6fd5dd90b..fd80ecfa7195 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mad.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/mad.c @@ -37,7 +37,7 @@ #include "mlx5_core.h" int mlx5_core_mad_ifc(struct mlx5_core_dev *dev, void *inb, void *outb, - u16 opmod, int port) + u16 opmod, u8 port) { struct mlx5_mad_ifc_mbox_in *in = NULL; struct mlx5_mad_ifc_mbox_out *out = NULL; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 4b7f9da4bf11..fd782bf49dc6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -311,7 +311,7 @@ static int handle_hca_cap(struct mlx5_core_dev *dev) copy_rw_fields(&set_ctx->hca_cap, &query_out->hca_cap); - if (dev->profile->mask & MLX5_PROF_MASK_QP_SIZE) + if (dev->profile && dev->profile->mask & MLX5_PROF_MASK_QP_SIZE) set_ctx->hca_cap.log_max_qp = dev->profile->log_max_qp; flags = be64_to_cpu(query_out->hca_cap.flags); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c index c2a953ef0e67..d476918ef269 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c @@ -51,7 +51,7 @@ enum { struct mlx5_pages_req { struct mlx5_core_dev *dev; - u32 func_id; + u16 func_id; s32 npages; struct work_struct work; }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/port.c b/drivers/net/ethernet/mellanox/mlx5/core/port.c index 8c9ac870ecb1..313965853e10 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/port.c @@ -86,7 +86,7 @@ struct mlx5_reg_pcap { __be32 caps_31_0; }; -int mlx5_set_port_caps(struct mlx5_core_dev *dev, int port_num, u32 caps) +int mlx5_set_port_caps(struct mlx5_core_dev *dev, u8 port_num, u32 caps) { struct mlx5_reg_pcap in; struct mlx5_reg_pcap out; diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h index 3406cfb1267a..334947151dfc 100644 --- a/include/linux/mlx5/device.h +++ b/include/linux/mlx5/device.h @@ -456,9 +456,6 @@ struct mlx5_eqe_cq_err { u8 syndrome; }; -struct mlx5_eqe_dropped_packet { -}; - struct mlx5_eqe_port_state { u8 reserved0[8]; u8 port; @@ -498,7 +495,6 @@ union ev_data { struct mlx5_eqe_comp comp; struct mlx5_eqe_qp_srq qp_srq; struct mlx5_eqe_cq_err cq_err; - struct mlx5_eqe_dropped_packet dp; struct mlx5_eqe_port_state port; struct mlx5_eqe_gpio gpio; struct mlx5_eqe_congestion cong; diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index d0cb5984a45f..76de0cc41640 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -381,8 +381,8 @@ struct mlx5_buf { struct mlx5_buf_list *page_list; int nbufs; int npages; - int page_shift; int size; + u8 page_shift; }; struct mlx5_eq { @@ -736,7 +736,7 @@ int mlx5_core_dump_fill_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mr *mr, int mlx5_core_alloc_pd(struct mlx5_core_dev *dev, u32 *pdn); int mlx5_core_dealloc_pd(struct mlx5_core_dev *dev, u32 pdn); int mlx5_core_mad_ifc(struct mlx5_core_dev *dev, void *inb, void *outb, - u16 opmod, int port); + u16 opmod, u8 port); void mlx5_pagealloc_init(struct mlx5_core_dev *dev); void mlx5_pagealloc_cleanup(struct mlx5_core_dev *dev); int mlx5_pagealloc_start(struct mlx5_core_dev *dev); @@ -769,7 +769,7 @@ void mlx5_qp_debugfs_cleanup(struct mlx5_core_dev *dev); int mlx5_core_access_reg(struct mlx5_core_dev *dev, void *data_in, int size_in, void *data_out, int size_out, u16 reg_num, int arg, int write); -int mlx5_set_port_caps(struct mlx5_core_dev *dev, int port_num, u32 caps); +int mlx5_set_port_caps(struct mlx5_core_dev *dev, u8 port_num, u32 caps); int mlx5_debug_eq_add(struct mlx5_core_dev *dev, struct mlx5_eq *eq); void mlx5_debug_eq_remove(struct mlx5_core_dev *dev, struct mlx5_eq *eq); @@ -826,7 +826,7 @@ void mlx5_unregister_interface(struct mlx5_interface *intf); struct mlx5_profile { u64 mask; - u32 log_max_qp; + u8 log_max_qp; struct { int size; int limit; -- cgit v1.2.3 From 4d2f9bbb654b91a262638ac2c84dcb169d014aa6 Mon Sep 17 00:00:00 2001 From: Jack Morgenstein Date: Mon, 28 Jul 2014 23:30:24 +0300 Subject: mlx5: Adjust events to use unsigned long param instead of void * In the event flow, we currently pass only a port number in the void *data argument. Rather than pass a pointer to the event handlers, we should use an "unsigned long" parameter, and pass the port number value directly. In the future, if necessary for some events, we can use the unsigned long parameter to pass a pointer. Based on a patch by Eli Cohen Signed-off-by: Jack Morgenstein Signed-off-by: Eli Cohen Signed-off-by: David S. Miller --- drivers/infiniband/hw/mlx5/main.c | 14 +++++++------- drivers/net/ethernet/mellanox/mlx5/core/eq.c | 3 ++- drivers/net/ethernet/mellanox/mlx5/core/main.c | 4 ++-- include/linux/mlx5/driver.h | 4 ++-- 4 files changed, 13 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 166335a95c59..d8907b20522a 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -905,7 +905,7 @@ static struct device_attribute *mlx5_class_attributes[] = { }; static void mlx5_ib_event(struct mlx5_core_dev *dev, void *context, - enum mlx5_dev_event event, void *data) + enum mlx5_dev_event event, unsigned long param) { struct mlx5_ib_dev *ibdev = (struct mlx5_ib_dev *)context; struct ib_event ibev; @@ -920,12 +920,12 @@ static void mlx5_ib_event(struct mlx5_core_dev *dev, void *context, case MLX5_DEV_EVENT_PORT_UP: ibev.event = IB_EVENT_PORT_ACTIVE; - port = *(u8 *)data; + port = (u8)param; break; case MLX5_DEV_EVENT_PORT_DOWN: ibev.event = IB_EVENT_PORT_ERR; - port = *(u8 *)data; + port = (u8)param; break; case MLX5_DEV_EVENT_PORT_INITIALIZED: @@ -934,22 +934,22 @@ static void mlx5_ib_event(struct mlx5_core_dev *dev, void *context, case MLX5_DEV_EVENT_LID_CHANGE: ibev.event = IB_EVENT_LID_CHANGE; - port = *(u8 *)data; + port = (u8)param; break; case MLX5_DEV_EVENT_PKEY_CHANGE: ibev.event = IB_EVENT_PKEY_CHANGE; - port = *(u8 *)data; + port = (u8)param; break; case MLX5_DEV_EVENT_GUID_CHANGE: ibev.event = IB_EVENT_GID_CHANGE; - port = *(u8 *)data; + port = (u8)param; break; case MLX5_DEV_EVENT_CLIENT_REREG: ibev.event = IB_EVENT_CLIENT_REREGISTER; - port = *(u8 *)data; + port = (u8)param; break; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c index 67cead2c079e..4e8bd0b34bb0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c @@ -253,7 +253,8 @@ static int mlx5_eq_int(struct mlx5_core_dev *dev, struct mlx5_eq *eq) case MLX5_PORT_CHANGE_SUBTYPE_CLIENT_REREG: case MLX5_PORT_CHANGE_SUBTYPE_INITIALIZED: if (dev->event) - dev->event(dev, port_subtype_event(eqe->sub_type), &port); + dev->event(dev, port_subtype_event(eqe->sub_type), + (unsigned long)port); break; default: mlx5_core_warn(dev, "Port event with unrecognized subtype: port %d, sub_type %d\n", diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index fd782bf49dc6..f2716cc1f51d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -704,7 +704,7 @@ void mlx5_unregister_interface(struct mlx5_interface *intf) EXPORT_SYMBOL(mlx5_unregister_interface); static void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event, - void *data) + unsigned long param) { struct mlx5_priv *priv = &dev->priv; struct mlx5_device_context *dev_ctx; @@ -714,7 +714,7 @@ static void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event list_for_each_entry(dev_ctx, &priv->ctx_list, list) if (dev_ctx->intf->event) - dev_ctx->intf->event(dev, dev_ctx->context, event, data); + dev_ctx->intf->event(dev, dev_ctx->context, event, param); spin_unlock_irqrestore(&priv->ctx_lock, flags); } diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index 76de0cc41640..9f3a5476bb71 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -559,7 +559,7 @@ struct mlx5_core_dev { struct mlx5_init_seg __iomem *iseg; void (*event) (struct mlx5_core_dev *dev, enum mlx5_dev_event event, - void *data); + unsigned long param); struct mlx5_priv priv; struct mlx5_profile *profile; atomic_t num_qps; @@ -817,7 +817,7 @@ struct mlx5_interface { void * (*add)(struct mlx5_core_dev *dev); void (*remove)(struct mlx5_core_dev *dev, void *context); void (*event)(struct mlx5_core_dev *dev, void *context, - enum mlx5_dev_event event, void *data); + enum mlx5_dev_event event, unsigned long param); struct list_head list; }; -- cgit v1.2.3 From 24a9ee0fa3d40415765d2d9f6064930d72ad8b5a Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 25 Jul 2014 17:46:10 -0700 Subject: f2fs: add tracepoint for f2fs_issue_flush This patch adds a tracepoint for f2fs_issue_flush. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 3 +++ include/trace/events/f2fs.h | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+) (limited to 'include') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index e016b97be2ac..5a53a0ac6e4f 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -239,6 +239,9 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi) struct flush_cmd_control *fcc = SM_I(sbi)->cmd_control_info; struct flush_cmd cmd; + trace_f2fs_issue_flush(sbi->sb, test_opt(sbi, NOBARRIER), + test_opt(sbi, FLUSH_MERGE)); + if (test_opt(sbi, NOBARRIER)) return 0; diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index b983990b4a9f..7d2e70e75f16 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -926,6 +926,30 @@ TRACE_EVENT(f2fs_issue_discard, (unsigned long long)__entry->blkstart, (unsigned long long)__entry->blklen) ); + +TRACE_EVENT(f2fs_issue_flush, + + TP_PROTO(struct super_block *sb, bool nobarrier, bool flush_merge), + + TP_ARGS(sb, nobarrier, flush_merge), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(bool, nobarrier) + __field(bool, flush_merge) + ), + + TP_fast_assign( + __entry->dev = sb->s_dev; + __entry->nobarrier = nobarrier; + __entry->flush_merge = flush_merge; + ), + + TP_printk("dev = (%d,%d), %s %s", + show_dev(__entry), + __entry->nobarrier ? "skip (nobarrier)" : "issue", + __entry->flush_merge ? " with flush_merge" : "") +); #endif /* _TRACE_F2FS_H */ /* This part must be outside protection */ -- cgit v1.2.3 From 95cb5745983c222867cc9ac593aebb2ad67d72c0 Mon Sep 17 00:00:00 2001 From: Dmitry Popov Date: Tue, 29 Jul 2014 03:07:52 +0400 Subject: ip_tunnel(ipv4): fix tunnels with "local any remote $remote_ip" Ipv4 tunnels created with "local any remote $ip" didn't work properly since 7d442fab0 (ipv4: Cache dst in tunnels). 99% of packets sent via those tunnels had src addr = 0.0.0.0. That was because only dst_entry was cached, although fl4.saddr has to be cached too. Every time ip_tunnel_xmit used cached dst_entry (tunnel_rtable_get returned non-NULL), fl4.saddr was initialized with tnl_params->saddr (= 0 in our case), and wasn't changed until iptunnel_xmit(). This patch adds saddr to ip_tunnel->dst_cache, fixing this issue. Reported-by: Sergey Popov Signed-off-by: Dmitry Popov Signed-off-by: David S. Miller --- include/net/ip_tunnels.h | 1 + net/ipv4/ip_tunnel.c | 29 ++++++++++++++++++----------- 2 files changed, 19 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index a4daf9eb8562..8dd8cab88b87 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h @@ -40,6 +40,7 @@ struct ip_tunnel_prl_entry { struct ip_tunnel_dst { struct dst_entry __rcu *dst; + __be32 saddr; }; struct ip_tunnel { diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 6f9de61dce5f..45920d928341 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -69,23 +69,25 @@ static unsigned int ip_tunnel_hash(__be32 key, __be32 remote) } static void __tunnel_dst_set(struct ip_tunnel_dst *idst, - struct dst_entry *dst) + struct dst_entry *dst, __be32 saddr) { struct dst_entry *old_dst; dst_clone(dst); old_dst = xchg((__force struct dst_entry **)&idst->dst, dst); dst_release(old_dst); + idst->saddr = saddr; } -static void tunnel_dst_set(struct ip_tunnel *t, struct dst_entry *dst) +static void tunnel_dst_set(struct ip_tunnel *t, + struct dst_entry *dst, __be32 saddr) { - __tunnel_dst_set(this_cpu_ptr(t->dst_cache), dst); + __tunnel_dst_set(this_cpu_ptr(t->dst_cache), dst, saddr); } static void tunnel_dst_reset(struct ip_tunnel *t) { - tunnel_dst_set(t, NULL); + tunnel_dst_set(t, NULL, 0); } void ip_tunnel_dst_reset_all(struct ip_tunnel *t) @@ -93,20 +95,25 @@ void ip_tunnel_dst_reset_all(struct ip_tunnel *t) int i; for_each_possible_cpu(i) - __tunnel_dst_set(per_cpu_ptr(t->dst_cache, i), NULL); + __tunnel_dst_set(per_cpu_ptr(t->dst_cache, i), NULL, 0); } EXPORT_SYMBOL(ip_tunnel_dst_reset_all); -static struct rtable *tunnel_rtable_get(struct ip_tunnel *t, u32 cookie) +static struct rtable *tunnel_rtable_get(struct ip_tunnel *t, + u32 cookie, __be32 *saddr) { + struct ip_tunnel_dst *idst; struct dst_entry *dst; rcu_read_lock(); - dst = rcu_dereference(this_cpu_ptr(t->dst_cache)->dst); + idst = this_cpu_ptr(t->dst_cache); + dst = rcu_dereference(idst->dst); if (dst && !atomic_inc_not_zero(&dst->__refcnt)) dst = NULL; if (dst) { - if (dst->obsolete && dst->ops->check(dst, cookie) == NULL) { + if (!dst->obsolete || dst->ops->check(dst, cookie)) { + *saddr = idst->saddr; + } else { tunnel_dst_reset(t); dst_release(dst); dst = NULL; @@ -367,7 +374,7 @@ static int ip_tunnel_bind_dev(struct net_device *dev) if (!IS_ERR(rt)) { tdev = rt->dst.dev; - tunnel_dst_set(tunnel, &rt->dst); + tunnel_dst_set(tunnel, &rt->dst, fl4.saddr); ip_rt_put(rt); } if (dev->type != ARPHRD_ETHER) @@ -610,7 +617,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, init_tunnel_flow(&fl4, protocol, dst, tnl_params->saddr, tunnel->parms.o_key, RT_TOS(tos), tunnel->parms.link); - rt = connected ? tunnel_rtable_get(tunnel, 0) : NULL; + rt = connected ? tunnel_rtable_get(tunnel, 0, &fl4.saddr) : NULL; if (!rt) { rt = ip_route_output_key(tunnel->net, &fl4); @@ -620,7 +627,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, goto tx_error; } if (connected) - tunnel_dst_set(tunnel, &rt->dst); + tunnel_dst_set(tunnel, &rt->dst, fl4.saddr); } if (rt->dst.dev == dev) { -- cgit v1.2.3 From 3900623bfaffde4eeb93ca0ba0475598a1b0cef5 Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Fri, 25 Jul 2014 07:13:38 -0300 Subject: [media] media: atmel-isi: add v4l2 async probe support Signed-off-by: Josh Wu Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/atmel-isi.c | 5 +++++ include/media/atmel-isi.h | 4 ++++ 2 files changed, 9 insertions(+) (limited to 'include') diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index 38c723aca438..c0d46128cad9 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -980,6 +980,11 @@ static int atmel_isi_probe(struct platform_device *pdev) soc_host->v4l2_dev.dev = &pdev->dev; soc_host->nr = pdev->id; + if (isi->pdata.asd_sizes) { + soc_host->asd = isi->pdata.asd; + soc_host->asd_sizes = isi->pdata.asd_sizes; + } + ret = soc_camera_host_register(soc_host); if (ret) { dev_err(&pdev->dev, "Unable to register soc camera host\n"); diff --git a/include/media/atmel-isi.h b/include/media/atmel-isi.h index 2b023471ac89..c2e570336269 100644 --- a/include/media/atmel-isi.h +++ b/include/media/atmel-isi.h @@ -106,6 +106,8 @@ #define ISI_DATAWIDTH_8 0x01 #define ISI_DATAWIDTH_10 0x02 +struct v4l2_async_subdev; + struct isi_platform_data { u8 has_emb_sync; u8 emb_crc_sync; @@ -118,6 +120,8 @@ struct isi_platform_data { u32 frate; /* Using for ISI_MCK */ u32 mck_hz; + struct v4l2_async_subdev **asd; /* Flat array, arranged in groups */ + int *asd_sizes; /* 0-terminated array of asd group sizes */ }; #endif /* __ATMEL_ISI_H__ */ -- cgit v1.2.3 From 9b62da7332a74888036e268ebb78c7681ef45216 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 30 Jul 2014 12:20:33 +0800 Subject: ACPICA: Remove a redundant cast to acpi_size for ACPI_OFFSET() macro. It is already casted to acpi_size by ACPI_PTR_DIFF() macro. Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- include/acpi/actypes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index 608a04019372..4d9b29c436b2 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -517,7 +517,7 @@ typedef u64 acpi_integer; #define ACPI_TO_POINTER(i) ACPI_ADD_PTR (void, (void *) NULL,(acpi_size) i) #define ACPI_TO_INTEGER(p) ACPI_PTR_DIFF (p, (void *) NULL) -#define ACPI_OFFSET(d, f) (acpi_size) ACPI_PTR_DIFF (&(((d *)0)->f), (void *) NULL) +#define ACPI_OFFSET(d, f) ACPI_PTR_DIFF (&(((d *) 0)->f), (void *) NULL) #define ACPI_PHYSADDR_TO_PTR(i) ACPI_TO_POINTER(i) #define ACPI_PTR_TO_PHYSADDR(i) ACPI_TO_INTEGER(i) -- cgit v1.2.3 From 90da690bc488bdc6333fe7565e6ab8daa5540327 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 30 Jul 2014 12:20:39 +0800 Subject: ACPICA: Disassembler: Add support for the ToUUID opererator (macro). This change adds support to disassemble a UUID back to the original ToUUID operator. It will detect a UUID within a standard AML Buffer. Also, a description of the UUID is emitted for "known" UUIDs, defined as UUIDs that are defined in the ACPI specification. Since this is a change for disassembler which is not shipped in the Linux kernel, the Linux kernel is not affected. Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/aclocal.h | 13 +++++++------ include/acpi/acconfig.h | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 91f801a2e689..4c9fd7ceeb24 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -730,12 +730,13 @@ union acpi_parse_value { #define ACPI_DASM_STRING 0x02 /* Buffer is a ASCII string */ #define ACPI_DASM_UNICODE 0x03 /* Buffer is a Unicode string */ #define ACPI_DASM_PLD_METHOD 0x04 /* Buffer is a _PLD method bit-packed buffer */ -#define ACPI_DASM_EISAID 0x05 /* Integer is an EISAID */ -#define ACPI_DASM_MATCHOP 0x06 /* Parent opcode is a Match() operator */ -#define ACPI_DASM_LNOT_PREFIX 0x07 /* Start of a Lnot_equal (etc.) pair of opcodes */ -#define ACPI_DASM_LNOT_SUFFIX 0x08 /* End of a Lnot_equal (etc.) pair of opcodes */ -#define ACPI_DASM_HID_STRING 0x09 /* String is a _HID or _CID */ -#define ACPI_DASM_IGNORE 0x0A /* Not used at this time */ +#define ACPI_DASM_UUID 0x05 /* Buffer is a UUID/GUID */ +#define ACPI_DASM_EISAID 0x06 /* Integer is an EISAID */ +#define ACPI_DASM_MATCHOP 0x07 /* Parent opcode is a Match() operator */ +#define ACPI_DASM_LNOT_PREFIX 0x08 /* Start of a Lnot_equal (etc.) pair of opcodes */ +#define ACPI_DASM_LNOT_SUFFIX 0x09 /* End of a Lnot_equal (etc.) pair of opcodes */ +#define ACPI_DASM_HID_STRING 0x0A /* String is a _HID or _CID */ +#define ACPI_DASM_IGNORE 0x0B /* Not used at this time */ /* * Generic operation (for example: If, While, Store) diff --git a/include/acpi/acconfig.h b/include/acpi/acconfig.h index 932a60d6ed82..5a0a3e5daf85 100644 --- a/include/acpi/acconfig.h +++ b/include/acpi/acconfig.h @@ -217,6 +217,24 @@ #define ACPI_NUM_sx_d_METHODS 4 #define ACPI_NUM_sx_w_METHODS 5 +/****************************************************************************** + * + * Miscellaneous constants + * + *****************************************************************************/ + +/* UUID constants */ + +#define UUID_BUFFER_LENGTH 16 /* Length of UUID in memory */ +#define UUID_STRING_LENGTH 36 /* Total length of a UUID string */ + +/* Positions for required hyphens (dashes) in UUID strings */ + +#define UUID_HYPHEN1_OFFSET 8 +#define UUID_HYPHEN2_OFFSET 13 +#define UUID_HYPHEN3_OFFSET 18 +#define UUID_HYPHEN4_OFFSET 23 + /****************************************************************************** * * ACPI AML Debugger -- cgit v1.2.3 From 83118b0de3a84c4728822abf6ce60bf6c8d1dbce Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 30 Jul 2014 12:21:00 +0800 Subject: ACPICA: Tables: Update for DMAR table changes. Update table compiler and disassembler for new DMAR fields introduced in Sept. 2013. Note that Linux DMAR users need to be updated after applying this change. [zetalog: changing drivers/iommu/dmar.c accordingly] Cc: David Woodhouse Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/iommu/dmar.c | 28 ++++++++++++++-------------- include/acpi/actbl2.h | 14 +++++++------- 2 files changed, 21 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index 9a4f05e5b23f..bbe33a81015c 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c @@ -84,7 +84,7 @@ void *dmar_alloc_dev_scope(void *start, void *end, int *cnt) *cnt = 0; while (start < end) { scope = start; - if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ACPI || + if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_NAMESPACE || scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT || scope->entry_type == ACPI_DMAR_SCOPE_TYPE_BRIDGE) (*cnt)++; @@ -380,7 +380,7 @@ static int __init dmar_parse_one_andd(struct acpi_dmar_header *header) struct acpi_dmar_andd *andd = (void *)header; /* Check for NUL termination within the designated length */ - if (strnlen(andd->object_name, header->length - 8) == header->length - 8) { + if (strnlen(andd->device_name, header->length - 8) == header->length - 8) { WARN_TAINT(1, TAINT_FIRMWARE_WORKAROUND, "Your BIOS is broken; ANDD object name is not NUL-terminated\n" "BIOS vendor: %s; Ver: %s; Product Version: %s\n", @@ -390,7 +390,7 @@ static int __init dmar_parse_one_andd(struct acpi_dmar_header *header) return -EINVAL; } pr_info("ANDD device: %x name: %s\n", andd->device_number, - andd->object_name); + andd->device_name); return 0; } @@ -448,17 +448,17 @@ dmar_table_print_dmar_entry(struct acpi_dmar_header *header) (unsigned long long)rmrr->base_address, (unsigned long long)rmrr->end_address); break; - case ACPI_DMAR_TYPE_ATSR: + case ACPI_DMAR_TYPE_ROOT_ATS: atsr = container_of(header, struct acpi_dmar_atsr, header); pr_info("ATSR flags: %#x\n", atsr->flags); break; - case ACPI_DMAR_HARDWARE_AFFINITY: + case ACPI_DMAR_TYPE_HARDWARE_AFFINITY: rhsa = container_of(header, struct acpi_dmar_rhsa, header); pr_info("RHSA base: %#016Lx proximity domain: %#x\n", (unsigned long long)rhsa->base_address, rhsa->proximity_domain); break; - case ACPI_DMAR_TYPE_ANDD: + case ACPI_DMAR_TYPE_NAMESPACE: /* We don't print this here because we need to sanity-check it first. So print it in dmar_parse_one_andd() instead. */ break; @@ -539,15 +539,15 @@ parse_dmar_table(void) case ACPI_DMAR_TYPE_RESERVED_MEMORY: ret = dmar_parse_one_rmrr(entry_header); break; - case ACPI_DMAR_TYPE_ATSR: + case ACPI_DMAR_TYPE_ROOT_ATS: ret = dmar_parse_one_atsr(entry_header); break; - case ACPI_DMAR_HARDWARE_AFFINITY: + case ACPI_DMAR_TYPE_HARDWARE_AFFINITY: #ifdef CONFIG_ACPI_NUMA ret = dmar_parse_one_rhsa(entry_header); #endif break; - case ACPI_DMAR_TYPE_ANDD: + case ACPI_DMAR_TYPE_NAMESPACE: ret = dmar_parse_one_andd(entry_header); break; default: @@ -631,7 +631,7 @@ static void __init dmar_acpi_insert_dev_scope(u8 device_number, for (scope = (void *)(drhd + 1); (unsigned long)scope < ((unsigned long)drhd) + drhd->header.length; scope = ((void *)scope) + scope->length) { - if (scope->entry_type != ACPI_DMAR_SCOPE_TYPE_ACPI) + if (scope->entry_type != ACPI_DMAR_SCOPE_TYPE_NAMESPACE) continue; if (scope->enumeration_id != device_number) continue; @@ -666,21 +666,21 @@ static int __init dmar_acpi_dev_scope_init(void) for (andd = (void *)dmar_tbl + sizeof(struct acpi_table_dmar); ((unsigned long)andd) < ((unsigned long)dmar_tbl) + dmar_tbl->length; andd = ((void *)andd) + andd->header.length) { - if (andd->header.type == ACPI_DMAR_TYPE_ANDD) { + if (andd->header.type == ACPI_DMAR_TYPE_NAMESPACE) { acpi_handle h; struct acpi_device *adev; if (!ACPI_SUCCESS(acpi_get_handle(ACPI_ROOT_OBJECT, - andd->object_name, + andd->device_name, &h))) { pr_err("Failed to find handle for ACPI object %s\n", - andd->object_name); + andd->device_name); continue; } acpi_bus_get_device(h, &adev); if (!adev) { pr_err("Failed to get device for ACPI object %s\n", - andd->object_name); + andd->device_name); continue; } dmar_acpi_insert_dev_scope(andd->device_number, adev); diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h index 21314d37cb07..ecff62405f17 100644 --- a/include/acpi/actbl2.h +++ b/include/acpi/actbl2.h @@ -396,7 +396,7 @@ struct acpi_table_dbgp { * Version 1 * * Conforms to "Intel Virtualization Technology for Directed I/O", - * Version 1.2, Sept. 2008 + * Version 2.2, Sept. 2013 * ******************************************************************************/ @@ -423,9 +423,9 @@ struct acpi_dmar_header { enum acpi_dmar_type { ACPI_DMAR_TYPE_HARDWARE_UNIT = 0, ACPI_DMAR_TYPE_RESERVED_MEMORY = 1, - ACPI_DMAR_TYPE_ATSR = 2, - ACPI_DMAR_HARDWARE_AFFINITY = 3, - ACPI_DMAR_TYPE_ANDD = 4, + ACPI_DMAR_TYPE_ROOT_ATS = 2, + ACPI_DMAR_TYPE_HARDWARE_AFFINITY = 3, + ACPI_DMAR_TYPE_NAMESPACE = 4, ACPI_DMAR_TYPE_RESERVED = 5 /* 5 and greater are reserved */ }; @@ -439,7 +439,7 @@ struct acpi_dmar_device_scope { u8 bus; }; -/* Values for entry_type in struct acpi_dmar_device_scope */ +/* Values for entry_type in struct acpi_dmar_device_scope - device types */ enum acpi_dmar_scope_type { ACPI_DMAR_SCOPE_TYPE_NOT_USED = 0, @@ -447,7 +447,7 @@ enum acpi_dmar_scope_type { ACPI_DMAR_SCOPE_TYPE_BRIDGE = 2, ACPI_DMAR_SCOPE_TYPE_IOAPIC = 3, ACPI_DMAR_SCOPE_TYPE_HPET = 4, - ACPI_DMAR_SCOPE_TYPE_ACPI = 5, + ACPI_DMAR_SCOPE_TYPE_NAMESPACE = 5, ACPI_DMAR_SCOPE_TYPE_RESERVED = 6 /* 6 and greater are reserved */ }; @@ -516,7 +516,7 @@ struct acpi_dmar_andd { struct acpi_dmar_header header; u8 reserved[3]; u8 device_number; - char object_name[1]; + char device_name[1]; }; /******************************************************************************* -- cgit v1.2.3 From a9f65e01aa52692756ac5e6357502921a55f8a0e Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 30 Jul 2014 12:21:26 +0800 Subject: ACPICA: ACPI 5.1: Support for the _DSD predefined name. Adds full support for _DSD. David Box. Signed-off-by: David E. Box Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/acpredef.h | 13 ++++++++++++- include/acpi/acnames.h | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/acpi/acpica/acpredef.h b/drivers/acpi/acpica/acpredef.h index bd08817cafd8..c5bfadf5294a 100644 --- a/drivers/acpi/acpica/acpredef.h +++ b/drivers/acpi/acpica/acpredef.h @@ -105,6 +105,11 @@ * count = 0 (optional) * (Used for _DLM) * + * ACPI_PTYPE2_UUID_PAIR: Each subpackage is preceded by a UUID Buffer. The UUID + * defines the format of the package. Zero-length parent package is + * allowed. + * (Used for _DSD) + * *****************************************************************************/ enum acpi_return_package_types { @@ -117,7 +122,8 @@ enum acpi_return_package_types { ACPI_PTYPE2_FIXED = 7, ACPI_PTYPE2_MIN = 8, ACPI_PTYPE2_REV_FIXED = 9, - ACPI_PTYPE2_FIX_VAR = 10 + ACPI_PTYPE2_FIX_VAR = 10, + ACPI_PTYPE2_UUID_PAIR = 11 }; /* Support macros for users of the predefined info table */ @@ -436,6 +442,11 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = { {{"_DOS", METHOD_1ARGS(ACPI_TYPE_INTEGER), METHOD_NO_RETURN_VALUE}}, + {{"_DSD", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each: 1 Buf, 1 Pkg */ + PACKAGE_INFO(ACPI_PTYPE2_UUID_PAIR, ACPI_RTYPE_BUFFER, 1, + ACPI_RTYPE_PACKAGE, 1, 0), + {{"_DSM", METHOD_4ARGS(ACPI_TYPE_BUFFER, ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER, ACPI_TYPE_PACKAGE), diff --git a/include/acpi/acnames.h b/include/acpi/acnames.h index 3dd6e838dc30..c728113374f5 100644 --- a/include/acpi/acnames.h +++ b/include/acpi/acnames.h @@ -55,6 +55,7 @@ #define METHOD_NAME__HID "_HID" #define METHOD_NAME__INI "_INI" #define METHOD_NAME__PLD "_PLD" +#define METHOD_NAME__DSD "_DSD" #define METHOD_NAME__PRS "_PRS" #define METHOD_NAME__PRT "_PRT" #define METHOD_NAME__PRW "_PRW" -- cgit v1.2.3 From b3cc1356a6bd5c1b1269f2201e7ae65a458b662d Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 30 Jul 2014 12:21:31 +0800 Subject: ACPICA: ACPI 5.1: New notify value for System Affinity Update. New value for the Notify() operator. Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/utdecode.c | 5 +++-- include/acpi/actypes.h | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/acpi/acpica/utdecode.c b/drivers/acpi/acpica/utdecode.c index 98f541873810..40e923e675fc 100644 --- a/drivers/acpi/acpica/utdecode.c +++ b/drivers/acpi/acpica/utdecode.c @@ -448,7 +448,8 @@ static const char *acpi_gbl_generic_notify[ACPI_NOTIFY_MAX + 1] = { /* 09 */ "Device PLD Check", /* 0A */ "Reserved", /* 0B */ "System Locality Update", - /* 0C */ "Shutdown Request" + /* 0C */ "Shutdown Request", + /* 0D */ "System Resource Affinity Update" }; static const char *acpi_gbl_device_notify[4] = { @@ -475,7 +476,7 @@ static const char *acpi_gbl_thermal_notify[4] = { const char *acpi_ut_get_notify_name(u32 notify_value, acpi_object_type type) { - /* 00 - 0C are common to all object types */ + /* 00 - 0D are common to all object types */ if (notify_value <= ACPI_NOTIFY_MAX) { return (acpi_gbl_generic_notify[notify_value]); diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index 4d9b29c436b2..ac03ec81d342 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -612,8 +612,9 @@ typedef u64 acpi_integer; #define ACPI_NOTIFY_RESERVED (u8) 0x0A #define ACPI_NOTIFY_LOCALITY_UPDATE (u8) 0x0B #define ACPI_NOTIFY_SHUTDOWN_REQUEST (u8) 0x0C +#define ACPI_NOTIFY_AFFINITY_UPDATE (u8) 0x0D -#define ACPI_NOTIFY_MAX 0x0C +#define ACPI_NOTIFY_MAX 0x0D /* * Types associated with ACPI names and objects. The first group of -- cgit v1.2.3 From 9eb1105babb8948c402ff785f5b43e35f79c0a0c Mon Sep 17 00:00:00 2001 From: Graeme Gregory Date: Wed, 30 Jul 2014 12:21:43 +0800 Subject: ACPICA/ARM: ACPI 5.1: Update for FADT changes. Adds ARM flags and FADT minor revision. Graeme Gregory. Signed-off-by: Graeme Gregory Signed-off-by: Hanjun Guo Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- include/acpi/actbl.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/acpi/actbl.h b/include/acpi/actbl.h index 1cc7ef13c01a..bee19d8170c5 100644 --- a/include/acpi/actbl.h +++ b/include/acpi/actbl.h @@ -270,7 +270,8 @@ struct acpi_table_fadt { u32 flags; /* Miscellaneous flag bits (see below for individual flags) */ struct acpi_generic_address reset_register; /* 64-bit address of the Reset register */ u8 reset_value; /* Value to write to the reset_register port to reset the system */ - u8 reserved4[3]; /* Reserved, must be zero */ + u16 arm_boot_flags; /* ARM-Specific Boot Flags (see below for individual flags) (ACPI 5.1) */ + u8 minor_revision; /* FADT Minor Revision (ACPI 5.1) */ u64 Xfacs; /* 64-bit physical address of FACS */ u64 Xdsdt; /* 64-bit physical address of DSDT */ struct acpi_generic_address xpm1a_event_block; /* 64-bit Extended Power Mgt 1a Event Reg Blk address */ @@ -285,7 +286,7 @@ struct acpi_table_fadt { struct acpi_generic_address sleep_status; /* 64-bit Sleep Status register (ACPI 5.0) */ }; -/* Masks for FADT Boot Architecture Flags (boot_flags) [Vx]=Introduced in this FADT revision */ +/* Masks for FADT IA-PC Boot Architecture Flags (boot_flags) [Vx]=Introduced in this FADT revision */ #define ACPI_FADT_LEGACY_DEVICES (1) /* 00: [V2] System has LPC or ISA bus devices */ #define ACPI_FADT_8042 (1<<1) /* 01: [V3] System has an 8042 controller on port 60/64 */ @@ -296,6 +297,11 @@ struct acpi_table_fadt { #define FADT2_REVISION_ID 3 +/* Masks for FADT ARM Boot Architecture Flags (arm_boot_flags) ACPI 5.1 */ + +#define ACPI_FADT_PSCI_COMPLIANT (1) /* 00: [V5+] PSCI 0.2+ is implemented */ +#define ACPI_FADT_PSCI_USE_HVC (1<<1) /* 01: [V5+] HVC must be used instead of SMC as the PSCI conduit */ + /* Masks for FADT flags */ #define ACPI_FADT_WBINVD (1) /* 00: [V1] The WBINVD instruction works properly */ @@ -399,7 +405,7 @@ struct acpi_table_desc { * FADT V5 size: 0x10C */ #define ACPI_FADT_V1_SIZE (u32) (ACPI_FADT_OFFSET (flags) + 4) -#define ACPI_FADT_V2_SIZE (u32) (ACPI_FADT_OFFSET (reserved4[0]) + 3) +#define ACPI_FADT_V2_SIZE (u32) (ACPI_FADT_OFFSET (minor_revision) + 1) #define ACPI_FADT_V3_SIZE (u32) (ACPI_FADT_OFFSET (sleep_control)) #define ACPI_FADT_V5_SIZE (u32) (sizeof (struct acpi_table_fadt)) -- cgit v1.2.3 From d0c383e48d1b6cdc39cf9cc2f082bbb6f3c28b89 Mon Sep 17 00:00:00 2001 From: Tomasz Nowicki Date: Wed, 30 Jul 2014 12:21:50 +0800 Subject: ACPICA/ARM: ACPI 5.1: Update for MADT changes. New subtables and changes to other subtables. Tomasz Nowicki. Signed-off-by: Tomasz Nowicki Signed-off-by: Hanjun Guo Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- include/acpi/actbl1.h | 44 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h index 9613e8e97960..7626bfeac2cb 100644 --- a/include/acpi/actbl1.h +++ b/include/acpi/actbl1.h @@ -671,7 +671,9 @@ enum acpi_madt_type { ACPI_MADT_TYPE_LOCAL_X2APIC_NMI = 10, ACPI_MADT_TYPE_GENERIC_INTERRUPT = 11, ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR = 12, - ACPI_MADT_TYPE_RESERVED = 13 /* 13 and greater are reserved */ + ACPI_MADT_TYPE_GENERIC_MSI_FRAME = 13, + ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR = 14, + ACPI_MADT_TYPE_RESERVED = 15 /* 15 and greater are reserved */ }; /* @@ -797,15 +799,26 @@ struct acpi_madt_local_x2apic_nmi { struct acpi_madt_generic_interrupt { struct acpi_subtable_header header; u16 reserved; /* reserved - must be zero */ - u32 gic_id; + u32 cpu_interface_number; u32 uid; u32 flags; u32 parking_version; u32 performance_interrupt; u64 parked_address; u64 base_address; + u64 gicv_base_address; + u64 gich_base_address; + u32 vgic_interrupt; + u64 gicr_base_address; + u64 arm_mpidr; }; +/* Masks for Flags field above */ + +/* ACPI_MADT_ENABLED (1) Processor is usable if set */ +#define ACPI_MADT_PERFORMANCE_IRQ_MODE (1<<1) /* 01: Performance Interrupt Mode */ +#define ACPI_MADT_VGIC_IRQ_MODE (1<<2) /* 02: VGIC Maintenance Interrupt mode */ + /* 12: Generic Distributor (ACPI 5.0) */ struct acpi_madt_generic_distributor { @@ -817,11 +830,36 @@ struct acpi_madt_generic_distributor { u32 reserved2; /* reserved - must be zero */ }; +/* 13: Generic MSI Frame (ACPI 5.1) */ + +struct acpi_madt_generic_msi_frame { + struct acpi_subtable_header header; + u16 reserved; /* reserved - must be zero */ + u32 msi_frame_id; + u64 base_address; + u32 flags; + u16 spi_count; + u16 spi_base; +}; + +/* Masks for Flags field above */ + +#define ACPI_MADT_OVERRIDE_SPI_VALUES (1) + +/* 14: Generic Redistributor (ACPI 5.1) */ + +struct acpi_madt_generic_redistributor { + struct acpi_subtable_header header; + u16 reserved; /* reserved - must be zero */ + u64 base_address; + u32 length; +}; + /* * Common flags fields for MADT subtables */ -/* MADT Local APIC flags (lapic_flags) and GIC flags */ +/* MADT Local APIC flags */ #define ACPI_MADT_ENABLED (1) /* 00: Processor is usable if set */ -- cgit v1.2.3 From 54ea4247d389059674aaacad7af224459b135fb9 Mon Sep 17 00:00:00 2001 From: Tomasz Nowicki Date: Wed, 30 Jul 2014 12:21:58 +0800 Subject: ACPICA/ARM: ACPI 5.1: Update for GTDT table changes. New fields and new subtables. Tomasz Nowicki. Signed-off-by: Tomasz Nowicki Signed-off-by: Hanjun Guo Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- include/acpi/actbl3.h | 93 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 78 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/acpi/actbl3.h b/include/acpi/actbl3.h index c2295cc4a5c0..41ad9648ca52 100644 --- a/include/acpi/actbl3.h +++ b/include/acpi/actbl3.h @@ -241,33 +241,96 @@ struct acpi_s3pt_suspend { /******************************************************************************* * - * GTDT - Generic Timer Description Table (ACPI 5.0) - * Version 1 + * GTDT - Generic Timer Description Table (ACPI 5.1) + * Version 2 * ******************************************************************************/ struct acpi_table_gtdt { struct acpi_table_header header; /* Common ACPI table header */ - u64 address; - u32 flags; - u32 secure_pl1_interrupt; - u32 secure_pl1_flags; - u32 non_secure_pl1_interrupt; - u32 non_secure_pl1_flags; + u64 counter_block_addresss; + u32 reserved; + u32 secure_el1_interrupt; + u32 secure_el1_flags; + u32 non_secure_el1_interrupt; + u32 non_secure_el1_flags; u32 virtual_timer_interrupt; u32 virtual_timer_flags; - u32 non_secure_pl2_interrupt; - u32 non_secure_pl2_flags; + u32 non_secure_el2_interrupt; + u32 non_secure_el2_flags; + u64 counter_read_block_address; + u32 platform_timer_count; + u32 platform_timer_offset; }; -/* Values for Flags field above */ +/* Flag Definitions: Timer Block Physical Timers and Virtual timers */ + +#define ACPI_GTDT_INTERRUPT_MODE (1) +#define ACPI_GTDT_INTERRUPT_POLARITY (1<<1) +#define ACPI_GTDT_ALWAYS_ON (1<<2) + +/* Common GTDT subtable header */ + +struct acpi_gtdt_header { + u8 type; + u16 length; +}; + +/* Values for GTDT subtable type above */ + +enum acpi_gtdt_type { + ACPI_GTDT_TYPE_TIMER_BLOCK = 0, + ACPI_GTDT_TYPE_WATCHDOG = 1, + ACPI_GTDT_TYPE_RESERVED = 2 /* 2 and greater are reserved */ +}; -#define ACPI_GTDT_MAPPED_BLOCK_PRESENT 1 +/* GTDT Subtables, correspond to Type in struct acpi_gtdt_header */ + +/* 0: Generic Timer Block */ + +struct acpi_gtdt_timer_block { + struct acpi_gtdt_header header; + u8 reserved; + u64 block_address; + u32 timer_count; + u32 timer_offset; +}; + +/* Timer Sub-Structure, one per timer */ + +struct acpi_gtdt_timer_entry { + u8 frame_number; + u8 reserved[3]; + u64 base_address; + u64 el0_base_address; + u32 timer_interrupt; + u32 timer_flags; + u32 virtual_timer_interrupt; + u32 virtual_timer_flags; + u32 common_flags; +}; + +/* Flag Definitions: common_flags above */ + +#define ACPI_GTDT_GT_IS_SECURE_TIMER (1) +#define ACPI_GTDT_GT_ALWAYS_ON (1<<1) + +/* 1: SBSA Generic Watchdog Structure */ + +struct acpi_gtdt_watchdog { + struct acpi_gtdt_header header; + u8 reserved; + u64 refresh_frame_address; + u64 control_frame_address; + u32 timer_interrupt; + u32 timer_flags; +}; -/* Values for all "TimerFlags" fields above */ +/* Flag Definitions: timer_flags above */ -#define ACPI_GTDT_INTERRUPT_MODE 1 -#define ACPI_GTDT_INTERRUPT_POLARITY 2 +#define ACPI_GTDT_WATCHDOG_IRQ_MODE (1) +#define ACPI_GTDT_WATCHDOG_IRQ_POLARITY (1<<1) +#define ACPI_GTDT_WATCHDOG_SECURE (1<<2) /******************************************************************************* * -- cgit v1.2.3 From 67a3d6203cdc389cc602af80d0beba819b2f2631 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 30 Jul 2014 12:22:05 +0800 Subject: ACPICA: ACPI 5.1: Update for PCCT table changes. One new subtable. Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- include/acpi/actbl3.h | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/acpi/actbl3.h b/include/acpi/actbl3.h index 41ad9648ca52..787bcc814463 100644 --- a/include/acpi/actbl3.h +++ b/include/acpi/actbl3.h @@ -448,7 +448,8 @@ struct acpi_table_pcct { enum acpi_pcct_type { ACPI_PCCT_TYPE_GENERIC_SUBSPACE = 0, - ACPI_PCCT_TYPE_RESERVED = 1 /* 1 and greater are reserved */ + ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE = 1, + ACPI_PCCT_TYPE_RESERVED = 2 /* 2 and greater are reserved */ }; /* @@ -470,6 +471,28 @@ struct acpi_pcct_subspace { u16 min_turnaround_time; }; +/* 1: HW-reduced Communications Subspace (ACPI 5.1) */ + +struct acpi_pcct_hw_reduced { + struct acpi_subtable_header header; + u32 doorbell_interrupt; + u8 flags; + u8 reserved; + u64 base_address; + u64 length; + struct acpi_generic_address doorbell_register; + u64 preserve_mask; + u64 write_mask; + u32 latency; + u32 max_access_rate; + u16 min_turnaround_time; +}; + +/* Values for doorbell flags above */ + +#define ACPI_PCCT_INTERRUPT_POLARITY (1) +#define ACPI_PCCT_INTERRUPT_MODE (1<<1) + /* * PCC memory structures (not part of the ACPI table) */ -- cgit v1.2.3 From 796ca778585d833023c18e1815b2e30a2d2f00fd Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 30 Jul 2014 12:22:12 +0800 Subject: ACPICA: Update version to 20140724. Version 20140724. Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- 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 508e5649dda9..04b2c5d5a8ed 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -46,7 +46,7 @@ /* Current ACPICA subsystem version in YYYYMMDD format */ -#define ACPI_CA_VERSION 0x20140627 +#define ACPI_CA_VERSION 0x20140724 #include #include -- cgit v1.2.3 From 8f1d26d0e59b9676587c54578f976709b625d6e9 Mon Sep 17 00:00:00 2001 From: Atsushi Kumagai Date: Wed, 30 Jul 2014 16:08:39 -0700 Subject: kexec: export free_huge_page to VMCOREINFO PG_head_mask was added into VMCOREINFO to filter huge pages in b3acc56bfe1 ("kexec: save PG_head_mask in VMCOREINFO"), but makedumpfile still need another symbol to filter *hugetlbfs* pages. If a user hope to filter user pages, makedumpfile tries to exclude them by checking the condition whether the page is anonymous, but hugetlbfs pages aren't anonymous while they also be user pages. We know it's possible to detect them in the same way as PageHuge(), so we need the start address of free_huge_page(): int PageHuge(struct page *page) { if (!PageCompound(page)) return 0; page = compound_head(page); return get_compound_page_dtor(page) == free_huge_page; } For that reason, this patch changes free_huge_page() into public to export it to VMCOREINFO. Signed-off-by: Atsushi Kumagai Acked-by: Baoquan He Cc: Vivek Goyal Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hugetlb.h | 1 + kernel/kexec.c | 2 ++ mm/hugetlb.c | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 255cd5cc0754..a23c096b3080 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -80,6 +80,7 @@ int dequeue_hwpoisoned_huge_page(struct page *page); bool isolate_huge_page(struct page *page, struct list_head *list); void putback_active_hugepage(struct page *page); bool is_hugepage_active(struct page *page); +void free_huge_page(struct page *page); #ifdef CONFIG_ARCH_WANT_HUGE_PMD_SHARE pte_t *huge_pmd_share(struct mm_struct *mm, unsigned long addr, pud_t *pud); diff --git a/kernel/kexec.c b/kernel/kexec.c index 369f41a94124..23a088fec3c0 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -1619,6 +1620,7 @@ static int __init crash_save_vmcoreinfo_init(void) #endif VMCOREINFO_NUMBER(PG_head_mask); VMCOREINFO_NUMBER(PAGE_BUDDY_MAPCOUNT_VALUE); + VMCOREINFO_SYMBOL(free_huge_page); arch_crash_save_vmcoreinfo(); update_vmcoreinfo_note(); diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 9221c02ed9e2..7a0a73d2fcff 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -856,7 +856,7 @@ struct hstate *size_to_hstate(unsigned long size) return NULL; } -static void free_huge_page(struct page *page) +void free_huge_page(struct page *page) { /* * Can't pass hstate in here because it is called from the -- cgit v1.2.3 From 16eecd9be4b05e3216b8b12707aa6f51fb197903 Mon Sep 17 00:00:00 2001 From: Anish Bhatt Date: Mon, 28 Jul 2014 20:57:07 -0700 Subject: dcbnl : Fix misleading dcb_app->priority explanation Current explanation of dcb_app->priority is wrong. It says priority is expected to be a 3-bit unsigned integer which is only true when working with DCBx-IEEE. Use of dcb_app->priority by DCBx-CEE expects it to be 802.1p user priority bitmap. Updated accordingly This affects the cxgb4 driver, but I will post those changes as part of a larger changeset shortly. Fixes: 3e29027af4372 ("dcbnl: add support for ieee8021Qaz attributes") Signed-off-by: Anish Bhatt Acked-by: John Fastabend Signed-off-by: David S. Miller --- include/uapi/linux/dcbnl.h | 3 ++- net/dcb/dcbnl.c | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/dcbnl.h b/include/uapi/linux/dcbnl.h index 6bb43382f3f3..e711f20dc522 100644 --- a/include/uapi/linux/dcbnl.h +++ b/include/uapi/linux/dcbnl.h @@ -148,7 +148,8 @@ struct cee_pfc { * * @selector: protocol identifier type * @protocol: protocol of type indicated - * @priority: 3-bit unsigned integer indicating priority + * @priority: 3-bit unsigned integer indicating priority for IEEE + * 8-bit 802.1p user priority bitmap for CEE * * ---- * Selector field values diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c index c34af7a1d2d4..ca11d283bbeb 100644 --- a/net/dcb/dcbnl.c +++ b/net/dcb/dcbnl.c @@ -1776,7 +1776,7 @@ EXPORT_SYMBOL(dcb_getapp); * * Priority 0 is an invalid priority in CEE spec. This routine * removes applications from the app list if the priority is - * set to zero. + * set to zero. Priority is expected to be 8-bit 802.1p user priority bitmap */ int dcb_setapp(struct net_device *dev, struct dcb_app *new) { @@ -1837,7 +1837,8 @@ EXPORT_SYMBOL(dcb_ieee_getapp_mask); * * This adds Application data to the list. Multiple application * entries may exists for the same selector and protocol as long - * as the priorities are different. + * as the priorities are different. Priority is expected to be a + * 3-bit unsigned integer */ int dcb_ieee_setapp(struct net_device *dev, struct dcb_app *new) { -- cgit v1.2.3 From e10038a8ec06ac819b7552bb67aaa6d2d6f850c1 Mon Sep 17 00:00:00 2001 From: Pablo Neira Date: Tue, 29 Jul 2014 18:12:15 +0200 Subject: netfilter: xt_bpf: add mising opaque struct sk_filter definition This structure is not exposed to userspace, so fix this by defining struct sk_filter; so we skip the casting in kernelspace. This is safe since userspace has no way to lurk with that internal pointer. Fixes: e6f30c7 ("netfilter: x_tables: add xt_bpf match") Signed-off-by: Pablo Neira Ayuso Acked-by: Willem de Bruijn Signed-off-by: David S. Miller --- include/uapi/linux/netfilter/xt_bpf.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/netfilter/xt_bpf.h b/include/uapi/linux/netfilter/xt_bpf.h index 5dda450eb55b..2ec9fbcd06f9 100644 --- a/include/uapi/linux/netfilter/xt_bpf.h +++ b/include/uapi/linux/netfilter/xt_bpf.h @@ -6,6 +6,8 @@ #define XT_BPF_MAX_NUM_INSTR 64 +struct sk_filter; + struct xt_bpf_info { __u16 bpf_program_num_elem; struct sock_filter bpf_program[XT_BPF_MAX_NUM_INSTR]; -- cgit v1.2.3 From 0c1d77dfb56660329d639090352bf690d3c33466 Mon Sep 17 00:00:00 2001 From: Vince Bridgers Date: Tue, 29 Jul 2014 15:19:57 -0500 Subject: net: libphy: Add phy specific function to access mmd phy registers libphy was originally written assuming all phy devices support clause 45 access extensions to the mmd registers through the indirection registers located within the first 16 phy registers. This assumption is not true in all cases, and one specific example is the Micrel ksz9021 10/100/1000 Mbps phy. Using the stmmac driver, accessing the mmd registers to query and configure energy efficient Ethernet (EEE) features yielded unexpected behavior. This patch adds mmd access functions to the phy driver that can be overriden by the phy specific driver if the phy does not support this mechanism or uses it's own non-standard access mechanism. By default, the IEEE Compatible clause 45 access mechanism described in clause 22 is used. With this patch, EEE query/configure functions as expected using the stmmac and the Micrel ksz9021 phy. Signed-off-by: Vince Bridgers Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 61 ++++++++++++++++++++++++++++++++------------------- include/linux/phy.h | 18 +++++++++++++++ 2 files changed, 56 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index e56e269a6eb3..c94e2a27446a 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -942,7 +942,7 @@ static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad, /** * phy_read_mmd_indirect - reads data from the MMD registers - * @bus: the target MII bus + * @phydev: The PHY device bus * @prtad: MMD Address * @devad: MMD DEVAD * @addr: PHY address on the MII bus @@ -955,18 +955,26 @@ static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad, * 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) +static int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, + int devad, int addr) { - mmd_phy_indirect(bus, prtad, devad, addr); + struct phy_driver *phydrv = phydev->drv; + int value = -1; - /* Read the content of the MMD's selected register */ - return bus->read(bus, addr, MII_MMD_DATA); + if (phydrv->read_mmd_indirect == NULL) { + mmd_phy_indirect(phydev->bus, prtad, devad, addr); + + /* Read the content of the MMD's selected register */ + value = phydev->bus->read(phydev->bus, addr, MII_MMD_DATA); + } else { + value = phydrv->read_mmd_indirect(phydev, prtad, devad, addr); + } + return value; } /** * phy_write_mmd_indirect - writes data to the MMD registers - * @bus: the target MII bus + * @phydev: The PHY device * @prtad: MMD Address * @devad: MMD DEVAD * @addr: PHY address on the MII bus @@ -980,13 +988,19 @@ static int phy_read_mmd_indirect(struct mii_bus *bus, int prtad, int devad, * 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) +static void phy_write_mmd_indirect(struct phy_device *phydev, int prtad, + int devad, int addr, u32 data) { - mmd_phy_indirect(bus, prtad, devad, addr); + struct phy_driver *phydrv = phydev->drv; - /* Write the data into MMD's selected register */ - bus->write(bus, addr, MII_MMD_DATA, data); + if (phydrv->write_mmd_indirect == NULL) { + mmd_phy_indirect(phydev->bus, prtad, devad, addr); + + /* Write the data into MMD's selected register */ + phydev->bus->write(phydev->bus, addr, MII_MMD_DATA, data); + } else { + phydrv->write_mmd_indirect(phydev, prtad, devad, addr, data); + } } /** @@ -1020,7 +1034,7 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) return status; /* First check if the EEE ability is supported */ - eee_cap = phy_read_mmd_indirect(phydev->bus, MDIO_PCS_EEE_ABLE, + eee_cap = phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_ABLE, MDIO_MMD_PCS, phydev->addr); if (eee_cap < 0) return eee_cap; @@ -1032,12 +1046,12 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) /* 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, + eee_lp = phy_read_mmd_indirect(phydev, 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, + eee_adv = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN, phydev->addr); if (eee_adv < 0) return eee_adv; @@ -1052,15 +1066,16 @@ int phy_init_eee(struct phy_device *phydev, bool 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, + int val = phy_read_mmd_indirect(phydev, 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); + phy_write_mmd_indirect(phydev, MDIO_CTRL1, + MDIO_MMD_PCS, phydev->addr, + val); } return 0; /* EEE supported */ @@ -1079,7 +1094,7 @@ EXPORT_SYMBOL(phy_init_eee); */ int phy_get_eee_err(struct phy_device *phydev) { - return phy_read_mmd_indirect(phydev->bus, MDIO_PCS_EEE_WK_ERR, + return phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_WK_ERR, MDIO_MMD_PCS, phydev->addr); } EXPORT_SYMBOL(phy_get_eee_err); @@ -1097,21 +1112,21 @@ 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, + val = phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_ABLE, MDIO_MMD_PCS, phydev->addr); if (val < 0) return val; data->supported = mmd_eee_cap_to_ethtool_sup_t(val); /* Get advertisement EEE */ - val = phy_read_mmd_indirect(phydev->bus, MDIO_AN_EEE_ADV, + val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN, phydev->addr); if (val < 0) return val; data->advertised = mmd_eee_adv_to_ethtool_adv_t(val); /* Get LP advertisement EEE */ - val = phy_read_mmd_indirect(phydev->bus, MDIO_AN_EEE_LPABLE, + val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_LPABLE, MDIO_MMD_AN, phydev->addr); if (val < 0) return val; @@ -1132,7 +1147,7 @@ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data) { int val = ethtool_adv_to_mmd_eee_adv_t(data->advertised); - phy_write_mmd_indirect(phydev->bus, MDIO_AN_EEE_ADV, MDIO_MMD_AN, + phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN, phydev->addr, val); return 0; diff --git a/include/linux/phy.h b/include/linux/phy.h index 68041446c450..ed39956b5613 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -545,6 +545,24 @@ struct phy_driver { */ void (*link_change_notify)(struct phy_device *dev); + /* A function provided by a phy specific driver to override the + * the PHY driver framework support for reading a MMD register + * from the PHY. If not supported, return -1. This function is + * optional for PHY specific drivers, if not provided then the + * default MMD read function is used by the PHY framework. + */ + int (*read_mmd_indirect)(struct phy_device *dev, int ptrad, + int devnum, int regnum); + + /* A function provided by a phy specific driver to override the + * the PHY driver framework support for writing a MMD register + * from the PHY. This function is optional for PHY specific drivers, + * if not provided then the default MMD read function is used by + * the PHY framework. + */ + void (*write_mmd_indirect)(struct phy_device *dev, int ptrad, + int devnum, int regnum, u32 val); + struct device_driver driver; }; #define to_phy_driver(d) container_of(d, struct phy_driver, driver) -- cgit v1.2.3 From d17d8f9dedb9dd76fd540a5c497101529d9eb25a Mon Sep 17 00:00:00 2001 From: Dave Hansen Date: Thu, 31 Jul 2014 08:40:59 -0700 Subject: x86/mm: Add tracepoints for TLB flushes We don't have any good way to figure out what kinds of flushes are being attempted. Right now, we can try to use the vm counters, but those only tell us what we actually did with the hardware (one-by-one vs full) and don't tell us what was actually _requested_. This allows us to select out "interesting" TLB flushes that we might want to optimize (like the ranged ones) and ignore the ones that we have very little control over (the ones at context switch). Signed-off-by: Dave Hansen Link: http://lkml.kernel.org/r/20140731154059.4C96CBA5@viggo.jf.intel.com Acked-by: Rik van Riel Cc: Mel Gorman Signed-off-by: H. Peter Anvin --- arch/x86/include/asm/mmu_context.h | 6 ++++++ arch/x86/mm/init.c | 7 +++++++ arch/x86/mm/tlb.c | 11 +++++++++-- include/linux/mm_types.h | 8 ++++++++ include/trace/events/tlb.h | 40 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 include/trace/events/tlb.h (limited to 'include') diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h index be12c534fd59..166af2a8e865 100644 --- a/arch/x86/include/asm/mmu_context.h +++ b/arch/x86/include/asm/mmu_context.h @@ -3,6 +3,10 @@ #include #include +#include + +#include + #include #include #include @@ -44,6 +48,7 @@ static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, /* Re-load page tables */ load_cr3(next->pgd); + trace_tlb_flush(TLB_FLUSH_ON_TASK_SWITCH, TLB_FLUSH_ALL); /* Stop flush ipis for the previous mm */ cpumask_clear_cpu(cpu, mm_cpumask(prev)); @@ -71,6 +76,7 @@ static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, * to make sure to use no freed page tables. */ load_cr3(next->pgd); + trace_tlb_flush(TLB_FLUSH_ON_TASK_SWITCH, TLB_FLUSH_ALL); load_LDT_nolock(&next->context); } } diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index f97130618113..66dba36f2343 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -18,6 +18,13 @@ #include /* for MAX_DMA_PFN */ #include +/* + * We need to define the tracepoints somewhere, and tlb.c + * is only compied when SMP=y. + */ +#define CREATE_TRACE_POINTS +#include + #include "mm_internal.h" static unsigned long __initdata pgt_buf_start; diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c index add5a0fc3c5f..6f00ecb9feeb 100644 --- a/arch/x86/mm/tlb.c +++ b/arch/x86/mm/tlb.c @@ -49,6 +49,7 @@ void leave_mm(int cpu) if (cpumask_test_cpu(cpu, mm_cpumask(active_mm))) { cpumask_clear_cpu(cpu, mm_cpumask(active_mm)); load_cr3(swapper_pg_dir); + trace_tlb_flush(TLB_FLUSH_ON_TASK_SWITCH, TLB_FLUSH_ALL); } } EXPORT_SYMBOL_GPL(leave_mm); @@ -107,15 +108,19 @@ static void flush_tlb_func(void *info) count_vm_tlb_event(NR_TLB_REMOTE_FLUSH_RECEIVED); if (this_cpu_read(cpu_tlbstate.state) == TLBSTATE_OK) { - if (f->flush_end == TLB_FLUSH_ALL) + if (f->flush_end == TLB_FLUSH_ALL) { local_flush_tlb(); - else { + trace_tlb_flush(TLB_REMOTE_SHOOTDOWN, TLB_FLUSH_ALL); + } else { unsigned long addr; + unsigned long nr_pages = + f->flush_end - f->flush_start / PAGE_SIZE; addr = f->flush_start; while (addr < f->flush_end) { __flush_tlb_single(addr); addr += PAGE_SIZE; } + trace_tlb_flush(TLB_REMOTE_SHOOTDOWN, nr_pages); } } else leave_mm(smp_processor_id()); @@ -153,6 +158,7 @@ void flush_tlb_current_task(void) count_vm_tlb_event(NR_TLB_LOCAL_FLUSH_ALL); local_flush_tlb(); + trace_tlb_flush(TLB_LOCAL_SHOOTDOWN, TLB_FLUSH_ALL); 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(); @@ -191,6 +197,7 @@ void flush_tlb_mm_range(struct mm_struct *mm, unsigned long start, __flush_tlb_single(addr); } } + trace_tlb_flush(TLB_LOCAL_MM_SHOOTDOWN, base_pages_to_flush); out: if (base_pages_to_flush == TLB_FLUSH_ALL) { start = 0UL; diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 96c5750e3110..796deac19fcf 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -516,4 +516,12 @@ struct vm_special_mapping struct page **pages; }; +enum tlb_flush_reason { + TLB_FLUSH_ON_TASK_SWITCH, + TLB_REMOTE_SHOOTDOWN, + TLB_LOCAL_SHOOTDOWN, + TLB_LOCAL_MM_SHOOTDOWN, + NR_TLB_FLUSH_REASONS, +}; + #endif /* _LINUX_MM_TYPES_H */ diff --git a/include/trace/events/tlb.h b/include/trace/events/tlb.h new file mode 100644 index 000000000000..13391d288107 --- /dev/null +++ b/include/trace/events/tlb.h @@ -0,0 +1,40 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM tlb + +#if !defined(_TRACE_TLB_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_TLB_H + +#include +#include + +#define TLB_FLUSH_REASON \ + { TLB_FLUSH_ON_TASK_SWITCH, "flush on task switch" }, \ + { TLB_REMOTE_SHOOTDOWN, "remote shootdown" }, \ + { TLB_LOCAL_SHOOTDOWN, "local shootdown" }, \ + { TLB_LOCAL_MM_SHOOTDOWN, "local mm shootdown" } + +TRACE_EVENT(tlb_flush, + + TP_PROTO(int reason, unsigned long pages), + TP_ARGS(reason, pages), + + TP_STRUCT__entry( + __field( int, reason) + __field(unsigned long, pages) + ), + + TP_fast_assign( + __entry->reason = reason; + __entry->pages = pages; + ), + + TP_printk("pages:%ld reason:%s (%d)", + __entry->pages, + __print_symbolic(__entry->reason, TLB_FLUSH_REASON), + __entry->reason) +); + +#endif /* _TRACE_TLB_H */ + +/* This part must be outside protection */ +#include -- cgit v1.2.3 From dc6be9f54a4ecb0a09765d1f515ed947d86b7528 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Wed, 30 Jul 2014 23:21:06 +0200 Subject: bcma: use NS prefix for names of Northstar specific cores MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's cleaner and we don't have quite identical names like BCMA_CORE_PCIEG2 and BCMA_CORE_PCIE2. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/bcma/scan.c | 22 +++++++++++----------- include/linux/bcma/bcma.h | 22 +++++++++++----------- 2 files changed, 22 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/bcma/scan.c b/drivers/bcma/scan.c index 37768401d113..b4764c6bcf17 100644 --- a/drivers/bcma/scan.c +++ b/drivers/bcma/scan.c @@ -32,17 +32,17 @@ static const struct bcma_device_id_name bcma_bcm_device_names[] = { { BCMA_CORE_4706_CHIPCOMMON, "BCM4706 ChipCommon" }, { BCMA_CORE_4706_SOC_RAM, "BCM4706 SOC RAM" }, { BCMA_CORE_4706_MAC_GBIT, "BCM4706 GBit MAC" }, - { BCMA_CORE_PCIEG2, "PCIe Gen 2" }, - { BCMA_CORE_DMA, "DMA" }, - { BCMA_CORE_SDIO3, "SDIO3" }, - { BCMA_CORE_USB20, "USB 2.0" }, - { BCMA_CORE_USB30, "USB 3.0" }, - { BCMA_CORE_A9JTAG, "ARM Cortex A9 JTAG" }, - { BCMA_CORE_DDR23, "Denali DDR2/DDR3 memory controller" }, - { BCMA_CORE_ROM, "ROM" }, - { BCMA_CORE_NAND, "NAND flash controller" }, - { BCMA_CORE_QSPI, "SPI flash controller" }, - { BCMA_CORE_CHIPCOMMON_B, "Chipcommon B" }, + { BCMA_CORE_NS_PCIEG2, "PCIe Gen 2" }, + { BCMA_CORE_NS_DMA, "DMA" }, + { BCMA_CORE_NS_SDIO3, "SDIO3" }, + { BCMA_CORE_NS_USB20, "USB 2.0" }, + { BCMA_CORE_NS_USB30, "USB 3.0" }, + { BCMA_CORE_NS_A9JTAG, "ARM Cortex A9 JTAG" }, + { BCMA_CORE_NS_DDR23, "Denali DDR2/DDR3 memory controller" }, + { BCMA_CORE_NS_ROM, "ROM" }, + { BCMA_CORE_NS_NAND, "NAND flash controller" }, + { BCMA_CORE_NS_QSPI, "SPI flash controller" }, + { BCMA_CORE_NS_CHIPCOMMON_B, "Chipcommon B" }, { BCMA_CORE_ARMCA9, "ARM Cortex A9 core (ihost)" }, { BCMA_CORE_AMEMC, "AMEMC (DDR)" }, { BCMA_CORE_ALTA, "ALTA (I2S)" }, diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 70b8d88b3982..0272e49135d0 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -73,17 +73,17 @@ 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_PCIEG2 0x501 -#define BCMA_CORE_DMA 0x502 -#define BCMA_CORE_SDIO3 0x503 -#define BCMA_CORE_USB20 0x504 -#define BCMA_CORE_USB30 0x505 -#define BCMA_CORE_A9JTAG 0x506 -#define BCMA_CORE_DDR23 0x507 -#define BCMA_CORE_ROM 0x508 -#define BCMA_CORE_NAND 0x509 -#define BCMA_CORE_QSPI 0x50A -#define BCMA_CORE_CHIPCOMMON_B 0x50B +#define BCMA_CORE_NS_PCIEG2 0x501 +#define BCMA_CORE_NS_DMA 0x502 +#define BCMA_CORE_NS_SDIO3 0x503 +#define BCMA_CORE_NS_USB20 0x504 +#define BCMA_CORE_NS_USB30 0x505 +#define BCMA_CORE_NS_A9JTAG 0x506 +#define BCMA_CORE_NS_DDR23 0x507 +#define BCMA_CORE_NS_ROM 0x508 +#define BCMA_CORE_NS_NAND 0x509 +#define BCMA_CORE_NS_QSPI 0x50A +#define BCMA_CORE_NS_CHIPCOMMON_B 0x50B #define BCMA_CORE_4706_SOC_RAM 0x50E #define BCMA_CORE_ARMCA9 0x510 #define BCMA_CORE_4706_MAC_GBIT 0x52D -- cgit v1.2.3 From d7821953cfe9803c593a682320468ce2de862803 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Tue, 29 Jul 2014 18:38:39 +0800 Subject: ASoC: wm8962: Let CODEC driver enable and disable its own MCLK snd_soc_open() will trigger pm_runtime resume() which will then enable the regulator and initialization. So we should make sure the MCLK is enabled before this resume(). Previously we let the machine driver get the clock and enable it in its probe(). However, considering about power saving, it'll be better to enable it when it's going to be used and disable it after using. So this patch just simply adds clk_get() and clk_enable() to WM8962 driver. Meanwhile, it marks clock pointer to NULL if no clock assigned to it so it will not break any current function. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown --- include/sound/wm8962.h | 1 + sound/soc/codecs/wm8962.c | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) (limited to 'include') diff --git a/include/sound/wm8962.h b/include/sound/wm8962.h index 79e6d427b858..0af7c1674cbf 100644 --- a/include/sound/wm8962.h +++ b/include/sound/wm8962.h @@ -37,6 +37,7 @@ #define WM8962_GPIO_FN_MICSCD 22 struct wm8962_pdata { + struct clk *mclk; int gpio_base; u32 gpio_init[WM8962_MAX_GPIO]; diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index ca2fda9d72be..eac29031ae26 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -3541,6 +3542,8 @@ static int wm8962_set_pdata_from_of(struct i2c_client *i2c, pdata->gpio_init[i] = 0x0; } + pdata->mclk = devm_clk_get(&i2c->dev, NULL); + return 0; } @@ -3572,6 +3575,14 @@ static int wm8962_i2c_probe(struct i2c_client *i2c, return ret; } + /* Mark the mclk pointer to NULL if no mclk assigned */ + if (IS_ERR(wm8962->pdata.mclk)) { + /* But do not ignore the request for probe defer */ + if (PTR_ERR(wm8962->pdata.mclk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + wm8962->pdata.mclk = NULL; + } + for (i = 0; i < ARRAY_SIZE(wm8962->supplies); i++) wm8962->supplies[i].supply = wm8962_supply_names[i]; @@ -3780,6 +3791,12 @@ static int wm8962_runtime_resume(struct device *dev) struct wm8962_priv *wm8962 = dev_get_drvdata(dev); int ret; + ret = clk_prepare_enable(wm8962->pdata.mclk); + if (ret) { + dev_err(dev, "Failed to enable MCLK: %d\n", ret); + return ret; + } + ret = regulator_bulk_enable(ARRAY_SIZE(wm8962->supplies), wm8962->supplies); if (ret != 0) { @@ -3839,6 +3856,8 @@ static int wm8962_runtime_suspend(struct device *dev) regulator_bulk_disable(ARRAY_SIZE(wm8962->supplies), wm8962->supplies); + clk_disable_unprepare(wm8962->pdata.mclk); + return 0; } #endif -- cgit v1.2.3 From a779ca5fa766e270b9e11c162d877295e2904f4e Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 29 Jul 2014 17:26:04 -0400 Subject: xprtrdma: Remove RPCRDMA_PERSISTENT_REGISTRATION macro Clean up. RPCRDMA_PERSISTENT_REGISTRATION was a compile-time switch between RPCRDMA_REGISTER mode and RPCRDMA_ALLPHYSICAL mode. Since RPCRDMA_REGISTER has been removed, there's no need for the extra conditional compilation. Signed-off-by: Chuck Lever Tested-by: Steve Wise Tested-by: Shirley Ma Tested-by: Devesh Sharma Signed-off-by: Anna Schumaker --- include/linux/sunrpc/xprtrdma.h | 2 -- net/sunrpc/xprtrdma/verbs.c | 13 ------------- 2 files changed, 15 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/xprtrdma.h b/include/linux/sunrpc/xprtrdma.h index c2f04e1ae159..64a0a0a97b23 100644 --- a/include/linux/sunrpc/xprtrdma.h +++ b/include/linux/sunrpc/xprtrdma.h @@ -62,8 +62,6 @@ #define RPCRDMA_INLINE_PAD_THRESH (512)/* payload threshold to pad (bytes) */ /* memory registration strategies */ -#define RPCRDMA_PERSISTENT_REGISTRATION (1) - enum rpcrdma_memreg { RPCRDMA_BOUNCEBUFFERS = 0, RPCRDMA_REGISTER, diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index 1208ab2e655f..c2253d4c64df 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -561,12 +561,7 @@ rpcrdma_ia_open(struct rpcrdma_xprt *xprt, struct sockaddr *addr, int memreg) if (!ia->ri_id->device->alloc_fmr) { dprintk("RPC: %s: MTHCAFMR registration " "not supported by HCA\n", __func__); -#if RPCRDMA_PERSISTENT_REGISTRATION memreg = RPCRDMA_ALLPHYSICAL; -#else - rc = -ENOMEM; - goto out2; -#endif } } @@ -581,20 +576,16 @@ rpcrdma_ia_open(struct rpcrdma_xprt *xprt, struct sockaddr *addr, int memreg) switch (memreg) { case RPCRDMA_FRMR: break; -#if RPCRDMA_PERSISTENT_REGISTRATION case RPCRDMA_ALLPHYSICAL: mem_priv = IB_ACCESS_LOCAL_WRITE | IB_ACCESS_REMOTE_WRITE | IB_ACCESS_REMOTE_READ; goto register_setup; -#endif case RPCRDMA_MTHCAFMR: if (ia->ri_have_dma_lkey) break; mem_priv = IB_ACCESS_LOCAL_WRITE; -#if RPCRDMA_PERSISTENT_REGISTRATION register_setup: -#endif ia->ri_bind_mem = ib_get_dma_mr(ia->ri_pd, mem_priv); if (IS_ERR(ia->ri_bind_mem)) { printk(KERN_ALERT "%s: ib_get_dma_mr for " @@ -1905,7 +1896,6 @@ rpcrdma_register_external(struct rpcrdma_mr_seg *seg, switch (ia->ri_memreg_strategy) { -#if RPCRDMA_PERSISTENT_REGISTRATION case RPCRDMA_ALLPHYSICAL: rpcrdma_map_one(ia, seg, writing); seg->mr_rkey = ia->ri_bind_mem->rkey; @@ -1913,7 +1903,6 @@ rpcrdma_register_external(struct rpcrdma_mr_seg *seg, seg->mr_nsegs = 1; nsegs = 1; break; -#endif /* Registration using frmr registration */ case RPCRDMA_FRMR: @@ -1943,13 +1932,11 @@ rpcrdma_deregister_external(struct rpcrdma_mr_seg *seg, switch (ia->ri_memreg_strategy) { -#if RPCRDMA_PERSISTENT_REGISTRATION case RPCRDMA_ALLPHYSICAL: read_lock(&ia->ri_qplock); rpcrdma_unmap_one(ia, seg); read_unlock(&ia->ri_qplock); break; -#endif case RPCRDMA_FRMR: rc = rpcrdma_deregister_frmr_external(seg, ia, r_xprt); -- cgit v1.2.3 From 3b57de958e2aa39abe020eb31bf19000d5899389 Mon Sep 17 00:00:00 2001 From: Vince Bridgers Date: Thu, 31 Jul 2014 15:49:17 -0500 Subject: net: stmmac: Support devicetree configs for mcast and ucast filter entries This patch adds and modifies code to support multiple Multicast and Unicast Synopsys MAC filter configurations. The default configuration is defined to support legacy driver behavior, which is 64 Multicast bins. The Unicast filter code previously assumed all controllers support 32 or 16 Unicast addresses based on controller version number, but this has been corrected to support a default of 1 Unicast address. The filter configuration may be specified through the devicetree using a Synopsys specific device tree entry. This information was verified with Synopsys through Synopsys Support Case #8000684337 and shared with the maintainer. Signed-off-by: Vince Bridgers Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 8 ++- drivers/net/ethernet/stmicro/stmmac/dwmac1000.h | 1 + .../net/ethernet/stmicro/stmmac/dwmac1000_core.c | 66 +++++++++++++++------ .../net/ethernet/stmicro/stmmac/dwmac100_core.c | 3 +- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 6 +- .../net/ethernet/stmicro/stmmac/stmmac_platform.c | 67 ++++++++++++++++++++++ include/linux/stmmac.h | 2 + 7 files changed, 132 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 49f72e1ffbef..de507c32036c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -381,7 +381,7 @@ struct stmmac_ops { int (*host_irq_status)(struct mac_device_info *hw, struct stmmac_extra_stats *x); /* Multicast filter setting */ - void (*set_filter)(struct net_device *dev); + void (*set_filter)(struct mac_device_info *hw, struct net_device *dev); /* Flow control setting */ void (*flow_ctrl)(struct mac_device_info *hw, unsigned int duplex, unsigned int fc, unsigned int pause_time); @@ -442,9 +442,13 @@ struct mac_device_info { struct mac_link link; unsigned int synopsys_uid; void __iomem *pcsr; /* vpointer to device CSRs */ + int multicast_filter_bins; + int unicast_filter_entries; + int mcast_bits_log2; }; -struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr); +struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr, int mcbins, + int perfect_uc_entries); struct mac_device_info *dwmac100_setup(void __iomem *ioaddr); void stmmac_set_mac_addr(void __iomem *ioaddr, u8 addr[6], diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h index 285e3056f362..71b5419256c1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h @@ -261,6 +261,7 @@ enum rtc_control { #define GMAC_MMC_RX_INTR 0x104 #define GMAC_MMC_TX_INTR 0x108 #define GMAC_MMC_RX_CSUM_OFFLOAD 0x208 +#define GMAC_EXTHASH_BASE 0x500 extern const struct stmmac_dma_ops dwmac1000_dma_ops; #endif /* __DWMAC1000_H__ */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c index cdcbad1f1ac0..d8ef18786a1c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c @@ -97,12 +97,41 @@ static void dwmac1000_get_umac_addr(struct mac_device_info *hw, GMAC_ADDR_LOW(reg_n)); } -static void dwmac1000_set_filter(struct net_device *dev) +static void dwmac1000_set_mchash(void __iomem *ioaddr, u32 *mcfilterbits, + int mcbitslog2) +{ + int numhashregs, regs; + + switch (mcbitslog2) { + case 6: + writel(mcfilterbits[0], ioaddr + GMAC_HASH_LOW); + writel(mcfilterbits[1], ioaddr + GMAC_HASH_HIGH); + return; + break; + case 7: + numhashregs = 4; + break; + case 8: + numhashregs = 8; + break; + default: + pr_debug("STMMAC: err in setting mulitcast filter\n"); + return; + break; + } + for (regs = 0; regs < numhashregs; regs++) + writel(mcfilterbits[regs], + ioaddr + GMAC_EXTHASH_BASE + regs * 4); +} + +static void dwmac1000_set_filter(struct mac_device_info *hw, + struct net_device *dev) { void __iomem *ioaddr = (void __iomem *)dev->base_addr; unsigned int value = 0; - unsigned int perfect_addr_number; + unsigned int perfect_addr_number = hw->unicast_filter_entries; u32 mc_filter[2]; + int mcbitslog2 = hw->mcast_bits_log2; pr_debug("%s: # mcasts %d, # unicast %d\n", __func__, netdev_mc_count(dev), netdev_uc_count(dev)); @@ -120,10 +149,14 @@ static void dwmac1000_set_filter(struct net_device *dev) value = GMAC_FRAME_FILTER_HMC; netdev_for_each_mc_addr(ha, dev) { - /* The upper 6 bits of the calculated CRC are used to - * index the contens of the hash table + /* The upper n bits of the calculated CRC are used to + * index the contents of the hash table. The number of + * bits used depends on the hardware configuration + * selected at core configuration time. */ - int bit_nr = bitrev32(~crc32_le(~0, ha->addr, 6)) >> 26; + int bit_nr = bitrev32(~crc32_le(~0, ha->addr, + ETH_ALEN)) >> + (32 - mcbitslog2); /* The most significant bit determines the register to * use (H/L) while the other 5 bits determine the bit * within the register. @@ -132,15 +165,12 @@ static void dwmac1000_set_filter(struct net_device *dev) } } - writel(mc_filter[0], ioaddr + GMAC_HASH_LOW); - writel(mc_filter[1], ioaddr + GMAC_HASH_HIGH); - - perfect_addr_number = GMAC_MAX_PERFECT_ADDRESSES; + dwmac1000_set_mchash(ioaddr, mc_filter, mcbitslog2); /* Handle multiple unicast addresses (perfect filtering) */ if (netdev_uc_count(dev) > perfect_addr_number) - /* Switch to promiscuous mode if more than 16 addrs - * are required + /* Switch to promiscuous mode if more than unicast + * addresses are requested than supported by hardware. */ value |= GMAC_FRAME_FILTER_PR; else { @@ -160,10 +190,6 @@ static void dwmac1000_set_filter(struct net_device *dev) value |= GMAC_FRAME_FILTER_RA; #endif writel(value, ioaddr + GMAC_FRAME_FILTER); - - pr_debug("\tFilter: 0x%08x\n\tHash: HI 0x%08x, LO 0x%08x\n", - readl(ioaddr + GMAC_FRAME_FILTER), - readl(ioaddr + GMAC_HASH_HIGH), readl(ioaddr + GMAC_HASH_LOW)); } @@ -382,7 +408,8 @@ static const struct stmmac_ops dwmac1000_ops = { .get_adv = dwmac1000_get_adv, }; -struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr) +struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr, int mcbins, + int perfect_uc_entries) { struct mac_device_info *mac; u32 hwid = readl(ioaddr + GMAC_VERSION); @@ -392,6 +419,13 @@ struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr) return NULL; mac->pcsr = ioaddr; + mac->multicast_filter_bins = mcbins; + mac->unicast_filter_entries = perfect_uc_entries; + mac->mcast_bits_log2 = 0; + + if (mac->multicast_filter_bins) + mac->mcast_bits_log2 = ilog2(mac->multicast_filter_bins); + mac->mac = &dwmac1000_ops; mac->dma = &dwmac1000_dma_ops; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c index 3a2d63388f8d..f8dd773f246c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c @@ -95,7 +95,8 @@ static void dwmac100_get_umac_addr(struct mac_device_info *hw, stmmac_get_mac_addr(ioaddr, addr, MAC_ADDR_HIGH, MAC_ADDR_LOW); } -static void dwmac100_set_filter(struct net_device *dev) +static void dwmac100_set_filter(struct mac_device_info *hw, + struct net_device *dev) { void __iomem *ioaddr = (void __iomem *)dev->base_addr; u32 value = readl(ioaddr + MAC_CONTROL); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index cff2b69e62ee..08addd653728 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2225,7 +2225,7 @@ static void stmmac_set_rx_mode(struct net_device *dev) struct stmmac_priv *priv = netdev_priv(dev); spin_lock(&priv->lock); - priv->hw->mac->set_filter(dev); + priv->hw->mac->set_filter(priv->hw, dev); spin_unlock(&priv->lock); } @@ -2598,7 +2598,9 @@ static int stmmac_hw_init(struct stmmac_priv *priv) /* Identify the MAC HW device */ if (priv->plat->has_gmac) { priv->dev->priv_flags |= IFF_UNICAST_FLT; - mac = dwmac1000_setup(priv->ioaddr); + mac = dwmac1000_setup(priv->ioaddr, + priv->plat->multicast_filter_bins, + priv->plat->unicast_filter_entries); } else { mac = dwmac100_setup(priv->ioaddr); } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index ea7a65be1f9a..bb524a932be4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -52,6 +52,59 @@ static const struct of_device_id stmmac_dt_ids[] = { MODULE_DEVICE_TABLE(of, stmmac_dt_ids); #ifdef CONFIG_OF + +/* This function validates the number of Multicast filtering bins specified + * by the configuration through the device tree. The Synopsys GMAC supports + * 64 bins, 128 bins, or 256 bins. "bins" refer to the division of CRC + * number space. 64 bins correspond to 6 bits of the CRC, 128 corresponds + * to 7 bits, and 256 refers to 8 bits of the CRC. Any other setting is + * invalid and will cause the filtering algorithm to use Multicast + * promiscuous mode. + */ +static int dwmac1000_validate_mcast_bins(int mcast_bins) +{ + int x = mcast_bins; + + switch (x) { + case HASH_TABLE_SIZE: + case 128: + case 256: + break; + default: + x = 0; + pr_info("Hash table entries set to unexpected value %d", + mcast_bins); + break; + } + return x; +} + +/* This function validates the number of Unicast address entries supported + * by a particular Synopsys 10/100/1000 controller. The Synopsys controller + * supports 1, 32, 64, or 128 Unicast filter entries for it's Unicast filter + * logic. This function validates a valid, supported configuration is + * selected, and defaults to 1 Unicast address if an unsupported + * configuration is selected. + */ +static int dwmac1000_validate_ucast_entries(int ucast_entries) +{ + int x = ucast_entries; + + switch (x) { + case 1: + case 32: + case 64: + case 128: + break; + default: + x = 1; + pr_info("Unicast table entries set to unexpected value %d\n", + ucast_entries); + break; + } + return x; +} + static int stmmac_probe_config_dt(struct platform_device *pdev, struct plat_stmmacenet_data *plat, const char **mac) @@ -115,6 +168,12 @@ static int stmmac_probe_config_dt(struct platform_device *pdev, */ plat->maxmtu = JUMBO_LEN; + /* Set default value for multicast hash bins */ + plat->multicast_filter_bins = HASH_TABLE_SIZE; + + /* Set default value for unicast filter entries */ + plat->unicast_filter_entries = 1; + /* * Currently only the properties needed on SPEAr600 * are provided. All other properties should be added @@ -131,6 +190,14 @@ static int stmmac_probe_config_dt(struct platform_device *pdev, * are clearly MTUs */ of_property_read_u32(np, "max-frame-size", &plat->maxmtu); + of_property_read_u32(np, "snps,multicast-filter-bins", + &plat->multicast_filter_bins); + of_property_read_u32(np, "snps,perfect-filter-entries", + &plat->unicast_filter_entries); + plat->unicast_filter_entries = dwmac1000_validate_ucast_entries( + plat->unicast_filter_entries); + plat->multicast_filter_bins = dwmac1000_validate_mcast_bins( + plat->multicast_filter_bins); plat->has_gmac = 1; plat->pmt = 1; } diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index 6f27d4f957bd..cd63851b57f2 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -112,6 +112,8 @@ struct plat_stmmacenet_data { int riwt_off; int max_speed; int maxmtu; + int multicast_filter_bins; + int unicast_filter_entries; void (*fix_mac_speed)(void *priv, unsigned int speed); void (*bus_setup)(void __iomem *ioaddr); void *(*setup)(struct platform_device *pdev); -- cgit v1.2.3 From a6138db815df5ee542d848318e5dae681590fccd Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 28 Jul 2014 16:26:53 -0700 Subject: mnt: Only change user settable mount flags in remount Kenton Varda discovered that by remounting a read-only bind mount read-only in a user namespace the MNT_LOCK_READONLY bit would be cleared, allowing an unprivileged user to the remount a read-only mount read-write. Correct this by replacing the mask of mount flags to preserve with a mask of mount flags that may be changed, and preserve all others. This ensures that any future bugs with this mask and remount will fail in an easy to detect way where new mount flags simply won't change. Cc: stable@vger.kernel.org Acked-by: Serge E. Hallyn Signed-off-by: "Eric W. Biederman" --- fs/namespace.c | 2 +- include/linux/mount.h | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/fs/namespace.c b/fs/namespace.c index 7187d01329c3..cb40449ea0df 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1937,7 +1937,7 @@ static int do_remount(struct path *path, int flags, int mnt_flags, err = do_remount_sb(sb, flags, data, 0); if (!err) { lock_mount_hash(); - mnt_flags |= mnt->mnt.mnt_flags & MNT_PROPAGATION_MASK; + mnt_flags |= mnt->mnt.mnt_flags & ~MNT_USER_SETTABLE_MASK; mnt->mnt.mnt_flags = mnt_flags; touch_mnt_namespace(mnt->mnt_ns); unlock_mount_hash(); diff --git a/include/linux/mount.h b/include/linux/mount.h index 839bac270904..b637a89e1fae 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -42,7 +42,9 @@ struct mnt_namespace; * flag, consider how it interacts with shared mounts. */ #define MNT_SHARED_MASK (MNT_UNBINDABLE) -#define MNT_PROPAGATION_MASK (MNT_SHARED | MNT_UNBINDABLE) +#define MNT_USER_SETTABLE_MASK (MNT_NOSUID | MNT_NODEV | MNT_NOEXEC \ + | MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME \ + | MNT_READONLY) #define MNT_INTERNAL_FLAGS (MNT_SHARED | MNT_WRITE_HOLD | MNT_INTERNAL | \ MNT_DOOMED | MNT_SYNC_UMOUNT | MNT_MARKED) -- cgit v1.2.3 From 9566d6742852c527bf5af38af5cbb878dad75705 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 28 Jul 2014 17:26:07 -0700 Subject: mnt: Correct permission checks in do_remount While invesgiating the issue where in "mount --bind -oremount,ro ..." would result in later "mount --bind -oremount,rw" succeeding even if the mount started off locked I realized that there are several additional mount flags that should be locked and are not. In particular MNT_NOSUID, MNT_NODEV, MNT_NOEXEC, and the atime flags in addition to MNT_READONLY should all be locked. These flags are all per superblock, can all be changed with MS_BIND, and should not be changable if set by a more privileged user. The following additions to the current logic are added in this patch. - nosuid may not be clearable by a less privileged user. - nodev may not be clearable by a less privielged user. - noexec may not be clearable by a less privileged user. - atime flags may not be changeable by a less privileged user. The logic with atime is that always setting atime on access is a global policy and backup software and auditing software could break if atime bits are not updated (when they are configured to be updated), and serious performance degradation could result (DOS attack) if atime updates happen when they have been explicitly disabled. Therefore an unprivileged user should not be able to mess with the atime bits set by a more privileged user. The additional restrictions are implemented with the addition of MNT_LOCK_NOSUID, MNT_LOCK_NODEV, MNT_LOCK_NOEXEC, and MNT_LOCK_ATIME mnt flags. Taken together these changes and the fixes for MNT_LOCK_READONLY should make it safe for an unprivileged user to create a user namespace and to call "mount --bind -o remount,... ..." without the danger of mount flags being changed maliciously. Cc: stable@vger.kernel.org Acked-by: Serge E. Hallyn Signed-off-by: "Eric W. Biederman" --- fs/namespace.c | 36 +++++++++++++++++++++++++++++++++--- include/linux/mount.h | 5 +++++ 2 files changed, 38 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/fs/namespace.c b/fs/namespace.c index 1105a577a14f..dd9c93b5a9d5 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -890,8 +890,21 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root, mnt->mnt.mnt_flags = old->mnt.mnt_flags & ~(MNT_WRITE_HOLD|MNT_MARKED); /* Don't allow unprivileged users to change mount flags */ - if ((flag & CL_UNPRIVILEGED) && (mnt->mnt.mnt_flags & MNT_READONLY)) - mnt->mnt.mnt_flags |= MNT_LOCK_READONLY; + if (flag & CL_UNPRIVILEGED) { + mnt->mnt.mnt_flags |= MNT_LOCK_ATIME; + + if (mnt->mnt.mnt_flags & MNT_READONLY) + mnt->mnt.mnt_flags |= MNT_LOCK_READONLY; + + if (mnt->mnt.mnt_flags & MNT_NODEV) + mnt->mnt.mnt_flags |= MNT_LOCK_NODEV; + + if (mnt->mnt.mnt_flags & MNT_NOSUID) + mnt->mnt.mnt_flags |= MNT_LOCK_NOSUID; + + if (mnt->mnt.mnt_flags & MNT_NOEXEC) + mnt->mnt.mnt_flags |= MNT_LOCK_NOEXEC; + } /* Don't allow unprivileged users to reveal what is under a mount */ if ((flag & CL_UNPRIVILEGED) && list_empty(&old->mnt_expire)) @@ -1931,6 +1944,23 @@ static int do_remount(struct path *path, int flags, int mnt_flags, !(mnt_flags & MNT_READONLY)) { return -EPERM; } + if ((mnt->mnt.mnt_flags & MNT_LOCK_NODEV) && + !(mnt_flags & MNT_NODEV)) { + return -EPERM; + } + if ((mnt->mnt.mnt_flags & MNT_LOCK_NOSUID) && + !(mnt_flags & MNT_NOSUID)) { + return -EPERM; + } + if ((mnt->mnt.mnt_flags & MNT_LOCK_NOEXEC) && + !(mnt_flags & MNT_NOEXEC)) { + return -EPERM; + } + if ((mnt->mnt.mnt_flags & MNT_LOCK_ATIME) && + ((mnt->mnt.mnt_flags & MNT_ATIME_MASK) != (mnt_flags & MNT_ATIME_MASK))) { + return -EPERM; + } + err = security_sb_remount(sb, data); if (err) return err; @@ -2129,7 +2159,7 @@ static int do_new_mount(struct path *path, const char *fstype, int flags, */ if (!(type->fs_flags & FS_USERNS_DEV_MOUNT)) { flags |= MS_NODEV; - mnt_flags |= MNT_NODEV; + mnt_flags |= MNT_NODEV | MNT_LOCK_NODEV; } } diff --git a/include/linux/mount.h b/include/linux/mount.h index b637a89e1fae..b0c1e6574e7f 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -45,12 +45,17 @@ struct mnt_namespace; #define MNT_USER_SETTABLE_MASK (MNT_NOSUID | MNT_NODEV | MNT_NOEXEC \ | MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME \ | MNT_READONLY) +#define MNT_ATIME_MASK (MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME ) #define MNT_INTERNAL_FLAGS (MNT_SHARED | MNT_WRITE_HOLD | MNT_INTERNAL | \ MNT_DOOMED | MNT_SYNC_UMOUNT | MNT_MARKED) #define MNT_INTERNAL 0x4000 +#define MNT_LOCK_ATIME 0x040000 +#define MNT_LOCK_NOEXEC 0x080000 +#define MNT_LOCK_NOSUID 0x100000 +#define MNT_LOCK_NODEV 0x200000 #define MNT_LOCK_READONLY 0x400000 #define MNT_LOCKED 0x800000 #define MNT_DOOMED 0x1000000 -- cgit v1.2.3 From 536721b1cb3fb50034bf6f6c7a7ea16166970e69 Mon Sep 17 00:00:00 2001 From: Karoly Kemeny Date: Wed, 30 Jul 2014 20:27:36 +0200 Subject: net: kernel-doc compliant documentation for net_device Net_device is a vast and important structure, but it has no kernel-doc compliant documentation. This patch extracts the comments from the structure to clean it up, and let the scripts extract documentation from it. I know that the patch is big, but it's just reordering of comments into the appropriate form, and adding a few more, for the missing members. Signed-off-by: Karoly Kemeny Acked-by: Randy Dunlap Signed-off-by: David S. Miller --- include/linux/netdevice.h | 381 ++++++++++++++++++++++++++++++---------------- 1 file changed, 250 insertions(+), 131 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 8e8fb3ed574b..38377392d082 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1231,42 +1231,228 @@ enum netdev_priv_flags { #define IFF_LIVE_ADDR_CHANGE IFF_LIVE_ADDR_CHANGE #define IFF_MACVLAN IFF_MACVLAN -/* - * The DEVICE structure. - * Actually, this whole structure is a big mistake. It mixes I/O - * data with strictly "high-level" data, and it has to know about - * almost every data structure used in the INET module. +/** + * struct net_device - The DEVICE structure. + * Actually, this whole structure is a big mistake. It mixes I/O + * data with strictly "high-level" data, and it has to know about + * almost every data structure used in the INET module. + * + * @name: This is the first field of the "visible" part of this structure + * (i.e. as seen by users in the "Space.c" file). It is the name + * of the interface. + * + * @name_hlist: Device name hash chain, please keep it close to name[] + * @ifalias: SNMP alias + * @mem_end: Shared memory end + * @mem_start: Shared memory start + * @base_addr: Device I/O address + * @irq: Device IRQ number + * + * @state: Generic network queuing layer state, see netdev_state_t + * @dev_list: The global list of network devices + * @napi_list: List entry, that is used for polling napi devices + * @unreg_list: List entry, that is used, when we are unregistering the + * device, see the function unregister_netdev + * @close_list: List entry, that is used, when we are closing the device + * + * @adj_list: Directly linked devices, like slaves for bonding + * @all_adj_list: All linked devices, *including* neighbours + * @features: Currently active device features + * @hw_features: User-changeable features + * + * @wanted_features: User-requested features + * @vlan_features: Mask of features inheritable by VLAN devices + * + * @hw_enc_features: Mask of features inherited by encapsulating devices + * This field indicates what encapsulation + * offloads the hardware is capable of doing, + * and drivers will need to set them appropriately. + * + * @mpls_features: Mask of features inheritable by MPLS + * + * @ifindex: interface index + * @iflink: unique device identifier + * + * @stats: Statistics struct, which was left as a legacy, use + * rtnl_link_stats64 instead + * + * @rx_dropped: Dropped packets by core network, + * do not use this in drivers + * @tx_dropped: Dropped packets by core network, + * do not use this in drivers + * + * @carrier_changes: Stats to monitor carrier on<->off transitions + * + * @wireless_handlers: List of functions to handle Wireless Extensions, + * instead of ioctl, + * see for details. + * @wireless_data: Instance data managed by the core of wireless extensions + * + * @netdev_ops: Includes several pointers to callbacks, + * if one wants to override the ndo_*() functions + * @ethtool_ops: Management operations + * @fwd_ops: Management operations + * @header_ops: Includes callbacks for creating,parsing,rebuilding,etc + * of Layer 2 headers. + * + * @flags: Interface flags (a la BSD) + * @priv_flags: Like 'flags' but invisible to userspace, + * see if.h for the definitions + * @gflags: Global flags ( kept as legacy ) + * @padded: How much padding added by alloc_netdev() + * @operstate: RFC2863 operstate + * @link_mode: Mapping policy to operstate + * @if_port: Selectable AUI, TP, ... + * @dma: DMA channel + * @mtu: Interface MTU value + * @type: Interface hardware type + * @hard_header_len: Hardware header length + * + * @needed_headroom: Extra headroom the hardware may need, but not in all + * cases can this be guaranteed + * @needed_tailroom: Extra tailroom the hardware may need, but not in all + * cases can this be guaranteed. Some cases also use + * LL_MAX_HEADER instead to allocate the skb + * + * interface address info: + * + * @perm_addr: Permanent hw address + * @addr_assign_type: Hw address assignment type + * @addr_len: Hardware address length + * @neigh_priv_len; Used in neigh_alloc(), + * initialized only in atm/clip.c + * @dev_id: Used to differentiate devices that share + * the same link layer address + * @dev_port: Used to differentiate devices that share + * the same function + * @addr_list_lock: XXX: need comments on this one + * @uc: unicast mac addresses + * @mc: multicast mac addresses + * @dev_addrs: list of device hw addresses + * @queues_kset: Group of all Kobjects in the Tx and RX queues + * @uc_promisc: Counter, that indicates, that promiscuous mode + * has been enabled due to the need to listen to + * additional unicast addresses in a device that + * does not implement ndo_set_rx_mode() + * @promiscuity: Number of times, the NIC is told to work in + * Promiscuous mode, if it becomes 0 the NIC will + * exit from working in Promiscuous mode + * @allmulti: Counter, enables or disables allmulticast mode + * + * @vlan_info: VLAN info + * @dsa_ptr: dsa specific data + * @tipc_ptr: TIPC specific data + * @atalk_ptr: AppleTalk link + * @ip_ptr: IPv4 specific data + * @dn_ptr: DECnet specific data + * @ip6_ptr: IPv6 specific data + * @ax25_ptr: AX.25 specific data + * @ieee80211_ptr: IEEE 802.11 specific data, assign before registering + * + * @last_rx: Time of last Rx + * @dev_addr: Hw address (before bcast, + * because most packets are unicast) + * + * @_rx: Array of RX queues + * @num_rx_queues: Number of RX queues + * allocated at register_netdev() time + * @real_num_rx_queues: Number of RX queues currently active in device + * + * @rx_handler: handler for received packets + * @rx_handler_data: XXX: need comments on this one + * @ingress_queue: XXX: need comments on this one + * @broadcast: hw bcast address + * + * @_tx: Array of TX queues + * @num_tx_queues: Number of TX queues allocated at alloc_netdev_mq() time + * @real_num_tx_queues: Number of TX queues currently active in device + * @qdisc: Root qdisc from userspace point of view + * @tx_queue_len: Max frames per queue allowed + * @tx_global_lock: XXX: need comments on this one + * + * @xps_maps: XXX: need comments on this one + * + * @rx_cpu_rmap: CPU reverse-mapping for RX completion interrupts, + * indexed by RX queue number. Assigned by driver. + * This must only be set if the ndo_rx_flow_steer + * operation is defined + * + * @trans_start: Time (in jiffies) of last Tx + * @watchdog_timeo: Represents the timeout that is used by + * the watchdog ( see dev_watchdog() ) + * @watchdog_timer: List of timers + * + * @pcpu_refcnt: Number of references to this device + * @todo_list: Delayed register/unregister + * @index_hlist: Device index hash chain + * @link_watch_list: XXX: need comments on this one + * + * @reg_state: Register/unregister state machine + * @dismantle: Device is going to be freed + * @rtnl_link_state: This enum represents the phases of creating + * a new link + * + * @destructor: Called from unregister, + * can be used to call free_netdev + * @npinfo: XXX: need comments on this one + * @nd_net: Network namespace this network device is inside + * + * @ml_priv: Mid-layer private + * @lstats: Loopback statistics + * @tstats: Tunnel statistics + * @dstats: Dummy statistics + * @vstats: Virtual ethernet statistics + * + * @garp_port: GARP + * @mrp_port: MRP + * + * @dev: Class/net/name entry + * @sysfs_groups: Space for optional device, statistics and wireless + * sysfs groups + * + * @sysfs_rx_queue_group: Space for optional per-rx queue attributes + * @rtnl_link_ops: Rtnl_link_ops + * + * @gso_max_size: Maximum size of generic segmentation offload + * @gso_max_segs: Maximum number of segments that can be passed to the + * NIC for GSO + * + * @dcbnl_ops: Data Center Bridging netlink ops + * @num_tc: Number of traffic classes in the net device + * @tc_to_txq: XXX: need comments on this one + * @prio_tc_map XXX: need comments on this one + * + * @fcoe_ddp_xid: Max exchange id for FCoE LRO by ddp + * + * @priomap: XXX: need comments on this one + * @phydev: Physical device may attach itself + * for hardware timestamping + * + * @qdisc_tx_busylock: XXX: need comments on this one + * + * @group: The group, that the device belongs to + * @pm_qos_req: Power Management QoS object * * FIXME: cleanup struct net_device such that network protocol info * moves out. */ struct net_device { - - /* - * This is the first field of the "visible" part of this structure - * (i.e. as seen by users in the "Space.c" file). It is the name - * of the interface. - */ char name[IFNAMSIZ]; - - /* device name hash chain, please keep it close to name[] */ struct hlist_node name_hlist; - - /* snmp alias */ char *ifalias; - /* * I/O specific fields * FIXME: Merge these and struct ifmap into one */ - unsigned long mem_end; /* shared mem end */ - unsigned long mem_start; /* shared mem start */ - unsigned long base_addr; /* device I/O address */ - int irq; /* device IRQ number */ + unsigned long mem_end; + unsigned long mem_start; + unsigned long base_addr; + int irq; /* - * Some hardware also needs these fields, but they are not + * Some hardware also needs these fields (state,dev_list, + * napi_list,unreg_list,close_list) but they are not * part of the usual set specified in Space.c. */ @@ -1277,106 +1463,74 @@ struct net_device { struct list_head unreg_list; struct list_head close_list; - /* directly linked devices, like slaves for bonding */ struct { struct list_head upper; struct list_head lower; } adj_list; - /* all linked devices, *including* neighbours */ struct { struct list_head upper; struct list_head lower; } all_adj_list; - - /* currently active device features */ netdev_features_t features; - /* user-changeable features */ netdev_features_t hw_features; - /* user-requested features */ netdev_features_t wanted_features; - /* mask of features inheritable by VLAN devices */ netdev_features_t vlan_features; - /* mask of features inherited by encapsulating devices - * This field indicates what encapsulation offloads - * the hardware is capable of doing, and drivers will - * need to set them appropriately. - */ netdev_features_t hw_enc_features; - /* mask of fetures inheritable by MPLS */ netdev_features_t mpls_features; - /* Interface index. Unique device identifier */ int ifindex; int iflink; struct net_device_stats stats; - /* dropped packets by core network, Do not use this in drivers */ atomic_long_t rx_dropped; atomic_long_t tx_dropped; - /* Stats to monitor carrier on<->off transitions */ atomic_t carrier_changes; #ifdef CONFIG_WIRELESS_EXT - /* List of functions to handle Wireless Extensions (instead of ioctl). - * See for details. Jean II */ const struct iw_handler_def * wireless_handlers; - /* Instance data managed by the core of Wireless Extensions. */ struct iw_public_data * wireless_data; #endif - /* Management operations */ const struct net_device_ops *netdev_ops; const struct ethtool_ops *ethtool_ops; const struct forwarding_accel_ops *fwd_ops; - /* Hardware header description */ const struct header_ops *header_ops; - unsigned int flags; /* interface flags (a la BSD) */ - unsigned int priv_flags; /* Like 'flags' but invisible to userspace. - * See if.h for definitions. */ + unsigned int flags; + unsigned int priv_flags; + unsigned short gflags; - unsigned short padded; /* How much padding added by alloc_netdev() */ + unsigned short padded; - unsigned char operstate; /* RFC2863 operstate */ - unsigned char link_mode; /* mapping policy to operstate */ + unsigned char operstate; + unsigned char link_mode; - unsigned char if_port; /* Selectable AUI, TP,..*/ - unsigned char dma; /* DMA channel */ + unsigned char if_port; + unsigned char dma; - unsigned int mtu; /* interface MTU value */ - unsigned short type; /* interface hardware type */ - unsigned short hard_header_len; /* hardware hdr length */ + unsigned int mtu; + unsigned short type; + unsigned short hard_header_len; - /* extra head- and tailroom the hardware may need, but not in all cases - * can this be guaranteed, especially tailroom. Some cases also use - * LL_MAX_HEADER instead to allocate the skb. - */ unsigned short needed_headroom; unsigned short needed_tailroom; /* Interface address info. */ - unsigned char perm_addr[MAX_ADDR_LEN]; /* permanent hw address */ - unsigned char addr_assign_type; /* hw address assignment type */ - unsigned char addr_len; /* hardware address length */ + unsigned char perm_addr[MAX_ADDR_LEN]; + unsigned char addr_assign_type; + unsigned char addr_len; unsigned short neigh_priv_len; - unsigned short dev_id; /* Used to differentiate devices - * that share the same link - * layer address - */ - unsigned short dev_port; /* Used to differentiate - * devices that share the same - * function - */ + unsigned short dev_id; + unsigned short dev_port; spinlock_t addr_list_lock; - struct netdev_hw_addr_list uc; /* Unicast mac addresses */ - struct netdev_hw_addr_list mc; /* Multicast mac addresses */ - struct netdev_hw_addr_list dev_addrs; /* list of device - * hw addresses - */ + struct netdev_hw_addr_list uc; + struct netdev_hw_addr_list mc; + struct netdev_hw_addr_list dev_addrs; + #ifdef CONFIG_SYSFS struct kset *queues_kset; #endif @@ -1391,40 +1545,34 @@ struct net_device { /* Protocol specific pointers */ #if IS_ENABLED(CONFIG_VLAN_8021Q) - struct vlan_info __rcu *vlan_info; /* VLAN info */ + struct vlan_info __rcu *vlan_info; #endif #if IS_ENABLED(CONFIG_NET_DSA) - struct dsa_switch_tree *dsa_ptr; /* dsa specific data */ + struct dsa_switch_tree *dsa_ptr; #endif #if IS_ENABLED(CONFIG_TIPC) - struct tipc_bearer __rcu *tipc_ptr; /* TIPC specific data */ + struct tipc_bearer __rcu *tipc_ptr; #endif - void *atalk_ptr; /* AppleTalk link */ - struct in_device __rcu *ip_ptr; /* IPv4 specific data */ - struct dn_dev __rcu *dn_ptr; /* DECnet specific data */ - struct inet6_dev __rcu *ip6_ptr; /* IPv6 specific data */ - void *ax25_ptr; /* AX.25 specific data */ - struct wireless_dev *ieee80211_ptr; /* IEEE 802.11 specific data, - assign before registering */ + void *atalk_ptr; + struct in_device __rcu *ip_ptr; + struct dn_dev __rcu *dn_ptr; + struct inet6_dev __rcu *ip6_ptr; + void *ax25_ptr; + struct wireless_dev *ieee80211_ptr; /* * Cache lines mostly used on receive path (including eth_type_trans()) */ - unsigned long last_rx; /* Time of last Rx */ + unsigned long last_rx; /* Interface address info used in eth_type_trans() */ - unsigned char *dev_addr; /* hw address, (before bcast - because most packets are - unicast) */ + unsigned char *dev_addr; #ifdef CONFIG_SYSFS struct netdev_rx_queue *_rx; - /* Number of RX queues allocated at register_netdev() time */ unsigned int num_rx_queues; - - /* Number of RX queues currently active in device */ unsigned int real_num_rx_queues; #endif @@ -1433,33 +1581,23 @@ struct net_device { void __rcu *rx_handler_data; struct netdev_queue __rcu *ingress_queue; - unsigned char broadcast[MAX_ADDR_LEN]; /* hw bcast add */ + unsigned char broadcast[MAX_ADDR_LEN]; /* * Cache lines mostly used on transmit path */ struct netdev_queue *_tx ____cacheline_aligned_in_smp; - - /* Number of TX queues allocated at alloc_netdev_mq() time */ unsigned int num_tx_queues; - - /* Number of TX queues currently active in device */ unsigned int real_num_tx_queues; - - /* root qdisc from userspace point of view */ struct Qdisc *qdisc; - - unsigned long tx_queue_len; /* Max frames per queue allowed */ + unsigned long tx_queue_len; spinlock_t tx_global_lock; #ifdef CONFIG_XPS struct xps_dev_maps __rcu *xps_maps; #endif #ifdef CONFIG_RFS_ACCEL - /* CPU reverse-mapping for RX completion interrupts, indexed - * by RX queue number. Assigned by driver. This must only be - * set if the ndo_rx_flow_steer operation is defined. */ struct cpu_rmap *rx_cpu_rmap; #endif @@ -1469,22 +1607,17 @@ struct net_device { * trans_start here is expensive for high speed devices on SMP, * please use netdev_queue->trans_start instead. */ - unsigned long trans_start; /* Time (in jiffies) of last Tx */ + unsigned long trans_start; - int watchdog_timeo; /* used by dev_watchdog() */ + int watchdog_timeo; struct timer_list watchdog_timer; - /* Number of references to this device */ int __percpu *pcpu_refcnt; - - /* delayed register/unregister */ struct list_head todo_list; - /* device index hash chain */ - struct hlist_node index_hlist; + struct hlist_node index_hlist; struct list_head link_watch_list; - /* register/unregister state machine */ enum { NETREG_UNINITIALIZED=0, NETREG_REGISTERED, /* completed register_netdevice */ NETREG_UNREGISTERING, /* called unregister_netdevice */ @@ -1493,14 +1626,13 @@ struct net_device { NETREG_DUMMY, /* dummy device for NAPI poll */ } reg_state:8; - bool dismantle; /* device is going do be freed */ + bool dismantle; enum { RTNL_LINK_INITIALIZED, RTNL_LINK_INITIALIZING, } rtnl_link_state:16; - /* Called from unregister, can be used to call free_netdev */ void (*destructor)(struct net_device *dev); #ifdef CONFIG_NETPOLL @@ -1508,31 +1640,25 @@ struct net_device { #endif #ifdef CONFIG_NET_NS - /* Network namespace this network device is inside */ struct net *nd_net; #endif /* mid-layer private */ union { - void *ml_priv; - struct pcpu_lstats __percpu *lstats; /* loopback stats */ + void *ml_priv; + struct pcpu_lstats __percpu *lstats; struct pcpu_sw_netstats __percpu *tstats; - struct pcpu_dstats __percpu *dstats; /* dummy stats */ - struct pcpu_vstats __percpu *vstats; /* veth stats */ + struct pcpu_dstats __percpu *dstats; + struct pcpu_vstats __percpu *vstats; }; - /* GARP */ + struct garp_port __rcu *garp_port; - /* MRP */ struct mrp_port __rcu *mrp_port; - /* class/net/name entry */ - struct device dev; - /* space for optional device, statistics, and wireless sysfs groups */ + struct device dev; const struct attribute_group *sysfs_groups[4]; - /* space for optional per-rx queue attributes */ const struct attribute_group *sysfs_rx_queue_group; - /* rtnetlink link ops */ const struct rtnl_link_ops *rtnl_link_ops; /* for setting kernel sock attribute on TCP connection setup */ @@ -1542,7 +1668,6 @@ struct net_device { u16 gso_max_segs; #ifdef CONFIG_DCB - /* Data Center Bridging netlink ops */ const struct dcbnl_rtnl_ops *dcbnl_ops; #endif u8 num_tc; @@ -1550,20 +1675,14 @@ struct net_device { u8 prio_tc_map[TC_BITMASK + 1]; #if IS_ENABLED(CONFIG_FCOE) - /* max exchange id for FCoE LRO by ddp */ unsigned int fcoe_ddp_xid; #endif #if IS_ENABLED(CONFIG_CGROUP_NET_PRIO) struct netprio_map __rcu *priomap; #endif - /* phy device may attach itself for hardware timestamping */ struct phy_device *phydev; - struct lock_class_key *qdisc_tx_busylock; - - /* 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 299ee123e19889d511092347f5fc14db0f10e3a6 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 30 Jul 2014 12:40:53 -0600 Subject: sctp: Fixup v4mapped behaviour to comply with Sock API The SCTP socket extensions API document describes the v4mapping option as follows: 8.1.15. Set/Clear IPv4 Mapped Addresses (SCTP_I_WANT_MAPPED_V4_ADDR) This socket option is a Boolean flag which turns on or off the mapping of IPv4 addresses. If this option is turned on, then IPv4 addresses will be mapped to V6 representation. If this option is turned off, then no mapping will be done of V4 addresses and a user will receive both PF_INET6 and PF_INET type addresses on the socket. See [RFC3542] for more details on mapped V6 addresses. This description isn't really in line with what the code does though. Introduce addr_to_user (renamed addr_v4map), which should be called before any sockaddr is passed back to user space. The new function places the sockaddr into the correct format depending on the SCTP_I_WANT_MAPPED_V4_ADDR option. Audit all places that touched v4mapped and either sanely construct a v4 or v6 address then call addr_to_user, or drop the unnecessary v4mapped check entirely. Audit all places that call addr_to_user and verify they are on a sycall return path. Add a custom getname that formats the address properly. Several bugs are addressed: - SCTP_I_WANT_MAPPED_V4_ADDR=0 often returned garbage for addresses to user space - The addr_len returned from recvmsg was not correct when returning AF_INET on a v6 socket - flowlabel and scope_id were not zerod when promoting a v4 to v6 - Some syscalls like bind and connect behaved differently depending on v4mapped Tested bind, getpeername, getsockname, connect, and recvmsg for proper behaviour in v4mapped = 1 and 0 cases. Signed-off-by: Neil Horman Tested-by: Jason Gunthorpe Signed-off-by: Jason Gunthorpe Signed-off-by: David S. Miller --- include/net/sctp/sctp.h | 2 + include/net/sctp/structs.h | 8 +-- net/sctp/ipv6.c | 156 ++++++++++++++++++++++++--------------------- net/sctp/protocol.c | 12 ++-- net/sctp/socket.c | 33 +++++----- net/sctp/transport.c | 4 +- net/sctp/ulpevent.c | 2 +- 7 files changed, 112 insertions(+), 105 deletions(-) (limited to 'include') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 90c1cccd164d..f6e7397e799d 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -554,6 +554,8 @@ static inline void sctp_v6_map_v4(union sctp_addr *addr) static inline void sctp_v4_map_v6(union sctp_addr *addr) { addr->v6.sin6_family = AF_INET6; + addr->v6.sin6_flowinfo = 0; + addr->v6.sin6_scope_id = 0; addr->v6.sin6_port = addr->v4.sin_port; addr->v6.sin6_addr.s6_addr32[3] = addr->v4.sin_addr.s_addr; addr->v6.sin6_addr.s6_addr32[0] = 0; diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 7741d1b66967..4ff3f67be62c 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -465,10 +465,6 @@ struct sctp_af { int saddr); void (*from_sk) (union sctp_addr *, struct sock *sk); - void (*to_sk_saddr) (union sctp_addr *, - struct sock *sk); - void (*to_sk_daddr) (union sctp_addr *, - struct sock *sk); void (*from_addr_param) (union sctp_addr *, union sctp_addr_param *, __be16 port, int iif); @@ -509,7 +505,9 @@ struct sctp_pf { int (*supported_addrs)(const struct sctp_sock *, __be16 *); struct sock *(*create_accept_sk) (struct sock *sk, struct sctp_association *asoc); - void (*addr_v4map) (struct sctp_sock *, union sctp_addr *); + int (*addr_to_user)(struct sctp_sock *sk, union sctp_addr *addr); + void (*to_sk_saddr)(union sctp_addr *, struct sock *sk); + void (*to_sk_daddr)(union sctp_addr *, struct sock *sk); struct sctp_af *af; }; diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 1999592ba88c..0e4198ee2370 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -434,7 +434,7 @@ static void sctp_v6_from_sk(union sctp_addr *addr, struct sock *sk) /* Initialize sk->sk_rcv_saddr from sctp_addr. */ static void sctp_v6_to_sk_saddr(union sctp_addr *addr, struct sock *sk) { - if (addr->sa.sa_family == AF_INET && sctp_sk(sk)->v4mapped) { + if (addr->sa.sa_family == AF_INET) { sk->sk_v6_rcv_saddr.s6_addr32[0] = 0; sk->sk_v6_rcv_saddr.s6_addr32[1] = 0; sk->sk_v6_rcv_saddr.s6_addr32[2] = htonl(0x0000ffff); @@ -448,7 +448,7 @@ static void sctp_v6_to_sk_saddr(union sctp_addr *addr, struct sock *sk) /* Initialize sk->sk_daddr from sctp_addr. */ static void sctp_v6_to_sk_daddr(union sctp_addr *addr, struct sock *sk) { - if (addr->sa.sa_family == AF_INET && sctp_sk(sk)->v4mapped) { + if (addr->sa.sa_family == AF_INET) { sk->sk_v6_daddr.s6_addr32[0] = 0; sk->sk_v6_daddr.s6_addr32[1] = 0; sk->sk_v6_daddr.s6_addr32[2] = htonl(0x0000ffff); @@ -556,8 +556,6 @@ static int sctp_v6_available(union sctp_addr *addr, struct sctp_sock *sp) if (IPV6_ADDR_ANY == type) return 1; if (type == IPV6_ADDR_MAPPED) { - if (sp && !sp->v4mapped) - return 0; if (sp && ipv6_only_sock(sctp_opt2sk(sp))) return 0; sctp_v6_map_v4(addr); @@ -587,8 +585,6 @@ static int sctp_v6_addr_valid(union sctp_addr *addr, /* Note: This routine is used in input, so v4-mapped-v6 * are disallowed here when there is no sctp_sock. */ - if (!sp || !sp->v4mapped) - return 0; if (sp && ipv6_only_sock(sctp_opt2sk(sp))) return 0; sctp_v6_map_v4(addr); @@ -675,11 +671,23 @@ out: return newsk; } -/* Map v4 address to mapped v6 address */ -static void sctp_v6_addr_v4map(struct sctp_sock *sp, union sctp_addr *addr) +/* Format a sockaddr for return to user space. This makes sure the return is + * AF_INET or AF_INET6 depending on the SCTP_I_WANT_MAPPED_V4_ADDR option. + */ +static int sctp_v6_addr_to_user(struct sctp_sock *sp, union sctp_addr *addr) { - if (sp->v4mapped && AF_INET == addr->sa.sa_family) - sctp_v4_map_v6(addr); + if (sp->v4mapped) { + if (addr->sa.sa_family == AF_INET) + sctp_v4_map_v6(addr); + } else { + if (addr->sa.sa_family == AF_INET6 && + ipv6_addr_v4mapped(&addr->v6.sin6_addr)) + sctp_v6_map_v4(addr); + } + + if (addr->sa.sa_family == AF_INET) + return sizeof(struct sockaddr_in); + return sizeof(struct sockaddr_in6); } /* Where did this skb come from? */ @@ -706,82 +714,68 @@ static void sctp_v6_ecn_capable(struct sock *sk) inet6_sk(sk)->tclass |= INET_ECN_ECT_0; } -/* Initialize a PF_INET6 socket msg_name. */ -static void sctp_inet6_msgname(char *msgname, int *addr_len) -{ - struct sockaddr_in6 *sin6; - - sin6 = (struct sockaddr_in6 *)msgname; - sin6->sin6_family = AF_INET6; - sin6->sin6_flowinfo = 0; - sin6->sin6_scope_id = 0; /*FIXME */ - *addr_len = sizeof(struct sockaddr_in6); -} - /* Initialize a PF_INET msgname from a ulpevent. */ static void sctp_inet6_event_msgname(struct sctp_ulpevent *event, char *msgname, int *addrlen) { - struct sockaddr_in6 *sin6, *sin6from; - - if (msgname) { - union sctp_addr *addr; - struct sctp_association *asoc; - - asoc = event->asoc; - sctp_inet6_msgname(msgname, addrlen); - sin6 = (struct sockaddr_in6 *)msgname; - sin6->sin6_port = htons(asoc->peer.port); - addr = &asoc->peer.primary_addr; + union sctp_addr *addr; + struct sctp_association *asoc; + union sctp_addr *paddr; - /* Note: If we go to a common v6 format, this code - * will change. - */ + if (!msgname) + return; - /* Map ipv4 address into v4-mapped-on-v6 address. */ - if (sctp_sk(asoc->base.sk)->v4mapped && - AF_INET == addr->sa.sa_family) { - sctp_v4_map_v6((union sctp_addr *)sin6); - sin6->sin6_addr.s6_addr32[3] = - addr->v4.sin_addr.s_addr; - return; - } + addr = (union sctp_addr *)msgname; + asoc = event->asoc; + paddr = &asoc->peer.primary_addr; - sin6from = &asoc->peer.primary_addr.v6; - sin6->sin6_addr = sin6from->sin6_addr; - if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) - sin6->sin6_scope_id = sin6from->sin6_scope_id; + if (paddr->sa.sa_family == AF_INET) { + addr->v4.sin_family = AF_INET; + addr->v4.sin_port = htons(asoc->peer.port); + addr->v4.sin_addr = paddr->v4.sin_addr; + } else { + addr->v6.sin6_family = AF_INET6; + addr->v6.sin6_flowinfo = 0; + if (ipv6_addr_type(&paddr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) + addr->v6.sin6_scope_id = paddr->v6.sin6_scope_id; + else + addr->v6.sin6_scope_id = 0; + addr->v6.sin6_port = htons(asoc->peer.port); + addr->v6.sin6_addr = paddr->v6.sin6_addr; } + + *addrlen = sctp_v6_addr_to_user(sctp_sk(asoc->base.sk), addr); } /* Initialize a msg_name from an inbound skb. */ static void sctp_inet6_skb_msgname(struct sk_buff *skb, char *msgname, int *addr_len) { + union sctp_addr *addr; struct sctphdr *sh; - struct sockaddr_in6 *sin6; - - if (msgname) { - sctp_inet6_msgname(msgname, addr_len); - sin6 = (struct sockaddr_in6 *)msgname; - sh = sctp_hdr(skb); - sin6->sin6_port = sh->source; - - /* Map ipv4 address into v4-mapped-on-v6 address. */ - if (sctp_sk(skb->sk)->v4mapped && - ip_hdr(skb)->version == 4) { - sctp_v4_map_v6((union sctp_addr *)sin6); - sin6->sin6_addr.s6_addr32[3] = ip_hdr(skb)->saddr; - return; - } - /* Otherwise, just copy the v6 address. */ - sin6->sin6_addr = ipv6_hdr(skb)->saddr; - if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) { + if (!msgname) + return; + + addr = (union sctp_addr *)msgname; + sh = sctp_hdr(skb); + + if (ip_hdr(skb)->version == 4) { + addr->v4.sin_family = AF_INET; + addr->v4.sin_port = sh->source; + addr->v4.sin_addr.s_addr = ip_hdr(skb)->saddr; + } else { + addr->v6.sin6_family = AF_INET6; + addr->v6.sin6_flowinfo = 0; + addr->v6.sin6_port = sh->source; + addr->v6.sin6_addr = ipv6_hdr(skb)->saddr; + if (ipv6_addr_type(&addr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) { struct sctp_ulpevent *ev = sctp_skb2event(skb); - sin6->sin6_scope_id = ev->iif; + addr->v6.sin6_scope_id = ev->iif; } } + + *addr_len = sctp_v6_addr_to_user(sctp_sk(skb->sk), addr); } /* Do we support this AF? */ @@ -857,9 +851,6 @@ static int sctp_inet6_bind_verify(struct sctp_sock *opt, union sctp_addr *addr) return 0; } rcu_read_unlock(); - } else if (type == IPV6_ADDR_MAPPED) { - if (!opt->v4mapped) - return 0; } af = opt->pf->af; @@ -914,6 +905,23 @@ static int sctp_inet6_supported_addrs(const struct sctp_sock *opt, return 1; } +/* Handle SCTP_I_WANT_MAPPED_V4_ADDR for getpeername() and getsockname() */ +static int sctp_getname(struct socket *sock, struct sockaddr *uaddr, + int *uaddr_len, int peer) +{ + int rc; + + rc = inet6_getname(sock, uaddr, uaddr_len, peer); + + if (rc != 0) + return rc; + + *uaddr_len = sctp_v6_addr_to_user(sctp_sk(sock->sk), + (union sctp_addr *)uaddr); + + return rc; +} + static const struct proto_ops inet6_seqpacket_ops = { .family = PF_INET6, .owner = THIS_MODULE, @@ -922,7 +930,7 @@ static const struct proto_ops inet6_seqpacket_ops = { .connect = inet_dgram_connect, .socketpair = sock_no_socketpair, .accept = inet_accept, - .getname = inet6_getname, + .getname = sctp_getname, .poll = sctp_poll, .ioctl = inet6_ioctl, .listen = sctp_inet_listen, @@ -974,8 +982,6 @@ static struct sctp_af sctp_af_inet6 = { .copy_addrlist = sctp_v6_copy_addrlist, .from_skb = sctp_v6_from_skb, .from_sk = sctp_v6_from_sk, - .to_sk_saddr = sctp_v6_to_sk_saddr, - .to_sk_daddr = sctp_v6_to_sk_daddr, .from_addr_param = sctp_v6_from_addr_param, .to_addr_param = sctp_v6_to_addr_param, .cmp_addr = sctp_v6_cmp_addr, @@ -1005,7 +1011,9 @@ static struct sctp_pf sctp_pf_inet6 = { .send_verify = sctp_inet6_send_verify, .supported_addrs = sctp_inet6_supported_addrs, .create_accept_sk = sctp_v6_create_accept_sk, - .addr_v4map = sctp_v6_addr_v4map, + .addr_to_user = sctp_v6_addr_to_user, + .to_sk_saddr = sctp_v6_to_sk_saddr, + .to_sk_daddr = sctp_v6_to_sk_daddr, .af = &sctp_af_inet6, }; diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 6789d785e698..6240834f4b95 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -576,10 +576,10 @@ out: return newsk; } -/* Map address, empty for v4 family */ -static void sctp_v4_addr_v4map(struct sctp_sock *sp, union sctp_addr *addr) +static int sctp_v4_addr_to_user(struct sctp_sock *sp, union sctp_addr *addr) { - /* Empty */ + /* No address mapping for V4 sockets */ + return sizeof(struct sockaddr_in); } /* Dump the v4 addr to the seq file. */ @@ -976,7 +976,9 @@ static struct sctp_pf sctp_pf_inet = { .send_verify = sctp_inet_send_verify, .supported_addrs = sctp_inet_supported_addrs, .create_accept_sk = sctp_v4_create_accept_sk, - .addr_v4map = sctp_v4_addr_v4map, + .addr_to_user = sctp_v4_addr_to_user, + .to_sk_saddr = sctp_v4_to_sk_saddr, + .to_sk_daddr = sctp_v4_to_sk_daddr, .af = &sctp_af_inet }; @@ -1047,8 +1049,6 @@ static struct sctp_af sctp_af_inet = { .copy_addrlist = sctp_v4_copy_addrlist, .from_skb = sctp_v4_from_skb, .from_sk = sctp_v4_from_sk, - .to_sk_saddr = sctp_v4_to_sk_saddr, - .to_sk_daddr = sctp_v4_to_sk_daddr, .from_addr_param = sctp_v4_from_addr_param, .to_addr_param = sctp_v4_to_addr_param, .cmp_addr = sctp_v4_cmp_addr, diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 743308f40544..eb71d49e7653 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -254,7 +254,7 @@ static struct sctp_transport *sctp_addr_id2transport(struct sock *sk, if (id_asoc && (id_asoc != addr_asoc)) return NULL; - sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk), + sctp_get_pf_specific(sk->sk_family)->addr_to_user(sctp_sk(sk), (union sctp_addr *)addr); return transport; @@ -396,7 +396,7 @@ static int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len) /* Copy back into socket for getsockname() use. */ if (!ret) { inet_sk(sk)->inet_sport = htons(inet_sk(sk)->inet_num); - af->to_sk_saddr(addr, sk); + sp->pf->to_sk_saddr(addr, sk); } return ret; @@ -1053,7 +1053,6 @@ static int __sctp_connect(struct sock *sk, struct sctp_association *asoc2; struct sctp_transport *transport; union sctp_addr to; - struct sctp_af *af; sctp_scope_t scope; long timeo; int err = 0; @@ -1081,6 +1080,8 @@ static int __sctp_connect(struct sock *sk, /* Walk through the addrs buffer and count the number of addresses. */ addr_buf = kaddrs; while (walk_size < addrs_size) { + struct sctp_af *af; + if (walk_size + sizeof(sa_family_t) > addrs_size) { err = -EINVAL; goto out_free; @@ -1205,8 +1206,7 @@ static int __sctp_connect(struct sock *sk, /* Initialize sk's dport and daddr for getpeername() */ inet_sk(sk)->inet_dport = htons(asoc->peer.port); - af = sctp_get_af_specific(sa_addr->sa.sa_family); - af->to_sk_daddr(sa_addr, sk); + sp->pf->to_sk_daddr(sa_addr, sk); sk->sk_err = 0; /* in-kernel sockets don't generally have a file allocated to them @@ -4255,7 +4255,7 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, memcpy(&status.sstat_primary.spinfo_address, &transport->ipaddr, transport->af_specific->sockaddr_len); /* Map ipv4 address into v4-mapped-on-v6 address. */ - sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk), + sctp_get_pf_specific(sk->sk_family)->addr_to_user(sctp_sk(sk), (union sctp_addr *)&status.sstat_primary.spinfo_address); status.sstat_primary.spinfo_state = transport->state; status.sstat_primary.spinfo_cwnd = transport->cwnd; @@ -4413,8 +4413,8 @@ static int sctp_getsockopt_autoclose(struct sock *sk, int len, char __user *optv int sctp_do_peeloff(struct sock *sk, sctp_assoc_t id, struct socket **sockp) { struct sctp_association *asoc = sctp_id2assoc(sk, id); + struct sctp_sock *sp = sctp_sk(sk); struct socket *sock; - struct sctp_af *af; int err = 0; if (!asoc) @@ -4436,8 +4436,7 @@ int sctp_do_peeloff(struct sock *sk, sctp_assoc_t id, struct socket **sockp) /* Make peeled-off sockets more like 1-1 accepted sockets. * Set the daddr and initialize id to something more random */ - af = sctp_get_af_specific(asoc->peer.primary_addr.sa.sa_family); - af->to_sk_daddr(&asoc->peer.primary_addr, sk); + sp->pf->to_sk_daddr(&asoc->peer.primary_addr, sk); /* Populate the fields of the newsk from the oldsk and migrate the * asoc to the newsk. @@ -4821,8 +4820,8 @@ static int sctp_getsockopt_peer_addrs(struct sock *sk, int len, list_for_each_entry(from, &asoc->peer.transport_addr_list, transports) { memcpy(&temp, &from->ipaddr, sizeof(temp)); - sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp); - addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; + addrlen = sctp_get_pf_specific(sk->sk_family) + ->addr_to_user(sp, &temp); if (space_left < addrlen) return -ENOMEM; if (copy_to_user(to, &temp, addrlen)) @@ -4866,9 +4865,9 @@ static int sctp_copy_laddrs(struct sock *sk, __u16 port, void *to, if (!temp.v4.sin_port) temp.v4.sin_port = htons(port); - sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk), - &temp); - addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; + addrlen = sctp_get_pf_specific(sk->sk_family) + ->addr_to_user(sctp_sk(sk), &temp); + if (space_left < addrlen) { cnt = -ENOMEM; break; @@ -4956,8 +4955,8 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len, */ list_for_each_entry(addr, &bp->address_list, list) { memcpy(&temp, &addr->a, sizeof(temp)); - sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp); - addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; + addrlen = sctp_get_pf_specific(sk->sk_family) + ->addr_to_user(sp, &temp); if (space_left < addrlen) { err = -ENOMEM; /*fixme: right error?*/ goto out; @@ -5016,7 +5015,7 @@ static int sctp_getsockopt_primary_addr(struct sock *sk, int len, memcpy(&prim.ssp_addr, &asoc->peer.primary_path->ipaddr, asoc->peer.primary_path->af_specific->sockaddr_len); - sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, + sctp_get_pf_specific(sk->sk_family)->addr_to_user(sp, (union sctp_addr *)&prim.ssp_addr); if (put_user(len, optlen)) diff --git a/net/sctp/transport.c b/net/sctp/transport.c index b10e047bbd15..a0a431824f63 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -289,8 +289,8 @@ void sctp_transport_route(struct sctp_transport *transport, */ if (asoc && (!asoc->peer.primary_path || (transport == asoc->peer.active_path))) - opt->pf->af->to_sk_saddr(&transport->saddr, - asoc->base.sk); + opt->pf->to_sk_saddr(&transport->saddr, + asoc->base.sk); } else transport->pathmtu = SCTP_DEFAULT_MAXSEGMENT; } diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c index e049298ecfa0..d1e38308f615 100644 --- a/net/sctp/ulpevent.c +++ b/net/sctp/ulpevent.c @@ -341,7 +341,7 @@ struct sctp_ulpevent *sctp_ulpevent_make_peer_addr_change( memcpy(&spc->spc_aaddr, aaddr, sizeof(struct sockaddr_storage)); /* Map ipv4 address into v4-mapped-on-v6 address. */ - sctp_get_pf_specific(asoc->base.sk->sk_family)->addr_v4map( + sctp_get_pf_specific(asoc->base.sk->sk_family)->addr_to_user( sctp_sk(asoc->base.sk), (union sctp_addr *)&spc->spc_aaddr); -- cgit v1.2.3 From 09ff742c62a6bbfca7aed485f44c8b16f52e1096 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Sat, 12 Jul 2014 15:51:51 -0500 Subject: libiscsi: return new error code when nop times out When a iscsi nop as ping timedout we were failing with the common connection error code, ISCSI_ERR_CONN_FAILED. This patch adds a new error code for this problem so can properly track/distinguish in userspace. Signed-off-by: Mike Christie Acked-by: Vikas Chaudhary Reviewed-by: Hannes Reinecke Signed-off-by: Christoph Hellwig --- drivers/scsi/libiscsi.c | 2 +- include/scsi/iscsi_if.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index f9f3a1224dfa..ea025e4806b6 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -2097,7 +2097,7 @@ static void iscsi_check_transport_timeouts(unsigned long data) conn->ping_timeout, conn->recv_timeout, last_recv, conn->last_ping, jiffies); spin_unlock(&session->frwd_lock); - iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); + iscsi_conn_failure(conn, ISCSI_ERR_NOP_TIMEDOUT); return; } diff --git a/include/scsi/iscsi_if.h b/include/scsi/iscsi_if.h index fd0421c6d40a..95ed9424a11a 100644 --- a/include/scsi/iscsi_if.h +++ b/include/scsi/iscsi_if.h @@ -527,6 +527,7 @@ enum iscsi_err { ISCSI_ERR_XMIT_FAILED = ISCSI_ERR_BASE + 19, ISCSI_ERR_TCP_CONN_CLOSE = ISCSI_ERR_BASE + 20, ISCSI_ERR_SCSI_EH_SESSION_RST = ISCSI_ERR_BASE + 21, + ISCSI_ERR_NOP_TIMEDOUT = ISCSI_ERR_BASE + 22, }; /* -- cgit v1.2.3 From 3e3dc25fe7d5e33026bdfca5e8fab08be6a8729c Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Fri, 25 Jul 2014 02:53:38 -0700 Subject: crypto: Resolve shadow warnings Change formal parameters to not clash with global names to eliminate many W=2 warnings. Signed-off-by: Mark Rustad Signed-off-by: Jeff Kirsher Signed-off-by: Herbert Xu --- crypto/cryptd.c | 12 ++++---- crypto/eseqiv.c | 8 ++--- crypto/gcm.c | 30 +++++++++--------- crypto/seqiv.c | 16 +++++----- crypto/tcrypt.c | 77 ++++++++++++++++++++++++----------------------- include/crypto/aead.h | 4 +-- include/crypto/hash.h | 4 +-- include/crypto/skcipher.h | 4 +-- include/linux/crypto.h | 8 ++--- 9 files changed, 82 insertions(+), 81 deletions(-) (limited to 'include') diff --git a/crypto/cryptd.c b/crypto/cryptd.c index 7bdd61b867c8..e592c90abebb 100644 --- a/crypto/cryptd.c +++ b/crypto/cryptd.c @@ -233,7 +233,7 @@ static void cryptd_blkcipher_decrypt(struct crypto_async_request *req, int err) } static int cryptd_blkcipher_enqueue(struct ablkcipher_request *req, - crypto_completion_t complete) + crypto_completion_t compl) { struct cryptd_blkcipher_request_ctx *rctx = ablkcipher_request_ctx(req); struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req); @@ -241,7 +241,7 @@ static int cryptd_blkcipher_enqueue(struct ablkcipher_request *req, queue = cryptd_get_queue(crypto_ablkcipher_tfm(tfm)); rctx->complete = req->base.complete; - req->base.complete = complete; + req->base.complete = compl; return cryptd_enqueue_request(queue, &req->base); } @@ -414,7 +414,7 @@ static int cryptd_hash_setkey(struct crypto_ahash *parent, } static int cryptd_hash_enqueue(struct ahash_request *req, - crypto_completion_t complete) + crypto_completion_t compl) { struct cryptd_hash_request_ctx *rctx = ahash_request_ctx(req); struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); @@ -422,7 +422,7 @@ static int cryptd_hash_enqueue(struct ahash_request *req, cryptd_get_queue(crypto_ahash_tfm(tfm)); rctx->complete = req->base.complete; - req->base.complete = complete; + req->base.complete = compl; return cryptd_enqueue_request(queue, &req->base); } @@ -667,14 +667,14 @@ static void cryptd_aead_decrypt(struct crypto_async_request *areq, int err) } static int cryptd_aead_enqueue(struct aead_request *req, - crypto_completion_t complete) + crypto_completion_t compl) { struct cryptd_aead_request_ctx *rctx = aead_request_ctx(req); struct crypto_aead *tfm = crypto_aead_reqtfm(req); struct cryptd_queue *queue = cryptd_get_queue(crypto_aead_tfm(tfm)); rctx->complete = req->base.complete; - req->base.complete = complete; + req->base.complete = compl; return cryptd_enqueue_request(queue, &req->base); } diff --git a/crypto/eseqiv.c b/crypto/eseqiv.c index 42ce9f570aec..bf7ab4a89493 100644 --- a/crypto/eseqiv.c +++ b/crypto/eseqiv.c @@ -68,7 +68,7 @@ static int eseqiv_givencrypt(struct skcipher_givcrypt_request *req) struct eseqiv_ctx *ctx = crypto_ablkcipher_ctx(geniv); struct eseqiv_request_ctx *reqctx = skcipher_givcrypt_reqctx(req); struct ablkcipher_request *subreq; - crypto_completion_t complete; + crypto_completion_t compl; void *data; struct scatterlist *osrc, *odst; struct scatterlist *dst; @@ -86,7 +86,7 @@ static int eseqiv_givencrypt(struct skcipher_givcrypt_request *req) ablkcipher_request_set_tfm(subreq, skcipher_geniv_cipher(geniv)); giv = req->giv; - complete = req->creq.base.complete; + compl = req->creq.base.complete; data = req->creq.base.data; osrc = req->creq.src; @@ -101,11 +101,11 @@ static int eseqiv_givencrypt(struct skcipher_givcrypt_request *req) if (vsrc != giv + ivsize && vdst != giv + ivsize) { giv = PTR_ALIGN((u8 *)reqctx->tail, crypto_ablkcipher_alignmask(geniv) + 1); - complete = eseqiv_complete; + compl = eseqiv_complete; data = req; } - ablkcipher_request_set_callback(subreq, req->creq.base.flags, complete, + ablkcipher_request_set_callback(subreq, req->creq.base.flags, compl, data); sg_init_table(reqctx->src, 2); diff --git a/crypto/gcm.c b/crypto/gcm.c index b4f017939004..276cdac567b6 100644 --- a/crypto/gcm.c +++ b/crypto/gcm.c @@ -228,14 +228,14 @@ static void gcm_hash_final_done(struct crypto_async_request *areq, int err); static int gcm_hash_update(struct aead_request *req, struct crypto_gcm_req_priv_ctx *pctx, - crypto_completion_t complete, + crypto_completion_t compl, struct scatterlist *src, unsigned int len) { struct ahash_request *ahreq = &pctx->u.ahreq; ahash_request_set_callback(ahreq, aead_request_flags(req), - complete, req); + compl, req); ahash_request_set_crypt(ahreq, src, NULL, len); return crypto_ahash_update(ahreq); @@ -244,12 +244,12 @@ static int gcm_hash_update(struct aead_request *req, static int gcm_hash_remain(struct aead_request *req, struct crypto_gcm_req_priv_ctx *pctx, unsigned int remain, - crypto_completion_t complete) + crypto_completion_t compl) { struct ahash_request *ahreq = &pctx->u.ahreq; ahash_request_set_callback(ahreq, aead_request_flags(req), - complete, req); + compl, req); sg_init_one(pctx->src, gcm_zeroes, remain); ahash_request_set_crypt(ahreq, pctx->src, NULL, remain); @@ -375,14 +375,14 @@ static void __gcm_hash_assoc_remain_done(struct aead_request *req, int err) { struct crypto_gcm_req_priv_ctx *pctx = crypto_gcm_reqctx(req); struct crypto_gcm_ghash_ctx *gctx = &pctx->ghash_ctx; - crypto_completion_t complete; + crypto_completion_t compl; unsigned int remain = 0; if (!err && gctx->cryptlen) { remain = gcm_remain(gctx->cryptlen); - complete = remain ? gcm_hash_crypt_done : + compl = remain ? gcm_hash_crypt_done : gcm_hash_crypt_remain_done; - err = gcm_hash_update(req, pctx, complete, + err = gcm_hash_update(req, pctx, compl, gctx->src, gctx->cryptlen); if (err == -EINPROGRESS || err == -EBUSY) return; @@ -429,14 +429,14 @@ static void gcm_hash_assoc_done(struct crypto_async_request *areq, int err) static void __gcm_hash_init_done(struct aead_request *req, int err) { struct crypto_gcm_req_priv_ctx *pctx = crypto_gcm_reqctx(req); - crypto_completion_t complete; + crypto_completion_t compl; unsigned int remain = 0; if (!err && req->assoclen) { remain = gcm_remain(req->assoclen); - complete = remain ? gcm_hash_assoc_done : + compl = remain ? gcm_hash_assoc_done : gcm_hash_assoc_remain_done; - err = gcm_hash_update(req, pctx, complete, + err = gcm_hash_update(req, pctx, compl, req->assoc, req->assoclen); if (err == -EINPROGRESS || err == -EBUSY) return; @@ -462,7 +462,7 @@ static int gcm_hash(struct aead_request *req, struct crypto_gcm_ghash_ctx *gctx = &pctx->ghash_ctx; struct crypto_gcm_ctx *ctx = crypto_tfm_ctx(req->base.tfm); unsigned int remain; - crypto_completion_t complete; + crypto_completion_t compl; int err; ahash_request_set_tfm(ahreq, ctx->ghash); @@ -473,8 +473,8 @@ static int gcm_hash(struct aead_request *req, if (err) return err; remain = gcm_remain(req->assoclen); - complete = remain ? gcm_hash_assoc_done : gcm_hash_assoc_remain_done; - err = gcm_hash_update(req, pctx, complete, req->assoc, req->assoclen); + compl = remain ? gcm_hash_assoc_done : gcm_hash_assoc_remain_done; + err = gcm_hash_update(req, pctx, compl, req->assoc, req->assoclen); if (err) return err; if (remain) { @@ -484,8 +484,8 @@ static int gcm_hash(struct aead_request *req, return err; } remain = gcm_remain(gctx->cryptlen); - complete = remain ? gcm_hash_crypt_done : gcm_hash_crypt_remain_done; - err = gcm_hash_update(req, pctx, complete, gctx->src, gctx->cryptlen); + compl = remain ? gcm_hash_crypt_done : gcm_hash_crypt_remain_done; + err = gcm_hash_update(req, pctx, compl, gctx->src, gctx->cryptlen); if (err) return err; if (remain) { diff --git a/crypto/seqiv.c b/crypto/seqiv.c index f2cba4ed6f25..ee190fcedcd2 100644 --- a/crypto/seqiv.c +++ b/crypto/seqiv.c @@ -100,7 +100,7 @@ static int seqiv_givencrypt(struct skcipher_givcrypt_request *req) struct crypto_ablkcipher *geniv = skcipher_givcrypt_reqtfm(req); struct seqiv_ctx *ctx = crypto_ablkcipher_ctx(geniv); struct ablkcipher_request *subreq = skcipher_givcrypt_reqctx(req); - crypto_completion_t complete; + crypto_completion_t compl; void *data; u8 *info; unsigned int ivsize; @@ -108,7 +108,7 @@ static int seqiv_givencrypt(struct skcipher_givcrypt_request *req) ablkcipher_request_set_tfm(subreq, skcipher_geniv_cipher(geniv)); - complete = req->creq.base.complete; + compl = req->creq.base.complete; data = req->creq.base.data; info = req->creq.info; @@ -122,11 +122,11 @@ static int seqiv_givencrypt(struct skcipher_givcrypt_request *req) if (!info) return -ENOMEM; - complete = seqiv_complete; + compl = seqiv_complete; data = req; } - ablkcipher_request_set_callback(subreq, req->creq.base.flags, complete, + ablkcipher_request_set_callback(subreq, req->creq.base.flags, compl, data); ablkcipher_request_set_crypt(subreq, req->creq.src, req->creq.dst, req->creq.nbytes, info); @@ -146,7 +146,7 @@ static int seqiv_aead_givencrypt(struct aead_givcrypt_request *req) struct seqiv_ctx *ctx = crypto_aead_ctx(geniv); struct aead_request *areq = &req->areq; struct aead_request *subreq = aead_givcrypt_reqctx(req); - crypto_completion_t complete; + crypto_completion_t compl; void *data; u8 *info; unsigned int ivsize; @@ -154,7 +154,7 @@ static int seqiv_aead_givencrypt(struct aead_givcrypt_request *req) aead_request_set_tfm(subreq, aead_geniv_base(geniv)); - complete = areq->base.complete; + compl = areq->base.complete; data = areq->base.data; info = areq->iv; @@ -168,11 +168,11 @@ static int seqiv_aead_givencrypt(struct aead_givcrypt_request *req) if (!info) return -ENOMEM; - complete = seqiv_aead_complete; + compl = seqiv_aead_complete; data = req; } - aead_request_set_callback(subreq, areq->base.flags, complete, data); + aead_request_set_callback(subreq, areq->base.flags, compl, data); aead_request_set_crypt(subreq, areq->src, areq->dst, areq->cryptlen, info); aead_request_set_assoc(subreq, areq->assoc, areq->assoclen); diff --git a/crypto/tcrypt.c b/crypto/tcrypt.c index c48d07857a2b..890449e6e7ef 100644 --- a/crypto/tcrypt.c +++ b/crypto/tcrypt.c @@ -73,13 +73,13 @@ static char *check[] = { }; static int test_cipher_jiffies(struct blkcipher_desc *desc, int enc, - struct scatterlist *sg, int blen, int sec) + struct scatterlist *sg, int blen, int secs) { unsigned long start, end; int bcount; int ret; - for (start = jiffies, end = start + sec * HZ, bcount = 0; + for (start = jiffies, end = start + secs * HZ, bcount = 0; time_before(jiffies, end); bcount++) { if (enc) ret = crypto_blkcipher_encrypt(desc, sg, sg, blen); @@ -91,7 +91,7 @@ static int test_cipher_jiffies(struct blkcipher_desc *desc, int enc, } printk("%d operations in %d seconds (%ld bytes)\n", - bcount, sec, (long)bcount * blen); + bcount, secs, (long)bcount * blen); return 0; } @@ -143,13 +143,13 @@ out: } static int test_aead_jiffies(struct aead_request *req, int enc, - int blen, int sec) + int blen, int secs) { unsigned long start, end; int bcount; int ret; - for (start = jiffies, end = start + sec * HZ, bcount = 0; + for (start = jiffies, end = start + secs * HZ, bcount = 0; time_before(jiffies, end); bcount++) { if (enc) ret = crypto_aead_encrypt(req); @@ -161,7 +161,7 @@ static int test_aead_jiffies(struct aead_request *req, int enc, } printk("%d operations in %d seconds (%ld bytes)\n", - bcount, sec, (long)bcount * blen); + bcount, secs, (long)bcount * blen); return 0; } @@ -265,7 +265,7 @@ static void sg_init_aead(struct scatterlist *sg, char *xbuf[XBUFSIZE], } } -static void test_aead_speed(const char *algo, int enc, unsigned int sec, +static void test_aead_speed(const char *algo, int enc, unsigned int secs, struct aead_speed_template *template, unsigned int tcount, u8 authsize, unsigned int aad_size, u8 *keysize) @@ -379,8 +379,9 @@ static void test_aead_speed(const char *algo, int enc, unsigned int sec, aead_request_set_crypt(req, sg, sgout, *b_size, iv); aead_request_set_assoc(req, asg, aad_size); - if (sec) - ret = test_aead_jiffies(req, enc, *b_size, sec); + if (secs) + ret = test_aead_jiffies(req, enc, *b_size, + secs); else ret = test_aead_cycles(req, enc, *b_size); @@ -410,7 +411,7 @@ out_noxbuf: return; } -static void test_cipher_speed(const char *algo, int enc, unsigned int sec, +static void test_cipher_speed(const char *algo, int enc, unsigned int secs, struct cipher_speed_template *template, unsigned int tcount, u8 *keysize) { @@ -489,9 +490,9 @@ static void test_cipher_speed(const char *algo, int enc, unsigned int sec, crypto_blkcipher_set_iv(tfm, iv, iv_len); } - if (sec) + if (secs) ret = test_cipher_jiffies(&desc, enc, sg, - *b_size, sec); + *b_size, secs); else ret = test_cipher_cycles(&desc, enc, sg, *b_size); @@ -512,13 +513,13 @@ out: static int test_hash_jiffies_digest(struct hash_desc *desc, struct scatterlist *sg, int blen, - char *out, int sec) + char *out, int secs) { unsigned long start, end; int bcount; int ret; - for (start = jiffies, end = start + sec * HZ, bcount = 0; + for (start = jiffies, end = start + secs * HZ, bcount = 0; time_before(jiffies, end); bcount++) { ret = crypto_hash_digest(desc, sg, blen, out); if (ret) @@ -526,22 +527,22 @@ static int test_hash_jiffies_digest(struct hash_desc *desc, } printk("%6u opers/sec, %9lu bytes/sec\n", - bcount / sec, ((long)bcount * blen) / sec); + bcount / secs, ((long)bcount * blen) / secs); return 0; } static int test_hash_jiffies(struct hash_desc *desc, struct scatterlist *sg, - int blen, int plen, char *out, int sec) + int blen, int plen, char *out, int secs) { unsigned long start, end; int bcount, pcount; int ret; if (plen == blen) - return test_hash_jiffies_digest(desc, sg, blen, out, sec); + return test_hash_jiffies_digest(desc, sg, blen, out, secs); - for (start = jiffies, end = start + sec * HZ, bcount = 0; + for (start = jiffies, end = start + secs * HZ, bcount = 0; time_before(jiffies, end); bcount++) { ret = crypto_hash_init(desc); if (ret) @@ -558,7 +559,7 @@ static int test_hash_jiffies(struct hash_desc *desc, struct scatterlist *sg, } printk("%6u opers/sec, %9lu bytes/sec\n", - bcount / sec, ((long)bcount * blen) / sec); + bcount / secs, ((long)bcount * blen) / secs); return 0; } @@ -679,7 +680,7 @@ static void test_hash_sg_init(struct scatterlist *sg) } } -static void test_hash_speed(const char *algo, unsigned int sec, +static void test_hash_speed(const char *algo, unsigned int secs, struct hash_speed *speed) { struct scatterlist sg[TVMEMSIZE]; @@ -725,9 +726,9 @@ static void test_hash_speed(const char *algo, unsigned int sec, "(%5u byte blocks,%5u bytes per update,%4u updates): ", i, speed[i].blen, speed[i].plen, speed[i].blen / speed[i].plen); - if (sec) + if (secs) ret = test_hash_jiffies(&desc, sg, speed[i].blen, - speed[i].plen, output, sec); + speed[i].plen, output, secs); else ret = test_hash_cycles(&desc, sg, speed[i].blen, speed[i].plen, output); @@ -772,13 +773,13 @@ static inline int do_one_ahash_op(struct ahash_request *req, int ret) } static int test_ahash_jiffies_digest(struct ahash_request *req, int blen, - char *out, int sec) + char *out, int secs) { unsigned long start, end; int bcount; int ret; - for (start = jiffies, end = start + sec * HZ, bcount = 0; + for (start = jiffies, end = start + secs * HZ, bcount = 0; time_before(jiffies, end); bcount++) { ret = do_one_ahash_op(req, crypto_ahash_digest(req)); if (ret) @@ -786,22 +787,22 @@ static int test_ahash_jiffies_digest(struct ahash_request *req, int blen, } printk("%6u opers/sec, %9lu bytes/sec\n", - bcount / sec, ((long)bcount * blen) / sec); + bcount / secs, ((long)bcount * blen) / secs); return 0; } static int test_ahash_jiffies(struct ahash_request *req, int blen, - int plen, char *out, int sec) + int plen, char *out, int secs) { unsigned long start, end; int bcount, pcount; int ret; if (plen == blen) - return test_ahash_jiffies_digest(req, blen, out, sec); + return test_ahash_jiffies_digest(req, blen, out, secs); - for (start = jiffies, end = start + sec * HZ, bcount = 0; + for (start = jiffies, end = start + secs * HZ, bcount = 0; time_before(jiffies, end); bcount++) { ret = crypto_ahash_init(req); if (ret) @@ -818,7 +819,7 @@ static int test_ahash_jiffies(struct ahash_request *req, int blen, } pr_cont("%6u opers/sec, %9lu bytes/sec\n", - bcount / sec, ((long)bcount * blen) / sec); + bcount / secs, ((long)bcount * blen) / secs); return 0; } @@ -918,7 +919,7 @@ out: return 0; } -static void test_ahash_speed(const char *algo, unsigned int sec, +static void test_ahash_speed(const char *algo, unsigned int secs, struct hash_speed *speed) { struct scatterlist sg[TVMEMSIZE]; @@ -968,9 +969,9 @@ static void test_ahash_speed(const char *algo, unsigned int sec, ahash_request_set_crypt(req, sg, output, speed[i].plen); - if (sec) + if (secs) ret = test_ahash_jiffies(req, speed[i].blen, - speed[i].plen, output, sec); + speed[i].plen, output, secs); else ret = test_ahash_cycles(req, speed[i].blen, speed[i].plen, output); @@ -1002,13 +1003,13 @@ static inline int do_one_acipher_op(struct ablkcipher_request *req, int ret) } static int test_acipher_jiffies(struct ablkcipher_request *req, int enc, - int blen, int sec) + int blen, int secs) { unsigned long start, end; int bcount; int ret; - for (start = jiffies, end = start + sec * HZ, bcount = 0; + for (start = jiffies, end = start + secs * HZ, bcount = 0; time_before(jiffies, end); bcount++) { if (enc) ret = do_one_acipher_op(req, @@ -1022,7 +1023,7 @@ static int test_acipher_jiffies(struct ablkcipher_request *req, int enc, } pr_cont("%d operations in %d seconds (%ld bytes)\n", - bcount, sec, (long)bcount * blen); + bcount, secs, (long)bcount * blen); return 0; } @@ -1073,7 +1074,7 @@ out: return ret; } -static void test_acipher_speed(const char *algo, int enc, unsigned int sec, +static void test_acipher_speed(const char *algo, int enc, unsigned int secs, struct cipher_speed_template *template, unsigned int tcount, u8 *keysize) { @@ -1177,9 +1178,9 @@ static void test_acipher_speed(const char *algo, int enc, unsigned int sec, ablkcipher_request_set_crypt(req, sg, sg, *b_size, iv); - if (sec) + if (secs) ret = test_acipher_jiffies(req, enc, - *b_size, sec); + *b_size, secs); else ret = test_acipher_cycles(req, enc, *b_size); diff --git a/include/crypto/aead.h b/include/crypto/aead.h index 0edf949f6369..94b19be67574 100644 --- a/include/crypto/aead.h +++ b/include/crypto/aead.h @@ -75,9 +75,9 @@ static inline void aead_givcrypt_free(struct aead_givcrypt_request *req) static inline void aead_givcrypt_set_callback( struct aead_givcrypt_request *req, u32 flags, - crypto_completion_t complete, void *data) + crypto_completion_t compl, void *data) { - aead_request_set_callback(&req->areq, flags, complete, data); + aead_request_set_callback(&req->areq, flags, compl, data); } static inline void aead_givcrypt_set_crypt(struct aead_givcrypt_request *req, diff --git a/include/crypto/hash.h b/include/crypto/hash.h index 26cb1eb16f4c..a39195539601 100644 --- a/include/crypto/hash.h +++ b/include/crypto/hash.h @@ -238,10 +238,10 @@ static inline struct ahash_request *ahash_request_cast( static inline void ahash_request_set_callback(struct ahash_request *req, u32 flags, - crypto_completion_t complete, + crypto_completion_t compl, void *data) { - req->base.complete = complete; + req->base.complete = compl; req->base.data = data; req->base.flags = flags; } diff --git a/include/crypto/skcipher.h b/include/crypto/skcipher.h index 25fd6126522d..07d245f073d1 100644 --- a/include/crypto/skcipher.h +++ b/include/crypto/skcipher.h @@ -86,9 +86,9 @@ static inline void skcipher_givcrypt_free(struct skcipher_givcrypt_request *req) static inline void skcipher_givcrypt_set_callback( struct skcipher_givcrypt_request *req, u32 flags, - crypto_completion_t complete, void *data) + crypto_completion_t compl, void *data) { - ablkcipher_request_set_callback(&req->creq, flags, complete, data); + ablkcipher_request_set_callback(&req->creq, flags, compl, data); } static inline void skcipher_givcrypt_set_crypt( diff --git a/include/linux/crypto.h b/include/linux/crypto.h index b92eadf92d72..d45e949699ea 100644 --- a/include/linux/crypto.h +++ b/include/linux/crypto.h @@ -710,9 +710,9 @@ static inline void ablkcipher_request_free(struct ablkcipher_request *req) static inline void ablkcipher_request_set_callback( struct ablkcipher_request *req, - u32 flags, crypto_completion_t complete, void *data) + u32 flags, crypto_completion_t compl, void *data) { - req->base.complete = complete; + req->base.complete = compl; req->base.data = data; req->base.flags = flags; } @@ -841,10 +841,10 @@ static inline void aead_request_free(struct aead_request *req) static inline void aead_request_set_callback(struct aead_request *req, u32 flags, - crypto_completion_t complete, + crypto_completion_t compl, void *data) { - req->base.complete = complete; + req->base.complete = compl; req->base.data = data; req->base.flags = flags; } -- cgit v1.2.3 From 41c3bd2039e0d7b3dc32313141773f20716ec524 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Fri, 1 Aug 2014 11:17:03 -0400 Subject: netlabel: fix a problem when setting bits below the previously lowest bit The NetLabel category (catmap) functions have a problem in that they assume categories will be set in an increasing manner, e.g. the next category set will always be larger than the last. Unfortunately, this is not a valid assumption and could result in problems when attempting to set categories less than the startbit in the lowest catmap node. In some cases kernel panics and other nasties can result. This patch corrects the problem by checking for this and allocating a new catmap node instance and placing it at the front of the list. Cc: stable@vger.kernel.org Reported-by: Christian Evans Signed-off-by: Paul Moore Tested-by: Casey Schaufler --- include/net/netlabel.h | 8 ++++---- net/ipv4/cipso_ipv4.c | 6 +++--- net/netlabel/netlabel_kapi.c | 26 ++++++++++++++++++-------- security/smack/smack_access.c | 2 +- 4 files changed, 26 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/net/netlabel.h b/include/net/netlabel.h index 4fe018c48ed9..1c40d658d008 100644 --- a/include/net/netlabel.h +++ b/include/net/netlabel.h @@ -394,10 +394,10 @@ int netlbl_secattr_catmap_walk(struct netlbl_lsm_secattr_catmap *catmap, u32 offset); int netlbl_secattr_catmap_walk_rng(struct netlbl_lsm_secattr_catmap *catmap, u32 offset); -int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap *catmap, +int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap **catmap, u32 bit, gfp_t flags); -int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap *catmap, +int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap **catmap, u32 start, u32 end, gfp_t flags); @@ -505,14 +505,14 @@ static inline int netlbl_secattr_catmap_walk_rng( return -ENOENT; } static inline int netlbl_secattr_catmap_setbit( - struct netlbl_lsm_secattr_catmap *catmap, + struct netlbl_lsm_secattr_catmap **catmap, u32 bit, gfp_t flags) { return 0; } static inline int netlbl_secattr_catmap_setrng( - struct netlbl_lsm_secattr_catmap *catmap, + struct netlbl_lsm_secattr_catmap **catmap, u32 start, u32 end, gfp_t flags) diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c index 69e77c8ff285..dd433c943537 100644 --- a/net/ipv4/cipso_ipv4.c +++ b/net/ipv4/cipso_ipv4.c @@ -973,7 +973,7 @@ static int cipso_v4_map_cat_rbm_ntoh(const struct cipso_v4_doi *doi_def, return -EPERM; break; } - ret_val = netlbl_secattr_catmap_setbit(secattr->attr.mls.cat, + ret_val = netlbl_secattr_catmap_setbit(&secattr->attr.mls.cat, host_spot, GFP_ATOMIC); if (ret_val != 0) @@ -1075,7 +1075,7 @@ static int cipso_v4_map_cat_enum_ntoh(const struct cipso_v4_doi *doi_def, u32 iter; for (iter = 0; iter < net_cat_len; iter += 2) { - ret_val = netlbl_secattr_catmap_setbit(secattr->attr.mls.cat, + ret_val = netlbl_secattr_catmap_setbit(&secattr->attr.mls.cat, get_unaligned_be16(&net_cat[iter]), GFP_ATOMIC); if (ret_val != 0) @@ -1217,7 +1217,7 @@ static int cipso_v4_map_cat_rng_ntoh(const struct cipso_v4_doi *doi_def, else cat_low = 0; - ret_val = netlbl_secattr_catmap_setrng(secattr->attr.mls.cat, + ret_val = netlbl_secattr_catmap_setrng(&secattr->attr.mls.cat, cat_low, cat_high, GFP_ATOMIC); diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index 3045a964f39c..84e810bef39a 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -522,7 +522,7 @@ int netlbl_secattr_catmap_walk_rng(struct netlbl_lsm_secattr_catmap *catmap, /** * netlbl_secattr_catmap_setbit - Set a bit in a LSM secattr catmap - * @catmap: the category bitmap + * @catmap: pointer to the category bitmap * @bit: the bit to set * @flags: memory allocation flags * @@ -531,18 +531,25 @@ int netlbl_secattr_catmap_walk_rng(struct netlbl_lsm_secattr_catmap *catmap, * negative values on failure. * */ -int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap *catmap, +int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap **catmap, u32 bit, gfp_t flags) { - struct netlbl_lsm_secattr_catmap *iter = catmap; + struct netlbl_lsm_secattr_catmap *iter = *catmap; u32 node_bit; u32 node_idx; while (iter->next != NULL && bit >= (iter->startbit + NETLBL_CATMAP_SIZE)) iter = iter->next; - if (bit >= (iter->startbit + NETLBL_CATMAP_SIZE)) { + if (bit < iter->startbit) { + iter = netlbl_secattr_catmap_alloc(flags); + if (iter == NULL) + return -ENOMEM; + iter->next = *catmap; + iter->startbit = bit & ~(NETLBL_CATMAP_SIZE - 1); + *catmap = iter; + } else if (bit >= (iter->startbit + NETLBL_CATMAP_SIZE)) { iter->next = netlbl_secattr_catmap_alloc(flags); if (iter->next == NULL) return -ENOMEM; @@ -560,7 +567,7 @@ int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap *catmap, /** * netlbl_secattr_catmap_setrng - Set a range of bits in a LSM secattr catmap - * @catmap: the category bitmap + * @catmap: pointer to the category bitmap * @start: the starting bit * @end: the last bit in the string * @flags: memory allocation flags @@ -570,15 +577,16 @@ int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap *catmap, * on success, negative values on failure. * */ -int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap *catmap, +int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap **catmap, u32 start, u32 end, gfp_t flags) { int ret_val = 0; - struct netlbl_lsm_secattr_catmap *iter = catmap; + struct netlbl_lsm_secattr_catmap *iter = *catmap; u32 iter_max_spot; u32 spot; + u32 orig_spot = iter->startbit; /* XXX - This could probably be made a bit faster by combining writes * to the catmap instead of setting a single bit each time, but for @@ -596,7 +604,9 @@ int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap *catmap, iter = iter->next; iter_max_spot = iter->startbit + NETLBL_CATMAP_SIZE; } - ret_val = netlbl_secattr_catmap_setbit(iter, spot, flags); + ret_val = netlbl_secattr_catmap_setbit(&iter, spot, flags); + if (iter->startbit < orig_spot) + *catmap = iter; } return ret_val; diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 14293cd9b1e5..9ecf4f4b67a1 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -444,7 +444,7 @@ int smk_netlbl_mls(int level, char *catset, struct netlbl_lsm_secattr *sap, for (m = 0x80; m != 0; m >>= 1, cat++) { if ((m & *cp) == 0) continue; - rc = netlbl_secattr_catmap_setbit(sap->attr.mls.cat, + rc = netlbl_secattr_catmap_setbit(&sap->attr.mls.cat, cat, GFP_ATOMIC); if (rc < 0) { netlbl_secattr_catmap_free(sap->attr.mls.cat); -- cgit v1.2.3 From 4b8feff251da3d7058b5779e21b33a85c686b974 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Fri, 1 Aug 2014 11:17:17 -0400 Subject: netlabel: fix the horribly broken catmap functions The NetLabel secattr catmap functions, and the SELinux import/export glue routines, were broken in many horrible ways and the SELinux glue code fiddled with the NetLabel catmap structures in ways that we probably shouldn't allow. At some point this "worked", but that was likely due to a bit of dumb luck and sub-par testing (both inflicted by yours truly). This patch corrects these problems by basically gutting the code in favor of something less obtuse and restoring the NetLabel abstractions in the SELinux catmap glue code. Everything is working now, and if it decides to break itself in the future this code will be much easier to debug than the code it replaces. One noteworthy side effect of the changes is that it is no longer necessary to allocate a NetLabel catmap before calling one of the NetLabel APIs to set a bit in the catmap. NetLabel will automatically allocate the catmap nodes when needed, resulting in less allocations when the lowest bit is greater than 255 and less code in the LSMs. Cc: stable@vger.kernel.org Reported-by: Christian Evans Signed-off-by: Paul Moore Tested-by: Casey Schaufler --- include/net/netlabel.h | 26 ++++- net/ipv4/cipso_ipv4.c | 12 --- net/netlabel/netlabel_kapi.c | 216 ++++++++++++++++++++++++++++++++---------- security/selinux/ss/ebitmap.c | 127 ++++++++++--------------- security/smack/smack_access.c | 5 +- 5 files changed, 240 insertions(+), 146 deletions(-) (limited to 'include') diff --git a/include/net/netlabel.h b/include/net/netlabel.h index 1c40d658d008..bda7a121f31e 100644 --- a/include/net/netlabel.h +++ b/include/net/netlabel.h @@ -285,11 +285,11 @@ static inline void netlbl_secattr_catmap_free( { struct netlbl_lsm_secattr_catmap *iter; - do { + while (catmap) { iter = catmap; catmap = catmap->next; kfree(iter); - } while (catmap); + } } /** @@ -394,6 +394,9 @@ int netlbl_secattr_catmap_walk(struct netlbl_lsm_secattr_catmap *catmap, u32 offset); int netlbl_secattr_catmap_walk_rng(struct netlbl_lsm_secattr_catmap *catmap, u32 offset); +int netlbl_secattr_catmap_getlong(struct netlbl_lsm_secattr_catmap *catmap, + u32 *offset, + unsigned long *bitmap); int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap **catmap, u32 bit, gfp_t flags); @@ -401,6 +404,10 @@ int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap **catmap, u32 start, u32 end, gfp_t flags); +int netlbl_secattr_catmap_setlong(struct netlbl_lsm_secattr_catmap **catmap, + u32 offset, + unsigned long bitmap, + gfp_t flags); /* * LSM protocol operations (NetLabel LSM/kernel API) @@ -504,6 +511,13 @@ static inline int netlbl_secattr_catmap_walk_rng( { return -ENOENT; } +static inline int netlbl_secattr_catmap_getlong( + struct netlbl_lsm_secattr_catmap *catmap, + u32 *offset, + unsigned long *bitmap) +{ + return 0; +} static inline int netlbl_secattr_catmap_setbit( struct netlbl_lsm_secattr_catmap **catmap, u32 bit, @@ -519,6 +533,14 @@ static inline int netlbl_secattr_catmap_setrng( { return 0; } +static int netlbl_secattr_catmap_setlong( + struct netlbl_lsm_secattr_catmap **catmap, + u32 offset, + unsigned long bitmap, + gfp_t flags) +{ + return 0; +} static inline int netlbl_enabled(void) { return 0; diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c index dd433c943537..8a0c7bd6eff4 100644 --- a/net/ipv4/cipso_ipv4.c +++ b/net/ipv4/cipso_ipv4.c @@ -1335,10 +1335,6 @@ static int cipso_v4_parsetag_rbm(const struct cipso_v4_doi *doi_def, secattr->flags |= NETLBL_SECATTR_MLS_LVL; if (tag_len > 4) { - secattr->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC); - if (secattr->attr.mls.cat == NULL) - return -ENOMEM; - ret_val = cipso_v4_map_cat_rbm_ntoh(doi_def, &tag[4], tag_len - 4, @@ -1430,10 +1426,6 @@ static int cipso_v4_parsetag_enum(const struct cipso_v4_doi *doi_def, secattr->flags |= NETLBL_SECATTR_MLS_LVL; if (tag_len > 4) { - secattr->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC); - if (secattr->attr.mls.cat == NULL) - return -ENOMEM; - ret_val = cipso_v4_map_cat_enum_ntoh(doi_def, &tag[4], tag_len - 4, @@ -1524,10 +1516,6 @@ static int cipso_v4_parsetag_rng(const struct cipso_v4_doi *doi_def, secattr->flags |= NETLBL_SECATTR_MLS_LVL; if (tag_len > 4) { - secattr->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC); - if (secattr->attr.mls.cat == NULL) - return -ENOMEM; - ret_val = cipso_v4_map_cat_rng_ntoh(doi_def, &tag[4], tag_len - 4, diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index 84e810bef39a..d9e10466b928 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -405,6 +405,63 @@ out_entry: * Security Attribute Functions */ +#define _CM_F_NONE 0x00000000 +#define _CM_F_ALLOC 0x00000001 + +/** + * _netlbl_secattr_catmap_getnode - Get a individual node from a catmap + * @catmap: pointer to the category bitmap + * @offset: the requested offset + * @cm_flags: catmap flags, see _CM_F_* + * @gfp_flags: memory allocation flags + * + * Description: + * Iterate through the catmap looking for the node associated with @offset; if + * the _CM_F_ALLOC flag is set in @cm_flags and there is no associated node, + * one will be created and inserted into the catmap. Returns a pointer to the + * node on success, NULL on failure. + * + */ +static struct netlbl_lsm_secattr_catmap *_netlbl_secattr_catmap_getnode( + struct netlbl_lsm_secattr_catmap **catmap, + u32 offset, + unsigned int cm_flags, + gfp_t gfp_flags) +{ + struct netlbl_lsm_secattr_catmap *iter = *catmap; + struct netlbl_lsm_secattr_catmap *prev = NULL; + + if (iter == NULL || offset < iter->startbit) + goto secattr_catmap_getnode_alloc; + while (iter && offset >= (iter->startbit + NETLBL_CATMAP_SIZE)) { + prev = iter; + iter = iter->next; + } + if (iter == NULL || offset < iter->startbit) + goto secattr_catmap_getnode_alloc; + + return iter; + +secattr_catmap_getnode_alloc: + if (!(cm_flags & _CM_F_ALLOC)) + return NULL; + + iter = netlbl_secattr_catmap_alloc(gfp_flags); + if (iter == NULL) + return NULL; + iter->startbit = offset & ~(NETLBL_CATMAP_SIZE - 1); + + if (prev == NULL) { + iter->next = *catmap; + *catmap = iter; + } else { + iter->next = prev->next; + prev->next = iter; + } + + return iter; +} + /** * netlbl_secattr_catmap_walk - Walk a LSM secattr catmap looking for a bit * @catmap: the category bitmap @@ -520,6 +577,54 @@ int netlbl_secattr_catmap_walk_rng(struct netlbl_lsm_secattr_catmap *catmap, return -ENOENT; } +/** + * netlbl_secattr_catmap_getlong - Export an unsigned long bitmap + * @catmap: pointer to the category bitmap + * @offset: pointer to the requested offset + * @bitmap: the exported bitmap + * + * Description: + * Export a bitmap with an offset greater than or equal to @offset and return + * it in @bitmap. The @offset must be aligned to an unsigned long and will be + * updated on return if different from what was requested; if the catmap is + * empty at the requested offset and beyond, the @offset is set to (u32)-1. + * Returns zero on sucess, negative values on failure. + * + */ +int netlbl_secattr_catmap_getlong(struct netlbl_lsm_secattr_catmap *catmap, + u32 *offset, + unsigned long *bitmap) +{ + struct netlbl_lsm_secattr_catmap *iter; + u32 off = *offset; + u32 idx; + + /* only allow aligned offsets */ + if ((off & (BITS_PER_LONG - 1)) != 0) + return -EINVAL; + + if (off < catmap->startbit) { + off = catmap->startbit; + *offset = off; + } + iter = _netlbl_secattr_catmap_getnode(&catmap, off, _CM_F_NONE, 0); + if (iter == NULL) { + *offset = (u32)-1; + return 0; + } + + if (off < iter->startbit) { + off = iter->startbit; + *offset = off; + } else + off -= iter->startbit; + + idx = off / NETLBL_CATMAP_MAPSIZE; + *bitmap = iter->bitmap[idx] >> (off % NETLBL_CATMAP_SIZE); + + return 0; +} + /** * netlbl_secattr_catmap_setbit - Set a bit in a LSM secattr catmap * @catmap: pointer to the category bitmap @@ -535,32 +640,16 @@ int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap **catmap, u32 bit, gfp_t flags) { - struct netlbl_lsm_secattr_catmap *iter = *catmap; - u32 node_bit; - u32 node_idx; + struct netlbl_lsm_secattr_catmap *iter; + u32 idx; - while (iter->next != NULL && - bit >= (iter->startbit + NETLBL_CATMAP_SIZE)) - iter = iter->next; - if (bit < iter->startbit) { - iter = netlbl_secattr_catmap_alloc(flags); - if (iter == NULL) - return -ENOMEM; - iter->next = *catmap; - iter->startbit = bit & ~(NETLBL_CATMAP_SIZE - 1); - *catmap = iter; - } else if (bit >= (iter->startbit + NETLBL_CATMAP_SIZE)) { - iter->next = netlbl_secattr_catmap_alloc(flags); - if (iter->next == NULL) - return -ENOMEM; - iter = iter->next; - iter->startbit = bit & ~(NETLBL_CATMAP_SIZE - 1); - } + iter = _netlbl_secattr_catmap_getnode(catmap, bit, _CM_F_ALLOC, flags); + if (iter == NULL) + return -ENOMEM; - /* gcc always rounds to zero when doing integer division */ - node_idx = (bit - iter->startbit) / NETLBL_CATMAP_MAPSIZE; - node_bit = bit - iter->startbit - (NETLBL_CATMAP_MAPSIZE * node_idx); - iter->bitmap[node_idx] |= NETLBL_CATMAP_BIT << node_bit; + bit -= iter->startbit; + idx = bit / NETLBL_CATMAP_MAPSIZE; + iter->bitmap[idx] |= NETLBL_CATMAP_BIT << (bit % NETLBL_CATMAP_MAPSIZE); return 0; } @@ -582,34 +671,61 @@ int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap **catmap, u32 end, gfp_t flags) { - int ret_val = 0; - struct netlbl_lsm_secattr_catmap *iter = *catmap; - u32 iter_max_spot; - u32 spot; - u32 orig_spot = iter->startbit; - - /* XXX - This could probably be made a bit faster by combining writes - * to the catmap instead of setting a single bit each time, but for - * right now skipping to the start of the range in the catmap should - * be a nice improvement over calling the individual setbit function - * repeatedly from a loop. */ - - while (iter->next != NULL && - start >= (iter->startbit + NETLBL_CATMAP_SIZE)) - iter = iter->next; - iter_max_spot = iter->startbit + NETLBL_CATMAP_SIZE; - - for (spot = start; spot <= end && ret_val == 0; spot++) { - if (spot >= iter_max_spot && iter->next != NULL) { - iter = iter->next; - iter_max_spot = iter->startbit + NETLBL_CATMAP_SIZE; - } - ret_val = netlbl_secattr_catmap_setbit(&iter, spot, flags); - if (iter->startbit < orig_spot) - *catmap = iter; + int rc = 0; + u32 spot = start; + + while (rc == 0 && spot <= end) { + if (((spot & (BITS_PER_LONG - 1)) != 0) && + ((end - spot) > BITS_PER_LONG)) { + rc = netlbl_secattr_catmap_setlong(catmap, + spot, + (unsigned long)-1, + flags); + spot += BITS_PER_LONG; + } else + rc = netlbl_secattr_catmap_setbit(catmap, + spot++, + flags); } - return ret_val; + return rc; +} + +/** + * netlbl_secattr_catmap_setlong - Import an unsigned long bitmap + * @catmap: pointer to the category bitmap + * @offset: offset to the start of the imported bitmap + * @bitmap: the bitmap to import + * @flags: memory allocation flags + * + * Description: + * Import the bitmap specified in @bitmap into @catmap, using the offset + * in @offset. The offset must be aligned to an unsigned long. Returns zero + * on success, negative values on failure. + * + */ +int netlbl_secattr_catmap_setlong(struct netlbl_lsm_secattr_catmap **catmap, + u32 offset, + unsigned long bitmap, + gfp_t flags) +{ + struct netlbl_lsm_secattr_catmap *iter; + u32 idx; + + /* only allow aligned offsets */ + if ((offset & (BITS_PER_LONG - 1)) != 0) + return -EINVAL; + + iter = _netlbl_secattr_catmap_getnode(catmap, + offset, _CM_F_ALLOC, flags); + if (iter == NULL) + return -ENOMEM; + + offset -= iter->startbit; + idx = offset / NETLBL_CATMAP_MAPSIZE; + iter->bitmap[idx] |= bitmap << (offset % NETLBL_CATMAP_MAPSIZE); + + return 0; } /* diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c index 820313a04d49..842deca9484d 100644 --- a/security/selinux/ss/ebitmap.c +++ b/security/selinux/ss/ebitmap.c @@ -89,48 +89,33 @@ int ebitmap_netlbl_export(struct ebitmap *ebmap, struct netlbl_lsm_secattr_catmap **catmap) { struct ebitmap_node *e_iter = ebmap->node; - struct netlbl_lsm_secattr_catmap *c_iter; - u32 cmap_idx, cmap_sft; - int i; - - /* NetLabel's NETLBL_CATMAP_MAPTYPE is defined as an array of u64, - * however, it is not always compatible with an array of unsigned long - * in ebitmap_node. - * In addition, you should pay attention the following implementation - * assumes unsigned long has a width equal with or less than 64-bit. - */ + unsigned long e_map; + u32 offset; + unsigned int iter; + int rc; if (e_iter == NULL) { *catmap = NULL; return 0; } - c_iter = netlbl_secattr_catmap_alloc(GFP_ATOMIC); - if (c_iter == NULL) - return -ENOMEM; - *catmap = c_iter; - c_iter->startbit = e_iter->startbit & ~(NETLBL_CATMAP_SIZE - 1); + if (*catmap != NULL) + netlbl_secattr_catmap_free(*catmap); + *catmap = NULL; while (e_iter) { - for (i = 0; i < EBITMAP_UNIT_NUMS; i++) { - unsigned int delta, e_startbit, c_endbit; - - e_startbit = e_iter->startbit + i * EBITMAP_UNIT_SIZE; - c_endbit = c_iter->startbit + NETLBL_CATMAP_SIZE; - if (e_startbit >= c_endbit) { - c_iter->next - = netlbl_secattr_catmap_alloc(GFP_ATOMIC); - if (c_iter->next == NULL) + offset = e_iter->startbit; + for (iter = 0; iter < EBITMAP_UNIT_NUMS; iter++) { + e_map = e_iter->maps[iter]; + if (e_map != 0) { + rc = netlbl_secattr_catmap_setlong(catmap, + offset, + e_map, + GFP_ATOMIC); + if (rc != 0) goto netlbl_export_failure; - c_iter = c_iter->next; - c_iter->startbit - = e_startbit & ~(NETLBL_CATMAP_SIZE - 1); } - delta = e_startbit - c_iter->startbit; - cmap_idx = delta / NETLBL_CATMAP_MAPSIZE; - cmap_sft = delta % NETLBL_CATMAP_MAPSIZE; - c_iter->bitmap[cmap_idx] - |= e_iter->maps[i] << cmap_sft; + offset += EBITMAP_UNIT_SIZE; } e_iter = e_iter->next; } @@ -155,56 +140,42 @@ netlbl_export_failure: int ebitmap_netlbl_import(struct ebitmap *ebmap, struct netlbl_lsm_secattr_catmap *catmap) { + int rc; struct ebitmap_node *e_iter = NULL; - struct ebitmap_node *emap_prev = NULL; - struct netlbl_lsm_secattr_catmap *c_iter = catmap; - u32 c_idx, c_pos, e_idx, e_sft; - - /* NetLabel's NETLBL_CATMAP_MAPTYPE is defined as an array of u64, - * however, it is not always compatible with an array of unsigned long - * in ebitmap_node. - * In addition, you should pay attention the following implementation - * assumes unsigned long has a width equal with or less than 64-bit. - */ - - do { - for (c_idx = 0; c_idx < NETLBL_CATMAP_MAPCNT; c_idx++) { - unsigned int delta; - u64 map = c_iter->bitmap[c_idx]; - - if (!map) - continue; + struct ebitmap_node *e_prev = NULL; + u32 offset = 0, idx; + unsigned long bitmap; + + for (;;) { + rc = netlbl_secattr_catmap_getlong(catmap, &offset, &bitmap); + if (rc < 0) + goto netlbl_import_failure; + if (offset == (u32)-1) + return 0; - c_pos = c_iter->startbit - + c_idx * NETLBL_CATMAP_MAPSIZE; - if (!e_iter - || c_pos >= e_iter->startbit + EBITMAP_SIZE) { - e_iter = kzalloc(sizeof(*e_iter), GFP_ATOMIC); - if (!e_iter) - goto netlbl_import_failure; - e_iter->startbit - = c_pos - (c_pos % EBITMAP_SIZE); - if (emap_prev == NULL) - ebmap->node = e_iter; - else - emap_prev->next = e_iter; - emap_prev = e_iter; - } - delta = c_pos - e_iter->startbit; - e_idx = delta / EBITMAP_UNIT_SIZE; - e_sft = delta % EBITMAP_UNIT_SIZE; - while (map) { - e_iter->maps[e_idx++] |= map & (-1UL); - map = EBITMAP_SHIFT_UNIT_SIZE(map); - } + if (e_iter == NULL || + offset >= e_iter->startbit + EBITMAP_SIZE) { + e_prev = e_iter; + e_iter = kzalloc(sizeof(*e_iter), GFP_ATOMIC); + if (e_iter == NULL) + goto netlbl_import_failure; + e_iter->startbit = offset & ~(EBITMAP_SIZE - 1); + if (e_prev == NULL) + ebmap->node = e_iter; + else + e_prev->next = e_iter; + ebmap->highbit = e_iter->startbit + EBITMAP_SIZE; } - c_iter = c_iter->next; - } while (c_iter); - if (e_iter != NULL) - ebmap->highbit = e_iter->startbit + EBITMAP_SIZE; - else - ebitmap_destroy(ebmap); + /* offset will always be aligned to an unsigned long */ + idx = EBITMAP_NODE_INDEX(e_iter, offset); + e_iter->maps[idx] = bitmap; + + /* next */ + offset += EBITMAP_UNIT_SIZE; + } + + /* NOTE: we should never reach this return */ return 0; netlbl_import_failure: diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 9ecf4f4b67a1..ea1bc5055792 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -435,10 +435,7 @@ int smk_netlbl_mls(int level, char *catset, struct netlbl_lsm_secattr *sap, sap->flags |= NETLBL_SECATTR_MLS_CAT; sap->attr.mls.lvl = level; - sap->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC); - if (!sap->attr.mls.cat) - return -ENOMEM; - sap->attr.mls.cat->startbit = 0; + sap->attr.mls.cat = NULL; for (cat = 1, cp = catset, byte = 0; byte < len; cp++, byte++) for (m = 0x80; m != 0; m >>= 1, cat++) { -- cgit v1.2.3 From 4fbe63d1c773cceef3fe1f6ed0c9c268f4f24760 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Fri, 1 Aug 2014 11:17:37 -0400 Subject: netlabel: shorter names for the NetLabel catmap funcs/structs Historically the NetLabel LSM secattr catmap functions and data structures have had very long names which makes a mess of the NetLabel code and anyone who uses NetLabel. This patch renames the catmap functions and structures from "*_secattr_catmap_*" to just "*_catmap_*" which improves things greatly. There are no substantial code or logic changes in this patch. Signed-off-by: Paul Moore Tested-by: Casey Schaufler --- include/net/netlabel.h | 100 +++++++++++++++++--------------------- net/ipv4/cipso_ipv4.c | 35 +++++++------- net/netlabel/netlabel_kapi.c | 109 ++++++++++++++++++++---------------------- security/selinux/ss/ebitmap.c | 18 +++---- security/selinux/ss/ebitmap.h | 8 ++-- security/smack/smack_access.c | 6 +-- security/smack/smack_lsm.c | 6 +-- security/smack/smackfs.c | 14 +++--- 8 files changed, 139 insertions(+), 157 deletions(-) (limited to 'include') diff --git a/include/net/netlabel.h b/include/net/netlabel.h index bda7a121f31e..a4fc39bb3e4f 100644 --- a/include/net/netlabel.h +++ b/include/net/netlabel.h @@ -139,7 +139,7 @@ struct netlbl_lsm_cache { }; /** - * struct netlbl_lsm_secattr_catmap - NetLabel LSM secattr category bitmap + * struct netlbl_lsm_catmap - NetLabel LSM secattr category bitmap * @startbit: the value of the lowest order bit in the bitmap * @bitmap: the category bitmap * @next: pointer to the next bitmap "node" or NULL @@ -162,10 +162,10 @@ struct netlbl_lsm_cache { #define NETLBL_CATMAP_SIZE (NETLBL_CATMAP_MAPSIZE * \ NETLBL_CATMAP_MAPCNT) #define NETLBL_CATMAP_BIT (NETLBL_CATMAP_MAPTYPE)0x01 -struct netlbl_lsm_secattr_catmap { +struct netlbl_lsm_catmap { u32 startbit; NETLBL_CATMAP_MAPTYPE bitmap[NETLBL_CATMAP_MAPCNT]; - struct netlbl_lsm_secattr_catmap *next; + struct netlbl_lsm_catmap *next; }; /** @@ -209,7 +209,7 @@ struct netlbl_lsm_secattr { struct netlbl_lsm_cache *cache; struct { struct { - struct netlbl_lsm_secattr_catmap *cat; + struct netlbl_lsm_catmap *cat; u32 lvl; } mls; u32 secid; @@ -258,7 +258,7 @@ static inline void netlbl_secattr_cache_free(struct netlbl_lsm_cache *cache) } /** - * netlbl_secattr_catmap_alloc - Allocate a LSM secattr catmap + * netlbl_catmap_alloc - Allocate a LSM secattr catmap * @flags: memory allocation flags * * Description: @@ -266,24 +266,22 @@ static inline void netlbl_secattr_cache_free(struct netlbl_lsm_cache *cache) * on failure. * */ -static inline struct netlbl_lsm_secattr_catmap *netlbl_secattr_catmap_alloc( - gfp_t flags) +static inline struct netlbl_lsm_catmap *netlbl_catmap_alloc(gfp_t flags) { - return kzalloc(sizeof(struct netlbl_lsm_secattr_catmap), flags); + return kzalloc(sizeof(struct netlbl_lsm_catmap), flags); } /** - * netlbl_secattr_catmap_free - Free a LSM secattr catmap + * netlbl_catmap_free - Free a LSM secattr catmap * @catmap: the category bitmap * * Description: * Free a LSM secattr catmap. * */ -static inline void netlbl_secattr_catmap_free( - struct netlbl_lsm_secattr_catmap *catmap) +static inline void netlbl_catmap_free(struct netlbl_lsm_catmap *catmap) { - struct netlbl_lsm_secattr_catmap *iter; + struct netlbl_lsm_catmap *iter; while (catmap) { iter = catmap; @@ -321,7 +319,7 @@ static inline void netlbl_secattr_destroy(struct netlbl_lsm_secattr *secattr) if (secattr->flags & NETLBL_SECATTR_CACHE) netlbl_secattr_cache_free(secattr->cache); if (secattr->flags & NETLBL_SECATTR_MLS_CAT) - netlbl_secattr_catmap_free(secattr->attr.mls.cat); + netlbl_catmap_free(secattr->attr.mls.cat); } /** @@ -390,24 +388,22 @@ int netlbl_cfg_cipsov4_map_add(u32 doi, /* * LSM security attribute operations */ -int netlbl_secattr_catmap_walk(struct netlbl_lsm_secattr_catmap *catmap, - u32 offset); -int netlbl_secattr_catmap_walk_rng(struct netlbl_lsm_secattr_catmap *catmap, - u32 offset); -int netlbl_secattr_catmap_getlong(struct netlbl_lsm_secattr_catmap *catmap, - u32 *offset, - unsigned long *bitmap); -int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap **catmap, - u32 bit, - gfp_t flags); -int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap **catmap, - u32 start, - u32 end, - gfp_t flags); -int netlbl_secattr_catmap_setlong(struct netlbl_lsm_secattr_catmap **catmap, - u32 offset, - unsigned long bitmap, - gfp_t flags); +int netlbl_catmap_walk(struct netlbl_lsm_catmap *catmap, u32 offset); +int netlbl_catmap_walkrng(struct netlbl_lsm_catmap *catmap, u32 offset); +int netlbl_catmap_getlong(struct netlbl_lsm_catmap *catmap, + u32 *offset, + unsigned long *bitmap); +int netlbl_catmap_setbit(struct netlbl_lsm_catmap **catmap, + u32 bit, + gfp_t flags); +int netlbl_catmap_setrng(struct netlbl_lsm_catmap **catmap, + u32 start, + u32 end, + gfp_t flags); +int netlbl_catmap_setlong(struct netlbl_lsm_catmap **catmap, + u32 offset, + unsigned long bitmap, + gfp_t flags); /* * LSM protocol operations (NetLabel LSM/kernel API) @@ -499,45 +495,39 @@ static inline int netlbl_cfg_cipsov4_map_add(u32 doi, { return -ENOSYS; } -static inline int netlbl_secattr_catmap_walk( - struct netlbl_lsm_secattr_catmap *catmap, - u32 offset) +static inline int netlbl_catmap_walk(struct netlbl_lsm_catmap *catmap, + u32 offset) { return -ENOENT; } -static inline int netlbl_secattr_catmap_walk_rng( - struct netlbl_lsm_secattr_catmap *catmap, - u32 offset) +static inline int netlbl_catmap_walkrng(struct netlbl_lsm_catmap *catmap, + u32 offset) { return -ENOENT; } -static inline int netlbl_secattr_catmap_getlong( - struct netlbl_lsm_secattr_catmap *catmap, - u32 *offset, - unsigned long *bitmap) +static inline int netlbl_catmap_getlong(struct netlbl_lsm_catmap *catmap, + u32 *offset, + unsigned long *bitmap) { return 0; } -static inline int netlbl_secattr_catmap_setbit( - struct netlbl_lsm_secattr_catmap **catmap, - u32 bit, - gfp_t flags) +static inline int netlbl_catmap_setbit(struct netlbl_lsm_catmap **catmap, + u32 bit, + gfp_t flags) { return 0; } -static inline int netlbl_secattr_catmap_setrng( - struct netlbl_lsm_secattr_catmap **catmap, - u32 start, - u32 end, - gfp_t flags) +static inline int netlbl_catmap_setrng(struct netlbl_lsm_catmap **catmap, + u32 start, + u32 end, + gfp_t flags) { return 0; } -static int netlbl_secattr_catmap_setlong( - struct netlbl_lsm_secattr_catmap **catmap, - u32 offset, - unsigned long bitmap, - gfp_t flags) +static int netlbl_catmap_setlong(struct netlbl_lsm_catmap **catmap, + u32 offset, + unsigned long bitmap, + gfp_t flags) { return 0; } diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c index 8a0c7bd6eff4..05b708bbdb0d 100644 --- a/net/ipv4/cipso_ipv4.c +++ b/net/ipv4/cipso_ipv4.c @@ -890,8 +890,8 @@ static int cipso_v4_map_cat_rbm_hton(const struct cipso_v4_doi *doi_def, } for (;;) { - host_spot = netlbl_secattr_catmap_walk(secattr->attr.mls.cat, - host_spot + 1); + host_spot = netlbl_catmap_walk(secattr->attr.mls.cat, + host_spot + 1); if (host_spot < 0) break; @@ -973,7 +973,7 @@ static int cipso_v4_map_cat_rbm_ntoh(const struct cipso_v4_doi *doi_def, return -EPERM; break; } - ret_val = netlbl_secattr_catmap_setbit(&secattr->attr.mls.cat, + ret_val = netlbl_catmap_setbit(&secattr->attr.mls.cat, host_spot, GFP_ATOMIC); if (ret_val != 0) @@ -1039,8 +1039,7 @@ static int cipso_v4_map_cat_enum_hton(const struct cipso_v4_doi *doi_def, u32 cat_iter = 0; for (;;) { - cat = netlbl_secattr_catmap_walk(secattr->attr.mls.cat, - cat + 1); + cat = netlbl_catmap_walk(secattr->attr.mls.cat, cat + 1); if (cat < 0) break; if ((cat_iter + 2) > net_cat_len) @@ -1075,9 +1074,9 @@ static int cipso_v4_map_cat_enum_ntoh(const struct cipso_v4_doi *doi_def, u32 iter; for (iter = 0; iter < net_cat_len; iter += 2) { - ret_val = netlbl_secattr_catmap_setbit(&secattr->attr.mls.cat, - get_unaligned_be16(&net_cat[iter]), - GFP_ATOMIC); + ret_val = netlbl_catmap_setbit(&secattr->attr.mls.cat, + get_unaligned_be16(&net_cat[iter]), + GFP_ATOMIC); if (ret_val != 0) return ret_val; } @@ -1155,8 +1154,7 @@ static int cipso_v4_map_cat_rng_hton(const struct cipso_v4_doi *doi_def, return -ENOSPC; for (;;) { - iter = netlbl_secattr_catmap_walk(secattr->attr.mls.cat, - iter + 1); + iter = netlbl_catmap_walk(secattr->attr.mls.cat, iter + 1); if (iter < 0) break; cat_size += (iter == 0 ? 0 : sizeof(u16)); @@ -1164,8 +1162,7 @@ static int cipso_v4_map_cat_rng_hton(const struct cipso_v4_doi *doi_def, return -ENOSPC; array[array_cnt++] = iter; - iter = netlbl_secattr_catmap_walk_rng(secattr->attr.mls.cat, - iter); + iter = netlbl_catmap_walkrng(secattr->attr.mls.cat, iter); if (iter < 0) return -EFAULT; cat_size += sizeof(u16); @@ -1217,10 +1214,10 @@ static int cipso_v4_map_cat_rng_ntoh(const struct cipso_v4_doi *doi_def, else cat_low = 0; - ret_val = netlbl_secattr_catmap_setrng(&secattr->attr.mls.cat, - cat_low, - cat_high, - GFP_ATOMIC); + ret_val = netlbl_catmap_setrng(&secattr->attr.mls.cat, + cat_low, + cat_high, + GFP_ATOMIC); if (ret_val != 0) return ret_val; } @@ -1340,7 +1337,7 @@ static int cipso_v4_parsetag_rbm(const struct cipso_v4_doi *doi_def, tag_len - 4, secattr); if (ret_val != 0) { - netlbl_secattr_catmap_free(secattr->attr.mls.cat); + netlbl_catmap_free(secattr->attr.mls.cat); return ret_val; } @@ -1431,7 +1428,7 @@ static int cipso_v4_parsetag_enum(const struct cipso_v4_doi *doi_def, tag_len - 4, secattr); if (ret_val != 0) { - netlbl_secattr_catmap_free(secattr->attr.mls.cat); + netlbl_catmap_free(secattr->attr.mls.cat); return ret_val; } @@ -1521,7 +1518,7 @@ static int cipso_v4_parsetag_rng(const struct cipso_v4_doi *doi_def, tag_len - 4, secattr); if (ret_val != 0) { - netlbl_secattr_catmap_free(secattr->attr.mls.cat); + netlbl_catmap_free(secattr->attr.mls.cat); return ret_val; } diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index bc3883814890..05ea4a4cc0ac 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -410,7 +410,7 @@ out_entry: #define _CM_F_WALK 0x00000002 /** - * _netlbl_secattr_catmap_getnode - Get a individual node from a catmap + * _netlbl_catmap_getnode - Get a individual node from a catmap * @catmap: pointer to the category bitmap * @offset: the requested offset * @cm_flags: catmap flags, see _CM_F_* @@ -424,36 +424,36 @@ out_entry: * be returned. Returns a pointer to the node on success, NULL on failure. * */ -static struct netlbl_lsm_secattr_catmap *_netlbl_secattr_catmap_getnode( - struct netlbl_lsm_secattr_catmap **catmap, - u32 offset, - unsigned int cm_flags, - gfp_t gfp_flags) +static struct netlbl_lsm_catmap *_netlbl_catmap_getnode( + struct netlbl_lsm_catmap **catmap, + u32 offset, + unsigned int cm_flags, + gfp_t gfp_flags) { - struct netlbl_lsm_secattr_catmap *iter = *catmap; - struct netlbl_lsm_secattr_catmap *prev = NULL; + struct netlbl_lsm_catmap *iter = *catmap; + struct netlbl_lsm_catmap *prev = NULL; if (iter == NULL) - goto secattr_catmap_getnode_alloc; + goto catmap_getnode_alloc; if (offset < iter->startbit) - goto secattr_catmap_getnode_walk; + goto catmap_getnode_walk; while (iter && offset >= (iter->startbit + NETLBL_CATMAP_SIZE)) { prev = iter; iter = iter->next; } if (iter == NULL || offset < iter->startbit) - goto secattr_catmap_getnode_walk; + goto catmap_getnode_walk; return iter; -secattr_catmap_getnode_walk: +catmap_getnode_walk: if (cm_flags & _CM_F_WALK) return iter; -secattr_catmap_getnode_alloc: +catmap_getnode_alloc: if (!(cm_flags & _CM_F_ALLOC)) return NULL; - iter = netlbl_secattr_catmap_alloc(gfp_flags); + iter = netlbl_catmap_alloc(gfp_flags); if (iter == NULL) return NULL; iter->startbit = offset & ~(NETLBL_CATMAP_SIZE - 1); @@ -470,7 +470,7 @@ secattr_catmap_getnode_alloc: } /** - * netlbl_secattr_catmap_walk - Walk a LSM secattr catmap looking for a bit + * netlbl_catmap_walk - Walk a LSM secattr catmap looking for a bit * @catmap: the category bitmap * @offset: the offset to start searching at, in bits * @@ -479,15 +479,14 @@ secattr_catmap_getnode_alloc: * returns the spot of the first set bit or -ENOENT if no bits are set. * */ -int netlbl_secattr_catmap_walk(struct netlbl_lsm_secattr_catmap *catmap, - u32 offset) +int netlbl_catmap_walk(struct netlbl_lsm_catmap *catmap, u32 offset) { - struct netlbl_lsm_secattr_catmap *iter = catmap; + struct netlbl_lsm_catmap *iter = catmap; u32 idx; u32 bit; NETLBL_CATMAP_MAPTYPE bitmap; - iter = _netlbl_secattr_catmap_getnode(&catmap, offset, _CM_F_WALK, 0); + iter = _netlbl_catmap_getnode(&catmap, offset, _CM_F_WALK, 0); if (iter == NULL) return -ENOENT; if (offset > iter->startbit) { @@ -524,7 +523,7 @@ int netlbl_secattr_catmap_walk(struct netlbl_lsm_secattr_catmap *catmap, } /** - * netlbl_secattr_catmap_walk_rng - Find the end of a string of set bits + * netlbl_catmap_walkrng - Find the end of a string of set bits * @catmap: the category bitmap * @offset: the offset to start searching at, in bits * @@ -534,17 +533,16 @@ int netlbl_secattr_catmap_walk(struct netlbl_lsm_secattr_catmap *catmap, * the end of the bitmap. * */ -int netlbl_secattr_catmap_walk_rng(struct netlbl_lsm_secattr_catmap *catmap, - u32 offset) +int netlbl_catmap_walkrng(struct netlbl_lsm_catmap *catmap, u32 offset) { - struct netlbl_lsm_secattr_catmap *iter; - struct netlbl_lsm_secattr_catmap *prev = NULL; + struct netlbl_lsm_catmap *iter; + struct netlbl_lsm_catmap *prev = NULL; u32 idx; u32 bit; NETLBL_CATMAP_MAPTYPE bitmask; NETLBL_CATMAP_MAPTYPE bitmap; - iter = _netlbl_secattr_catmap_getnode(&catmap, offset, _CM_F_WALK, 0); + iter = _netlbl_catmap_getnode(&catmap, offset, _CM_F_WALK, 0); if (iter == NULL) return -ENOENT; if (offset > iter->startbit) { @@ -584,7 +582,7 @@ int netlbl_secattr_catmap_walk_rng(struct netlbl_lsm_secattr_catmap *catmap, } /** - * netlbl_secattr_catmap_getlong - Export an unsigned long bitmap + * netlbl_catmap_getlong - Export an unsigned long bitmap * @catmap: pointer to the category bitmap * @offset: pointer to the requested offset * @bitmap: the exported bitmap @@ -597,11 +595,11 @@ int netlbl_secattr_catmap_walk_rng(struct netlbl_lsm_secattr_catmap *catmap, * Returns zero on sucess, negative values on failure. * */ -int netlbl_secattr_catmap_getlong(struct netlbl_lsm_secattr_catmap *catmap, - u32 *offset, - unsigned long *bitmap) +int netlbl_catmap_getlong(struct netlbl_lsm_catmap *catmap, + u32 *offset, + unsigned long *bitmap) { - struct netlbl_lsm_secattr_catmap *iter; + struct netlbl_lsm_catmap *iter; u32 off = *offset; u32 idx; @@ -613,7 +611,7 @@ int netlbl_secattr_catmap_getlong(struct netlbl_lsm_secattr_catmap *catmap, off = catmap->startbit; *offset = off; } - iter = _netlbl_secattr_catmap_getnode(&catmap, off, _CM_F_NONE, 0); + iter = _netlbl_catmap_getnode(&catmap, off, _CM_F_NONE, 0); if (iter == NULL) { *offset = (u32)-1; return 0; @@ -632,7 +630,7 @@ int netlbl_secattr_catmap_getlong(struct netlbl_lsm_secattr_catmap *catmap, } /** - * netlbl_secattr_catmap_setbit - Set a bit in a LSM secattr catmap + * netlbl_catmap_setbit - Set a bit in a LSM secattr catmap * @catmap: pointer to the category bitmap * @bit: the bit to set * @flags: memory allocation flags @@ -642,14 +640,14 @@ int netlbl_secattr_catmap_getlong(struct netlbl_lsm_secattr_catmap *catmap, * negative values on failure. * */ -int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap **catmap, - u32 bit, - gfp_t flags) +int netlbl_catmap_setbit(struct netlbl_lsm_catmap **catmap, + u32 bit, + gfp_t flags) { - struct netlbl_lsm_secattr_catmap *iter; + struct netlbl_lsm_catmap *iter; u32 idx; - iter = _netlbl_secattr_catmap_getnode(catmap, bit, _CM_F_ALLOC, flags); + iter = _netlbl_catmap_getnode(catmap, bit, _CM_F_ALLOC, flags); if (iter == NULL) return -ENOMEM; @@ -661,7 +659,7 @@ int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap **catmap, } /** - * netlbl_secattr_catmap_setrng - Set a range of bits in a LSM secattr catmap + * netlbl_catmap_setrng - Set a range of bits in a LSM secattr catmap * @catmap: pointer to the category bitmap * @start: the starting bit * @end: the last bit in the string @@ -672,10 +670,10 @@ int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap **catmap, * on success, negative values on failure. * */ -int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap **catmap, - u32 start, - u32 end, - gfp_t flags) +int netlbl_catmap_setrng(struct netlbl_lsm_catmap **catmap, + u32 start, + u32 end, + gfp_t flags) { int rc = 0; u32 spot = start; @@ -683,22 +681,20 @@ int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap **catmap, while (rc == 0 && spot <= end) { if (((spot & (BITS_PER_LONG - 1)) != 0) && ((end - spot) > BITS_PER_LONG)) { - rc = netlbl_secattr_catmap_setlong(catmap, - spot, - (unsigned long)-1, - flags); + rc = netlbl_catmap_setlong(catmap, + spot, + (unsigned long)-1, + flags); spot += BITS_PER_LONG; } else - rc = netlbl_secattr_catmap_setbit(catmap, - spot++, - flags); + rc = netlbl_catmap_setbit(catmap, spot++, flags); } return rc; } /** - * netlbl_secattr_catmap_setlong - Import an unsigned long bitmap + * netlbl_catmap_setlong - Import an unsigned long bitmap * @catmap: pointer to the category bitmap * @offset: offset to the start of the imported bitmap * @bitmap: the bitmap to import @@ -710,20 +706,19 @@ int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap **catmap, * on success, negative values on failure. * */ -int netlbl_secattr_catmap_setlong(struct netlbl_lsm_secattr_catmap **catmap, - u32 offset, - unsigned long bitmap, - gfp_t flags) +int netlbl_catmap_setlong(struct netlbl_lsm_catmap **catmap, + u32 offset, + unsigned long bitmap, + gfp_t flags) { - struct netlbl_lsm_secattr_catmap *iter; + struct netlbl_lsm_catmap *iter; u32 idx; /* only allow aligned offsets */ if ((offset & (BITS_PER_LONG - 1)) != 0) return -EINVAL; - iter = _netlbl_secattr_catmap_getnode(catmap, - offset, _CM_F_ALLOC, flags); + iter = _netlbl_catmap_getnode(catmap, offset, _CM_F_ALLOC, flags); if (iter == NULL) return -ENOMEM; diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c index 842deca9484d..afe6a269ec17 100644 --- a/security/selinux/ss/ebitmap.c +++ b/security/selinux/ss/ebitmap.c @@ -86,7 +86,7 @@ int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src) * */ int ebitmap_netlbl_export(struct ebitmap *ebmap, - struct netlbl_lsm_secattr_catmap **catmap) + struct netlbl_lsm_catmap **catmap) { struct ebitmap_node *e_iter = ebmap->node; unsigned long e_map; @@ -100,7 +100,7 @@ int ebitmap_netlbl_export(struct ebitmap *ebmap, } if (*catmap != NULL) - netlbl_secattr_catmap_free(*catmap); + netlbl_catmap_free(*catmap); *catmap = NULL; while (e_iter) { @@ -108,10 +108,10 @@ int ebitmap_netlbl_export(struct ebitmap *ebmap, for (iter = 0; iter < EBITMAP_UNIT_NUMS; iter++) { e_map = e_iter->maps[iter]; if (e_map != 0) { - rc = netlbl_secattr_catmap_setlong(catmap, - offset, - e_map, - GFP_ATOMIC); + rc = netlbl_catmap_setlong(catmap, + offset, + e_map, + GFP_ATOMIC); if (rc != 0) goto netlbl_export_failure; } @@ -123,7 +123,7 @@ int ebitmap_netlbl_export(struct ebitmap *ebmap, return 0; netlbl_export_failure: - netlbl_secattr_catmap_free(*catmap); + netlbl_catmap_free(*catmap); return -ENOMEM; } @@ -138,7 +138,7 @@ netlbl_export_failure: * */ int ebitmap_netlbl_import(struct ebitmap *ebmap, - struct netlbl_lsm_secattr_catmap *catmap) + struct netlbl_lsm_catmap *catmap) { int rc; struct ebitmap_node *e_iter = NULL; @@ -147,7 +147,7 @@ int ebitmap_netlbl_import(struct ebitmap *ebmap, unsigned long bitmap; for (;;) { - rc = netlbl_secattr_catmap_getlong(catmap, &offset, &bitmap); + rc = netlbl_catmap_getlong(catmap, &offset, &bitmap); if (rc < 0) goto netlbl_import_failure; if (offset == (u32)-1) diff --git a/security/selinux/ss/ebitmap.h b/security/selinux/ss/ebitmap.h index 712c8a7b8e8b..9637b8c71085 100644 --- a/security/selinux/ss/ebitmap.h +++ b/security/selinux/ss/ebitmap.h @@ -132,17 +132,17 @@ int ebitmap_write(struct ebitmap *e, void *fp); #ifdef CONFIG_NETLABEL int ebitmap_netlbl_export(struct ebitmap *ebmap, - struct netlbl_lsm_secattr_catmap **catmap); + struct netlbl_lsm_catmap **catmap); int ebitmap_netlbl_import(struct ebitmap *ebmap, - struct netlbl_lsm_secattr_catmap *catmap); + struct netlbl_lsm_catmap *catmap); #else static inline int ebitmap_netlbl_export(struct ebitmap *ebmap, - struct netlbl_lsm_secattr_catmap **catmap) + struct netlbl_lsm_catmap **catmap) { return -ENOMEM; } static inline int ebitmap_netlbl_import(struct ebitmap *ebmap, - struct netlbl_lsm_secattr_catmap *catmap) + struct netlbl_lsm_catmap *catmap) { return -ENOMEM; } diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index ea1bc5055792..732df7b91227 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -441,10 +441,10 @@ int smk_netlbl_mls(int level, char *catset, struct netlbl_lsm_secattr *sap, for (m = 0x80; m != 0; m >>= 1, cat++) { if ((m & *cp) == 0) continue; - rc = netlbl_secattr_catmap_setbit(&sap->attr.mls.cat, - cat, GFP_ATOMIC); + rc = netlbl_catmap_setbit(&sap->attr.mls.cat, + cat, GFP_ATOMIC); if (rc < 0) { - netlbl_secattr_catmap_free(sap->attr.mls.cat); + netlbl_catmap_free(sap->attr.mls.cat); return rc; } } diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 14f52be78c75..c32bba566df9 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -3091,9 +3091,9 @@ static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap, break; } for (acat = -1, kcat = -1; acat == kcat; ) { - acat = netlbl_secattr_catmap_walk( - sap->attr.mls.cat, acat + 1); - kcat = netlbl_secattr_catmap_walk( + acat = netlbl_catmap_walk(sap->attr.mls.cat, + acat + 1); + kcat = netlbl_catmap_walk( skp->smk_netlabel.attr.mls.cat, kcat + 1); if (acat < 0 || kcat < 0) diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 3198cfe1dcc6..893b06b93f6d 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -777,7 +777,7 @@ static int cipso_seq_show(struct seq_file *s, void *v) struct list_head *list = v; struct smack_known *skp = list_entry(list, struct smack_known, list); - struct netlbl_lsm_secattr_catmap *cmp = skp->smk_netlabel.attr.mls.cat; + struct netlbl_lsm_catmap *cmp = skp->smk_netlabel.attr.mls.cat; char sep = '/'; int i; @@ -794,8 +794,8 @@ static int cipso_seq_show(struct seq_file *s, void *v) seq_printf(s, "%s %3d", skp->smk_known, skp->smk_netlabel.attr.mls.lvl); - for (i = netlbl_secattr_catmap_walk(cmp, 0); i >= 0; - i = netlbl_secattr_catmap_walk(cmp, i + 1)) { + for (i = netlbl_catmap_walk(cmp, 0); i >= 0; + i = netlbl_catmap_walk(cmp, i + 1)) { seq_printf(s, "%c%d", sep, i); sep = ','; } @@ -916,7 +916,7 @@ static ssize_t smk_set_cipso(struct file *file, const char __user *buf, rc = smk_netlbl_mls(maplevel, mapcatset, &ncats, SMK_CIPSOLEN); if (rc >= 0) { - netlbl_secattr_catmap_free(skp->smk_netlabel.attr.mls.cat); + netlbl_catmap_free(skp->smk_netlabel.attr.mls.cat); skp->smk_netlabel.attr.mls.cat = ncats.attr.mls.cat; skp->smk_netlabel.attr.mls.lvl = ncats.attr.mls.lvl; rc = count; @@ -966,14 +966,14 @@ static int cipso2_seq_show(struct seq_file *s, void *v) struct list_head *list = v; struct smack_known *skp = list_entry(list, struct smack_known, list); - struct netlbl_lsm_secattr_catmap *cmp = skp->smk_netlabel.attr.mls.cat; + struct netlbl_lsm_catmap *cmp = skp->smk_netlabel.attr.mls.cat; char sep = '/'; int i; seq_printf(s, "%s %3d", skp->smk_known, skp->smk_netlabel.attr.mls.lvl); - for (i = netlbl_secattr_catmap_walk(cmp, 0); i >= 0; - i = netlbl_secattr_catmap_walk(cmp, i + 1)) { + for (i = netlbl_catmap_walk(cmp, 0); i >= 0; + i = netlbl_catmap_walk(cmp, i + 1)) { seq_printf(s, "%c%d", sep, i); sep = ','; } -- cgit v1.2.3 From 9dcfee01930e6cc1e84d28c232664f0c19a1f86c Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Mon, 14 Jul 2014 10:28:04 +0200 Subject: drivers: of: add automated assignment of reserved regions to client devices This patch adds code for automated assignment of reserved memory regions to struct device. reserved_mem->ops->device_init()/device_cleanup() callbacks are called to perform reserved memory driver specific initialization and cleanup Based on previous code provided by Josh Cartwright Signed-off-by: Marek Szyprowski Signed-off-by: Grant Likely --- drivers/of/of_reserved_mem.c | 70 +++++++++++++++++++++++++++++++++++++++++ include/linux/of_reserved_mem.h | 7 +++++ 2 files changed, 77 insertions(+) (limited to 'include') diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index 632aae861375..59fb12e84e6b 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -206,8 +206,16 @@ void __init fdt_init_reserved_mem(void) for (i = 0; i < reserved_mem_count; i++) { struct reserved_mem *rmem = &reserved_mem[i]; unsigned long node = rmem->fdt_node; + int len; + const __be32 *prop; int err = 0; + prop = of_get_flat_dt_prop(node, "phandle", &len); + if (!prop) + prop = of_get_flat_dt_prop(node, "linux,phandle", &len); + if (prop) + rmem->phandle = of_read_number(prop, len/4); + if (rmem->size == 0) err = __reserved_mem_alloc_size(node, rmem->name, &rmem->base, &rmem->size); @@ -215,3 +223,65 @@ void __init fdt_init_reserved_mem(void) __reserved_mem_init_node(rmem); } } + +static inline struct reserved_mem *__find_rmem(struct device_node *node) +{ + unsigned int i; + + if (!node->phandle) + return NULL; + + for (i = 0; i < reserved_mem_count; i++) + if (reserved_mem[i].phandle == node->phandle) + return &reserved_mem[i]; + return NULL; +} + +/** + * of_reserved_mem_device_init() - assign reserved memory region to given device + * + * This function assign memory region pointed by "memory-region" device tree + * property to the given device. + */ +void of_reserved_mem_device_init(struct device *dev) +{ + struct reserved_mem *rmem; + struct device_node *np; + + np = of_parse_phandle(dev->of_node, "memory-region", 0); + if (!np) + return; + + rmem = __find_rmem(np); + of_node_put(np); + + if (!rmem || !rmem->ops || !rmem->ops->device_init) + return; + + rmem->ops->device_init(rmem, dev); + dev_info(dev, "assigned reserved memory node %s\n", rmem->name); +} + +/** + * of_reserved_mem_device_release() - release reserved memory device structures + * + * This function releases structures allocated for memory region handling for + * the given device. + */ +void of_reserved_mem_device_release(struct device *dev) +{ + struct reserved_mem *rmem; + struct device_node *np; + + np = of_parse_phandle(dev->of_node, "memory-region", 0); + if (!np) + return; + + rmem = __find_rmem(np); + of_node_put(np); + + if (!rmem || !rmem->ops || !rmem->ops->device_release) + return; + + rmem->ops->device_release(rmem, dev); +} diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h index 4669ddfdd5af..5b5efae09135 100644 --- a/include/linux/of_reserved_mem.h +++ b/include/linux/of_reserved_mem.h @@ -8,6 +8,7 @@ struct reserved_mem_ops; struct reserved_mem { const char *name; unsigned long fdt_node; + unsigned long phandle; const struct reserved_mem_ops *ops; phys_addr_t base; phys_addr_t size; @@ -27,10 +28,16 @@ typedef int (*reservedmem_of_init_fn)(struct reserved_mem *rmem); _OF_DECLARE(reservedmem, name, compat, init, reservedmem_of_init_fn) #ifdef CONFIG_OF_RESERVED_MEM +void of_reserved_mem_device_init(struct device *dev); +void of_reserved_mem_device_release(struct device *dev); + void fdt_init_reserved_mem(void); void fdt_reserved_mem_save_node(unsigned long node, const char *uname, phys_addr_t base, phys_addr_t size); #else +static inline void of_reserved_mem_device_init(struct device *dev) { } +static inline void of_reserved_mem_device_release(struct device *pdev) { } + static inline void fdt_init_reserved_mem(void) { } static inline void fdt_reserved_mem_save_node(unsigned long node, const char *uname, phys_addr_t base, phys_addr_t size) { } -- cgit v1.2.3 From b008c0fc95140ca7aad65861a77f0338ce31e8b5 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Wed, 23 Jul 2014 17:26:36 +0200 Subject: drm: remove unused "struct drm_freelist" This object is not used except for static fields in drm_bufs *cough*. Inline the watermark fields and drop the unused structure definition. Signed-off-by: David Herrmann Reviewed-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_bufs.c | 17 ++++++++--------- drivers/gpu/drm/drm_info.c | 2 +- include/drm/drmP.h | 15 ++------------- 3 files changed, 11 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c index 68175b54504b..61acb8f6756d 100644 --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -1217,7 +1217,6 @@ int drm_infobufs(struct drm_device *dev, void *data, struct drm_buf_desc __user *to = &request->list[count]; struct drm_buf_entry *from = &dma->bufs[i]; - struct drm_freelist *list = &dma->bufs[i].freelist; if (copy_to_user(&to->count, &from->buf_count, sizeof(from->buf_count)) || @@ -1225,19 +1224,19 @@ int drm_infobufs(struct drm_device *dev, void *data, &from->buf_size, sizeof(from->buf_size)) || copy_to_user(&to->low_mark, - &list->low_mark, - sizeof(list->low_mark)) || + &from->low_mark, + sizeof(from->low_mark)) || copy_to_user(&to->high_mark, - &list->high_mark, - sizeof(list->high_mark))) + &from->high_mark, + sizeof(from->high_mark))) return -EFAULT; DRM_DEBUG("%d %d %d %d %d\n", i, dma->bufs[i].buf_count, dma->bufs[i].buf_size, - dma->bufs[i].freelist.low_mark, - dma->bufs[i].freelist.high_mark); + dma->bufs[i].low_mark, + dma->bufs[i].high_mark); ++count; } } @@ -1290,8 +1289,8 @@ int drm_markbufs(struct drm_device *dev, void *data, if (request->high_mark < 0 || request->high_mark > entry->buf_count) return -EINVAL; - entry->freelist.low_mark = request->low_mark; - entry->freelist.high_mark = request->high_mark; + entry->low_mark = request->low_mark; + entry->high_mark = request->high_mark; return 0; } diff --git a/drivers/gpu/drm/drm_info.c b/drivers/gpu/drm/drm_info.c index 86feedd5e6f6..ecaf0fa2eec8 100644 --- a/drivers/gpu/drm/drm_info.c +++ b/drivers/gpu/drm/drm_info.c @@ -132,7 +132,7 @@ int drm_bufs_info(struct seq_file *m, void *data) i, dma->bufs[i].buf_size, dma->bufs[i].buf_count, - atomic_read(&dma->bufs[i].freelist.count), + 0, dma->bufs[i].seg_count, seg_pages, seg_pages * PAGE_SIZE / 1024); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 9b6a445f8602..335b7b8f5e7d 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -346,18 +346,6 @@ struct drm_waitlist { spinlock_t write_lock; }; -struct drm_freelist { - int initialized; /**< Freelist in use */ - atomic_t count; /**< Number of free buffers */ - struct drm_buf *next; /**< End pointer */ - - wait_queue_head_t waiting; /**< Processes waiting on free bufs */ - int low_mark; /**< Low water mark */ - int high_mark; /**< High water mark */ - atomic_t wfh; /**< If waiting for high mark */ - spinlock_t lock; -}; - typedef struct drm_dma_handle { dma_addr_t busaddr; void *vaddr; @@ -375,7 +363,8 @@ struct drm_buf_entry { int page_order; struct drm_dma_handle **seglist; - struct drm_freelist freelist; + int low_mark; /**< Low water mark */ + int high_mark; /**< High water mark */ }; /* Event queued up for userspace to read */ -- cgit v1.2.3 From 344f4b0f4c14a12206b05aa033f3174cefbd282f Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Wed, 23 Jul 2014 17:26:37 +0200 Subject: drm: drop unused "struct drm_queue" This object is unused, drop it. Signed-off-by: David Herrmann Reviewed-by: Daniel Vetter Signed-off-by: Dave Airlie --- include/drm/drmP.h | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'include') diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 335b7b8f5e7d..d3d9be6b83ef 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -430,23 +430,6 @@ struct drm_file { struct drm_prime_file_private prime; }; -/** Wait queue */ -struct drm_queue { - atomic_t use_count; /**< Outstanding uses (+1) */ - atomic_t finalization; /**< Finalization in progress */ - atomic_t block_count; /**< Count of processes waiting */ - atomic_t block_read; /**< Queue blocked for reads */ - wait_queue_head_t read_queue; /**< Processes waiting on block_read */ - atomic_t block_write; /**< Queue blocked for writes */ - wait_queue_head_t write_queue; /**< Processes waiting on block_write */ - atomic_t total_queued; /**< Total queued statistic */ - atomic_t total_flushed; /**< Total flushes statistic */ - atomic_t total_locks; /**< Total locks statistics */ - enum drm_ctx_flags flags; /**< Context preserving and 2D-only */ - struct drm_waitlist waitlist; /**< Pending buffers */ - wait_queue_head_t flush_queue; /**< Processes waiting until flush */ -}; - /** * Lock data. */ -- cgit v1.2.3 From 7e6edb9b2e0bcfb2a588db390c44d120213c57ae Mon Sep 17 00:00:00 2001 From: Matan Barak Date: Thu, 31 Jul 2014 11:01:28 +0300 Subject: IB/core: Add user MR re-registration support Memory re-registration is a feature that enables changing the attributes of a memory region registered by user-space, including PD, translation (address and length) and access flags. Add the required support in uverbs and the kernel verbs API. Signed-off-by: Matan Barak Signed-off-by: Or Gerlitz Signed-off-by: Roland Dreier --- drivers/infiniband/core/uverbs.h | 1 + drivers/infiniband/core/uverbs_cmd.c | 93 +++++++++++++++++++++++++++++++++++ drivers/infiniband/core/uverbs_main.c | 1 + include/rdma/ib_verbs.h | 10 +++- include/uapi/rdma/ib_user_verbs.h | 16 ++++++ 5 files changed, 120 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/infiniband/core/uverbs.h b/drivers/infiniband/core/uverbs.h index a283274a5a09..643c08a025a5 100644 --- a/drivers/infiniband/core/uverbs.h +++ b/drivers/infiniband/core/uverbs.h @@ -221,6 +221,7 @@ IB_UVERBS_DECLARE_CMD(query_port); IB_UVERBS_DECLARE_CMD(alloc_pd); IB_UVERBS_DECLARE_CMD(dealloc_pd); IB_UVERBS_DECLARE_CMD(reg_mr); +IB_UVERBS_DECLARE_CMD(rereg_mr); IB_UVERBS_DECLARE_CMD(dereg_mr); IB_UVERBS_DECLARE_CMD(alloc_mw); IB_UVERBS_DECLARE_CMD(dealloc_mw); diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index ea6203ee7bcc..0600c50e6215 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -1002,6 +1002,99 @@ err_free: return ret; } +ssize_t ib_uverbs_rereg_mr(struct ib_uverbs_file *file, + const char __user *buf, int in_len, + int out_len) +{ + struct ib_uverbs_rereg_mr cmd; + struct ib_uverbs_rereg_mr_resp resp; + struct ib_udata udata; + struct ib_pd *pd = NULL; + struct ib_mr *mr; + struct ib_pd *old_pd; + int ret; + struct ib_uobject *uobj; + + if (out_len < sizeof(resp)) + return -ENOSPC; + + if (copy_from_user(&cmd, buf, sizeof(cmd))) + return -EFAULT; + + INIT_UDATA(&udata, buf + sizeof(cmd), + (unsigned long) cmd.response + sizeof(resp), + in_len - sizeof(cmd), out_len - sizeof(resp)); + + if (cmd.flags & ~IB_MR_REREG_SUPPORTED || !cmd.flags) + return -EINVAL; + + if ((cmd.flags & IB_MR_REREG_TRANS) && + (!cmd.start || !cmd.hca_va || 0 >= cmd.length || + (cmd.start & ~PAGE_MASK) != (cmd.hca_va & ~PAGE_MASK))) + return -EINVAL; + + uobj = idr_write_uobj(&ib_uverbs_mr_idr, cmd.mr_handle, + file->ucontext); + + if (!uobj) + return -EINVAL; + + mr = uobj->object; + + if (cmd.flags & IB_MR_REREG_ACCESS) { + ret = ib_check_mr_access(cmd.access_flags); + if (ret) + goto put_uobjs; + } + + if (cmd.flags & IB_MR_REREG_PD) { + pd = idr_read_pd(cmd.pd_handle, file->ucontext); + if (!pd) { + ret = -EINVAL; + goto put_uobjs; + } + } + + if (atomic_read(&mr->usecnt)) { + ret = -EBUSY; + goto put_uobj_pd; + } + + old_pd = mr->pd; + ret = mr->device->rereg_user_mr(mr, cmd.flags, cmd.start, + cmd.length, cmd.hca_va, + cmd.access_flags, pd, &udata); + if (!ret) { + if (cmd.flags & IB_MR_REREG_PD) { + atomic_inc(&pd->usecnt); + mr->pd = pd; + atomic_dec(&old_pd->usecnt); + } + } else { + goto put_uobj_pd; + } + + memset(&resp, 0, sizeof(resp)); + resp.lkey = mr->lkey; + resp.rkey = mr->rkey; + + if (copy_to_user((void __user *)(unsigned long)cmd.response, + &resp, sizeof(resp))) + ret = -EFAULT; + else + ret = in_len; + +put_uobj_pd: + if (cmd.flags & IB_MR_REREG_PD) + put_pd_read(pd); + +put_uobjs: + + put_uobj_write(mr->uobject); + + return ret; +} + ssize_t ib_uverbs_dereg_mr(struct ib_uverbs_file *file, const char __user *buf, int in_len, int out_len) diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index 08219fb3338b..c73b22a257fe 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -87,6 +87,7 @@ static ssize_t (*uverbs_cmd_table[])(struct ib_uverbs_file *file, [IB_USER_VERBS_CMD_ALLOC_PD] = ib_uverbs_alloc_pd, [IB_USER_VERBS_CMD_DEALLOC_PD] = ib_uverbs_dealloc_pd, [IB_USER_VERBS_CMD_REG_MR] = ib_uverbs_reg_mr, + [IB_USER_VERBS_CMD_REREG_MR] = ib_uverbs_rereg_mr, [IB_USER_VERBS_CMD_DEREG_MR] = ib_uverbs_dereg_mr, [IB_USER_VERBS_CMD_ALLOC_MW] = ib_uverbs_alloc_mw, [IB_USER_VERBS_CMD_DEALLOC_MW] = ib_uverbs_dealloc_mw, diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 7ccef342f724..ed44cc07a7b3 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -1097,7 +1097,8 @@ struct ib_mr_attr { enum ib_mr_rereg_flags { IB_MR_REREG_TRANS = 1, IB_MR_REREG_PD = (1<<1), - IB_MR_REREG_ACCESS = (1<<2) + IB_MR_REREG_ACCESS = (1<<2), + IB_MR_REREG_SUPPORTED = ((IB_MR_REREG_ACCESS << 1) - 1) }; /** @@ -1547,6 +1548,13 @@ struct ib_device { u64 virt_addr, int mr_access_flags, struct ib_udata *udata); + int (*rereg_user_mr)(struct ib_mr *mr, + int flags, + u64 start, u64 length, + u64 virt_addr, + int mr_access_flags, + struct ib_pd *pd, + struct ib_udata *udata); int (*query_mr)(struct ib_mr *mr, struct ib_mr_attr *mr_attr); int (*dereg_mr)(struct ib_mr *mr); diff --git a/include/uapi/rdma/ib_user_verbs.h b/include/uapi/rdma/ib_user_verbs.h index cbfdd4ca9510..26daf55ff76e 100644 --- a/include/uapi/rdma/ib_user_verbs.h +++ b/include/uapi/rdma/ib_user_verbs.h @@ -276,6 +276,22 @@ struct ib_uverbs_reg_mr_resp { __u32 rkey; }; +struct ib_uverbs_rereg_mr { + __u64 response; + __u32 mr_handle; + __u32 flags; + __u64 start; + __u64 length; + __u64 hca_va; + __u32 pd_handle; + __u32 access_flags; +}; + +struct ib_uverbs_rereg_mr_resp { + __u32 lkey; + __u32 rkey; +}; + struct ib_uverbs_dereg_mr { __u32 mr_handle; }; -- cgit v1.2.3 From e630664c8383f300c4146d7613d61e5a8eb1f8e3 Mon Sep 17 00:00:00 2001 From: Matan Barak Date: Thu, 31 Jul 2014 11:01:29 +0300 Subject: mlx4_core: Add helper functions to support MR re-registration Add few helper functions to support a mechanism of getting an MPT, modifying it and updating the HCA with the modified object. The code takes 2 paths, one for directly changing the MPT (and sometimes its related MTTs) and another one which queries the MPT and updates the HCA via fw command SW2HW_MPT. The first path is used in native mode; the second path is slower and is used only in SRIOV. Signed-off-by: Jack Morgenstein Signed-off-by: Matan Barak Signed-off-by: Or Gerlitz Signed-off-by: Roland Dreier --- drivers/net/ethernet/mellanox/mlx4/mlx4.h | 2 + drivers/net/ethernet/mellanox/mlx4/mr.c | 160 +++++++++++++++++++++ .../net/ethernet/mellanox/mlx4/resource_tracker.c | 26 +++- include/linux/mlx4/device.h | 16 +++ 4 files changed, 202 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index 1d8af7336807..b40d587974fa 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -279,6 +279,8 @@ struct mlx4_icm_table { #define MLX4_MPT_FLAG_PHYSICAL (1 << 9) #define MLX4_MPT_FLAG_REGION (1 << 8) +#define MLX4_MPT_PD_MASK (0x1FFFFUL) +#define MLX4_MPT_PD_VF_MASK (0xFE0000UL) #define MLX4_MPT_PD_FLAG_FAST_REG (1 << 27) #define MLX4_MPT_PD_FLAG_RAE (1 << 28) #define MLX4_MPT_PD_FLAG_EN_INV (3 << 24) diff --git a/drivers/net/ethernet/mellanox/mlx4/mr.c b/drivers/net/ethernet/mellanox/mlx4/mr.c index 2839abb878a6..7d717eccb7b0 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mr.c +++ b/drivers/net/ethernet/mellanox/mlx4/mr.c @@ -298,6 +298,131 @@ static int mlx4_HW2SW_MPT(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED); } +int mlx4_mr_hw_get_mpt(struct mlx4_dev *dev, struct mlx4_mr *mmr, + struct mlx4_mpt_entry ***mpt_entry) +{ + int err; + int key = key_to_hw_index(mmr->key) & (dev->caps.num_mpts - 1); + struct mlx4_cmd_mailbox *mailbox = NULL; + + /* Make sure that at this point we have single-threaded access only */ + + if (mmr->enabled != MLX4_MPT_EN_HW) + return -EINVAL; + + err = mlx4_HW2SW_MPT(dev, NULL, key); + + if (err) { + mlx4_warn(dev, "HW2SW_MPT failed (%d).", err); + mlx4_warn(dev, "Most likely the MR has MWs bound to it.\n"); + return err; + } + + mmr->enabled = MLX4_MPT_EN_SW; + + if (!mlx4_is_mfunc(dev)) { + **mpt_entry = mlx4_table_find( + &mlx4_priv(dev)->mr_table.dmpt_table, + key, NULL); + } else { + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR_OR_NULL(mailbox)) + return PTR_ERR(mailbox); + + err = mlx4_cmd_box(dev, 0, mailbox->dma, key, + 0, MLX4_CMD_QUERY_MPT, + MLX4_CMD_TIME_CLASS_B, + MLX4_CMD_WRAPPED); + + if (err) + goto free_mailbox; + + *mpt_entry = (struct mlx4_mpt_entry **)&mailbox->buf; + } + + if (!(*mpt_entry) || !(**mpt_entry)) { + err = -ENOMEM; + goto free_mailbox; + } + + return 0; + +free_mailbox: + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} +EXPORT_SYMBOL_GPL(mlx4_mr_hw_get_mpt); + +int mlx4_mr_hw_write_mpt(struct mlx4_dev *dev, struct mlx4_mr *mmr, + struct mlx4_mpt_entry **mpt_entry) +{ + int err; + + if (!mlx4_is_mfunc(dev)) { + /* Make sure any changes to this entry are flushed */ + wmb(); + + *(u8 *)(*mpt_entry) = MLX4_MPT_STATUS_HW; + + /* Make sure the new status is written */ + wmb(); + + err = mlx4_SYNC_TPT(dev); + } else { + int key = key_to_hw_index(mmr->key) & (dev->caps.num_mpts - 1); + + struct mlx4_cmd_mailbox *mailbox = + container_of((void *)mpt_entry, struct mlx4_cmd_mailbox, + buf); + + err = mlx4_SW2HW_MPT(dev, mailbox, key); + } + + mmr->pd = be32_to_cpu((*mpt_entry)->pd_flags) & MLX4_MPT_PD_MASK; + if (!err) + mmr->enabled = MLX4_MPT_EN_HW; + return err; +} +EXPORT_SYMBOL_GPL(mlx4_mr_hw_write_mpt); + +void mlx4_mr_hw_put_mpt(struct mlx4_dev *dev, + struct mlx4_mpt_entry **mpt_entry) +{ + if (mlx4_is_mfunc(dev)) { + struct mlx4_cmd_mailbox *mailbox = + container_of((void *)mpt_entry, struct mlx4_cmd_mailbox, + buf); + mlx4_free_cmd_mailbox(dev, mailbox); + } +} +EXPORT_SYMBOL_GPL(mlx4_mr_hw_put_mpt); + +int mlx4_mr_hw_change_pd(struct mlx4_dev *dev, struct mlx4_mpt_entry *mpt_entry, + u32 pdn) +{ + u32 pd_flags = be32_to_cpu(mpt_entry->pd_flags); + /* The wrapper function will put the slave's id here */ + if (mlx4_is_mfunc(dev)) + pd_flags &= ~MLX4_MPT_PD_VF_MASK; + mpt_entry->pd_flags = cpu_to_be32((pd_flags & ~MLX4_MPT_PD_MASK) | + (pdn & MLX4_MPT_PD_MASK) + | MLX4_MPT_PD_FLAG_EN_INV); + return 0; +} +EXPORT_SYMBOL_GPL(mlx4_mr_hw_change_pd); + +int mlx4_mr_hw_change_access(struct mlx4_dev *dev, + struct mlx4_mpt_entry *mpt_entry, + u32 access) +{ + u32 flags = (be32_to_cpu(mpt_entry->flags) & ~MLX4_PERM_MASK) | + (access & MLX4_PERM_MASK); + + mpt_entry->flags = cpu_to_be32(flags); + return 0; +} +EXPORT_SYMBOL_GPL(mlx4_mr_hw_change_access); + static int mlx4_mr_alloc_reserved(struct mlx4_dev *dev, u32 mridx, u32 pd, u64 iova, u64 size, u32 access, int npages, int page_shift, struct mlx4_mr *mr) @@ -463,6 +588,41 @@ int mlx4_mr_free(struct mlx4_dev *dev, struct mlx4_mr *mr) } EXPORT_SYMBOL_GPL(mlx4_mr_free); +void mlx4_mr_rereg_mem_cleanup(struct mlx4_dev *dev, struct mlx4_mr *mr) +{ + mlx4_mtt_cleanup(dev, &mr->mtt); +} +EXPORT_SYMBOL_GPL(mlx4_mr_rereg_mem_cleanup); + +int mlx4_mr_rereg_mem_write(struct mlx4_dev *dev, struct mlx4_mr *mr, + u64 iova, u64 size, int npages, + int page_shift, struct mlx4_mpt_entry *mpt_entry) +{ + int err; + + mpt_entry->start = cpu_to_be64(mr->iova); + mpt_entry->length = cpu_to_be64(mr->size); + mpt_entry->entity_size = cpu_to_be32(mr->mtt.page_shift); + + err = mlx4_mtt_init(dev, npages, page_shift, &mr->mtt); + if (err) + return err; + + if (mr->mtt.order < 0) { + mpt_entry->flags |= cpu_to_be32(MLX4_MPT_FLAG_PHYSICAL); + mpt_entry->mtt_addr = 0; + } else { + mpt_entry->mtt_addr = cpu_to_be64(mlx4_mtt_addr(dev, + &mr->mtt)); + if (mr->mtt.page_shift == 0) + mpt_entry->mtt_sz = cpu_to_be32(1 << mr->mtt.order); + } + mr->enabled = MLX4_MPT_EN_SW; + + return 0; +} +EXPORT_SYMBOL_GPL(mlx4_mr_rereg_mem_write); + int mlx4_mr_enable(struct mlx4_dev *dev, struct mlx4_mr *mr) { struct mlx4_cmd_mailbox *mailbox; diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index 0efc1368e5a8..1089367fed22 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -2613,12 +2613,34 @@ int mlx4_QUERY_MPT_wrapper(struct mlx4_dev *dev, int slave, if (err) return err; - if (mpt->com.from_state != RES_MPT_HW) { + if (mpt->com.from_state == RES_MPT_MAPPED) { + /* In order to allow rereg in SRIOV, we need to alter the MPT entry. To do + * that, the VF must read the MPT. But since the MPT entry memory is not + * in the VF's virtual memory space, it must use QUERY_MPT to obtain the + * entry contents. To guarantee that the MPT cannot be changed, the driver + * must perform HW2SW_MPT before this query and return the MPT entry to HW + * ownership fofollowing the change. The change here allows the VF to + * perform QUERY_MPT also when the entry is in SW ownership. + */ + struct mlx4_mpt_entry *mpt_entry = mlx4_table_find( + &mlx4_priv(dev)->mr_table.dmpt_table, + mpt->key, NULL); + + if (NULL == mpt_entry || NULL == outbox->buf) { + err = -EINVAL; + goto out; + } + + memcpy(outbox->buf, mpt_entry, sizeof(*mpt_entry)); + + err = 0; + } else if (mpt->com.from_state == RES_MPT_HW) { + err = mlx4_DMA_wrapper(dev, slave, vhcr, inbox, outbox, cmd); + } else { err = -EBUSY; goto out; } - err = mlx4_DMA_wrapper(dev, slave, vhcr, inbox, outbox, cmd); out: put_res(dev, slave, id, RES_MPT); diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 35b51e7af886..bac002167ace 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -262,6 +262,7 @@ enum { MLX4_PERM_REMOTE_WRITE = 1 << 13, MLX4_PERM_ATOMIC = 1 << 14, MLX4_PERM_BIND_MW = 1 << 15, + MLX4_PERM_MASK = 0xFC00 }; enum { @@ -1243,4 +1244,19 @@ int mlx4_vf_smi_enabled(struct mlx4_dev *dev, int slave, int port); int mlx4_vf_get_enable_smi_admin(struct mlx4_dev *dev, int slave, int port); int mlx4_vf_set_enable_smi_admin(struct mlx4_dev *dev, int slave, int port, int enable); +int mlx4_mr_hw_get_mpt(struct mlx4_dev *dev, struct mlx4_mr *mmr, + struct mlx4_mpt_entry ***mpt_entry); +int mlx4_mr_hw_write_mpt(struct mlx4_dev *dev, struct mlx4_mr *mmr, + struct mlx4_mpt_entry **mpt_entry); +int mlx4_mr_hw_change_pd(struct mlx4_dev *dev, struct mlx4_mpt_entry *mpt_entry, + u32 pdn); +int mlx4_mr_hw_change_access(struct mlx4_dev *dev, + struct mlx4_mpt_entry *mpt_entry, + u32 access); +void mlx4_mr_hw_put_mpt(struct mlx4_dev *dev, + struct mlx4_mpt_entry **mpt_entry); +void mlx4_mr_rereg_mem_cleanup(struct mlx4_dev *dev, struct mlx4_mr *mr); +int mlx4_mr_rereg_mem_write(struct mlx4_dev *dev, struct mlx4_mr *mr, + u64 iova, u64 size, int npages, + int page_shift, struct mlx4_mpt_entry *mpt_entry); #endif /* MLX4_DEVICE_H */ -- cgit v1.2.3 From cd83ce9e6195aa3ea15ab4db92892802c20df5d0 Mon Sep 17 00:00:00 2001 From: James P Michels III Date: Sun, 27 Jul 2014 13:28:04 -0400 Subject: usb-core bInterval quirk This patch adds a usb quirk to support devices with interupt endpoints and bInterval values expressed as microframes. The quirk causes the parse endpoint function to modify the reported bInterval to a standards conforming value. There is currently code in the endpoint parser that checks for bIntervals that are outside of the valid range (1-16 for USB 2+ high speed and super speed interupt endpoints). In this case, the code assumes the bInterval is being reported in 1ms frames. As well, the correction is only applied if the original bInterval value is out of the 1-16 range. With this quirk applied to the device, the bInterval will be accurately adjusted from microframes to an exponent. Signed-off-by: James P Michels III Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/config.c | 11 +++++++++++ drivers/usb/core/quirks.c | 4 ++++ include/linux/usb/quirks.h | 11 +++++++++++ 3 files changed, 26 insertions(+) (limited to 'include') diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 1ab4df1de2da..b2a540b43f97 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -199,6 +199,17 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, if (n == 0) n = 9; /* 32 ms = 2^(9-1) uframes */ j = 16; + + /* + * Adjust bInterval for quirked devices. + * This quirk fixes bIntervals reported in + * linear microframes. + */ + if (to_usb_device(ddev)->quirks & + USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL) { + n = clamp(fls(d->bInterval), i, j); + i = j = n; + } break; default: /* USB_SPEED_FULL or _LOW */ /* For low-speed, 10 ms is the official minimum. diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 2c9ba4077075..bae636e2a1a3 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -145,6 +145,10 @@ static const struct usb_device_id usb_quirk_list[] = { /* SKYMEDI USB_DRIVE */ { USB_DEVICE(0x1516, 0x8628), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Razer - Razer Blade Keyboard */ + { USB_DEVICE(0x1532, 0x0116), .driver_info = + USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL }, + /* BUILDWIN Photo Frame */ { USB_DEVICE(0x1908, 0x1315), .driver_info = USB_QUIRK_HONOR_BNUMINTERFACES }, diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h index 52f944dfe2fd..55a17b188daa 100644 --- a/include/linux/usb/quirks.h +++ b/include/linux/usb/quirks.h @@ -30,4 +30,15 @@ descriptor */ #define USB_QUIRK_DELAY_INIT 0x00000040 +/* + * For high speed and super speed interupt endpoints, the USB 2.0 and + * USB 3.0 spec require the interval in microframes + * (1 microframe = 125 microseconds) to be calculated as + * interval = 2 ^ (bInterval-1). + * + * Devices with this quirk report their bInterval as the result of this + * calculation instead of the exponent variable used in the calculation. + */ +#define USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL 0x00000080 + #endif /* __LINUX_USB_QUIRKS_H */ -- cgit v1.2.3 From 70407fad85f2ec87a0cf56057c3267cd3aa22768 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 31 Jul 2014 21:11:22 +0800 Subject: f2fs: add tracepoint for f2fs_direct_IO This patch adds a tracepoint for f2fs_direct_IO. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 5 ++++ include/trace/events/f2fs.h | 63 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) (limited to 'include') diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index ec3c8860539e..03313099c51c 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1086,9 +1086,14 @@ static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb, /* clear fsync mark to recover these blocks */ fsync_mark_clear(F2FS_SB(inode->i_sb), inode->i_ino); + trace_f2fs_direct_IO_enter(inode, offset, count, rw); + err = blockdev_direct_IO(rw, iocb, inode, iter, offset, get_data_block); if (err < 0 && (rw & WRITE)) f2fs_write_failed(mapping, offset + count); + + trace_f2fs_direct_IO_exit(inode, offset, count, rw, err); + return err; } diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 7d2e70e75f16..d06d44363fea 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -587,6 +587,69 @@ TRACE_EVENT(f2fs_fallocate, __entry->ret) ); +TRACE_EVENT(f2fs_direct_IO_enter, + + TP_PROTO(struct inode *inode, loff_t offset, unsigned long len, int rw), + + TP_ARGS(inode, offset, len, rw), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(loff_t, pos) + __field(unsigned long, len) + __field(int, rw) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->pos = offset; + __entry->len = len; + __entry->rw = rw; + ), + + TP_printk("dev = (%d,%d), ino = %lu pos = %lld len = %lu rw = %d", + show_dev_ino(__entry), + __entry->pos, + __entry->len, + __entry->rw) +); + +TRACE_EVENT(f2fs_direct_IO_exit, + + TP_PROTO(struct inode *inode, loff_t offset, unsigned long len, + int rw, int ret), + + TP_ARGS(inode, offset, len, rw, ret), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(loff_t, pos) + __field(unsigned long, len) + __field(int, rw) + __field(int, ret) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->pos = offset; + __entry->len = len; + __entry->rw = rw; + __entry->ret = ret; + ), + + TP_printk("dev = (%d,%d), ino = %lu pos = %lld len = %lu " + "rw = %d ret = %d", + show_dev_ino(__entry), + __entry->pos, + __entry->len, + __entry->rw, + __entry->ret) +); + TRACE_EVENT(f2fs_reserve_new_block, TP_PROTO(struct inode *inode, nid_t nid, unsigned int ofs_in_node), -- cgit v1.2.3 From 278571baca2aecf5fb5cb5c8b002dbfa0a6c524c Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Wed, 30 Jul 2014 20:34:12 -0700 Subject: net: filter: simplify socket charging attaching bpf program to a socket involves multiple socket memory arithmetic, since size of 'sk_filter' is changing when classic BPF is converted to eBPF. Also common path of program creation has to deal with two ways of freeing the memory. Simplify the code by delaying socket charging until program is ready and its size is known Signed-off-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/filter.h | 2 +- net/core/filter.c | 87 +++++++++++++++++++++----------------------------- net/core/sock.c | 9 ++++-- 3 files changed, 45 insertions(+), 53 deletions(-) (limited to 'include') diff --git a/include/linux/filter.h b/include/linux/filter.h index 20dd50ef7271..00640edc166f 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -366,7 +366,7 @@ int sk_chk_filter(const struct sock_filter *filter, unsigned int flen); int sk_get_filter(struct sock *sk, struct sock_filter __user *filter, unsigned int len); -void sk_filter_charge(struct sock *sk, struct sk_filter *fp); +bool sk_filter_charge(struct sock *sk, struct sk_filter *fp); void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp); u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5); diff --git a/net/core/filter.c b/net/core/filter.c index 42c1944b0c63..5a6aeb1d40b8 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -872,41 +872,30 @@ static void sk_filter_release(struct sk_filter *fp) void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp) { - atomic_sub(sk_filter_size(fp->len), &sk->sk_omem_alloc); - sk_filter_release(fp); -} + u32 filter_size = sk_filter_size(fp->len); -void sk_filter_charge(struct sock *sk, struct sk_filter *fp) -{ - atomic_inc(&fp->refcnt); - atomic_add(sk_filter_size(fp->len), &sk->sk_omem_alloc); + atomic_sub(filter_size, &sk->sk_omem_alloc); + sk_filter_release(fp); } -static struct sk_filter *__sk_migrate_realloc(struct sk_filter *fp, - struct sock *sk, - unsigned int len) +/* try to charge the socket memory if there is space available + * return true on success + */ +bool sk_filter_charge(struct sock *sk, struct sk_filter *fp) { - struct sk_filter *fp_new; - - if (sk == NULL) - return krealloc(fp, len, GFP_KERNEL); - - fp_new = sock_kmalloc(sk, len, GFP_KERNEL); - if (fp_new) { - *fp_new = *fp; - /* As we're keeping orig_prog in fp_new along, - * we need to make sure we're not evicting it - * from the old fp. - */ - fp->orig_prog = NULL; - sk_filter_uncharge(sk, fp); + u32 filter_size = sk_filter_size(fp->len); + + /* same check as in sock_kmalloc() */ + if (filter_size <= sysctl_optmem_max && + atomic_read(&sk->sk_omem_alloc) + filter_size < sysctl_optmem_max) { + atomic_inc(&fp->refcnt); + atomic_add(filter_size, &sk->sk_omem_alloc); + return true; } - - return fp_new; + return false; } -static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp, - struct sock *sk) +static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp) { struct sock_filter *old_prog; struct sk_filter *old_fp; @@ -938,7 +927,7 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp, /* Expand fp for appending the new filter representation. */ old_fp = fp; - fp = __sk_migrate_realloc(old_fp, sk, sk_filter_size(new_len)); + fp = krealloc(old_fp, sk_filter_size(new_len), GFP_KERNEL); if (!fp) { /* The old_fp is still around in case we couldn't * allocate new memory, so uncharge on that one. @@ -956,7 +945,7 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp, /* 2nd sk_convert_filter() can fail only if it fails * to allocate memory, remapping must succeed. Note, * that at this time old_fp has already been released - * by __sk_migrate_realloc(). + * by krealloc(). */ goto out_err_free; @@ -968,16 +957,11 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp, out_err_free: kfree(old_prog); out_err: - /* Rollback filter setup. */ - if (sk != NULL) - sk_filter_uncharge(sk, fp); - else - kfree(fp); + __sk_filter_release(fp); return ERR_PTR(err); } -static struct sk_filter *__sk_prepare_filter(struct sk_filter *fp, - struct sock *sk) +static struct sk_filter *__sk_prepare_filter(struct sk_filter *fp) { int err; @@ -986,10 +970,7 @@ static struct sk_filter *__sk_prepare_filter(struct sk_filter *fp, err = sk_chk_filter(fp->insns, fp->len); if (err) { - if (sk != NULL) - sk_filter_uncharge(sk, fp); - else - kfree(fp); + __sk_filter_release(fp); return ERR_PTR(err); } @@ -1002,7 +983,7 @@ static struct sk_filter *__sk_prepare_filter(struct sk_filter *fp, * internal BPF translation for the optimized interpreter. */ if (!fp->jited) - fp = __sk_migrate_filter(fp, sk); + fp = __sk_migrate_filter(fp); return fp; } @@ -1041,10 +1022,10 @@ int sk_unattached_filter_create(struct sk_filter **pfp, */ fp->orig_prog = NULL; - /* __sk_prepare_filter() already takes care of uncharging + /* __sk_prepare_filter() already takes care of freeing * memory in case something goes wrong. */ - fp = __sk_prepare_filter(fp, NULL); + fp = __sk_prepare_filter(fp); if (IS_ERR(fp)) return PTR_ERR(fp); @@ -1083,31 +1064,37 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk) if (fprog->filter == NULL) return -EINVAL; - fp = sock_kmalloc(sk, sk_fsize, GFP_KERNEL); + fp = kmalloc(sk_fsize, GFP_KERNEL); if (!fp) return -ENOMEM; if (copy_from_user(fp->insns, fprog->filter, fsize)) { - sock_kfree_s(sk, fp, sk_fsize); + kfree(fp); return -EFAULT; } - atomic_set(&fp->refcnt, 1); fp->len = fprog->len; err = sk_store_orig_filter(fp, fprog); if (err) { - sk_filter_uncharge(sk, fp); + kfree(fp); return -ENOMEM; } - /* __sk_prepare_filter() already takes care of uncharging + /* __sk_prepare_filter() already takes care of freeing * memory in case something goes wrong. */ - fp = __sk_prepare_filter(fp, sk); + fp = __sk_prepare_filter(fp); if (IS_ERR(fp)) return PTR_ERR(fp); + atomic_set(&fp->refcnt, 0); + + if (!sk_filter_charge(sk, fp)) { + __sk_filter_release(fp); + return -ENOMEM; + } + old_fp = rcu_dereference_protected(sk->sk_filter, sock_owned_by_user(sk)); rcu_assign_pointer(sk->sk_filter, fp); diff --git a/net/core/sock.c b/net/core/sock.c index 134291d73fcd..a741163568fa 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1474,6 +1474,7 @@ static void sk_update_clone(const struct sock *sk, struct sock *newsk) struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority) { struct sock *newsk; + bool is_charged = true; newsk = sk_prot_alloc(sk->sk_prot, priority, sk->sk_family); if (newsk != NULL) { @@ -1518,9 +1519,13 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority) filter = rcu_dereference_protected(newsk->sk_filter, 1); if (filter != NULL) - sk_filter_charge(newsk, filter); + /* though it's an empty new sock, the charging may fail + * if sysctl_optmem_max was changed between creation of + * original socket and cloning + */ + is_charged = sk_filter_charge(newsk, filter); - if (unlikely(xfrm_sk_clone_policy(newsk))) { + if (unlikely(!is_charged || xfrm_sk_clone_policy(newsk))) { /* It is still raw copy of parent, so invalidate * destructor and make plain sk_free() */ newsk->sk_destruct = NULL; -- cgit v1.2.3 From 009937e78a45553a86d26654f192b2fd9ebe289d Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Wed, 30 Jul 2014 20:34:13 -0700 Subject: net: filter: rename sk_filter_proglen -> bpf_classic_proglen trivial rename to better match semantics of macro Signed-off-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/filter.h | 3 +-- net/core/filter.c | 8 ++++---- net/core/sock_diag.c | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/filter.h b/include/linux/filter.h index 00640edc166f..3769341a745d 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -344,8 +344,7 @@ static inline unsigned int sk_filter_size(unsigned int proglen) offsetof(struct sk_filter, insns[proglen])); } -#define sk_filter_proglen(fprog) \ - (fprog->len * sizeof(fprog->filter[0])) +#define bpf_classic_proglen(fprog) (fprog->len * sizeof(fprog->filter[0])) int sk_filter(struct sock *sk, struct sk_buff *skb); diff --git a/net/core/filter.c b/net/core/filter.c index 5a6aeb1d40b8..d6cb287e4f59 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -813,7 +813,7 @@ EXPORT_SYMBOL(sk_chk_filter); static int sk_store_orig_filter(struct sk_filter *fp, const struct sock_fprog *fprog) { - unsigned int fsize = sk_filter_proglen(fprog); + unsigned int fsize = bpf_classic_proglen(fprog); struct sock_fprog_kern *fkprog; fp->orig_prog = kmalloc(sizeof(*fkprog), GFP_KERNEL); @@ -1001,7 +1001,7 @@ static struct sk_filter *__sk_prepare_filter(struct sk_filter *fp) int sk_unattached_filter_create(struct sk_filter **pfp, struct sock_fprog_kern *fprog) { - unsigned int fsize = sk_filter_proglen(fprog); + unsigned int fsize = bpf_classic_proglen(fprog); struct sk_filter *fp; /* Make sure new filter is there and in the right amounts. */ @@ -1053,7 +1053,7 @@ EXPORT_SYMBOL_GPL(sk_unattached_filter_destroy); int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk) { struct sk_filter *fp, *old_fp; - unsigned int fsize = sk_filter_proglen(fprog); + unsigned int fsize = bpf_classic_proglen(fprog); unsigned int sk_fsize = sk_filter_size(fprog->len); int err; @@ -1154,7 +1154,7 @@ int sk_get_filter(struct sock *sk, struct sock_filter __user *ubuf, goto out; ret = -EFAULT; - if (copy_to_user(ubuf, fprog->filter, sk_filter_proglen(fprog))) + if (copy_to_user(ubuf, fprog->filter, bpf_classic_proglen(fprog))) goto out; /* Instead of bytes, the API requests to return the number diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c index a4216a4c9572..57d922320c59 100644 --- a/net/core/sock_diag.c +++ b/net/core/sock_diag.c @@ -69,7 +69,7 @@ int sock_diag_put_filterinfo(bool may_report_filterinfo, struct sock *sk, goto out; fprog = filter->orig_prog; - flen = sk_filter_proglen(fprog); + flen = bpf_classic_proglen(fprog); attr = nla_reserve(skb, attrtype, flen); if (attr == NULL) { -- cgit v1.2.3 From 4df95ff488eb796aab9566652c250330179def17 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Wed, 30 Jul 2014 20:34:14 -0700 Subject: net: filter: rename sk_chk_filter() -> bpf_check_classic() trivial rename to indicate that this functions performs classic BPF checking Signed-off-by: Alexei Starovoitov Signed-off-by: David S. Miller --- Documentation/networking/filter.txt | 2 +- include/linux/filter.h | 2 +- kernel/bpf/core.c | 2 +- kernel/seccomp.c | 4 ++-- net/core/filter.c | 10 +++++----- 5 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/Documentation/networking/filter.txt b/Documentation/networking/filter.txt index ee78eba78a9d..712068be8171 100644 --- a/Documentation/networking/filter.txt +++ b/Documentation/networking/filter.txt @@ -591,7 +591,7 @@ sk_unattached_filter_destroy() for destroying it. The macro SK_RUN_FILTER(filter, ctx) transparently invokes eBPF interpreter or JITed code to run the filter. 'filter' is a pointer to struct sk_filter that we got from sk_unattached_filter_create(), and 'ctx' the given context (e.g. -skb pointer). All constraints and restrictions from sk_chk_filter() apply +skb pointer). All constraints and restrictions from bpf_check_classic() apply before a conversion to the new layout is being done behind the scenes! Currently, the classic BPF format is being used for JITing on most of the diff --git a/include/linux/filter.h b/include/linux/filter.h index 3769341a745d..c4d0be4c5e75 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -361,7 +361,7 @@ void sk_unattached_filter_destroy(struct sk_filter *fp); int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk); int sk_detach_filter(struct sock *sk); -int sk_chk_filter(const struct sock_filter *filter, unsigned int flen); +int bpf_check_classic(const struct sock_filter *filter, unsigned int flen); int sk_get_filter(struct sock *sk, struct sock_filter __user *filter, unsigned int len); diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 265a02cc822d..b479807ec383 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -18,7 +18,7 @@ * 2 of the License, or (at your option) any later version. * * Andi Kleen - Fix a few bad bugs and races. - * Kris Katterjohn - Added many additional checks in sk_chk_filter() + * Kris Katterjohn - Added many additional checks in bpf_check_classic() */ #include #include diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 565743db5384..f4a77d23f209 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -87,7 +87,7 @@ static void populate_seccomp_data(struct seccomp_data *sd) * @filter: filter to verify * @flen: length of filter * - * Takes a previously checked filter (by sk_chk_filter) and + * Takes a previously checked filter (by bpf_check_classic) and * redirects all filter code that loads struct sk_buff data * and related data through seccomp_bpf_load. It also * enforces length and alignment checking of those loads. @@ -239,7 +239,7 @@ static long seccomp_attach_filter(struct sock_fprog *fprog) goto free_prog; /* Check and rewrite the fprog via the skb checker */ - ret = sk_chk_filter(fp, fprog->len); + ret = bpf_check_classic(fp, fprog->len); if (ret) goto free_prog; diff --git a/net/core/filter.c b/net/core/filter.c index d6cb287e4f59..5740ea08a3ad 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -18,7 +18,7 @@ * 2 of the License, or (at your option) any later version. * * Andi Kleen - Fix a few bad bugs and races. - * Kris Katterjohn - Added many additional checks in sk_chk_filter() + * Kris Katterjohn - Added many additional checks in bpf_check_classic() */ #include @@ -721,7 +721,7 @@ static bool chk_code_allowed(u16 code_to_probe) } /** - * sk_chk_filter - verify socket filter code + * bpf_check_classic - verify socket filter code * @filter: filter to verify * @flen: length of filter * @@ -734,7 +734,7 @@ static bool chk_code_allowed(u16 code_to_probe) * * Returns 0 if the rule set is legal or -EINVAL if not. */ -int sk_chk_filter(const struct sock_filter *filter, unsigned int flen) +int bpf_check_classic(const struct sock_filter *filter, unsigned int flen) { bool anc_found; int pc; @@ -808,7 +808,7 @@ int sk_chk_filter(const struct sock_filter *filter, unsigned int flen) return -EINVAL; } -EXPORT_SYMBOL(sk_chk_filter); +EXPORT_SYMBOL(bpf_check_classic); static int sk_store_orig_filter(struct sk_filter *fp, const struct sock_fprog *fprog) @@ -968,7 +968,7 @@ static struct sk_filter *__sk_prepare_filter(struct sk_filter *fp) fp->bpf_func = NULL; fp->jited = 0; - err = sk_chk_filter(fp->insns, fp->len); + err = bpf_check_classic(fp->insns, fp->len); if (err) { __sk_filter_release(fp); return ERR_PTR(err); -- cgit v1.2.3 From 8fb575ca396bc31d9fa99c26336e2432b41d1bfc Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Wed, 30 Jul 2014 20:34:15 -0700 Subject: net: filter: rename sk_convert_filter() -> bpf_convert_filter() to indicate that this function is converting classic BPF into eBPF and not related to sockets Signed-off-by: Alexei Starovoitov Signed-off-by: David S. Miller --- arch/x86/net/bpf_jit_comp.c | 2 +- include/linux/filter.h | 4 ++-- kernel/bpf/core.c | 2 +- kernel/seccomp.c | 4 ++-- net/core/filter.c | 16 ++++++++-------- 5 files changed, 14 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index 71737a83f022..e2ecc1380b3d 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -235,7 +235,7 @@ static int do_jit(struct sk_filter *bpf_prog, int *addrs, u8 *image, /* mov qword ptr [rbp-X],rbx */ EMIT3_off32(0x48, 0x89, 0x9D, -stacksize); - /* sk_convert_filter() maps classic BPF register X to R7 and uses R8 + /* bpf_convert_filter() maps classic BPF register X to R7 and uses R8 * as temporary, so all tcpdump filters need to spill/fill R7(r13) and * R8(r14). R9(r15) spill could be made conditional, but there is only * one 'bpf_error' return path out of helper functions inside bpf_jit.S diff --git a/include/linux/filter.h b/include/linux/filter.h index c4d0be4c5e75..7cb9b40e9a2f 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -351,8 +351,8 @@ int sk_filter(struct sock *sk, struct sk_buff *skb); void sk_filter_select_runtime(struct sk_filter *fp); void sk_filter_free(struct sk_filter *fp); -int sk_convert_filter(struct sock_filter *prog, int len, - struct bpf_insn *new_prog, int *new_len); +int bpf_convert_filter(struct sock_filter *prog, int len, + struct bpf_insn *new_prog, int *new_len); int sk_unattached_filter_create(struct sk_filter **pfp, struct sock_fprog_kern *fprog); diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index b479807ec383..188ac5ba3900 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -446,7 +446,7 @@ load_word: /* BPF_LD + BPD_ABS and BPF_LD + BPF_IND insns are * only appearing in the programs where ctx == * skb. All programs keep 'ctx' in regs[BPF_REG_CTX] - * == BPF_R6, sk_convert_filter() saves it in BPF_R6, + * == BPF_R6, bpf_convert_filter() saves it in BPF_R6, * internal BPF verifier will check that BPF_R6 == * ctx. * diff --git a/kernel/seccomp.c b/kernel/seccomp.c index f4a77d23f209..33a3a97e2b58 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -249,7 +249,7 @@ static long seccomp_attach_filter(struct sock_fprog *fprog) goto free_prog; /* Convert 'sock_filter' insns to 'bpf_insn' insns */ - ret = sk_convert_filter(fp, fprog->len, NULL, &new_len); + ret = bpf_convert_filter(fp, fprog->len, NULL, &new_len); if (ret) goto free_prog; @@ -265,7 +265,7 @@ static long seccomp_attach_filter(struct sock_fprog *fprog) if (!filter->prog) goto free_filter; - ret = sk_convert_filter(fp, fprog->len, filter->prog->insnsi, &new_len); + ret = bpf_convert_filter(fp, fprog->len, filter->prog->insnsi, &new_len); if (ret) goto free_filter_prog; kfree(fp); diff --git a/net/core/filter.c b/net/core/filter.c index 5740ea08a3ad..6ac901613bee 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -312,7 +312,7 @@ static bool convert_bpf_extensions(struct sock_filter *fp, } /** - * sk_convert_filter - convert filter program + * bpf_convert_filter - convert filter program * @prog: the user passed filter program * @len: the length of the user passed filter program * @new_prog: buffer where converted program will be stored @@ -322,12 +322,12 @@ static bool convert_bpf_extensions(struct sock_filter *fp, * Conversion workflow: * * 1) First pass for calculating the new program length: - * sk_convert_filter(old_prog, old_len, NULL, &new_len) + * bpf_convert_filter(old_prog, old_len, NULL, &new_len) * * 2) 2nd pass to remap in two passes: 1st pass finds new * jump offsets, 2nd pass remapping: * new_prog = kmalloc(sizeof(struct bpf_insn) * new_len); - * sk_convert_filter(old_prog, old_len, new_prog, &new_len); + * bpf_convert_filter(old_prog, old_len, new_prog, &new_len); * * User BPF's register A is mapped to our BPF register 6, user BPF * register X is mapped to BPF register 7; frame pointer is always @@ -335,8 +335,8 @@ static bool convert_bpf_extensions(struct sock_filter *fp, * for socket filters: ctx == 'struct sk_buff *', for seccomp: * ctx == 'struct seccomp_data *'. */ -int sk_convert_filter(struct sock_filter *prog, int len, - struct bpf_insn *new_prog, int *new_len) +int bpf_convert_filter(struct sock_filter *prog, int len, + struct bpf_insn *new_prog, int *new_len) { int new_flen = 0, pass = 0, target, i; struct bpf_insn *new_insn; @@ -921,7 +921,7 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp) } /* 1st pass: calculate the new program length. */ - err = sk_convert_filter(old_prog, old_len, NULL, &new_len); + err = bpf_convert_filter(old_prog, old_len, NULL, &new_len); if (err) goto out_err_free; @@ -940,9 +940,9 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp) fp->len = new_len; /* 2nd pass: remap sock_filter insns into bpf_insn insns. */ - err = sk_convert_filter(old_prog, old_len, fp->insnsi, &new_len); + err = bpf_convert_filter(old_prog, old_len, fp->insnsi, &new_len); if (err) - /* 2nd sk_convert_filter() can fail only if it fails + /* 2nd bpf_convert_filter() can fail only if it fails * to allocate memory, remapping must succeed. Note, * that at this time old_fp has already been released * by krealloc(). -- cgit v1.2.3 From 7ae457c1e5b45a1b826fad9d62b32191d2bdcfdb Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Wed, 30 Jul 2014 20:34:16 -0700 Subject: net: filter: split 'struct sk_filter' into socket and bpf parts clean up names related to socket filtering and bpf in the following way: - everything that deals with sockets keeps 'sk_*' prefix - everything that is pure BPF is changed to 'bpf_*' prefix split 'struct sk_filter' into struct sk_filter { atomic_t refcnt; struct rcu_head rcu; struct bpf_prog *prog; }; and struct bpf_prog { u32 jited:1, len:31; struct sock_fprog_kern *orig_prog; unsigned int (*bpf_func)(const struct sk_buff *skb, const struct bpf_insn *filter); union { struct sock_filter insns[0]; struct bpf_insn insnsi[0]; struct work_struct work; }; }; so that 'struct bpf_prog' can be used independent of sockets and cleans up 'unattached' bpf use cases split SK_RUN_FILTER macro into: SK_RUN_FILTER to be used with 'struct sk_filter *' and BPF_PROG_RUN to be used with 'struct bpf_prog *' __sk_filter_release(struct sk_filter *) gains __bpf_prog_release(struct bpf_prog *) helper function also perform related renames for the functions that work with 'struct bpf_prog *', since they're on the same lines: sk_filter_size -> bpf_prog_size sk_filter_select_runtime -> bpf_prog_select_runtime sk_filter_free -> bpf_prog_free sk_unattached_filter_create -> bpf_prog_create sk_unattached_filter_destroy -> bpf_prog_destroy sk_store_orig_filter -> bpf_prog_store_orig_filter sk_release_orig_filter -> bpf_release_orig_filter __sk_migrate_filter -> bpf_migrate_filter __sk_prepare_filter -> bpf_prepare_filter API for attaching classic BPF to a socket stays the same: sk_attach_filter(prog, struct sock *)/sk_detach_filter(struct sock *) and SK_RUN_FILTER(struct sk_filter *, ctx) to execute a program which is used by sockets, tun, af_packet API for 'unattached' BPF programs becomes: bpf_prog_create(struct bpf_prog **)/bpf_prog_destroy(struct bpf_prog *) and BPF_PROG_RUN(struct bpf_prog *, ctx) to execute a program which is used by isdn, ppp, team, seccomp, ptp, xt_bpf, cls_bpf, test_bpf Signed-off-by: Alexei Starovoitov Signed-off-by: David S. Miller --- Documentation/networking/filter.txt | 10 ++-- arch/arm/net/bpf_jit_32.c | 8 +-- arch/mips/net/bpf_jit.c | 8 +-- arch/powerpc/net/bpf_jit_comp.c | 8 +-- arch/s390/net/bpf_jit_comp.c | 4 +- arch/sparc/net/bpf_jit_comp.c | 4 +- arch/x86/net/bpf_jit_comp.c | 12 ++--- drivers/isdn/i4l/isdn_ppp.c | 26 +++++---- drivers/net/ppp/ppp_generic.c | 28 +++++----- drivers/net/team/team_mode_loadbalance.c | 14 ++--- include/linux/filter.h | 40 ++++++++------ include/linux/isdn_ppp.h | 4 +- include/uapi/linux/netfilter/xt_bpf.h | 4 +- kernel/bpf/core.c | 30 +++++------ kernel/seccomp.c | 10 ++-- lib/test_bpf.c | 24 ++++----- net/core/filter.c | 92 ++++++++++++++++++-------------- net/core/ptp_classifier.c | 6 +-- net/core/sock_diag.c | 2 +- net/netfilter/xt_bpf.c | 6 +-- net/sched/cls_bpf.c | 12 ++--- 21 files changed, 183 insertions(+), 169 deletions(-) (limited to 'include') diff --git a/Documentation/networking/filter.txt b/Documentation/networking/filter.txt index 712068be8171..c48a9704bda8 100644 --- a/Documentation/networking/filter.txt +++ b/Documentation/networking/filter.txt @@ -586,11 +586,11 @@ team driver's classifier for its load-balancing mode, netfilter's xt_bpf extension, PTP dissector/classifier, and much more. They are all internally converted by the kernel into the new instruction set representation and run in the eBPF interpreter. For in-kernel handlers, this all works transparently -by using sk_unattached_filter_create() for setting up the filter, resp. -sk_unattached_filter_destroy() for destroying it. The macro -SK_RUN_FILTER(filter, ctx) transparently invokes eBPF interpreter or JITed -code to run the filter. 'filter' is a pointer to struct sk_filter that we -got from sk_unattached_filter_create(), and 'ctx' the given context (e.g. +by using bpf_prog_create() for setting up the filter, resp. +bpf_prog_destroy() for destroying it. The macro +BPF_PROG_RUN(filter, ctx) transparently invokes eBPF interpreter or JITed +code to run the filter. 'filter' is a pointer to struct bpf_prog that we +got from bpf_prog_create(), and 'ctx' the given context (e.g. skb pointer). All constraints and restrictions from bpf_check_classic() apply before a conversion to the new layout is being done behind the scenes! diff --git a/arch/arm/net/bpf_jit_32.c b/arch/arm/net/bpf_jit_32.c index fb5503ce016f..a37b989a2f91 100644 --- a/arch/arm/net/bpf_jit_32.c +++ b/arch/arm/net/bpf_jit_32.c @@ -56,7 +56,7 @@ #define FLAG_NEED_X_RESET (1 << 0) struct jit_ctx { - const struct sk_filter *skf; + const struct bpf_prog *skf; unsigned idx; unsigned prologue_bytes; int ret0_fp_idx; @@ -465,7 +465,7 @@ static inline void update_on_xread(struct jit_ctx *ctx) static int build_body(struct jit_ctx *ctx) { void *load_func[] = {jit_get_skb_b, jit_get_skb_h, jit_get_skb_w}; - const struct sk_filter *prog = ctx->skf; + const struct bpf_prog *prog = ctx->skf; const struct sock_filter *inst; unsigned i, load_order, off, condt; int imm12; @@ -857,7 +857,7 @@ b_epilogue: } -void bpf_jit_compile(struct sk_filter *fp) +void bpf_jit_compile(struct bpf_prog *fp) { struct jit_ctx ctx; unsigned tmp_idx; @@ -926,7 +926,7 @@ out: return; } -void bpf_jit_free(struct sk_filter *fp) +void bpf_jit_free(struct bpf_prog *fp) { if (fp->jited) module_free(NULL, fp->bpf_func); diff --git a/arch/mips/net/bpf_jit.c b/arch/mips/net/bpf_jit.c index b87390a56a2f..05a56619ece2 100644 --- a/arch/mips/net/bpf_jit.c +++ b/arch/mips/net/bpf_jit.c @@ -131,7 +131,7 @@ * @target: Memory location for the compiled filter */ struct jit_ctx { - const struct sk_filter *skf; + const struct bpf_prog *skf; unsigned int prologue_bytes; u32 idx; u32 flags; @@ -789,7 +789,7 @@ static int pkt_type_offset(void) static int build_body(struct jit_ctx *ctx) { void *load_func[] = {jit_get_skb_b, jit_get_skb_h, jit_get_skb_w}; - const struct sk_filter *prog = ctx->skf; + const struct bpf_prog *prog = ctx->skf; const struct sock_filter *inst; unsigned int i, off, load_order, condt; u32 k, b_off __maybe_unused; @@ -1369,7 +1369,7 @@ jmp_cmp: int bpf_jit_enable __read_mostly; -void bpf_jit_compile(struct sk_filter *fp) +void bpf_jit_compile(struct bpf_prog *fp) { struct jit_ctx ctx; unsigned int alloc_size, tmp_idx; @@ -1423,7 +1423,7 @@ out: kfree(ctx.offsets); } -void bpf_jit_free(struct sk_filter *fp) +void bpf_jit_free(struct bpf_prog *fp) { if (fp->jited) module_free(NULL, fp->bpf_func); diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c index 82e82cadcde5..3afa6f4c1957 100644 --- a/arch/powerpc/net/bpf_jit_comp.c +++ b/arch/powerpc/net/bpf_jit_comp.c @@ -25,7 +25,7 @@ static inline void bpf_flush_icache(void *start, void *end) flush_icache_range((unsigned long)start, (unsigned long)end); } -static void bpf_jit_build_prologue(struct sk_filter *fp, u32 *image, +static void bpf_jit_build_prologue(struct bpf_prog *fp, u32 *image, struct codegen_context *ctx) { int i; @@ -121,7 +121,7 @@ static void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx) ((int)K < 0 ? ((int)K >= SKF_LL_OFF ? func##_negative_offset : func) : func##_positive_offset) /* Assemble the body code between the prologue & epilogue. */ -static int bpf_jit_build_body(struct sk_filter *fp, u32 *image, +static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, struct codegen_context *ctx, unsigned int *addrs) { @@ -569,7 +569,7 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image, return 0; } -void bpf_jit_compile(struct sk_filter *fp) +void bpf_jit_compile(struct bpf_prog *fp) { unsigned int proglen; unsigned int alloclen; @@ -693,7 +693,7 @@ out: return; } -void bpf_jit_free(struct sk_filter *fp) +void bpf_jit_free(struct bpf_prog *fp) { if (fp->jited) module_free(NULL, fp->bpf_func); diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c index a2cbd875543a..61e45b7c04d7 100644 --- a/arch/s390/net/bpf_jit_comp.c +++ b/arch/s390/net/bpf_jit_comp.c @@ -812,7 +812,7 @@ static struct bpf_binary_header *bpf_alloc_binary(unsigned int bpfsize, return header; } -void bpf_jit_compile(struct sk_filter *fp) +void bpf_jit_compile(struct bpf_prog *fp) { struct bpf_binary_header *header = NULL; unsigned long size, prg_len, lit_len; @@ -875,7 +875,7 @@ out: kfree(addrs); } -void bpf_jit_free(struct sk_filter *fp) +void bpf_jit_free(struct bpf_prog *fp) { unsigned long addr = (unsigned long)fp->bpf_func & PAGE_MASK; struct bpf_binary_header *header = (void *)addr; diff --git a/arch/sparc/net/bpf_jit_comp.c b/arch/sparc/net/bpf_jit_comp.c index 892a102671ad..1f76c22a6a75 100644 --- a/arch/sparc/net/bpf_jit_comp.c +++ b/arch/sparc/net/bpf_jit_comp.c @@ -354,7 +354,7 @@ do { *prog++ = BR_OPC | WDISP22(OFF); \ * emit_jump() calls with adjusted offsets. */ -void bpf_jit_compile(struct sk_filter *fp) +void bpf_jit_compile(struct bpf_prog *fp) { unsigned int cleanup_addr, proglen, oldproglen = 0; u32 temp[8], *prog, *func, seen = 0, pass; @@ -808,7 +808,7 @@ out: return; } -void bpf_jit_free(struct sk_filter *fp) +void bpf_jit_free(struct bpf_prog *fp) { if (fp->jited) module_free(NULL, fp->bpf_func); diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index e2ecc1380b3d..5c8cb8043c5a 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -211,7 +211,7 @@ struct jit_context { bool seen_ld_abs; }; -static int do_jit(struct sk_filter *bpf_prog, int *addrs, u8 *image, +static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, int oldproglen, struct jit_context *ctx) { struct bpf_insn *insn = bpf_prog->insnsi; @@ -841,7 +841,7 @@ common_load: ctx->seen_ld_abs = true; /* By design x64 JIT should support all BPF instructions * This error will be seen if new instruction was added * to interpreter, but not to JIT - * or if there is junk in sk_filter + * or if there is junk in bpf_prog */ pr_err("bpf_jit: unknown opcode %02x\n", insn->code); return -EINVAL; @@ -862,11 +862,11 @@ common_load: ctx->seen_ld_abs = true; return proglen; } -void bpf_jit_compile(struct sk_filter *prog) +void bpf_jit_compile(struct bpf_prog *prog) { } -void bpf_int_jit_compile(struct sk_filter *prog) +void bpf_int_jit_compile(struct bpf_prog *prog) { struct bpf_binary_header *header = NULL; int proglen, oldproglen = 0; @@ -932,7 +932,7 @@ out: static void bpf_jit_free_deferred(struct work_struct *work) { - struct sk_filter *fp = container_of(work, struct sk_filter, work); + struct bpf_prog *fp = container_of(work, struct bpf_prog, work); unsigned long addr = (unsigned long)fp->bpf_func & PAGE_MASK; struct bpf_binary_header *header = (void *)addr; @@ -941,7 +941,7 @@ static void bpf_jit_free_deferred(struct work_struct *work) kfree(fp); } -void bpf_jit_free(struct sk_filter *fp) +void bpf_jit_free(struct bpf_prog *fp) { if (fp->jited) { INIT_WORK(&fp->work, bpf_jit_free_deferred); diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c index 62f0688d45a5..c4198fa490bf 100644 --- a/drivers/isdn/i4l/isdn_ppp.c +++ b/drivers/isdn/i4l/isdn_ppp.c @@ -379,12 +379,12 @@ isdn_ppp_release(int min, struct file *file) #endif #ifdef CONFIG_IPPP_FILTER if (is->pass_filter) { - sk_unattached_filter_destroy(is->pass_filter); + bpf_prog_destroy(is->pass_filter); is->pass_filter = NULL; } if (is->active_filter) { - sk_unattached_filter_destroy(is->active_filter); + bpf_prog_destroy(is->active_filter); is->active_filter = NULL; } #endif @@ -639,12 +639,11 @@ isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg) fprog.filter = code; if (is->pass_filter) { - sk_unattached_filter_destroy(is->pass_filter); + bpf_prog_destroy(is->pass_filter); is->pass_filter = NULL; } if (fprog.filter != NULL) - err = sk_unattached_filter_create(&is->pass_filter, - &fprog); + err = bpf_prog_create(&is->pass_filter, &fprog); else err = 0; kfree(code); @@ -664,12 +663,11 @@ isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg) fprog.filter = code; if (is->active_filter) { - sk_unattached_filter_destroy(is->active_filter); + bpf_prog_destroy(is->active_filter); is->active_filter = NULL; } if (fprog.filter != NULL) - err = sk_unattached_filter_create(&is->active_filter, - &fprog); + err = bpf_prog_create(&is->active_filter, &fprog); else err = 0; kfree(code); @@ -1174,14 +1172,14 @@ isdn_ppp_push_higher(isdn_net_dev *net_dev, isdn_net_local *lp, struct sk_buff * } if (is->pass_filter - && SK_RUN_FILTER(is->pass_filter, skb) == 0) { + && BPF_PROG_RUN(is->pass_filter, skb) == 0) { if (is->debug & 0x2) printk(KERN_DEBUG "IPPP: inbound frame filtered.\n"); kfree_skb(skb); return; } if (!(is->active_filter - && SK_RUN_FILTER(is->active_filter, skb) == 0)) { + && BPF_PROG_RUN(is->active_filter, skb) == 0)) { if (is->debug & 0x2) printk(KERN_DEBUG "IPPP: link-active filter: resetting huptimer.\n"); lp->huptimer = 0; @@ -1320,14 +1318,14 @@ isdn_ppp_xmit(struct sk_buff *skb, struct net_device *netdev) } if (ipt->pass_filter - && SK_RUN_FILTER(ipt->pass_filter, skb) == 0) { + && BPF_PROG_RUN(ipt->pass_filter, skb) == 0) { if (ipt->debug & 0x4) printk(KERN_DEBUG "IPPP: outbound frame filtered.\n"); kfree_skb(skb); goto unlock; } if (!(ipt->active_filter - && SK_RUN_FILTER(ipt->active_filter, skb) == 0)) { + && BPF_PROG_RUN(ipt->active_filter, skb) == 0)) { if (ipt->debug & 0x4) printk(KERN_DEBUG "IPPP: link-active filter: resetting huptimer.\n"); lp->huptimer = 0; @@ -1517,9 +1515,9 @@ int isdn_ppp_autodial_filter(struct sk_buff *skb, isdn_net_local *lp) } drop |= is->pass_filter - && SK_RUN_FILTER(is->pass_filter, skb) == 0; + && BPF_PROG_RUN(is->pass_filter, skb) == 0; drop |= is->active_filter - && SK_RUN_FILTER(is->active_filter, skb) == 0; + && BPF_PROG_RUN(is->active_filter, skb) == 0; skb_push(skb, IPPP_MAX_HEADER - 4); return drop; diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 765248b42a0a..fa0d71727894 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -143,8 +143,8 @@ struct ppp { struct sk_buff_head mrq; /* MP: receive reconstruction queue */ #endif /* CONFIG_PPP_MULTILINK */ #ifdef CONFIG_PPP_FILTER - struct sk_filter *pass_filter; /* filter for packets to pass */ - struct sk_filter *active_filter;/* filter for pkts to reset idle */ + struct bpf_prog *pass_filter; /* filter for packets to pass */ + struct bpf_prog *active_filter; /* filter for pkts to reset idle */ #endif /* CONFIG_PPP_FILTER */ struct net *ppp_net; /* the net we belong to */ struct ppp_link_stats stats64; /* 64 bit network stats */ @@ -762,12 +762,12 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ppp_lock(ppp); if (ppp->pass_filter) { - sk_unattached_filter_destroy(ppp->pass_filter); + bpf_prog_destroy(ppp->pass_filter); ppp->pass_filter = NULL; } if (fprog.filter != NULL) - err = sk_unattached_filter_create(&ppp->pass_filter, - &fprog); + err = bpf_prog_create(&ppp->pass_filter, + &fprog); else err = 0; kfree(code); @@ -788,12 +788,12 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ppp_lock(ppp); if (ppp->active_filter) { - sk_unattached_filter_destroy(ppp->active_filter); + bpf_prog_destroy(ppp->active_filter); ppp->active_filter = NULL; } if (fprog.filter != NULL) - err = sk_unattached_filter_create(&ppp->active_filter, - &fprog); + err = bpf_prog_create(&ppp->active_filter, + &fprog); else err = 0; kfree(code); @@ -1205,7 +1205,7 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb) a four-byte PPP header on each packet */ *skb_push(skb, 2) = 1; if (ppp->pass_filter && - SK_RUN_FILTER(ppp->pass_filter, skb) == 0) { + BPF_PROG_RUN(ppp->pass_filter, skb) == 0) { if (ppp->debug & 1) netdev_printk(KERN_DEBUG, ppp->dev, "PPP: outbound frame " @@ -1215,7 +1215,7 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb) } /* if this packet passes the active filter, record the time */ if (!(ppp->active_filter && - SK_RUN_FILTER(ppp->active_filter, skb) == 0)) + BPF_PROG_RUN(ppp->active_filter, skb) == 0)) ppp->last_xmit = jiffies; skb_pull(skb, 2); #else @@ -1839,7 +1839,7 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb) *skb_push(skb, 2) = 0; if (ppp->pass_filter && - SK_RUN_FILTER(ppp->pass_filter, skb) == 0) { + BPF_PROG_RUN(ppp->pass_filter, skb) == 0) { if (ppp->debug & 1) netdev_printk(KERN_DEBUG, ppp->dev, "PPP: inbound frame " @@ -1848,7 +1848,7 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb) return; } if (!(ppp->active_filter && - SK_RUN_FILTER(ppp->active_filter, skb) == 0)) + BPF_PROG_RUN(ppp->active_filter, skb) == 0)) ppp->last_recv = jiffies; __skb_pull(skb, 2); } else @@ -2829,12 +2829,12 @@ static void ppp_destroy_interface(struct ppp *ppp) #endif /* CONFIG_PPP_MULTILINK */ #ifdef CONFIG_PPP_FILTER if (ppp->pass_filter) { - sk_unattached_filter_destroy(ppp->pass_filter); + bpf_prog_destroy(ppp->pass_filter); ppp->pass_filter = NULL; } if (ppp->active_filter) { - sk_unattached_filter_destroy(ppp->active_filter); + bpf_prog_destroy(ppp->active_filter); ppp->active_filter = NULL; } #endif /* CONFIG_PPP_FILTER */ diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c index d7be9b36bce6..a1536d0d83a9 100644 --- a/drivers/net/team/team_mode_loadbalance.c +++ b/drivers/net/team/team_mode_loadbalance.c @@ -58,7 +58,7 @@ struct lb_priv_ex { }; struct lb_priv { - struct sk_filter __rcu *fp; + struct bpf_prog __rcu *fp; lb_select_tx_port_func_t __rcu *select_tx_port_func; struct lb_pcpu_stats __percpu *pcpu_stats; struct lb_priv_ex *ex; /* priv extension */ @@ -174,14 +174,14 @@ static lb_select_tx_port_func_t *lb_select_tx_port_get_func(const char *name) static unsigned int lb_get_skb_hash(struct lb_priv *lb_priv, struct sk_buff *skb) { - struct sk_filter *fp; + struct bpf_prog *fp; uint32_t lhash; unsigned char *c; fp = rcu_dereference_bh(lb_priv->fp); if (unlikely(!fp)) return 0; - lhash = SK_RUN_FILTER(fp, skb); + lhash = BPF_PROG_RUN(fp, skb); c = (char *) &lhash; return c[0] ^ c[1] ^ c[2] ^ c[3]; } @@ -271,8 +271,8 @@ static void __fprog_destroy(struct sock_fprog_kern *fprog) static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx) { struct lb_priv *lb_priv = get_lb_priv(team); - struct sk_filter *fp = NULL; - struct sk_filter *orig_fp = NULL; + struct bpf_prog *fp = NULL; + struct bpf_prog *orig_fp = NULL; struct sock_fprog_kern *fprog = NULL; int err; @@ -281,7 +281,7 @@ static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx) ctx->data.bin_val.ptr); if (err) return err; - err = sk_unattached_filter_create(&fp, fprog); + err = bpf_prog_create(&fp, fprog); if (err) { __fprog_destroy(fprog); return err; @@ -300,7 +300,7 @@ static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx) if (orig_fp) { synchronize_rcu(); - sk_unattached_filter_destroy(orig_fp); + bpf_prog_destroy(orig_fp); } return 0; } diff --git a/include/linux/filter.h b/include/linux/filter.h index 7cb9b40e9a2f..a5227ab8ccb1 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -296,7 +296,8 @@ enum { }) /* Macro to invoke filter function. */ -#define SK_RUN_FILTER(filter, ctx) (*filter->bpf_func)(ctx, filter->insnsi) +#define SK_RUN_FILTER(filter, ctx) \ + (*filter->prog->bpf_func)(ctx, filter->prog->insnsi) struct bpf_insn { __u8 code; /* opcode */ @@ -323,12 +324,10 @@ struct sk_buff; struct sock; struct seccomp_data; -struct sk_filter { - atomic_t refcnt; +struct bpf_prog { u32 jited:1, /* Is our filter JIT'ed? */ len:31; /* Number of filter blocks */ struct sock_fprog_kern *orig_prog; /* Original BPF program */ - struct rcu_head rcu; unsigned int (*bpf_func)(const struct sk_buff *skb, const struct bpf_insn *filter); union { @@ -338,25 +337,32 @@ struct sk_filter { }; }; -static inline unsigned int sk_filter_size(unsigned int proglen) +struct sk_filter { + atomic_t refcnt; + struct rcu_head rcu; + struct bpf_prog *prog; +}; + +#define BPF_PROG_RUN(filter, ctx) (*filter->bpf_func)(ctx, filter->insnsi) + +static inline unsigned int bpf_prog_size(unsigned int proglen) { - return max(sizeof(struct sk_filter), - offsetof(struct sk_filter, insns[proglen])); + return max(sizeof(struct bpf_prog), + offsetof(struct bpf_prog, insns[proglen])); } #define bpf_classic_proglen(fprog) (fprog->len * sizeof(fprog->filter[0])) int sk_filter(struct sock *sk, struct sk_buff *skb); -void sk_filter_select_runtime(struct sk_filter *fp); -void sk_filter_free(struct sk_filter *fp); +void bpf_prog_select_runtime(struct bpf_prog *fp); +void bpf_prog_free(struct bpf_prog *fp); int bpf_convert_filter(struct sock_filter *prog, int len, struct bpf_insn *new_prog, int *new_len); -int sk_unattached_filter_create(struct sk_filter **pfp, - struct sock_fprog_kern *fprog); -void sk_unattached_filter_destroy(struct sk_filter *fp); +int bpf_prog_create(struct bpf_prog **pfp, struct sock_fprog_kern *fprog); +void bpf_prog_destroy(struct bpf_prog *fp); int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk); int sk_detach_filter(struct sock *sk); @@ -369,7 +375,7 @@ bool sk_filter_charge(struct sock *sk, struct sk_filter *fp); void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp); u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5); -void bpf_int_jit_compile(struct sk_filter *fp); +void bpf_int_jit_compile(struct bpf_prog *fp); #define BPF_ANC BIT(15) @@ -423,8 +429,8 @@ static inline void *bpf_load_pointer(const struct sk_buff *skb, int k, #include #include -void bpf_jit_compile(struct sk_filter *fp); -void bpf_jit_free(struct sk_filter *fp); +void bpf_jit_compile(struct bpf_prog *fp); +void bpf_jit_free(struct bpf_prog *fp); static inline void bpf_jit_dump(unsigned int flen, unsigned int proglen, u32 pass, void *image) @@ -438,11 +444,11 @@ static inline void bpf_jit_dump(unsigned int flen, unsigned int proglen, #else #include -static inline void bpf_jit_compile(struct sk_filter *fp) +static inline void bpf_jit_compile(struct bpf_prog *fp) { } -static inline void bpf_jit_free(struct sk_filter *fp) +static inline void bpf_jit_free(struct bpf_prog *fp) { kfree(fp); } diff --git a/include/linux/isdn_ppp.h b/include/linux/isdn_ppp.h index 8e10f57f109f..a0070c6dfaf8 100644 --- a/include/linux/isdn_ppp.h +++ b/include/linux/isdn_ppp.h @@ -180,8 +180,8 @@ struct ippp_struct { struct slcompress *slcomp; #endif #ifdef CONFIG_IPPP_FILTER - struct sk_filter *pass_filter; /* filter for packets to pass */ - struct sk_filter *active_filter; /* filter for pkts to reset idle */ + struct bpf_prog *pass_filter; /* filter for packets to pass */ + struct bpf_prog *active_filter; /* filter for pkts to reset idle */ #endif unsigned long debug; struct isdn_ppp_compressor *compressor,*decompressor; diff --git a/include/uapi/linux/netfilter/xt_bpf.h b/include/uapi/linux/netfilter/xt_bpf.h index 2ec9fbcd06f9..1fad2c27ac32 100644 --- a/include/uapi/linux/netfilter/xt_bpf.h +++ b/include/uapi/linux/netfilter/xt_bpf.h @@ -6,14 +6,14 @@ #define XT_BPF_MAX_NUM_INSTR 64 -struct sk_filter; +struct bpf_prog; struct xt_bpf_info { __u16 bpf_program_num_elem; struct sock_filter bpf_program[XT_BPF_MAX_NUM_INSTR]; /* only used in the kernel */ - struct sk_filter *filter __attribute__((aligned(8))); + struct bpf_prog *filter __attribute__((aligned(8))); }; #endif /*_XT_BPF_H */ diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 188ac5ba3900..7f0dbcbb34af 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -73,15 +73,13 @@ noinline u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5) } /** - * __sk_run_filter - run a filter on a given context - * @ctx: buffer to run the filter on - * @insn: filter to apply + * __bpf_prog_run - run eBPF program on a given context + * @ctx: is the data we are operating on + * @insn: is the array of eBPF instructions * - * Decode and apply filter instructions to the skb->data. Return length to - * keep, 0 for none. @ctx is the data we are operating on, @insn is the - * array of filter instructions. + * Decode and execute eBPF instructions. */ -static unsigned int __sk_run_filter(void *ctx, const struct bpf_insn *insn) +static unsigned int __bpf_prog_run(void *ctx, const struct bpf_insn *insn) { u64 stack[MAX_BPF_STACK / sizeof(u64)]; u64 regs[MAX_BPF_REG], tmp; @@ -508,29 +506,29 @@ load_byte: return 0; } -void __weak bpf_int_jit_compile(struct sk_filter *prog) +void __weak bpf_int_jit_compile(struct bpf_prog *prog) { } /** - * sk_filter_select_runtime - select execution runtime for BPF program - * @fp: sk_filter populated with internal BPF program + * bpf_prog_select_runtime - select execution runtime for BPF program + * @fp: bpf_prog populated with internal BPF program * * try to JIT internal BPF program, if JIT is not available select interpreter - * BPF program will be executed via SK_RUN_FILTER() macro + * BPF program will be executed via BPF_PROG_RUN() macro */ -void sk_filter_select_runtime(struct sk_filter *fp) +void bpf_prog_select_runtime(struct bpf_prog *fp) { - fp->bpf_func = (void *) __sk_run_filter; + fp->bpf_func = (void *) __bpf_prog_run; /* Probe if internal BPF can be JITed */ bpf_int_jit_compile(fp); } -EXPORT_SYMBOL_GPL(sk_filter_select_runtime); +EXPORT_SYMBOL_GPL(bpf_prog_select_runtime); /* free internal BPF program */ -void sk_filter_free(struct sk_filter *fp) +void bpf_prog_free(struct bpf_prog *fp) { bpf_jit_free(fp); } -EXPORT_SYMBOL_GPL(sk_filter_free); +EXPORT_SYMBOL_GPL(bpf_prog_free); diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 33a3a97e2b58..2f3fa2cc2eac 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -54,7 +54,7 @@ struct seccomp_filter { atomic_t usage; struct seccomp_filter *prev; - struct sk_filter *prog; + struct bpf_prog *prog; }; /* Limit any path through the tree to 256KB worth of instructions. */ @@ -187,7 +187,7 @@ static u32 seccomp_run_filters(int syscall) * value always takes priority (ignoring the DATA). */ for (f = current->seccomp.filter; f; f = f->prev) { - u32 cur_ret = SK_RUN_FILTER(f->prog, (void *)&sd); + u32 cur_ret = BPF_PROG_RUN(f->prog, (void *)&sd); if ((cur_ret & SECCOMP_RET_ACTION) < (ret & SECCOMP_RET_ACTION)) ret = cur_ret; @@ -260,7 +260,7 @@ static long seccomp_attach_filter(struct sock_fprog *fprog) if (!filter) goto free_prog; - filter->prog = kzalloc(sk_filter_size(new_len), + filter->prog = kzalloc(bpf_prog_size(new_len), GFP_KERNEL|__GFP_NOWARN); if (!filter->prog) goto free_filter; @@ -273,7 +273,7 @@ static long seccomp_attach_filter(struct sock_fprog *fprog) atomic_set(&filter->usage, 1); filter->prog->len = new_len; - sk_filter_select_runtime(filter->prog); + bpf_prog_select_runtime(filter->prog); /* * If there is an existing filter, make it the prev and don't drop its @@ -337,7 +337,7 @@ void put_seccomp_filter(struct task_struct *tsk) while (orig && atomic_dec_and_test(&orig->usage)) { struct seccomp_filter *freeme = orig; orig = orig->prev; - sk_filter_free(freeme->prog); + bpf_prog_free(freeme->prog); kfree(freeme); } } diff --git a/lib/test_bpf.c b/lib/test_bpf.c index 5f48623ee1a7..89e0345733bd 100644 --- a/lib/test_bpf.c +++ b/lib/test_bpf.c @@ -1761,9 +1761,9 @@ static int probe_filter_length(struct sock_filter *fp) return len + 1; } -static struct sk_filter *generate_filter(int which, int *err) +static struct bpf_prog *generate_filter(int which, int *err) { - struct sk_filter *fp; + struct bpf_prog *fp; struct sock_fprog_kern fprog; unsigned int flen = probe_filter_length(tests[which].u.insns); __u8 test_type = tests[which].aux & TEST_TYPE_MASK; @@ -1773,7 +1773,7 @@ static struct sk_filter *generate_filter(int which, int *err) fprog.filter = tests[which].u.insns; fprog.len = flen; - *err = sk_unattached_filter_create(&fp, &fprog); + *err = bpf_prog_create(&fp, &fprog); if (tests[which].aux & FLAG_EXPECTED_FAIL) { if (*err == -EINVAL) { pr_cont("PASS\n"); @@ -1798,7 +1798,7 @@ static struct sk_filter *generate_filter(int which, int *err) break; case INTERNAL: - fp = kzalloc(sk_filter_size(flen), GFP_KERNEL); + fp = kzalloc(bpf_prog_size(flen), GFP_KERNEL); if (fp == NULL) { pr_cont("UNEXPECTED_FAIL no memory left\n"); *err = -ENOMEM; @@ -1809,7 +1809,7 @@ static struct sk_filter *generate_filter(int which, int *err) memcpy(fp->insnsi, tests[which].u.insns_int, fp->len * sizeof(struct bpf_insn)); - sk_filter_select_runtime(fp); + bpf_prog_select_runtime(fp); break; } @@ -1817,21 +1817,21 @@ static struct sk_filter *generate_filter(int which, int *err) return fp; } -static void release_filter(struct sk_filter *fp, int which) +static void release_filter(struct bpf_prog *fp, int which) { __u8 test_type = tests[which].aux & TEST_TYPE_MASK; switch (test_type) { case CLASSIC: - sk_unattached_filter_destroy(fp); + bpf_prog_destroy(fp); break; case INTERNAL: - sk_filter_free(fp); + bpf_prog_free(fp); break; } } -static int __run_one(const struct sk_filter *fp, const void *data, +static int __run_one(const struct bpf_prog *fp, const void *data, int runs, u64 *duration) { u64 start, finish; @@ -1840,7 +1840,7 @@ static int __run_one(const struct sk_filter *fp, const void *data, start = ktime_to_us(ktime_get()); for (i = 0; i < runs; i++) - ret = SK_RUN_FILTER(fp, data); + ret = BPF_PROG_RUN(fp, data); finish = ktime_to_us(ktime_get()); @@ -1850,7 +1850,7 @@ static int __run_one(const struct sk_filter *fp, const void *data, return ret; } -static int run_one(const struct sk_filter *fp, struct bpf_test *test) +static int run_one(const struct bpf_prog *fp, struct bpf_test *test) { int err_cnt = 0, i, runs = MAX_TESTRUNS; @@ -1884,7 +1884,7 @@ static __init int test_bpf(void) int i, err_cnt = 0, pass_cnt = 0; for (i = 0; i < ARRAY_SIZE(tests); i++) { - struct sk_filter *fp; + struct bpf_prog *fp; int err; pr_info("#%d %s ", i, tests[i].descr); diff --git a/net/core/filter.c b/net/core/filter.c index 6ac901613bee..d814b8a89d0f 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -810,8 +810,8 @@ int bpf_check_classic(const struct sock_filter *filter, unsigned int flen) } EXPORT_SYMBOL(bpf_check_classic); -static int sk_store_orig_filter(struct sk_filter *fp, - const struct sock_fprog *fprog) +static int bpf_prog_store_orig_filter(struct bpf_prog *fp, + const struct sock_fprog *fprog) { unsigned int fsize = bpf_classic_proglen(fprog); struct sock_fprog_kern *fkprog; @@ -831,7 +831,7 @@ static int sk_store_orig_filter(struct sk_filter *fp, return 0; } -static void sk_release_orig_filter(struct sk_filter *fp) +static void bpf_release_orig_filter(struct bpf_prog *fp) { struct sock_fprog_kern *fprog = fp->orig_prog; @@ -841,10 +841,16 @@ static void sk_release_orig_filter(struct sk_filter *fp) } } +static void __bpf_prog_release(struct bpf_prog *prog) +{ + bpf_release_orig_filter(prog); + bpf_prog_free(prog); +} + static void __sk_filter_release(struct sk_filter *fp) { - sk_release_orig_filter(fp); - sk_filter_free(fp); + __bpf_prog_release(fp->prog); + kfree(fp); } /** @@ -872,7 +878,7 @@ static void sk_filter_release(struct sk_filter *fp) void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp) { - u32 filter_size = sk_filter_size(fp->len); + u32 filter_size = bpf_prog_size(fp->prog->len); atomic_sub(filter_size, &sk->sk_omem_alloc); sk_filter_release(fp); @@ -883,7 +889,7 @@ void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp) */ bool sk_filter_charge(struct sock *sk, struct sk_filter *fp) { - u32 filter_size = sk_filter_size(fp->len); + u32 filter_size = bpf_prog_size(fp->prog->len); /* same check as in sock_kmalloc() */ if (filter_size <= sysctl_optmem_max && @@ -895,10 +901,10 @@ bool sk_filter_charge(struct sock *sk, struct sk_filter *fp) return false; } -static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp) +static struct bpf_prog *bpf_migrate_filter(struct bpf_prog *fp) { struct sock_filter *old_prog; - struct sk_filter *old_fp; + struct bpf_prog *old_fp; int err, new_len, old_len = fp->len; /* We are free to overwrite insns et al right here as it @@ -927,7 +933,7 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp) /* Expand fp for appending the new filter representation. */ old_fp = fp; - fp = krealloc(old_fp, sk_filter_size(new_len), GFP_KERNEL); + fp = krealloc(old_fp, bpf_prog_size(new_len), GFP_KERNEL); if (!fp) { /* The old_fp is still around in case we couldn't * allocate new memory, so uncharge on that one. @@ -949,7 +955,7 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp) */ goto out_err_free; - sk_filter_select_runtime(fp); + bpf_prog_select_runtime(fp); kfree(old_prog); return fp; @@ -957,11 +963,11 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp) out_err_free: kfree(old_prog); out_err: - __sk_filter_release(fp); + __bpf_prog_release(fp); return ERR_PTR(err); } -static struct sk_filter *__sk_prepare_filter(struct sk_filter *fp) +static struct bpf_prog *bpf_prepare_filter(struct bpf_prog *fp) { int err; @@ -970,7 +976,7 @@ static struct sk_filter *__sk_prepare_filter(struct sk_filter *fp) err = bpf_check_classic(fp->insns, fp->len); if (err) { - __sk_filter_release(fp); + __bpf_prog_release(fp); return ERR_PTR(err); } @@ -983,13 +989,13 @@ static struct sk_filter *__sk_prepare_filter(struct sk_filter *fp) * internal BPF translation for the optimized interpreter. */ if (!fp->jited) - fp = __sk_migrate_filter(fp); + fp = bpf_migrate_filter(fp); return fp; } /** - * sk_unattached_filter_create - create an unattached filter + * bpf_prog_create - create an unattached filter * @pfp: the unattached filter that is created * @fprog: the filter program * @@ -998,23 +1004,21 @@ static struct sk_filter *__sk_prepare_filter(struct sk_filter *fp) * If an error occurs or there is insufficient memory for the filter * a negative errno code is returned. On success the return is zero. */ -int sk_unattached_filter_create(struct sk_filter **pfp, - struct sock_fprog_kern *fprog) +int bpf_prog_create(struct bpf_prog **pfp, struct sock_fprog_kern *fprog) { unsigned int fsize = bpf_classic_proglen(fprog); - struct sk_filter *fp; + struct bpf_prog *fp; /* Make sure new filter is there and in the right amounts. */ if (fprog->filter == NULL) return -EINVAL; - fp = kmalloc(sk_filter_size(fprog->len), GFP_KERNEL); + fp = kmalloc(bpf_prog_size(fprog->len), GFP_KERNEL); if (!fp) return -ENOMEM; memcpy(fp->insns, fprog->filter, fsize); - atomic_set(&fp->refcnt, 1); fp->len = fprog->len; /* Since unattached filters are not copied back to user * space through sk_get_filter(), we do not need to hold @@ -1022,23 +1026,23 @@ int sk_unattached_filter_create(struct sk_filter **pfp, */ fp->orig_prog = NULL; - /* __sk_prepare_filter() already takes care of freeing + /* bpf_prepare_filter() already takes care of freeing * memory in case something goes wrong. */ - fp = __sk_prepare_filter(fp); + fp = bpf_prepare_filter(fp); if (IS_ERR(fp)) return PTR_ERR(fp); *pfp = fp; return 0; } -EXPORT_SYMBOL_GPL(sk_unattached_filter_create); +EXPORT_SYMBOL_GPL(bpf_prog_create); -void sk_unattached_filter_destroy(struct sk_filter *fp) +void bpf_prog_destroy(struct bpf_prog *fp) { - __sk_filter_release(fp); + __bpf_prog_release(fp); } -EXPORT_SYMBOL_GPL(sk_unattached_filter_destroy); +EXPORT_SYMBOL_GPL(bpf_prog_destroy); /** * sk_attach_filter - attach a socket filter @@ -1054,7 +1058,8 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk) { struct sk_filter *fp, *old_fp; unsigned int fsize = bpf_classic_proglen(fprog); - unsigned int sk_fsize = sk_filter_size(fprog->len); + unsigned int bpf_fsize = bpf_prog_size(fprog->len); + struct bpf_prog *prog; int err; if (sock_flag(sk, SOCK_FILTER_LOCKED)) @@ -1064,29 +1069,36 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk) if (fprog->filter == NULL) return -EINVAL; - fp = kmalloc(sk_fsize, GFP_KERNEL); - if (!fp) + prog = kmalloc(bpf_fsize, GFP_KERNEL); + if (!prog) return -ENOMEM; - if (copy_from_user(fp->insns, fprog->filter, fsize)) { - kfree(fp); + if (copy_from_user(prog->insns, fprog->filter, fsize)) { + kfree(prog); return -EFAULT; } - fp->len = fprog->len; + prog->len = fprog->len; - err = sk_store_orig_filter(fp, fprog); + err = bpf_prog_store_orig_filter(prog, fprog); if (err) { - kfree(fp); + kfree(prog); return -ENOMEM; } - /* __sk_prepare_filter() already takes care of freeing + /* bpf_prepare_filter() already takes care of freeing * memory in case something goes wrong. */ - fp = __sk_prepare_filter(fp); - if (IS_ERR(fp)) - return PTR_ERR(fp); + prog = bpf_prepare_filter(prog); + if (IS_ERR(prog)) + return PTR_ERR(prog); + + fp = kmalloc(sizeof(*fp), GFP_KERNEL); + if (!fp) { + __bpf_prog_release(prog); + return -ENOMEM; + } + fp->prog = prog; atomic_set(&fp->refcnt, 0); @@ -1142,7 +1154,7 @@ int sk_get_filter(struct sock *sk, struct sock_filter __user *ubuf, /* We're copying the filter that has been originally attached, * so no conversion/decode needed anymore. */ - fprog = filter->orig_prog; + fprog = filter->prog->orig_prog; ret = fprog->len; if (!len) diff --git a/net/core/ptp_classifier.c b/net/core/ptp_classifier.c index 12ab7b4be609..4eab4a94a59d 100644 --- a/net/core/ptp_classifier.c +++ b/net/core/ptp_classifier.c @@ -107,11 +107,11 @@ #include #include -static struct sk_filter *ptp_insns __read_mostly; +static struct bpf_prog *ptp_insns __read_mostly; unsigned int ptp_classify_raw(const struct sk_buff *skb) { - return SK_RUN_FILTER(ptp_insns, skb); + return BPF_PROG_RUN(ptp_insns, skb); } EXPORT_SYMBOL_GPL(ptp_classify_raw); @@ -189,5 +189,5 @@ void __init ptp_classifier_init(void) .len = ARRAY_SIZE(ptp_filter), .filter = ptp_filter, }; - BUG_ON(sk_unattached_filter_create(&ptp_insns, &ptp_prog)); + BUG_ON(bpf_prog_create(&ptp_insns, &ptp_prog)); } diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c index 57d922320c59..ad704c757bb4 100644 --- a/net/core/sock_diag.c +++ b/net/core/sock_diag.c @@ -68,7 +68,7 @@ int sock_diag_put_filterinfo(bool may_report_filterinfo, struct sock *sk, if (!filter) goto out; - fprog = filter->orig_prog; + fprog = filter->prog->orig_prog; flen = bpf_classic_proglen(fprog); attr = nla_reserve(skb, attrtype, flen); diff --git a/net/netfilter/xt_bpf.c b/net/netfilter/xt_bpf.c index bbffdbdaf603..dffee9d47ec4 100644 --- a/net/netfilter/xt_bpf.c +++ b/net/netfilter/xt_bpf.c @@ -28,7 +28,7 @@ static int bpf_mt_check(const struct xt_mtchk_param *par) program.len = info->bpf_program_num_elem; program.filter = info->bpf_program; - if (sk_unattached_filter_create(&info->filter, &program)) { + if (bpf_prog_create(&info->filter, &program)) { pr_info("bpf: check failed: parse error\n"); return -EINVAL; } @@ -40,13 +40,13 @@ static bool bpf_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_bpf_info *info = par->matchinfo; - return SK_RUN_FILTER(info->filter, skb); + return BPF_PROG_RUN(info->filter, skb); } static void bpf_mt_destroy(const struct xt_mtdtor_param *par) { const struct xt_bpf_info *info = par->matchinfo; - sk_unattached_filter_destroy(info->filter); + bpf_prog_destroy(info->filter); } static struct xt_match bpf_mt_reg __read_mostly = { diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index 13f64df2c710..0e30d58149da 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -30,7 +30,7 @@ struct cls_bpf_head { }; struct cls_bpf_prog { - struct sk_filter *filter; + struct bpf_prog *filter; struct sock_filter *bpf_ops; struct tcf_exts exts; struct tcf_result res; @@ -54,7 +54,7 @@ static int cls_bpf_classify(struct sk_buff *skb, const struct tcf_proto *tp, int ret; list_for_each_entry(prog, &head->plist, link) { - int filter_res = SK_RUN_FILTER(prog->filter, skb); + int filter_res = BPF_PROG_RUN(prog->filter, skb); if (filter_res == 0) continue; @@ -92,7 +92,7 @@ static void cls_bpf_delete_prog(struct tcf_proto *tp, struct cls_bpf_prog *prog) tcf_unbind_filter(tp, &prog->res); tcf_exts_destroy(tp, &prog->exts); - sk_unattached_filter_destroy(prog->filter); + bpf_prog_destroy(prog->filter); kfree(prog->bpf_ops); kfree(prog); @@ -161,7 +161,7 @@ static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp, struct sock_filter *bpf_ops, *bpf_old; struct tcf_exts exts; struct sock_fprog_kern tmp; - struct sk_filter *fp, *fp_old; + struct bpf_prog *fp, *fp_old; u16 bpf_size, bpf_len; u32 classid; int ret; @@ -193,7 +193,7 @@ static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp, tmp.len = bpf_len; tmp.filter = bpf_ops; - ret = sk_unattached_filter_create(&fp, &tmp); + ret = bpf_prog_create(&fp, &tmp); if (ret) goto errout_free; @@ -211,7 +211,7 @@ static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp, tcf_exts_change(tp, &prog->exts, &exts); if (fp_old) - sk_unattached_filter_destroy(fp_old); + bpf_prog_destroy(fp_old); if (bpf_old) kfree(bpf_old); -- cgit v1.2.3 From 06aa8b8a0345c78f4d9a1fb3f852952b12a0e40c Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Fri, 1 Aug 2014 12:29:44 +0200 Subject: inet: frags: rename last_in to flags The last_in field has been used to store various flags different from first/last frag in so give it a more descriptive name: flags. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- include/net/inet_frag.h | 2 +- net/ieee802154/reassembly.c | 14 +++++++------- net/ipv4/inet_fragment.c | 14 +++++++------- net/ipv4/ip_fragment.c | 20 ++++++++++---------- net/ipv6/netfilter/nf_conntrack_reasm.c | 12 ++++++------ net/ipv6/reassembly.c | 18 +++++++++--------- 6 files changed, 40 insertions(+), 40 deletions(-) (limited to 'include') diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h index 6f4930a0b660..5024d6c20407 100644 --- a/include/net/inet_frag.h +++ b/include/net/inet_frag.h @@ -25,7 +25,7 @@ struct inet_frag_queue { ktime_t stamp; int len; /* total length of orig datagram */ int meat; - __u8 last_in; /* first/last segment arrived? */ + __u8 flags; /* first/last segment arrived? */ #define INET_FRAG_EVICTED 8 #define INET_FRAG_COMPLETE 4 diff --git a/net/ieee802154/reassembly.c b/net/ieee802154/reassembly.c index f13d4f32e207..5607accd2fee 100644 --- a/net/ieee802154/reassembly.c +++ b/net/ieee802154/reassembly.c @@ -99,7 +99,7 @@ static void lowpan_frag_expire(unsigned long data) spin_lock(&fq->q.lock); - if (fq->q.last_in & INET_FRAG_COMPLETE) + if (fq->q.flags & INET_FRAG_COMPLETE) goto out; inet_frag_kill(&fq->q, &lowpan_frags); @@ -142,7 +142,7 @@ static int lowpan_frag_queue(struct lowpan_frag_queue *fq, struct net_device *dev; int end, offset; - if (fq->q.last_in & INET_FRAG_COMPLETE) + if (fq->q.flags & INET_FRAG_COMPLETE) goto err; offset = lowpan_cb(skb)->d_offset << 3; @@ -154,14 +154,14 @@ static int lowpan_frag_queue(struct lowpan_frag_queue *fq, * or have different end, the segment is corrupted. */ if (end < fq->q.len || - ((fq->q.last_in & INET_FRAG_LAST_IN) && end != fq->q.len)) + ((fq->q.flags & INET_FRAG_LAST_IN) && end != fq->q.len)) goto err; - fq->q.last_in |= INET_FRAG_LAST_IN; + fq->q.flags |= INET_FRAG_LAST_IN; fq->q.len = end; } else { if (end > fq->q.len) { /* Some bits beyond end -> corruption. */ - if (fq->q.last_in & INET_FRAG_LAST_IN) + if (fq->q.flags & INET_FRAG_LAST_IN) goto err; fq->q.len = end; } @@ -201,13 +201,13 @@ found: if (frag_type == LOWPAN_DISPATCH_FRAG1) { /* Calculate uncomp. 6lowpan header to estimate full size */ fq->q.meat += lowpan_uncompress_size(skb, NULL); - fq->q.last_in |= INET_FRAG_FIRST_IN; + fq->q.flags |= INET_FRAG_FIRST_IN; } else { fq->q.meat += skb->len; } add_frag_mem_limit(&fq->q, skb->truesize); - if (fq->q.last_in == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) && + if (fq->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) && fq->q.meat == fq->q.len) { int res; unsigned long orefdst = skb->_skb_refdst; diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index 62b1f73749dc..e3ebc6608e5d 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -152,8 +152,8 @@ evict_again: } /* suppress xmit of (icmp) error packet */ - fq->last_in &= ~INET_FRAG_FIRST_IN; - fq->last_in |= INET_FRAG_EVICTED; + fq->flags &= ~INET_FRAG_FIRST_IN; + fq->flags |= INET_FRAG_EVICTED; hlist_del(&fq->list); hlist_add_head(&fq->list, &expired); ++evicted; @@ -289,16 +289,16 @@ void inet_frag_kill(struct inet_frag_queue *fq, struct inet_frags *f) if (del_timer(&fq->timer)) atomic_dec(&fq->refcnt); - if (!(fq->last_in & INET_FRAG_COMPLETE)) { + if (!(fq->flags & INET_FRAG_COMPLETE)) { fq_unlink(fq, f); atomic_dec(&fq->refcnt); - fq->last_in |= INET_FRAG_COMPLETE; + fq->flags |= INET_FRAG_COMPLETE; } } EXPORT_SYMBOL(inet_frag_kill); static inline void frag_kfree_skb(struct netns_frags *nf, struct inet_frags *f, - struct sk_buff *skb) + struct sk_buff *skb) { if (f->skb_free) f->skb_free(skb); @@ -311,7 +311,7 @@ void inet_frag_destroy(struct inet_frag_queue *q, struct inet_frags *f) struct netns_frags *nf; unsigned int sum, sum_truesize = 0; - WARN_ON(!(q->last_in & INET_FRAG_COMPLETE)); + WARN_ON(!(q->flags & INET_FRAG_COMPLETE)); WARN_ON(del_timer(&q->timer) != 0); /* Release all fragment data. */ @@ -349,7 +349,7 @@ static struct inet_frag_queue *inet_frag_intern(struct netns_frags *nf, if (qp->net == nf && f->match(qp, arg)) { atomic_inc(&qp->refcnt); spin_unlock(&hb->chain_lock); - qp_in->last_in |= INET_FRAG_COMPLETE; + qp_in->flags |= INET_FRAG_COMPLETE; inet_frag_put(qp_in, f); return qp; } diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 634fc31aa243..6fce1ecc5bca 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -185,16 +185,16 @@ static void ip_expire(unsigned long arg) spin_lock(&qp->q.lock); - if (qp->q.last_in & INET_FRAG_COMPLETE) + if (qp->q.flags & INET_FRAG_COMPLETE) goto out; ipq_kill(qp); - if (!(qp->q.last_in & INET_FRAG_EVICTED)) + if (!(qp->q.flags & INET_FRAG_EVICTED)) IP_INC_STATS_BH(net, IPSTATS_MIB_REASMTIMEOUT); IP_INC_STATS_BH(net, IPSTATS_MIB_REASMFAILS); - if ((qp->q.last_in & INET_FRAG_FIRST_IN) && qp->q.fragments != NULL) { + if ((qp->q.flags & INET_FRAG_FIRST_IN) && qp->q.fragments != NULL) { struct sk_buff *head = qp->q.fragments; const struct iphdr *iph; int err; @@ -302,7 +302,7 @@ static int ip_frag_reinit(struct ipq *qp) } while (fp); sub_frag_mem_limit(&qp->q, sum_truesize); - qp->q.last_in = 0; + qp->q.flags = 0; qp->q.len = 0; qp->q.meat = 0; qp->q.fragments = NULL; @@ -323,7 +323,7 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb) int err = -ENOENT; u8 ecn; - if (qp->q.last_in & INET_FRAG_COMPLETE) + if (qp->q.flags & INET_FRAG_COMPLETE) goto err; if (!(IPCB(skb)->flags & IPSKB_FRAG_COMPLETE) && @@ -350,9 +350,9 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb) * or have different end, the segment is corrupted. */ if (end < qp->q.len || - ((qp->q.last_in & INET_FRAG_LAST_IN) && end != qp->q.len)) + ((qp->q.flags & INET_FRAG_LAST_IN) && end != qp->q.len)) goto err; - qp->q.last_in |= INET_FRAG_LAST_IN; + qp->q.flags |= INET_FRAG_LAST_IN; qp->q.len = end; } else { if (end&7) { @@ -362,7 +362,7 @@ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb) } if (end > qp->q.len) { /* Some bits beyond end -> corruption. */ - if (qp->q.last_in & INET_FRAG_LAST_IN) + if (qp->q.flags & INET_FRAG_LAST_IN) goto err; qp->q.len = end; } @@ -471,13 +471,13 @@ found: qp->ecn |= ecn; add_frag_mem_limit(&qp->q, skb->truesize); if (offset == 0) - qp->q.last_in |= INET_FRAG_FIRST_IN; + qp->q.flags |= INET_FRAG_FIRST_IN; if (ip_hdr(skb)->frag_off & htons(IP_DF) && skb->len + ihl > qp->q.max_size) qp->q.max_size = skb->len + ihl; - if (qp->q.last_in == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) && + if (qp->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) && qp->q.meat == qp->q.len) { unsigned long orefdst = skb->_skb_refdst; diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 3d4bccf6d67d..cca686e42b97 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -222,7 +222,7 @@ static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb, int offset, end; u8 ecn; - if (fq->q.last_in & INET_FRAG_COMPLETE) { + if (fq->q.flags & INET_FRAG_COMPLETE) { pr_debug("Already completed\n"); goto err; } @@ -253,11 +253,11 @@ static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb, * or have different end, the segment is corrupted. */ if (end < fq->q.len || - ((fq->q.last_in & INET_FRAG_LAST_IN) && end != fq->q.len)) { + ((fq->q.flags & INET_FRAG_LAST_IN) && end != fq->q.len)) { pr_debug("already received last fragment\n"); goto err; } - fq->q.last_in |= INET_FRAG_LAST_IN; + fq->q.flags |= INET_FRAG_LAST_IN; fq->q.len = end; } else { /* Check if the fragment is rounded to 8 bytes. @@ -272,7 +272,7 @@ static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb, } if (end > fq->q.len) { /* Some bits beyond end -> corruption. */ - if (fq->q.last_in & INET_FRAG_LAST_IN) { + if (fq->q.flags & INET_FRAG_LAST_IN) { pr_debug("last packet already reached.\n"); goto err; } @@ -354,7 +354,7 @@ found: */ if (offset == 0) { fq->nhoffset = nhoff; - fq->q.last_in |= INET_FRAG_FIRST_IN; + fq->q.flags |= INET_FRAG_FIRST_IN; } return 0; @@ -617,7 +617,7 @@ struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb, u32 user) goto ret_orig; } - if (fq->q.last_in == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) && + if (fq->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) && fq->q.meat == fq->q.len) { ret_skb = nf_ct_frag6_reasm(fq, dev); if (ret_skb == NULL) diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 512ccc027ce3..b4baceed0d0d 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -131,7 +131,7 @@ void ip6_expire_frag_queue(struct net *net, struct frag_queue *fq, spin_lock(&fq->q.lock); - if (fq->q.last_in & INET_FRAG_COMPLETE) + if (fq->q.flags & INET_FRAG_COMPLETE) goto out; inet_frag_kill(&fq->q, frags); @@ -141,13 +141,13 @@ void ip6_expire_frag_queue(struct net *net, struct frag_queue *fq, if (!dev) goto out_rcu_unlock; - if (!(fq->q.last_in & INET_FRAG_EVICTED)) + if (!(fq->q.flags & INET_FRAG_EVICTED)) IP6_INC_STATS_BH(net, __in6_dev_get(dev), IPSTATS_MIB_REASMTIMEOUT); IP6_INC_STATS_BH(net, __in6_dev_get(dev), IPSTATS_MIB_REASMFAILS); /* Don't send error if the first segment did not arrive. */ - if (!(fq->q.last_in & INET_FRAG_FIRST_IN) || !fq->q.fragments) + if (!(fq->q.flags & INET_FRAG_FIRST_IN) || !fq->q.fragments) goto out_rcu_unlock; /* @@ -209,7 +209,7 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, struct net *net = dev_net(skb_dst(skb)->dev); u8 ecn; - if (fq->q.last_in & INET_FRAG_COMPLETE) + if (fq->q.flags & INET_FRAG_COMPLETE) goto err; offset = ntohs(fhdr->frag_off) & ~0x7; @@ -240,9 +240,9 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, * or have different end, the segment is corrupted. */ if (end < fq->q.len || - ((fq->q.last_in & INET_FRAG_LAST_IN) && end != fq->q.len)) + ((fq->q.flags & INET_FRAG_LAST_IN) && end != fq->q.len)) goto err; - fq->q.last_in |= INET_FRAG_LAST_IN; + fq->q.flags |= INET_FRAG_LAST_IN; fq->q.len = end; } else { /* Check if the fragment is rounded to 8 bytes. @@ -260,7 +260,7 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, } if (end > fq->q.len) { /* Some bits beyond end -> corruption. */ - if (fq->q.last_in & INET_FRAG_LAST_IN) + if (fq->q.flags & INET_FRAG_LAST_IN) goto err; fq->q.len = end; } @@ -335,10 +335,10 @@ found: */ if (offset == 0) { fq->nhoffset = nhoff; - fq->q.last_in |= INET_FRAG_FIRST_IN; + fq->q.flags |= INET_FRAG_FIRST_IN; } - if (fq->q.last_in == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) && + if (fq->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) && fq->q.meat == fq->q.len) { int res; unsigned long orefdst = skb->_skb_refdst; -- cgit v1.2.3 From 1ab1934ed80aa90b268a62a561f8fdc60812793c Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Fri, 1 Aug 2014 12:29:45 +0200 Subject: inet: frags: enum the flag definitions and add descriptions Move the flags to an enum definion, swap FIRST_IN/LAST_IN to be in increasing order and add comments explaining each flag and the inet_frag_queue struct members. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- include/net/inet_frag.h | 46 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h index 5024d6c20407..90015c47b447 100644 --- a/include/net/inet_frag.h +++ b/include/net/inet_frag.h @@ -15,25 +15,49 @@ struct netns_frags { int low_thresh; }; +/** + * fragment queue flags + * + * @INET_FRAG_FIRST_IN: first fragment has arrived + * @INET_FRAG_LAST_IN: final fragment has arrived + * @INET_FRAG_COMPLETE: frag queue has been processed and is due for destruction + * @INET_FRAG_EVICTED: frag queue is being evicted + */ +enum { + INET_FRAG_FIRST_IN = BIT(0), + INET_FRAG_LAST_IN = BIT(1), + INET_FRAG_COMPLETE = BIT(2), + INET_FRAG_EVICTED = BIT(3) +}; + +/** + * struct inet_frag_queue - fragment queue + * + * @lock: spinlock protecting the queue + * @timer: queue expiration timer + * @list: hash bucket list + * @refcnt: reference count of the queue + * @fragments: received fragments head + * @fragments_tail: received fragments tail + * @stamp: timestamp of the last received fragment + * @len: total length of the original datagram + * @meat: length of received fragments so far + * @flags: fragment queue flags + * @max_size: (ipv4 only) maximum received fragment size with IP_DF set + * @net: namespace that this frag belongs to + */ struct inet_frag_queue { spinlock_t lock; - struct timer_list timer; /* when will this queue expire? */ + struct timer_list timer; struct hlist_node list; atomic_t refcnt; - struct sk_buff *fragments; /* list of received fragments */ + struct sk_buff *fragments; struct sk_buff *fragments_tail; ktime_t stamp; - int len; /* total length of orig datagram */ + int len; int meat; - __u8 flags; /* first/last segment arrived? */ - -#define INET_FRAG_EVICTED 8 -#define INET_FRAG_COMPLETE 4 -#define INET_FRAG_FIRST_IN 2 -#define INET_FRAG_LAST_IN 1 - + __u8 flags; u16 max_size; - struct netns_frags *net; }; -- cgit v1.2.3 From d4ad4d22e7ac6b8711b35d7e86eb29f03f8ac153 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Fri, 1 Aug 2014 12:29:48 +0200 Subject: inet: frags: use kmem_cache for inet_frag_queue Use kmem_cache to allocate/free inet_frag_queue objects since they're all the same size per inet_frags user and are alloced/freed in high volumes thus making it a perfect case for kmem_cache. Signed-off-by: Nikolay Aleksandrov Acked-by: Florian Westphal Signed-off-by: David S. Miller --- include/net/inet_frag.h | 4 +++- net/ieee802154/reassembly.c | 7 ++++++- net/ipv4/inet_fragment.c | 13 ++++++++++--- net/ipv4/ip_fragment.c | 5 ++++- net/ipv6/netfilter/nf_conntrack_reasm.c | 8 ++++++-- net/ipv6/reassembly.c | 7 ++++++- 6 files changed, 35 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h index 90015c47b447..65a8855e99fe 100644 --- a/include/net/inet_frag.h +++ b/include/net/inet_frag.h @@ -101,9 +101,11 @@ struct inet_frags { void (*destructor)(struct inet_frag_queue *); void (*skb_free)(struct sk_buff *); void (*frag_expire)(unsigned long data); + struct kmem_cache *frags_cachep; + const char *frags_cache_name; }; -void inet_frags_init(struct inet_frags *); +int inet_frags_init(struct inet_frags *); void inet_frags_fini(struct inet_frags *); void inet_frags_init_net(struct netns_frags *nf); diff --git a/net/ieee802154/reassembly.c b/net/ieee802154/reassembly.c index 5607accd2fee..ffec6ce51005 100644 --- a/net/ieee802154/reassembly.c +++ b/net/ieee802154/reassembly.c @@ -30,6 +30,8 @@ #include "reassembly.h" +static const char lowpan_frags_cache_name[] = "lowpan-frags"; + struct lowpan_frag_info { __be16 d_tag; u16 d_size; @@ -571,7 +573,10 @@ int __init lowpan_net_frag_init(void) lowpan_frags.qsize = sizeof(struct frag_queue); lowpan_frags.match = lowpan_frag_match; lowpan_frags.frag_expire = lowpan_frag_expire; - inet_frags_init(&lowpan_frags); + lowpan_frags.frags_cache_name = lowpan_frags_cache_name; + ret = inet_frags_init(&lowpan_frags); + if (ret) + goto err_pernet; return ret; err_pernet: diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index 4baa76c60398..9eb89f3f0ee4 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -198,7 +198,7 @@ static void inet_frag_schedule_worker(struct inet_frags *f) schedule_work(&f->frags_work); } -void inet_frags_init(struct inet_frags *f) +int inet_frags_init(struct inet_frags *f) { int i; @@ -213,6 +213,12 @@ void inet_frags_init(struct inet_frags *f) seqlock_init(&f->rnd_seqlock); f->last_rebuild_jiffies = 0; + f->frags_cachep = kmem_cache_create(f->frags_cache_name, f->qsize, 0, 0, + NULL); + if (!f->frags_cachep) + return -ENOMEM; + + return 0; } EXPORT_SYMBOL(inet_frags_init); @@ -225,6 +231,7 @@ EXPORT_SYMBOL(inet_frags_init_net); void inet_frags_fini(struct inet_frags *f) { cancel_work_sync(&f->frags_work); + kmem_cache_destroy(f->frags_cachep); } EXPORT_SYMBOL(inet_frags_fini); @@ -327,7 +334,7 @@ void inet_frag_destroy(struct inet_frag_queue *q, struct inet_frags *f) if (f->destructor) f->destructor(q); - kfree(q); + kmem_cache_free(f->frags_cachep, q); } EXPORT_SYMBOL(inet_frag_destroy); @@ -377,7 +384,7 @@ static struct inet_frag_queue *inet_frag_alloc(struct netns_frags *nf, return NULL; } - q = kzalloc(f->qsize, GFP_ATOMIC); + q = kmem_cache_zalloc(f->frags_cachep, GFP_ATOMIC); if (q == NULL) return NULL; diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index cb56bcc1eee2..15f0e2bad7ad 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -55,6 +55,7 @@ */ static int sysctl_ipfrag_max_dist __read_mostly = 64; +static const char ip_frag_cache_name[] = "ip4-frags"; struct ipfrag_skb_cb { @@ -860,5 +861,7 @@ void __init ipfrag_init(void) ip4_frags.qsize = sizeof(struct ipq); ip4_frags.match = ip4_frag_match; ip4_frags.frag_expire = ip_expire; - inet_frags_init(&ip4_frags); + ip4_frags.frags_cache_name = ip_frag_cache_name; + if (inet_frags_init(&ip4_frags)) + panic("IP: failed to allocate ip4_frags cache\n"); } diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index cca686e42b97..6f187c8d8a1b 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -50,6 +50,7 @@ #include #include +static const char nf_frags_cache_name[] = "nf-frags"; struct nf_ct_frag6_skb_cb { @@ -677,12 +678,15 @@ int nf_ct_frag6_init(void) nf_frags.qsize = sizeof(struct frag_queue); nf_frags.match = ip6_frag_match; nf_frags.frag_expire = nf_ct_frag6_expire; - inet_frags_init(&nf_frags); - + nf_frags.frags_cache_name = nf_frags_cache_name; + ret = inet_frags_init(&nf_frags); + if (ret) + goto out; ret = register_pernet_subsys(&nf_ct_net_ops); if (ret) inet_frags_fini(&nf_frags); +out: return ret; } diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index beb6872a8fa5..c6557d9f7808 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -60,6 +60,8 @@ #include #include +static const char ip6_frag_cache_name[] = "ip6-frags"; + struct ip6frag_skb_cb { struct inet6_skb_parm h; @@ -748,7 +750,10 @@ int __init ipv6_frag_init(void) ip6_frags.qsize = sizeof(struct frag_queue); ip6_frags.match = ip6_frag_match; ip6_frags.frag_expire = ip6_frag_expire; - inet_frags_init(&ip6_frags); + ip6_frags.frags_cache_name = ip6_frag_cache_name; + ret = inet_frags_init(&ip6_frags); + if (ret) + goto err_pernet; out: return ret; -- cgit v1.2.3 From dbcdd4d58c7230bea3157d56d6ef77c493b3865b Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 1 Aug 2014 14:01:51 +0200 Subject: cdc_subset: deal with a device that needs reset for timeout This device needs to be reset to recover from a timeout. Unfortunately this can be handled only at the level of the subdrivers. Signed-off-by: Oliver Neukum Signed-off-by: David S. Miller --- drivers/net/usb/cdc_subset.c | 27 ++++++++++++++++++++++++++- drivers/net/usb/usbnet.c | 8 ++++++-- include/linux/usb/usbnet.h | 3 +++ 3 files changed, 35 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/net/usb/cdc_subset.c b/drivers/net/usb/cdc_subset.c index 91f0919fe278..6ea98cff2d3b 100644 --- a/drivers/net/usb/cdc_subset.c +++ b/drivers/net/usb/cdc_subset.c @@ -85,14 +85,28 @@ static int always_connected (struct usbnet *dev) * *-------------------------------------------------------------------------*/ +static void m5632_recover(struct usbnet *dev) +{ + struct usb_device *udev = dev->udev; + struct usb_interface *intf = dev->intf; + int r; + + r = usb_lock_device_for_reset(udev, intf); + if (r < 0) + return; + + usb_reset_device(udev); + usb_unlock_device(udev); +} + static const struct driver_info ali_m5632_info = { .description = "ALi M5632", .flags = FLAG_POINTTOPOINT, + .recover = m5632_recover, }; #endif - #ifdef CONFIG_USB_AN2720 #define HAVE_HARDWARE @@ -326,12 +340,23 @@ static const struct usb_device_id products [] = { MODULE_DEVICE_TABLE(usb, products); /*-------------------------------------------------------------------------*/ +static int dummy_prereset(struct usb_interface *intf) +{ + return 0; +} + +static int dummy_postreset(struct usb_interface *intf) +{ + return 0; +} static struct usb_driver cdc_subset_driver = { .name = "cdc_subset", .probe = usbnet_probe, .suspend = usbnet_suspend, .resume = usbnet_resume, + .pre_reset = dummy_prereset, + .post_reset = dummy_postreset, .disconnect = usbnet_disconnect, .id_table = products, .disable_hub_initiated_lpm = 1, diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index f9e96c427558..5173821a9575 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -1218,8 +1218,12 @@ void usbnet_tx_timeout (struct net_device *net) unlink_urbs (dev, &dev->txq); tasklet_schedule (&dev->bh); - - // FIXME: device recovery -- reset? + /* this needs to be handled individually because the generic layer + * doesn't know what is sufficient and could not restore private + * information if a remedy of an unconditional reset were used. + */ + if (dev->driver_info->recover) + (dev->driver_info->recover)(dev); } EXPORT_SYMBOL_GPL(usbnet_tx_timeout); diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h index 0662e98fef72..26088feb6608 100644 --- a/include/linux/usb/usbnet.h +++ b/include/linux/usb/usbnet.h @@ -148,6 +148,9 @@ struct driver_info { struct sk_buff *(*tx_fixup)(struct usbnet *dev, struct sk_buff *skb, gfp_t flags); + /* recover from timeout */ + void (*recover)(struct usbnet *dev); + /* early initialization code, can sleep. This is for minidrivers * having 'subminidrivers' that need to do extra initialization * right after minidriver have initialized hardware. */ -- cgit v1.2.3 From 7e1e77636e36075ebf118298855268468f1028e8 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Sat, 2 Aug 2014 11:47:44 +0200 Subject: lib: Resizable, Scalable, Concurrent Hash Table Generic implementation of a resizable, scalable, concurrent hash table based on [0]. The implementation supports both, fixed size keys specified via an offset and length, or arbitrary keys via own hash and compare functions. Lookups are lockless and protected as RCU read side critical sections. Automatic growing/shrinking based on user configurable watermarks is available while allowing concurrent lookups to take place. Objects to be hashed must include a struct rhash_head. The reason for not using the existing struct hlist_head is that the expansion and shrinking will have two buckets point to a single entry which would lead in obscure reverse chaining behaviour. Code includes a boot selftest if CONFIG_TEST_RHASHTABLE is defined. [0] https://www.usenix.org/legacy/event/atc11/tech/final_files/Triplett.pdf Signed-off-by: Thomas Graf Reviewed-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 213 ++++++++++++ lib/Kconfig.debug | 8 + lib/Makefile | 2 +- lib/rhashtable.c | 797 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1019 insertions(+), 1 deletion(-) create mode 100644 include/linux/rhashtable.h create mode 100644 lib/rhashtable.c (limited to 'include') diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h new file mode 100644 index 000000000000..9cda293c867d --- /dev/null +++ b/include/linux/rhashtable.h @@ -0,0 +1,213 @@ +/* + * Resizable, Scalable, Concurrent Hash Table + * + * Copyright (c) 2014 Thomas Graf + * Copyright (c) 2008-2014 Patrick McHardy + * + * Based on the following paper by Josh Triplett, Paul E. McKenney + * and Jonathan Walpole: + * https://www.usenix.org/legacy/event/atc11/tech/final_files/Triplett.pdf + * + * Code partially derived from nft_hash + * + * 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_RHASHTABLE_H +#define _LINUX_RHASHTABLE_H + +#include + +struct rhash_head { + struct rhash_head *next; +}; + +#define INIT_HASH_HEAD(ptr) ((ptr)->next = NULL) + +struct bucket_table { + size_t size; + struct rhash_head __rcu *buckets[]; +}; + +typedef u32 (*rht_hashfn_t)(const void *data, u32 len, u32 seed); +typedef u32 (*rht_obj_hashfn_t)(const void *data, u32 seed); + +struct rhashtable; + +/** + * struct rhashtable_params - Hash table construction parameters + * @nelem_hint: Hint on number of elements, should be 75% of desired size + * @key_len: Length of key + * @key_offset: Offset of key in struct to be hashed + * @head_offset: Offset of rhash_head in struct to be hashed + * @hash_rnd: Seed to use while hashing + * @max_shift: Maximum number of shifts while expanding + * @hashfn: Function to hash key + * @obj_hashfn: Function to hash object + * @grow_decision: If defined, may return true if table should expand + * @shrink_decision: If defined, may return true if table should shrink + * @mutex_is_held: Must return true if protecting mutex is held + */ +struct rhashtable_params { + size_t nelem_hint; + size_t key_len; + size_t key_offset; + size_t head_offset; + u32 hash_rnd; + size_t max_shift; + rht_hashfn_t hashfn; + rht_obj_hashfn_t obj_hashfn; + bool (*grow_decision)(const struct rhashtable *ht, + size_t new_size); + bool (*shrink_decision)(const struct rhashtable *ht, + size_t new_size); + int (*mutex_is_held)(void); +}; + +/** + * struct rhashtable - Hash table handle + * @tbl: Bucket table + * @nelems: Number of elements in table + * @shift: Current size (1 << shift) + * @p: Configuration parameters + */ +struct rhashtable { + struct bucket_table __rcu *tbl; + size_t nelems; + size_t shift; + struct rhashtable_params p; +}; + +#ifdef CONFIG_PROVE_LOCKING +int lockdep_rht_mutex_is_held(const struct rhashtable *ht); +#else +static inline int lockdep_rht_mutex_is_held(const struct rhashtable *ht) +{ + return 1; +} +#endif /* CONFIG_PROVE_LOCKING */ + +int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params); + +u32 rhashtable_hashfn(const struct rhashtable *ht, const void *key, u32 len); +u32 rhashtable_obj_hashfn(const struct rhashtable *ht, void *ptr); + +void rhashtable_insert(struct rhashtable *ht, struct rhash_head *node, gfp_t); +bool rhashtable_remove(struct rhashtable *ht, struct rhash_head *node, gfp_t); +void rhashtable_remove_pprev(struct rhashtable *ht, struct rhash_head *obj, + struct rhash_head **pprev, gfp_t flags); + +bool rht_grow_above_75(const struct rhashtable *ht, size_t new_size); +bool rht_shrink_below_30(const struct rhashtable *ht, size_t new_size); + +int rhashtable_expand(struct rhashtable *ht, gfp_t flags); +int rhashtable_shrink(struct rhashtable *ht, gfp_t flags); + +void *rhashtable_lookup(const struct rhashtable *ht, const void *key); +void *rhashtable_lookup_compare(const struct rhashtable *ht, u32 hash, + bool (*compare)(void *, void *), void *arg); + +void rhashtable_destroy(const struct rhashtable *ht); + +#define rht_dereference(p, ht) \ + rcu_dereference_protected(p, lockdep_rht_mutex_is_held(ht)) + +#define rht_dereference_rcu(p, ht) \ + rcu_dereference_check(p, lockdep_rht_mutex_is_held(ht)) + +/* Internal, use rht_obj() instead */ +#define rht_entry(ptr, type, member) container_of(ptr, type, member) +#define rht_entry_safe(ptr, type, member) \ +({ \ + typeof(ptr) __ptr = (ptr); \ + __ptr ? rht_entry(__ptr, type, member) : NULL; \ +}) +#define rht_entry_safe_rcu(ptr, type, member) \ +({ \ + typeof(*ptr) __rcu *__ptr = (typeof(*ptr) __rcu __force *)ptr; \ + __ptr ? container_of((typeof(ptr))rcu_dereference_raw(__ptr), type, member) : NULL; \ +}) + +#define rht_next_entry_safe(pos, ht, member) \ +({ \ + pos ? rht_entry_safe(rht_dereference((pos)->member.next, ht), \ + typeof(*(pos)), member) : NULL; \ +}) + +/** + * rht_for_each - iterate over hash chain + * @pos: &struct rhash_head to use as a loop cursor. + * @head: head of the hash chain (struct rhash_head *) + * @ht: pointer to your struct rhashtable + */ +#define rht_for_each(pos, head, ht) \ + for (pos = rht_dereference(head, ht); \ + pos; \ + pos = rht_dereference((pos)->next, ht)) + +/** + * rht_for_each_entry - iterate over hash chain of given type + * @pos: type * to use as a loop cursor. + * @head: head of the hash chain (struct rhash_head *) + * @ht: pointer to your struct rhashtable + * @member: name of the rhash_head within the hashable struct. + */ +#define rht_for_each_entry(pos, head, ht, member) \ + for (pos = rht_entry_safe(rht_dereference(head, ht), \ + typeof(*(pos)), member); \ + pos; \ + pos = rht_next_entry_safe(pos, ht, member)) + +/** + * rht_for_each_entry_safe - safely iterate over hash chain of given type + * @pos: type * to use as a loop cursor. + * @n: type * to use for temporary next object storage + * @head: head of the hash chain (struct rhash_head *) + * @ht: pointer to your struct rhashtable + * @member: name of the rhash_head within the hashable struct. + * + * This hash chain list-traversal primitive allows for the looped code to + * remove the loop cursor from the list. + */ +#define rht_for_each_entry_safe(pos, n, head, ht, member) \ + for (pos = rht_entry_safe(rht_dereference(head, ht), \ + typeof(*(pos)), member), \ + n = rht_next_entry_safe(pos, ht, member); \ + pos; \ + pos = n, \ + n = rht_next_entry_safe(pos, ht, member)) + +/** + * rht_for_each_rcu - iterate over rcu hash chain + * @pos: &struct rhash_head to use as a loop cursor. + * @head: head of the hash chain (struct rhash_head *) + * @ht: pointer to your struct rhashtable + * + * This hash chain list-traversal primitive may safely run concurrently with + * the _rcu fkht mutation primitives such as rht_insert() as long as the + * traversal is guarded by rcu_read_lock(). + */ +#define rht_for_each_rcu(pos, head, ht) \ + for (pos = rht_dereference_rcu(head, ht); \ + pos; \ + pos = rht_dereference_rcu((pos)->next, ht)) + +/** + * rht_for_each_entry_rcu - iterate over rcu hash chain of given type + * @pos: type * to use as a loop cursor. + * @head: head of the hash chain (struct rhash_head *) + * @member: name of the rhash_head within the hashable struct. + * + * This hash chain list-traversal primitive may safely run concurrently with + * the _rcu fkht mutation primitives such as rht_insert() as long as the + * traversal is guarded by rcu_read_lock(). + */ +#define rht_for_each_entry_rcu(pos, head, member) \ + for (pos = rht_entry_safe_rcu(head, typeof(*(pos)), member); \ + pos; \ + pos = rht_entry_safe_rcu((pos)->member.next, \ + typeof(*(pos)), member)) + +#endif /* _LINUX_RHASHTABLE_H */ diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 7a638aa3545b..f11a2e8f6157 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -1550,6 +1550,14 @@ config TEST_STRING_HELPERS config TEST_KSTRTOX tristate "Test kstrto*() family of functions at runtime" +config TEST_RHASHTABLE + bool "Perform selftest on resizable hash table" + default n + help + Enable this option to test the rhashtable functions at boot. + + If unsure, say N. + endmenu # runtime tests config PROVIDE_OHCI1394_DMA_INIT diff --git a/lib/Makefile b/lib/Makefile index ba967a19edba..fd248e4c05ad 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -26,7 +26,7 @@ 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 \ gcd.o lcm.o list_sort.o uuid.o flex_array.o iovec.o clz_ctz.o \ bsearch.o find_last_bit.o find_next_bit.o llist.o memweight.o kfifo.o \ - percpu-refcount.o percpu_ida.o hash.o + percpu-refcount.o percpu_ida.o hash.o rhashtable.o obj-y += string_helpers.o obj-$(CONFIG_TEST_STRING_HELPERS) += test-string_helpers.o obj-y += kstrtox.o diff --git a/lib/rhashtable.c b/lib/rhashtable.c new file mode 100644 index 000000000000..e6940cf16628 --- /dev/null +++ b/lib/rhashtable.c @@ -0,0 +1,797 @@ +/* + * Resizable, Scalable, Concurrent Hash Table + * + * Copyright (c) 2014 Thomas Graf + * Copyright (c) 2008-2014 Patrick McHardy + * + * Based on the following paper: + * https://www.usenix.org/legacy/event/atc11/tech/final_files/Triplett.pdf + * + * Code partially derived from nft_hash + * + * 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 + +#define HASH_DEFAULT_SIZE 64UL +#define HASH_MIN_SIZE 4UL + +#define ASSERT_RHT_MUTEX(HT) BUG_ON(!lockdep_rht_mutex_is_held(HT)) + +#ifdef CONFIG_PROVE_LOCKING +int lockdep_rht_mutex_is_held(const struct rhashtable *ht) +{ + return ht->p.mutex_is_held(); +} +EXPORT_SYMBOL_GPL(lockdep_rht_mutex_is_held); +#endif + +/** + * rht_obj - cast hash head to outer object + * @ht: hash table + * @he: hashed node + */ +void *rht_obj(const struct rhashtable *ht, const struct rhash_head *he) +{ + return (void *) he - ht->p.head_offset; +} +EXPORT_SYMBOL_GPL(rht_obj); + +static u32 __hashfn(const struct rhashtable *ht, const void *key, + u32 len, u32 hsize) +{ + u32 h; + + h = ht->p.hashfn(key, len, ht->p.hash_rnd); + + return h & (hsize - 1); +} + +/** + * rhashtable_hashfn - compute hash for key of given length + * @ht: hash table to compuate for + * @key: pointer to key + * @len: length of key + * + * Computes the hash value using the hash function provided in the 'hashfn' + * of struct rhashtable_params. The returned value is guaranteed to be + * smaller than the number of buckets in the hash table. + */ +u32 rhashtable_hashfn(const struct rhashtable *ht, const void *key, u32 len) +{ + struct bucket_table *tbl = rht_dereference_rcu(ht->tbl, ht); + + return __hashfn(ht, key, len, tbl->size); +} +EXPORT_SYMBOL_GPL(rhashtable_hashfn); + +static u32 obj_hashfn(const struct rhashtable *ht, const void *ptr, u32 hsize) +{ + if (unlikely(!ht->p.key_len)) { + u32 h; + + h = ht->p.obj_hashfn(ptr, ht->p.hash_rnd); + + return h & (hsize - 1); + } + + return __hashfn(ht, ptr + ht->p.key_offset, ht->p.key_len, hsize); +} + +/** + * rhashtable_obj_hashfn - compute hash for hashed object + * @ht: hash table to compuate for + * @ptr: pointer to hashed object + * + * Computes the hash value using the hash function `hashfn` respectively + * 'obj_hashfn' depending on whether the hash table is set up to work with + * a fixed length key. The returned value is guaranteed to be smaller than + * the number of buckets in the hash table. + */ +u32 rhashtable_obj_hashfn(const struct rhashtable *ht, void *ptr) +{ + struct bucket_table *tbl = rht_dereference_rcu(ht->tbl, ht); + + return obj_hashfn(ht, ptr, tbl->size); +} +EXPORT_SYMBOL_GPL(rhashtable_obj_hashfn); + +static u32 head_hashfn(const struct rhashtable *ht, + const struct rhash_head *he, u32 hsize) +{ + return obj_hashfn(ht, rht_obj(ht, he), hsize); +} + +static struct bucket_table *bucket_table_alloc(size_t nbuckets, gfp_t flags) +{ + struct bucket_table *tbl; + size_t size; + + size = sizeof(*tbl) + nbuckets * sizeof(tbl->buckets[0]); + tbl = kzalloc(size, flags); + if (tbl == NULL) + tbl = vzalloc(size); + + if (tbl == NULL) + return NULL; + + tbl->size = nbuckets; + + return tbl; +} + +static void bucket_table_free(const struct bucket_table *tbl) +{ + kvfree(tbl); +} + +/** + * rht_grow_above_75 - returns true if nelems > 0.75 * table-size + * @ht: hash table + * @new_size: new table size + */ +bool rht_grow_above_75(const struct rhashtable *ht, size_t new_size) +{ + /* Expand table when exceeding 75% load */ + return ht->nelems > (new_size / 4 * 3); +} +EXPORT_SYMBOL_GPL(rht_grow_above_75); + +/** + * rht_shrink_below_30 - returns true if nelems < 0.3 * table-size + * @ht: hash table + * @new_size: new table size + */ +bool rht_shrink_below_30(const struct rhashtable *ht, size_t new_size) +{ + /* Shrink table beneath 30% load */ + return ht->nelems < (new_size * 3 / 10); +} +EXPORT_SYMBOL_GPL(rht_shrink_below_30); + +static void hashtable_chain_unzip(const struct rhashtable *ht, + const struct bucket_table *new_tbl, + struct bucket_table *old_tbl, size_t n) +{ + struct rhash_head *he, *p, *next; + unsigned int h; + + /* Old bucket empty, no work needed. */ + p = rht_dereference(old_tbl->buckets[n], ht); + if (!p) + return; + + /* Advance the old bucket pointer one or more times until it + * reaches a node that doesn't hash to the same bucket as the + * previous node p. Call the previous node p; + */ + h = head_hashfn(ht, p, new_tbl->size); + rht_for_each(he, p->next, ht) { + if (head_hashfn(ht, he, new_tbl->size) != h) + break; + p = he; + } + RCU_INIT_POINTER(old_tbl->buckets[n], p->next); + + /* Find the subsequent node which does hash to the same + * bucket as node P, or NULL if no such node exists. + */ + next = NULL; + if (he) { + rht_for_each(he, he->next, ht) { + if (head_hashfn(ht, he, new_tbl->size) == h) { + next = he; + break; + } + } + } + + /* Set p's next pointer to that subsequent node pointer, + * bypassing the nodes which do not hash to p's bucket + */ + RCU_INIT_POINTER(p->next, next); +} + +/** + * rhashtable_expand - Expand hash table while allowing concurrent lookups + * @ht: the hash table to expand + * @flags: allocation flags + * + * A secondary bucket array is allocated and the hash entries are migrated + * while keeping them on both lists until the end of the RCU grace period. + * + * This function may only be called in a context where it is safe to call + * synchronize_rcu(), e.g. not within a rcu_read_lock() section. + * + * The caller must ensure that no concurrent table mutations take place. + * It is however valid to have concurrent lookups if they are RCU protected. + */ +int rhashtable_expand(struct rhashtable *ht, gfp_t flags) +{ + struct bucket_table *new_tbl, *old_tbl = rht_dereference(ht->tbl, ht); + struct rhash_head *he; + unsigned int i, h; + bool complete; + + ASSERT_RHT_MUTEX(ht); + + if (ht->p.max_shift && ht->shift >= ht->p.max_shift) + return 0; + + new_tbl = bucket_table_alloc(old_tbl->size * 2, flags); + if (new_tbl == NULL) + return -ENOMEM; + + ht->shift++; + + /* For each new bucket, search the corresponding old bucket + * for the first entry that hashes to the new bucket, and + * link the new bucket to that entry. Since all the entries + * which will end up in the new bucket appear in the same + * old bucket, this constructs an entirely valid new hash + * table, but with multiple buckets "zipped" together into a + * single imprecise chain. + */ + for (i = 0; i < new_tbl->size; i++) { + h = i & (old_tbl->size - 1); + rht_for_each(he, old_tbl->buckets[h], ht) { + if (head_hashfn(ht, he, new_tbl->size) == i) { + RCU_INIT_POINTER(new_tbl->buckets[i], he); + break; + } + } + } + + /* Publish the new table pointer. Lookups may now traverse + * the new table, but they will not benefit from any + * additional efficiency until later steps unzip the buckets. + */ + rcu_assign_pointer(ht->tbl, new_tbl); + + /* Unzip interleaved hash chains */ + do { + /* Wait for readers. All new readers will see the new + * table, and thus no references to the old table will + * remain. + */ + synchronize_rcu(); + + /* For each bucket in the old table (each of which + * contains items from multiple buckets of the new + * table): ... + */ + complete = true; + for (i = 0; i < old_tbl->size; i++) { + hashtable_chain_unzip(ht, new_tbl, old_tbl, i); + if (old_tbl->buckets[i] != NULL) + complete = false; + } + } while (!complete); + + bucket_table_free(old_tbl); + return 0; +} +EXPORT_SYMBOL_GPL(rhashtable_expand); + +/** + * rhashtable_shrink - Shrink hash table while allowing concurrent lookups + * @ht: the hash table to shrink + * @flags: allocation flags + * + * This function may only be called in a context where it is safe to call + * synchronize_rcu(), e.g. not within a rcu_read_lock() section. + * + * The caller must ensure that no concurrent table mutations take place. + * It is however valid to have concurrent lookups if they are RCU protected. + */ +int rhashtable_shrink(struct rhashtable *ht, gfp_t flags) +{ + struct bucket_table *ntbl, *tbl = rht_dereference(ht->tbl, ht); + struct rhash_head __rcu **pprev; + unsigned int i; + + ASSERT_RHT_MUTEX(ht); + + if (tbl->size <= HASH_MIN_SIZE) + return 0; + + ntbl = bucket_table_alloc(tbl->size / 2, flags); + if (ntbl == NULL) + return -ENOMEM; + + ht->shift--; + + /* Link each bucket in the new table to the first bucket + * in the old table that contains entries which will hash + * to the new bucket. + */ + for (i = 0; i < ntbl->size; i++) { + ntbl->buckets[i] = tbl->buckets[i]; + + /* Link each bucket in the new table to the first bucket + * in the old table that contains entries which will hash + * to the new bucket. + */ + for (pprev = &ntbl->buckets[i]; *pprev != NULL; + pprev = &rht_dereference(*pprev, ht)->next) + ; + RCU_INIT_POINTER(*pprev, tbl->buckets[i + ntbl->size]); + } + + /* Publish the new, valid hash table */ + rcu_assign_pointer(ht->tbl, ntbl); + + /* Wait for readers. No new readers will have references to the + * old hash table. + */ + synchronize_rcu(); + + bucket_table_free(tbl); + + return 0; +} +EXPORT_SYMBOL_GPL(rhashtable_shrink); + +/** + * rhashtable_insert - insert object into hash hash table + * @ht: hash table + * @obj: pointer to hash head inside object + * @flags: allocation flags (table expansion) + * + * Will automatically grow the table via rhashtable_expand() if the the + * grow_decision function specified at rhashtable_init() returns true. + * + * The caller must ensure that no concurrent table mutations occur. It is + * however valid to have concurrent lookups if they are RCU protected. + */ +void rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj, + gfp_t flags) +{ + struct bucket_table *tbl = rht_dereference(ht->tbl, ht); + u32 hash; + + ASSERT_RHT_MUTEX(ht); + + hash = head_hashfn(ht, obj, tbl->size); + RCU_INIT_POINTER(obj->next, tbl->buckets[hash]); + rcu_assign_pointer(tbl->buckets[hash], obj); + ht->nelems++; + + if (ht->p.grow_decision && ht->p.grow_decision(ht, tbl->size)) + rhashtable_expand(ht, flags); +} +EXPORT_SYMBOL_GPL(rhashtable_insert); + +/** + * rhashtable_remove_pprev - remove object from hash table given previous element + * @ht: hash table + * @obj: pointer to hash head inside object + * @pprev: pointer to previous element + * @flags: allocation flags (table expansion) + * + * Identical to rhashtable_remove() but caller is alreayd aware of the element + * in front of the element to be deleted. This is in particular useful for + * deletion when combined with walking or lookup. + */ +void rhashtable_remove_pprev(struct rhashtable *ht, struct rhash_head *obj, + struct rhash_head **pprev, gfp_t flags) +{ + struct bucket_table *tbl = rht_dereference(ht->tbl, ht); + + ASSERT_RHT_MUTEX(ht); + + RCU_INIT_POINTER(*pprev, obj->next); + ht->nelems--; + + if (ht->p.shrink_decision && + ht->p.shrink_decision(ht, tbl->size)) + rhashtable_shrink(ht, flags); +} +EXPORT_SYMBOL_GPL(rhashtable_remove_pprev); + +/** + * rhashtable_remove - remove object from hash table + * @ht: hash table + * @obj: pointer to hash head inside object + * @flags: allocation flags (table expansion) + * + * Since the hash chain is single linked, the removal operation needs to + * walk the bucket chain upon removal. The removal operation is thus + * considerable slow if the hash table is not correctly sized. + * + * Will automatically shrink the table via rhashtable_expand() if the the + * shrink_decision function specified at rhashtable_init() returns true. + * + * The caller must ensure that no concurrent table mutations occur. It is + * however valid to have concurrent lookups if they are RCU protected. + */ +bool rhashtable_remove(struct rhashtable *ht, struct rhash_head *obj, + gfp_t flags) +{ + struct bucket_table *tbl = rht_dereference(ht->tbl, ht); + struct rhash_head __rcu **pprev; + struct rhash_head *he; + u32 h; + + ASSERT_RHT_MUTEX(ht); + + h = head_hashfn(ht, obj, tbl->size); + + pprev = &tbl->buckets[h]; + rht_for_each(he, tbl->buckets[h], ht) { + if (he != obj) { + pprev = &he->next; + continue; + } + + rhashtable_remove_pprev(ht, he, pprev, flags); + return true; + } + + return false; +} +EXPORT_SYMBOL_GPL(rhashtable_remove); + +/** + * rhashtable_lookup - lookup key in hash table + * @ht: hash table + * @key: pointer to key + * + * Computes the hash value for the key and traverses the bucket chain looking + * for a entry with an identical key. The first matching entry is returned. + * + * This lookup function may only be used for fixed key hash table (key_len + * paramter set). It will BUG() if used inappropriately. + * + * Lookups may occur in parallel with hash mutations as long as the lookup is + * guarded by rcu_read_lock(). The caller must take care of this. + */ +void *rhashtable_lookup(const struct rhashtable *ht, const void *key) +{ + const struct bucket_table *tbl = rht_dereference_rcu(ht->tbl, ht); + struct rhash_head *he; + u32 h; + + BUG_ON(!ht->p.key_len); + + h = __hashfn(ht, key, ht->p.key_len, tbl->size); + rht_for_each_rcu(he, tbl->buckets[h], ht) { + if (memcmp(rht_obj(ht, he) + ht->p.key_offset, key, + ht->p.key_len)) + continue; + return (void *) he - ht->p.head_offset; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(rhashtable_lookup); + +/** + * rhashtable_lookup_compare - search hash table with compare function + * @ht: hash table + * @hash: hash value of desired entry + * @compare: compare function, must return true on match + * @arg: argument passed on to compare function + * + * Traverses the bucket chain behind the provided hash value and calls the + * specified compare function for each entry. + * + * Lookups may occur in parallel with hash mutations as long as the lookup is + * guarded by rcu_read_lock(). The caller must take care of this. + * + * Returns the first entry on which the compare function returned true. + */ +void *rhashtable_lookup_compare(const struct rhashtable *ht, u32 hash, + bool (*compare)(void *, void *), void *arg) +{ + const struct bucket_table *tbl = rht_dereference_rcu(ht->tbl, ht); + struct rhash_head *he; + + if (unlikely(hash >= tbl->size)) + return NULL; + + rht_for_each_rcu(he, tbl->buckets[hash], ht) { + if (!compare(rht_obj(ht, he), arg)) + continue; + return (void *) he - ht->p.head_offset; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(rhashtable_lookup_compare); + +static size_t rounded_hashtable_size(unsigned int nelem) +{ + return max(roundup_pow_of_two(nelem * 4 / 3), HASH_MIN_SIZE); +} + +/** + * rhashtable_init - initialize a new hash table + * @ht: hash table to be initialized + * @params: configuration parameters + * + * Initializes a new hash table based on the provided configuration + * parameters. A table can be configured either with a variable or + * fixed length key: + * + * Configuration Example 1: Fixed length keys + * struct test_obj { + * int key; + * void * my_member; + * struct rhash_head node; + * }; + * + * struct rhashtable_params params = { + * .head_offset = offsetof(struct test_obj, node), + * .key_offset = offsetof(struct test_obj, key), + * .key_len = sizeof(int), + * .hashfn = arch_fast_hash, + * .mutex_is_held = &my_mutex_is_held, + * }; + * + * Configuration Example 2: Variable length keys + * struct test_obj { + * [...] + * struct rhash_head node; + * }; + * + * u32 my_hash_fn(const void *data, u32 seed) + * { + * struct test_obj *obj = data; + * + * return [... hash ...]; + * } + * + * struct rhashtable_params params = { + * .head_offset = offsetof(struct test_obj, node), + * .hashfn = arch_fast_hash, + * .obj_hashfn = my_hash_fn, + * .mutex_is_held = &my_mutex_is_held, + * }; + */ +int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params) +{ + struct bucket_table *tbl; + size_t size; + + size = HASH_DEFAULT_SIZE; + + if ((params->key_len && !params->hashfn) || + (!params->key_len && !params->obj_hashfn)) + return -EINVAL; + + if (params->nelem_hint) + size = rounded_hashtable_size(params->nelem_hint); + + tbl = bucket_table_alloc(size, GFP_KERNEL); + if (tbl == NULL) + return -ENOMEM; + + memset(ht, 0, sizeof(*ht)); + ht->shift = ilog2(tbl->size); + memcpy(&ht->p, params, sizeof(*params)); + RCU_INIT_POINTER(ht->tbl, tbl); + + if (!ht->p.hash_rnd) + get_random_bytes(&ht->p.hash_rnd, sizeof(ht->p.hash_rnd)); + + return 0; +} +EXPORT_SYMBOL_GPL(rhashtable_init); + +/** + * rhashtable_destroy - destroy hash table + * @ht: the hash table to destroy + * + * Frees the bucket array. + */ +void rhashtable_destroy(const struct rhashtable *ht) +{ + const struct bucket_table *tbl = rht_dereference(ht->tbl, ht); + + bucket_table_free(tbl); +} +EXPORT_SYMBOL_GPL(rhashtable_destroy); + +/************************************************************************** + * Self Test + **************************************************************************/ + +#ifdef CONFIG_TEST_RHASHTABLE + +#define TEST_HT_SIZE 8 +#define TEST_ENTRIES 2048 +#define TEST_PTR ((void *) 0xdeadbeef) +#define TEST_NEXPANDS 4 + +static int test_mutex_is_held(void) +{ + return 1; +} + +struct test_obj { + void *ptr; + int value; + struct rhash_head node; +}; + +static int __init test_rht_lookup(struct rhashtable *ht) +{ + unsigned int i; + + for (i = 0; i < TEST_ENTRIES * 2; i++) { + struct test_obj *obj; + bool expected = !(i % 2); + u32 key = i; + + obj = rhashtable_lookup(ht, &key); + + if (expected && !obj) { + pr_warn("Test failed: Could not find key %u\n", key); + return -ENOENT; + } else if (!expected && obj) { + pr_warn("Test failed: Unexpected entry found for key %u\n", + key); + return -EEXIST; + } else if (expected && obj) { + if (obj->ptr != TEST_PTR || obj->value != i) { + pr_warn("Test failed: Lookup value mismatch %p!=%p, %u!=%u\n", + obj->ptr, TEST_PTR, obj->value, i); + return -EINVAL; + } + } + } + + return 0; +} + +static void test_bucket_stats(struct rhashtable *ht, + struct bucket_table *tbl, + bool quiet) +{ + unsigned int cnt, i, total = 0; + struct test_obj *obj; + + for (i = 0; i < tbl->size; i++) { + cnt = 0; + + if (!quiet) + pr_info(" [%#4x/%zu]", i, tbl->size); + + rht_for_each_entry_rcu(obj, tbl->buckets[i], node) { + cnt++; + total++; + if (!quiet) + pr_cont(" [%p],", obj); + } + + if (!quiet) + pr_cont("\n [%#x] first element: %p, chain length: %u\n", + i, tbl->buckets[i], cnt); + } + + pr_info(" Traversal complete: counted=%u, nelems=%zu, entries=%d\n", + total, ht->nelems, TEST_ENTRIES); +} + +static int __init test_rhashtable(struct rhashtable *ht) +{ + struct bucket_table *tbl; + struct test_obj *obj, *next; + int err; + unsigned int i; + + /* + * Insertion Test: + * Insert TEST_ENTRIES into table with all keys even numbers + */ + pr_info(" Adding %d keys\n", TEST_ENTRIES); + for (i = 0; i < TEST_ENTRIES; i++) { + struct test_obj *obj; + + obj = kzalloc(sizeof(*obj), GFP_KERNEL); + if (!obj) { + err = -ENOMEM; + goto error; + } + + obj->ptr = TEST_PTR; + obj->value = i * 2; + + rhashtable_insert(ht, &obj->node, GFP_KERNEL); + } + + rcu_read_lock(); + tbl = rht_dereference_rcu(ht->tbl, ht); + test_bucket_stats(ht, tbl, true); + test_rht_lookup(ht); + rcu_read_unlock(); + + for (i = 0; i < TEST_NEXPANDS; i++) { + pr_info(" Table expansion iteration %u...\n", i); + rhashtable_expand(ht, GFP_KERNEL); + + rcu_read_lock(); + pr_info(" Verifying lookups...\n"); + test_rht_lookup(ht); + rcu_read_unlock(); + } + + for (i = 0; i < TEST_NEXPANDS; i++) { + pr_info(" Table shrinkage iteration %u...\n", i); + rhashtable_shrink(ht, GFP_KERNEL); + + rcu_read_lock(); + pr_info(" Verifying lookups...\n"); + test_rht_lookup(ht); + rcu_read_unlock(); + } + + pr_info(" Deleting %d keys\n", TEST_ENTRIES); + for (i = 0; i < TEST_ENTRIES; i++) { + u32 key = i * 2; + + obj = rhashtable_lookup(ht, &key); + BUG_ON(!obj); + + rhashtable_remove(ht, &obj->node, GFP_KERNEL); + kfree(obj); + } + + return 0; + +error: + tbl = rht_dereference_rcu(ht->tbl, ht); + for (i = 0; i < tbl->size; i++) + rht_for_each_entry_safe(obj, next, tbl->buckets[i], ht, node) + kfree(obj); + + return err; +} + +static int __init test_rht_init(void) +{ + struct rhashtable ht; + struct rhashtable_params params = { + .nelem_hint = TEST_HT_SIZE, + .head_offset = offsetof(struct test_obj, node), + .key_offset = offsetof(struct test_obj, value), + .key_len = sizeof(int), + .hashfn = arch_fast_hash, + .mutex_is_held = &test_mutex_is_held, + .grow_decision = rht_grow_above_75, + .shrink_decision = rht_shrink_below_30, + }; + int err; + + pr_info("Running resizable hashtable tests...\n"); + + err = rhashtable_init(&ht, ¶ms); + if (err < 0) { + pr_warn("Test failed: Unable to initialize hashtable: %d\n", + err); + return err; + } + + err = test_rhashtable(&ht); + + rhashtable_destroy(&ht); + + return err; +} + +subsys_initcall(test_rht_init); + +#endif /* CONFIG_TEST_RHASHTABLE */ -- cgit v1.2.3 From 3854fab24e899c02439657956ab1d2c85001958c Mon Sep 17 00:00:00 2001 From: YoungJun Cho Date: Thu, 17 Jul 2014 18:01:21 +0900 Subject: drm/exynos: fimd: support LCD I80 interface To support MIPI command mode based I80 interface panel, FIMD should do followings: - Sets LCD I80 interface timings configuration. - Uses "lcd_sys" as an IRQ resource and sets relevant IRQ configuration. - Sets LCD block configuration for I80 interface. - Sets ideal(pixel) clock is 2 times faster than the original one to generate frame done IRQ prior to the next TE signal. - Implements trigger feature that transfers image data if there is page flip request, and implements TE handler to call trigger function. Signed-off-by: YoungJun Cho Acked-by: Inki Dae Acked-by: Kyungmin Park Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/Kconfig | 1 + drivers/gpu/drm/exynos/exynos_drm_fimd.c | 276 ++++++++++++++++++++++++++----- include/video/samsung_fimd.h | 3 +- 3 files changed, 235 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 178d2a9672a8..9ba1aaeb8070 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -28,6 +28,7 @@ config DRM_EXYNOS_FIMD bool "Exynos DRM FIMD" depends on DRM_EXYNOS && !FB_S3C select FB_MODE_HELPERS + select MFD_SYSCON help Choose this option if you want to use Exynos FIMD for DRM. diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 33161ad38201..28a3168e24a7 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include